Some cleanup to better support backward reconciliation as well as dynamic player spawning on connect

This commit is contained in:
karlberg
2021-05-12 13:41:18 -07:00
parent 35500981eb
commit d0b006c209
20 changed files with 262 additions and 129 deletions
+1 -1
View File
@@ -1247,7 +1247,7 @@ bool CSystem::InitShine([[maybe_unused]] const SSystemInitParams& initParams)
if (!m_env.pLyShine)
{
AZ_Error(AZ_TRACE_SYSTEM_WINDOW, false, "LYShine System did not initialize correctly. Please check that the LyShine gem is enabled for this project in ProjectConfigurator.");
AZ_Error(AZ_TRACE_SYSTEM_WINDOW, false, "LYShine System did not initialize correctly. Please check that the LyShine gem is enabled for this project in *_dependencies.cmake.");
return false;
}
return true;
+74 -5
View File
@@ -15,6 +15,7 @@
#include <AzCore/RTTI/RTTI.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzNetworking/DataStructures/ByteBuffer.h>
#include <Include/INetworkEntityManager.h>
#include <Include/INetworkTime.h>
#include <Include/MultiplayerStats.h>
@@ -46,6 +47,7 @@ namespace Multiplayer
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
using SessionShutdownEvent = AZ::Event<AzNetworking::INetworkInterface*>;
using OnConnectFunctor = AZStd::function<NetworkEntityHandle(AzNetworking::IConnection*, MultiplayerAgentDatum)>;
//! IMultiplayer provides insight into the Multiplayer session and its Agents
class IMultiplayer
@@ -55,26 +57,30 @@ namespace Multiplayer
virtual ~IMultiplayer() = default;
//! Gets the type of Agent this IMultiplayer impl represents
//! Gets the type of Agent this IMultiplayer impl represents.
//! @return The type of agents represented
virtual MultiplayerAgentType GetAgentType() const = 0;
//! Sets the type of this Multiplayer connection and calls any related callback
//! Sets the type of this Multiplayer connection and calls any related callback.
//! @param state The state of this connection
virtual void InitializeMultiplayer(MultiplayerAgentType state) = 0;
//! Adds a ConnectionAcquiredEvent Handler which is invoked when a new endpoint connects to the session
//! Adds a ConnectionAcquiredEvent Handler which is invoked when a new endpoint connects to the session.
//! @param handler The SessionInitEvent Handler to add
virtual void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) = 0;
//! Adds a SessionInitEvent Handler which is invoked when a new network session starts
//! Adds a SessionInitEvent Handler which is invoked when a new network session starts.
//! @param handler The SessionInitEvent Handler to add
virtual void AddSessionInitHandler(SessionInitEvent::Handler& handler) = 0;
//! Adds a SessionShutdownEvent Handler which is invoked when the current network session ends
//! Adds a SessionShutdownEvent Handler which is invoked when the current network session ends.
//! @param handler The SessionShutdownEvent handler to add
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
//! Overrides the default connect behaviour with the provided functor.
//! @param functor the function to invoke during a new connection event
virtual void SetOnConnectFunctor(const OnConnectFunctor& functor) = 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;
@@ -87,6 +93,14 @@ namespace Multiplayer
//! @return the current server time in milliseconds
virtual AZ::TimeMs GetCurrentHostTimeMs() const = 0;
//! Returns the network time instance bound to this multiplayer instance.
//! @return pointer to the network time instance bound to this multiplayer instance
virtual INetworkTime* GetNetworkTime() = 0;
//! Returns the network entity manager instance bound to this multiplayer instance.
//! @return pointer to the network entity manager instance bound to this multiplayer instance
virtual INetworkEntityManager* GetNetworkEntityManager() = 0;
//! Returns the gem name associated with the provided component index.
//! @param netComponentId the componentId to return the gem name of
//! @return the name of the gem that contains the requested component
@@ -117,6 +131,61 @@ namespace Multiplayer
MultiplayerStats m_stats;
};
// Convenience helpers
inline IMultiplayer* GetMultiplayer()
{
return AZ::Interface<IMultiplayer>::Get();
}
inline INetworkTime* GetNetworkTime()
{
return GetMultiplayer()->GetNetworkTime();
}
inline INetworkEntityManager* GetNetworkEntityManager()
{
return GetMultiplayer()->GetNetworkEntityManager();
}
inline NetworkEntityTracker* GetNetworkEntityTracker()
{
return GetNetworkEntityManager()->GetNetworkEntityTracker();
}
inline NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker()
{
return GetNetworkEntityManager()->GetNetworkEntityAuthorityTracker();
}
inline MultiplayerComponentRegistry* GetMultiplayerComponentRegistry()
{
return GetNetworkEntityManager()->GetMultiplayerComponentRegistry();
}
//! @class ScopedAlterTime
//! @brief This is a wrapper that temporarily adjusts global program time for backward reconciliation purposes.
class ScopedAlterTime final
{
public:
inline ScopedAlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId connectionId)
{
INetworkTime* time = GetNetworkTime();
m_previousHostFrameId = time->GetHostFrameId();
m_previousHostTimeMs = time->GetHostTimeMs();
m_previousRewindConnectionId = time->GetRewindingConnectionId();
time->AlterTime(frameId, timeMs, connectionId);
}
inline ~ScopedAlterTime()
{
INetworkTime* time = GetNetworkTime();
time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId);
}
private:
HostFrameId m_previousHostFrameId = InvalidHostFrameId;
AZ::TimeMs m_previousHostTimeMs = AZ::TimeMs{ 0 };
AzNetworking::ConnectionId m_previousRewindConnectionId = AzNetworking::InvalidConnectionId;
};
inline const char* GetEnumString(MultiplayerAgentType value)
{
switch (value)
@@ -59,9 +59,24 @@ namespace Multiplayer
//! Creates new entities of the given archetype
//! @param prefabEntryId the name of the spawnable to spawn
virtual EntityList CreateEntitiesImmediate(
const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole, AutoActivate autoActivate,
const AZ::Transform& transform) = 0;
virtual EntityList CreateEntitiesImmediate
(
const PrefabEntityId& prefabEntryId,
NetEntityRole netEntityRole,
const AZ::Transform& transform
) = 0;
//! Creates new entities of the given archetype
//! This interface is internally used to spawn replicated entities
//! @param prefabEntryId the name of the spawnable to spawn
virtual EntityList CreateEntitiesImmediate
(
const PrefabEntityId& prefabEntryId,
NetEntityId netEntityId,
NetEntityRole netEntityRole,
AutoActivate autoActivate,
const AZ::Transform& transform
) = 0;
//! Returns an ConstEntityPtr for the provided entityId.
//! @param netEntityId the netEntityId to get an ConstEntityPtr for
@@ -134,25 +149,4 @@ namespace Multiplayer
//! @param entityRpcMessage the local rpc message to handle
virtual void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) = 0;
};
// Convenience helpers
inline INetworkEntityManager* GetNetworkEntityManager()
{
return AZ::Interface<INetworkEntityManager>::Get();
}
inline NetworkEntityTracker* GetNetworkEntityTracker()
{
return GetNetworkEntityManager()->GetNetworkEntityTracker();
}
inline NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker()
{
return GetNetworkEntityManager()->GetNetworkEntityAuthorityTracker();
}
inline MultiplayerComponentRegistry* GetMultiplayerComponentRegistry()
{
return GetNetworkEntityManager()->GetMultiplayerComponentRegistry();
}
}
+7 -27
View File
@@ -47,9 +47,6 @@ namespace Multiplayer
//! @return the hosts current timeMs
virtual AZ::TimeMs GetHostTimeMs() const = 0;
//! Synchronizes rewindable entity state for the current application time.
virtual void SyncRewindableEntityState() = 0;
//! Get the controlling connection that may be currently altering global game time.
//! Note this abstraction is required at a relatively high level to allow for 'don't rewind the shooter' semantics
//! @return the ConnectionId of the connection requesting the rewind operation
@@ -67,6 +64,13 @@ namespace Multiplayer
//! @param rewindConnectionId the rewinding ConnectionId
virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) = 0;
//! Syncs all entities contained within a volume to the current rewind state.
//! @param rewindVolume the volume to rewind entities within (needed for physics entities)
virtual void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) = 0;
//! Restores all rewound entities to the current application time.
virtual void ClearRewoundEntities() = 0;
AZ_DISABLE_COPY_MOVE(INetworkTime);
};
@@ -79,28 +83,4 @@ namespace Multiplayer
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
};
using INetworkTimeRequestBus = AZ::EBus<INetworkTime, INetworkTimeRequests>;
//! @class ScopedAlterTime
//! @brief This is a wrapper that temporarily adjusts global program time for backward reconciliation purposes.
class ScopedAlterTime final
{
public:
inline ScopedAlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId connectionId)
{
INetworkTime* time = AZ::Interface<INetworkTime>::Get();
m_previousHostFrameId = time->GetHostFrameId();
m_previousHostTimeMs = time->GetHostTimeMs();
m_previousRewindConnectionId = time->GetRewindingConnectionId();
time->AlterTime(frameId, timeMs, connectionId);
}
inline ~ScopedAlterTime()
{
INetworkTime* time = AZ::Interface<INetworkTime>::Get();
time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId);
}
private:
HostFrameId m_previousHostFrameId = InvalidHostFrameId;
AZ::TimeMs m_previousHostTimeMs = AZ::TimeMs{ 0 };
AzNetworking::ConnectionId m_previousRewindConnectionId = AzNetworking::InvalidConnectionId;
};
}
@@ -22,7 +22,7 @@ namespace {{ Namespace }}
void RegisterMultiplayerComponents()
{
Multiplayer::MultiplayerComponentRegistry* multiplayerComponentRegistry = GetMultiplayerComponentRegistry();
Multiplayer::MultiplayerStats& stats = AZ::Interface<Multiplayer::IMultiplayer>::Get()->GetStats();
Multiplayer::MultiplayerStats& stats = GetMultiplayer()->GetStats();
{% for Component in dataFiles %}
{% set ComponentName = Component.attrib['Name'] %}
{% set ComponentBaseName = ComponentName %}
@@ -476,7 +476,7 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
{% endcall %}
{% if networkPropertyCount.value > 0 %}
MultiplayerStats& stats = AZ::Interface<IMultiplayer>::Get()->GetStats();
MultiplayerStats& stats = GetMultiplayer()->GetStats();
// We modify the record if we are writing an update so that we don't notify for a change that really didn't change the value (just a duplicated send from the server)
[[maybe_unused]] bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject;
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %}
@@ -1141,16 +1141,23 @@ namespace {{ Component.attrib['Namespace'] }}
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<{{ ComponentName }}>("{{ ComponentName }}", "{{ Component.attrib['Description'] }}")
editContext->Class<{{ ComponentBaseName }}>("{{ ComponentBaseName }}", "{{ Component.attrib['Description'] }}")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Authority', ComponentName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Client', ComponentName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Autonomous', ComponentName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Autonomous', 'Authority', ComponentName)|indent(20) }}
{{ DefineArchetypePropertyEditReflection(Component, ComponentName)|indent(20) }};
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Authority', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(20) }}
{{ DefineArchetypePropertyEditReflection(Component, ComponentBaseName)|indent(20) }};
{% if ComponentDerived %}
editContext->Class<{{ ComponentName }}>("{{ ComponentName }}", "{{ Component.attrib['Description'] }}")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
{% endif %}
}
}
}
@@ -94,7 +94,7 @@ namespace Multiplayer
if (entityIsMigrating == EntityIsMigrating::True)
{
m_allowMigrateClientInput = true;
m_serverMigrateFrameId = AZ::Interface<INetworkTime>::Get()->GetHostFrameId();
m_serverMigrateFrameId = GetNetworkTime()->GetHostFrameId();
}
}
@@ -492,8 +492,8 @@ namespace Multiplayer
const uint32_t maxClientInputs = inputRate > 0.0 ? static_cast<uint32_t>(maxRewindHistory / inputRate) : 0;
INetworkTime* networkTime = AZ::Interface<INetworkTime>::Get();
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
IMultiplayer* multiplayer = GetMultiplayer();
INetworkTime* networkTime = GetNetworkTime();
while (m_moveAccumulator >= inputRate)
{
m_moveAccumulator -= inputRate;
@@ -23,6 +23,9 @@
#include <AzCore/Interface/Interface.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzFramework/Spawnable/Spawnable.h>
namespace AZ::ConsoleTypeHelpers
{
@@ -69,6 +72,7 @@ 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(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::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The default spawnable to use when a new player connects");
void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context)
{
@@ -411,6 +415,11 @@ namespace Multiplayer
void MultiplayerSystemComponent::OnConnect(AzNetworking::IConnection* connection)
{
MultiplayerAgentDatum datum;
datum.m_id = connection->GetConnectionId();
datum.m_isInvited = false;
datum.m_agentType = MultiplayerAgentType::Client;
if (connection->GetConnectionRole() == ConnectionRole::Connector)
{
AZLOG_INFO("New outgoing connection to remote address: %s", connection->GetRemoteAddress().GetString().c_str());
@@ -419,36 +428,45 @@ namespace Multiplayer
else
{
AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str());
MultiplayerAgentDatum datum;
datum.m_id = connection->GetConnectionId();
datum.m_isInvited = false;
datum.m_agentType = MultiplayerAgentType::Client;
m_connAcquiredEvent.Signal(datum);
}
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
if (m_onConnectFunctor)
{
// TODO: This needs to be set to the players autonomous proxy ------------v
NetworkEntityHandle controlledEntity = GetNetworkEntityTracker()->Get(NetEntityId{ 0 });
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));
}
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
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
{
connection->SetUserData(new ClientToServerConnectionData(connection, *this));
}
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()), 1);
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity());
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs);
NetworkEntityHandle controlledEntity;
if (entityList.size() > 0)
{
controlledEntity = entityList[0];
}
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));
}
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));
}
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs);
}
}
}
@@ -521,6 +539,11 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent);
}
void MultiplayerSystemComponent::SetOnConnectFunctor(const OnConnectFunctor& functor)
{
m_onConnectFunctor = functor;
}
void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates)
{
IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet();
@@ -542,6 +565,16 @@ namespace Multiplayer
}
}
INetworkTime* MultiplayerSystemComponent::GetNetworkTime()
{
return &m_networkTime;
}
INetworkEntityManager* MultiplayerSystemComponent::GetNetworkEntityManager()
{
return &m_networkEntityManager;
}
const char* MultiplayerSystemComponent::GetComponentGemName(NetComponentId netComponentId) const
{
return GetMultiplayerComponentRegistry()->GetComponentGemName(netComponentId);
@@ -89,8 +89,11 @@ namespace Multiplayer
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
void SetOnConnectFunctor(const OnConnectFunctor& functor) override;
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
AZ::TimeMs GetCurrentHostTimeMs() const override;
INetworkTime* GetNetworkTime() override;
INetworkEntityManager* GetNetworkEntityManager() override;
const char* GetComponentGemName(NetComponentId netComponentId) const override;
const char* GetComponentName(NetComponentId netComponentId) const override;
const char* GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const override;
@@ -121,6 +124,8 @@ namespace Multiplayer
SessionShutdownEvent m_shutdownEvent;
ConnectionAcquiredEvent m_connAcquiredEvent;
OnConnectFunctor m_onConnectFunctor = nullptr;
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId;
};
@@ -824,7 +824,7 @@ namespace Multiplayer
{
if (entityReplicator == nullptr)
{
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
IMultiplayer* multiplayer = GetMultiplayer();
AZLOG_INFO
(
"EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %u has already been deleted",
@@ -448,7 +448,7 @@ namespace Multiplayer
void EntityReplicator::DeferRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
{
// Received rpc metrics, log rpc sent, number of bytes, and the componentId/rpcId for bandwidth metrics
MultiplayerStats& stats = AZ::Interface<IMultiplayer>::Get()->GetStats();
MultiplayerStats& stats = GetMultiplayer()->GetStats();
stats.RecordRpcSent(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex(), entityRpcMessage.GetEstimatedSerializeSize());
m_replicationManager.AddDeferredRpcMessage(entityRpcMessage);
@@ -631,7 +631,7 @@ namespace Multiplayer
bool EntityReplicator::HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& entityRpcMessage)
{
// Received rpc metrics, log rpc received, time spent, number of bytes, and the componentId/rpcId for bandwidth metrics
MultiplayerStats& stats = AZ::Interface<IMultiplayer>::Get()->GetStats();
MultiplayerStats& stats = GetMultiplayer()->GetStats();
stats.RecordRpcReceived(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex(), entityRpcMessage.GetEstimatedSerializeSize());
if (!m_netBindComponent)
@@ -38,7 +38,6 @@ namespace Multiplayer
, m_onSpawnedHandler([this](AZ::Data::Asset<AzFramework::Spawnable> spawnable) { this->OnSpawned(spawnable); })
, m_onDespawnedHandler([this](AZ::Data::Asset<AzFramework::Spawnable> spawnable) { this->OnDespawned(spawnable); })
{
AZ::Interface<INetworkEntityManager>::Register(this);
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
AzFramework::SpawnableEntitiesInterface::Get()->AddOnSpawnedHandler(m_onSpawnedHandler);
@@ -48,7 +47,6 @@ namespace Multiplayer
NetworkEntityManager::~NetworkEntityManager()
{
AzFramework::RootSpawnableNotificationBus::Handler::BusDisconnect();
AZ::Interface<INetworkEntityManager>::Unregister(this);
}
void NetworkEntityManager::Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
@@ -365,9 +363,24 @@ namespace Multiplayer
return returnList;
}
INetworkEntityManager::EntityList NetworkEntityManager::CreateEntitiesImmediate(
const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole,
AutoActivate autoActivate, const AZ::Transform& transform)
INetworkEntityManager::EntityList NetworkEntityManager::CreateEntitiesImmediate
(
const PrefabEntityId& prefabEntryId,
NetEntityRole netEntityRole,
const AZ::Transform& transform
)
{
return CreateEntitiesImmediate(prefabEntryId, NextId(), netEntityRole, AutoActivate::Activate, transform);
}
INetworkEntityManager::EntityList NetworkEntityManager::CreateEntitiesImmediate
(
const PrefabEntityId& prefabEntryId,
NetEntityId netEntityId,
NetEntityRole netEntityRole,
AutoActivate autoActivate,
const AZ::Transform& transform
)
{
INetworkEntityManager::EntityList returnList;
@@ -436,7 +449,7 @@ namespace Multiplayer
void NetworkEntityManager::OnRootSpawnableAssigned(
[[maybe_unused]] AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, [[maybe_unused]] uint32_t generation)
{
auto* multiplayer = AZ::Interface<IMultiplayer>::Get();
auto* multiplayer = GetMultiplayer();
const auto agentType = multiplayer->GetAgentType();
if (agentType == MultiplayerAgentType::Client)
@@ -448,7 +461,7 @@ namespace Multiplayer
void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)
{
// TODO: Do we need to clear all entities here?
auto* multiplayer = AZ::Interface<IMultiplayer>::Get();
auto* multiplayer = GetMultiplayer();
const auto agentType = multiplayer->GetAgentType();
if (agentType == MultiplayerAgentType::Client)
@@ -494,7 +507,7 @@ namespace Multiplayer
return;
}
auto* multiplayer = AZ::Interface<IMultiplayer>::Get();
auto* multiplayer = GetMultiplayer();
const auto agentType = multiplayer->GetAgentType();
const bool spawnImmediately =
@@ -47,10 +47,20 @@ namespace Multiplayer
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override;
EntityList CreateEntitiesImmediate(const AzFramework::Spawnable& spawnable, NetEntityRole netEntityRole);
EntityList CreateEntitiesImmediate(
const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole,
AutoActivate autoActivate, const AZ::Transform& transform) override;
EntityList CreateEntitiesImmediate
(
const PrefabEntityId& prefabEntryId,
NetEntityRole netEntityRole,
const AZ::Transform& transform
) override;
EntityList CreateEntitiesImmediate
(
const PrefabEntityId& prefabEntryId,
NetEntityId netEntityId,
NetEntityRole netEntityRole,
AutoActivate autoActivate,
const AZ::Transform& transform
) override;
uint32_t GetEntityCount() const override;
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override;
@@ -81,7 +91,6 @@ namespace Multiplayer
private:
void RemoveEntities();
NetEntityId NextId();
void OnSpawned(AZ::Data::Asset<AzFramework::Spawnable> spawnable);
@@ -11,7 +11,7 @@
*/
#include <Source/NetworkInput/NetworkInputChild.h>
#include <Include/INetworkEntityManager.h>
#include <Include/IMultiplayer.h>
#include <AzNetworking/Serialization/ISerializer.h>
namespace Multiplayer
@@ -11,7 +11,7 @@
*/
#include <Source/NetworkInput/NetworkInputMigrationVector.h>
#include <Include/INetworkEntityManager.h>
#include <Include/IMultiplayer.h>
#include <AzNetworking/Serialization/ISerializer.h>
namespace Multiplayer
@@ -11,19 +11,12 @@
*/
#include <Source/NetworkTime/NetworkTime.h>
#include <Source/Components/NetBindComponent.h>
#include <Include/IMultiplayer.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
namespace Multiplayer
{
NetworkTime::NetworkTime()
{
AZ::Interface<INetworkTime>::Register(this);
}
NetworkTime::~NetworkTime()
{
AZ::Interface<INetworkTime>::Unregister(this);
}
bool NetworkTime::IsTimeRewound() const
{
return m_rewindingConnectionId != AzNetworking::InvalidConnectionId;
@@ -51,11 +44,6 @@ namespace Multiplayer
return m_hostTimeMs;
}
void NetworkTime::SyncRewindableEntityState()
{
}
AzNetworking::ConnectionId NetworkTime::GetRewindingConnectionId() const
{
return m_rewindingConnectionId;
@@ -72,4 +60,38 @@ namespace Multiplayer
m_hostTimeMs = timeMs;
m_rewindingConnectionId = rewindConnectionId;
}
void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume)
{
// TODO: extrude rewind volume for initial gather
AZStd::vector<AzFramework::VisibilityEntry*> gatheredEntries;
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(rewindVolume, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size());
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
{
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
{
// TODO: offset aabb for exact rewound position and check against the non-extruded rewind volume
gatheredEntries.push_back(visEntry);
}
}
});
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
[[maybe_unused]] NetBindComponent* entryNetBindComponent = entity->template FindComponent<NetBindComponent>();
if (entryNetBindComponent != nullptr)
{
// TODO: invoke the sync to rewind event on the netBindComponent and add the entity to the rewound entity set
}
}
}
void NetworkTime::ClearRewoundEntities()
{
AZ_Assert(!IsTimeRewound(), "Cannot clear rewound entity state while still within scoped rewind");
// TODO: iterate all rewound entities, signal them to sync rewind state, and clear the rewound entity set
}
}
@@ -23,8 +23,8 @@ namespace Multiplayer
: public INetworkTime
{
public:
NetworkTime();
virtual ~NetworkTime();
NetworkTime() = default;
virtual ~NetworkTime() = default;
//! INetworkTime overrides.
//! @{
@@ -33,10 +33,11 @@ namespace Multiplayer
HostFrameId GetUnalteredHostFrameId() const override;
void IncrementHostFrameId() override;
AZ::TimeMs GetHostTimeMs() const override;
void SyncRewindableEntityState() override;
AzNetworking::ConnectionId GetRewindingConnectionId() const override;
HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const override;
void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) override;
void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) override;
void ClearRewoundEntities() override;
//! @}
private:
@@ -47,7 +47,7 @@ namespace Multiplayer
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
inline RewindableObject<BASE_TYPE, REWIND_SIZE> &RewindableObject<BASE_TYPE, REWIND_SIZE>::operator =(const RewindableObject<BASE_TYPE, REWIND_SIZE>& rhs)
{
INetworkTime* networkTime = AZ::Interface<INetworkTime>::Get();
INetworkTime* networkTime = GetNetworkTime();
SetValueForTime(rhs.GetValueForTime(networkTime->GetHostFrameId()), GetCurrentTimeForProperty());
return *this;
}
@@ -115,7 +115,7 @@ namespace Multiplayer
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
inline HostFrameId RewindableObject<BASE_TYPE, REWIND_SIZE>::GetCurrentTimeForProperty() const
{
INetworkTime* networkTime = AZ::Interface<INetworkTime>::Get();
INetworkTime* networkTime = GetNetworkTime();
return networkTime->GetHostFrameIdForRewindingConnection(m_owningConnectionId);
}
@@ -65,7 +65,7 @@ namespace Multiplayer
{
AZ::Entity* entity = m_controlledEntity.GetEntity();
AZ_Assert(entity, "Invalid controlled entity provided to replication window");
m_controlledEntityTransform = entity->GetTransform();
m_controlledEntityTransform = entity ? entity->GetTransform() : nullptr;
AZ_Assert(m_controlledEntityTransform, "Controlled player entity must have a transform");
//// this one is optional
@@ -12,7 +12,7 @@
#pragma once
#include <Include/INetworkEntityManager.h>
#include <Include/IMultiplayer.h>
#include <Include/IReplicationWindow.h>
#include <Include/NetworkEntityHandle.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>