merging latest dev + resolving 1 conflict
Signed-off-by: Gene Walters <genewalt@amazon.com>
This commit is contained in:
@@ -179,6 +179,10 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_googletest(
|
||||
NAME Gem::Multiplayer.Tests
|
||||
)
|
||||
ly_add_googlebenchmark(
|
||||
NAME Gem::Multiplayer.Benchmarks
|
||||
TARGET Gem::Multiplayer.Tests
|
||||
)
|
||||
|
||||
if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
|
||||
@@ -403,6 +403,8 @@ namespace {{ Component.attrib['Namespace'] }}
|
||||
: public Multiplayer::MultiplayerController
|
||||
{
|
||||
public:
|
||||
using ComponentType = {{ ComponentName }};
|
||||
|
||||
{{ ControllerBaseName }}({{ ComponentName }}& owner);
|
||||
~{{ ControllerBaseName }}() override = default;
|
||||
|
||||
|
||||
+2
-2
@@ -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;
|
||||
@@ -201,6 +193,7 @@ namespace Multiplayer
|
||||
friend class EntityReplicationManager;
|
||||
|
||||
friend class HierarchyTests;
|
||||
friend class HierarchyBenchmarkBase;
|
||||
};
|
||||
|
||||
bool NetworkRoleHasController(NetEntityRole networkRole);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -96,5 +96,7 @@ namespace Multiplayer
|
||||
|
||||
//! Set to false when deactivating or otherwise not to be included in hierarchy considerations.
|
||||
bool m_isHierarchyEnabled = true;
|
||||
|
||||
friend class HierarchyBenchmarkBase;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Multiplayer
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
private:
|
||||
void OnTransformChangedEvent(const AZ::Transform& worldTm);
|
||||
void OnTransformChangedEvent(const AZ::Transform& localTm, const AZ::Transform& worldTm);
|
||||
void OnParentIdChangedEvent(AZ::EntityId oldParent, AZ::EntityId newParent);
|
||||
|
||||
AZ::TransformChangedEvent::Handler m_transformChangedHandler;
|
||||
|
||||
@@ -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
|
||||
@@ -50,5 +49,13 @@ namespace Multiplayer
|
||||
//! Sets the state of connection whether update messages can be sent or not.
|
||||
//! @param canSendUpdates the state value
|
||||
virtual void SetCanSendUpdates(bool canSendUpdates) = 0;
|
||||
|
||||
//! Fetches the state of connection whether handshake logic has completed
|
||||
//! @return true if handshake has completed
|
||||
virtual bool DidHandshake() const = 0;
|
||||
|
||||
//! Sets the state of connection whether handshake logic has completed
|
||||
//! @param didHandshake if handshake logic has completed
|
||||
virtual void SetDidHandshake(bool didHandshake) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 ServerAcceptanceReceivedEvent = AZ::Event<>;
|
||||
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
|
||||
@@ -79,26 +83,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;
|
||||
@@ -115,7 +135,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;
|
||||
|
||||
@@ -127,7 +158,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;
|
||||
@@ -146,9 +177,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}");
|
||||
|
||||
+17
-14
@@ -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;
|
||||
+2
-1
@@ -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,9 +13,11 @@
|
||||
#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>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
@@ -58,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())
|
||||
@@ -75,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())
|
||||
@@ -92,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())
|
||||
@@ -109,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())
|
||||
@@ -171,6 +173,8 @@ namespace Multiplayer
|
||||
{
|
||||
GetNetworkEntityManager()->NotifyControllersDeactivated(m_netEntityHandle, EntityIsMigrating::False);
|
||||
}
|
||||
|
||||
GetNetworkEntityTracker()->UnregisterNetBindComponent(this);
|
||||
}
|
||||
|
||||
NetEntityRole NetBindComponent::GetNetEntityRole() const
|
||||
@@ -390,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);
|
||||
}
|
||||
@@ -430,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);
|
||||
@@ -522,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())
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Multiplayer/Components/NetworkCharacterComponent.h>
|
||||
#include <Multiplayer/Components/NetworkRigidBodyComponent.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
|
||||
#include <AzFramework/Physics/CharacterBus.h>
|
||||
#include <AzFramework/Physics/Character.h>
|
||||
@@ -19,7 +20,7 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
|
||||
bool CollisionLayerBasedControllerFilter(const physx::PxController& controllerA, const physx::PxController& controllerB)
|
||||
{
|
||||
PHYSX_SCENE_READ_LOCK(controllerA.getActor()->getScene());
|
||||
@@ -82,7 +83,7 @@ namespace Multiplayer
|
||||
|
||||
return physx::PxQueryHitType::eNONE;
|
||||
}
|
||||
|
||||
|
||||
void NetworkCharacterComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
@@ -95,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); })
|
||||
{
|
||||
@@ -116,7 +128,7 @@ namespace Multiplayer
|
||||
callbackManager->SetObjectPreFilter(CollisionLayerBasedObjectPreFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!HasController())
|
||||
{
|
||||
GetNetworkTransformComponent()->TranslationAddEvent(m_translationEventHandler);
|
||||
@@ -134,7 +146,7 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::OnSyncRewind()
|
||||
{
|
||||
{
|
||||
if (m_physicsCharacter == nullptr)
|
||||
{
|
||||
return;
|
||||
@@ -195,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
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
void NetworkTransformComponent::NetworkTransformComponent::Reflect(AZ::ReflectContext* context)
|
||||
void NetworkTransformComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
@@ -43,6 +43,11 @@ namespace Multiplayer
|
||||
GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler);
|
||||
GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler);
|
||||
ParentEntityIdAddEvent(m_parentChangedEventHandler);
|
||||
|
||||
if (!HasController())
|
||||
{
|
||||
OnParentChanged(GetParentEntityId());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
@@ -77,9 +82,20 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetTransformComponent()->GetWorldTM().IsClose(blendTransform))
|
||||
AzFramework::TransformComponent* transformComponent = GetTransformComponent();
|
||||
if (GetParentEntityId() == InvalidNetEntityId)
|
||||
{
|
||||
GetTransformComponent()->SetWorldTM(blendTransform);
|
||||
if (!transformComponent->GetWorldTM().IsClose(blendTransform))
|
||||
{
|
||||
transformComponent->SetWorldTM(blendTransform);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!transformComponent->GetLocalTM().IsClose(blendTransform))
|
||||
{
|
||||
transformComponent->SetLocalTM(blendTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,31 +109,45 @@ namespace Multiplayer
|
||||
targetTransform.SetUniformScale(GetScale());
|
||||
|
||||
// Hard set the entities transform
|
||||
if (!GetTransformComponent()->GetWorldTM().IsClose(targetTransform))
|
||||
AzFramework::TransformComponent* transformComponent = GetTransformComponent();
|
||||
if (GetParentEntityId() == InvalidNetEntityId)
|
||||
{
|
||||
GetTransformComponent()->SetWorldTM(targetTransform);
|
||||
if (!transformComponent->GetWorldTM().IsClose(targetTransform))
|
||||
{
|
||||
transformComponent->SetWorldTM(targetTransform);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!transformComponent->GetLocalTM().IsClose(targetTransform))
|
||||
{
|
||||
transformComponent->SetLocalTM(targetTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnParentChanged(NetEntityId parentId)
|
||||
{
|
||||
const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId);
|
||||
if (parentEntityHandle.Exists())
|
||||
if (AZ::TransformInterface* transformComponent = GetEntity()->GetTransform())
|
||||
{
|
||||
if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity())
|
||||
const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId);
|
||||
if (parentEntityHandle.Exists())
|
||||
{
|
||||
GetEntity()->GetTransform()->SetParent(parentEntity->GetId());
|
||||
if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity())
|
||||
{
|
||||
transformComponent->SetParent(parentEntity->GetId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
transformComponent->SetParent(AZ::EntityId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetEntity()->GetTransform()->SetParent(AZ::EntityId());
|
||||
}
|
||||
}
|
||||
|
||||
NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent)
|
||||
: NetworkTransformComponentControllerBase(parent)
|
||||
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformChangedEvent(worldTm); })
|
||||
, m_transformChangedHandler([this](const AZ::Transform& localTm, const AZ::Transform& worldTm) { OnTransformChangedEvent(localTm, worldTm); })
|
||||
, m_parentIdChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId newParent) { OnParentIdChangedEvent(oldParent, newParent); })
|
||||
{
|
||||
;
|
||||
@@ -125,11 +155,14 @@ namespace Multiplayer
|
||||
|
||||
void NetworkTransformComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
GetParent().GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
OnTransformChangedEvent(GetParent().GetTransformComponent()->GetWorldTM());
|
||||
if (AzFramework::TransformComponent* parentTransform = GetParent().GetTransformComponent())
|
||||
{
|
||||
parentTransform->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
OnTransformChangedEvent(parentTransform->GetLocalTM(), parentTransform->GetWorldTM());
|
||||
|
||||
GetParent().GetTransformComponent()->BindParentChangedEventHandler(m_parentIdChangedHandler);
|
||||
OnParentIdChangedEvent(AZ::EntityId(), GetParent().GetTransformComponent()->GetParentId());
|
||||
parentTransform->BindParentChangedEventHandler(m_parentIdChangedHandler);
|
||||
OnParentIdChangedEvent(AZ::EntityId(), parentTransform->GetParentId());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
@@ -137,11 +170,12 @@ namespace Multiplayer
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkTransformComponentController::OnTransformChangedEvent(const AZ::Transform& worldTm)
|
||||
void NetworkTransformComponentController::OnTransformChangedEvent(const AZ::Transform& localTm, const AZ::Transform& worldTm)
|
||||
{
|
||||
SetRotation(worldTm.GetRotation());
|
||||
SetTranslation(worldTm.GetTranslation());
|
||||
SetScale(worldTm.GetUniformScale());
|
||||
const AZ::Transform& localOrWorld = GetParentEntityId() == InvalidNetEntityId ? worldTm : localTm;
|
||||
SetRotation(localOrWorld.GetRotation());
|
||||
SetTranslation(localOrWorld.GetTranslation());
|
||||
SetScale(localOrWorld.GetUniformScale());
|
||||
}
|
||||
|
||||
void NetworkTransformComponentController::OnParentIdChangedEvent([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent)
|
||||
@@ -150,7 +184,10 @@ namespace Multiplayer
|
||||
if (parentEntity)
|
||||
{
|
||||
const ConstNetworkEntityHandle parentHandle(parentEntity, GetNetworkEntityTracker());
|
||||
SetParentEntityId(parentHandle.GetNetEntityId());
|
||||
if (parentHandle.Exists())
|
||||
{
|
||||
SetParentEntityId(parentHandle.GetNetEntityId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +30,11 @@ 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;
|
||||
bool DidHandshake() const override;
|
||||
void SetDidHandshake(bool didHandshake) override;
|
||||
//! @}
|
||||
|
||||
const AZStd::string& GetProviderTicket() const;
|
||||
@@ -43,6 +45,7 @@ namespace Multiplayer
|
||||
AZStd::string m_providerTicket;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
bool m_canSendUpdates = true;
|
||||
bool m_didHandshake = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,14 @@ namespace Multiplayer
|
||||
{
|
||||
m_providerTicket = ticket;
|
||||
}
|
||||
|
||||
inline bool ClientToServerConnectionData::DidHandshake() const
|
||||
{
|
||||
return m_didHandshake;
|
||||
}
|
||||
|
||||
inline void ClientToServerConnectionData::SetDidHandshake(bool didHandshake)
|
||||
{
|
||||
m_didHandshake = didHandshake;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +30,11 @@ 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;
|
||||
bool DidHandshake() const override;
|
||||
void SetDidHandshake(bool didHandshake) override;
|
||||
//! @}
|
||||
|
||||
NetworkEntityHandle GetPrimaryPlayerEntity();
|
||||
@@ -42,7 +44,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;
|
||||
@@ -52,6 +54,7 @@ namespace Multiplayer
|
||||
AZStd::string m_providerTicket;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
bool m_canSendUpdates = false;
|
||||
bool m_didHandshake = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -38,4 +38,14 @@ namespace Multiplayer
|
||||
{
|
||||
m_providerTicket = ticket;
|
||||
}
|
||||
|
||||
inline bool ServerToClientConnectionData::DidHandshake() const
|
||||
{
|
||||
return m_didHandshake;
|
||||
}
|
||||
|
||||
inline void ServerToClientConnectionData::SetDidHandshake(bool didHandshake)
|
||||
{
|
||||
m_didHandshake = didHandshake;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Multiplayer
|
||||
MultiplayerEditorConnection();
|
||||
~MultiplayerEditorConnection() = default;
|
||||
|
||||
bool IsHandshakeComplete() const { return true; };
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerInit& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReady& packet);
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzFramework/Components/CameraBus.h>
|
||||
#include <AzFramework/Session/ISessionRequests.h>
|
||||
#include <AzFramework/Session/SessionConfig.h>
|
||||
@@ -71,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");
|
||||
@@ -79,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)
|
||||
{
|
||||
@@ -94,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>()
|
||||
@@ -112,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>();
|
||||
@@ -173,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()
|
||||
@@ -185,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
|
||||
@@ -195,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)
|
||||
@@ -208,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());
|
||||
@@ -310,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;
|
||||
|
||||
@@ -347,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++;
|
||||
@@ -394,6 +394,11 @@ namespace Multiplayer
|
||||
{
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
}
|
||||
|
||||
if (bg_multiplayerDebugDraw)
|
||||
{
|
||||
m_networkEntityManager.DebugDraw();
|
||||
}
|
||||
}
|
||||
|
||||
int MultiplayerSystemComponent::GetTickOrder()
|
||||
@@ -441,9 +446,9 @@ namespace Multiplayer
|
||||
MultiplayerPackets::SyncConsole m_syncPacket;
|
||||
};
|
||||
|
||||
bool MultiplayerSystemComponent::IsHandshakeComplete() const
|
||||
bool MultiplayerSystemComponent::IsHandshakeComplete(AzNetworking::IConnection* connection) const
|
||||
{
|
||||
return m_didHandshake;
|
||||
return reinterpret_cast<IConnectionData*>(connection->GetUserData())->DidHandshake();
|
||||
}
|
||||
|
||||
bool MultiplayerSystemComponent::HandleRequest
|
||||
@@ -459,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);
|
||||
@@ -468,9 +473,9 @@ 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;
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetDidHandshake(true);
|
||||
|
||||
// Sync our console
|
||||
ConsoleReplicator consoleReplicator(connection);
|
||||
@@ -487,11 +492,9 @@ namespace Multiplayer
|
||||
[[maybe_unused]] MultiplayerPackets::Accept& packet
|
||||
)
|
||||
{
|
||||
m_didHandshake = true;
|
||||
|
||||
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetDidHandshake(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());
|
||||
|
||||
@@ -608,7 +611,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
|
||||
@@ -642,7 +660,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
|
||||
@@ -656,27 +674,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));
|
||||
}
|
||||
}
|
||||
@@ -755,13 +760,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;
|
||||
@@ -780,14 +793,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::AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler)
|
||||
@@ -805,6 +838,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();
|
||||
@@ -851,6 +894,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();
|
||||
@@ -910,7 +963,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)
|
||||
@@ -918,7 +971,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);
|
||||
@@ -938,7 +991,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);
|
||||
@@ -997,7 +1050,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");
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Multiplayer
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
MultiplayerSystemComponent();
|
||||
~MultiplayerSystemComponent() override = default;
|
||||
~MultiplayerSystemComponent() override;
|
||||
|
||||
//! AZ::Component overrides.
|
||||
//! @{
|
||||
@@ -78,7 +78,7 @@ namespace Multiplayer
|
||||
int GetTickOrder() override;
|
||||
//! @}
|
||||
|
||||
bool IsHandshakeComplete() const;
|
||||
bool IsHandshakeComplete(AzNetworking::IConnection* connection) const;
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Connect& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Accept& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet);
|
||||
@@ -107,14 +107,20 @@ 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;
|
||||
void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::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;
|
||||
@@ -122,6 +128,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.
|
||||
@@ -151,9 +159,13 @@ namespace Multiplayer
|
||||
|
||||
SessionInitEvent m_initEvent;
|
||||
SessionShutdownEvent m_shutdownEvent;
|
||||
ConnectionAcquiredEvent m_connAcquiredEvent;
|
||||
ConnectionAcquiredEvent m_connectionAcquiredEvent;
|
||||
ServerAcceptanceReceivedEvent m_serverAcceptanceReceivedEvent;
|
||||
ClientDisconnectedEvent m_clientDisconnectedEvent;
|
||||
ClientMigrationStartEvent m_clientMigrationStartEvent;
|
||||
ClientMigrationEndEvent m_clientMigrationEndEvent;
|
||||
NotifyClientMigrationEvent m_notifyClientMigrationEvent;
|
||||
NotifyEntityMigrationEvent m_notifyEntityMigrationEvent;
|
||||
|
||||
AZStd::queue<AZStd::string> m_pendingConnectionTickets;
|
||||
|
||||
@@ -163,7 +175,7 @@ namespace Multiplayer
|
||||
double m_serverSendAccumulator = 0.0;
|
||||
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;
|
||||
|
||||
+146
-133
@@ -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()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
|
||||
@@ -37,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;
|
||||
};
|
||||
}
|
||||
|
||||
+44
-21
@@ -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
|
||||
{
|
||||
|
||||
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_BENCHMARK
|
||||
#include <CommonHierarchySetup.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Console/Console.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class BenchmarkComponentApplicationRequests : public AZ::ComponentApplicationRequests
|
||||
{
|
||||
public:
|
||||
void RegisterComponentDescriptor([[maybe_unused]] const AZ::ComponentDescriptor* descriptor) override {}
|
||||
void UnregisterComponentDescriptor([[maybe_unused]] const AZ::ComponentDescriptor* descriptor) override {}
|
||||
AZ::ComponentApplication* GetApplication() override { return {}; }
|
||||
void RegisterEntityAddedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
|
||||
void RegisterEntityRemovedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
|
||||
void RegisterEntityActivatedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
|
||||
void RegisterEntityDeactivatedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
|
||||
void SignalEntityActivated([[maybe_unused]] AZ::Entity* entity) override {}
|
||||
void SignalEntityDeactivated([[maybe_unused]] AZ::Entity* entity) override {}
|
||||
bool RemoveEntity([[maybe_unused]] AZ::Entity* entity) override { return {}; }
|
||||
bool DeleteEntity([[maybe_unused]] const AZ::EntityId& id) override { return {}; }
|
||||
void EnumerateEntities([[maybe_unused]] const EntityCallback& callback) override {}
|
||||
AZ::SerializeContext* GetSerializeContext() override { return {}; }
|
||||
AZ::BehaviorContext* GetBehaviorContext() override { return {}; }
|
||||
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return {}; }
|
||||
const char* GetAppRoot() const override { return {}; }
|
||||
const char* GetEngineRoot() const override { return {}; }
|
||||
const char* GetExecutableFolder() const override { return {}; }
|
||||
void QueryApplicationType([[maybe_unused]] AZ::ApplicationTypeQuery& appType) const override {}
|
||||
|
||||
AZStd::map<AZ::EntityId, AZ::Entity*> m_entities;
|
||||
|
||||
bool AddEntity(AZ::Entity* entity) override
|
||||
{
|
||||
m_entities[entity->GetId()] = entity;
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::Entity* FindEntity(const AZ::EntityId& id) override
|
||||
{
|
||||
const auto iterator = m_entities.find(id);
|
||||
if (iterator != m_entities.end())
|
||||
{
|
||||
return iterator->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BenchmarkConnectionListener : public AzNetworking::IConnectionListener
|
||||
{
|
||||
public:
|
||||
ConnectResult ValidateConnect([[maybe_unused]] const IpAddress& remoteAddress, [[maybe_unused]] const IPacketHeader& packetHeader, [[maybe_unused]] ISerializer& serializer) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void OnConnect([[maybe_unused]] IConnection* connection) override
|
||||
{
|
||||
}
|
||||
|
||||
PacketDispatchResult OnPacketReceived([[maybe_unused]] IConnection* connection, [[maybe_unused]] const IPacketHeader& packetHeader, [[maybe_unused]] ISerializer& serializer) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void OnPacketLost([[maybe_unused]] IConnection* connection, [[maybe_unused]] PacketId packetId) override
|
||||
{
|
||||
}
|
||||
|
||||
void OnDisconnect([[maybe_unused]] IConnection* connection, [[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class BenchmarkTime : public AZ::ITime
|
||||
{
|
||||
public:
|
||||
AZ::TimeMs GetElapsedTimeMs() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
AZ::TimeUs GetElapsedTimeUs() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
class BenchmarkNetworkTime : public Multiplayer::INetworkTime
|
||||
{
|
||||
public:
|
||||
bool IsTimeRewound() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HostFrameId GetHostFrameId() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
HostFrameId GetUnalteredHostFrameId() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void IncrementHostFrameId() override
|
||||
{
|
||||
}
|
||||
|
||||
AZ::TimeMs GetHostTimeMs() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
float GetHostBlendFactor() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AzNetworking::ConnectionId GetRewindingConnectionId() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void ForceSetTime([[maybe_unused]] HostFrameId frameId, [[maybe_unused]] AZ::TimeMs timeMs) override
|
||||
{
|
||||
}
|
||||
|
||||
void SyncEntitiesToRewindState([[maybe_unused]] const AZ::Aabb& rewindVolume) override
|
||||
{
|
||||
}
|
||||
|
||||
void ClearRewoundEntities() override
|
||||
{
|
||||
}
|
||||
|
||||
void AlterTime([[maybe_unused]] HostFrameId frameId, [[maybe_unused]] AZ::TimeMs timeMs, [[maybe_unused]] float blendFactor, [[maybe_unused]] AzNetworking::ConnectionId rewindConnectionId) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class BenchmarkMultiplayerConnection : public IConnection
|
||||
{
|
||||
public:
|
||||
BenchmarkMultiplayerConnection(ConnectionId connectionId, const IpAddress& address, [[maybe_unused]] ConnectionRole connectionRole)
|
||||
: IConnection(connectionId, address)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
~BenchmarkMultiplayerConnection() override = default;
|
||||
|
||||
bool SendReliablePacket([[maybe_unused]] const IPacket& packet) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PacketId SendUnreliablePacket([[maybe_unused]] const IPacket& packet) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool WasPacketAcked([[maybe_unused]] PacketId packetId) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectionState GetConnectionState() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
ConnectionRole GetConnectionRole() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Disconnect([[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetConnectionMtu([[maybe_unused]] uint32_t connectionMtu) override
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t GetConnectionMtu() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class BenchmarkNetworkEntityManager : public Multiplayer::INetworkEntityManager
|
||||
{
|
||||
public:
|
||||
BenchmarkNetworkEntityManager() : m_authorityTracker(*this) {}
|
||||
|
||||
NetworkEntityTracker* GetNetworkEntityTracker() override { return &m_tracker; }
|
||||
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override { return &m_authorityTracker; }
|
||||
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override { return &m_multiplayerComponentRegistry; }
|
||||
const HostId& GetHostId() const override { return m_hostId; }
|
||||
EntityList CreateEntitiesImmediate(
|
||||
[[maybe_unused]] const PrefabEntityId& prefabEntryId,
|
||||
[[maybe_unused]] NetEntityRole netEntityRole,
|
||||
[[maybe_unused]] const AZ::Transform& transform,
|
||||
[[maybe_unused]] AutoActivate autoActivate) override {
|
||||
return {};
|
||||
}
|
||||
EntityList CreateEntitiesImmediate(
|
||||
[[maybe_unused]] const PrefabEntityId& prefabEntryId,
|
||||
[[maybe_unused]] NetEntityId netEntityId,
|
||||
[[maybe_unused]] NetEntityRole netEntityRole,
|
||||
[[maybe_unused]] AutoActivate autoActivate,
|
||||
[[maybe_unused]] const AZ::Transform& transform) override {
|
||||
return {};
|
||||
}
|
||||
void SetupNetEntity(
|
||||
[[maybe_unused]] AZ::Entity* netEntity,
|
||||
[[maybe_unused]] PrefabEntityId prefabEntityId,
|
||||
[[maybe_unused]] NetEntityRole netEntityRole) override {}
|
||||
uint32_t GetEntityCount() const override { return {}; }
|
||||
void MarkForRemoval(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
|
||||
bool IsMarkedForRemoval(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const override {
|
||||
return {};
|
||||
}
|
||||
void ClearEntityFromRemovalList(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
|
||||
void ClearAllEntities() override {}
|
||||
void AddEntityMarkedDirtyHandler(
|
||||
[[maybe_unused]] AZ::Event<>::Handler& entityMarkedDirtyHandle) override {}
|
||||
void AddEntityNotifyChangesHandler(
|
||||
[[maybe_unused]] AZ::Event<>::Handler& entityNotifyChangesHandle) override {}
|
||||
void AddEntityExitDomainHandler(
|
||||
[[maybe_unused]] EntityExitDomainEvent::Handler& entityExitDomainHandler) override {}
|
||||
void AddControllersActivatedHandler(
|
||||
[[maybe_unused]] ControllersActivatedEvent::Handler& controllersActivatedHandler) override {}
|
||||
void AddControllersDeactivatedHandler(
|
||||
[[maybe_unused]] ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) override {}
|
||||
void NotifyEntitiesDirtied() override {}
|
||||
void NotifyEntitiesChanged() override {}
|
||||
void NotifyControllersActivated(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
|
||||
[[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
|
||||
void NotifyControllersDeactivated(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
|
||||
[[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
|
||||
void HandleLocalRpcMessage(
|
||||
[[maybe_unused]] NetworkEntityRpcMessage& message) override {}
|
||||
|
||||
mutable AZStd::map<NetEntityId, AZ::Entity*> m_networkEntityMap;
|
||||
|
||||
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override
|
||||
{
|
||||
m_networkEntityMap[netEntityId] = entity;
|
||||
return NetworkEntityHandle(entity, &m_tracker);
|
||||
}
|
||||
|
||||
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override
|
||||
{
|
||||
AZ::Entity* entity = m_networkEntityMap[netEntityId];
|
||||
return ConstNetworkEntityHandle(entity, &m_tracker);
|
||||
}
|
||||
|
||||
NetEntityId GetNetEntityIdById(const AZ::EntityId& entityId) const override
|
||||
{
|
||||
for (const auto& pair : m_networkEntityMap)
|
||||
{
|
||||
if (pair.second->GetId() == entityId)
|
||||
{
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
return InvalidNetEntityId;
|
||||
}
|
||||
|
||||
[[nodiscard]] AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
|
||||
[[maybe_unused]] const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable,
|
||||
[[maybe_unused]] const AZ::Transform& transform) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void Initialize([[maybe_unused]] const HostId& hostId, [[maybe_unused]] AZStd::unique_ptr<IEntityDomain> entityDomain) override {}
|
||||
bool IsInitialized() const override { return true; }
|
||||
IEntityDomain* GetEntityDomain() const override { return nullptr; }
|
||||
void DebugDraw() const override {}
|
||||
|
||||
NetworkEntityTracker m_tracker;
|
||||
NetworkEntityAuthorityTracker m_authorityTracker;
|
||||
MultiplayerComponentRegistry m_multiplayerComponentRegistry;
|
||||
HostId m_hostId;
|
||||
};
|
||||
|
||||
class BenchmarkMultiplayer : public Multiplayer::IMultiplayer
|
||||
{
|
||||
public:
|
||||
BenchmarkMultiplayer(BenchmarkNetworkEntityManager& manager) : m_manager(manager) {}
|
||||
|
||||
MultiplayerAgentType GetAgentType() const override { return {}; }
|
||||
void InitializeMultiplayer([[maybe_unused]] MultiplayerAgentType state) override {}
|
||||
bool StartHosting([[maybe_unused]] uint16_t port, [[maybe_unused]] bool isDedicated) override { return {}; }
|
||||
bool Connect([[maybe_unused]] const AZStd::string& remoteAddress, [[maybe_unused]] uint16_t port) override { return {}; }
|
||||
void Terminate([[maybe_unused]] AzNetworking::DisconnectReason reason) override {}
|
||||
void AddClientDisconnectedHandler([[maybe_unused]] ClientDisconnectedEvent::Handler& handler) override {}
|
||||
void AddConnectionAcquiredHandler([[maybe_unused]] ConnectionAcquiredEvent::Handler& handler) override {}
|
||||
void AddSessionInitHandler([[maybe_unused]] SessionInitEvent::Handler& handler) override {}
|
||||
void AddSessionShutdownHandler([[maybe_unused]] SessionShutdownEvent::Handler& handler) override {}
|
||||
void SendReadyForEntityUpdates([[maybe_unused]] bool readyForEntityUpdates) override {}
|
||||
AZ::TimeMs GetCurrentHostTimeMs() const override { return {}; }
|
||||
float GetCurrentBlendFactor() const override { return {}; }
|
||||
INetworkTime* GetNetworkTime() override { return {}; }
|
||||
INetworkEntityManager* GetNetworkEntityManager() override { return &m_manager; }
|
||||
void SetFilterEntityManager([[maybe_unused]] IFilterEntityManager* entityFilter) override {}
|
||||
IFilterEntityManager* GetFilterEntityManager() override { return {}; }
|
||||
void AddClientMigrationStartEventHandler([[maybe_unused]] ClientMigrationStartEvent::Handler& handler) override {}
|
||||
void AddClientMigrationEndEventHandler([[maybe_unused]] ClientMigrationEndEvent::Handler& handler) override {}
|
||||
void AddNotifyClientMigrationHandler([[maybe_unused]] NotifyClientMigrationEvent::Handler& handler) override {}
|
||||
void AddNotifyEntityMigrationEventHandler([[maybe_unused]] NotifyEntityMigrationEvent::Handler& handler) override {}
|
||||
void SendNotifyClientMigrationEvent([[maybe_unused]] const HostId& hostId, [[maybe_unused]] uint64_t userIdentifier, [[maybe_unused]] ClientInputId lastClientInputId) override {}
|
||||
void SendNotifyEntityMigrationEvent([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] const HostId& remoteHostId) override {}
|
||||
void SetShouldSpawnNetworkEntities([[maybe_unused]] bool value) override {}
|
||||
bool GetShouldSpawnNetworkEntities() const override { return true; }
|
||||
|
||||
BenchmarkNetworkEntityManager& m_manager;
|
||||
};
|
||||
|
||||
class HierarchyBenchmarkBase
|
||||
: public benchmark::Fixture
|
||||
, public AllocatorsBase
|
||||
{
|
||||
public:
|
||||
void SetUp(const benchmark::State&) override
|
||||
{
|
||||
internalSetUp();
|
||||
}
|
||||
void SetUp(benchmark::State&) override
|
||||
{
|
||||
internalSetUp();
|
||||
}
|
||||
|
||||
void TearDown(const benchmark::State&) override
|
||||
{
|
||||
internalTearDown();
|
||||
}
|
||||
void TearDown(benchmark::State&) override
|
||||
{
|
||||
internalTearDown();
|
||||
}
|
||||
|
||||
virtual void internalSetUp()
|
||||
{
|
||||
SetupAllocator();
|
||||
AZ::NameDictionary::Create();
|
||||
|
||||
m_ComponentApplicationRequests = AZStd::make_unique<BenchmarkComponentApplicationRequests>();
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Register(m_ComponentApplicationRequests.get());
|
||||
|
||||
// register components involved in testing
|
||||
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
|
||||
|
||||
m_transformDescriptor.reset(AzFramework::TransformComponent::CreateDescriptor());
|
||||
m_transformDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_netBindDescriptor.reset(NetBindComponent::CreateDescriptor());
|
||||
m_netBindDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_hierarchyRootDescriptor.reset(NetworkHierarchyRootComponent::CreateDescriptor());
|
||||
m_hierarchyRootDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_hierarchyChildDescriptor.reset(NetworkHierarchyChildComponent::CreateDescriptor());
|
||||
m_hierarchyChildDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor());
|
||||
m_netTransformDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_NetworkEntityManager = AZStd::make_unique<BenchmarkNetworkEntityManager>();
|
||||
|
||||
m_Multiplayer = AZStd::make_unique<BenchmarkMultiplayer>(*m_NetworkEntityManager);
|
||||
AZ::Interface<IMultiplayer>::Register(m_Multiplayer.get());
|
||||
|
||||
// Create space for replication stats
|
||||
// Without Multiplayer::RegisterMultiplayerComponents() the stats go to invalid id, which is fine for unit tests
|
||||
GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0);
|
||||
|
||||
m_Time = AZStd::make_unique<BenchmarkTime>();
|
||||
AZ::Interface<AZ::ITime>::Register(m_Time.get());
|
||||
|
||||
m_NetworkTime = AZStd::make_unique<BenchmarkNetworkTime>();
|
||||
AZ::Interface<INetworkTime>::Register(m_NetworkTime.get());
|
||||
|
||||
EXPECT_NE(AZ::Interface<IMultiplayer>::Get()->GetNetworkEntityManager(), nullptr);
|
||||
|
||||
const IpAddress address("localhost", 1, ProtocolType::Udp);
|
||||
m_Connection = AZStd::make_unique<BenchmarkMultiplayerConnection>(ConnectionId{ 1 }, address, ConnectionRole::Connector);
|
||||
m_ConnectionListener = AZStd::make_unique<BenchmarkConnectionListener>();
|
||||
|
||||
m_entityReplicationManager = AZStd::make_unique<EntityReplicationManager>(*m_Connection, *m_ConnectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer);
|
||||
|
||||
m_console.reset(aznew AZ::Console());
|
||||
AZ::Interface<AZ::IConsole>::Register(m_console.get());
|
||||
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
|
||||
|
||||
RegisterMultiplayerComponents();
|
||||
}
|
||||
|
||||
virtual void internalTearDown()
|
||||
{
|
||||
AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
|
||||
m_console.reset();
|
||||
|
||||
m_entityReplicationManager.reset();
|
||||
|
||||
m_Connection.reset();
|
||||
m_ConnectionListener.reset();
|
||||
|
||||
AZ::Interface<INetworkTime>::Unregister(m_NetworkTime.get());
|
||||
AZ::Interface<AZ::ITime>::Unregister(m_Time.get());
|
||||
AZ::Interface<IMultiplayer>::Unregister(m_Multiplayer.get());
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(m_ComponentApplicationRequests.get());
|
||||
|
||||
m_Time.reset();
|
||||
|
||||
m_NetworkEntityManager.reset();
|
||||
m_Multiplayer.reset();
|
||||
|
||||
m_transformDescriptor.reset();
|
||||
m_netTransformDescriptor.reset();
|
||||
m_hierarchyRootDescriptor.reset();
|
||||
m_hierarchyChildDescriptor.reset();
|
||||
m_netBindDescriptor.reset();
|
||||
m_serializeContext.reset();
|
||||
m_ComponentApplicationRequests.reset();
|
||||
|
||||
AZ::NameDictionary::Destroy();
|
||||
TeardownAllocator();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::IConsole> m_console;
|
||||
|
||||
AZStd::unique_ptr<BenchmarkComponentApplicationRequests> m_ComponentApplicationRequests;
|
||||
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_transformDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_netBindDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyRootDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyChildDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_netTransformDescriptor;
|
||||
|
||||
AZStd::unique_ptr<BenchmarkMultiplayer> m_Multiplayer;
|
||||
AZStd::unique_ptr<BenchmarkNetworkEntityManager> m_NetworkEntityManager;
|
||||
AZStd::unique_ptr<BenchmarkTime> m_Time;
|
||||
AZStd::unique_ptr<BenchmarkNetworkTime> m_NetworkTime;
|
||||
|
||||
AZStd::unique_ptr<BenchmarkMultiplayerConnection> m_Connection;
|
||||
AZStd::unique_ptr<BenchmarkConnectionListener> m_ConnectionListener;
|
||||
|
||||
AZStd::unique_ptr<EntityReplicationManager> m_entityReplicationManager;
|
||||
|
||||
void SetupEntity(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId netId, NetEntityRole role)
|
||||
{
|
||||
const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
|
||||
EXPECT_NE(netBindComponent, nullptr);
|
||||
netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role);
|
||||
entity->Init();
|
||||
}
|
||||
|
||||
static void StopEntity(const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
|
||||
EXPECT_NE(netBindComponent, nullptr);
|
||||
netBindComponent->StopEntity();
|
||||
}
|
||||
|
||||
static void StopAndDeactivateEntity(AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
if (entity)
|
||||
{
|
||||
StopEntity(entity);
|
||||
entity->Deactivate();
|
||||
entity.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateEntityWithRootHierarchy(AZStd::unique_ptr<AZ::Entity>& rootEntity)
|
||||
{
|
||||
rootEntity->CreateComponent<AzFramework::TransformComponent>();
|
||||
rootEntity->CreateComponent<NetBindComponent>();
|
||||
rootEntity->CreateComponent<NetworkTransformComponent>();
|
||||
rootEntity->CreateComponent<NetworkHierarchyRootComponent>();
|
||||
}
|
||||
|
||||
void CreateEntityWithChildHierarchy(AZStd::unique_ptr<AZ::Entity>& childEntity)
|
||||
{
|
||||
childEntity->CreateComponent<AzFramework::TransformComponent>();
|
||||
childEntity->CreateComponent<NetBindComponent>();
|
||||
childEntity->CreateComponent<NetworkTransformComponent>();
|
||||
childEntity->CreateComponent<NetworkHierarchyChildComponent>();
|
||||
}
|
||||
|
||||
void SetParentIdOnNetworkTransform(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId netParentId)
|
||||
{
|
||||
/* Derived from NetworkTransformComponent.AutoComponent.xml */
|
||||
constexpr int totalBits = 6 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::Count*/;
|
||||
constexpr int parentIdBit = 4 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::parentEntityId_DirtyFlag*/;
|
||||
|
||||
ReplicationRecord currentRecord;
|
||||
currentRecord.m_authorityToClient.AddBits(totalBits);
|
||||
currentRecord.m_authorityToClient.SetBit(parentIdBit, true);
|
||||
|
||||
constexpr uint32_t bufferSize = 100;
|
||||
AZStd::array<uint8_t, bufferSize> buffer = {};
|
||||
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
|
||||
inSerializer.Serialize(reinterpret_cast<uint32_t&>(netParentId),
|
||||
"parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */
|
||||
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
ReplicationRecord notifyRecord = currentRecord;
|
||||
entity->FindComponent<NetworkTransformComponent>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
|
||||
entity->FindComponent<NetworkTransformComponent>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId value)
|
||||
{
|
||||
/* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */
|
||||
constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/;
|
||||
constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/;
|
||||
|
||||
ReplicationRecord currentRecord;
|
||||
currentRecord.m_authorityToClient.AddBits(totalBits);
|
||||
currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true);
|
||||
|
||||
constexpr uint32_t bufferSize = 100;
|
||||
AZStd::array<uint8_t, bufferSize> buffer = {};
|
||||
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
|
||||
inSerializer.Serialize(reinterpret_cast<uint32_t&>(value),
|
||||
"hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */
|
||||
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
ReplicationRecord notifyRecord = currentRecord;
|
||||
entity->FindComponent<Component>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
|
||||
entity->FindComponent<Component>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
struct EntityInfo
|
||||
{
|
||||
enum class Role
|
||||
{
|
||||
Root,
|
||||
Child,
|
||||
None
|
||||
};
|
||||
|
||||
EntityInfo(AZ::u64 entityId, const char* entityName, NetEntityId netId, Role role)
|
||||
: m_entity(AZStd::make_unique<AZ::Entity>(AZ::EntityId(entityId), entityName))
|
||||
, m_netId(netId)
|
||||
, m_role(role)
|
||||
{
|
||||
}
|
||||
|
||||
~EntityInfo()
|
||||
{
|
||||
StopAndDeactivateEntity(m_entity);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> m_entity;
|
||||
NetEntityId m_netId;
|
||||
AZStd::unique_ptr<EntityReplicator> m_replicator;
|
||||
Role m_role = Role::None;
|
||||
};
|
||||
|
||||
void PopulateHierarchicalEntity(const EntityInfo& entityInfo)
|
||||
{
|
||||
entityInfo.m_entity->CreateComponent<AzFramework::TransformComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetBindComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetworkTransformComponent>();
|
||||
switch (entityInfo.m_role)
|
||||
{
|
||||
case EntityInfo::Role::Root:
|
||||
entityInfo.m_entity->CreateComponent<NetworkHierarchyRootComponent>();
|
||||
break;
|
||||
case EntityInfo::Role::Child:
|
||||
entityInfo.m_entity->CreateComponent<NetworkHierarchyChildComponent>();
|
||||
break;
|
||||
case EntityInfo::Role::None:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateParent(EntityInfo& parent)
|
||||
{
|
||||
PopulateHierarchicalEntity(parent);
|
||||
|
||||
SetupEntity(parent.m_entity, parent.m_netId, NetEntityRole::Authority);
|
||||
|
||||
// Create an entity replicator for the child entity
|
||||
const NetworkEntityHandle childHandle(parent.m_entity.get(), m_NetworkEntityManager->GetNetworkEntityTracker());
|
||||
parent.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_Connection.get(), NetEntityRole::Client, childHandle);
|
||||
parent.m_replicator->Initialize(childHandle);
|
||||
|
||||
parent.m_entity->Activate();
|
||||
}
|
||||
|
||||
void CreateChildForParent(EntityInfo& child, EntityInfo& parent)
|
||||
{
|
||||
PopulateHierarchicalEntity(child);
|
||||
|
||||
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority);
|
||||
|
||||
// we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
|
||||
SetParentIdOnNetworkTransform(child.m_entity, parent.m_netId);
|
||||
|
||||
// Create an entity replicator for the child entity
|
||||
const NetworkEntityHandle childHandle(child.m_entity.get(), m_NetworkEntityManager->GetNetworkEntityTracker());
|
||||
child.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_Connection.get(), NetEntityRole::Client, childHandle);
|
||||
child.m_replicator->Initialize(childHandle);
|
||||
|
||||
child.m_entity->Activate();
|
||||
}
|
||||
|
||||
void ForceRebuildHierarchy(const AZStd::unique_ptr<AZ::Entity>& rootEntity)
|
||||
{
|
||||
if (NetworkHierarchyRootComponent* root = rootEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
root->RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <MockInterfaces.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Console/Console.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
@@ -25,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
|
||||
{
|
||||
@@ -205,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
|
||||
@@ -248,17 +249,19 @@ namespace Multiplayer
|
||||
|
||||
void SetupEntity(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId netId, NetEntityRole role)
|
||||
{
|
||||
const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
|
||||
EXPECT_NE(netBindComponent, nullptr);
|
||||
netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role);
|
||||
entity->Init();
|
||||
if (const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>())
|
||||
{
|
||||
netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role);
|
||||
entity->Init();
|
||||
}
|
||||
}
|
||||
|
||||
static void StopEntity(const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
|
||||
EXPECT_NE(netBindComponent, nullptr);
|
||||
netBindComponent->StopEntity();
|
||||
if (const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>())
|
||||
{
|
||||
netBindComponent->StopEntity();
|
||||
}
|
||||
}
|
||||
|
||||
static void StopAndDeactivateEntity(AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
@@ -311,6 +314,29 @@ namespace Multiplayer
|
||||
entity->FindComponent<NetworkTransformComponent>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
void SetTranslationOnNetworkTransform(const AZStd::unique_ptr<AZ::Entity>& entity, AZ::Vector3 translation)
|
||||
{
|
||||
/* Derived from NetworkTransformComponent.AutoComponent.xml */
|
||||
constexpr int totalBits = 6 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::Count*/;
|
||||
constexpr int translationBit = 1 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::translation_DirtyFlag*/;
|
||||
|
||||
ReplicationRecord currentRecord;
|
||||
currentRecord.m_authorityToClient.AddBits(totalBits);
|
||||
currentRecord.m_authorityToClient.SetBit(translationBit, true);
|
||||
|
||||
constexpr uint32_t bufferSize = 100;
|
||||
AZStd::array<uint8_t, bufferSize> buffer = {};
|
||||
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
|
||||
static_cast<ISerializer*>(&inSerializer)->Serialize(translation,
|
||||
"translation" /* Derived from NetworkTransformComponent.AutoComponent.xml */);
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
ReplicationRecord notifyRecord = currentRecord;
|
||||
entity->FindComponent<NetworkTransformComponent>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
|
||||
entity->FindComponent<NetworkTransformComponent>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId value)
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
};
|
||||
|
||||
|
||||
@@ -95,8 +95,9 @@ namespace UnitTest
|
||||
|
||||
TEST_F(MultiplayerSystemTests, TestConnectionDatum)
|
||||
{
|
||||
IMultiplayerConnectionMock connMock1 = IMultiplayerConnectionMock(aznumeric_cast<AzNetworking::ConnectionId>(10), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Acceptor);
|
||||
IMultiplayerConnectionMock connMock2 = IMultiplayerConnectionMock(aznumeric_cast<AzNetworking::ConnectionId>(15), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Acceptor);
|
||||
using namespace testing;
|
||||
NiceMock<IMultiplayerConnectionMock> connMock1(aznumeric_cast<AzNetworking::ConnectionId>(10), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Acceptor);
|
||||
NiceMock<IMultiplayerConnectionMock> connMock2(aznumeric_cast<AzNetworking::ConnectionId>(15), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Acceptor);
|
||||
m_mpComponent->OnConnect(&connMock1);
|
||||
m_mpComponent->OnConnect(&connMock2);
|
||||
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* 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 <CommonHierarchySetup.h>
|
||||
#include <MockInterfaces.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Console/Console.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/UnitTest/UnitTest.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
using namespace testing;
|
||||
using namespace ::UnitTest;
|
||||
|
||||
/*
|
||||
* (Networked) Parent -> (Networked) Child
|
||||
*/
|
||||
class ServerNetTransformTests : public HierarchyTests
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
HierarchyTests::SetUp();
|
||||
|
||||
m_root = AZStd::make_unique<EntityInfo>(1, "root", NetEntityId{ 1 }, EntityInfo::Role::Root);
|
||||
m_child = AZStd::make_unique<EntityInfo>(2, "child", NetEntityId{ 2 }, EntityInfo::Role::Child);
|
||||
|
||||
CreateNetworkParentChild(*m_root, *m_child);
|
||||
|
||||
AZ::Transform rootTransform = AZ::Transform::CreateIdentity();
|
||||
rootTransform.SetTranslation(AZ::Vector3::CreateOne());
|
||||
m_root->m_entity->FindComponent<AzFramework::TransformComponent>()->SetWorldTM(rootTransform);
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetWorldTM(rootTransform);
|
||||
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_root->m_entity->GetId());
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetLocalTM(AZ::Transform::CreateIdentity());
|
||||
|
||||
AZ::EntityBus::Broadcast(&AZ::EntityBus::Events::OnEntityActivated, m_root->m_entity->GetId());
|
||||
|
||||
MultiplayerTick();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_child.reset();
|
||||
m_root.reset();
|
||||
|
||||
HierarchyTests::TearDown();
|
||||
}
|
||||
|
||||
void PopulateNetworkEntity(const EntityInfo& entityInfo)
|
||||
{
|
||||
entityInfo.m_entity->CreateComponent<AzFramework::TransformComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetBindComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetworkTransformComponent>();
|
||||
}
|
||||
|
||||
void CreateNetworkParentChild(EntityInfo& root, EntityInfo& child)
|
||||
{
|
||||
PopulateNetworkEntity(root);
|
||||
SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority);
|
||||
|
||||
PopulateNetworkEntity(child);
|
||||
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority);
|
||||
|
||||
// Create an entity replicator for the child entity
|
||||
const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get());
|
||||
child.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle);
|
||||
child.m_replicator->Initialize(childHandle);
|
||||
|
||||
// Create an entity replicator for the root entity
|
||||
const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get());
|
||||
root.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle);
|
||||
root.m_replicator->Initialize(rootHandle);
|
||||
|
||||
root.m_entity->Activate();
|
||||
child.m_entity->Activate();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<EntityInfo> m_root;
|
||||
AZStd::unique_ptr<EntityInfo> m_child;
|
||||
|
||||
void MultiplayerTick()
|
||||
{
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->NotifyPreRender(0.1f);
|
||||
m_child->m_entity->FindComponent<NetBindComponent>()->NotifyPreRender(0.1f);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ServerNetTransformTests, SanityCheck)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
m_root->m_entity->FindComponent<AzFramework::TransformComponent>()->GetWorldTM().GetTranslation(),
|
||||
AZ::Vector3::CreateOne()
|
||||
);
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetWorldTM().GetTranslation(),
|
||||
AZ::Vector3::CreateOne()
|
||||
);
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetLocalTM().GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ServerNetTransformTests, NetTransformSavesLocalTransformWhenParentSet)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetworkTransformComponent>()->GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ServerNetTransformTests, NetTransformSavesWorldTransformWhenParentIsNotSet)
|
||||
{
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
MultiplayerTick();
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetworkTransformComponent>()->GetTranslation(),
|
||||
AZ::Vector3::CreateOne() // back at the parent translation
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ServerNetTransformTests, ParentMovesChildNetTransformDoesntChange)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetworkTransformComponent>()->GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
|
||||
// move the parent
|
||||
AZ::Transform rootTransform = AZ::Transform::CreateIdentity();
|
||||
rootTransform.SetTranslation(AZ::Vector3::CreateOne() * 10.f);
|
||||
m_root->m_entity->FindComponent<AzFramework::TransformComponent>()->SetWorldTM(rootTransform);
|
||||
|
||||
MultiplayerTick();
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetWorldTM().GetTranslation(),
|
||||
AZ::Vector3::CreateOne() * 10.f
|
||||
);
|
||||
// child local tm doesn't change
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetLocalTM().GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetworkTransformComponent>()->GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* (Networked) Parent -> (Networked) Child
|
||||
*/
|
||||
class ClientNetTransformTests : public HierarchyTests
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
HierarchyTests::SetUp();
|
||||
|
||||
m_root = AZStd::make_unique<EntityInfo>(1, "root", NetEntityId{ 1 }, EntityInfo::Role::Root);
|
||||
m_child = AZStd::make_unique<EntityInfo>(2, "child", NetEntityId{ 2 }, EntityInfo::Role::Child);
|
||||
|
||||
CreateNetworkParentChild(*m_root, *m_child);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_child.reset();
|
||||
m_root.reset();
|
||||
|
||||
HierarchyTests::TearDown();
|
||||
}
|
||||
|
||||
void PopulateNetworkEntity(const EntityInfo& entityInfo)
|
||||
{
|
||||
entityInfo.m_entity->CreateComponent<AzFramework::TransformComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetBindComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetworkTransformComponent>();
|
||||
}
|
||||
|
||||
void CreateNetworkParentChild(EntityInfo& root, EntityInfo& child)
|
||||
{
|
||||
PopulateNetworkEntity(root);
|
||||
SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Client);
|
||||
|
||||
PopulateNetworkEntity(child);
|
||||
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Client);
|
||||
|
||||
// Create an entity replicator for the child entity
|
||||
const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get());
|
||||
child.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle);
|
||||
child.m_replicator->Initialize(childHandle);
|
||||
|
||||
// Create an entity replicator for the root entity
|
||||
const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get());
|
||||
root.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, rootHandle);
|
||||
root.m_replicator->Initialize(rootHandle);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<EntityInfo> m_root;
|
||||
AZStd::unique_ptr<EntityInfo> m_child;
|
||||
|
||||
void MultiplayerTick()
|
||||
{
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->NotifyPreRender(0.1f);
|
||||
m_child->m_entity->FindComponent<NetBindComponent>()->NotifyPreRender(0.1f);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ClientNetTransformTests, ClientSetsLocalTmWhenParentIsSet)
|
||||
{
|
||||
m_root->m_entity->Activate();
|
||||
m_child->m_entity->Activate();
|
||||
|
||||
SetTranslationOnNetworkTransform(m_root->m_entity, AZ::Vector3::CreateOne());
|
||||
|
||||
SetParentIdOnNetworkTransform(m_child->m_entity, NetEntityId{ 1 });
|
||||
SetTranslationOnNetworkTransform(m_child->m_entity, AZ::Vector3::CreateZero());
|
||||
|
||||
AZ::EntityBus::Broadcast(&AZ::EntityBus::Events::OnEntityActivated, m_root->m_entity->GetId());
|
||||
|
||||
MultiplayerTick();
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetWorldTM().GetTranslation(),
|
||||
AZ::Vector3::CreateOne()
|
||||
);
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetLocalTM().GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ClientNetTransformTests, ClientSetsWorldTmWhenParentIsNotSet)
|
||||
{
|
||||
m_root->m_entity->Activate();
|
||||
m_child->m_entity->Activate();
|
||||
|
||||
SetTranslationOnNetworkTransform(m_root->m_entity, AZ::Vector3::CreateOne());
|
||||
SetTranslationOnNetworkTransform(m_child->m_entity, AZ::Vector3::CreateZero());
|
||||
|
||||
AZ::EntityBus::Broadcast(&AZ::EntityBus::Events::OnEntityActivated, m_root->m_entity->GetId());
|
||||
|
||||
MultiplayerTick();
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetWorldTM().GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetLocalTM().GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ClientNetTransformTests, ChildFollowsWhenParentMovesOnServer)
|
||||
{
|
||||
m_root->m_entity->Activate();
|
||||
m_child->m_entity->Activate();
|
||||
|
||||
SetTranslationOnNetworkTransform(m_root->m_entity, AZ::Vector3::CreateOne());
|
||||
|
||||
SetParentIdOnNetworkTransform(m_child->m_entity, NetEntityId{ 1 });
|
||||
SetTranslationOnNetworkTransform(m_child->m_entity, AZ::Vector3::CreateZero());
|
||||
|
||||
AZ::EntityBus::Broadcast(&AZ::EntityBus::Events::OnEntityActivated, m_root->m_entity->GetId());
|
||||
|
||||
MultiplayerTick();
|
||||
|
||||
// now parent moves
|
||||
SetTranslationOnNetworkTransform(m_root->m_entity, AZ::Vector3::CreateOne() * 2.f);
|
||||
MultiplayerTick();
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetWorldTM().GetTranslation(),
|
||||
AZ::Vector3::CreateOne() * 2.f
|
||||
);
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetLocalTM().GetTranslation(),
|
||||
AZ::Vector3::CreateZero()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ClientNetTransformTests, ChildAttachesToParentIfParentIdIsSetBeforeActivation)
|
||||
{
|
||||
m_root->m_entity->Activate();
|
||||
|
||||
SetTranslationOnNetworkTransform(m_root->m_entity, AZ::Vector3::CreateOne());
|
||||
|
||||
SetParentIdOnNetworkTransform(m_child->m_entity, NetEntityId{ 1 });
|
||||
SetTranslationOnNetworkTransform(m_child->m_entity, AZ::Vector3::CreateZero());
|
||||
|
||||
m_child->m_entity->Activate();
|
||||
|
||||
AZ::EntityBus::Broadcast(&AZ::EntityBus::Events::OnEntityActivated, m_root->m_entity->GetId());
|
||||
|
||||
MultiplayerTick();
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->GetParentId(),
|
||||
AZ::EntityId(1)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_BENCHMARK
|
||||
#include <CommonBenchmarkSetup.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
/*
|
||||
* Hierarchy of 16 entities: Parent -> Child_2 -> .. -> Child_16
|
||||
* By default the maximum size of a hierarchy is defined by bg_hierarchyEntityMaxLimit (16).
|
||||
*/
|
||||
class ServerDeepHierarchyBenchmark : public HierarchyBenchmarkBase
|
||||
{
|
||||
public:
|
||||
const NetEntityId RootNetEntityId = NetEntityId{ 1 };
|
||||
const NetEntityId ChildNetEntityId = NetEntityId{ 2 };
|
||||
const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 };
|
||||
|
||||
void internalSetUp() override
|
||||
{
|
||||
HierarchyBenchmarkBase::internalSetUp();
|
||||
|
||||
m_root = AZStd::make_unique<EntityInfo>((1), "root", RootNetEntityId, EntityInfo::Role::Root);
|
||||
CreateParent(*m_root);
|
||||
|
||||
m_children = AZStd::make_unique<AZStd::vector<AZStd::shared_ptr<EntityInfo>>>();
|
||||
|
||||
EntityInfo* parent = m_root.get();
|
||||
for (int i = 0; i < 15; ++i)
|
||||
{
|
||||
m_children->push_back(AZStd::make_shared<EntityInfo>((i + 2), "child", ChildNetEntityId, EntityInfo::Role::Child));
|
||||
CreateChildForParent(*m_children->back(), *parent);
|
||||
|
||||
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parent->m_entity->GetId());
|
||||
parent = m_children->back().get();
|
||||
}
|
||||
}
|
||||
|
||||
void internalTearDown() override
|
||||
{
|
||||
m_children.reset();
|
||||
m_root.reset();
|
||||
|
||||
HierarchyBenchmarkBase::internalTearDown();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<EntityInfo> m_root;
|
||||
AZStd::unique_ptr<AZStd::vector<AZStd::shared_ptr<EntityInfo>>> m_children;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchy)(benchmark::State& state)
|
||||
{
|
||||
for ([[maybe_unused]] auto value : state)
|
||||
{
|
||||
ForceRebuildHierarchy(m_root->m_entity);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchy)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
;
|
||||
|
||||
// Should be roughly twice the time of @RebuildHierarchy benchmark
|
||||
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddLastChild)(benchmark::State& state)
|
||||
{
|
||||
const AZ::EntityId parentOfLastChild = m_children->at(m_children->size() - 2)->m_entity->GetId();
|
||||
|
||||
for ([[maybe_unused]] auto value : state)
|
||||
{
|
||||
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parentOfLastChild);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddLastChild)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
;
|
||||
|
||||
// Should be roughly twice the time of @RebuildHierarchy benchmark
|
||||
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddMiddleChild)(benchmark::State& state)
|
||||
{
|
||||
const AZ::EntityId parentOfMiddleChild = m_children->at(4)->m_entity->GetId();
|
||||
|
||||
for ([[maybe_unused]] auto value : state)
|
||||
{
|
||||
m_children->at(5)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_children->at(5)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parentOfMiddleChild);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddMiddleChild)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
;
|
||||
|
||||
// Should be roughly twice the time of @RebuildHierarchy benchmark
|
||||
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddFirstChild)(benchmark::State& state)
|
||||
{
|
||||
const AZ::EntityId rootId = m_children->front()->m_entity->GetId();
|
||||
|
||||
for ([[maybe_unused]] auto value : state)
|
||||
{
|
||||
m_children->at(1)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_children->at(1)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(rootId);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddFirstChild)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Tests/ClientHierarchyTests.cpp
|
||||
Tests/ServerHierarchyBenchmarks.cpp
|
||||
Tests/CommonHierarchySetup.h
|
||||
Tests/CommonBenchmarkSetup.h
|
||||
Tests/IMultiplayerConnectionMock.h
|
||||
Tests/Main.cpp
|
||||
Tests/MockInterfaces.h
|
||||
Tests/ClientHierarchyTests.cpp
|
||||
Tests/ServerHierarchyTests.cpp
|
||||
Tests/CommonHierarchySetup.h
|
||||
Tests/IMultiplayerConnectionMock.h
|
||||
Tests/MultiplayerSystemTests.cpp
|
||||
Tests/NetworkTransformTests.cpp
|
||||
Tests/RewindableContainerTests.cpp
|
||||
Tests/RewindableObjectTests.cpp
|
||||
Tests/ServerHierarchyTests.cpp
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user