Merge branch 'upstream/development' into LYN-7655_MultiplayerEditorToEditorServerConnectionReverse
This commit is contained in:
@@ -175,6 +175,11 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Gem::Multiplayer.Static
|
||||
AUTOGEN_RULES
|
||||
*.AutoComponent.xml,AutoComponent_Header.jinja,$path/$fileprefix.AutoComponent.h
|
||||
*.AutoComponent.xml,AutoComponent_Source.jinja,$path/$fileprefix.AutoComponent.cpp
|
||||
*.AutoComponent.xml,AutoComponentTypes_Header.jinja,$path/AutoComponentTypes.h
|
||||
*.AutoComponent.xml,AutoComponentTypes_Source.jinja,$path/AutoComponentTypes.cpp
|
||||
)
|
||||
ly_add_googletest(
|
||||
NAME Gem::Multiplayer.Tests
|
||||
|
||||
+10
@@ -57,6 +57,14 @@ namespace Multiplayer
|
||||
const AzNetworking::PacketEncodingBuffer& correction
|
||||
) override;
|
||||
|
||||
//! Forcibly enables ProcessInput to execute on the entity.
|
||||
//! Note that this function is quite dangerous and should normally never be used
|
||||
void ForceEnableAutonomousUpdate();
|
||||
|
||||
//! Forcibly disables ProcessInput from executing on the entity.
|
||||
//! Note that this function is quite dangerous and should normally never be used
|
||||
void ForceDisableAutonomousUpdate();
|
||||
|
||||
//! Return true if we're currently migrating from one host to another.
|
||||
//! @return boolean true if we're currently migrating from one host to another
|
||||
bool IsMigrating() const;
|
||||
@@ -71,6 +79,8 @@ namespace Multiplayer
|
||||
void UpdateAutonomous(AZ::TimeMs deltaTimeMs);
|
||||
void UpdateBankedTime(AZ::TimeMs deltaTimeMs);
|
||||
|
||||
bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer);
|
||||
|
||||
using StateHistoryItem = AZStd::unique_ptr<AzNetworking::StringifySerializer>;
|
||||
AZStd::map<ClientInputId, StateHistoryItem> m_predictiveStateHistory;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Multiplayer
|
||||
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
|
||||
using EntityDirtiedEvent = AZ::Event<>;
|
||||
using EntitySyncRewindEvent = AZ::Event<>;
|
||||
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&, AzNetworking::ConnectionId>;
|
||||
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
|
||||
using EntityPreRenderEvent = AZ::Event<float>;
|
||||
using EntityCorrectionEvent = AZ::Event<>;
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Multiplayer
|
||||
void MarkDirty();
|
||||
void NotifyLocalChanges();
|
||||
void NotifySyncRewindState();
|
||||
void NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId);
|
||||
void NotifyServerMigration(const HostId& remoteHostId);
|
||||
void NotifyPreRender(float deltaTime);
|
||||
void NotifyCorrection();
|
||||
|
||||
|
||||
+6
-6
@@ -58,16 +58,13 @@ namespace Multiplayer
|
||||
void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override;
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
//! Used by @NetworkHierarchyRootComponent
|
||||
void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot);
|
||||
|
||||
private:
|
||||
//! Used by @NetworkHierarchyRootComponent
|
||||
void SetTopLevelHierarchyRootEntity(AZ::Entity* previousHierarchyRoot, AZ::Entity* newHierarchyRoot);
|
||||
|
||||
AZ::ChildChangedEvent::Handler m_childChangedHandler;
|
||||
AZ::ParentChangedEvent::Handler m_parentChangedHandler;
|
||||
|
||||
void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child);
|
||||
void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent);
|
||||
|
||||
//! Points to the top level root.
|
||||
AZ::Entity* m_rootEntity = nullptr;
|
||||
@@ -82,5 +79,8 @@ namespace Multiplayer
|
||||
bool m_isHierarchyEnabled = true;
|
||||
|
||||
void NotifyChildrenHierarchyDisbanded();
|
||||
|
||||
AzNetworking::ConnectionId m_previousOwningConnectionId = AzNetworking::InvalidConnectionId;
|
||||
void SetOwningConnectionId(AzNetworking::ConnectionId connectionId) override;
|
||||
};
|
||||
}
|
||||
|
||||
+32
-17
@@ -29,6 +29,7 @@ namespace Multiplayer
|
||||
, public NetworkHierarchyRequestBus::Handler
|
||||
{
|
||||
friend class NetworkHierarchyChildComponent;
|
||||
friend class NetworkHierarchyRootComponentController;
|
||||
public:
|
||||
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase);
|
||||
|
||||
@@ -57,10 +58,11 @@ namespace Multiplayer
|
||||
void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override;
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot);
|
||||
bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer);
|
||||
|
||||
private:
|
||||
void SetTopLevelHierarchyRootEntity(AZ::Entity* previousHierarchyRoot, AZ::Entity* newHierarchyRoot);
|
||||
|
||||
AZ::ChildChangedEvent::Handler m_childChangedHandler;
|
||||
AZ::ParentChangedEvent::Handler m_parentChangedHandler;
|
||||
|
||||
@@ -77,26 +79,39 @@ namespace Multiplayer
|
||||
|
||||
//! Rebuilds hierarchy starting from this root component's entity.
|
||||
void RebuildHierarchy();
|
||||
|
||||
//! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy
|
||||
//! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method,
|
||||
//! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities.
|
||||
//! @currentEntityCount will be modified to reflect the total entity count upon completion of this method.
|
||||
//! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise
|
||||
bool RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount);
|
||||
|
||||
//! @param entity Add the child entity and any of its relevant children to the hierarchy
|
||||
//! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method,
|
||||
//! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities.
|
||||
//! @currentEntityCount will be modified to reflect the total entity count upon completion of this method.
|
||||
//! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise
|
||||
bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount);
|
||||
//! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy.
|
||||
//! Builds the hierarchy using breadth-first iterative method.
|
||||
void InternalBuildHierarchyList(AZ::Entity* underEntity);
|
||||
|
||||
void SetRootForEntity(AZ::Entity* previousKnownRoot, AZ::Entity* newRoot, const AZ::Entity* childEntity);
|
||||
|
||||
void SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity);
|
||||
|
||||
//! Set to false when deactivating or otherwise not to be included in hierarchy considerations.
|
||||
bool m_isHierarchyEnabled = true;
|
||||
|
||||
AzNetworking::ConnectionId m_previousOwningConnectionId = AzNetworking::InvalidConnectionId;
|
||||
void SetOwningConnectionId(AzNetworking::ConnectionId connectionId) override;
|
||||
|
||||
friend class HierarchyBenchmarkBase;
|
||||
};
|
||||
|
||||
|
||||
//! NetworkHierarchyRootComponentController
|
||||
//! This is the network controller for NetworkHierarchyRootComponent.
|
||||
//! Class provides the ability to process input for hierarchies.
|
||||
class NetworkHierarchyRootComponentController final
|
||||
: public NetworkHierarchyRootComponentControllerBase
|
||||
{
|
||||
public:
|
||||
NetworkHierarchyRootComponentController(NetworkHierarchyRootComponent& parent);
|
||||
|
||||
// NetworkHierarchyRootComponentControllerBase
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
//! MultiplayerController interface
|
||||
Multiplayer::MultiplayerController::InputPriorityOrder GetInputOrder() const override;
|
||||
void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override;
|
||||
void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Multiplayer
|
||||
using ClientMigrationStartEvent = AZ::Event<ClientInputId>;
|
||||
using ClientMigrationEndEvent = AZ::Event<>;
|
||||
using ClientDisconnectedEvent = AZ::Event<>;
|
||||
using NotifyClientMigrationEvent = AZ::Event<const HostId&, uint64_t, ClientInputId>;
|
||||
using NotifyClientMigrationEvent = AZ::Event<AzNetworking::ConnectionId, const HostId&, uint64_t, ClientInputId, NetEntityId>;
|
||||
using NotifyEntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
|
||||
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
|
||||
using ServerAcceptanceReceivedEvent = AZ::Event<>;
|
||||
@@ -136,10 +136,12 @@ namespace Multiplayer
|
||||
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
|
||||
|
||||
//! 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;
|
||||
//! @param connectionId the connection id of the client that is migrating
|
||||
//! @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
|
||||
//! @param controlledEntityId the entityId of the clients autonomous entity
|
||||
virtual void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) = 0;
|
||||
|
||||
//! Signals a NotifyEntityMigrationEvent with the provided parameters.
|
||||
//! @param entityHandle the network entity handle of the entity being migrated
|
||||
@@ -181,6 +183,18 @@ namespace Multiplayer
|
||||
//! @return pointer to the filtered entity manager, or nullptr if not set
|
||||
virtual IFilterEntityManager* GetFilterEntityManager() = 0;
|
||||
|
||||
//! Registers a temp userId to allow a host to look up a players controlled entity in the event of a rejoin or migration event.
|
||||
//! @param temporaryUserIdentifier the temporary user identifier used to identify a player across hosts
|
||||
//! @param controlledEntityId the controlled entityId of the players autonomous entity
|
||||
virtual void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) = 0;
|
||||
|
||||
//! Completes a client migration event by informing the appropriate client to migrate between hosts.
|
||||
//! @param temporaryUserIdentifier the temporary user identifier used to identify a player across hosts
|
||||
//! @param connectionId the connection id of the player being migrated
|
||||
//! @param publicHostId the public address of the new host the client should connect to
|
||||
//! @param migratedClientInputId the last clientInputId processed prior to migration
|
||||
virtual void CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) = 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
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Multiplayer
|
||||
using HostId = AzNetworking::IpAddress;
|
||||
static const HostId InvalidHostId = HostId();
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint32_t);
|
||||
AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint64_t);
|
||||
static constexpr NetEntityId InvalidNetEntityId = static_cast<NetEntityId>(-1);
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL(NetComponentId, uint16_t);
|
||||
@@ -68,6 +68,7 @@ namespace Multiplayer
|
||||
Server, // A simulated proxy on a server
|
||||
Authority // An authoritative proxy on a server (full authority)
|
||||
};
|
||||
const char* GetEnumString(NetEntityRole value);
|
||||
|
||||
enum class ComponentSerializationType : uint8_t
|
||||
{
|
||||
@@ -113,6 +114,24 @@ namespace Multiplayer
|
||||
bool Serialize(AzNetworking::ISerializer& serializer);
|
||||
};
|
||||
|
||||
inline const char* GetEnumString(NetEntityRole value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case NetEntityRole::InvalidRole:
|
||||
return "InvalidRole";
|
||||
case NetEntityRole::Client:
|
||||
return "Client";
|
||||
case NetEntityRole::Autonomous:
|
||||
return "Autonomous";
|
||||
case NetEntityRole::Server:
|
||||
return "Server";
|
||||
case NetEntityRole::Authority:
|
||||
return "Authority";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
inline PrefabEntityId::PrefabEntityId(AZ::Name name, uint32_t entityOffset)
|
||||
: m_prefabName(name)
|
||||
, m_entityOffset(entityOffset)
|
||||
|
||||
-1
@@ -57,7 +57,6 @@ namespace Multiplayer
|
||||
EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode);
|
||||
~EntityReplicationManager() = default;
|
||||
|
||||
void SetRemoteHostId(const HostId& hostId);
|
||||
const HostId& GetRemoteHostId() const;
|
||||
|
||||
void ActivatePendingEntities();
|
||||
|
||||
+1
-7
@@ -43,8 +43,7 @@ namespace Multiplayer
|
||||
//! Constructor for an entity delete message.
|
||||
//! @param entityId the networkId of the entity being deleted
|
||||
//! @param isMigrated whether or not the entity is being migrated or deleted
|
||||
//! @param takeOwnership true if the remote replicator should take ownership of the entity
|
||||
explicit NetworkEntityUpdateMessage(NetEntityId entityId, bool isMigrated, bool takeOwnership);
|
||||
explicit NetworkEntityUpdateMessage(NetEntityId entityId, bool isMigrated);
|
||||
|
||||
NetworkEntityUpdateMessage& operator =(NetworkEntityUpdateMessage&& rhs);
|
||||
NetworkEntityUpdateMessage& operator =(const NetworkEntityUpdateMessage& rhs);
|
||||
@@ -71,10 +70,6 @@ namespace Multiplayer
|
||||
//! @return whether or not the entity was migrated
|
||||
bool GetWasMigrated() const;
|
||||
|
||||
//! Gets the current value of TakeOwnership.
|
||||
//! @return the current value of TakeOwnership
|
||||
bool GetTakeOwnership() const;
|
||||
|
||||
//! Gets the current value of HasValidPrefabId.
|
||||
//! @return the current value of HasValidPrefabId
|
||||
bool GetHasValidPrefabId() const;
|
||||
@@ -110,7 +105,6 @@ namespace Multiplayer
|
||||
NetEntityId m_entityId = InvalidNetEntityId;
|
||||
bool m_isDelete = false;
|
||||
bool m_wasMigrated = false;
|
||||
bool m_takeOwnership = false;
|
||||
bool m_hasValidPrefabId = false;
|
||||
PrefabEntityId m_prefabEntityId;
|
||||
|
||||
|
||||
+3
-4
@@ -12,10 +12,7 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! Max number of entities that can be children of our netbound player entity.
|
||||
static constexpr uint32_t MaxEntityHierarchyChildren = 16;
|
||||
|
||||
//! Used by the EntityHierarchyComponent. This component allows the gameplay programmer to specify inputs for dependent entities.
|
||||
//! Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify inputs for dependent entities.
|
||||
//! Since it is possible to for the Client/Server to disagree about the state of related entities,
|
||||
//! this network input encodes the entity that is associated with it.
|
||||
class NetworkInputChild
|
||||
@@ -37,4 +34,6 @@ namespace Multiplayer
|
||||
ConstNetworkEntityHandle m_owner;
|
||||
NetworkInput m_networkInput;
|
||||
};
|
||||
|
||||
using NetworkInputChildList = AZStd::vector<NetworkInputChild>;
|
||||
}
|
||||
+4
-3
@@ -9,12 +9,13 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<ComponentRelation Constraint="Weak" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
<ComponentRelation Constraint="Weak" HasController="true" Name="NetworkHierarchyRootComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkHierarchyRootComponent.h" />
|
||||
|
||||
<Include File="Multiplayer/MultiplayerTypes.h"/>
|
||||
<Include File="Multiplayer/NetworkInput/NetworkInput.h"/>
|
||||
<Include File="Source/NetworkInput/NetworkInputArray.h"/>
|
||||
<Include File="Source/NetworkInput/NetworkInputHistory.h"/>
|
||||
<Include File="Source/NetworkInput/NetworkInputMigrationVector.h"/>
|
||||
<Include File="Multiplayer/NetworkInput/NetworkInputArray.h"/>
|
||||
<Include File="Multiplayer/NetworkInput/NetworkInputHistory.h"/>
|
||||
<Include File="Multiplayer/NetworkInput/NetworkInputMigrationVector.h"/>
|
||||
<Include File="AzNetworking/DataStructures/ByteBuffer.h"/>
|
||||
|
||||
<NetworkProperty Type="Multiplayer::ClientInputId" Name="LastInputId" Init="Multiplayer::ClientInputId{ 0 }" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<Packet Name="Connect" HandshakePacket="true" Desc="Client connection packet, on success the server will reply with an Accept">
|
||||
<Member Type="uint16_t" Name="networkProtocolVersion" Init="0" />
|
||||
<Member Type="uint64_t" Name="temporaryUserId" Init="0" />
|
||||
<Member Type="Multiplayer::LongNetworkString" Name="ticket" />
|
||||
</Packet>
|
||||
|
||||
|
||||
+6
-2
@@ -4,11 +4,15 @@
|
||||
Name="NetworkHierarchyRootComponent"
|
||||
Namespace="Multiplayer"
|
||||
OverrideComponent="true"
|
||||
OverrideController="false"
|
||||
OverrideController="true"
|
||||
OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<Include File="Multiplayer/NetworkInput/NetworkInputChild.h"/>
|
||||
|
||||
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
|
||||
|
||||
<NetworkInput Type="NetworkInputChildList" Name="ChildInputs" Init="" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" />
|
||||
|
||||
<NetworkProperty Type="NetEntityId" Name="hierarchyRoot" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
</Component>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
|
||||
#include <AzNetworking/Serialization/StringifySerializer.h>
|
||||
#include <AzNetworking/Serialization/TrackChangedSerializer.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -211,7 +212,7 @@ namespace Multiplayer
|
||||
m_lastCorrectionSentTimeMs = currentTimeMs;
|
||||
|
||||
AzNetworking::HashSerializer hashSerializer;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(hashSerializer);
|
||||
SerializeEntityCorrection(hashSerializer);
|
||||
|
||||
const AZ::HashValue32 localAuthorityHash = hashSerializer.GetHash();
|
||||
|
||||
@@ -233,7 +234,7 @@ namespace Multiplayer
|
||||
// only deserialize if we have data (for client/server profile/debug mismatches)
|
||||
if (correction.GetSize() > 0)
|
||||
{
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serializer);
|
||||
SerializeEntityCorrection(serializer);
|
||||
}
|
||||
|
||||
correction.Resize(serializer.GetSize());
|
||||
@@ -313,7 +314,7 @@ namespace Multiplayer
|
||||
|
||||
// Apply the correction
|
||||
AzNetworking::TrackChangedSerializer<AzNetworking::NetworkOutputSerializer> serializer(correction.GetBuffer(), static_cast<uint32_t>(correction.GetSize()));
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serializer);
|
||||
SerializeEntityCorrection(serializer);
|
||||
GetNetBindComponent()->NotifyCorrection();
|
||||
|
||||
#ifndef AZ_RELEASE_BUILD
|
||||
@@ -325,7 +326,7 @@ namespace Multiplayer
|
||||
{
|
||||
// Read out state values
|
||||
AzNetworking::StringifySerializer serverValues;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serverValues);
|
||||
SerializeEntityCorrection(serverValues);
|
||||
PrintCorrectionDifferences(*iter->second, serverValues);
|
||||
}
|
||||
else
|
||||
@@ -353,6 +354,16 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::ForceEnableAutonomousUpdate()
|
||||
{
|
||||
m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true);
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::ForceDisableAutonomousUpdate()
|
||||
{
|
||||
m_autonomousUpdateEvent.RemoveFromQueue();
|
||||
}
|
||||
|
||||
bool LocalPredictionPlayerInputComponentController::IsMigrating() const
|
||||
{
|
||||
return m_lastMigratedInputId != ClientInputId{ 0 };
|
||||
@@ -452,7 +463,7 @@ namespace Multiplayer
|
||||
|
||||
// Generate a hash based on the current client predicted states
|
||||
AzNetworking::HashSerializer hashSerializer;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(hashSerializer);
|
||||
SerializeEntityCorrection(hashSerializer);
|
||||
|
||||
// Save this input and discard move history outside our client rewind window
|
||||
m_inputHistory.PushBack(input);
|
||||
@@ -480,7 +491,7 @@ namespace Multiplayer
|
||||
{
|
||||
m_predictiveStateHistory.erase(m_predictiveStateHistory.begin());
|
||||
}
|
||||
GetNetBindComponent()->SerializeEntityCorrection(*inputHistory);
|
||||
SerializeEntityCorrection(*inputHistory);
|
||||
m_predictiveStateHistory.emplace(m_clientInputId, AZStd::move(inputHistory));
|
||||
}
|
||||
#endif
|
||||
@@ -493,6 +504,18 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalPredictionPlayerInputComponentController::SerializeEntityCorrection(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
bool result = GetNetBindComponent()->SerializeEntityCorrection(serializer);
|
||||
|
||||
NetworkHierarchyRootComponent* hierarchyComponent = GetParent().GetNetworkHierarchyRootComponent();
|
||||
if (result && hierarchyComponent)
|
||||
{
|
||||
result = hierarchyComponent->SerializeEntityCorrection(serializer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::UpdateBankedTime(AZ::TimeMs deltaTimeMs)
|
||||
{
|
||||
const double deltaTime = static_cast<double>(deltaTimeMs) / 1000.0;
|
||||
|
||||
@@ -394,9 +394,9 @@ namespace Multiplayer
|
||||
m_syncRewindEvent.Signal();
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId)
|
||||
void NetBindComponent::NotifyServerMigration(const HostId& remoteHostId)
|
||||
{
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId);
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, remoteHostId);
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyPreRender(float deltaTime)
|
||||
|
||||
@@ -55,7 +55,6 @@ namespace Multiplayer
|
||||
|
||||
NetworkHierarchyChildComponent::NetworkHierarchyChildComponent()
|
||||
: m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); })
|
||||
, m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); })
|
||||
, m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); })
|
||||
{
|
||||
|
||||
@@ -75,7 +74,6 @@ namespace Multiplayer
|
||||
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
|
||||
{
|
||||
transformComponent->BindChildChangedEventHandler(m_childChangedHandler);
|
||||
transformComponent->BindParentChangedEventHandler(m_parentChangedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,45 +129,52 @@ namespace Multiplayer
|
||||
handler.Connect(m_networkHierarchyLeaveEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot)
|
||||
void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* previousHierarchyRoot, AZ::Entity* newHierarchyRoot)
|
||||
{
|
||||
m_rootEntity = hierarchyRoot;
|
||||
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
if (newHierarchyRoot)
|
||||
{
|
||||
NetworkHierarchyChildComponentController* controller = static_cast<NetworkHierarchyChildComponentController*>(GetController());
|
||||
if (m_rootEntity)
|
||||
if (m_rootEntity != newHierarchyRoot)
|
||||
{
|
||||
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId());
|
||||
controller->SetHierarchyRoot(netRootId);
|
||||
m_rootEntity = newHierarchyRoot;
|
||||
|
||||
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
NetworkHierarchyChildComponentController* controller = static_cast<NetworkHierarchyChildComponentController*>(GetController());
|
||||
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId());
|
||||
controller->SetHierarchyRoot(netRootId);
|
||||
}
|
||||
|
||||
GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent<NetBindComponent>()->GetOwningConnectionId());
|
||||
m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId());
|
||||
}
|
||||
else
|
||||
{
|
||||
controller->SetHierarchyRoot(InvalidNetEntityId);
|
||||
|
||||
m_networkHierarchyLeaveEvent.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_rootEntity == nullptr)
|
||||
else if ((previousHierarchyRoot && m_rootEntity == previousHierarchyRoot) || !previousHierarchyRoot)
|
||||
{
|
||||
m_rootEntity = nullptr;
|
||||
|
||||
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
NetworkHierarchyChildComponentController* controller = static_cast<NetworkHierarchyChildComponentController*>(GetController());
|
||||
controller->SetHierarchyRoot(InvalidNetEntityId);
|
||||
}
|
||||
|
||||
GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId);
|
||||
m_networkHierarchyLeaveEvent.Signal();
|
||||
|
||||
NotifyChildrenHierarchyDisbanded();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child)
|
||||
void NetworkHierarchyChildComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
if (m_rootEntity)
|
||||
NetworkHierarchyChildComponentBase::SetOwningConnectionId(connectionId);
|
||||
if (IsHierarchicalChild() == false)
|
||||
{
|
||||
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
root->RebuildHierarchy();
|
||||
}
|
||||
m_previousOwningConnectionId = connectionId;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, [[maybe_unused]] AZ::EntityId parent)
|
||||
void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child)
|
||||
{
|
||||
if (m_rootEntity)
|
||||
{
|
||||
@@ -189,32 +194,38 @@ namespace Multiplayer
|
||||
if (m_rootEntity != newRoot)
|
||||
{
|
||||
m_rootEntity = newRoot;
|
||||
|
||||
m_previousOwningConnectionId = GetNetBindComponent()->GetOwningConnectionId();
|
||||
GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent<NetBindComponent>()->GetOwningConnectionId());
|
||||
|
||||
m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId);
|
||||
m_isHierarchyEnabled = false;
|
||||
m_rootEntity = nullptr;
|
||||
m_networkHierarchyLeaveEvent.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::NotifyChildrenHierarchyDisbanded()
|
||||
{
|
||||
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
|
||||
|
||||
AZStd::vector<AZ::EntityId> allChildren;
|
||||
AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren);
|
||||
for (const AZ::EntityId& childEntityId : allChildren)
|
||||
{
|
||||
if (const AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(childEntityId))
|
||||
if (const AZ::Entity* childEntity = componentApplication->FindEntity(childEntityId))
|
||||
{
|
||||
if (auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>())
|
||||
{
|
||||
hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr);
|
||||
hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr, nullptr);
|
||||
}
|
||||
else if (auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr);
|
||||
hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
AZ_CVAR(uint32_t, bg_hierarchyEntityMaxLimit, 16, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"Maximum allowed size of network entity hierarchies, including top level entity.");
|
||||
|
||||
static constexpr int CommonHierarchyEntityMaxLimit = 16; // Should match @bg_hierarchyEntityMaxLimit
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
void NetworkHierarchyRootComponent::Reflect(AZ::ReflectContext* context)
|
||||
@@ -105,7 +107,7 @@ namespace Multiplayer
|
||||
{
|
||||
if (const AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(childEntityId))
|
||||
{
|
||||
SetRootForEntity(nullptr, childEntity);
|
||||
SetRootForEntity(GetEntity(), nullptr, childEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,6 +175,29 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
static AZStd::tuple<NetworkHierarchyRootComponent*, NetworkHierarchyChildComponent*> GetHierarchyComponents(const AZ::Entity* entity)
|
||||
{
|
||||
NetworkHierarchyChildComponent* childComponent = nullptr;
|
||||
NetworkHierarchyRootComponent* rootComponent = nullptr;
|
||||
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
if (component->GetUnderlyingComponentType() == NetworkHierarchyChildComponent::TYPEINFO_Uuid())
|
||||
{
|
||||
childComponent = static_cast<NetworkHierarchyChildComponent*>(component);
|
||||
break;
|
||||
}
|
||||
|
||||
if (component->GetUnderlyingComponentType() == NetworkHierarchyRootComponent::TYPEINFO_Uuid())
|
||||
{
|
||||
rootComponent = static_cast<NetworkHierarchyRootComponent*>(component);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return AZStd::tie(rootComponent, childComponent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent)
|
||||
{
|
||||
// If the parent is part of a hierarchy, it will detect this entity as a new child and rebuild hierarchy.
|
||||
@@ -181,10 +206,10 @@ namespace Multiplayer
|
||||
|
||||
if (AZ::Entity* parentEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(newParent))
|
||||
{
|
||||
if (parentEntity->FindComponent<NetworkHierarchyRootComponent>() == nullptr &&
|
||||
parentEntity->FindComponent<NetworkHierarchyChildComponent>() == nullptr)
|
||||
auto [rootComponent, childComponent] = GetHierarchyComponents(parentEntity);
|
||||
if (rootComponent == nullptr && childComponent == nullptr)
|
||||
{
|
||||
RebuildHierarchy();
|
||||
SetRootForEntity(nullptr, nullptr, GetEntity());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -194,7 +219,7 @@ namespace Multiplayer
|
||||
else
|
||||
{
|
||||
// Detached from parent
|
||||
RebuildHierarchy();
|
||||
SetRootForEntity(nullptr, nullptr, GetEntity());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,10 +228,9 @@ namespace Multiplayer
|
||||
AZStd::vector<AZ::Entity*> previousEntities;
|
||||
m_hierarchicalEntities.swap(previousEntities);
|
||||
|
||||
m_hierarchicalEntities.push_back(GetEntity()); // Add the root.
|
||||
m_hierarchicalEntities.reserve(bg_hierarchyEntityMaxLimit);
|
||||
|
||||
uint32_t currentEntityCount = aznumeric_cast<uint32_t>(m_hierarchicalEntities.size());
|
||||
RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount);
|
||||
InternalBuildHierarchyList(GetEntity());
|
||||
|
||||
bool hierarchyChanged = false;
|
||||
|
||||
@@ -223,14 +247,14 @@ namespace Multiplayer
|
||||
{
|
||||
// This is a newly added entity to the network hierarchy.
|
||||
hierarchyChanged = true;
|
||||
SetRootForEntity(GetEntity(), currentEntity);
|
||||
SetRootForEntity(nullptr, GetEntity(), currentEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// These entities were removed since last rebuild.
|
||||
for (const AZ::Entity* previousEntity : previousEntities)
|
||||
{
|
||||
SetRootForEntity(nullptr, previousEntity);
|
||||
SetRootForEntity(GetEntity(), nullptr, previousEntity);
|
||||
}
|
||||
|
||||
if (!previousEntities.empty())
|
||||
@@ -244,86 +268,248 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity)
|
||||
void NetworkHierarchyRootComponent::InternalBuildHierarchyList(AZ::Entity* underEntity)
|
||||
{
|
||||
if (auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>())
|
||||
{
|
||||
hierarchyChildComponent->SetTopLevelHierarchyRootEntity(root);
|
||||
}
|
||||
else if (auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
hierarchyRootComponent->SetTopLevelHierarchyRootEntity(root);
|
||||
}
|
||||
}
|
||||
AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
|
||||
|
||||
bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount)
|
||||
{
|
||||
AZStd::vector<AZ::EntityId> allChildren;
|
||||
AZ::TransformBus::EventResult(allChildren, underEntity, &AZ::TransformBus::Events::GetChildren);
|
||||
AZStd::deque<AZ::Entity*, AZStd::allocator, CommonHierarchyEntityMaxLimit> candidates;
|
||||
candidates.push_back(underEntity);
|
||||
|
||||
for (const AZ::EntityId& newChildId : allChildren)
|
||||
while (!candidates.empty())
|
||||
{
|
||||
if (!RecursiveAttachHierarchicalChild(newChildId, currentEntityCount))
|
||||
AZ::Entity* candidate = candidates.front();
|
||||
candidates.pop_front();
|
||||
|
||||
if (candidate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto [hierarchyRootComponent, hierarchyChildComponent] = GetHierarchyComponents(candidate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount)
|
||||
{
|
||||
if (currentEntityCount >= bg_hierarchyEntityMaxLimit)
|
||||
{
|
||||
AZLOG_WARN("Entity %s is trying to build a network hierarchy that is too large. bg_hierarchyEntityMaxLimit is currently set to (%u)",
|
||||
GetEntity()->GetName().c_str(), static_cast<uint32_t>(bg_hierarchyEntityMaxLimit));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entity))
|
||||
{
|
||||
auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>();
|
||||
auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>();
|
||||
|
||||
if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) ||
|
||||
(hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled()))
|
||||
{
|
||||
m_hierarchicalEntities.push_back(childEntity);
|
||||
++currentEntityCount;
|
||||
|
||||
if (!RecursiveAttachHierarchicalEntities(entity, currentEntityCount))
|
||||
if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) ||
|
||||
(hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled()))
|
||||
{
|
||||
return false;
|
||||
m_hierarchicalEntities.push_back(candidate);
|
||||
|
||||
if (m_hierarchicalEntities.size() >= bg_hierarchyEntityMaxLimit)
|
||||
{
|
||||
AZLOG_WARN("Network hierarchy size exceeded, current limit is %d, root entity was %s",
|
||||
static_cast<int>(bg_hierarchyEntityMaxLimit),
|
||||
GetEntity()->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
const AZStd::vector<AZ::EntityId> allChildren = candidate->GetTransform()->GetChildren();
|
||||
for (const AZ::EntityId& newChildId : allChildren)
|
||||
{
|
||||
candidates.push_back(componentApplicationRequests->FindEntity(newChildId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot)
|
||||
void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* previousKnownRoot, AZ::Entity* newRoot, const AZ::Entity* childEntity)
|
||||
{
|
||||
m_rootEntity = hierarchyRoot;
|
||||
auto [hierarchyRootComponent, hierarchyChildComponent] = GetHierarchyComponents(childEntity);
|
||||
|
||||
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
if (hierarchyChildComponent)
|
||||
{
|
||||
NetworkHierarchyChildComponentController* controller = static_cast<NetworkHierarchyChildComponentController*>(GetController());
|
||||
if (hierarchyRoot)
|
||||
hierarchyChildComponent->SetTopLevelHierarchyRootEntity(previousKnownRoot, newRoot);
|
||||
}
|
||||
else if (hierarchyRootComponent)
|
||||
{
|
||||
hierarchyRootComponent->SetTopLevelHierarchyRootEntity(previousKnownRoot, newRoot);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* previousHierarchyRoot, AZ::Entity* newHierarchyRoot)
|
||||
{
|
||||
if (newHierarchyRoot)
|
||||
{
|
||||
if (m_rootEntity != newHierarchyRoot)
|
||||
{
|
||||
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetId());
|
||||
controller->SetHierarchyRoot(netRootId);
|
||||
}
|
||||
else
|
||||
{
|
||||
controller->SetHierarchyRoot(InvalidNetEntityId);
|
||||
m_rootEntity = newHierarchyRoot;
|
||||
|
||||
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
NetworkHierarchyRootComponentController* controller = static_cast<NetworkHierarchyRootComponentController*>(GetController());
|
||||
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId());
|
||||
controller->SetHierarchyRoot(netRootId);
|
||||
}
|
||||
|
||||
GetNetBindComponent()->SetOwningConnectionId(m_rootEntity->FindComponent<NetBindComponent>()->GetOwningConnectionId());
|
||||
m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_rootEntity == nullptr)
|
||||
else if ((previousHierarchyRoot && m_rootEntity == previousHierarchyRoot) || !previousHierarchyRoot)
|
||||
{
|
||||
m_rootEntity = nullptr;
|
||||
|
||||
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
NetworkHierarchyRootComponentController* controller = static_cast<NetworkHierarchyRootComponentController*>(GetController());
|
||||
controller->SetHierarchyRoot(InvalidNetEntityId);
|
||||
}
|
||||
|
||||
GetNetBindComponent()->SetOwningConnectionId(m_previousOwningConnectionId);
|
||||
m_networkHierarchyLeaveEvent.Signal();
|
||||
|
||||
// We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy.
|
||||
RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
NetworkHierarchyRootComponentBase::SetOwningConnectionId(connectionId);
|
||||
if (IsHierarchicalChild() == false)
|
||||
{
|
||||
m_previousOwningConnectionId = connectionId;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkHierarchyRootComponentController::NetworkHierarchyRootComponentController(NetworkHierarchyRootComponent& parent)
|
||||
: NetworkHierarchyRootComponentControllerBase(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Multiplayer::MultiplayerController::InputPriorityOrder NetworkHierarchyRootComponentController::GetInputOrder() const
|
||||
{
|
||||
return Multiplayer::MultiplayerController::InputPriorityOrder::SubEntities;
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponentController::CreateInput(Multiplayer::NetworkInput& input, float deltaTime)
|
||||
{
|
||||
NetworkHierarchyRootComponent& component = GetParent();
|
||||
if (!component.IsHierarchicalRoot())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
|
||||
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
|
||||
|
||||
const AZStd::vector<AZ::Entity*>& entities = component.m_hierarchicalEntities;
|
||||
|
||||
auto* networkInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>();
|
||||
networkInput->m_childInputs.clear();
|
||||
networkInput->m_childInputs.reserve(entities.size());
|
||||
|
||||
for (AZ::Entity* child : entities)
|
||||
{
|
||||
if (child == component.GetEntity())
|
||||
{
|
||||
continue; // Avoid infinite recursion
|
||||
}
|
||||
|
||||
NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId());
|
||||
AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
|
||||
|
||||
ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId);
|
||||
NetBindComponent* netComp = childEntityHandle.GetNetBindComponent();
|
||||
|
||||
AZ_Assert(netComp, "No NetBindComponent, this should be impossible");
|
||||
// Validate we still have a controller and we aren't in the middle of removing them
|
||||
if (netComp->HasController())
|
||||
{
|
||||
NetworkInputChild subInput;
|
||||
subInput.Attach(childEntityHandle);
|
||||
subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId());
|
||||
|
||||
netComp->CreateInput(subInput.GetNetworkInput(), deltaTime);
|
||||
|
||||
// make sure our input sub commands have the same time as the original
|
||||
subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId());
|
||||
networkInput->m_childInputs.emplace_back(subInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponentController::ProcessInput(Multiplayer::NetworkInput& input, float deltaTime)
|
||||
{
|
||||
if (auto* networkInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>())
|
||||
{
|
||||
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
|
||||
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
|
||||
|
||||
// Build a set of Net IDs for the children
|
||||
AZStd::unordered_set<NetEntityId> currentChildren;
|
||||
NetworkHierarchyRootComponent& component = GetParent();
|
||||
for (AZ::Entity* child : component.m_hierarchicalEntities)
|
||||
{
|
||||
if (child == component.GetEntity()) // Skip the root entity
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId());
|
||||
AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
|
||||
currentChildren.insert(childNetEntitydId);
|
||||
}
|
||||
|
||||
// Process the input for the child entities
|
||||
for (NetworkInputChild& subInput : networkInput->m_childInputs)
|
||||
{
|
||||
const ConstNetworkEntityHandle& inputOwnerHandle = subInput.GetOwner();
|
||||
NetEntityId inputOwnerNetEntitydId = inputOwnerHandle.GetNetEntityId();
|
||||
|
||||
if (currentChildren.count(inputOwnerNetEntitydId) == 0)
|
||||
{
|
||||
// Skip the input for entities which are not a part of this hierarchy
|
||||
continue;
|
||||
}
|
||||
|
||||
ConstNetworkEntityHandle localEntityHandle = networkEntityManager->GetEntity(inputOwnerNetEntitydId);
|
||||
if (localEntityHandle.Exists())
|
||||
{
|
||||
auto* netComp = localEntityHandle.GetNetBindComponent();
|
||||
AZ_Assert(netComp, "No NetBindComponent, this should be impossible");
|
||||
// We do not rewind entity role changes, so make sure we are the correct role prior to processing
|
||||
if (netComp->HasController())
|
||||
{
|
||||
subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId());
|
||||
netComp->ProcessInput(subInput.GetNetworkInput(), deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::SerializeEntityCorrection(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
|
||||
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
|
||||
|
||||
for (AZ::Entity* child : m_hierarchicalEntities)
|
||||
{
|
||||
if (child == GetEntity())
|
||||
{
|
||||
// Skip the root entity
|
||||
continue;
|
||||
}
|
||||
|
||||
NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId());
|
||||
AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
|
||||
|
||||
ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId);
|
||||
NetBindComponent* netBindComponent = childEntityHandle.GetNetBindComponent();
|
||||
AZ_Assert(netBindComponent, "No NetBindComponent, this should be impossible");
|
||||
|
||||
result = result && netBindComponent->SerializeEntityCorrection(serializer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,22 +23,16 @@ namespace Multiplayer
|
||||
ServerToClientConnectionData::ServerToClientConnectionData
|
||||
(
|
||||
AzNetworking::IConnection* connection,
|
||||
AzNetworking::IConnectionListener& connectionListener,
|
||||
NetworkEntityHandle controlledEntity
|
||||
AzNetworking::IConnectionListener& connectionListener
|
||||
)
|
||||
: m_connection(connection)
|
||||
, m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); })
|
||||
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId) { OnControlledEntityMigration(entityHandle, remoteHostId, connectionId); })
|
||||
, m_controlledEntity(controlledEntity)
|
||||
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
|
||||
{
|
||||
OnControlledEntityMigration(entityHandle, remoteHostId);
|
||||
})
|
||||
, m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient)
|
||||
{
|
||||
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler);
|
||||
netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler);
|
||||
}
|
||||
|
||||
m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(sv_ClientMaxRemoteEntitiesPendingCreationCount);
|
||||
m_entityReplicationManager.SetEntityPendingRemovalMs(sv_ClientEntityReplicatorPendingRemovalTimeMs);
|
||||
}
|
||||
@@ -54,6 +48,20 @@ namespace Multiplayer
|
||||
m_controlledEntityRemovedHandler.Disconnect();
|
||||
}
|
||||
|
||||
void ServerToClientConnectionData::SetControlledEntity(NetworkEntityHandle primaryPlayerEntity)
|
||||
{
|
||||
m_controlledEntityRemovedHandler.Disconnect();
|
||||
m_controlledEntityMigrationHandler.Disconnect();
|
||||
|
||||
m_controlledEntity = primaryPlayerEntity;
|
||||
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler);
|
||||
netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler);
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionDataType ServerToClientConnectionData::GetConnectionDataType() const
|
||||
{
|
||||
return ConnectionDataType::ServerToClient;
|
||||
@@ -94,8 +102,7 @@ namespace Multiplayer
|
||||
void ServerToClientConnectionData::OnControlledEntityMigration
|
||||
(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
|
||||
[[maybe_unused]] const HostId& remoteHostId,
|
||||
[[maybe_unused]] AzNetworking::ConnectionId connectionId
|
||||
const HostId& remoteHostId
|
||||
)
|
||||
{
|
||||
ClientInputId migratedClientInputId = ClientInputId{ 0 };
|
||||
@@ -109,14 +116,12 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
// 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();
|
||||
const uint64_t temporaryUserIdentifier = 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);
|
||||
GetMultiplayer()->SendNotifyClientMigrationEvent(GetConnection()->GetConnectionId(), remoteHostId, temporaryUserIdentifier, migratedClientInputId, m_controlledEntity.GetNetEntityId());
|
||||
// We need to send a MultiplayerPackets::ClientMigration packet to complete this process
|
||||
// This happens inside MultiplayerSystemComponent, once we're certain the remote host has appropriately prepared
|
||||
|
||||
m_controlledEntity = NetworkEntityHandle();
|
||||
m_canSendUpdates = false;
|
||||
|
||||
@@ -20,11 +20,12 @@ namespace Multiplayer
|
||||
ServerToClientConnectionData
|
||||
(
|
||||
AzNetworking::IConnection* connection,
|
||||
AzNetworking::IConnectionListener& connectionListener,
|
||||
NetworkEntityHandle controlledEntity
|
||||
AzNetworking::IConnectionListener& connectionListener
|
||||
);
|
||||
~ServerToClientConnectionData() override;
|
||||
|
||||
void SetControlledEntity(NetworkEntityHandle primaryPlayerEntity);
|
||||
|
||||
//! IConnectionData interface
|
||||
//! @{
|
||||
ConnectionDataType GetConnectionDataType() const override;
|
||||
@@ -44,7 +45,7 @@ namespace Multiplayer
|
||||
|
||||
private:
|
||||
void OnControlledEntityRemove();
|
||||
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId);
|
||||
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId);
|
||||
void OnGameplayStarted();
|
||||
|
||||
EntityReplicationManager m_entityReplicationManager;
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Multiplayer
|
||||
m_canSendUpdates = canSendUpdates;
|
||||
}
|
||||
|
||||
|
||||
inline NetworkEntityHandle ServerToClientConnectionData::GetPrimaryPlayerEntity()
|
||||
{
|
||||
return m_controlledEntity;
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Multiplayer
|
||||
{
|
||||
ImGui::Text("%s", entity->GetId().ToString().c_str());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId()));
|
||||
ImGui::Text("%llu", static_cast<AZ::u64>(GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId())));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s", entity->GetName().c_str());
|
||||
ImGui::NextColumn();
|
||||
|
||||
@@ -28,24 +28,29 @@ namespace Multiplayer
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent"));
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatbile)
|
||||
{
|
||||
incompatbile.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent"));
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::Activate()
|
||||
{
|
||||
#ifdef IMGUI_ENABLED
|
||||
ImGui::ImGuiUpdateListenerBus::Handler::BusConnect();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::Deactivate()
|
||||
{
|
||||
#ifdef IMGUI_ENABLED
|
||||
@@ -75,6 +80,7 @@ namespace Multiplayer
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond)
|
||||
{
|
||||
uint64_t summedCalls = 0;
|
||||
@@ -107,6 +113,7 @@ namespace Multiplayer
|
||||
ImGui::Text("%11.2f", bytesPerSecond);
|
||||
return open;
|
||||
}
|
||||
|
||||
bool DrawSummaryRow(const char* name, const MultiplayerStats& stats)
|
||||
{
|
||||
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics();
|
||||
@@ -123,6 +130,7 @@ namespace Multiplayer
|
||||
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
|
||||
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
|
||||
}
|
||||
|
||||
bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId)
|
||||
{
|
||||
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
|
||||
@@ -139,6 +147,7 @@ namespace Multiplayer
|
||||
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
|
||||
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
|
||||
}
|
||||
|
||||
void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId)
|
||||
{
|
||||
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
|
||||
@@ -503,4 +512,3 @@ void OnDebugEntities_ShowBandwidth_Changed(const bool& showBandwidth)
|
||||
AZ::Interface<Multiplayer::IMultiplayerDebug>::Get()->HideEntityBandwidthDebugOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Multiplayer/MultiplayerConstants.h>
|
||||
#include <Multiplayer/Components/MultiplayerComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <MultiplayerSystemComponent.h>
|
||||
#include <ConnectionData/ClientToServerConnectionData.h>
|
||||
#include <ConnectionData/ServerToClientConnectionData.h>
|
||||
@@ -76,6 +77,7 @@ namespace Multiplayer
|
||||
"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");
|
||||
AZ_CVAR(uint16_t, sv_portRange, 999, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The range of ports the host will incrementally attempt to bind to when initializing");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_map, "nolevel", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The map the server should load");
|
||||
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");
|
||||
@@ -168,6 +170,7 @@ namespace Multiplayer
|
||||
AZ::ConsoleFunctorFlags flags,
|
||||
AZ::ConsoleInvokedFrom invokedFrom
|
||||
) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); })
|
||||
, m_autonomousEntityReplicatorCreatedHandler([this]([[maybe_unused]] NetEntityId netEntityId) { OnAutonomousEntityReplicatorCreated(); })
|
||||
{
|
||||
AZ::Interface<IMultiplayer>::Register(this);
|
||||
}
|
||||
@@ -205,8 +208,23 @@ namespace Multiplayer
|
||||
|
||||
bool MultiplayerSystemComponent::StartHosting(uint16_t port, bool isDedicated)
|
||||
{
|
||||
InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer);
|
||||
return m_networkInterface->Listen(port);
|
||||
if (port != sv_port)
|
||||
{
|
||||
sv_port = port;
|
||||
}
|
||||
|
||||
const uint16_t maxPort = sv_port + sv_portRange;
|
||||
while (sv_port <= maxPort)
|
||||
{
|
||||
if (m_networkInterface->Listen(sv_port))
|
||||
{
|
||||
InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer);
|
||||
return true;
|
||||
}
|
||||
AZLOG_WARN("Failed to start listening on port %u, port is in use?", static_cast<uint32_t>(sv_port));
|
||||
sv_port = sv_port + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MultiplayerSystemComponent::Connect(const AZStd::string& remoteAddress, uint16_t port)
|
||||
@@ -328,6 +346,11 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
if (bg_multiplayerDebugDraw)
|
||||
{
|
||||
m_networkEntityManager.DebugDraw();
|
||||
}
|
||||
|
||||
const AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f));
|
||||
const AZ::TimeMs serverRateMs = static_cast<AZ::TimeMs>(sv_serverSendRateMs);
|
||||
const float serverRateSeconds = static_cast<float>(serverRateMs) / 1000.0f;
|
||||
@@ -412,11 +435,6 @@ namespace Multiplayer
|
||||
{
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
}
|
||||
|
||||
if (bg_multiplayerDebugDraw)
|
||||
{
|
||||
m_networkEntityManager.DebugDraw();
|
||||
}
|
||||
}
|
||||
|
||||
int MultiplayerSystemComponent::GetTickOrder()
|
||||
@@ -487,17 +505,39 @@ namespace Multiplayer
|
||||
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); };
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str());
|
||||
|
||||
// Hosts will spawn a new default player prefab for the user that just connected
|
||||
if (GetAgentType() == MultiplayerAgentType::ClientServer
|
||||
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
|
||||
{
|
||||
// We use a temporary userId over the clients address so we can maintain client lookups even in the event of wifi handoff
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(packet.GetTemporaryUserId());
|
||||
EnableAutonomousControl(controlledEntity, connection->GetConnectionId());
|
||||
|
||||
ServerToClientConnectionData* connectionData = reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData());
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
|
||||
connectionData->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
|
||||
connectionData->SetControlledEntity(controlledEntity);
|
||||
|
||||
// If this is a migrate or rejoin, immediately ready the connection for updates
|
||||
if (packet.GetTemporaryUserId() != 0)
|
||||
{
|
||||
connectionData->SetCanSendUpdates(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map)))
|
||||
{
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetDidHandshake(true);
|
||||
|
||||
// Sync our console
|
||||
ConsoleReplicator consoleReplicator(connection);
|
||||
AZ::Interface<AZ::IConsole>::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); });
|
||||
if (packet.GetTemporaryUserId() == 0)
|
||||
{
|
||||
// Sync our console
|
||||
ConsoleReplicator consoleReplicator(connection);
|
||||
AZ::Interface<AZ::IConsole>::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -511,10 +551,26 @@ namespace Multiplayer
|
||||
)
|
||||
{
|
||||
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());
|
||||
if (m_temporaryUserIdentifier == 0)
|
||||
{
|
||||
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());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bypass map loading and immediately ready the connection for updates
|
||||
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
|
||||
if (connectionData)
|
||||
{
|
||||
connectionData->SetCanSendUpdates(true);
|
||||
|
||||
// @nt: TODO - delete once dropped RPC problem fixed
|
||||
// Connection has migrated, we are now waiting for the autonomous entity replicator to be created
|
||||
connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
m_serverAcceptanceReceivedEvent.Signal();
|
||||
return true;
|
||||
@@ -637,13 +693,17 @@ namespace Multiplayer
|
||||
|
||||
// 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
|
||||
m_temporaryUserIdentifier = packet.GetTemporaryUserIdentifier();
|
||||
|
||||
// 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());
|
||||
if (m_networkInterface->Connect(packet.GetRemoteServerAddress()) == AzNetworking::InvalidConnectionId)
|
||||
{
|
||||
AZLOG_ERROR("Failed to connect to new host during client migration event");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -673,7 +733,7 @@ namespace Multiplayer
|
||||
providerTicket = m_pendingConnectionTickets.front();
|
||||
m_pendingConnectionTickets.pop();
|
||||
}
|
||||
connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket.c_str()));
|
||||
connection->SendReliablePacket(MultiplayerPackets::Connect(0, m_temporaryUserIdentifier, providerTicket.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -681,20 +741,10 @@ namespace Multiplayer
|
||||
m_connectionAcquiredEvent.Signal(datum);
|
||||
}
|
||||
|
||||
// Hosts will spawn a new default player prefab for the user that just connected
|
||||
if (GetAgentType() == MultiplayerAgentType::ClientServer
|
||||
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
|
||||
{
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
|
||||
if (controlledEntity.Exists())
|
||||
{
|
||||
controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
|
||||
}
|
||||
controlledEntity.Activate();
|
||||
|
||||
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));
|
||||
connection->SetUserData(new ServerToClientConnectionData(connection, *this));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -716,9 +766,9 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::OnDisconnect(AzNetworking::IConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint)
|
||||
{
|
||||
const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remote host disconnected";
|
||||
const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remotely disconnected";
|
||||
AZStd::string reasonString = ToString(reason);
|
||||
AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str());
|
||||
AZLOG_INFO("%s from remote address %s due to %s", endpointString, connection->GetRemoteAddress().GetString().c_str(), reasonString.c_str());
|
||||
|
||||
// The client is disconnecting
|
||||
if (GetAgentType() == MultiplayerAgentType::Client)
|
||||
@@ -800,12 +850,8 @@ namespace Multiplayer
|
||||
// Spawn the default player for this host since the host is also a player (not a dedicated server)
|
||||
if (m_agentType == MultiplayerAgentType::ClientServer)
|
||||
{
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
|
||||
if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent())
|
||||
{
|
||||
controlledEntityNetBindComponent->SetAllowAutonomy(true);
|
||||
}
|
||||
controlledEntity.Activate();
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(0);
|
||||
EnableAutonomousControl(controlledEntity, AzNetworking::InvalidConnectionId);
|
||||
}
|
||||
|
||||
AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType));
|
||||
@@ -856,9 +902,9 @@ namespace Multiplayer
|
||||
handler.Connect(m_shutdownEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId)
|
||||
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId)
|
||||
{
|
||||
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId);
|
||||
m_notifyClientMigrationEvent.Signal(connectionId, hostId, userIdentifier, lastClientInputId, controlledEntityId);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
|
||||
@@ -912,6 +958,22 @@ namespace Multiplayer
|
||||
return m_filterEntityManager;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId)
|
||||
{
|
||||
m_playerRejoinData[temporaryUserIdentifier] = controlledEntityId;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId)
|
||||
{
|
||||
IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId);
|
||||
if (connection != nullptr) // Make sure the player has not disconnected since the start of migration
|
||||
{
|
||||
// Tell the client who to join
|
||||
MultiplayerPackets::ClientMigration clientMigration(publicHostId, temporaryUserIdentifier, migratedClientInputId);
|
||||
connection->SendReliablePacket(clientMigration);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value)
|
||||
{
|
||||
m_spawnNetboundEntities = value;
|
||||
@@ -1042,6 +1104,13 @@ namespace Multiplayer
|
||||
m_cvarCommands.PushBackItem(AZStd::move(replicateString));
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::OnAutonomousEntityReplicatorCreated()
|
||||
{
|
||||
m_autonomousEntityReplicatorCreatedHandler.Disconnect();
|
||||
//m_networkEntityManager.GetNetworkEntityAuthorityTracker()->ResetTimeoutTime(AZ::TimeMs{ 2000 });
|
||||
m_clientMigrationEndEvent.Signal();
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::ExecuteConsoleCommandList(IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands)
|
||||
{
|
||||
AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
|
||||
@@ -1053,11 +1122,22 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab()
|
||||
NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab(uint64_t temporaryUserIdentifier)
|
||||
{
|
||||
const auto node = m_playerRejoinData.find(temporaryUserIdentifier);
|
||||
if (node != m_playerRejoinData.end())
|
||||
{
|
||||
return m_networkEntityManager.GetNetworkEntityTracker()->Get(node->second);
|
||||
}
|
||||
|
||||
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()));
|
||||
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate);
|
||||
|
||||
for (NetworkEntityHandle subEntity : entityList)
|
||||
{
|
||||
subEntity.Activate();
|
||||
}
|
||||
|
||||
NetworkEntityHandle controlledEntity;
|
||||
if (!entityList.empty())
|
||||
{
|
||||
@@ -1066,11 +1146,45 @@ namespace Multiplayer
|
||||
return controlledEntity;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::EnableAutonomousControl(NetworkEntityHandle entityHandle, AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
if (!entityHandle.Exists())
|
||||
{
|
||||
AZLOG_WARN("Attempting to enable autonomous control for an invalid entity");
|
||||
return;
|
||||
}
|
||||
|
||||
entityHandle.GetNetBindComponent()->SetOwningConnectionId(connectionId);
|
||||
if (connectionId == InvalidConnectionId)
|
||||
{
|
||||
entityHandle.GetNetBindComponent()->SetAllowAutonomy(true);
|
||||
}
|
||||
|
||||
auto* hierarchyComponent = entityHandle.FindComponent<NetworkHierarchyRootComponent>();
|
||||
if (hierarchyComponent != nullptr)
|
||||
{
|
||||
for (AZ::Entity* subEntity : hierarchyComponent->GetHierarchicalEntities())
|
||||
{
|
||||
NetworkEntityHandle subEntityHandle = NetworkEntityHandle(subEntity);
|
||||
NetBindComponent* subEntityNetBindComponent = subEntityHandle.GetNetBindComponent();
|
||||
|
||||
if (subEntityNetBindComponent != nullptr)
|
||||
{
|
||||
subEntityNetBindComponent->SetOwningConnectionId(connectionId);
|
||||
if (connectionId == InvalidConnectionId)
|
||||
{
|
||||
subEntityNetBindComponent->SetAllowAutonomy(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
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));
|
||||
AZLOG_ERROR("Failed to start listening on any allocated port");
|
||||
}
|
||||
}
|
||||
AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to");
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Multiplayer
|
||||
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
|
||||
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
|
||||
void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) override;
|
||||
void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override;
|
||||
void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override;
|
||||
void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override;
|
||||
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
|
||||
AZ::TimeMs GetCurrentHostTimeMs() const override;
|
||||
@@ -132,6 +132,8 @@ namespace Multiplayer
|
||||
INetworkEntityManager* GetNetworkEntityManager() override;
|
||||
void SetFilterEntityManager(IFilterEntityManager* entityFilter) override;
|
||||
IFilterEntityManager* GetFilterEntityManager() override;
|
||||
void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) override;
|
||||
void CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) override;
|
||||
void SetShouldSpawnNetworkEntities(bool value) override;
|
||||
bool GetShouldSpawnNetworkEntities() const override;
|
||||
//! @}
|
||||
@@ -145,9 +147,11 @@ namespace Multiplayer
|
||||
|
||||
void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds);
|
||||
void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom);
|
||||
void OnAutonomousEntityReplicatorCreated();
|
||||
void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands);
|
||||
NetworkEntityHandle SpawnDefaultPlayerPrefab();
|
||||
|
||||
NetworkEntityHandle SpawnDefaultPlayerPrefab(uint64_t temporaryUserIdentifier);
|
||||
void EnableAutonomousControl(NetworkEntityHandle entityHandle, AzNetworking::ConnectionId connectionId);
|
||||
|
||||
AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session");
|
||||
|
||||
AzNetworking::INetworkInterface* m_networkInterface = nullptr;
|
||||
@@ -170,12 +174,16 @@ namespace Multiplayer
|
||||
ClientMigrationEndEvent m_clientMigrationEndEvent;
|
||||
NotifyClientMigrationEvent m_notifyClientMigrationEvent;
|
||||
NotifyEntityMigrationEvent m_notifyEntityMigrationEvent;
|
||||
AZ::Event<NetEntityId>::Handler m_autonomousEntityReplicatorCreatedHandler;
|
||||
|
||||
AZStd::queue<AZStd::string> m_pendingConnectionTickets;
|
||||
AZStd::unordered_map<uint64_t, NetEntityId> m_playerRejoinData;
|
||||
|
||||
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
|
||||
HostFrameId m_lastReplicatedHostFrameId = HostFrameId(0);
|
||||
|
||||
uint64_t m_temporaryUserIdentifier = 0; // Used in the event of a migration or rejoin
|
||||
|
||||
double m_serverSendAccumulator = 0.0;
|
||||
float m_renderBlendFactor = 0.0f;
|
||||
float m_tickFactor = 0.0f;
|
||||
|
||||
+55
-43
@@ -47,6 +47,9 @@ namespace Multiplayer
|
||||
, m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); })
|
||||
, m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); })
|
||||
{
|
||||
// Set up our remote host identifier, by default we use the IP address of the remote host
|
||||
m_remoteHostId = connection.GetRemoteAddress();
|
||||
|
||||
// Our max payload size is whatever is passed in, minus room for a udp packetheader
|
||||
m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead;
|
||||
|
||||
@@ -62,12 +65,10 @@ namespace Multiplayer
|
||||
networkEntityManager->AddEntityExitDomainHandler(m_entityExitDomainEventHandler);
|
||||
}
|
||||
|
||||
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler);
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SetRemoteHostId(const HostId& hostId)
|
||||
{
|
||||
m_remoteHostId = hostId;
|
||||
if (m_updateMode == Mode::LocalServerToRemoteServer)
|
||||
{
|
||||
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler);
|
||||
}
|
||||
}
|
||||
|
||||
const HostId& EntityReplicationManager::GetRemoteHostId() const
|
||||
@@ -258,8 +259,8 @@ namespace Multiplayer
|
||||
{
|
||||
AZLOG_WARN
|
||||
(
|
||||
"Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d",
|
||||
aznumeric_cast<uint32_t>(replicator->GetEntityHandle().GetNetEntityId()),
|
||||
"Serializing extremely large entity (%llu) - MaxPayload: %d NeededSize %d",
|
||||
aznumeric_cast<AZ::u64>(replicator->GetEntityHandle().GetNetEntityId()),
|
||||
m_maxPayloadSize,
|
||||
nextMessageSize
|
||||
);
|
||||
@@ -364,15 +365,29 @@ namespace Multiplayer
|
||||
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
|
||||
bool changedLocalRole(false);
|
||||
if (AZ::Entity* localEnt = entityReplicator->GetEntityHandle().GetEntity())
|
||||
NetBindComponent* netBindComponent = entityReplicator->GetEntityHandle().GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
NetBindComponent* netBindComponent = entityReplicator->GetEntityHandle().GetNetBindComponent();
|
||||
AZ_Assert(netBindComponent != nullptr, "No NetBindComponent");
|
||||
changedLocalRole = (netBindComponent->GetNetEntityRole() != entityReplicator->GetBoundLocalNetworkRole());
|
||||
}
|
||||
|
||||
if (changedRemoteRole || changedLocalRole)
|
||||
{
|
||||
const AZ::u64 intEntityId = static_cast<AZ::u64>(netBindComponent->GetNetEntityId());
|
||||
const char* entityName = entityReplicator->GetEntityHandle().GetEntity()->GetName().c_str();
|
||||
if (changedLocalRole)
|
||||
{
|
||||
const char* oldRoleString = GetEnumString(entityReplicator->GetRemoteNetworkRole());
|
||||
const char* newRoleString = GetEnumString(remoteNetworkRole);
|
||||
AZLOG(NET_ReplicatorRoles, "Replicator %s(%llu) changed local role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString);
|
||||
}
|
||||
if (changedRemoteRole)
|
||||
{
|
||||
const char* oldRoleString = GetEnumString(entityReplicator->GetBoundLocalNetworkRole());
|
||||
const char* newRoleString = GetEnumString(netBindComponent->GetNetEntityRole());
|
||||
AZLOG(NET_ReplicatorRoles, "Replicator %s(%llu) changed remote role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString);
|
||||
}
|
||||
|
||||
// If we changed roles, we need to reset everything
|
||||
if (!entityReplicator->IsMarkedForRemoval())
|
||||
{
|
||||
@@ -387,8 +402,8 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_RepDeletes,
|
||||
"Reinited replicator for %u from remote host %s role %d",
|
||||
entityHandle.GetNetEntityId(),
|
||||
"Reinited replicator for netEntityId %llu from remote host %s role %d",
|
||||
static_cast<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
GetRemoteHostId().GetString().c_str(),
|
||||
aznumeric_cast<int32_t>(remoteNetworkRole)
|
||||
);
|
||||
@@ -404,8 +419,8 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_RepDeletes,
|
||||
"Added replicator for %u from remote host %s role %d",
|
||||
entityHandle.GetNetEntityId(),
|
||||
"Added replicator for netEntityId %llu from remote host %s role %d",
|
||||
static_cast<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
GetRemoteHostId().GetString().c_str(),
|
||||
aznumeric_cast<int32_t>(remoteNetworkRole)
|
||||
);
|
||||
@@ -413,7 +428,7 @@ namespace Multiplayer
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG_ERROR("Failed to add entity replicator, entity does not exist, entity id %u", entityHandle.GetNetEntityId());
|
||||
AZLOG_ERROR("Failed to add entity replicator, entity does not exist, netEntityId %llu", static_cast<AZ::u64>(entityHandle.GetNetEntityId()));
|
||||
AZ_Assert(false, "Failed to add entity replicator, entity does not exist");
|
||||
}
|
||||
return entityReplicator;
|
||||
@@ -502,24 +517,20 @@ namespace Multiplayer
|
||||
{
|
||||
if (entityReplicator->IsMarkedForRemoval())
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %llu remote host %s", static_cast<AZ::u64>(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 host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %llu remote host %s", static_cast<AZ::u64>(updateMessage.GetEntityId()), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldDeleteEntity = true;
|
||||
entityReplicator->MarkForRemoval();
|
||||
AZLOG(NET_RepDeletes, "Deleting replicater for entity id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "Deleting replicater for entity id %llu remote host %s", static_cast<AZ::u64>(updateMessage.GetEntityId()), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldDeleteEntity = updateMessage.GetTakeOwnership();
|
||||
}
|
||||
|
||||
// Handle entity cleanup
|
||||
if (shouldDeleteEntity)
|
||||
@@ -529,17 +540,17 @@ namespace Multiplayer
|
||||
{
|
||||
if (updateMessage.GetWasMigrated())
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "Leaving id %u using timeout remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "Leaving id %llu using timeout remote host %s", static_cast<AZ::u64>(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "Deleting entity id %u remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "Deleting entity id %llu remote host %s", static_cast<AZ::u64>(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str());
|
||||
GetNetworkEntityManager()->MarkForRemoval(entity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "Trying to delete entity id %u remote host %s, but it has been removed", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "Trying to delete entity id %llu remote host %s, but it has been removed", static_cast<AZ::u64>(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,9 +594,9 @@ namespace Multiplayer
|
||||
NetBindComponent* netBindComponent = replicatorEntity.GetNetBindComponent();
|
||||
AZ_Assert(netBindComponent != nullptr, "No NetBindComponent");
|
||||
|
||||
if (createEntity)
|
||||
if (netBindComponent->GetOwningConnectionId() != invokingConnection->GetConnectionId())
|
||||
{
|
||||
// Always set our invoking connectionId for any newly created entities, since this connection now 'owns' them from a rewind perspective
|
||||
// Always ensure our owning connectionId is correct for correct rewind behaviour
|
||||
netBindComponent->SetOwningConnectionId(invokingConnection->GetConnectionId());
|
||||
}
|
||||
|
||||
@@ -595,10 +606,11 @@ namespace Multiplayer
|
||||
AZ_Assert(localNetworkRole != NetEntityRole::Authority, "UpdateMessage trying to set local role to Authority, this should only happen via migration");
|
||||
AZLOG_INFO
|
||||
(
|
||||
"EntityReplicationManager: Changing network role on entity %u, old role %u new role %u",
|
||||
aznumeric_cast<uint32_t>(netEntityId),
|
||||
aznumeric_cast<uint32_t>(netBindComponent->GetNetEntityRole()),
|
||||
aznumeric_cast<uint32_t>(localNetworkRole)
|
||||
"EntityReplicationManager: Changing network role on entity %s(%llu), old role %s new role %s",
|
||||
replicatorEntity.GetEntity()->GetName().c_str(),
|
||||
aznumeric_cast<AZ::u64>(netEntityId),
|
||||
GetEnumString(netBindComponent->GetNetEntityRole()),
|
||||
GetEnumString(localNetworkRole)
|
||||
);
|
||||
|
||||
if (NetworkRoleHasController(localNetworkRole))
|
||||
@@ -708,9 +720,9 @@ namespace Multiplayer
|
||||
AZLOG_WARN
|
||||
(
|
||||
"Dropping Packet and LocalServerToRemoteClient connection, unexpected packet "
|
||||
"LocalShard=%s EntityId=%u RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s",
|
||||
"LocalShard=%s EntityId=%llu RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s",
|
||||
GetNetworkEntityManager()->GetHostId().GetString().c_str(),
|
||||
aznumeric_cast<uint32_t>(entityReplicator->GetEntityHandle().GetNetEntityId()),
|
||||
aznumeric_cast<AZ::u64>(entityReplicator->GetEntityHandle().GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(entityReplicator->GetRemoteNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityReplicator->GetBoundLocalNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityReplicator->GetNetBindComponent()->GetNetEntityRole()),
|
||||
@@ -760,13 +772,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 host %s",
|
||||
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %llu, sequence %d latest sequence %d from remote host %s",
|
||||
(AZ::u64)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 host %s",
|
||||
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %llu, sequence %d latest sequence %d from remote host %s",
|
||||
(AZ::u64)updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -853,10 +865,10 @@ namespace Multiplayer
|
||||
{
|
||||
AZLOG_INFO
|
||||
(
|
||||
"EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %u has already been deleted",
|
||||
"EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %llu has already been deleted",
|
||||
GetMultiplayerComponentRegistry()->GetComponentName(message.GetComponentId()),
|
||||
GetMultiplayerComponentRegistry()->GetComponentRpcName(message.GetComponentId(), message.GetRpcIndex()),
|
||||
message.GetEntityId()
|
||||
static_cast<AZ::u64>(message.GetEntityId())
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -1113,7 +1125,7 @@ namespace Multiplayer
|
||||
|
||||
if (m_updateMode == EntityReplicationManager::Mode::LocalServerToRemoteServer)
|
||||
{
|
||||
netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetConnection().GetConnectionId());
|
||||
netBindComponent->NotifyServerMigration(GetRemoteHostId());
|
||||
}
|
||||
|
||||
bool didSucceed = true;
|
||||
@@ -1145,7 +1157,7 @@ namespace Multiplayer
|
||||
AZ_Assert(didSucceed, "Failed to migrate entity from server");
|
||||
|
||||
m_sendMigrateEntityEvent.Signal(m_connection, message);
|
||||
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote host %s", netEntityId, GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "Migration packet sent %llu to remote host %s", static_cast<AZ::u64>(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());
|
||||
@@ -1201,7 +1213,7 @@ namespace Multiplayer
|
||||
// Change the role on the replicator
|
||||
AddEntityReplicator(entityHandle, NetEntityRole::Server);
|
||||
|
||||
AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote host %s", entityHandle.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
AZLOG(NET_RepDeletes, "Handle Migration %llu new authority from remote host %s", static_cast<AZ::u64>(entityHandle.GetNetEntityId()), GetRemoteHostId().GetString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+65
-101
@@ -103,8 +103,8 @@ namespace Multiplayer
|
||||
AZ_Assert
|
||||
(
|
||||
m_boundLocalNetworkRole != m_remoteNetworkRole,
|
||||
"Invalid configuration detected, bound local role must differ from remote network role Role: %d",
|
||||
aznumeric_cast<int32_t>(m_boundLocalNetworkRole)
|
||||
"Invalid configuration detected, bound local role must differ from remote network role: %s",
|
||||
GetEnumString(m_boundLocalNetworkRole)
|
||||
);
|
||||
|
||||
if (RemoteManagerOwnsEntityLifetime())
|
||||
@@ -176,7 +176,6 @@ namespace Multiplayer
|
||||
switch (GetBoundLocalNetworkRole())
|
||||
{
|
||||
case NetEntityRole::Authority:
|
||||
{
|
||||
if (GetRemoteNetworkRole() == NetEntityRole::Client || GetRemoteNetworkRole() == NetEntityRole::Autonomous)
|
||||
{
|
||||
m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
|
||||
@@ -189,10 +188,8 @@ namespace Multiplayer
|
||||
{
|
||||
m_onForwardRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case NetEntityRole::Server:
|
||||
{
|
||||
if (GetRemoteNetworkRole() == NetEntityRole::Authority)
|
||||
{
|
||||
m_onSendRpcHandler.Connect(netBindComponent->GetSendServerToAuthorityRpcEvent());
|
||||
@@ -204,23 +201,21 @@ namespace Multiplayer
|
||||
// Listen for these to forward the rpc along to the other Client replicators
|
||||
m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
|
||||
}
|
||||
// NOTE: e_Autonomous is not connected to e_ServerProxy, it is always connected to an e_Authority
|
||||
AZ_Assert(GetRemoteNetworkRole() != NetEntityRole::Autonomous, "Unexpected autonomous remote role")
|
||||
}
|
||||
break;
|
||||
else if (GetRemoteNetworkRole() == NetEntityRole::Autonomous)
|
||||
{
|
||||
// NOTE: Autonomous is not connected to ServerProxy, it is always connected to an Authority
|
||||
AZ_Assert(false, "Unexpected autonomous remote role")
|
||||
}
|
||||
break;
|
||||
case NetEntityRole::Client:
|
||||
{
|
||||
// Nothing allowed, no Client to Server communication
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case NetEntityRole::Autonomous:
|
||||
{
|
||||
if (GetRemoteNetworkRole() == NetEntityRole::Authority)
|
||||
{
|
||||
m_onSendRpcHandler.Connect(netBindComponent->GetSendAutonomousToAuthorityRpcEvent());
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
AZ_Assert(false, "Unexpected network role");
|
||||
}
|
||||
@@ -252,22 +247,9 @@ namespace Multiplayer
|
||||
|
||||
if (entity->GetState() != AZ::Entity::State::Init)
|
||||
{
|
||||
AZLOG_WARN("Trying to activate an entity that is not in the Init state (%u)", GetEntityHandle().GetNetEntityId());
|
||||
AZLOG_WARN("Trying to activate an entity that is not in the Init state (%llu)", static_cast<AZ::u64>(GetEntityHandle().GetNetEntityId()));
|
||||
}
|
||||
|
||||
// First we need to make sure the transform component has been updated with the correct value prior to activation
|
||||
// This is because vanilla az components may only depend on the transform component, not the multiplayer transform component
|
||||
//if (auto* locationComponent = FindCommonComponent<LocationComponent::Common>(GetEntityHandle()))
|
||||
//{
|
||||
// AZ::Transform newTransform = locationComponent->GetTransform();
|
||||
// auto* transformComponent = entity->FindComponent<AzFramework::TransformComponent>();
|
||||
// if (transformComponent)
|
||||
// {
|
||||
// // We can't use EBus here since the TransFormBus does not get connected until the activate call below
|
||||
// transformComponent->SetWorldTM(newTransform);
|
||||
// }
|
||||
//}
|
||||
// Ugly, but this is the only time we need to call a non-const function on this entity
|
||||
entity->Activate();
|
||||
|
||||
m_replicationManager.m_orphanedEntityRpcs.DispatchOrphanedRpcs(*this);
|
||||
@@ -281,8 +263,7 @@ namespace Multiplayer
|
||||
NetBindComponent* netBindComponent = m_netBindComponent;
|
||||
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
|
||||
|
||||
bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
|
||||
bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
|
||||
bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client;
|
||||
bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous;
|
||||
if (isAuthority || isClient || isAutonomous)
|
||||
@@ -296,10 +277,10 @@ namespace Multiplayer
|
||||
bool EntityReplicator::OwnsReplicatorLifetime() const
|
||||
{
|
||||
bool ret(false);
|
||||
if (GetBoundLocalNetworkRole() == NetEntityRole::Authority
|
||||
|| (GetBoundLocalNetworkRole() == NetEntityRole::Server
|
||||
if (GetBoundLocalNetworkRole() == NetEntityRole::Authority // Authority always owns lifetime
|
||||
|| (GetBoundLocalNetworkRole() == NetEntityRole::Server // Server also owns lifetime if the remote endpoint is a client of some form
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Client
|
||||
|| GetRemoteNetworkRole() == NetEntityRole::Autonomous)))
|
||||
|| GetRemoteNetworkRole() == NetEntityRole::Autonomous)))
|
||||
{
|
||||
ret = true;
|
||||
}
|
||||
@@ -309,10 +290,9 @@ namespace Multiplayer
|
||||
bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const
|
||||
{
|
||||
bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority);
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority);
|
||||
bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client)
|
||||
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
|
||||
|
||||
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
|
||||
return isServer || isClient;
|
||||
}
|
||||
|
||||
@@ -429,10 +409,8 @@ namespace Multiplayer
|
||||
if (const NetworkTransformComponent* networkTransform = entity->FindComponent<NetworkTransformComponent>())
|
||||
{
|
||||
const NetEntityId parentId = networkTransform->GetParentEntityId();
|
||||
/*
|
||||
* For root entities attached to a level, a network parent won't be set.
|
||||
* In this case, this entity is the root entity of the hierarchy and it will be activated first.
|
||||
*/
|
||||
// For root entities attached to a level, a network parent won't be set.
|
||||
// In this case, this entity is the root entity of the hierarchy and it will be activated first.
|
||||
if (parentId != InvalidNetEntityId)
|
||||
{
|
||||
ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId);
|
||||
@@ -452,9 +430,9 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_HierarchyActivationInfo,
|
||||
"Hierchical entity %s asking for activation - waiting on the parent %u",
|
||||
"Hierchical entity %s asking for activation - waiting on the parent %llu",
|
||||
entity->GetName().c_str(),
|
||||
aznumeric_cast<uint32_t>(parentId)
|
||||
aznumeric_cast<AZ::u64>(parentId)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -472,19 +450,19 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_RepDeletes,
|
||||
"Sending delete replicator id %u migrated %d to remote host %s",
|
||||
aznumeric_cast<uint32_t>(GetEntityHandle().GetNetEntityId()),
|
||||
"Sending delete replicator id %llu migrated %d to remote host %s",
|
||||
aznumeric_cast<AZ::u64>(GetEntityHandle().GetNetEntityId()),
|
||||
WasMigrated() ? 1 : 0,
|
||||
m_replicationManager.GetRemoteHostId().GetString().c_str()
|
||||
);
|
||||
return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated(), m_propertyPublisher->IsRemoteReplicatorEstablished());
|
||||
return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
@@ -553,42 +531,33 @@ namespace Multiplayer
|
||||
switch (entityRpcMessage.GetRpcDeliveryType())
|
||||
{
|
||||
case RpcDeliveryType::AuthorityToClient:
|
||||
{
|
||||
if (((GetBoundLocalNetworkRole() == NetEntityRole::Client) || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous))
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
{
|
||||
// We are a local client, and we are connected to server, aka AuthorityToClient
|
||||
result = RpcValidationResult::HandleRpc;
|
||||
}
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Server)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Server) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
{
|
||||
// We are on a server, and we received this message from another server, therefore we should forward this to any connected clients
|
||||
result = RpcValidationResult::ForwardToClient;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case RpcDeliveryType::AuthorityToAutonomous:
|
||||
{
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
{
|
||||
// We are an autonomous client, and we are connected to server, aka AuthorityToAutonomous
|
||||
result = RpcValidationResult::HandleRpc;
|
||||
}
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Server))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server))
|
||||
{
|
||||
// We are on a server, and we received this message from another server, therefore we should forward this to our autonomous player
|
||||
// This can occur if we've recently migrated
|
||||
result = RpcValidationResult::ForwardToAutonomous;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case RpcDeliveryType::AutonomousToAuthority:
|
||||
{
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Autonomous))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Autonomous))
|
||||
{
|
||||
if (IsMarkedForRemoval())
|
||||
{
|
||||
@@ -610,12 +579,9 @@ namespace Multiplayer
|
||||
result = RpcValidationResult::HandleRpc;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case RpcDeliveryType::ServerToAuthority:
|
||||
{
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Server))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server))
|
||||
{
|
||||
// if we're marked for removal, then we should forward to whomever now owns this entity
|
||||
if (IsMarkedForRemoval())
|
||||
@@ -638,9 +604,9 @@ namespace Multiplayer
|
||||
result = RpcValidationResult::HandleRpc;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == RpcValidationResult::DropRpcAndDisconnect)
|
||||
{
|
||||
bool isLocalServer = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) || (GetBoundLocalNetworkRole() == NetEntityRole::Server);
|
||||
@@ -654,30 +620,29 @@ namespace Multiplayer
|
||||
{
|
||||
AZLOG_ERROR
|
||||
(
|
||||
"Dropping RPC and Connection EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
|
||||
"Dropping RPC and Connection EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
|
||||
GetEnumString(GetBoundLocalNetworkRole()),
|
||||
GetEnumString(GetRemoteNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
|
||||
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
|
||||
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
|
||||
IsMarkedForRemoval() ? "true" : "false"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == RpcValidationResult::DropRpc)
|
||||
{
|
||||
AZLOG
|
||||
(
|
||||
NET_Rpc,
|
||||
"Dropping RPC EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
|
||||
"Dropping RPC EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
|
||||
GetEnumString(GetBoundLocalNetworkRole()),
|
||||
GetEnumString(GetRemoteNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
|
||||
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
|
||||
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
|
||||
IsMarkedForRemoval() ? "true" : "false"
|
||||
);
|
||||
@@ -696,13 +661,12 @@ namespace Multiplayer
|
||||
{
|
||||
AZLOG_WARN
|
||||
(
|
||||
"Dropping RPC since entity deleted EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
|
||||
"Dropping RPC since entity deleted EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
|
||||
GetEnumString(GetBoundLocalNetworkRole()),
|
||||
GetEnumString(GetRemoteNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
|
||||
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
|
||||
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
|
||||
IsMarkedForRemoval() ? "true" : "false"
|
||||
);
|
||||
@@ -740,23 +704,23 @@ namespace Multiplayer
|
||||
case RpcValidationResult::DropRpcAndDisconnect:
|
||||
return false;
|
||||
case RpcValidationResult::ForwardToClient:
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage);
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case RpcValidationResult::ForwardToAutonomous:
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage);
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case RpcValidationResult::ForwardToAuthority:
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage);
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_AuthTracker,
|
||||
"AuthTracker: Removing timeout for networkEntityId %u from %s, new owner is %s",
|
||||
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
|
||||
"AuthTracker: Removing timeout for networkEntityId %llu from %s, new owner is %s",
|
||||
aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
timeoutData->second.m_previousOwner.GetString().c_str(),
|
||||
newOwner.GetString().c_str()
|
||||
);
|
||||
@@ -48,8 +48,8 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_AuthTracker,
|
||||
"AuthTracker: Assigning networkEntityId %u from %s to %s",
|
||||
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
|
||||
"AuthTracker: Assigning networkEntityId %llu from %s to %s",
|
||||
aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
iter->second.back().GetString().c_str(),
|
||||
newOwner.GetString().c_str()
|
||||
);
|
||||
@@ -59,8 +59,8 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_AuthTracker,
|
||||
"AuthTracker: Assigning networkEntityId %u to %s",
|
||||
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
|
||||
"AuthTracker: Assigning networkEntityId %llu to %s",
|
||||
aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
newOwner.GetString().c_str()
|
||||
);
|
||||
}
|
||||
@@ -87,7 +87,7 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %u from %s", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), previousOwner.GetString().c_str());
|
||||
AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %llu from %s", aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()), previousOwner.GetString().c_str());
|
||||
if (auto localEnt = entityHandle.GetEntity())
|
||||
{
|
||||
if (authorityStack.empty())
|
||||
@@ -114,14 +114,14 @@ namespace Multiplayer
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_AuthTracker, "AuthTracker: Skipping timeout for Autonomous networkEntityId %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()));
|
||||
AZLOG(NET_AuthTracker, "AuthTracker: Skipping timeout for Autonomous networkEntityId %llu", aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_AuthTracker, "AuthTracker: Remove authority called on networkEntityId that was never added %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()));
|
||||
AZLOG(NET_AuthTracker, "AuthTracker: Remove authority called on networkEntityId that was never added %llu", aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()));
|
||||
AZ_Assert(false, "AuthTracker: Remove authority called on entity that was never added");
|
||||
}
|
||||
}
|
||||
@@ -205,8 +205,8 @@ namespace Multiplayer
|
||||
{
|
||||
AZLOG_ERROR
|
||||
(
|
||||
"Timed out entity id %u during migration previous owner %s, removing it",
|
||||
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
|
||||
"Timed out entity id %llu during migration previous owner %s, removing it",
|
||||
aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
timeoutData->second.m_previousOwner.GetString().c_str()
|
||||
);
|
||||
m_networkEntityManager.MarkForRemoval(entityHandle);
|
||||
|
||||
@@ -18,22 +18,14 @@ namespace Multiplayer
|
||||
{
|
||||
ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* networkEntityTracker)
|
||||
: m_entity(entity)
|
||||
, m_networkEntityTracker(networkEntityTracker)
|
||||
, m_networkEntityTracker((networkEntityTracker != nullptr) ? networkEntityTracker : GetNetworkEntityTracker())
|
||||
{
|
||||
if (m_networkEntityTracker == nullptr)
|
||||
{
|
||||
m_networkEntityTracker = GetNetworkEntityTracker();
|
||||
}
|
||||
|
||||
if (m_networkEntityTracker)
|
||||
{
|
||||
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
|
||||
}
|
||||
AZ_Assert(m_networkEntityTracker, "NetworkEntityTracker is not valid");
|
||||
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
|
||||
|
||||
if (entity)
|
||||
{
|
||||
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
|
||||
m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity);
|
||||
m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(entity);
|
||||
if (m_netBindComponent != nullptr)
|
||||
{
|
||||
m_netEntityId = m_netBindComponent->GetNetEntityId();
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <Pipeline/NetworkSpawnableHolderComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
@@ -46,6 +48,20 @@ namespace Multiplayer
|
||||
void NetworkEntityManager::Initialize(const HostId& hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
|
||||
{
|
||||
m_hostId = hostId;
|
||||
|
||||
// Configure our vended NetEntityIds so that no two hosts generate the same NetEntityId
|
||||
{
|
||||
// Needs more thought
|
||||
const uint64_t addrPortion = hostId.GetAddress(AzNetworking::ByteOrder::Host);
|
||||
const uint64_t portPortion = hostId.GetPort(AzNetworking::ByteOrder::Host);
|
||||
const uint64_t hostIdentifier = (portPortion << 32) | addrPortion;
|
||||
const AZ::HashValue32 hostHash = AZ::TypeHash32(hostIdentifier);
|
||||
|
||||
NetEntityId hostEntityIdOffset = static_cast<NetEntityId>(hostHash) << 32;
|
||||
m_nextEntityId &= NetEntityId{ 0x0000000000000000FFFFFFFFFFFFFFFF };
|
||||
m_nextEntityId |= hostEntityIdOffset;
|
||||
}
|
||||
|
||||
m_entityDomain = AZStd::move(entityDomain);
|
||||
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
|
||||
m_entityDomain->ActivateTracking(m_ownedEntities);
|
||||
@@ -225,11 +241,19 @@ namespace Multiplayer
|
||||
{
|
||||
AZ::Entity* entity = it->second;
|
||||
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
|
||||
AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId());
|
||||
entityBounds.Expand(AZ::Vector3(0.01f));
|
||||
if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
const AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId());
|
||||
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax());
|
||||
debugDisplay->SetColor(AZ::Colors::Black);
|
||||
debugDisplay->SetAlpha(0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
debugDisplay->SetColor(AZ::Colors::DeepSkyBlue);
|
||||
debugDisplay->SetAlpha(0.25f);
|
||||
}
|
||||
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax());
|
||||
}
|
||||
|
||||
if (m_entityDomain != nullptr)
|
||||
@@ -272,15 +296,32 @@ namespace Multiplayer
|
||||
bool safeToExit = true;
|
||||
NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId);
|
||||
|
||||
// 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)
|
||||
//{
|
||||
// if (hierarchyController->GetParentRelatedEntity())
|
||||
// {
|
||||
// safeToExit = false;
|
||||
// }
|
||||
//}
|
||||
// We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together
|
||||
NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController<NetworkHierarchyRootComponentController>();
|
||||
NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController<NetworkHierarchyChildComponentController>();
|
||||
|
||||
// Find the root entity
|
||||
AZ::Entity* hierarchyRootEntity = nullptr;
|
||||
if (hierarchyRootController)
|
||||
{
|
||||
hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot();
|
||||
}
|
||||
else if (hierarchyChildController)
|
||||
{
|
||||
hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot();
|
||||
}
|
||||
|
||||
if (hierarchyRootEntity)
|
||||
{
|
||||
NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId());
|
||||
ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId);
|
||||
|
||||
// Check if the root entity is still tracked by this authority
|
||||
if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController())
|
||||
{
|
||||
safeToExit = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that we aren't already planning to remove this entity
|
||||
if (safeToExit)
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Multiplayer
|
||||
, m_entityId(rhs.m_entityId)
|
||||
, m_isDelete(rhs.m_isDelete)
|
||||
, m_wasMigrated(rhs.m_wasMigrated)
|
||||
, m_takeOwnership(rhs.m_takeOwnership)
|
||||
, m_hasValidPrefabId(rhs.m_hasValidPrefabId)
|
||||
, m_prefabEntityId(rhs.m_prefabEntityId)
|
||||
, m_data(AZStd::move(rhs.m_data))
|
||||
@@ -31,7 +30,6 @@ namespace Multiplayer
|
||||
, m_entityId(rhs.m_entityId)
|
||||
, m_isDelete(rhs.m_isDelete)
|
||||
, m_wasMigrated(rhs.m_wasMigrated)
|
||||
, m_takeOwnership(rhs.m_takeOwnership)
|
||||
, m_hasValidPrefabId(rhs.m_hasValidPrefabId)
|
||||
, m_prefabEntityId(rhs.m_prefabEntityId)
|
||||
{
|
||||
@@ -58,11 +56,10 @@ namespace Multiplayer
|
||||
;
|
||||
}
|
||||
|
||||
NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityId entityId, bool wasMigrated, bool takeOwnership)
|
||||
NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityId entityId, bool wasMigrated)
|
||||
: m_entityId(entityId)
|
||||
, m_isDelete(true)
|
||||
, m_wasMigrated(wasMigrated)
|
||||
, m_takeOwnership(takeOwnership)
|
||||
{
|
||||
// this is a delete entity message c-tor
|
||||
}
|
||||
@@ -73,7 +70,6 @@ namespace Multiplayer
|
||||
m_entityId = rhs.m_entityId;
|
||||
m_isDelete = rhs.m_isDelete;
|
||||
m_wasMigrated = rhs.m_wasMigrated;
|
||||
m_takeOwnership = rhs.m_takeOwnership;
|
||||
m_hasValidPrefabId = rhs.m_hasValidPrefabId;
|
||||
m_prefabEntityId = rhs.m_prefabEntityId;
|
||||
m_data = AZStd::move(rhs.m_data);
|
||||
@@ -86,7 +82,6 @@ namespace Multiplayer
|
||||
m_entityId = rhs.m_entityId;
|
||||
m_isDelete = rhs.m_isDelete;
|
||||
m_wasMigrated = rhs.m_wasMigrated;
|
||||
m_takeOwnership = rhs.m_takeOwnership;
|
||||
m_hasValidPrefabId = rhs.m_hasValidPrefabId;
|
||||
m_prefabEntityId = rhs.m_prefabEntityId;
|
||||
if (rhs.m_data != nullptr)
|
||||
@@ -104,7 +99,6 @@ namespace Multiplayer
|
||||
&& (m_entityId == rhs.m_entityId)
|
||||
&& (m_isDelete == rhs.m_isDelete)
|
||||
&& (m_wasMigrated == rhs.m_wasMigrated)
|
||||
&& (m_takeOwnership == rhs.m_takeOwnership)
|
||||
&& (m_hasValidPrefabId == rhs.m_hasValidPrefabId)
|
||||
&& (m_prefabEntityId == rhs.m_prefabEntityId));
|
||||
}
|
||||
@@ -160,11 +154,6 @@ namespace Multiplayer
|
||||
return m_wasMigrated;
|
||||
}
|
||||
|
||||
bool NetworkEntityUpdateMessage::GetTakeOwnership() const
|
||||
{
|
||||
return m_takeOwnership;
|
||||
}
|
||||
|
||||
bool NetworkEntityUpdateMessage::GetHasValidPrefabId() const
|
||||
{
|
||||
return m_hasValidPrefabId;
|
||||
@@ -210,17 +199,15 @@ namespace Multiplayer
|
||||
serializer.Serialize(m_entityId, "EntityId");
|
||||
|
||||
// Use the upper 4 bits for boolean flags, and the lower 4 bits for the network role
|
||||
uint8_t networkTypeAndFlags = (m_isDelete ? 0x80 : 0x00)
|
||||
| (m_wasMigrated ? 0x40 : 0x00)
|
||||
| (m_takeOwnership ? 0x20 : 0x00)
|
||||
uint8_t networkTypeAndFlags = (m_isDelete ? 0x40 : 0x00)
|
||||
| (m_wasMigrated ? 0x20 : 0x00)
|
||||
| (m_hasValidPrefabId ? 0x10 : 0x00)
|
||||
| static_cast<uint8_t>(m_networkRole);
|
||||
|
||||
if (serializer.Serialize(networkTypeAndFlags, "TypeAndFlags"))
|
||||
{
|
||||
m_isDelete = (networkTypeAndFlags & 0x80) == 0x80;
|
||||
m_wasMigrated = (networkTypeAndFlags & 0x40) == 0x40;
|
||||
m_takeOwnership = (networkTypeAndFlags & 0x20) == 0x20;
|
||||
m_isDelete = (networkTypeAndFlags & 0x40) == 0x40;
|
||||
m_wasMigrated = (networkTypeAndFlags & 0x20) == 0x20;
|
||||
m_hasValidPrefabId = (networkTypeAndFlags & 0x10) == 0x10;
|
||||
m_networkRole = static_cast<NetEntityRole>(networkTypeAndFlags & 0x0F);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkInput/NetworkInputArray.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInputArray.h>
|
||||
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzNetworking/Serialization/DeltaSerializer.h>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkInput/NetworkInputChild.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInputChild.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkInput/NetworkInputHistory.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInputHistory.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkInput/NetworkInputMigrationVector.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInputMigrationVector.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Multiplayer
|
||||
auto serializer = [](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool {
|
||||
AZ::IO::ByteContainerStream stream(&output);
|
||||
auto& asset = object.GetAsset();
|
||||
return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_JSON, &asset, asset.GetType());
|
||||
return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_BINARY, &asset, asset.GetType());
|
||||
};
|
||||
|
||||
auto&& [object, networkSpawnable] =
|
||||
|
||||
+26
-31
@@ -9,6 +9,7 @@
|
||||
#include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <AzFramework/Visibility/IVisibilitySystem.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
@@ -91,17 +92,10 @@ namespace Multiplayer
|
||||
return m_isPoorConnection ? sv_MinEntitiesToReplicate : sv_MaxEntitiesToReplicate;
|
||||
}
|
||||
|
||||
bool ServerToClientReplicationWindow::IsInWindow(const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const
|
||||
bool ServerToClientReplicationWindow::IsInWindow([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const
|
||||
{
|
||||
// TODO: Clean up this interface, this function is used for server->server migrations, and probably shouldn't be exposed in it's current setup
|
||||
AZ_Assert(false, "IsInWindow should not be called on the ServerToClientReplicationWindow");
|
||||
outNetworkRole = NetEntityRole::InvalidRole;
|
||||
auto iter = m_replicationSet.find(entityHandle);
|
||||
if (iter != m_replicationSet.end())
|
||||
{
|
||||
outNetworkRole = iter->second.m_netEntityRole;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -145,7 +139,7 @@ namespace Multiplayer
|
||||
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
|
||||
IFilterEntityManager* filterEntityManager = GetMultiplayer()->GetFilterEntityManager();
|
||||
|
||||
// Add all the neighbors
|
||||
// Add all the neighbours
|
||||
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
|
||||
{
|
||||
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
|
||||
@@ -174,11 +168,11 @@ namespace Multiplayer
|
||||
// Note: Do not add any Client entities after this point, otherwise you stomp over the Autonomous mode
|
||||
m_replicationSet[m_controlledEntity] = { NetEntityRole::Autonomous, 1.0f }; // Always replicate autonomous entities
|
||||
|
||||
//auto hierarchyController = FindController<EntityHierarchyComponent::Authority>(m_ControlledEntity);
|
||||
//if (hierarchyController != nullptr)
|
||||
//{
|
||||
// CollectControlledEntitiesRecursive(m_replicationSet, *hierarchyController);
|
||||
//}
|
||||
auto* hierarchyComponent = m_controlledEntity.FindComponent<NetworkHierarchyRootComponent>();
|
||||
if (hierarchyComponent != nullptr)
|
||||
{
|
||||
UpdateHierarchyReplicationSet(m_replicationSet, *hierarchyComponent);
|
||||
}
|
||||
}
|
||||
|
||||
AzNetworking::PacketId ServerToClientReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector)
|
||||
@@ -300,7 +294,6 @@ namespace Multiplayer
|
||||
void ServerToClientReplicationWindow::AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, [[maybe_unused]] float distanceSquared)
|
||||
{
|
||||
// Assumption: the entity has been checked for filtering prior to this call.
|
||||
|
||||
if (!sv_ReplicateServerProxies)
|
||||
{
|
||||
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
|
||||
@@ -311,11 +304,11 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set
|
||||
const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set
|
||||
const bool isInReplicationSet = m_replicationSet.find(entityHandle) != m_replicationSet.end();
|
||||
if (!isInReplicationSet)
|
||||
{
|
||||
if (isQueueFull) // if our set is full, then we need to remove the worst priority in our set
|
||||
if (isQueueFull) // If our set is full, then we need to remove the worst priority in our set
|
||||
{
|
||||
ConstNetworkEntityHandle removeEnt = m_candidateQueue.top().m_entityHandle;
|
||||
m_candidateQueue.pop();
|
||||
@@ -326,18 +319,20 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
//void ServerToClientReplicationWindow::CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController)
|
||||
//{
|
||||
// auto controlledEnts = hierarchyController.GetChildrenRelatedEntities();
|
||||
// for (auto& controlledEnt : controlledEnts)
|
||||
// {
|
||||
// AZ_Assert(controlledEnt != nullptr, "We have lost a controlled entity unexpectedly");
|
||||
// replicationSet[controlledEnt.GetConstEntity()] = EntityReplicationData(EntityNetworkRoleT::e_Autonomous, EntityPrioritySystem::k_MaxPriority); // Always replicate controlled entities
|
||||
// auto hierarchyController = controlledEnt.FindController<EntityHierarchyComponent::Authority>();
|
||||
// if (hierarchyController != nullptr)
|
||||
// {
|
||||
// CollectControlledEntitiesRecursive(replicationSet, *hierarchyController);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
void ServerToClientReplicationWindow::UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent)
|
||||
{
|
||||
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
|
||||
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
|
||||
|
||||
for (const AZ::Entity* controlledEntity : hierarchyComponent.GetHierarchicalEntities())
|
||||
{
|
||||
NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId());
|
||||
AZ_Assert(controlledNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
|
||||
|
||||
ConstNetworkEntityHandle controlledEntityHandle = networkEntityManager->GetEntity(controlledNetEntitydId);
|
||||
AZ_Assert(controlledEntityHandle != nullptr, "We have lost a controlled entity unexpectedly");
|
||||
|
||||
replicationSet[controlledEntityHandle] = { NetEntityRole::Autonomous, 1.0f };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
namespace Multiplayer
|
||||
{
|
||||
class NetSystemComponent;
|
||||
class NetworkHierarchyRootComponent;
|
||||
|
||||
class ServerToClientReplicationWindow
|
||||
: public IReplicationWindow
|
||||
@@ -56,7 +57,7 @@ namespace Multiplayer
|
||||
void OnEntityActivated(AZ::Entity* entity);
|
||||
void OnEntityDeactivated(AZ::Entity* entity);
|
||||
|
||||
//void CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController);
|
||||
void UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent);
|
||||
|
||||
void EvaluateConnection();
|
||||
void AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, float distanceSquared);
|
||||
@@ -75,8 +76,6 @@ namespace Multiplayer
|
||||
AZ::EntityActivatedEvent::Handler m_entityActivatedEventHandler;
|
||||
AZ::EntityDeactivatedEvent::Handler m_entityDeactivatedEventHandler;
|
||||
|
||||
//NetBindComponent* m_controlledNetBindComponent = nullptr;
|
||||
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
|
||||
// Cached values to detect a poor network connection
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<Component
|
||||
Name="TestMultiplayerComponent"
|
||||
Namespace="MultiplayerTest"
|
||||
OverrideComponent="true"
|
||||
OverrideController="true"
|
||||
OverrideInclude="Tests/TestMultiplayerComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<NetworkInput Type="uint64_t" Name="OwnerId" Init="0" />
|
||||
|
||||
</Component>
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInputArray.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -175,10 +177,10 @@ namespace Multiplayer
|
||||
void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child)
|
||||
{
|
||||
PopulateHierarchicalEntity(root);
|
||||
SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Client);
|
||||
SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Autonomous);
|
||||
|
||||
PopulateHierarchicalEntity(child);
|
||||
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Client);
|
||||
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Autonomous);
|
||||
|
||||
// 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, root.m_netId);
|
||||
@@ -211,9 +213,8 @@ namespace Multiplayer
|
||||
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());
|
||||
ISerializer& serializer = inSerializer;
|
||||
serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
@@ -301,6 +302,36 @@ namespace Multiplayer
|
||||
SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId);
|
||||
}
|
||||
|
||||
TEST_F(ClientSimpleHierarchyTests, ChildHasOwningConnectionIdOfParent)
|
||||
{
|
||||
// disconnect and assign new connection ids
|
||||
SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId);
|
||||
SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId);
|
||||
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 1 });
|
||||
m_child->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 2 });
|
||||
|
||||
const ConnectionId previousConnectionId = m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId();
|
||||
|
||||
// re-attach, child's owning connection id should then be root's connection id
|
||||
SetParentIdOnNetworkTransform(m_child->m_entity, RootNetEntityId);
|
||||
SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, RootNetEntityId);
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId()
|
||||
);
|
||||
|
||||
// detach, the child should roll back to his previous owning connection id
|
||||
SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId);
|
||||
SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId);
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
previousConnectionId
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parent -> Child -> ChildOfChild
|
||||
*/
|
||||
@@ -330,7 +361,7 @@ namespace Multiplayer
|
||||
void CreateDeepHierarchyOnClient(EntityInfo& childOfChild)
|
||||
{
|
||||
PopulateHierarchicalEntity(childOfChild);
|
||||
SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Client);
|
||||
SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Autonomous);
|
||||
|
||||
// we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
|
||||
SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childOfChild->m_netId);
|
||||
@@ -387,4 +418,66 @@ namespace Multiplayer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ClientDeepHierarchyTests, CreateProcessInputTest)
|
||||
{
|
||||
using MultiplayerTest::TestMultiplayerComponent;
|
||||
using MultiplayerTest::TestMultiplayerComponentController;
|
||||
using MultiplayerTest::TestMultiplayerComponentNetworkInput;
|
||||
|
||||
auto* rootNetBind = m_root->m_entity->FindComponent<NetBindComponent>();
|
||||
|
||||
NetworkInputArray inputArray(rootNetBind->GetEntityHandle());
|
||||
NetworkInput& input = inputArray[0];
|
||||
|
||||
const float deltaTime = 0.16f;
|
||||
rootNetBind->CreateInput(input, deltaTime);
|
||||
|
||||
auto ValidateCreatedInput = [](const NetworkInput& input, const HierarchyTests::EntityInfo& entityInfo)
|
||||
{
|
||||
// Validate test input for the root entity's TestMultiplayerComponent
|
||||
auto* testInput = input.FindComponentInput<TestMultiplayerComponentNetworkInput>();
|
||||
EXPECT_NE(testInput, nullptr);
|
||||
|
||||
auto* testMultiplayerComponent = entityInfo.m_entity->FindComponent<TestMultiplayerComponent>();
|
||||
EXPECT_NE(testMultiplayerComponent, nullptr);
|
||||
|
||||
EXPECT_EQ(testInput->m_ownerId, testMultiplayerComponent->GetId());
|
||||
};
|
||||
|
||||
// Validate root input
|
||||
ValidateCreatedInput(input, *m_root);
|
||||
|
||||
// Validate children input
|
||||
{
|
||||
NetworkHierarchyRootComponentNetworkInput* rootHierarchyInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>();
|
||||
const AZStd::vector<NetworkInputChild>& childInputs = rootHierarchyInput->m_childInputs;
|
||||
EXPECT_EQ(childInputs.size(), 2);
|
||||
ValidateCreatedInput(childInputs[0].GetNetworkInput(), *m_child);
|
||||
ValidateCreatedInput(childInputs[1].GetNetworkInput(), *m_childOfChild);
|
||||
}
|
||||
|
||||
// Test ProcessInput
|
||||
{
|
||||
AZStd::unordered_set<NetEntityId> inputProcessedEntities;
|
||||
size_t processInputCallCounter = 0;
|
||||
auto processInputCallback = [&inputProcessedEntities, &processInputCallCounter](NetEntityId netEntityId)
|
||||
{
|
||||
inputProcessedEntities.insert(netEntityId);
|
||||
processInputCallCounter++;
|
||||
};
|
||||
|
||||
// Set the callbacks for processing input. This allows us to inspect how many times the input was processed
|
||||
// and which entity's controller was invoked.
|
||||
m_root->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
|
||||
m_child->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
|
||||
m_childOfChild->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
|
||||
|
||||
rootNetBind->ProcessInput(input, deltaTime);
|
||||
|
||||
EXPECT_EQ(processInputCallCounter, 3);
|
||||
EXPECT_EQ(inputProcessedEntities,
|
||||
AZStd::unordered_set<NetEntityId>({ m_root->m_netId, m_child->m_netId, m_childOfChild->m_netId }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,8 +342,11 @@ namespace Multiplayer
|
||||
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 SendNotifyClientMigrationEvent([[maybe_unused]] AzNetworking::ConnectionId connectionId, [[maybe_unused]] const HostId& hostId,
|
||||
[[maybe_unused]] uint64_t userIdentifier, [[maybe_unused]] ClientInputId lastClientInputId, [[maybe_unused]] NetEntityId netEntityId) override {}
|
||||
void SendNotifyEntityMigrationEvent([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] const HostId& remoteHostId) override {}
|
||||
void RegisterPlayerIdentifierForRejoin(uint64_t, NetEntityId) override {}
|
||||
void CompleteClientMigration(uint64_t, AzNetworking::ConnectionId, const HostId&, ClientInputId) override {}
|
||||
void SetShouldSpawnNetworkEntities([[maybe_unused]] bool value) override {}
|
||||
bool GetShouldSpawnNetworkEntities() const override { return true; }
|
||||
|
||||
@@ -535,9 +538,8 @@ namespace Multiplayer
|
||||
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());
|
||||
ISerializer& serializer = inSerializer;
|
||||
serializer.Serialize(netParentId, "parentEntityId"); // Derived from NetworkTransformComponent.AutoComponent.xml
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
@@ -560,9 +562,8 @@ namespace Multiplayer
|
||||
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());
|
||||
ISerializer& serializer = inSerializer;
|
||||
serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <NetworkEntity/NetworkEntityAuthorityTracker.h>
|
||||
#include <NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Tests/TestMultiplayerComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -93,6 +94,12 @@ namespace Multiplayer
|
||||
m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor());
|
||||
m_netTransformDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_testMultiplayerComponentDescriptor.reset(MultiplayerTest::TestMultiplayerComponent::CreateDescriptor());
|
||||
m_testMultiplayerComponentDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_testInputDriverComponentDescriptor.reset(MultiplayerTest::TestInputDriverComponent::CreateDescriptor());
|
||||
m_testInputDriverComponentDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_mockMultiplayer = AZStd::make_unique<NiceMock<MockMultiplayer>>();
|
||||
AZ::Interface<IMultiplayer>::Register(m_mockMultiplayer.get());
|
||||
|
||||
@@ -103,6 +110,7 @@ namespace Multiplayer
|
||||
GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0);
|
||||
|
||||
m_mockNetworkEntityManager = AZStd::make_unique<NiceMock<MockNetworkEntityManager>>();
|
||||
AZ::Interface<INetworkEntityManager>::Register(m_mockNetworkEntityManager.get());
|
||||
|
||||
ON_CALL(*m_mockNetworkEntityManager, AddEntityToEntityMap(_, _)).WillByDefault(Invoke(this, &HierarchyTests::AddEntityToEntityMap));
|
||||
ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::GetEntity));
|
||||
@@ -136,6 +144,7 @@ namespace Multiplayer
|
||||
m_multiplayerComponentRegistry = AZStd::make_unique<MultiplayerComponentRegistry>();
|
||||
ON_CALL(*m_mockNetworkEntityManager, GetMultiplayerComponentRegistry()).WillByDefault(Return(m_multiplayerComponentRegistry.get()));
|
||||
RegisterMultiplayerComponents();
|
||||
MultiplayerTest::RegisterMultiplayerComponents();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
@@ -157,6 +166,7 @@ namespace Multiplayer
|
||||
|
||||
AZ::Interface<INetworkTime>::Unregister(m_mockNetworkTime.get());
|
||||
AZ::Interface<AZ::ITime>::Unregister(m_mockTime.get());
|
||||
AZ::Interface<INetworkEntityManager>::Unregister(m_mockNetworkEntityManager.get());
|
||||
AZ::Interface<IMultiplayer>::Unregister(m_mockMultiplayer.get());
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(m_mockComponentApplicationRequests.get());
|
||||
|
||||
@@ -165,6 +175,8 @@ namespace Multiplayer
|
||||
m_mockNetworkEntityManager.reset();
|
||||
m_mockMultiplayer.reset();
|
||||
|
||||
m_testInputDriverComponentDescriptor.reset();
|
||||
m_testMultiplayerComponentDescriptor.reset();
|
||||
m_transformDescriptor.reset();
|
||||
m_netTransformDescriptor.reset();
|
||||
m_hierarchyRootDescriptor.reset();
|
||||
@@ -186,6 +198,8 @@ namespace Multiplayer
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyRootDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyChildDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_netTransformDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_testMultiplayerComponentDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_testInputDriverComponentDescriptor;
|
||||
|
||||
AZStd::unique_ptr<NiceMock<MockMultiplayer>> m_mockMultiplayer;
|
||||
AZStd::unique_ptr<MockNetworkEntityManager> m_mockNetworkEntityManager;
|
||||
@@ -303,9 +317,8 @@ namespace Multiplayer
|
||||
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());
|
||||
ISerializer& serializer = inSerializer;
|
||||
serializer.Serialize(netParentId, "parentEntityId"); // Derived from NetworkTransformComponent.AutoComponent.xml
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
@@ -351,9 +364,8 @@ namespace Multiplayer
|
||||
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());
|
||||
ISerializer& serializer = inSerializer;
|
||||
serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
@@ -394,6 +406,9 @@ namespace Multiplayer
|
||||
entityInfo.m_entity->CreateComponent<AzFramework::TransformComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetBindComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetworkTransformComponent>();
|
||||
entityInfo.m_entity->CreateComponent<MultiplayerTest::TestMultiplayerComponent>();
|
||||
entityInfo.m_entity->CreateComponent<MultiplayerTest::TestInputDriverComponent>();
|
||||
|
||||
switch (entityInfo.m_role)
|
||||
{
|
||||
case EntityInfo::Role::Root:
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace UnitTest
|
||||
MOCK_METHOD1(AddServerAcceptanceReceivedHandler, void(Multiplayer::ServerAcceptanceReceivedEvent::Handler&));
|
||||
MOCK_METHOD1(AddSessionInitHandler, void(Multiplayer::SessionInitEvent::Handler&));
|
||||
MOCK_METHOD1(AddSessionShutdownHandler, void(Multiplayer::SessionShutdownEvent::Handler&));
|
||||
MOCK_METHOD3(SendNotifyClientMigrationEvent, void(const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId));
|
||||
MOCK_METHOD5(SendNotifyClientMigrationEvent, void(AzNetworking::ConnectionId, const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId, Multiplayer::NetEntityId));
|
||||
MOCK_METHOD2(SendNotifyEntityMigrationEvent, void(const Multiplayer::ConstNetworkEntityHandle&, const Multiplayer::HostId&));
|
||||
MOCK_METHOD1(SendReadyForEntityUpdates, void(bool));
|
||||
MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs());
|
||||
@@ -42,6 +42,8 @@ namespace UnitTest
|
||||
MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ());
|
||||
MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*));
|
||||
MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ());
|
||||
MOCK_METHOD2(RegisterPlayerIdentifierForRejoin, void(uint64_t, Multiplayer::NetEntityId));
|
||||
MOCK_METHOD4(CompleteClientMigration, void(uint64_t, AzNetworking::ConnectionId, const Multiplayer::HostId&, Multiplayer::ClientInputId));
|
||||
MOCK_METHOD1(SetShouldSpawnNetworkEntities, void(bool));
|
||||
MOCK_CONST_METHOD0(GetShouldSpawnNetworkEntities, bool());
|
||||
};
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInput.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <NetworkInput/NetworkInputArray.h>
|
||||
#include <NetworkInput/NetworkInputHistory.h>
|
||||
#include <NetworkInput/NetworkInputMigrationVector.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInputArray.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInputHistory.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInputMigrationVector.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
@@ -195,6 +195,49 @@ namespace Multiplayer
|
||||
m_child->m_entity.reset();
|
||||
}
|
||||
|
||||
TEST_F(ServerSimpleHierarchyTests, ChildPointsToRootAfterReattachment)
|
||||
{
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchyRoot(),
|
||||
InvalidNetEntityId
|
||||
);
|
||||
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_root->m_entity->GetId());
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchyRoot(),
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->GetNetEntityId()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ServerSimpleHierarchyTests, ChildHasOwningConnectionIdOfParent)
|
||||
{
|
||||
// disconnect and assign new connection ids
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 1 });
|
||||
m_child->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 2 });
|
||||
|
||||
const ConnectionId previousConnectionId = m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId();
|
||||
|
||||
// re-attach, child's owning connection id should then be root's connection id
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_root->m_entity->GetId());
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId()
|
||||
);
|
||||
|
||||
// detach, the child should roll back to his previous owning connection id
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
previousConnectionId
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parent -> Child -> ChildOfChild
|
||||
*/
|
||||
@@ -394,8 +437,8 @@ namespace Multiplayer
|
||||
m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2");
|
||||
|
||||
// remake the hierarchy
|
||||
m_root->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_root->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_root->m_entity->GetId());
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_root->m_entity->GetId());
|
||||
|
||||
EXPECT_EQ(
|
||||
m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities().size(),
|
||||
@@ -406,6 +449,17 @@ namespace Multiplayer
|
||||
m_console->GetCvarValue<uint32_t>("bg_hierarchyEntityMaxLimit", currentMaxLimit);
|
||||
}
|
||||
|
||||
TEST_F(ServerDeepHierarchyTests, ReattachMiddleChildRebuildInvokedTwice)
|
||||
{
|
||||
MockNetworkHierarchyCallbackHandler mock;
|
||||
EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root->m_entity->GetId())).Times(2);
|
||||
|
||||
m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler);
|
||||
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_root->m_entity->GetId());
|
||||
}
|
||||
|
||||
/*
|
||||
* Parent -> Child -> Child Of Child
|
||||
* -> Child2 -> Child Of Child2
|
||||
@@ -533,11 +587,11 @@ namespace Multiplayer
|
||||
);
|
||||
EXPECT_EQ(
|
||||
m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities()[2],
|
||||
m_childOfChild->m_entity.get()
|
||||
m_child2->m_entity.get()
|
||||
);
|
||||
EXPECT_EQ(
|
||||
m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities()[3],
|
||||
m_child2->m_entity.get()
|
||||
m_childOfChild->m_entity.get()
|
||||
);
|
||||
EXPECT_EQ(
|
||||
m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities()[4],
|
||||
@@ -811,6 +865,22 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ServerHierarchyOfHierarchyTests, InnerChildrenPointToInnerRootAfterDetachmentFromTopRoot)
|
||||
{
|
||||
m_root2->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_root->m_entity->GetId());
|
||||
// detach
|
||||
m_root2->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child2->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchyRoot(),
|
||||
m_root2->m_entity->FindComponent<NetBindComponent>()->GetNetEntityId()
|
||||
);
|
||||
EXPECT_EQ(
|
||||
m_childOfChild2->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchyRoot(),
|
||||
m_root2->m_entity->FindComponent<NetBindComponent>()->GetNetEntityId()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child)
|
||||
{
|
||||
m_root2->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_childOfChild->m_entity->GetId());
|
||||
@@ -994,6 +1064,59 @@ namespace Multiplayer
|
||||
m_console->GetCvarValue<uint32_t>("bg_hierarchyEntityMaxLimit", currentMaxLimit);
|
||||
}
|
||||
|
||||
TEST_F(ServerHierarchyOfHierarchyTests, InnerRootAndItsChildrenHaveOwningConnectionIdOfTopRoot)
|
||||
{
|
||||
// Assign new connection ids.
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 1 });
|
||||
m_root2->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 2 });
|
||||
|
||||
// Attach then inner hierarchy's owning connection id should then be top root's connection id.
|
||||
m_root2->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_childOfChild->m_entity->GetId());
|
||||
|
||||
EXPECT_EQ(
|
||||
m_root2->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId()
|
||||
);
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child2->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId()
|
||||
);
|
||||
|
||||
EXPECT_EQ(
|
||||
m_childOfChild2->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ServerHierarchyOfHierarchyTests, InnerRootAndItsChildrenHaveTheirOriginalOwningConnectionIdAfterDetachingFromTopRoot)
|
||||
{
|
||||
// Assign new connection ids.
|
||||
m_root->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 1 });
|
||||
m_root2->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 2 });
|
||||
|
||||
// Attach then inner hierarchy's owning connection id should then be top root's connection id.
|
||||
m_root2->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_childOfChild->m_entity->GetId());
|
||||
|
||||
// detach, inner hierarchy should roll back to his previous owning connection id
|
||||
m_root2->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
|
||||
EXPECT_EQ(
|
||||
m_root2->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
ConnectionId{ 2 }
|
||||
);
|
||||
|
||||
EXPECT_EQ(
|
||||
m_child2->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
m_root2->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId()
|
||||
);
|
||||
|
||||
EXPECT_EQ(
|
||||
m_childOfChild2->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
|
||||
m_root2->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId()
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parent -> Child -> ChildOfChild (not marked as in a hierarchy)
|
||||
*/
|
||||
@@ -1230,4 +1353,17 @@ namespace Multiplayer
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ServerHierarchyWithThreeRoots, InnerRootLeftTopRootThenLastChildGetsJoinedEventOnce)
|
||||
{
|
||||
m_root2->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_childOfChild->m_entity->GetId());
|
||||
m_root3->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_childOfChild->m_entity->GetId());
|
||||
|
||||
MockNetworkHierarchyCallbackHandler mock;
|
||||
EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root3->m_entity->GetId()));
|
||||
|
||||
m_childOfChild3->m_entity->FindComponent<NetworkHierarchyChildComponent>()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler);
|
||||
|
||||
m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 <Tests/TestMultiplayerComponent.h>
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace MultiplayerTest
|
||||
{
|
||||
void TestInputDriverComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<TestInputDriverComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void TestMultiplayerComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<TestMultiplayerComponent, TestMultiplayerComponentBase>()
|
||||
->Version(1);
|
||||
}
|
||||
TestMultiplayerComponentBase::Reflect(context);
|
||||
}
|
||||
|
||||
void TestMultiplayerComponent::OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
}
|
||||
|
||||
TestMultiplayerComponentController::TestMultiplayerComponentController(TestMultiplayerComponent& parent)
|
||||
: TestMultiplayerComponentControllerBase(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponentController::CreateInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
|
||||
{
|
||||
auto* networkInput = input.FindComponentInput<TestMultiplayerComponentNetworkInput>();
|
||||
networkInput->m_ownerId = GetParent().GetId();
|
||||
}
|
||||
|
||||
void TestMultiplayerComponentController::ProcessInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
|
||||
{
|
||||
auto& component = GetParent();
|
||||
[[maybe_unused]] auto* networkInput = input.FindComponentInput<TestMultiplayerComponentNetworkInput>();
|
||||
AZ_Assert(networkInput->m_ownerId == component.GetId(), "Input Id doesn't match the owner component Id");
|
||||
|
||||
if (component.m_processInputCallback)
|
||||
{
|
||||
component.m_processInputCallback(GetNetEntityId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Tests/AutoGen/TestMultiplayerComponent.AutoComponent.h>
|
||||
|
||||
namespace MultiplayerTest
|
||||
{
|
||||
// Dummy class for satisfying "MultiplayerInputDriver" component dependency
|
||||
class TestInputDriverComponent : public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(TestInputDriverComponent, "{C3877905-3B61-45AE-A636-9845C3AAA39D}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.emplace_back(AZ_CRC_CE("MultiplayerInputDriver"));
|
||||
}
|
||||
|
||||
void Activate() override {}
|
||||
void Deactivate() override {}
|
||||
};
|
||||
|
||||
// Test multiplayer component with ability to create and process network input
|
||||
class TestMultiplayerComponent
|
||||
: public TestMultiplayerComponentBase
|
||||
{
|
||||
public:
|
||||
AZ_MULTIPLAYER_COMPONENT(MultiplayerTest::TestMultiplayerComponent, s_testMultiplayerComponentConcreteUuid, MultiplayerTest::TestMultiplayerComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
void OnInit() override;
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
AZStd::function<void(Multiplayer::NetEntityId)> m_processInputCallback;
|
||||
};
|
||||
|
||||
// Multiplayer controller for the test component
|
||||
class TestMultiplayerComponentController
|
||||
: public TestMultiplayerComponentControllerBase
|
||||
{
|
||||
public:
|
||||
TestMultiplayerComponentController(TestMultiplayerComponent& parent);
|
||||
|
||||
//! TestMultiplayerComponentControllerBase
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
//! MultiplayerController interface
|
||||
void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override;
|
||||
void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override;
|
||||
};
|
||||
}
|
||||
@@ -46,6 +46,10 @@ set(FILES
|
||||
Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h
|
||||
Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h
|
||||
Include/Multiplayer/NetworkInput/NetworkInput.h
|
||||
Include/Multiplayer/NetworkInput/NetworkInputArray.h
|
||||
Include/Multiplayer/NetworkInput/NetworkInputChild.h
|
||||
Include/Multiplayer/NetworkInput/NetworkInputHistory.h
|
||||
Include/Multiplayer/NetworkInput/NetworkInputMigrationVector.h
|
||||
Include/Multiplayer/NetworkTime/INetworkTime.h
|
||||
Include/Multiplayer/NetworkTime/RewindableArray.h
|
||||
Include/Multiplayer/NetworkTime/RewindableArray.inl
|
||||
@@ -115,13 +119,9 @@ set(FILES
|
||||
Source/NetworkEntity/NetworkSpawnableLibrary.h
|
||||
Source/NetworkInput/NetworkInput.cpp
|
||||
Source/NetworkInput/NetworkInputArray.cpp
|
||||
Source/NetworkInput/NetworkInputArray.h
|
||||
Source/NetworkInput/NetworkInputChild.cpp
|
||||
Source/NetworkInput/NetworkInputChild.h
|
||||
Source/NetworkInput/NetworkInputHistory.cpp
|
||||
Source/NetworkInput/NetworkInputHistory.h
|
||||
Source/NetworkInput/NetworkInputMigrationVector.cpp
|
||||
Source/NetworkInput/NetworkInputMigrationVector.h
|
||||
Source/NetworkTime/NetworkTime.cpp
|
||||
Source/NetworkTime/NetworkTime.h
|
||||
Source/Pipeline/NetworkSpawnableHolderComponent.cpp
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja
|
||||
Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja
|
||||
Include/Multiplayer/AutoGen/AutoComponent_Common.jinja
|
||||
Include/Multiplayer/AutoGen/AutoComponent_Header.jinja
|
||||
Include/Multiplayer/AutoGen/AutoComponent_Source.jinja
|
||||
Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml
|
||||
Tests/ClientHierarchyTests.cpp
|
||||
Tests/ServerHierarchyBenchmarks.cpp
|
||||
Tests/CommonHierarchySetup.h
|
||||
@@ -20,4 +26,6 @@ set(FILES
|
||||
Tests/RewindableContainerTests.cpp
|
||||
Tests/RewindableObjectTests.cpp
|
||||
Tests/ServerHierarchyTests.cpp
|
||||
Tests/TestMultiplayerComponent.h
|
||||
Tests/TestMultiplayerComponent.cpp
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user