Some cleanup to better support backward reconciliation as well as dynamic player spawning on connect
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user