Merge pull request #4253 from aws-lumberyard-dev/MigrationFixup

Migration fixup
This commit is contained in:
kberg-amzn
2021-10-04 13:50:28 -07:00
committed by GitHub
80 changed files with 1085 additions and 579 deletions
@@ -1367,9 +1367,6 @@ namespace AZ
#endif
}
//=========================================================================
// Tick
//=========================================================================
void ComponentApplication::Tick(float deltaOverride /*= -1.f*/)
{
{
@@ -1397,9 +1394,6 @@ namespace AZ
}
}
//=========================================================================
// Tick
//=========================================================================
void ComponentApplication::TickSystem()
{
AZ_PROFILE_SCOPE(System, "Component application tick");
@@ -1547,5 +1541,4 @@ namespace AZ
AZ::SettingsRegistryScriptUtils::ReflectSettingsRegistryToBehaviorContext(*behaviorContext);
}
}
} // namespace AZ
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/ComponentApplicationBus.h>
-1
View File
@@ -5,7 +5,6 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/base.h>
+64 -1
View File
@@ -13,14 +13,26 @@
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/RTTI/TypeSafeIntegral.h>
#include <AzCore/std/time.h>
#include <AzCore/std/chrono/chrono.h>
namespace AZ
{
//! This is a strong typedef for representing a millisecond value since application start.
AZ_TYPE_SAFE_INTEGRAL(TimeMs, int64_t);
//! This is a strong typedef for representing a microsecond value since application start.
//! Using int64_t as the underlying type, this is good to represent approximately 292,471 years
AZ_TYPE_SAFE_INTEGRAL(TimeUs, int64_t);
//! @class ITime
//! @brief This is an AZ::Interface<> for managing time related operations.
//! AZ::ITime and associated types may not operate in realtime. These abstractions are to allow our application
//! simulation to operate both slower and faster than realtime in a well defined and user controllable manner
//! The rate at which time passes for AZ::ITime is controlled by the cvar t_scale
//! t_scale == 0 means simulation time should halt
//! 0 < t_scale < 1 will cause time to pass slower than realtime, with t_scale 0.1 being roughly 1/10th realtime
//! t_scale == 1 will cause time to pass at roughly realtime
//! t_scale > 1 will cause time to pass faster than normal, with t_scale 10 being roughly 10x realtime
class ITime
{
public:
@@ -33,6 +45,10 @@ namespace AZ
//! @return the number of milliseconds that have elapsed since application start
virtual TimeMs GetElapsedTimeMs() const = 0;
//! Returns the number of microseconds since application start.
//! @return the number of microseconds that have elapsed since application start
virtual TimeUs GetElapsedTimeUs() const = 0;
AZ_DISABLE_COPY_MOVE(ITime);
};
@@ -51,6 +67,53 @@ namespace AZ
{
return AZ::Interface<ITime>::Get()->GetElapsedTimeMs();
}
}
//! This is a simple convenience wrapper
inline TimeUs GetElapsedTimeUs()
{
return AZ::Interface<ITime>::Get()->GetElapsedTimeUs();
}
//! Converts from milliseconds to microseconds
inline TimeUs TimeMsToUs(TimeMs value)
{
return static_cast<TimeUs>(value * static_cast<TimeMs>(1000));
}
//! Converts from microseconds to milliseconds
inline TimeMs TimeUsToMs(TimeUs value)
{
return static_cast<TimeMs>(value / static_cast<TimeUs>(1000));
}
//! Converts from milliseconds to seconds
inline float TimeMsToSeconds(TimeMs value)
{
return static_cast<float>(value) / 1000.0f;
}
//! Converts from microseconds to seconds
inline float TimeUsToSeconds(TimeUs value)
{
return static_cast<float>(value) / 1000000.0f;
}
//! Converts from milliseconds to AZStd::chrono::time_point
inline auto TimeMsToChrono(TimeMs value)
{
auto epoch = AZStd::chrono::time_point<AZStd::chrono::high_resolution_clock>();
auto chronoValue = AZStd::chrono::milliseconds(aznumeric_cast<int64_t>(value));
return epoch + chronoValue;
}
//! Converts from microseconds to AZStd::chrono::time_point
inline auto TimeUsToChrono(TimeUs value)
{
auto epoch = AZStd::chrono::time_point<AZStd::chrono::high_resolution_clock>();
auto chronoValue = AZStd::chrono::microseconds(aznumeric_cast<int64_t>(value));
return epoch + chronoValue;
}
} // namespace AZ
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AZ::TimeMs);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AZ::TimeUs);
@@ -35,7 +35,7 @@ namespace AZ
TimeSystemComponent::TimeSystemComponent()
{
m_lastInvokedTimeMs = static_cast<TimeMs>(AZStd::GetTimeNowMicroSecond() / 1000);
m_lastInvokedTimeUs = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
AZ::Interface<ITime>::Register(this);
ITimeRequestBus::Handler::BusConnect();
}
@@ -58,18 +58,23 @@ namespace AZ
TimeMs TimeSystemComponent::GetElapsedTimeMs() const
{
TimeMs currentTime = static_cast<TimeMs>(AZStd::GetTimeNowMicroSecond() / 1000);
TimeMs deltaTime = currentTime - m_lastInvokedTimeMs;
return TimeUsToMs(GetElapsedTimeUs());
}
TimeUs TimeSystemComponent::GetElapsedTimeUs() const
{
TimeUs currentTime = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
TimeUs deltaTime = currentTime - m_lastInvokedTimeUs;
if (t_scale != 1.0f)
{
float floatDelta = static_cast<float>(deltaTime) * t_scale;
deltaTime = static_cast<TimeMs>(static_cast<int64_t>(floatDelta));
deltaTime = static_cast<TimeUs>(static_cast<int64_t>(floatDelta));
}
m_accumulatedTimeMs += deltaTime;
m_lastInvokedTimeMs = currentTime;
m_accumulatedTimeUs += deltaTime;
m_lastInvokedTimeUs = currentTime;
return m_accumulatedTimeMs;
return m_accumulatedTimeUs;
}
}
@@ -39,11 +39,12 @@ namespace AZ
//! ITime overrides.
//! @{
TimeMs GetElapsedTimeMs() const override;
TimeUs GetElapsedTimeUs() const override;
//! @}
private:
mutable TimeMs m_lastInvokedTimeMs = TimeMs{0};
mutable TimeMs m_accumulatedTimeMs = TimeMs{0};
mutable TimeUs m_lastInvokedTimeUs = TimeUs{0};
mutable TimeUs m_accumulatedTimeUs = TimeUs{0};
};
}
@@ -0,0 +1,56 @@
/*
* 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 <AzCore/Time/TimeSystemComponent.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
class TimeTests
: public AllocatorsFixture
{
public:
void SetUp() override
{
SetupAllocator();
m_timeComponent = new AZ::TimeSystemComponent;
}
void TearDown() override
{
delete m_timeComponent;
TeardownAllocator();
}
AZ::TimeSystemComponent* m_timeComponent = nullptr;
};
TEST_F(TimeTests, TestConversionUsToMs)
{
AZ::TimeUs timeUs = AZ::TimeUs{ 1000 };
AZ::TimeMs timeMs = AZ::TimeUsToMs(timeUs);
EXPECT_EQ(timeMs, AZ::TimeMs{ 1 });
}
TEST_F(TimeTests, TestConversionMsToUs)
{
AZ::TimeMs timeMs = AZ::TimeMs{ 1000 };
AZ::TimeUs timeUs = AZ::TimeMsToUs(timeMs);
EXPECT_EQ(timeUs, AZ::TimeUs{ 1000000 });
}
TEST_F(TimeTests, TestClocks)
{
AZ::TimeUs timeUs = AZ::GetElapsedTimeUs();
AZ::TimeMs timeMs = AZ::GetElapsedTimeMs();
AZ::TimeMs timeUsToMs = AZ::TimeUsToMs(timeUs);
int64_t delta = static_cast<int64_t>(timeMs) - static_cast<int64_t>(timeUsToMs);
EXPECT_LT(abs(delta), 1);
}
}
@@ -127,6 +127,7 @@ set(FILES
Serialization/Json/UnorderedSetSerializerTests.cpp
Serialization/Json/UnsupportedTypesSerializerTests.cpp
Serialization/Json/UuidSerializerTests.cpp
Time/TimeTests.cpp
Math/AabbTests.cpp
Math/ColorTests.cpp
Math/CrcTests.cpp
@@ -28,9 +28,12 @@ namespace AzFramework
//! @note This is used to drive event driven updates to the visibility system.
virtual void RefreshEntityLocalBoundsUnion(AZ::EntityId entityId) = 0;
//! Returns the cached union of all component Aabbs.
//! Returns the cached union of all component Aabbs in local entity space.
virtual AZ::Aabb GetEntityLocalBoundsUnion(AZ::EntityId entityId) const = 0;
//! Returns the cached union of all component Aabbs in world space.
virtual AZ::Aabb GetEntityWorldBoundsUnion(AZ::EntityId entityId) const = 0;
//! Writes the current changes made to all entities (transforms and bounds) to the visibility system.
//! @note During normal operation this is called every frame in OnTick but can
//! also be called explicitly (e.g. For testing purposes).
@@ -128,6 +128,24 @@ namespace AzFramework
return AZ::Aabb::CreateNull();
}
AZ::Aabb EntityVisibilityBoundsUnionSystem::GetEntityWorldBoundsUnion(const AZ::EntityId entityId) const
{
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
if (entity != nullptr)
{
// if the entity is not found in the mapping then return a null Aabb, this is to mimic
// as closely as possible the behavior of an individual GetLocalBounds call to an Entity that
// had been deleted (there would be no response, leaving the default value assigned)
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
{
return instance_it->second.m_localEntityBoundsUnion.GetTranslated(entity->GetTransform()->GetWorldTranslation());
}
}
return AZ::Aabb::CreateNull();
}
void EntityVisibilityBoundsUnionSystem::ProcessEntityBoundsUnionRequests()
{
AZ_PROFILE_FUNCTION(AzFramework);
@@ -31,6 +31,7 @@ namespace AzFramework
// EntityBoundsUnionRequestBus overrides ...
void RefreshEntityLocalBoundsUnion(AZ::EntityId entityId) override;
AZ::Aabb GetEntityLocalBoundsUnion(AZ::EntityId entityId) const override;
AZ::Aabb GetEntityWorldBoundsUnion(AZ::EntityId entityId) const override;
void ProcessEntityBoundsUnionRequests() override;
void OnTransformUpdated(AZ::Entity* entity) override;
@@ -94,6 +94,7 @@ namespace AzNetworking
class ITimeoutHandler
{
public:
virtual ~ITimeoutHandler() = default;
//! Handler callback for timed out items.
//! @param item containing registered timeout details
@@ -103,13 +103,13 @@ namespace AzNetworking
//! @return boolean true on success
virtual bool Disconnect(ConnectionId connectionId, DisconnectReason reason) = 0;
//! Sets whether this connection interface can disconnect by virtue of a timeout
//! @param timeoutEnabled If this connection interface will automatically disconnect due to a timeout
virtual void SetTimeoutEnabled(bool timeoutEnabled) = 0;
//! Sets the timeout time in milliseconds, 0 ms means timeouts are disabled.
//! @param timeoutMs the number of milliseconds with no traffic before we timeout and close a connection
virtual void SetTimeoutMs(AZ::TimeMs timeoutMs) = 0;
//! Whether this connection interface will disconnect by virtue of a time out (does not account for cvars affecting all connections)
//! @return boolean true if this connection will not disconnect on timeout (does not account for cvars affecting all connections)
virtual bool IsTimeoutEnabled() const = 0;
//! Retrieves the timeout time in milliseconds for this network interface, 0 ms means timeouts are disabled.
//! @return the timeout time in milliseconds for this network interface, 0 ms means timeouts are disabled
virtual AZ::TimeMs GetTimeoutMs() const = 0;
//! Const access to the metrics tracked by this network interface.
//! @return const reference to the metrics tracked by this network interface
@@ -321,4 +321,19 @@ namespace AzNetworking
return serializer.IsValid();
}
};
template <>
struct SerializeObjectHelper<AZ::Aabb>
{
static bool SerializeObject(ISerializer& serializer, AZ::Aabb& value)
{
AZ::Vector3 minValue = value.GetMin();
AZ::Vector3 maxValue = value.GetMax();
serializer.Serialize(minValue, "minValue");
serializer.Serialize(maxValue, "maxValue");
value.SetMin(minValue);
value.SetMax(maxValue);
return serializer.IsValid();
}
};
}
@@ -22,14 +22,15 @@ namespace AzNetworking
#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_TcpHearthbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Tcp connection heartbeat frequency");
AZ_CVAR(AZ::TimeMs, net_TcpTimeoutTimeMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Tcp connection");
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)
{
;
}
@@ -97,7 +98,7 @@ 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_TcpHearthbeatTimeMs);
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());
@@ -174,14 +175,14 @@ namespace AzNetworking
return connection->Disconnect(reason, TerminationEndpoint::Local);
}
void TcpNetworkInterface::SetTimeoutEnabled(bool timeoutEnabled)
void TcpNetworkInterface::SetTimeoutMs(AZ::TimeMs timeoutMs)
{
m_timeoutEnabled = timeoutEnabled;
m_timeoutMs = timeoutMs;
}
bool TcpNetworkInterface::IsTimeoutEnabled() const
AZ::TimeMs TcpNetworkInterface::GetTimeoutMs() const
{
return m_timeoutEnabled;
return m_timeoutMs;
}
void TcpNetworkInterface::QueueNewConnection(const PendingConnection& pendingConnection)
@@ -257,7 +258,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()), net_TcpTimeoutTimeMs);
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);
AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Acceptor, "Invalid role for connection");
GetConnectionListener().OnConnect(connection.get());
@@ -316,7 +317,7 @@ namespace AzNetworking
{
tcpConnection->SendReliablePacket(CorePackets::HeartbeatPacket());
}
else if (net_TcpTimeoutConnections && m_networkInterface.IsTimeoutEnabled())
else if (net_TcpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
{
tcpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;
@@ -99,8 +99,8 @@ namespace AzNetworking
bool WasPacketAcked(ConnectionId connectionId, PacketId packetId) override;
bool StopListening() override;
bool Disconnect(ConnectionId connectionId, DisconnectReason reason) override;
void SetTimeoutEnabled(bool timeoutEnabled) override;
bool IsTimeoutEnabled() const override;
void SetTimeoutMs(AZ::TimeMs timeoutMs) override;
AZ::TimeMs GetTimeoutMs() const override;
//! @}
//! Queues a new incoming connection for this network interface.
@@ -156,7 +156,7 @@ namespace AzNetworking
AZ::Name m_name;
TrustZone m_trustZone;
uint16_t m_port = 0;
bool m_timeoutEnabled = true;
AZ::TimeMs m_timeoutMs = AZ::TimeMs{ 0 };
IConnectionListener& m_connectionListener;
TcpConnectionSet m_connectionSet;
TcpSocketManager m_tcpSocketManager;
@@ -53,18 +53,11 @@ namespace AzNetworking
{
Close();
if (!SocketCreateInternal())
{
return false;
}
if (!BindSocketForListenInternal(port))
{
return false;
}
if (!(SetSocketNonBlocking(m_socketFd) && SetSocketNoDelay(m_socketFd)))
if (!SocketCreateInternal()
|| !BindSocketForListenInternal(port)
|| !(SetSocketNonBlocking(m_socketFd) && SetSocketNoDelay(m_socketFd)))
{
Close();
return false;
}
@@ -75,18 +68,11 @@ namespace AzNetworking
{
Close();
if (!SocketCreateInternal())
{
return false;
}
if (!BindSocketForConnectInternal(address))
{
return false;
}
if (!(SetSocketNonBlocking(m_socketFd) && SetSocketNoDelay(m_socketFd)))
if (!SocketCreateInternal()
|| !BindSocketForConnectInternal(address)
|| !(SetSocketNonBlocking(m_socketFd) && SetSocketNoDelay(m_socketFd)))
{
Close();
return false;
}
@@ -31,8 +31,8 @@ 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_UdpHearthbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency");
AZ_CVAR(AZ::TimeMs, net_UdpTimeoutTimeMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection");
AZ_CVAR(AZ::TimeMs, net_UdpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency");
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");
AZ_CVAR(float, net_RttFudgeScalar, 2.0f, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Scalar value to multiply computed Rtt by to determine an optimal packet timeout threshold");
@@ -61,6 +61,7 @@ namespace AzNetworking
, m_connectionListener(connectionListener)
, m_socket(net_UdpUseEncryption ? new DtlsSocket() : new UdpSocket())
, m_readerThread(readerThread)
, m_timeoutMs(net_UdpDefaultTimeoutMs)
{
const AZ::CVarFixedString compressor = static_cast<AZ::CVarFixedString>(net_UdpCompressor);
const AZ::Name compressorName = AZ::Name(compressor);
@@ -138,7 +139,7 @@ namespace AzNetworking
}
const ConnectionId connectionId = m_connectionSet.GetNextConnectionId();
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), net_UdpHearthbeatTimeMs);
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), m_timeoutMs);
AZStd::unique_ptr<UdpConnection> connection = AZStd::make_unique<UdpConnection>(connectionId, remoteAddress, *this, ConnectionRole::Connector);
UdpPacketEncodingBuffer dtlsData;
@@ -403,14 +404,14 @@ namespace AzNetworking
return connection->Disconnect(reason, TerminationEndpoint::Local);
}
void UdpNetworkInterface::SetTimeoutEnabled(bool timeoutEnabled)
void UdpNetworkInterface::SetTimeoutMs(AZ::TimeMs timeoutMs)
{
m_timeoutEnabled = timeoutEnabled;
m_timeoutMs = timeoutMs;
}
bool UdpNetworkInterface::IsTimeoutEnabled() const
AZ::TimeMs UdpNetworkInterface::GetTimeoutMs() const
{
return m_timeoutEnabled;
return m_timeoutMs;
}
bool UdpNetworkInterface::IsEncrypted() const
@@ -681,7 +682,7 @@ namespace AzNetworking
// How long should we sit in the timeout queue before heartbeating or disconnecting
const ConnectionId connectionId = m_connectionSet.GetNextConnectionId();
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), net_UdpTimeoutTimeMs);
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), m_timeoutMs);
AZLOG(Debug_UdpConnect, "Accepted new Udp Connection");
AZStd::unique_ptr<UdpConnection> connection = AZStd::make_unique<UdpConnection>(connectionId, connectPacket.m_address, *this, ConnectionRole::Acceptor);
@@ -745,7 +746,7 @@ namespace AzNetworking
{
udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket());
}
else if (net_UdpTimeoutConnections && m_networkInterface.IsTimeoutEnabled())
else if (net_UdpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
{
udpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;
@@ -104,8 +104,8 @@ namespace AzNetworking
bool WasPacketAcked(ConnectionId connectionId, PacketId packetId) override;
bool StopListening() override;
bool Disconnect(ConnectionId connectionId, DisconnectReason reason) override;
void SetTimeoutEnabled(bool timeoutEnabled) override;
bool IsTimeoutEnabled() const override;
void SetTimeoutMs(AZ::TimeMs timeoutMs) override;
AZ::TimeMs GetTimeoutMs() const override;
//! @}
//! Returns true if this is an encrypted socket, false if not.
@@ -181,7 +181,7 @@ namespace AzNetworking
TrustZone m_trustZone;
uint16_t m_port = 0;
bool m_allowIncomingConnections = false;
bool m_timeoutEnabled = true;
AZ::TimeMs m_timeoutMs = AZ::TimeMs{ 0 };
IConnectionListener& m_connectionListener;
UdpConnectionSet m_connectionSet;
TimeoutQueue m_connectionTimeoutQueue;
@@ -79,17 +79,15 @@ namespace AzNetworking
{
const int32_t error = GetLastNetworkError();
AZLOG_ERROR("Failed to bind UDP socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error));
Close();
return false;
}
}
if (!SetSocketBufferSizes(m_socketFd, net_UdpSendBufferSize, net_UdpRecvBufferSize))
{
return false;
}
if (!SetSocketNonBlocking(m_socketFd))
if (!SetSocketBufferSizes(m_socketFd, net_UdpSendBufferSize, net_UdpRecvBufferSize)
|| !SetSocketNonBlocking(m_socketFd))
{
Close();
return false;
}
@@ -149,8 +149,9 @@ namespace UnitTest
EXPECT_EQ(testServer.m_serverNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
EXPECT_EQ(testClient.m_clientNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
testClient.m_clientNetworkInterface->SetTimeoutEnabled(true);
EXPECT_TRUE(testClient.m_clientNetworkInterface->IsTimeoutEnabled());
const AZ::TimeMs timeoutMs = AZ::TimeMs{ 100 };
testClient.m_clientNetworkInterface->SetTimeoutMs(timeoutMs);
EXPECT_EQ(testClient.m_clientNetworkInterface->GetTimeoutMs(), timeoutMs);
EXPECT_TRUE(testServer.m_serverNetworkInterface->StopListening());
}
@@ -279,8 +279,9 @@ namespace UnitTest
EXPECT_EQ(testServer.m_serverNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
EXPECT_EQ(testClient.m_clientNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
testClient.m_clientNetworkInterface->SetTimeoutEnabled(true);
EXPECT_TRUE(testClient.m_clientNetworkInterface->IsTimeoutEnabled());
const AZ::TimeMs timeoutMs = AZ::TimeMs{ 100 };
testClient.m_clientNetworkInterface->SetTimeoutMs(timeoutMs);
EXPECT_EQ(testClient.m_clientNetworkInterface->GetTimeoutMs(), timeoutMs);
EXPECT_FALSE(dynamic_cast<UdpNetworkInterface*>(testClient.m_clientNetworkInterface)->IsEncrypted());
@@ -403,6 +403,8 @@ namespace {{ Component.attrib['Namespace'] }}
: public Multiplayer::MultiplayerController
{
public:
using ComponentType = {{ ComponentName }};
{{ ControllerBaseName }}({{ ComponentName }}& owner);
~{{ ControllerBaseName }}() override = default;
@@ -83,8 +83,8 @@ namespace Multiplayer
AZ::ScheduledEvent m_autonomousUpdateEvent; // Drives autonomous input collection
AZ::ScheduledEvent m_updateBankedTimeEvent; // Drives authority bank time updates
EntityMigrationStartEvent::Handler m_migrateStartHandler;
EntityMigrationEndEvent::Handler m_migrateEndHandler;
ClientMigrationStartEvent::Handler m_migrateStartHandler;
ClientMigrationEndEvent::Handler m_migrateEndHandler;
double m_moveAccumulator = 0.0;
double m_clientBankedTime = 0.0;
@@ -67,6 +67,9 @@ namespace Multiplayer
//! @return reference to the requested component data, an empty container will be returned if the NetComponentId does not exist
const ComponentData& GetMultiplayerComponentData(NetComponentId netComponentId) const;
//! This releases all owned memory, should only be called during multiplayer shutdown.
void Reset();
private:
NetComponentId m_nextNetComponentId = NetComponentId{ 0 };
AZStd::unordered_map<NetComponentId, ComponentData> m_componentData;
@@ -32,9 +32,7 @@ namespace Multiplayer
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using EntityDirtiedEvent = AZ::Event<>;
using EntitySyncRewindEvent = AZ::Event<>;
using EntityMigrationStartEvent = AZ::Event<ClientInputId>;
using EntityMigrationEndEvent = AZ::Event<>;
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&, AzNetworking::ConnectionId>;
using EntityPreRenderEvent = AZ::Event<float>;
using EntityCorrectionEvent = AZ::Event<>;
@@ -115,17 +113,13 @@ namespace Multiplayer
void MarkDirty();
void NotifyLocalChanges();
void NotifySyncRewindState();
void NotifyMigrationStart(ClientInputId migratedInputId);
void NotifyMigrationEnd();
void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId);
void NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId);
void NotifyPreRender(float deltaTime);
void NotifyCorrection();
void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler);
void AddEntityDirtiedEventHandler(EntityDirtiedEvent::Handler& eventHandler);
void AddEntitySyncRewindEventHandler(EntitySyncRewindEvent::Handler& eventHandler);
void AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler);
void AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler);
void AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler);
void AddEntityPreRenderEventHandler(EntityPreRenderEvent::Handler& eventHandler);
void AddEntityCorrectionEventHandler(EntityCorrectionEvent::Handler& handler);
@@ -174,8 +168,6 @@ namespace Multiplayer
EntityStopEvent m_entityStopEvent;
EntityDirtiedEvent m_dirtiedEvent;
EntitySyncRewindEvent m_syncRewindEvent;
EntityMigrationStartEvent m_entityMigrationStartEvent;
EntityMigrationEndEvent m_entityMigrationEndEvent;
EntityServerMigrationEvent m_entityServerMigrationEvent;
EntityPreRenderEvent m_entityPreRenderEvent;
EntityCorrectionEvent m_entityCorrectionEvent;
@@ -33,7 +33,6 @@ namespace Multiplayer
};
typedef AZ::EBus<NetworkCharacterRequests> NetworkCharacterRequestBus;
//! NetworkCharacterComponent
//! Provides multiplayer support for game-play player characters.
@@ -47,20 +46,11 @@ namespace Multiplayer
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkCharacterComponent, s_networkCharacterComponentConcreteUuid, Multiplayer::NetworkCharacterComponentBase)
static void Reflect(AZ::ReflectContext* context);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
NetworkCharacterComponent();
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
NetworkCharacterComponentBase::GetRequiredServices(required);
required.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
}
// AZ::Component
void OnInit() override {}
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.h>
@@ -60,10 +61,11 @@ namespace Multiplayer
{
public:
NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent);
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void HandleSendApplyImpulse(AzNetworking::IConnection* invokingConnection, const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) override;
private:
void OnTransformUpdate();
AZ::TransformChangedEvent::Handler m_transformChangedHandler;
};
} // namespace Multiplayer
@@ -40,8 +40,7 @@ namespace Multiplayer
virtual EntityReplicationManager& GetReplicationManager() = 0;
//! Creates and manages sending updates to the remote endpoint.
//! @param hostTimeMs current server game time in milliseconds
virtual void Update(AZ::TimeMs hostTimeMs) = 0;
virtual void Update() = 0;
//! Returns whether update messages can be sent to the connection.
//! @return true if update messages can be sent
@@ -21,6 +21,14 @@ namespace Multiplayer
virtual ~IEntityDomain() = default;
//! For domains that operate on a region of space, this sets the area the domain is responsible for.
//! @param aabb the aabb associated with this entity domain
virtual void SetAabb(const AZ::Aabb& aabb) = 0;
//! Retrieves the aabb representing the domain area, an invalid aabb will be returned for non-spatial domains.
//! @return the aabb associated with this entity domain
virtual const AZ::Aabb& GetAabb() const = 0;
//! Returns whether or not an entity should be owned by an entity manager.
//! @param entityHandle the handle of the netbound entity to check
//! @return false if this entity should not belong to the entity manger, true if it could be owned by the entity manager
@@ -31,8 +39,7 @@ namespace Multiplayer
virtual void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) = 0;
//! Return the set of netbound entities not included in this domain.
//! @param outEntitiesNotInDomain the set of known networked entities not included in this domain
virtual void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const = 0;
virtual const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const = 0;
//! Debug draw to visualize host entity domains.
virtual void DebugDraw() const = 0;
@@ -42,7 +42,11 @@ namespace Multiplayer
AzNetworking::ByteBuffer<2048> m_userData;
};
using ClientMigrationStartEvent = AZ::Event<ClientInputId>;
using ClientMigrationEndEvent = AZ::Event<>;
using ClientDisconnectedEvent = AZ::Event<>;
using NotifyClientMigrationEvent = AZ::Event<const HostId&, uint64_t, ClientInputId>;
using NotifyEntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
using SessionShutdownEvent = AZ::Event<AzNetworking::INetworkInterface*>;
@@ -78,26 +82,42 @@ namespace Multiplayer
//! @param state The state of this connection
virtual void InitializeMultiplayer(MultiplayerAgentType state) = 0;
//! Starts hosting a server
//! Starts hosting a server.
//! @param port The port to listen for connection on
//! @param isDedicated Whether the server is dedicated or client hosted
//! @return if the application successfully started hosting
virtual bool StartHosting(uint16_t port, bool isDedicated = true) = 0;
//! Connects to the specified IP as a Client
//! Connects to the specified IP as a Client.
//! @param remoteAddress The domain or IP to connect to
//! @param port The port to connect to
//! @result if a connection was successfully created
virtual bool Connect(AZStd::string remoteAddress, uint16_t port) = 0;
virtual bool Connect(const AZStd::string& remoteAddress, uint16_t port) = 0;
// Disconnects all multiplayer connections, stops listening on the server and invokes handlers appropriate to network context
// Disconnects all multiplayer connections, stops listening on the server and invokes handlers appropriate to network context.
//! @param reason The reason for terminating connections
virtual void Terminate(AzNetworking::DisconnectReason reason) = 0;
//! Adds a ClientDisconnectedEvent Handler which is invoked on the client when a disconnection occurs
//! Adds a ClientMigrationStartEvent Handler which is invoked at the start of a client migration.
//! @param handler The ClientMigrationStartEvent Handler to add
virtual void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) = 0;
//! Adds a ClientMigrationEndEvent Handler which is invoked when a client completes migration.
//! @param handler The ClientMigrationEndEvent Handler to add
virtual void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) = 0;
//! Adds a ClientDisconnectedEvent Handler which is invoked on the client when a disconnection occurs.
//! @param handler The ClientDisconnectedEvent Handler to add
virtual void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) = 0;
//! Adds a NotifyClientMigrationEvent Handler which is invoked when a client migrates from one host to another.
//! @param handler The NotifyClientMigrationEvent Handler to add
virtual void AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler) = 0;
//! Adds a NotifyEntityMigrationEvent Handler which is invoked when an entity migrates from one host to another.
//! @param handler The NotifyEntityMigrationEvent Handler to add
virtual void AddNotifyEntityMigrationEventHandler(NotifyEntityMigrationEvent::Handler& handler) = 0;
//! Adds a ConnectionAcquiredEvent Handler which is invoked when a new endpoint connects to the session.
//! @param handler The ConnectionAcquiredEvent Handler to add
virtual void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) = 0;
@@ -110,7 +130,18 @@ namespace Multiplayer
//! @param handler The SessionShutdownEvent handler to add
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
//! Sends a packet telling if entity update messages can be sent
//! Signals a NotifyClientMigrationEvent with the provided parameters.
//! @param hostId the host id of the host the client is migrating to
//! @param userIdentifier the user identifier the client will provide the new host to validate identity
//! @param lastClientInputId the last processed clientInputId by the current host
virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) = 0;
//! Signals a NotifyEntityMigrationEvent with the provided parameters.
//! @param entityHandle the network entity handle of the entity being migrated
//! @param remoteHostId the host id of the host the entity is migrating to
virtual void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) = 0;
//! Sends a packet telling if entity update messages can be sent.
//! @param readyForEntityUpdates Ready for entity updates or not
virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0;
@@ -122,7 +153,7 @@ namespace Multiplayer
//! @return the current server time in milliseconds
virtual AZ::TimeMs GetCurrentHostTimeMs() const = 0;
//! Returns the current blend factor for client side interpolation
//! Returns the current blend factor for client side interpolation.
//! This value is only relevant on the client and is used to smooth between host frames
//! @return the current blend factor
virtual float GetCurrentBlendFactor() const = 0;
@@ -141,9 +172,21 @@ namespace Multiplayer
//! @param entityFilter non-owning pointer, the caller is responsible for memory management.
virtual void SetFilterEntityManager(IFilterEntityManager* entityFilter) = 0;
//! @return pointer to the user-defined filtering manager of entities. By default, this isn't set and returns nullptr.
//! Returns a pointer to the user-defined filtering manager of entities.
//! @return pointer to the filtered entity manager, or nullptr if not set
virtual IFilterEntityManager* GetFilterEntityManager() = 0;
//! Enables or disables automatic instantiation of netbound entities.
//! This setting is controlled by the networking layer and should not be touched
//! If enabled, netbound entities will instantiate as spawnables are loaded into the game world, generally true for the server
//! If disabled, netbound entities will only stream from a host, always true for a client
//! @param value boolean value controlling netbound entity instantiation behaviour
virtual void SetShouldSpawnNetworkEntities(bool value) = 0;
//! Retrieves the current network entity instantiation behaviour.
//! @return boolean true if netbound entities should be auto instantiated, false if not
virtual bool GetShouldSpawnNetworkEntities() const = 0;
//! Retrieve the stats object bound to this multiplayer instance.
//! @return the stats object bound to this multiplayer instance
MultiplayerStats& GetStats() { return m_stats; }
@@ -13,6 +13,7 @@
#include <AzCore/RTTI/TypeSafeIntegral.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
#include <AzNetworking/Utilities/IpAddress.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/ConnectionLayer/ConnectionEnums.h>
#include <AzNetworking/DataStructures/ByteBuffer.h>
@@ -25,8 +26,8 @@ namespace Multiplayer
//! The default blend factor for ScopedAlterTime
static constexpr float DefaultBlendFactor = 1.f;
AZ_TYPE_SAFE_INTEGRAL(HostId, uint32_t);
static constexpr HostId InvalidHostId = static_cast<HostId>(-1);
using HostId = AzNetworking::IpAddress;
static const HostId InvalidHostId = HostId();
AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint32_t);
static constexpr NetEntityId InvalidNetEntityId = static_cast<NetEntityId>(-1);
@@ -105,9 +106,11 @@ namespace Multiplayer
struct EntityMigrationMessage
{
NetEntityId m_entityId;
NetEntityId m_netEntityId;
PrefabEntityId m_prefabEntityId;
AzNetworking::PacketEncodingBuffer m_propertyUpdateData;
bool operator!=(const EntityMigrationMessage& rhs) const;
bool Serialize(AzNetworking::ISerializer& serializer);
};
inline PrefabEntityId::PrefabEntityId(AZ::Name name, uint32_t entityOffset)
@@ -133,9 +136,23 @@ namespace Multiplayer
serializer.Serialize(m_entityOffset, "entityOffset");
return serializer.IsValid();
}
inline bool EntityMigrationMessage::operator!=(const EntityMigrationMessage& rhs) const
{
return m_netEntityId != rhs.m_netEntityId
|| m_prefabEntityId != rhs.m_prefabEntityId
|| m_propertyUpdateData != rhs.m_propertyUpdateData;
}
inline bool EntityMigrationMessage::Serialize(AzNetworking::ISerializer& serializer)
{
serializer.Serialize(m_netEntityId, "netEntityId");
serializer.Serialize(m_prefabEntityId, "prefabEntityId");
serializer.Serialize(m_propertyUpdateData, "propertyUpdateData");
return serializer.IsValid();
}
}
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetEntityId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetComponentId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::PropertyIndex);
@@ -145,7 +162,6 @@ AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostFrameId);
namespace AZ
{
AZ_TYPE_INFO_SPECIALIZE(Multiplayer::HostId, "{D04B3363-8E1B-4193-8B2B-D2140389C9D5}");
AZ_TYPE_INFO_SPECIALIZE(Multiplayer::NetEntityId, "{05E4C08B-3A1B-4390-8144-3767D8E56A81}");
AZ_TYPE_INFO_SPECIALIZE(Multiplayer::NetComponentId, "{8AF3B382-F187-4323-9014-B380638767E3}");
AZ_TYPE_INFO_SPECIALIZE(Multiplayer::PropertyIndex, "{F4460210-024D-4B3B-A10A-04B669C34230}");
@@ -8,7 +8,8 @@
#pragma once
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
@@ -35,7 +36,9 @@ namespace Multiplayer
{
class IEntityDomain;
class EntityReplicator;
using SendMigrateEntityEvent = AZ::Event<AzNetworking::IConnection&, const EntityMigrationMessage&>;
//! @class EntityReplicationManager
//! @brief Handles replication of relevant entities for one connection.
class EntityReplicationManager final
@@ -54,11 +57,11 @@ namespace Multiplayer
EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode);
~EntityReplicationManager() = default;
void SetRemoteHostId(HostId hostId);
HostId GetRemoteHostId() const;
void SetRemoteHostId(const HostId& hostId);
const HostId& GetRemoteHostId() const;
void ActivatePendingEntities();
void SendUpdates(AZ::TimeMs hostTimeMs);
void SendUpdates();
void Clear(bool forMigration);
bool SetEntityRebasing(NetworkEntityHandle& entityHandle);
@@ -69,8 +72,8 @@ namespace Multiplayer
bool HasRemoteAuthority(const ConstNetworkEntityHandle& entityHandle) const;
void SetEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain);
IEntityDomain* GetEntityDomain();
void SetRemoteEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain);
IEntityDomain* GetRemoteEntityDomain();
void SetReplicationWindow(AZStd::unique_ptr<IReplicationWindow> replicationWindow);
IReplicationWindow* GetReplicationWindow();
@@ -79,7 +82,8 @@ namespace Multiplayer
void AddDeferredRpcMessage(NetworkEntityRpcMessage& rpcMessage);
void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event<NetEntityId>::Handler& handler);
void AddAutonomousEntityReplicatorCreatedHandler(AZ::Event<NetEntityId>::Handler& handler);
void AddSendMigrateEntityEventHandler(SendMigrateEntityEvent::Handler& handler);
bool HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message);
bool HandleEntityDeleteMessage(EntityReplicator* entityReplicator, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
@@ -117,14 +121,12 @@ namespace Multiplayer
using EntityReplicatorList = AZStd::deque<EntityReplicator*>;
EntityReplicatorList GenerateEntityUpdateList();
void SendEntityUpdatesPacketHelper(AZ::TimeMs hostTimeMs, EntityReplicatorList& toSendList, uint32_t maxPayloadSize, AzNetworking::IConnection& connection);
void SendEntityUpdates(AZ::TimeMs hostTimeMs);
void SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable);
void SendEntityUpdateMessages(EntityReplicatorList& replicatorList);
void SendEntityRpcs(RpcMessages& rpcMessages, bool reliable);
void MigrateEntityInternal(NetEntityId entityId);
void OnEntityExitDomain(const ConstNetworkEntityHandle& entityHandle);
void OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, AzNetworking::ConnectionId connectionId);
void OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId);
EntityReplicator* AddEntityReplicator(const ConstNetworkEntityHandle& entityHandle, NetEntityRole netEntityRole);
@@ -153,7 +155,6 @@ namespace Multiplayer
{
public:
OrphanedEntityRpcs(EntityReplicationManager& replicationManager);
virtual ~OrphanedEntityRpcs() = default;
void Update();
bool DispatchOrphanedRpcs(EntityReplicator& entityReplicator);
void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityRpcMessage);
@@ -192,6 +193,8 @@ namespace Multiplayer
AZ::Event<NetEntityId> m_autonomousEntityReplicatorCreated;
EntityExitDomainEvent::Handler m_entityExitDomainEventHandler;
SendMigrateEntityEvent m_sendMigrateEntityEvent;
NotifyEntityMigrationEvent::Handler m_notifyEntityMigrationHandler;
AZ::ScheduledEvent m_clearRemovedReplicators;
AZ::ScheduledEvent m_updateWindow;
@@ -66,6 +66,7 @@ namespace Multiplayer
bool IsReadyToActivate() const;
NetworkEntityUpdateMessage GenerateUpdatePacket();
void FinalizeSerialization(AzNetworking::PacketId sentId);
AZ::TimeMs GetResendTimeoutTimeMs() const;
@@ -141,4 +142,4 @@ namespace Multiplayer
};
}
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.inl>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.inl>
@@ -21,6 +21,7 @@ namespace Multiplayer
class NetworkEntityAuthorityTracker;
class NetworkEntityRpcMessage;
class MultiplayerComponentRegistry;
class IEntityDomain;
using EntityExitDomainEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using ControllersActivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
@@ -38,6 +39,19 @@ namespace Multiplayer
virtual ~INetworkEntityManager() = default;
//! Configures the NetworkEntityManager to operate as an authoritative host.
//! @param hostId the hostId of this NetworkEntityManager
//! @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
virtual bool IsInitialized() const = 0;
//! Returns the entity domain associated with this network entity manager, this will be nullptr on clients.
//! @return boolean the entity domain for this network entity manager
virtual IEntityDomain* GetEntityDomain() const = 0;
//! Returns the NetworkEntityTracker for this INetworkEntityManager instance.
//! @return the NetworkEntityTracker for this INetworkEntityManager instance
virtual NetworkEntityTracker* GetNetworkEntityTracker() = 0;
@@ -52,7 +66,7 @@ namespace Multiplayer
//! Returns the HostId for this INetworkEntityManager instance.
//! @return the HostId for this INetworkEntityManager instance
virtual HostId GetHostId() const = 0;
virtual const HostId& GetHostId() const = 0;
//! Creates new entities of the given archetype
//! @param prefabEntryId the name of the spawnable to spawn
@@ -166,5 +180,8 @@ namespace Multiplayer
//! Handle a local rpc message.
//! @param entityRpcMessage the local rpc message to handle
virtual void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) = 0;
//! Visualization of network entity manager state.
virtual void DebugDraw() const = 0;
};
}
@@ -30,18 +30,8 @@ namespace Multiplayer
//! Constructs a ConstNetworkEntityHandle given an entity, an entity tracker
//! @param entity pointer to the entity to construct a ConstNetworkEntityHandle for
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* entityTracker);
//! Constructs a ConstNetworkEntityHandle given an entity, a networkEntityId, and an entity tracker
//! @param entity pointer to the entity to construct a ConstNetworkEntityHandle for
//! @param netEntityId the networkEntityId of the entity
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(AZ::Entity* entity, NetEntityId netEntityId, const NetworkEntityTracker* entityTracker);
//! Constructs a ConstNetworkEntityHandle given an entity, a networked entityId, an entity tracker, and a dirty version
//! @param netBindComponent pointer to the entities NetBindComponent
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(NetBindComponent* netBindComponent, const NetworkEntityTracker* entityTracker);
//! can optionally be null in which case the entity tracker will be looked up
ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* entityTracker = nullptr);
ConstNetworkEntityHandle(const ConstNetworkEntityHandle&) = default;
@@ -103,6 +103,7 @@ namespace Multiplayer
// Non-serialized RPC metadata
ReliabilityType m_isReliable = ReliabilityType::Reliable;
};
using NetworkEntityRpcVector = AZStd::fixed_vector<NetworkEntityRpcMessage, MaxAggregateRpcMessages>;
struct IRpcParamStruct
{
@@ -118,4 +118,5 @@ namespace Multiplayer
// This is to prevent blowing out stack memory if we declare an array of these EntityUpdateMessages
AZStd::unique_ptr<AzNetworking::PacketEncodingBuffer> m_data;
};
using NetworkEntityUpdateVector = AZStd::fixed_vector<NetworkEntityUpdateMessage, MaxAggregateEntityMessages>;
}
@@ -10,10 +10,14 @@
#include <Multiplayer/MultiplayerTypes.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <AzCore/std/containers/map.h>
namespace Multiplayer
{
class EntityReplicator;
struct EntityReplicationData
{
EntityReplicationData() = default;
@@ -21,6 +25,8 @@ namespace Multiplayer
float m_priority = 0.0f;
};
using ReplicationSet = AZStd::map<ConstNetworkEntityHandle, EntityReplicationData>;
using RpcMessages = AZStd::list<NetworkEntityRpcMessage>;
using EntityReplicatorList = AZStd::deque<EntityReplicator*>;
class IReplicationWindow
{
@@ -33,6 +39,8 @@ namespace Multiplayer
virtual uint32_t GetMaxProxyEntityReplicatorSendCount() const = 0;
virtual bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const = 0;
virtual void UpdateWindow() = 0;
virtual AzNetworking::PacketId SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) = 0;
virtual void SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) = 0;
virtual void DebugDraw() const = 0;
};
}
@@ -13,10 +13,9 @@
</Packet>
<Packet Name="Accept" HandshakePacket="true" Desc="Server accept packet">
<Member Type="Multiplayer::HostId" Name="hostId" Init="Multiplayer::InvalidHostId" />
<Member Type="Multiplayer::LongNetworkString" Name="map" />
</Packet>
<Packet Name="ReadyForEntityUpdates" Desc="Client confirming it is ready to receive entity updates">
<Member Type="bool" Name="readyForEntityUpdates" />
</Packet>
@@ -32,16 +31,16 @@
<Packet Name="EntityUpdates" Desc="A packet that contains multiple entity updates">
<Member Type="AZ::TimeMs" Name="hostTimeMs" Init="AZ::TimeMs{ 0 }" />
<Member Type="Multiplayer::HostFrameId" Name="hostFrameId" Init="Multiplayer::InvalidHostFrameId" />
<Member Type="Multiplayer::NetworkEntityUpdateMessage" Name="entityMessages" Container="Vector" Count="Multiplayer::MaxAggregateEntityMessages" />
<Member Type="Multiplayer::NetworkEntityUpdateVector" Name="entityMessages" />
</Packet>
<Packet Name="EntityRpcs" Desc="A packet that contains multiple entity rpcs">
<Member Type="Multiplayer::NetworkEntityRpcMessage" Name="entityRpcs" Container="Vector" Count="Multiplayer::MaxAggregateRpcMessages" />
<Member Type="Multiplayer::NetworkEntityRpcVector" Name="entityRpcs" />
</Packet>
<Packet Name="ClientMigration" Desc="Tell a client to migrate to a new server">
<Member Type="uint64_t" Name="temporaryUserIdentifier" Init="0" />
<Member Type="AzNetworking::IpAddress" Name="remoteServerAddress" Init="AzNetworking::IpAddress()" />
<Member Type="AZ::TimeMs" Name="lastInputGameTimeMs" Init="AZ::TimeMs{ 0 }" />
<Member Type="uint64_t" Name="temporaryUserIdentifier" Init="0" />
<Member Type="Multiplayer::ClientInputId" Name="lastClientInputId" Init="Multiplayer::ClientInputId{ 0 }" />
</Packet>
</PacketGroup>
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<Component
Name="NetworkConnectionComponent"
Namespace="Multiplayer"
OverrideComponent="false"
OverrideController="false"
OverrideInclude=""
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NetworkProperty Type="uint64_t" Name="UserIdentifier" Init="0" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
<NetworkProperty Type="HostId" Name="MigrationHostId" Init="InvalidHostId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
<NetworkProperty Type="NetEntityId" Name="ControlledEntity" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
</Component>
@@ -10,9 +10,11 @@
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
<NetworkProperty Type="AZ::Vector3" Name="LinearVelocity" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
<NetworkProperty Type="AZ::Vector3" Name="AngularVelocity" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
<RemoteProcedure Name="SendApplyImpulse" InvokeFrom="Server" HandleOn="Authority" IsPublic="true" IsReliable="true" GenerateEventBindings="false" Description="Applies an impulse">
<Param Type="AZ::Vector3" Name="impulse" />
<Param Type="AZ::Vector3" Name="worldPoint" />
</RemoteProcedure>
</Component>
@@ -123,8 +123,8 @@ namespace Multiplayer
if (IsAutonomous())
{
m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true);
GetParent().GetNetBindComponent()->AddEntityMigrationStartEventHandler(m_migrateStartHandler);
GetParent().GetNetBindComponent()->AddEntityMigrationEndEventHandler(m_migrateEndHandler);
GetMultiplayer()->AddClientMigrationStartEventHandler(m_migrateStartHandler);
GetMultiplayer()->AddClientMigrationEndEventHandler(m_migrateEndHandler);
}
}
@@ -57,4 +57,9 @@ namespace Multiplayer
}
return nullComponentData;
}
void MultiplayerComponentRegistry::Reset()
{
m_componentData.clear();
}
}
@@ -13,6 +13,7 @@
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
@@ -59,7 +60,7 @@ namespace Multiplayer
return false;
}
NetBindComponent* netBindComponent = entity-> FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (!netBindComponent)
{
AZ_Warning( "NetBindComponent", false, "NetBindComponent IsNetEntityRoleAuthority failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
@@ -76,7 +77,7 @@ namespace Multiplayer
return false;
}
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (!netBindComponent)
{
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleAutonomous failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
@@ -93,7 +94,7 @@ namespace Multiplayer
return false;
}
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (!netBindComponent)
{
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleClient failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
@@ -110,7 +111,7 @@ namespace Multiplayer
return false;
}
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (!netBindComponent)
{
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleServer failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
@@ -172,6 +173,8 @@ namespace Multiplayer
{
GetNetworkEntityManager()->NotifyControllersDeactivated(m_netEntityHandle, EntityIsMigrating::False);
}
GetNetworkEntityTracker()->UnregisterNetBindComponent(this);
}
NetEntityRole NetBindComponent::GetNetEntityRole() const
@@ -391,17 +394,7 @@ namespace Multiplayer
m_syncRewindEvent.Signal();
}
void NetBindComponent::NotifyMigrationStart(ClientInputId migratedInputId)
{
m_entityMigrationStartEvent.Signal(migratedInputId);
}
void NetBindComponent::NotifyMigrationEnd()
{
m_entityMigrationEndEvent.Signal();
}
void NetBindComponent::NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId)
void NetBindComponent::NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId)
{
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId);
}
@@ -431,16 +424,6 @@ namespace Multiplayer
eventHandler.Connect(m_syncRewindEvent);
}
void NetBindComponent::AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityMigrationStartEvent);
}
void NetBindComponent::AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityMigrationEndEvent);
}
void NetBindComponent::AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityServerMigrationEvent);
@@ -523,6 +506,9 @@ namespace Multiplayer
m_netEntityId = netEntityId;
m_netEntityRole = netEntityRole;
m_prefabEntityId = prefabEntityId;
GetNetworkEntityTracker()->RegisterNetBindComponent(entity, this);
m_netEntityHandle = GetNetworkEntityManager()->AddEntityToEntityMap(m_netEntityId, entity);
for (AZ::Component* component : entity->GetComponents())
@@ -96,6 +96,17 @@ namespace Multiplayer
NetworkCharacterComponentController::Reflect(context);
}
void NetworkCharacterComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
NetworkCharacterComponentBase::GetRequiredServices(required);
required.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
}
void NetworkCharacterComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
}
NetworkCharacterComponent::NetworkCharacterComponent()
: m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); })
{
@@ -196,7 +207,7 @@ namespace Multiplayer
// Ensure any entities that we might interact with are properly synchronized to their rewind state
if (IsAuthority())
{
const AZ::Aabb entityStartBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityLocalBoundsUnion(GetEntity()->GetId());
const AZ::Aabb entityStartBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(GetEntity()->GetId());
const AZ::Aabb entityFinalBounds = entityStartBounds.GetTranslated(velocity);
AZ::Aabb entitySweptBounds = entityStartBounds;
entitySweptBounds.AddAabb(entityFinalBounds);
@@ -57,16 +57,13 @@ namespace Multiplayer
NetworkRigidBodyRequestBus::Handler::BusConnect(GetEntityId());
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
GetEntity()->FindComponent<AzFramework::TransformComponent>()->BindTransformChangedEventHandler(m_transformChangedHandler);
GetEntity()->GetTransform()->BindTransformChangedEventHandler(m_transformChangedHandler);
m_physicsRigidBodyComponent =
Physics::RigidBodyRequestBus::FindFirstHandler(GetEntity()->GetId());
AZ_Assert(m_physicsRigidBodyComponent, "PhysX Rigid Body Component is required on entity %s", GetEntity()->GetName().c_str());
if (!HasController())
{
m_physicsRigidBodyComponent->SetKinematic(true);
}
// By default we're kinematic, activating a controller will allow us to simulate
m_physicsRigidBodyComponent->SetKinematic(true);
}
void NetworkRigidBodyComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
@@ -121,18 +118,26 @@ namespace Multiplayer
NetworkRigidBodyComponentController::NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent)
: NetworkRigidBodyComponentControllerBase(parent)
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform&) { OnTransformUpdate(); })
{
;
}
void NetworkRigidBodyComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
GetParent().m_physicsRigidBodyComponent->SetKinematic(false);
if (IsAuthority())
{
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
rigidBody->SetLinearVelocity(GetLinearVelocity());
rigidBody->SetAngularVelocity(GetAngularVelocity());
GetEntity()->GetTransform()->BindTransformChangedEventHandler(m_transformChangedHandler);
}
}
void NetworkRigidBodyComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
GetParent().m_physicsRigidBodyComponent->SetKinematic(true);
}
void NetworkRigidBodyComponentController::HandleSendApplyImpulse
@@ -145,4 +150,11 @@ namespace Multiplayer
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
rigidBody->ApplyLinearImpulseAtWorldPoint(impulse, worldPoint);
}
void NetworkRigidBodyComponentController::OnTransformUpdate()
{
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
SetLinearVelocity(rigidBody->GetLinearVelocity());
SetAngularVelocity(rigidBody->GetAngularVelocity());
}
} // namespace Multiplayer
@@ -13,6 +13,7 @@ namespace Multiplayer
// This can be used to help mitigate client side performance when large numbers of entities are created off the network
AZ_CVAR(uint32_t, cl_ClientMaxRemoteEntitiesPendingCreationCount, AZStd::numeric_limits<uint32_t>::max(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client");
AZ_CVAR(AZ::TimeMs, cl_ClientEntityReplicatorPendingRemovalTimeMs, AZ::TimeMs{ 10000 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "How long should wait prior to removing an entity for the client through a change in the replication window, entity deletes are still immediate");
AZ_CVAR(AZ::TimeMs, cl_DefaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
ClientToServerConnectionData::ClientToServerConnectionData
(
@@ -26,6 +27,7 @@ namespace Multiplayer
{
m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(cl_ClientMaxRemoteEntitiesPendingCreationCount);
m_entityReplicationManager.SetEntityPendingRemovalMs(cl_ClientEntityReplicatorPendingRemovalTimeMs);
m_entityReplicationManager.SetEntityActivationTimeSliceMs(cl_DefaultNetworkEntityActivationTimeSliceMs);
}
ClientToServerConnectionData::~ClientToServerConnectionData()
@@ -48,9 +50,9 @@ namespace Multiplayer
return m_entityReplicationManager;
}
void ClientToServerConnectionData::Update(AZ::TimeMs hostTimeMs)
void ClientToServerConnectionData::Update()
{
m_entityReplicationManager.ActivatePendingEntities();
m_entityReplicationManager.SendUpdates(hostTimeMs);
m_entityReplicationManager.SendUpdates();
}
}
@@ -9,7 +9,7 @@
#pragma once
#include <Multiplayer/ConnectionData/IConnectionData.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
namespace Multiplayer
{
@@ -30,7 +30,7 @@ namespace Multiplayer
ConnectionDataType GetConnectionDataType() const override;
AzNetworking::IConnection* GetConnection() const override;
EntityReplicationManager& GetReplicationManager() override;
void Update(AZ::TimeMs hostTimeMs) override;
void Update() override;
bool CanSendUpdates() const override;
void SetCanSendUpdates(bool canSendUpdates) override;
//! @}
@@ -7,7 +7,10 @@
*/
#include <Source/ConnectionData/ServerToClientConnectionData.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Multiplayer/Components/LocalPredictionPlayerInputComponent.h>
#include <Multiplayer/IMultiplayer.h>
#include <AzNetworking/Utilities/EncryptionCommon.h>
namespace Multiplayer
{
@@ -25,7 +28,7 @@ namespace Multiplayer
)
: m_connection(connection)
, m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); })
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, AzNetworking::ConnectionId connectionId) { OnControlledEntityMigration(entityHandle, remoteHostId, connectionId); })
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId) { OnControlledEntityMigration(entityHandle, remoteHostId, connectionId); })
, m_controlledEntity(controlledEntity)
, m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient)
{
@@ -66,7 +69,7 @@ namespace Multiplayer
return m_entityReplicationManager;
}
void ServerToClientConnectionData::Update(AZ::TimeMs hostTimeMs)
void ServerToClientConnectionData::Update()
{
m_entityReplicationManager.ActivatePendingEntities();
@@ -76,7 +79,7 @@ namespace Multiplayer
// potentially false if we just migrated the player, if that is the case, don't send any more updates
if (netBindComponent != nullptr && (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority))
{
m_entityReplicationManager.SendUpdates(hostTimeMs);
m_entityReplicationManager.SendUpdates();
}
}
}
@@ -91,40 +94,32 @@ namespace Multiplayer
void ServerToClientConnectionData::OnControlledEntityMigration
(
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
[[maybe_unused]] HostId remoteHostId,
[[maybe_unused]] const HostId& remoteHostId,
[[maybe_unused]] AzNetworking::ConnectionId connectionId
)
{
//Multiplayer::ServerAddrInfo serverAddr;
//if (gNovaGame->GetMultiplayerworkAgent().GetServerToServerNetwork().GetServerAddrInfoFromConnectionId(newConnectionId, serverAddr) == false)
//{
// AZLOG_WARN("MigrateClient::Failed to find servershard address, userID:%d", static_cast<uint32_t>(GetUserId()));
// return;
//}
//
//Multiplayer::GameTimePoint migratedClientGameTimePoint;
//
//if (m_ControlledEntity != nullptr)
//{
// if (const PlayerNetworkInputComponent::Authority* pComponent = Multiplayer::FindController<PlayerNetworkInputComponent::Authority>(m_ControlledEntity))
// {
// migratedClientGameTimePoint = pComponent->GetLastInputId().GetServerGameTimePoint();
// }
//}
//
// generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration
//const uint64_t randomUserIdentifier = 0;
//
//// Tell the server a new client is about to join
//MultiplayerPackets::NotifyClientMigration notifyClientMigration(randomUserIdentifier);
//gNovaGame->GetMultiplayerworkAgent().GetServerToServerNetwork().SendReliablePacket(newConnectionId, notifyClientMigration);
//
//// Tell the client who to join
//MultiplayerPackets::ClientMigration clientMigration(randomUserIdentifier, serverAddr, migratedClientGameTimePoint);
//GetConnection()->SendReliablePacket(clientMigration);
//
//m_controlledEntity = nullptr;
//m_canSendUpdates = false;
ClientInputId migratedClientInputId = ClientInputId{ 0 };
if (m_controlledEntity != nullptr)
{
auto controller = m_controlledEntity.FindController<LocalPredictionPlayerInputComponentController>();
if (controller != nullptr)
{
migratedClientInputId = controller->GetLastInputId();
}
}
// Generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration
const uint64_t randomUserIdentifier = AzNetworking::CryptoRand64();
// Tell the new host that a client is about to (re)join
GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, randomUserIdentifier, migratedClientInputId);
// Tell the client who to join
MultiplayerPackets::ClientMigration clientMigration(remoteHostId, randomUserIdentifier, migratedClientInputId);
GetConnection()->SendReliablePacket(clientMigration);
m_controlledEntity = NetworkEntityHandle();
m_canSendUpdates = false;
}
void ServerToClientConnectionData::OnGameplayStarted()
@@ -9,7 +9,7 @@
#pragma once
#include <Multiplayer/ConnectionData/IConnectionData.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
namespace Multiplayer
{
@@ -30,7 +30,7 @@ namespace Multiplayer
ConnectionDataType GetConnectionDataType() const override;
AzNetworking::IConnection* GetConnection() const override;
EntityReplicationManager& GetReplicationManager() override;
void Update(AZ::TimeMs hostTimeMs) override;
void Update() override;
bool CanSendUpdates() const override;
void SetCanSendUpdates(bool canSendUpdates) override;
//! @}
@@ -42,7 +42,7 @@ namespace Multiplayer
private:
void OnControlledEntityRemove();
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, AzNetworking::ConnectionId connectionId);
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId);
void OnGameplayStarted();
EntityReplicationManager m_entityReplicationManager;
@@ -32,7 +32,7 @@ namespace Multiplayer
{
m_networkEditorInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(
AZ::Name(MpEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this);
m_networkEditorInterface->SetTimeoutEnabled(false);
m_networkEditorInterface->SetTimeoutMs(AZ::TimeMs{ 0 }); // Disable timeouts on this network interface
if (editorsv_isDedicated)
{
uint16_t editorServerPort = DefaultServerEditorPort;
@@ -10,6 +10,17 @@
namespace Multiplayer
{
void FullOwnershipEntityDomain::SetAabb([[maybe_unused]] const AZ::Aabb& aabb)
{
; // Do nothing, by definition we own everything
}
const AZ::Aabb& FullOwnershipEntityDomain::GetAabb() const
{
static AZ::Aabb nullAabb = AZ::Aabb::CreateNull();
return nullAabb;
}
bool FullOwnershipEntityDomain::IsInDomain([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const
{
return true;
@@ -20,9 +31,9 @@ namespace Multiplayer
;
}
void FullOwnershipEntityDomain::RetrieveEntitiesNotInDomain([[maybe_unused]] EntitiesNotInDomain& outEntitiesNotInDomain) const
const IEntityDomain::EntitiesNotInDomain& FullOwnershipEntityDomain::RetrieveEntitiesNotInDomain() const
{
;
return m_entitiesNotInDomain;
}
void FullOwnershipEntityDomain::DebugDraw() const
@@ -21,10 +21,15 @@ namespace Multiplayer
//! IEntityDomain overrides.
//! @{
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;
void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const override;
const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const override;
void DebugDraw() const override;
//! @}
private:
EntitiesNotInDomain m_entitiesNotInDomain;
};
}
@@ -72,7 +72,7 @@ namespace Multiplayer
AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
"The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port");
AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
"The address of the remote server or host to connect to");
AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic");
AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic");
@@ -80,14 +80,13 @@ namespace Multiplayer
AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking");
AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server");
AZ_CVAR(bool, sv_isTransient, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether a dedicated server shuts down if all existing connections disconnect.");
AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
"Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
AZ_CVAR(AZ::TimeMs, sv_serverSendRateMs, AZ::TimeMs{ 50 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of milliseconds between each network update");
AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
"The default spawnable to use when a new player connects");
AZ_CVAR(float, cl_renderTickBlendBase, 0.15f, nullptr, AZ::ConsoleFunctorFlags::Null,
"The base used for blending between network updates, 0.1 will be quite linear, 0.2 or 0.3 will "
"slow down quicker and may be better suited to connections with highly variable latency");
AZ_CVAR(bool, bg_multiplayerDebugDraw, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables debug draw for the multiplayer gem");
void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context)
{
@@ -95,9 +94,6 @@ namespace Multiplayer
{
serializeContext->Class<MultiplayerSystemComponent, AZ::Component>()
->Version(1);
serializeContext->Class<HostId>()
->Version(1);
serializeContext->Class<NetEntityId>()
->Version(1);
serializeContext->Class<NetComponentId>()
@@ -113,7 +109,6 @@ namespace Multiplayer
}
else if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<HostId>();
behaviorContext->Class<NetEntityId>();
behaviorContext->Class<NetComponentId>();
behaviorContext->Class<PropertyIndex>();
@@ -174,7 +169,12 @@ namespace Multiplayer
AZ::ConsoleInvokedFrom invokedFrom
) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); })
{
;
AZ::Interface<IMultiplayer>::Register(this);
}
MultiplayerSystemComponent::~MultiplayerSystemComponent()
{
AZ::Interface<IMultiplayer>::Unregister(this);
}
void MultiplayerSystemComponent::Activate()
@@ -186,7 +186,6 @@ namespace Multiplayer
{
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
}
AZ::Interface<IMultiplayer>::Register(this);
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Register(this);
//! Register our gems multiplayer components to assign NetComponentIds
@@ -196,11 +195,12 @@ namespace Multiplayer
void MultiplayerSystemComponent::Deactivate()
{
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Unregister(this);
AZ::Interface<IMultiplayer>::Unregister(this);
m_consoleCommandHandler.Disconnect();
AZ::Interface<INetworking>::Get()->DestroyNetworkInterface(AZ::Name(MpNetworkInterfaceName));
AzFramework::SessionNotificationBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
m_networkEntityManager.Reset();
}
bool MultiplayerSystemComponent::StartHosting(uint16_t port, bool isDedicated)
@@ -209,7 +209,7 @@ namespace Multiplayer
return m_networkInterface->Listen(port);
}
bool MultiplayerSystemComponent::Connect(AZStd::string remoteAddress, uint16_t port)
bool MultiplayerSystemComponent::Connect(const AZStd::string& remoteAddress, uint16_t port)
{
InitializeMultiplayer(MultiplayerAgentType::Client);
const IpAddress address(remoteAddress.c_str(), port, m_networkInterface->GetType());
@@ -311,7 +311,6 @@ namespace Multiplayer
void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
const AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f));
const AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs();
const AZ::TimeMs serverRateMs = static_cast<AZ::TimeMs>(sv_serverSendRateMs);
const float serverRateSeconds = static_cast<float>(serverRateMs) / 1000.0f;
@@ -348,12 +347,12 @@ namespace Multiplayer
// Send out the game state update to all connections
{
auto sendNetworkUpdates = [hostTimeMs, &stats](IConnection& connection)
auto sendNetworkUpdates = [&stats](IConnection& connection)
{
if (connection.GetUserData() != nullptr)
{
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection.GetUserData());
connectionData->Update(hostTimeMs);
connectionData->Update();
if (connectionData->GetConnectionDataType() == ConnectionDataType::ServerToClient)
{
stats.m_clientConnectionCount++;
@@ -395,6 +394,11 @@ namespace Multiplayer
{
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
}
if (bg_multiplayerDebugDraw)
{
m_networkEntityManager.DebugDraw();
}
}
int MultiplayerSystemComponent::GetTickOrder()
@@ -460,7 +464,7 @@ namespace Multiplayer
AzFramework::PlayerConnectionConfig config;
config.m_playerConnectionId = aznumeric_cast<uint32_t>(connection->GetConnectionId());
config.m_playerSessionId = packet.GetTicket();
if(!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get()->ValidatePlayerJoinSession(config))
if (!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get()->ValidatePlayerJoinSession(config))
{
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); };
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
@@ -469,7 +473,7 @@ namespace Multiplayer
}
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str());
if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map)))
if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map)))
{
m_didHandshake = true;
@@ -489,10 +493,8 @@ namespace Multiplayer
)
{
m_didHandshake = true;
AZ::CVarFixedString commandString = "sv_map " + packet.GetMap();
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(commandString.c_str());
AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap();
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(loadLevelString.c_str());
return true;
@@ -607,7 +609,22 @@ namespace Multiplayer
[[maybe_unused]] MultiplayerPackets::ClientMigration& packet
)
{
return false;
if (GetAgentType() != MultiplayerAgentType::Client)
{
// Only clients are allowed to migrate from one server to another
return false;
}
// Store the temporary user identifier so we can transmit it with our next Connect packet
// The new server will use this to re-attach our set of autonomous entities
// Disconnect our existing server connection
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); };
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
AZLOG_INFO("Migrating to new server shard");
m_clientMigrationStartEvent.Signal(packet.GetLastClientInputId());
m_networkInterface->Connect(packet.GetRemoteServerAddress());
return true;
}
ConnectResult MultiplayerSystemComponent::ValidateConnect
@@ -641,7 +658,7 @@ namespace Multiplayer
else
{
AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str());
m_connAcquiredEvent.Signal(datum);
m_connectionAcquiredEvent.Signal(datum);
}
// Hosts will spawn a new default player prefab for the user that just connected
@@ -655,27 +672,14 @@ namespace Multiplayer
}
controlledEntity.Activate();
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
}
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
}
else
{
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket));
}
else
{
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetProviderTicket(providerTicket);
}
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs);
connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket));
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>(connection);
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
}
}
@@ -754,13 +758,21 @@ namespace Multiplayer
if (m_agentType == MultiplayerAgentType::Uninitialized)
{
m_spawnNetboundEntities = false;
if (multiplayerType == MultiplayerAgentType::ClientServer || multiplayerType == MultiplayerAgentType::DedicatedServer)
{
m_spawnNetboundEntities = true;
m_initEvent.Signal(m_networkInterface);
//const AZ::Aabb worldBounds = AZ::Interface<IPhysics>.Get()->GetWorldBounds();
AZStd::unique_ptr<IEntityDomain> newDomain = AZStd::make_unique<FullOwnershipEntityDomain>();
m_networkEntityManager.Initialize(InvalidHostId, AZStd::move(newDomain));
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);
m_networkEntityManager.Initialize(hostId, AZStd::make_unique<FullOwnershipEntityDomain>());
}
}
}
m_agentType = multiplayerType;
@@ -779,14 +791,34 @@ namespace Multiplayer
AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType));
}
void MultiplayerSystemComponent::AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler)
{
handler.Connect(m_clientMigrationStartEvent);
}
void MultiplayerSystemComponent::AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler)
{
handler.Connect(m_clientMigrationEndEvent);
}
void MultiplayerSystemComponent::AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler)
{
handler.Connect(m_clientDisconnectedEvent);
}
void MultiplayerSystemComponent::AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler)
{
handler.Connect(m_notifyClientMigrationEvent);
}
void MultiplayerSystemComponent::AddNotifyEntityMigrationEventHandler(NotifyEntityMigrationEvent::Handler& handler)
{
handler.Connect(m_notifyEntityMigrationEvent);
}
void MultiplayerSystemComponent::AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler)
{
handler.Connect(m_connAcquiredEvent);
handler.Connect(m_connectionAcquiredEvent);
}
void MultiplayerSystemComponent::AddSessionInitHandler(SessionInitEvent::Handler& handler)
@@ -799,6 +831,16 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent);
}
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId)
{
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId);
}
void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
{
m_notifyEntityMigrationEvent.Signal(entityHandle, remoteHostId);
}
void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates)
{
IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet();
@@ -845,6 +887,16 @@ namespace Multiplayer
return m_filterEntityManager;
}
void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value)
{
m_spawnNetboundEntities = value;
}
bool MultiplayerSystemComponent::GetShouldSpawnNetworkEntities() const
{
return m_spawnNetboundEntities;
}
void MultiplayerSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
const MultiplayerStats& stats = GetStats();
@@ -904,7 +956,7 @@ namespace Multiplayer
// Unfortunately necessary, as NotifyPreRender can update transforms and thus cause a deadlock inside the vis system
AZStd::vector<NetBindComponent*> gatheredEntities;
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(viewFrustum,
[&gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
[this, &gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size());
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
@@ -912,7 +964,7 @@ namespace Multiplayer
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_networkEntityManager.GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (netBindComponent != nullptr)
{
gatheredEntities.push_back(netBindComponent);
@@ -932,7 +984,7 @@ namespace Multiplayer
for (auto& iter : *(m_networkEntityManager.GetNetworkEntityTracker()))
{
AZ::Entity* entity = iter.second;
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_networkEntityManager.GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (netBindComponent != nullptr)
{
netBindComponent->NotifyPreRender(deltaTime);
@@ -991,7 +1043,10 @@ namespace Multiplayer
void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
AZ::Interface<IMultiplayer>::Get()->StartHosting(sv_port, sv_isDedicated);
if (!AZ::Interface<IMultiplayer>::Get()->StartHosting(sv_port, sv_isDedicated))
{
AZLOG_ERROR("Failed to start listening on port %u, port is in use?", static_cast<uint32_t>(sv_port));
}
}
AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to");
@@ -55,7 +55,7 @@ namespace Multiplayer
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
MultiplayerSystemComponent();
~MultiplayerSystemComponent() override = default;
~MultiplayerSystemComponent() override;
//! AZ::Component overrides.
//! @{
@@ -105,13 +105,19 @@ namespace Multiplayer
//! @{
MultiplayerAgentType GetAgentType() const override;
void InitializeMultiplayer(MultiplayerAgentType state) override;
bool StartHosting(uint16_t port, bool isDedicated = true) override;
bool Connect(const AZStd::string& remoteAddress, uint16_t port) override;
void Terminate(AzNetworking::DisconnectReason reason) override;
void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) override;
void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) override;
void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) override;
void AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler) override;
void AddNotifyEntityMigrationEventHandler(NotifyEntityMigrationEvent::Handler& handler) override;
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
bool StartHosting(uint16_t port, bool isDedicated = true) override;
bool Connect(AZStd::string remoteAddress, uint16_t port) override;
void Terminate(AzNetworking::DisconnectReason reason) override;
void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override;
void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override;
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
AZ::TimeMs GetCurrentHostTimeMs() const override;
float GetCurrentBlendFactor() const override;
@@ -119,6 +125,8 @@ namespace Multiplayer
INetworkEntityManager* GetNetworkEntityManager() override;
void SetFilterEntityManager(IFilterEntityManager* entityFilter) override;
IFilterEntityManager* GetFilterEntityManager() override;
void SetShouldSpawnNetworkEntities(bool value) override;
bool GetShouldSpawnNetworkEntities() const override;
//! @}
//! Console commands.
@@ -148,8 +156,12 @@ namespace Multiplayer
SessionInitEvent m_initEvent;
SessionShutdownEvent m_shutdownEvent;
ConnectionAcquiredEvent m_connAcquiredEvent;
ConnectionAcquiredEvent m_connectionAcquiredEvent;
ClientDisconnectedEvent m_clientDisconnectedEvent;
ClientMigrationStartEvent m_clientMigrationStartEvent;
ClientMigrationEndEvent m_clientMigrationEndEvent;
NotifyClientMigrationEvent m_notifyClientMigrationEvent;
NotifyEntityMigrationEvent m_notifyEntityMigrationEvent;
AZStd::queue<AZStd::string> m_pendingConnectionTickets;
@@ -160,6 +172,7 @@ namespace Multiplayer
float m_renderBlendFactor = 0.0f;
float m_tickFactor = 0.0f;
bool m_didHandshake = false;
bool m_spawnNetboundEntities = true;
#if !defined(AZ_RELEASE_BUILD)
MultiplayerEditorConnection m_editorConnectionListener;
@@ -6,11 +6,10 @@
*
*/
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
@@ -46,6 +45,7 @@ namespace Multiplayer
, m_clearRemovedReplicators([this]() { ClearRemovedReplicators(); }, AZ::Name("EntityReplicationManager::ClearRemovedReplicators"))
, m_updateWindow([this]() { UpdateWindow(); }, AZ::Name("EntityReplicationManager::UpdateWindow"))
, m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); })
, m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); })
{
// Our max payload size is whatever is passed in, minus room for a udp packetheader
m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead;
@@ -61,14 +61,16 @@ namespace Multiplayer
{
networkEntityManager->AddEntityExitDomainHandler(m_entityExitDomainEventHandler);
}
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler);
}
void EntityReplicationManager::SetRemoteHostId(HostId hostId)
void EntityReplicationManager::SetRemoteHostId(const HostId& hostId)
{
m_remoteHostId = hostId;
}
HostId EntityReplicationManager::GetRemoteHostId() const
const HostId& EntityReplicationManager::GetRemoteHostId() const
{
return m_remoteHostId;
}
@@ -107,10 +109,34 @@ namespace Multiplayer
}
}
void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs)
void EntityReplicationManager::SendUpdates()
{
m_frameTimeMs = AZ::GetElapsedTimeMs();
SendEntityUpdates(hostTimeMs);
{
EntityReplicatorList toSendList = GenerateEntityUpdateList();
AZLOG
(
NET_ReplicationInfo,
"Sending %zd updates from %s to %s",
toSendList.size(),
GetNetworkEntityManager()->GetHostId().GetString().c_str(),
GetRemoteHostId().GetString().c_str()
);
// Prep a replication record for send, at this point, everything needs to be sent
for (EntityReplicator* replicator : toSendList)
{
replicator->GetPropertyPublisher()->PrepareSerialization();
}
// While our to send list is not empty, build up another packet to send
do
{
SendEntityUpdateMessages(toSendList);
} while (!toSendList.empty());
}
SendEntityRpcs(m_deferredRpcMessagesReliable, true);
SendEntityRpcs(m_deferredRpcMessagesUnreliable, false);
@@ -120,9 +146,9 @@ namespace Multiplayer
AZLOG
(
NET_ReplicationInfo,
"Sending from %u to %u, replicator count %u orphan count %u deferred reliable count %u deferred unreliable count %u",
aznumeric_cast<uint32_t>(GetNetworkEntityManager()->GetHostId()),
aznumeric_cast<uint32_t>(GetRemoteHostId()),
"Sending from %s to %s, replicator count %u orphan count %u deferred reliable count %u deferred unreliable count %u",
GetNetworkEntityManager()->GetHostId().GetString().c_str(),
GetRemoteHostId().GetString().c_str(),
aznumeric_cast<uint32_t>(m_entityReplicatorMap.size()),
aznumeric_cast<uint32_t>(m_orphanedEntityRpcs.Size()),
aznumeric_cast<uint32_t>(m_deferredRpcMessagesReliable.size()),
@@ -130,65 +156,6 @@ namespace Multiplayer
);
}
void EntityReplicationManager::SendEntityUpdatesPacketHelper
(
AZ::TimeMs hostTimeMs,
EntityReplicatorList& toSendList,
uint32_t maxPayloadSize,
AzNetworking::IConnection& connection
)
{
uint32_t pendingPacketSize = 0;
EntityReplicatorList replicatorUpdatedList;
MultiplayerPackets::EntityUpdates entityUpdatePacket;
entityUpdatePacket.SetHostTimeMs(hostTimeMs);
entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId());
// Serialize everything
while (!toSendList.empty())
{
EntityReplicator* replicator = toSendList.front();
NetworkEntityUpdateMessage updateMessage(replicator->GenerateUpdatePacket());
const uint32_t nextMessageSize = updateMessage.GetEstimatedSerializeSize();
// Check if we are over our limits
const bool payloadFull = (pendingPacketSize + nextMessageSize > maxPayloadSize);
const bool capacityReached = (entityUpdatePacket.GetEntityMessages().size() >= entityUpdatePacket.GetEntityMessages().capacity());
const bool largeEntityDetected = (payloadFull && replicatorUpdatedList.empty());
if (capacityReached || (payloadFull && !largeEntityDetected))
{
break;
}
pendingPacketSize += nextMessageSize;
entityUpdatePacket.ModifyEntityMessages().push_back(updateMessage);
replicatorUpdatedList.push_back(replicator);
toSendList.pop_front();
if (largeEntityDetected)
{
AZLOG_WARN("\n\n*******************************");
AZLOG_WARN
(
"Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d",
aznumeric_cast<uint32_t>(replicator->GetEntityHandle().GetNetEntityId()),
maxPayloadSize,
nextMessageSize
);
AZLOG_WARN("*******************************");
break;
}
}
const AzNetworking::PacketId sentId = connection.SendUnreliablePacket(entityUpdatePacket);
// Update the sent things with the packet id
for (EntityReplicator* replicator : replicatorUpdatedList)
{
replicator->GetPropertyPublisher()->FinalizeSerialization(sentId);
}
}
EntityReplicationManager::EntityReplicatorList EntityReplicationManager::GenerateEntityUpdateList()
{
if (m_replicationWindow == nullptr)
@@ -260,69 +227,92 @@ namespace Multiplayer
return toSendList;
}
void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs)
void EntityReplicationManager::SendEntityUpdateMessages(EntityReplicatorList& replicatorList)
{
EntityReplicatorList toSendList = GenerateEntityUpdateList();
AZLOG(NET_ReplicationInfo, "Sending %zd updates from %d to %d", toSendList.size(), (uint8_t)GetNetworkEntityManager()->GetHostId(), (uint8_t)GetRemoteHostId());
// prep a replication record for send, at this point, everything needs to be sent
for (EntityReplicator* replicator : toSendList)
uint32_t pendingPacketSize = 0;
EntityReplicatorList replicatorUpdatedList;
NetworkEntityUpdateVector entityUpdates;
// Serialize everything
while (!replicatorList.empty())
{
replicator->GetPropertyPublisher()->PrepareSerialization();
EntityReplicator* replicator = replicatorList.front();
NetworkEntityUpdateMessage updateMessage(replicator->GenerateUpdatePacket());
const uint32_t nextMessageSize = updateMessage.GetEstimatedSerializeSize();
// Check if we are over our limits
const bool payloadFull = (pendingPacketSize + nextMessageSize > m_maxPayloadSize);
const bool capacityReached = (entityUpdates.size() >= entityUpdates.capacity());
const bool largeEntityDetected = (payloadFull && replicatorUpdatedList.empty());
if (capacityReached || (payloadFull && !largeEntityDetected))
{
break;
}
pendingPacketSize += nextMessageSize;
entityUpdates.push_back(updateMessage);
replicatorUpdatedList.push_back(replicator);
replicatorList.pop_front();
if (largeEntityDetected)
{
AZLOG_WARN
(
"Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d",
aznumeric_cast<uint32_t>(replicator->GetEntityHandle().GetNetEntityId()),
m_maxPayloadSize,
nextMessageSize
);
break;
}
}
// While our to send list is not empty, build up another packet to send
do
const AzNetworking::PacketId sentId = m_replicationWindow->SendEntityUpdateMessages(entityUpdates);
// Update the sent things with the packet id
for (EntityReplicator* replicator : replicatorUpdatedList)
{
SendEntityUpdatesPacketHelper(hostTimeMs, toSendList, m_maxPayloadSize, m_connection);
} while (!toSendList.empty());
replicator->FinalizeSerialization(sentId);
}
}
void EntityReplicationManager::SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable)
void EntityReplicationManager::SendEntityRpcs(RpcMessages& rpcMessages, bool reliable)
{
while (!deferredRpcs.empty())
while (!rpcMessages.empty())
{
MultiplayerPackets::EntityRpcs entityRpcsPacket;
NetworkEntityRpcVector entityRpcs;
uint32_t pendingPacketSize = 0;
while (!deferredRpcs.empty())
while (!rpcMessages.empty())
{
NetworkEntityRpcMessage& message = deferredRpcs.front();
NetworkEntityRpcMessage& message = rpcMessages.front();
const uint32_t nextRpcSize = message.GetEstimatedSerializeSize();
if ((pendingPacketSize + nextRpcSize) > m_maxPayloadSize)
{
// We're over our limit, break and send an Rpc packet
if (entityRpcsPacket.GetEntityRpcs().size() == 0)
if (entityRpcs.size() == 0)
{
AZLOG(NET_Replicator, "Encountered an RPC that is above our MTU, message will be segmented (object size %u, max allowed size %u)", nextRpcSize, m_maxPayloadSize);
entityRpcsPacket.ModifyEntityRpcs().push_back(message);
deferredRpcs.pop_front();
entityRpcs.push_back(message);
rpcMessages.pop_front();
}
break;
}
pendingPacketSize += nextRpcSize;
if (entityRpcsPacket.GetEntityRpcs().full())
if (entityRpcs.full())
{
// Packet was full, send what we've accumulated so far
AZLOG(NET_Replicator, "We've hit our RPC message limit (RPC count %u, packet size %u)", aznumeric_cast<uint32_t>(entityRpcsPacket.GetEntityRpcs().size()), pendingPacketSize);
AZLOG(NET_Replicator, "We've hit our RPC message limit (RPC count %u, packet size %u)", aznumeric_cast<uint32_t>(entityRpcs.size()), pendingPacketSize);
break;
}
entityRpcsPacket.ModifyEntityRpcs().push_back(message);
deferredRpcs.pop_front();
entityRpcs.push_back(message);
rpcMessages.pop_front();
}
if (reliable)
{
m_connection.SendReliablePacket(entityRpcsPacket);
}
else
{
m_connection.SendUnreliablePacket(entityRpcsPacket);
}
m_replicationWindow->SendEntityRpcs(entityRpcs, reliable);
}
}
@@ -368,10 +358,11 @@ namespace Multiplayer
entityReplicator = GetEntityReplicator(entityHandle);
if (entityReplicator)
{
// Check if we changed our remote role - this can happen during server entity migration. After we migrate ownership to the new server, we hold onto our entity replicator until we are sure
// the other side has received all the packets (and we haven't had to do resends). At this point, it is possible hear back from the remote side we migrated to on the old replicator prior to the timeout and cleanup on the old one
// Check if we changed our remote role - this can happen during server entity migration.
// Retain our replicator after migration until we are sure the other side has received all the packets (and we haven't had to do resends).
// At this point, the remote host should inform us we've migrated prior to the timeout and cleanup of the old replicator
const bool changedRemoteRole = (remoteNetworkRole != entityReplicator->GetRemoteNetworkRole());
// check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client
// Check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client
bool changedLocalRole(false);
if (AZ::Entity* localEnt = entityReplicator->GetEntityHandle().GetEntity())
{
@@ -391,19 +382,33 @@ namespace Multiplayer
// Reset our replicator, we are establishing a new one
entityReplicator->Reset(remoteNetworkRole);
}
// else case is when an entity had left relevancy and come back (but it was still pending a removal)
// Else case is when an entity had left relevancy and come back (but it was still pending a removal)
entityReplicator->Initialize(entityHandle);
AZLOG(NET_RepDeletes, "Reinited replicator for %u from remote manager id %d role %d", entityHandle.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()), aznumeric_cast<int32_t>(remoteNetworkRole));
AZLOG
(
NET_RepDeletes,
"Reinited replicator for %u from remote host %s role %d",
entityHandle.GetNetEntityId(),
GetRemoteHostId().GetString().c_str(),
aznumeric_cast<int32_t>(remoteNetworkRole)
);
}
else
{
// haven't seen him before, let's add him
// Haven't seen him before, let's add him
AZ_Assert(entityHandle.GetNetBindComponent(), "No NetBindComponent");
AZStd::unique_ptr<EntityReplicator> newEntityReplicator = AZStd::make_unique<EntityReplicator>(*this, &m_connection, remoteNetworkRole, entityHandle);
newEntityReplicator->Initialize(entityHandle);
entityReplicator = newEntityReplicator.get();
m_entityReplicatorMap.emplace(entityHandle.GetNetEntityId(), AZStd::move(newEntityReplicator));
AZLOG(NET_RepDeletes, "Added replicator for %u from remote manager id %d role %d", entityHandle.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()), aznumeric_cast<int32_t>(remoteNetworkRole));
AZLOG
(
NET_RepDeletes,
"Added replicator for %u from remote host %s role %d",
entityHandle.GetNetEntityId(),
GetRemoteHostId().GetString().c_str(),
aznumeric_cast<int32_t>(remoteNetworkRole)
);
}
}
else
@@ -453,11 +458,16 @@ namespace Multiplayer
}
// @nt: TODO - delete once dropped RPC problem fixed
void EntityReplicationManager::AddAutonomousEntityReplicatorCreatedHandle(AZ::Event<NetEntityId>::Handler& handler)
void EntityReplicationManager::AddAutonomousEntityReplicatorCreatedHandler(AZ::Event<NetEntityId>::Handler& handler)
{
handler.Connect(m_autonomousEntityReplicatorCreated);
}
void EntityReplicationManager::AddSendMigrateEntityEventHandler(SendMigrateEntityEvent::Handler& handler)
{
handler.Connect(m_sendMigrateEntityEvent);
}
const EntityReplicator* EntityReplicationManager::GetEntityReplicator(NetEntityId netEntityId) const
{
auto it = m_entityReplicatorMap.find(netEntityId);
@@ -492,18 +502,18 @@ namespace Multiplayer
{
if (entityReplicator->IsMarkedForRemoval())
{
AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %u remote manager id %d", updateMessage.GetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
}
else if (entityReplicator->OwnsReplicatorLifetime())
{
// This can occur if we migrate entities quickly - if this is a replicator from C to A, A migrates to B, B then migrates to C, and A's delete replicator has not arrived at C
AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %u remote manager id %d", updateMessage.GetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
}
else
{
shouldDeleteEntity = true;
entityReplicator->MarkForRemoval();
AZLOG(NET_RepDeletes, "Deleting replicater for entity id %u remote manager id %d", updateMessage.GetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepDeletes, "Deleting replicater for entity id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
}
}
else
@@ -519,17 +529,17 @@ namespace Multiplayer
{
if (updateMessage.GetWasMigrated())
{
AZLOG(NET_RepDeletes, "Leaving id %u using timeout remote manager id %d", entity.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepDeletes, "Leaving id %u using timeout remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
}
else
{
AZLOG(NET_RepDeletes, "Deleting entity id %u remote manager id %d", entity.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepDeletes, "Deleting entity id %u remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
GetNetworkEntityManager()->MarkForRemoval(entity);
}
}
else
{
AZLOG(NET_RepDeletes, "Trying to delete entity id %u remote manager id %d, but it has been removed", entity.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepDeletes, "Trying to delete entity id %u remote host %s, but it has been removed", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
}
}
@@ -698,8 +708,8 @@ namespace Multiplayer
AZLOG_WARN
(
"Dropping Packet and LocalServerToRemoteClient connection, unexpected packet "
"LocalShard=%u EntityId=%u RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s",
aznumeric_cast<uint32_t>(GetNetworkEntityManager()->GetHostId()),
"LocalShard=%s EntityId=%u RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s",
GetNetworkEntityManager()->GetHostId().GetString().c_str(),
aznumeric_cast<uint32_t>(entityReplicator->GetEntityHandle().GetNetEntityId()),
aznumeric_cast<uint32_t>(entityReplicator->GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityReplicator->GetBoundLocalNetworkRole()),
@@ -750,13 +760,13 @@ namespace Multiplayer
result = UpdateValidationResult::DropMessage;
if (updateMessage.GetIsDelete())
{
AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %u, sequence %d latest sequence %d from remote manager id %d",
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %u, sequence %d latest sequence %d from remote host %s",
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
}
else
{
AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %u, sequence %d latest sequence %d from remote manager id %d",
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %u, sequence %d latest sequence %d from remote host %s",
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
}
}
}
@@ -1062,12 +1072,12 @@ namespace Multiplayer
return false;
}
void EntityReplicationManager::SetEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain)
void EntityReplicationManager::SetRemoteEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain)
{
m_remoteEntityDomain = AZStd::move(entityDomain);
}
IEntityDomain* EntityReplicationManager::GetEntityDomain()
IEntityDomain* EntityReplicationManager::GetRemoteEntityDomain()
{
return m_remoteEntityDomain.get();
}
@@ -1108,7 +1118,7 @@ namespace Multiplayer
bool didSucceed = true;
EntityMigrationMessage message;
message.m_entityId = replicator->GetEntityHandle().GetNetEntityId();
message.m_netEntityId = replicator->GetEntityHandle().GetNetEntityId();
message.m_prefabEntityId = netBindComponent->GetPrefabEntityId();
if (localEnt->GetState() == AZ::Entity::State::Active)
@@ -1133,9 +1143,12 @@ namespace Multiplayer
message.m_propertyUpdateData.Resize(inputSerializer.GetSize());
}
AZ_Assert(didSucceed, "Failed to migrate entity from server");
// TODO: Move this to an event
//m_connection.SendReliablePacket(message);
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote manager id %d", netEntityId, aznumeric_cast<int32_t>(GetRemoteHostId()));
m_sendMigrateEntityEvent.Signal(m_connection, message);
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote host %s", netEntityId, GetRemoteHostId().GetString().c_str());
// Notify all other EntityReplicationManagers that this entity has migrated so they can adjust their own replicators given our new proxy status
GetMultiplayer()->SendNotifyEntityMigrationEvent(entityHandle, GetRemoteHostId());
// Immediately add a new replicator so that we catch RPC invocations, the remote side will make us a new one, and then remove us if needs be
AddEntityReplicator(entityHandle, NetEntityRole::Authority);
@@ -1144,7 +1157,7 @@ namespace Multiplayer
bool EntityReplicationManager::HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message)
{
EntityReplicator* replicator = GetEntityReplicator(message.m_entityId);
EntityReplicator* replicator = GetEntityReplicator(message.m_netEntityId);
{
if (message.m_propertyUpdateData.GetSize() > 0)
{
@@ -1154,7 +1167,7 @@ namespace Multiplayer
invokingConnection,
replicator,
AzNetworking::InvalidPacketId,
message.m_entityId,
message.m_netEntityId,
NetEntityRole::Server,
outputSerializer,
message.m_prefabEntityId
@@ -1168,7 +1181,7 @@ namespace Multiplayer
// The HandlePropertyChangeMessage will have made a replicator if we didn't have one already
if (!replicator)
{
replicator = GetEntityReplicator(message.m_entityId);
replicator = GetEntityReplicator(message.m_netEntityId);
}
AZ_Assert(replicator, "Do not have replicator after handling migration message");
@@ -1188,7 +1201,7 @@ namespace Multiplayer
// Change the role on the replicator
AddEntityReplicator(entityHandle, NetEntityRole::Server);
AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote manager id %d", entityHandle.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote host %s", entityHandle.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
return true;
}
@@ -1200,11 +1213,11 @@ namespace Multiplayer
}
}
void EntityReplicationManager::OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, [[maybe_unused]] AzNetworking::ConnectionId connectionId)
void EntityReplicationManager::OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
{
if (remoteHostId == GetRemoteHostId())
{
// don't handle self sent messages
// Don't handle self sent messages
return;
}
@@ -12,11 +12,10 @@
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
@@ -55,7 +54,7 @@ namespace Multiplayer
{
if (auto localEnt = m_entityHandle.GetEntity())
{
m_netBindComponent = localEnt->FindComponent<NetBindComponent>();
m_netBindComponent = m_entityHandle.GetNetBindComponent();
m_boundLocalNetworkRole = m_netBindComponent->GetNetEntityRole();
}
}
@@ -95,7 +94,7 @@ namespace Multiplayer
m_entityHandle = entityHandle;
if (auto localEntity = m_entityHandle.GetEntity())
{
m_netBindComponent = localEntity->FindComponent<NetBindComponent>();
m_netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(m_netBindComponent, "No Multiplayer::NetBindComponent");
m_boundLocalNetworkRole = m_netBindComponent->GetNetEntityRole();
SetPrefabEntityId(m_netBindComponent->GetPrefabEntityId());
@@ -126,7 +125,8 @@ namespace Multiplayer
!RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False,
m_netBindComponent,
*m_connection
);
);
m_onEntityDirtiedHandler.Disconnect();
m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler);
}
else
@@ -147,8 +147,9 @@ namespace Multiplayer
// Prepare event handlers
if (auto localEntity = m_entityHandle.GetEntity())
{
NetBindComponent* netBindComponent = localEntity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
m_onEntityStopHandler.Disconnect();
netBindComponent->AddEntityStopEventHandler(m_onEntityStopHandler);
AttachRPCHandlers();
}
@@ -169,7 +170,7 @@ namespace Multiplayer
if (auto localEntity = m_entityHandle.GetEntity())
{
NetBindComponent* netBindComponent = localEntity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
switch (GetBoundLocalNetworkRole())
@@ -471,19 +472,19 @@ namespace Multiplayer
AZLOG
(
NET_RepDeletes,
"Sending delete replicator id %u migrated %d to remote manager id %d",
"Sending delete replicator id %u migrated %d to remote host %s",
aznumeric_cast<uint32_t>(GetEntityHandle().GetNetEntityId()),
WasMigrated() ? 1 : 0,
aznumeric_cast<int32_t>(m_replicationManager.GetRemoteHostId())
m_replicationManager.GetRemoteHostId().GetString().c_str()
);
return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated(), m_propertyPublisher->IsRemoteReplicatorEstablished());
}
NetBindComponent* netBindComponent = GetNetBindComponent();
const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished();
//const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished();
NetworkEntityUpdateMessage updateMessage(GetRemoteNetworkRole(), GetEntityHandle().GetNetEntityId());
if (sendSliceName)
//if (sendSliceName)
{
updateMessage.SetPrefabEntityId(netBindComponent->GetPrefabEntityId());
}
@@ -495,6 +496,11 @@ namespace Multiplayer
return updateMessage;
}
void EntityReplicator::FinalizeSerialization(AzNetworking::PacketId sentId)
{
m_propertyPublisher->FinalizeSerialization(sentId);
}
void EntityReplicator::DeferRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
{
// Received rpc metrics, log rpc sent, number of bytes, and the componentId/rpcId for bandwidth metrics
@@ -336,7 +336,6 @@ namespace Multiplayer
case PropertyPublisher::EntityReplicatorState::Deleting:
{
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Prepared, "Unexpected serialization phase");
FinalizeDeleteEntityRecord(sentId);
}
break;
@@ -7,7 +7,7 @@
*/
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/Components/NetBindComponent.h>
namespace Multiplayer
@@ -24,7 +24,7 @@ namespace Multiplayer
;
}
bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId newOwner)
bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner)
{
bool ret = false;
auto timeoutData = m_timeoutDataMap.find(entityHandle.GetNetEntityId());
@@ -33,10 +33,10 @@ namespace Multiplayer
AZLOG
(
NET_AuthTracker,
"AuthTracker: Removing timeout for networkEntityId %u from %u, new owner is %u",
"AuthTracker: Removing timeout for networkEntityId %u from %s, new owner is %s",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(timeoutData->second.m_previousOwner),
aznumeric_cast<uint32_t>(newOwner)
timeoutData->second.m_previousOwner.GetString().c_str(),
newOwner.GetString().c_str()
);
m_timeoutDataMap.erase(timeoutData);
ret = true;
@@ -48,10 +48,10 @@ namespace Multiplayer
AZLOG
(
NET_AuthTracker,
"AuthTracker: Assigning networkEntityId %u from %u to %u",
"AuthTracker: Assigning networkEntityId %u from %s to %s",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(iter->second.back()),
aznumeric_cast<uint32_t>(newOwner)
iter->second.back().GetString().c_str(),
newOwner.GetString().c_str()
);
}
else
@@ -59,9 +59,9 @@ namespace Multiplayer
AZLOG
(
NET_AuthTracker,
"AuthTracker: Assigning networkEntityId %u to %u",
"AuthTracker: Assigning networkEntityId %u to %s",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(newOwner)
newOwner.GetString().c_str()
);
}
@@ -69,7 +69,7 @@ namespace Multiplayer
return ret;
}
void NetworkEntityAuthorityTracker::RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId previousOwner)
void NetworkEntityAuthorityTracker::RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner)
{
auto mapIter = m_entityAuthorityMap.find(entityHandle.GetNetEntityId());
if (mapIter != m_entityAuthorityMap.end())
@@ -87,7 +87,7 @@ namespace Multiplayer
}
}
AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %u from %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), aznumeric_cast<uint32_t>(previousOwner));
AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %u from %s", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), previousOwner.GetString().c_str());
if (auto localEnt = entityHandle.GetEntity())
{
if (authorityStack.empty())
@@ -167,7 +167,7 @@ namespace Multiplayer
return InvalidHostId;
}
NetworkEntityAuthorityTracker::TimeoutData::TimeoutData(ConstNetworkEntityHandle entityHandle, HostId previousOwner)
NetworkEntityAuthorityTracker::TimeoutData::TimeoutData(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner)
: m_entityHandle(entityHandle)
, m_previousOwner(previousOwner)
{
@@ -205,9 +205,9 @@ namespace Multiplayer
{
AZLOG_ERROR
(
"Timed out entity id %u during migration previous owner %u, removing it",
"Timed out entity id %u during migration previous owner %s, removing it",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(timeoutData->second.m_previousOwner)
timeoutData->second.m_previousOwner.GetString().c_str()
);
m_networkEntityManager.MarkForRemoval(entityHandle);
}
@@ -24,8 +24,8 @@ namespace Multiplayer
NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager);
bool DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const;
bool AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId newOwner);
void RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId previousOwner);
bool AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner);
void RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner);
HostId GetEntityAuthorityManager(ConstNetworkEntityHandle entityHandle) const;
private:
@@ -37,7 +37,7 @@ namespace Multiplayer
struct TimeoutData final
{
TimeoutData() = default;
TimeoutData(ConstNetworkEntityHandle entityHandle, HostId previousOwner);
TimeoutData(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner);
ConstNetworkEntityHandle m_entityHandle;
HostId m_previousOwner = InvalidHostId;
};
@@ -20,6 +20,11 @@ namespace Multiplayer
: m_entity(entity)
, m_networkEntityTracker(networkEntityTracker)
{
if (m_networkEntityTracker == nullptr)
{
m_networkEntityTracker = GetNetworkEntityTracker();
}
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
@@ -28,37 +33,18 @@ namespace Multiplayer
if (entity)
{
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
NetBindComponent* netBindComponent = m_entity->template FindComponent<NetBindComponent>();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
m_netBindComponent = netBindComponent;
m_netEntityId = netBindComponent->GetNetEntityId();
m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity);
if (m_netBindComponent != nullptr)
{
m_netEntityId = m_netBindComponent->GetNetEntityId();
}
else
{
*this = ConstNetworkEntityHandle();
}
}
}
ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, NetEntityId netEntityId, const NetworkEntityTracker* networkEntityTracker)
: m_entity(entity)
, m_netEntityId(netEntityId)
, m_networkEntityTracker(networkEntityTracker)
{
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
}
}
ConstNetworkEntityHandle::ConstNetworkEntityHandle(NetBindComponent* netBindComponent, const NetworkEntityTracker* networkEntityTracker)
: m_entity(netBindComponent->GetEntity())
, m_netBindComponent(netBindComponent)
, m_networkEntityTracker(networkEntityTracker)
, m_netEntityId(netBindComponent->GetNetEntityId())
{
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
}
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
}
bool ConstNetworkEntityHandle::Exists() const
{
if (!m_networkEntityTracker)
@@ -144,7 +130,7 @@ namespace Multiplayer
}
if (m_netBindComponent == nullptr)
{
m_netBindComponent = m_entity->template FindComponent<NetBindComponent>();
m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(m_entity);
}
return m_netBindComponent;
}
@@ -16,6 +16,8 @@
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
@@ -41,11 +43,22 @@ namespace Multiplayer
AZ::Interface<INetworkEntityManager>::Unregister(this);
}
void NetworkEntityManager::Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
void NetworkEntityManager::Initialize(const HostId& hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
{
m_hostId = hostId;
m_entityDomain = AZStd::move(entityDomain);
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
m_entityDomain->ActivateTracking(m_ownedEntities);
}
bool NetworkEntityManager::IsInitialized() const
{
return m_entityDomain != nullptr;
}
IEntityDomain* NetworkEntityManager::GetEntityDomain() const
{
return m_entityDomain.get();
}
NetworkEntityTracker* NetworkEntityManager::GetNetworkEntityTracker()
@@ -63,7 +76,7 @@ namespace Multiplayer
return &m_multiplayerComponentRegistry;
}
HostId NetworkEntityManager::GetHostId() const
const HostId& NetworkEntityManager::GetHostId() const
{
return m_hostId;
}
@@ -86,7 +99,7 @@ namespace Multiplayer
NetworkEntityHandle NetworkEntityManager::AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity)
{
m_networkEntityTracker.Add(netEntityId, entity);
return NetworkEntityHandle(entity, netEntityId, &m_networkEntityTracker);
return NetworkEntityHandle(entity, &m_networkEntityTracker);
}
void NetworkEntityManager::MarkForRemoval(const ConstNetworkEntityHandle& entityHandle)
@@ -96,8 +109,6 @@ namespace Multiplayer
if (net_DebugCheckNetworkEntityManager)
{
AZ_Assert(entityHandle.GetNetBindComponent(), "No NetBindComponent found on networked entity");
[[maybe_unused]] const bool isClientOnlyEntity = false;// (ServerIdFromEntityId(it->first) == InvalidHostId);
AZ_Assert(entityHandle.GetNetBindComponent()->IsNetEntityRoleAuthority() || isClientOnlyEntity, "Trying to delete a proxy entity, this will lead to issues deserializing entity updates");
}
m_removeList.push_back(entityHandle.GetNetEntityId());
m_removeEntitiesEvent.Enqueue(AZ::TimeMs{ 0 });
@@ -204,6 +215,29 @@ namespace Multiplayer
m_localDeferredRpcMessages.emplace_back(AZStd::move(message));
}
void NetworkEntityManager::DebugDraw() const
{
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
for (NetworkEntityTracker::const_iterator it = m_networkEntityTracker.begin(); it != m_networkEntityTracker.end(); ++it)
{
AZ::Entity* entity = it->second;
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
{
const AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId());
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax());
}
}
if (m_entityDomain != nullptr)
{
m_entityDomain->DebugDraw();
}
}
void NetworkEntityManager::DispatchLocalDeferredRpcMessages()
{
for (NetworkEntityRpcMessage& rpcMessage : m_localDeferredRpcMessages)
@@ -211,7 +245,7 @@ namespace Multiplayer
AZ::Entity* entity = m_networkEntityTracker.GetRaw(rpcMessage.GetEntityId());
if (entity != nullptr)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
AZ_Assert(netBindComponent != nullptr, "Attempting to send an RPC to an entity with no NetBindComponent");
netBindComponent->HandleRpcMessage(nullptr, NetEntityRole::Server, rpcMessage);
}
@@ -226,9 +260,8 @@ namespace Multiplayer
return;
}
m_entitiesNotInDomain.clear();
m_entityDomain->RetrieveEntitiesNotInDomain(m_entitiesNotInDomain);
for (NetEntityId exitingId : m_entitiesNotInDomain)
const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain = m_entityDomain->RetrieveEntitiesNotInDomain();
for (NetEntityId exitingId : entitiesNotInDomain)
{
OnEntityExitDomain(exitingId);
}
@@ -239,18 +272,6 @@ namespace Multiplayer
bool safeToExit = true;
NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId);
// ClientAutonomous entities need special handling here. When we migrate a player's entity the player's client must tell the new server which
// entity they were controlling. If we tell them to migrate before they know which entity they control it results in them requesting a new entity
// from the new server, resulting in an orphaned PlayerChar. PlayerControllerComponentServerAuthority::PlayerClientHasControlledEntity()
// will tell us whether the client sent an RPC acknowledging that they now know which entity is theirs.
if (AZ::Entity* entity = entityHandle.GetEntity())
{
//if (PlayerComponent::Authority* playerController = FindController<PlayerComponent::Authority>(nonConstExitingEntityPtr))
//{
// safeToExit = playerController->PlayerClientHasControlledEntity();
//}
}
// We also need special handling for the EntityHierarchyComponent as well, since related entities need to be migrated together
//auto* hierarchyController = FindController<EntityHierarchyComponent::Authority>(nonConstExitingEntityPtr);
//if (hierarchyController)
@@ -279,6 +300,21 @@ namespace Multiplayer
}
}
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();
m_controllersActivatedEvent.DisconnectAllHandlers();
m_controllersDeactivatedEvent.DisconnectAllHandlers();
m_localDeferredRpcMessages.clear();
}
void NetworkEntityManager::RemoveEntities()
{
AZStd::vector<NetEntityId> removeList;
@@ -330,6 +366,7 @@ namespace Multiplayer
originalToCloneIdMap[originalEntity->GetId()] = clone->GetId();
// Can't use NetworkEntityTracker to do the lookup since the entity has not activated yet
NetBindComponent* netBindComponent = clone->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
{
@@ -366,7 +403,6 @@ namespace Multiplayer
&AzFramework::GameEntityContextRequestBus::Events::AddGameEntity, clone);
returnList.push_back(netBindComponent->GetEntityHandle());
}
else
{
@@ -31,15 +31,15 @@ namespace Multiplayer
NetworkEntityManager();
~NetworkEntityManager();
//! Only invoked for authoritative hosts
void Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain);
//! INetworkEntityManager overrides.
//! @{
void Initialize(const HostId& hostId, AZStd::unique_ptr<IEntityDomain> entityDomain) override;
bool IsInitialized() const override;
IEntityDomain* GetEntityDomain() const override;
NetworkEntityTracker* GetNetworkEntityTracker() override;
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override;
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override;
HostId GetHostId() const override;
const HostId& GetHostId() const override;
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override;
NetEntityId GetNetEntityIdById(const AZ::EntityId& entityId) const override;
@@ -62,9 +62,7 @@ namespace Multiplayer
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform) override;
void SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) override;
uint32_t GetEntityCount() const override;
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override;
void MarkForRemoval(const ConstNetworkEntityHandle& entityHandle) override;
@@ -81,17 +79,22 @@ namespace Multiplayer
void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) override;
void DebugDraw() const override;
//! @}
void DispatchLocalDeferredRpcMessages();
void UpdateEntityDomain();
void OnEntityExitDomain(NetEntityId entityId);
//! RootSpawnableNotificationBus
//! @{
void OnRootSpawnableAssigned(AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, uint32_t generation) override;
void OnRootSpawnableReleased(uint32_t generation) override;
//! @}
//! Used to release all memory prior to shutdown.
void Reset();
private:
void RemoveEntities();
NetEntityId NextId();
@@ -105,7 +108,6 @@ namespace Multiplayer
AZStd::unique_ptr<IEntityDomain> m_entityDomain;
AZ::ScheduledEvent m_updateEntityDomainEvent;
IEntityDomain::EntitiesNotInDomain m_entitiesNotInDomain;
OwnedEntitySet m_ownedEntities;
EntityExitDomainEvent m_entityExitDomainEvent;
@@ -8,6 +8,7 @@
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
@@ -21,16 +22,26 @@ namespace Multiplayer
m_netEntityIdMap[entity->GetId()] = netEntityId;
}
void NetworkEntityTracker::RegisterNetBindComponent(AZ::Entity* entity, NetBindComponent* component)
{
m_netBindingMap[entity] = component;
}
void NetworkEntityTracker::UnregisterNetBindComponent(NetBindComponent* component)
{
m_netBindingMap.erase(component->GetEntity());
}
NetworkEntityHandle NetworkEntityTracker::Get(NetEntityId netEntityId)
{
AZ::Entity* entity = GetRaw(netEntityId);
return NetworkEntityHandle(entity, netEntityId, this);
return NetworkEntityHandle(entity, this);
}
ConstNetworkEntityHandle NetworkEntityTracker::Get(NetEntityId netEntityId) const
{
AZ::Entity* entity = GetRaw(netEntityId);
return ConstNetworkEntityHandle(entity, netEntityId, this);
return ConstNetworkEntityHandle(entity, this);
}
NetEntityId NetworkEntityTracker::Get(const AZ::EntityId& entityId) const
@@ -15,6 +15,8 @@
namespace Multiplayer
{
class NetBindComponent;
//! @class NetworkEntityTracker
//! @brief The responsibly of this class is to allow entity netEntityId's to be looked up.
class NetworkEntityTracker
@@ -23,16 +25,26 @@ namespace Multiplayer
using EntityMap = AZStd::unordered_map<NetEntityId, AZ::Entity*>;
using NetEntityIdMap = AZStd::unordered_map<AZ::EntityId, NetEntityId>;
using NetBindingMap = AZStd::unordered_map<AZ::Entity*, NetBindComponent*>;
using iterator = EntityMap::iterator;
using const_iterator = EntityMap::const_iterator;
NetworkEntityTracker() = default;
//! Adds a networked entity to the tracker
//! Adds a networked entity to the tracker.
//! @param netEntityId the networkId of the entity to add
//! @param entity pointer to the entity corresponding to the networkId
void Add(NetEntityId netEntityId, AZ::Entity* entity);
//! Registers a new NetBindComponent with the NetworkEntityTracker.
//! @param entity pointer to the entity we are registering the NetBindComponent for
//! @param component pointer to the NetBindComponent being registered
void RegisterNetBindComponent(AZ::Entity* entity, NetBindComponent* component);
//! Unregisters a NetBindComponent from the NetworkEntityTracker.
//! @param component pointer to the NetBindComponent being removed
void UnregisterNetBindComponent(NetBindComponent* component);
//! Returns an entity handle which can validate entity existence.
NetworkEntityHandle Get(NetEntityId netEntityId);
ConstNetworkEntityHandle Get(NetEntityId netEntityId) const;
@@ -46,9 +58,14 @@ namespace Multiplayer
//! Get a raw pointer of an entity.
AZ::Entity *GetRaw(NetEntityId netEntityId) const;
//! Moves the given iterator out of the entity holder and returns the ptr
//! Moves the given iterator out of the entity holder and returns the ptr.
AZ::Entity *Move(EntityMap::iterator iter);
//! Retrieves the NetBindComponent for the provided AZ::Entity, nullptr if the entity does not have netbinding.
//! @param entity pointer to the entity to retrieve the NetBindComponent for
//! @return pointer to the entities NetBindComponent, or nullptr if the entity doesn't exist or does not have netbinding
NetBindComponent* GetNetBindComponent(AZ::Entity* rawEntity) const;
//! Container overloads
//!@{
iterator begin();
@@ -79,6 +96,7 @@ namespace Multiplayer
EntityMap m_entityMap;
NetEntityIdMap m_netEntityIdMap;
NetBindingMap m_netBindingMap;
uint32_t m_deleteChangeDirty = 0;
uint32_t m_addChangeDirty = 0;
};
@@ -10,6 +10,16 @@
namespace Multiplayer
{
inline NetBindComponent* NetworkEntityTracker::GetNetBindComponent(AZ::Entity* rawEntity) const
{
auto found = m_netBindingMap.find(rawEntity);
if (found != m_netBindingMap.end())
{
return found->second;
}
return nullptr;
}
inline NetworkEntityTracker::iterator NetworkEntityTracker::begin()
{
return m_entityMap.begin();
@@ -13,10 +13,12 @@
#include <AzCore/Math/ShapeIntersection.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
namespace Multiplayer
{
AZ_CVAR(float, sv_RewindVolumeExtrudeDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The amount to increase rewind volume checks to account for fast moving entities");
AZ_CVAR(bool, bg_RewindDebugDraw, false, nullptr, AZ::ConsoleFunctorFlags::Null, "If true enables debug draw of rewind operations");
NetworkTime::NetworkTime()
{
@@ -94,21 +96,46 @@ namespace Multiplayer
// Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities
const AZ::Aabb expandedVolume = rewindVolume.GetExpanded(AZ::Vector3(sv_RewindVolumeExtrudeDistance));
AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
AZStd::vector<NetBindComponent*> gatheredEntities;
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume,
[entityBoundsUnion, rewindVolume, &gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
AzFramework::DebugDisplayRequests* debugDisplay = nullptr;
if (bg_RewindDebugDraw)
{
gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size());
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
}
if (debugDisplay)
{
debugDisplay->SetColor(AZ::Colors::Red);
debugDisplay->DrawWireBox(expandedVolume.GetMin(), expandedVolume.GetMax());
}
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume,
[this, debugDisplay, networkEntityTracker, entityBoundsUnion, rewindVolume](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
m_rewoundEntities.reserve(m_rewoundEntities.size() + nodeData.m_entries.size());
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
{
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityLocalBoundsUnion(entity->GetId());
const AZ::Vector3 currentCenter = currentBounds.GetCenter();
NetworkEntityHandle entityHandle(entity, networkEntityTracker);
if (entityHandle.GetNetBindComponent() == nullptr)
{
// Not a net-bound entity, terminate processing of this entity
return;
}
const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityWorldBoundsUnion(entity->GetId());
const AZ::Vector3 currentCenter = currentBounds.GetCenter();
NetworkTransformComponent* networkTransform = entity->template FindComponent<NetworkTransformComponent>();
if (debugDisplay)
{
debugDisplay->SetColor(AZ::Colors::White);
debugDisplay->DrawWireBox(currentBounds.GetMin(), currentBounds.GetMax());
}
if (networkTransform != nullptr)
{
@@ -123,24 +150,20 @@ namespace Multiplayer
}
const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
if (debugDisplay)
{
debugDisplay->SetColor(AZ::Colors::Grey);
debugDisplay->DrawWireBox(rewoundAabb.GetMin(), rewoundAabb.GetMax());
}
if (AZ::ShapeIntersection::Overlaps(rewoundAabb, rewindVolume)) // Validate the rewound aabb intersects our rewind volume
{
// Due to component constraints, netBindComponent must exist if networkTransform exists
NetBindComponent* netBindComponent = entity->template FindComponent<NetBindComponent>();
gatheredEntities.push_back(netBindComponent);
m_rewoundEntities.push_back(entityHandle);
}
}
}
}
});
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
for (NetBindComponent* netBindComponent : gatheredEntities)
{
netBindComponent->NotifySyncRewindState();
m_rewoundEntities.push_back(NetworkEntityHandle(netBindComponent, networkEntityTracker));
}
}
void NetworkTime::ClearRewoundEntities()
@@ -38,20 +38,19 @@ namespace Multiplayer
void NetworkSpawnableHolderComponent::Activate()
{
const auto agentType = GetMultiplayer()->GetAgentType();
const bool shouldSpawnNetEntities =
(agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer);
IMultiplayer* multiplayer = GetMultiplayer();
const bool shouldSpawnNetEntities = multiplayer->GetShouldSpawnNetworkEntities();
if(shouldSpawnNetEntities)
if (shouldSpawnNetEntities)
{
AZ::Transform rootEntityTransform = AZ::Transform::CreateIdentity();
if(auto* transformInterface = GetEntity()->GetTransform())
if (auto* transformInterface = GetEntity()->GetTransform())
{
rootEntityTransform = transformInterface->GetWorldTM();
}
INetworkEntityManager* networkEntityManager = GetNetworkEntityManager();
INetworkEntityManager* networkEntityManager = multiplayer->GetNetworkEntityManager();
AZ_Assert(networkEntityManager != nullptr,
"Network Entity Manager must be initialized before NetworkSpawnableHolderComponent is activated");
@@ -7,9 +7,16 @@
*/
#include <Source/ReplicationWindows/NullReplicationWindow.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
namespace Multiplayer
{
NullReplicationWindow::NullReplicationWindow(AzNetworking::IConnection* connection)
: m_connection(connection)
{
;
}
bool NullReplicationWindow::ReplicationSetUpdateReady()
{
return true;
@@ -36,6 +43,29 @@ namespace Multiplayer
;
}
AzNetworking::PacketId NullReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector)
{
MultiplayerPackets::EntityUpdates entityUpdatePacket;
entityUpdatePacket.SetHostTimeMs(GetNetworkTime()->GetHostTimeMs());
entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId());
entityUpdatePacket.SetEntityMessages(entityUpdateVector);
return m_connection->SendUnreliablePacket(entityUpdatePacket);
}
void NullReplicationWindow::SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable)
{
MultiplayerPackets::EntityRpcs entityRpcsPacket;
entityRpcsPacket.SetEntityRpcs(entityRpcVector);
if (reliable)
{
m_connection->SendReliablePacket(entityRpcsPacket);
}
else
{
m_connection->SendUnreliablePacket(entityRpcsPacket);
}
}
void NullReplicationWindow::DebugDraw() const
{
// Nothing to draw
@@ -9,6 +9,7 @@
#pragma once
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
namespace Multiplayer
{
@@ -16,7 +17,7 @@ namespace Multiplayer
: public IReplicationWindow
{
public:
NullReplicationWindow() = default;
NullReplicationWindow(AzNetworking::IConnection* connection);
//! IReplicationWindow interface
//! @{
@@ -25,10 +26,13 @@ namespace Multiplayer
uint32_t GetMaxProxyEntityReplicatorSendCount() const override;
bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const override;
void UpdateWindow() override;
AzNetworking::PacketId SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) override;
void SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) override;
void DebugDraw() const override;
//! @}
private:
ReplicationSet m_emptySet;
AzNetworking::IConnection* m_connection = nullptr;
};
}
@@ -7,6 +7,7 @@
*/
#include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
#include <AzCore/Component/TransformBus.h>
@@ -50,7 +51,7 @@ namespace Multiplayer
return m_priority < rhs.m_priority;
}
ServerToClientReplicationWindow::ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, const AzNetworking::IConnection* connection)
ServerToClientReplicationWindow::ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, AzNetworking::IConnection* connection)
: m_controlledEntity(controlledEntity)
, m_entityActivatedEventHandler([this](AZ::Entity* entity) { OnEntityActivated(entity); })
, m_entityDeactivatedEventHandler([this](AZ::Entity* entity) { OnEntityDeactivated(entity); })
@@ -106,7 +107,7 @@ namespace Multiplayer
void ServerToClientReplicationWindow::UpdateWindow()
{
// clear the candidate queue, we're going to rebuild it
// Clear the candidate queue, we're going to rebuild it
ReplicationCandidateQueue::container_type clearQueueContainer;
clearQueueContainer.reserve(sv_MaxEntitiesToTrackReplication);
// Move the clearQueueContainer into the ReplicationCandidateQueue to maintain the reserved memory
@@ -117,7 +118,7 @@ namespace Multiplayer
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
if (!netBindComponent || !netBindComponent->HasController())
{
// if we don't have a controlled entity, or we no longer have control of the entity, don't run the update
// If we don't have a controlled entity, or we no longer have control of the entity, don't run the update
return;
}
@@ -148,24 +149,25 @@ namespace Multiplayer
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
NetworkEntityHandle entityHandle(entity, networkEntityTracker);
if (entityHandle.GetNetBindComponent() == nullptr)
{
// Entity does not have netbinding, skip this entity
continue;
}
if (filterEntityManager && filterEntityManager->IsEntityFiltered(entity, m_controlledEntity, m_connection->GetConnectionId()))
{
continue;
}
NetBindComponent* entryNetBindComponent = entity->template FindComponent<NetBindComponent>();
if (entryNetBindComponent != nullptr)
{
// We want to find the closest extent to the player and prioritize using that distance
const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
NetworkEntityHandle entityHandle(entryNetBindComponent, networkEntityTracker);
AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
}
// We want to find the closest extent to the player and prioritize using that distance
const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
}
// Add in Autonomous Entities
@@ -179,6 +181,29 @@ namespace Multiplayer
//}
}
AzNetworking::PacketId ServerToClientReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector)
{
MultiplayerPackets::EntityUpdates entityUpdatePacket;
entityUpdatePacket.SetHostTimeMs(GetNetworkTime()->GetHostTimeMs());
entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId());
entityUpdatePacket.SetEntityMessages(entityUpdateVector);
return m_connection->SendUnreliablePacket(entityUpdatePacket);
}
void ServerToClientReplicationWindow::SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable)
{
MultiplayerPackets::EntityRpcs entityRpcsPacket;
entityRpcsPacket.SetEntityRpcs(entityRpcVector);
if (reliable)
{
m_connection->SendReliablePacket(entityRpcsPacket);
}
else
{
m_connection->SendUnreliablePacket(entityRpcsPacket);
}
}
void ServerToClientReplicationWindow::DebugDraw() const
{
//static const float BoundaryStripeHeight = 1.0f;
@@ -204,11 +229,10 @@ namespace Multiplayer
void ServerToClientReplicationWindow::OnEntityActivated(AZ::Entity* entity)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
ConstNetworkEntityHandle entityHandle(entity);
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
if (netBindComponent != nullptr)
{
ConstNetworkEntityHandle entityHandle(netBindComponent, GetNetworkEntityTracker());
if (netBindComponent->HasController())
{
if (IFilterEntityManager* filter = GetMultiplayer()->GetFilterEntityManager())
@@ -237,10 +261,9 @@ namespace Multiplayer
void ServerToClientReplicationWindow::OnEntityDeactivated(AZ::Entity* entity)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
ConstNetworkEntityHandle entityHandle(entity);
if (entityHandle.GetNetBindComponent() != nullptr)
{
ConstNetworkEntityHandle entityHandle(netBindComponent, GetNetworkEntityTracker());
m_replicationSet.erase(entityHandle);
}
}
@@ -38,7 +38,7 @@ namespace Multiplayer
// we sort lowest priority first, so that we can easily keep the biggest N priorities
using ReplicationCandidateQueue = AZStd::priority_queue<PrioritizedReplicationCandidate>;
ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, const AzNetworking::IConnection* connection);
ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, AzNetworking::IConnection* connection);
//! IReplicationWindow interface
//! @{
@@ -47,6 +47,8 @@ namespace Multiplayer
uint32_t GetMaxProxyEntityReplicatorSendCount() const override;
bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const override;
void UpdateWindow() override;
AzNetworking::PacketId SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) override;
void SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) override;
void DebugDraw() const override;
//! @}
@@ -75,7 +77,7 @@ namespace Multiplayer
//NetBindComponent* m_controlledNetBindComponent = nullptr;
const AzNetworking::IConnection* m_connection = nullptr;
AzNetworking::IConnection* m_connection = nullptr;
// Cached values to detect a poor network connection
uint32_t m_lastCheckedSentPackets = 0;
@@ -15,7 +15,7 @@
#include <AzTest/AzTest.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
namespace Multiplayer
{
@@ -26,10 +26,10 @@
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <NetworkEntity/NetworkEntityTracker.h>
#include <NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <NetworkEntity/EntityReplication/EntityReplicator.h>
namespace Multiplayer
{
@@ -206,7 +206,7 @@ namespace Multiplayer
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity)
{
m_networkEntityMap[netEntityId] = entity;
return NetworkEntityHandle(entity, netEntityId, m_networkEntityTracker.get());
return NetworkEntityHandle(entity, m_networkEntityTracker.get());
}
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const
+22 -9
View File
@@ -19,42 +19,53 @@ namespace UnitTest
class MockMultiplayer : public Multiplayer::IMultiplayer
{
public:
MOCK_CONST_METHOD0(GetCurrentBlendFactor, float ());
MOCK_CONST_METHOD0(GetAgentType, Multiplayer::MultiplayerAgentType());
MOCK_METHOD1(InitializeMultiplayer, void(Multiplayer::MultiplayerAgentType));
MOCK_METHOD2(StartHosting, bool(uint16_t, bool));
MOCK_METHOD2(Connect, bool(AZStd::string, uint16_t));
MOCK_METHOD2(Connect, bool(const AZStd::string&, uint16_t));
MOCK_METHOD1(Terminate, void(AzNetworking::DisconnectReason));
MOCK_METHOD1(AddClientMigrationStartEventHandler, void(Multiplayer::ClientMigrationStartEvent::Handler&));
MOCK_METHOD1(AddClientMigrationEndEventHandler, void(Multiplayer::ClientMigrationEndEvent::Handler&));
MOCK_METHOD1(AddClientDisconnectedHandler, void(AZ::Event<>::Handler&));
MOCK_METHOD1(AddNotifyClientMigrationHandler, void(Multiplayer::NotifyClientMigrationEvent::Handler&));
MOCK_METHOD1(AddNotifyEntityMigrationEventHandler, void(Multiplayer::NotifyEntityMigrationEvent::Handler&));
MOCK_METHOD1(AddConnectionAcquiredHandler, void(AZ::Event<Multiplayer::MultiplayerAgentDatum>::Handler&));
MOCK_METHOD1(AddSessionInitHandler, void(AZ::Event<AzNetworking::INetworkInterface*>::Handler&));
MOCK_METHOD1(AddSessionShutdownHandler, void(AZ::Event<AzNetworking::INetworkInterface*>::Handler&));
MOCK_METHOD3(SendNotifyClientMigrationEvent, void(const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId));
MOCK_METHOD2(SendNotifyEntityMigrationEvent, void(const Multiplayer::ConstNetworkEntityHandle&, const Multiplayer::HostId&));
MOCK_METHOD1(SendReadyForEntityUpdates, void(bool));
MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs());
MOCK_CONST_METHOD0(GetCurrentBlendFactor, float());
MOCK_METHOD0(GetNetworkTime, Multiplayer::INetworkTime* ());
MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ());
MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*));
MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ());
MOCK_METHOD1(SetShouldSpawnNetworkEntities, void(bool));
MOCK_CONST_METHOD0(GetShouldSpawnNetworkEntities, bool());
};
class MockNetworkEntityManager : public Multiplayer::INetworkEntityManager
{
public:
MOCK_METHOD2(RequestNetSpawnableInstantiation, AZStd::unique_ptr<AzFramework::EntitySpawnTicket> (const AZ::Data::Asset<AzFramework::Spawnable>&, const AZ::Transform&));
MOCK_METHOD4(
CreateEntitiesImmediate,
EntityList (const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate));
MOCK_CONST_METHOD1(GetNetEntityIdById, Multiplayer::NetEntityId (const AZ::EntityId&));
MOCK_METHOD2(Initialize, void(const Multiplayer::HostId&, AZStd::unique_ptr<Multiplayer::IEntityDomain>));
MOCK_CONST_METHOD0(IsInitialized, bool());
MOCK_CONST_METHOD0(GetEntityDomain, Multiplayer::IEntityDomain*());
MOCK_METHOD0(GetNetworkEntityTracker, Multiplayer::NetworkEntityTracker* ());
MOCK_METHOD0(GetNetworkEntityAuthorityTracker, Multiplayer::NetworkEntityAuthorityTracker* ());
MOCK_METHOD0(GetMultiplayerComponentRegistry, Multiplayer::MultiplayerComponentRegistry* ());
MOCK_CONST_METHOD0(GetHostId, Multiplayer::HostId());
MOCK_CONST_METHOD0(GetHostId, const Multiplayer::HostId&());
MOCK_CONST_METHOD1(GetEntity, Multiplayer::ConstNetworkEntityHandle(Multiplayer::NetEntityId));
MOCK_CONST_METHOD1(GetNetEntityIdById, Multiplayer::NetEntityId(const AZ::EntityId&));
MOCK_METHOD3(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::
Transform&));
MOCK_METHOD4(
CreateEntitiesImmediate,
EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate));
MOCK_METHOD5(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityId, Multiplayer::
NetEntityRole, Multiplayer::AutoActivate, const AZ::Transform&));
MOCK_METHOD2(RequestNetSpawnableInstantiation, AZStd::unique_ptr<AzFramework::EntitySpawnTicket>(const AZ::Data::Asset<AzFramework::Spawnable>&, const AZ::Transform&));
MOCK_METHOD3(SetupNetEntity, void(AZ::Entity*, Multiplayer::PrefabEntityId, Multiplayer::NetEntityRole));
MOCK_CONST_METHOD1(GetEntity, Multiplayer::ConstNetworkEntityHandle(Multiplayer::NetEntityId));
MOCK_CONST_METHOD0(GetEntityCount, uint32_t());
MOCK_METHOD2(AddEntityToEntityMap, Multiplayer::NetworkEntityHandle(Multiplayer::NetEntityId, AZ::Entity*));
MOCK_METHOD1(MarkForRemoval, void(const Multiplayer::ConstNetworkEntityHandle&));
@@ -73,6 +84,7 @@ 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_CONST_METHOD0(DebugDraw, void());
};
class MockConnectionListener : public AzNetworking::IConnectionListener
@@ -88,6 +100,7 @@ namespace UnitTest
class MockTime : public AZ::ITime
{
public:
MOCK_CONST_METHOD0(GetElapsedTimeUs, AZ::TimeUs());
MOCK_CONST_METHOD0(GetElapsedTimeMs, AZ::TimeMs());
};
@@ -18,7 +18,7 @@
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
namespace Multiplayer
{
@@ -33,6 +33,9 @@ set(FILES
Include/Multiplayer/MultiplayerConstants.h
Include/Multiplayer/MultiplayerStats.h
Include/Multiplayer/MultiplayerTypes.h
Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h
Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h
Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.inl
Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h
Include/Multiplayer/NetworkEntity/IFilterEntityManager.h
Include/Multiplayer/NetworkEntity/INetworkEntityManager.h
@@ -60,6 +63,7 @@ set(FILES
Source/AutoGen/Multiplayer.AutoPackets.xml
Source/AutoGen/MultiplayerEditor.AutoPackets.xml
Source/AutoGen/NetworkCharacterComponent.AutoComponent.xml
Source/AutoGen/NetworkConnectionComponent.AutoComponent.xml
Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.xml
Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.xml
Source/AutoGen/NetworkTransformComponent.AutoComponent.xml
@@ -90,10 +94,7 @@ set(FILES
Source/MultiplayerSystemComponent.cpp
Source/MultiplayerSystemComponent.h
Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp
Source/NetworkEntity/EntityReplication/EntityReplicationManager.h
Source/NetworkEntity/EntityReplication/EntityReplicator.cpp
Source/NetworkEntity/EntityReplication/EntityReplicator.h
Source/NetworkEntity/EntityReplication/EntityReplicator.inl
Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp
Source/NetworkEntity/EntityReplication/PropertyPublisher.h
Source/NetworkEntity/EntityReplication/PropertySubscriber.cpp