merging latest dev + resolving 1 conflict
Signed-off-by: Gene Walters <genewalt@amazon.com>
This commit is contained in:
@@ -13,10 +13,9 @@
|
||||
</Packet>
|
||||
|
||||
<Packet Name="Accept" HandshakePacket="true" Desc="Server accept packet">
|
||||
<Member Type="Multiplayer::HostId" Name="hostId" Init="Multiplayer::InvalidHostId" />
|
||||
<Member Type="Multiplayer::LongNetworkString" Name="map" />
|
||||
</Packet>
|
||||
|
||||
|
||||
<Packet Name="ReadyForEntityUpdates" Desc="Client confirming it is ready to receive entity updates">
|
||||
<Member Type="bool" Name="readyForEntityUpdates" />
|
||||
</Packet>
|
||||
@@ -32,16 +31,16 @@
|
||||
<Packet Name="EntityUpdates" Desc="A packet that contains multiple entity updates">
|
||||
<Member Type="AZ::TimeMs" Name="hostTimeMs" Init="AZ::TimeMs{ 0 }" />
|
||||
<Member Type="Multiplayer::HostFrameId" Name="hostFrameId" Init="Multiplayer::InvalidHostFrameId" />
|
||||
<Member Type="Multiplayer::NetworkEntityUpdateMessage" Name="entityMessages" Container="Vector" Count="Multiplayer::MaxAggregateEntityMessages" />
|
||||
<Member Type="Multiplayer::NetworkEntityUpdateVector" Name="entityMessages" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="EntityRpcs" Desc="A packet that contains multiple entity rpcs">
|
||||
<Member Type="Multiplayer::NetworkEntityRpcMessage" Name="entityRpcs" Container="Vector" Count="Multiplayer::MaxAggregateRpcMessages" />
|
||||
<Member Type="Multiplayer::NetworkEntityRpcVector" Name="entityRpcs" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="ClientMigration" Desc="Tell a client to migrate to a new server">
|
||||
<Member Type="uint64_t" Name="temporaryUserIdentifier" Init="0" />
|
||||
<Member Type="AzNetworking::IpAddress" Name="remoteServerAddress" Init="AzNetworking::IpAddress()" />
|
||||
<Member Type="AZ::TimeMs" Name="lastInputGameTimeMs" Init="AZ::TimeMs{ 0 }" />
|
||||
<Member Type="uint64_t" Name="temporaryUserIdentifier" Init="0" />
|
||||
<Member Type="Multiplayer::ClientInputId" Name="lastClientInputId" Init="Multiplayer::ClientInputId{ 0 }" />
|
||||
</Packet>
|
||||
</PacketGroup>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<Component
|
||||
Name="NetworkConnectionComponent"
|
||||
Namespace="Multiplayer"
|
||||
OverrideComponent="false"
|
||||
OverrideController="false"
|
||||
OverrideInclude=""
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<NetworkProperty Type="uint64_t" Name="UserIdentifier" Init="0" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
|
||||
<NetworkProperty Type="HostId" Name="MigrationHostId" Init="InvalidHostId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
|
||||
<NetworkProperty Type="NetEntityId" Name="ControlledEntity" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
|
||||
</Component>
|
||||
@@ -10,9 +10,11 @@
|
||||
|
||||
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
|
||||
<NetworkProperty Type="AZ::Vector3" Name="LinearVelocity" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
|
||||
<NetworkProperty Type="AZ::Vector3" Name="AngularVelocity" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
|
||||
|
||||
<RemoteProcedure Name="SendApplyImpulse" InvokeFrom="Server" HandleOn="Authority" IsPublic="true" IsReliable="true" GenerateEventBindings="false" Description="Applies an impulse">
|
||||
<Param Type="AZ::Vector3" Name="impulse" />
|
||||
<Param Type="AZ::Vector3" Name="worldPoint" />
|
||||
</RemoteProcedure>
|
||||
|
||||
</Component>
|
||||
|
||||
@@ -123,8 +123,8 @@ namespace Multiplayer
|
||||
if (IsAutonomous())
|
||||
{
|
||||
m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true);
|
||||
GetParent().GetNetBindComponent()->AddEntityMigrationStartEventHandler(m_migrateStartHandler);
|
||||
GetParent().GetNetBindComponent()->AddEntityMigrationEndEventHandler(m_migrateEndHandler);
|
||||
GetMultiplayer()->AddClientMigrationStartEventHandler(m_migrateStartHandler);
|
||||
GetMultiplayer()->AddClientMigrationEndEventHandler(m_migrateEndHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,4 +57,9 @@ namespace Multiplayer
|
||||
}
|
||||
return nullComponentData;
|
||||
}
|
||||
|
||||
void MultiplayerComponentRegistry::Reset()
|
||||
{
|
||||
m_componentData.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
|
||||
#include <Multiplayer/NetworkInput/NetworkInput.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
@@ -58,7 +60,7 @@ namespace Multiplayer
|
||||
return false;
|
||||
}
|
||||
|
||||
NetBindComponent* netBindComponent = entity-> FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
|
||||
if (!netBindComponent)
|
||||
{
|
||||
AZ_Warning( "NetBindComponent", false, "NetBindComponent IsNetEntityRoleAuthority failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
|
||||
@@ -75,7 +77,7 @@ namespace Multiplayer
|
||||
return false;
|
||||
}
|
||||
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
|
||||
if (!netBindComponent)
|
||||
{
|
||||
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleAutonomous failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
|
||||
@@ -92,7 +94,7 @@ namespace Multiplayer
|
||||
return false;
|
||||
}
|
||||
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
|
||||
if (!netBindComponent)
|
||||
{
|
||||
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleClient failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
|
||||
@@ -109,7 +111,7 @@ namespace Multiplayer
|
||||
return false;
|
||||
}
|
||||
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
|
||||
if (!netBindComponent)
|
||||
{
|
||||
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleServer failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
|
||||
@@ -171,6 +173,8 @@ namespace Multiplayer
|
||||
{
|
||||
GetNetworkEntityManager()->NotifyControllersDeactivated(m_netEntityHandle, EntityIsMigrating::False);
|
||||
}
|
||||
|
||||
GetNetworkEntityTracker()->UnregisterNetBindComponent(this);
|
||||
}
|
||||
|
||||
NetEntityRole NetBindComponent::GetNetEntityRole() const
|
||||
@@ -390,17 +394,7 @@ namespace Multiplayer
|
||||
m_syncRewindEvent.Signal();
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyMigrationStart(ClientInputId migratedInputId)
|
||||
{
|
||||
m_entityMigrationStartEvent.Signal(migratedInputId);
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyMigrationEnd()
|
||||
{
|
||||
m_entityMigrationEndEvent.Signal();
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId)
|
||||
void NetBindComponent::NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId);
|
||||
}
|
||||
@@ -430,16 +424,6 @@ namespace Multiplayer
|
||||
eventHandler.Connect(m_syncRewindEvent);
|
||||
}
|
||||
|
||||
void NetBindComponent::AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler)
|
||||
{
|
||||
eventHandler.Connect(m_entityMigrationStartEvent);
|
||||
}
|
||||
|
||||
void NetBindComponent::AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler)
|
||||
{
|
||||
eventHandler.Connect(m_entityMigrationEndEvent);
|
||||
}
|
||||
|
||||
void NetBindComponent::AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler)
|
||||
{
|
||||
eventHandler.Connect(m_entityServerMigrationEvent);
|
||||
@@ -522,6 +506,9 @@ namespace Multiplayer
|
||||
m_netEntityId = netEntityId;
|
||||
m_netEntityRole = netEntityRole;
|
||||
m_prefabEntityId = prefabEntityId;
|
||||
|
||||
GetNetworkEntityTracker()->RegisterNetBindComponent(entity, this);
|
||||
|
||||
m_netEntityHandle = GetNetworkEntityManager()->AddEntityToEntityMap(m_netEntityId, entity);
|
||||
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Multiplayer/Components/NetworkCharacterComponent.h>
|
||||
#include <Multiplayer/Components/NetworkRigidBodyComponent.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
|
||||
#include <AzFramework/Physics/CharacterBus.h>
|
||||
#include <AzFramework/Physics/Character.h>
|
||||
@@ -19,7 +20,7 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
|
||||
bool CollisionLayerBasedControllerFilter(const physx::PxController& controllerA, const physx::PxController& controllerB)
|
||||
{
|
||||
PHYSX_SCENE_READ_LOCK(controllerA.getActor()->getScene());
|
||||
@@ -82,7 +83,7 @@ namespace Multiplayer
|
||||
|
||||
return physx::PxQueryHitType::eNONE;
|
||||
}
|
||||
|
||||
|
||||
void NetworkCharacterComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
@@ -95,6 +96,17 @@ namespace Multiplayer
|
||||
NetworkCharacterComponentController::Reflect(context);
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
NetworkCharacterComponentBase::GetRequiredServices(required);
|
||||
required.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
|
||||
}
|
||||
|
||||
NetworkCharacterComponent::NetworkCharacterComponent()
|
||||
: m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); })
|
||||
{
|
||||
@@ -116,7 +128,7 @@ namespace Multiplayer
|
||||
callbackManager->SetObjectPreFilter(CollisionLayerBasedObjectPreFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!HasController())
|
||||
{
|
||||
GetNetworkTransformComponent()->TranslationAddEvent(m_translationEventHandler);
|
||||
@@ -134,7 +146,7 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::OnSyncRewind()
|
||||
{
|
||||
{
|
||||
if (m_physicsCharacter == nullptr)
|
||||
{
|
||||
return;
|
||||
@@ -195,7 +207,7 @@ namespace Multiplayer
|
||||
// Ensure any entities that we might interact with are properly synchronized to their rewind state
|
||||
if (IsAuthority())
|
||||
{
|
||||
const AZ::Aabb entityStartBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityLocalBoundsUnion(GetEntity()->GetId());
|
||||
const AZ::Aabb entityStartBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(GetEntity()->GetId());
|
||||
const AZ::Aabb entityFinalBounds = entityStartBounds.GetTranslated(velocity);
|
||||
AZ::Aabb entitySweptBounds = entityStartBounds;
|
||||
entitySweptBounds.AddAabb(entityFinalBounds);
|
||||
|
||||
@@ -57,16 +57,13 @@ namespace Multiplayer
|
||||
NetworkRigidBodyRequestBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
|
||||
GetEntity()->FindComponent<AzFramework::TransformComponent>()->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
GetEntity()->GetTransform()->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
|
||||
m_physicsRigidBodyComponent =
|
||||
Physics::RigidBodyRequestBus::FindFirstHandler(GetEntity()->GetId());
|
||||
AZ_Assert(m_physicsRigidBodyComponent, "PhysX Rigid Body Component is required on entity %s", GetEntity()->GetName().c_str());
|
||||
|
||||
if (!HasController())
|
||||
{
|
||||
m_physicsRigidBodyComponent->SetKinematic(true);
|
||||
}
|
||||
// By default we're kinematic, activating a controller will allow us to simulate
|
||||
m_physicsRigidBodyComponent->SetKinematic(true);
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
@@ -121,18 +118,26 @@ namespace Multiplayer
|
||||
|
||||
NetworkRigidBodyComponentController::NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent)
|
||||
: NetworkRigidBodyComponentControllerBase(parent)
|
||||
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform&) { OnTransformUpdate(); })
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
GetParent().m_physicsRigidBodyComponent->SetKinematic(false);
|
||||
if (IsAuthority())
|
||||
{
|
||||
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
|
||||
rigidBody->SetLinearVelocity(GetLinearVelocity());
|
||||
rigidBody->SetAngularVelocity(GetAngularVelocity());
|
||||
GetEntity()->GetTransform()->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
GetParent().m_physicsRigidBodyComponent->SetKinematic(true);
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponentController::HandleSendApplyImpulse
|
||||
@@ -145,4 +150,11 @@ namespace Multiplayer
|
||||
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
|
||||
rigidBody->ApplyLinearImpulseAtWorldPoint(impulse, worldPoint);
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponentController::OnTransformUpdate()
|
||||
{
|
||||
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
|
||||
SetLinearVelocity(rigidBody->GetLinearVelocity());
|
||||
SetAngularVelocity(rigidBody->GetAngularVelocity());
|
||||
}
|
||||
} // namespace Multiplayer
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
void NetworkTransformComponent::NetworkTransformComponent::Reflect(AZ::ReflectContext* context)
|
||||
void NetworkTransformComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
@@ -43,6 +43,11 @@ namespace Multiplayer
|
||||
GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler);
|
||||
GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler);
|
||||
ParentEntityIdAddEvent(m_parentChangedEventHandler);
|
||||
|
||||
if (!HasController())
|
||||
{
|
||||
OnParentChanged(GetParentEntityId());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
@@ -77,9 +82,20 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetTransformComponent()->GetWorldTM().IsClose(blendTransform))
|
||||
AzFramework::TransformComponent* transformComponent = GetTransformComponent();
|
||||
if (GetParentEntityId() == InvalidNetEntityId)
|
||||
{
|
||||
GetTransformComponent()->SetWorldTM(blendTransform);
|
||||
if (!transformComponent->GetWorldTM().IsClose(blendTransform))
|
||||
{
|
||||
transformComponent->SetWorldTM(blendTransform);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!transformComponent->GetLocalTM().IsClose(blendTransform))
|
||||
{
|
||||
transformComponent->SetLocalTM(blendTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,31 +109,45 @@ namespace Multiplayer
|
||||
targetTransform.SetUniformScale(GetScale());
|
||||
|
||||
// Hard set the entities transform
|
||||
if (!GetTransformComponent()->GetWorldTM().IsClose(targetTransform))
|
||||
AzFramework::TransformComponent* transformComponent = GetTransformComponent();
|
||||
if (GetParentEntityId() == InvalidNetEntityId)
|
||||
{
|
||||
GetTransformComponent()->SetWorldTM(targetTransform);
|
||||
if (!transformComponent->GetWorldTM().IsClose(targetTransform))
|
||||
{
|
||||
transformComponent->SetWorldTM(targetTransform);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!transformComponent->GetLocalTM().IsClose(targetTransform))
|
||||
{
|
||||
transformComponent->SetLocalTM(targetTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnParentChanged(NetEntityId parentId)
|
||||
{
|
||||
const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId);
|
||||
if (parentEntityHandle.Exists())
|
||||
if (AZ::TransformInterface* transformComponent = GetEntity()->GetTransform())
|
||||
{
|
||||
if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity())
|
||||
const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId);
|
||||
if (parentEntityHandle.Exists())
|
||||
{
|
||||
GetEntity()->GetTransform()->SetParent(parentEntity->GetId());
|
||||
if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity())
|
||||
{
|
||||
transformComponent->SetParent(parentEntity->GetId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
transformComponent->SetParent(AZ::EntityId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetEntity()->GetTransform()->SetParent(AZ::EntityId());
|
||||
}
|
||||
}
|
||||
|
||||
NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent)
|
||||
: NetworkTransformComponentControllerBase(parent)
|
||||
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformChangedEvent(worldTm); })
|
||||
, m_transformChangedHandler([this](const AZ::Transform& localTm, const AZ::Transform& worldTm) { OnTransformChangedEvent(localTm, worldTm); })
|
||||
, m_parentIdChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId newParent) { OnParentIdChangedEvent(oldParent, newParent); })
|
||||
{
|
||||
;
|
||||
@@ -125,11 +155,14 @@ namespace Multiplayer
|
||||
|
||||
void NetworkTransformComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
GetParent().GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
OnTransformChangedEvent(GetParent().GetTransformComponent()->GetWorldTM());
|
||||
if (AzFramework::TransformComponent* parentTransform = GetParent().GetTransformComponent())
|
||||
{
|
||||
parentTransform->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
OnTransformChangedEvent(parentTransform->GetLocalTM(), parentTransform->GetWorldTM());
|
||||
|
||||
GetParent().GetTransformComponent()->BindParentChangedEventHandler(m_parentIdChangedHandler);
|
||||
OnParentIdChangedEvent(AZ::EntityId(), GetParent().GetTransformComponent()->GetParentId());
|
||||
parentTransform->BindParentChangedEventHandler(m_parentIdChangedHandler);
|
||||
OnParentIdChangedEvent(AZ::EntityId(), parentTransform->GetParentId());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
@@ -137,11 +170,12 @@ namespace Multiplayer
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkTransformComponentController::OnTransformChangedEvent(const AZ::Transform& worldTm)
|
||||
void NetworkTransformComponentController::OnTransformChangedEvent(const AZ::Transform& localTm, const AZ::Transform& worldTm)
|
||||
{
|
||||
SetRotation(worldTm.GetRotation());
|
||||
SetTranslation(worldTm.GetTranslation());
|
||||
SetScale(worldTm.GetUniformScale());
|
||||
const AZ::Transform& localOrWorld = GetParentEntityId() == InvalidNetEntityId ? worldTm : localTm;
|
||||
SetRotation(localOrWorld.GetRotation());
|
||||
SetTranslation(localOrWorld.GetTranslation());
|
||||
SetScale(localOrWorld.GetUniformScale());
|
||||
}
|
||||
|
||||
void NetworkTransformComponentController::OnParentIdChangedEvent([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent)
|
||||
@@ -150,7 +184,10 @@ namespace Multiplayer
|
||||
if (parentEntity)
|
||||
{
|
||||
const ConstNetworkEntityHandle parentHandle(parentEntity, GetNetworkEntityTracker());
|
||||
SetParentEntityId(parentHandle.GetNetEntityId());
|
||||
if (parentHandle.Exists())
|
||||
{
|
||||
SetParentEntityId(parentHandle.GetNetEntityId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Multiplayer
|
||||
// This can be used to help mitigate client side performance when large numbers of entities are created off the network
|
||||
AZ_CVAR(uint32_t, cl_ClientMaxRemoteEntitiesPendingCreationCount, AZStd::numeric_limits<uint32_t>::max(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client");
|
||||
AZ_CVAR(AZ::TimeMs, cl_ClientEntityReplicatorPendingRemovalTimeMs, AZ::TimeMs{ 10000 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "How long should wait prior to removing an entity for the client through a change in the replication window, entity deletes are still immediate");
|
||||
AZ_CVAR(AZ::TimeMs, cl_DefaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
|
||||
|
||||
ClientToServerConnectionData::ClientToServerConnectionData
|
||||
(
|
||||
@@ -26,6 +27,7 @@ namespace Multiplayer
|
||||
{
|
||||
m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(cl_ClientMaxRemoteEntitiesPendingCreationCount);
|
||||
m_entityReplicationManager.SetEntityPendingRemovalMs(cl_ClientEntityReplicatorPendingRemovalTimeMs);
|
||||
m_entityReplicationManager.SetEntityActivationTimeSliceMs(cl_DefaultNetworkEntityActivationTimeSliceMs);
|
||||
}
|
||||
|
||||
ClientToServerConnectionData::~ClientToServerConnectionData()
|
||||
@@ -48,9 +50,9 @@ namespace Multiplayer
|
||||
return m_entityReplicationManager;
|
||||
}
|
||||
|
||||
void ClientToServerConnectionData::Update(AZ::TimeMs hostTimeMs)
|
||||
void ClientToServerConnectionData::Update()
|
||||
{
|
||||
m_entityReplicationManager.ActivatePendingEntities();
|
||||
m_entityReplicationManager.SendUpdates(hostTimeMs);
|
||||
m_entityReplicationManager.SendUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Multiplayer/ConnectionData/IConnectionData.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -30,9 +30,11 @@ namespace Multiplayer
|
||||
ConnectionDataType GetConnectionDataType() const override;
|
||||
AzNetworking::IConnection* GetConnection() const override;
|
||||
EntityReplicationManager& GetReplicationManager() override;
|
||||
void Update(AZ::TimeMs hostTimeMs) override;
|
||||
void Update() override;
|
||||
bool CanSendUpdates() const override;
|
||||
void SetCanSendUpdates(bool canSendUpdates) override;
|
||||
bool DidHandshake() const override;
|
||||
void SetDidHandshake(bool didHandshake) override;
|
||||
//! @}
|
||||
|
||||
const AZStd::string& GetProviderTicket() const;
|
||||
@@ -43,6 +45,7 @@ namespace Multiplayer
|
||||
AZStd::string m_providerTicket;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
bool m_canSendUpdates = true;
|
||||
bool m_didHandshake = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,14 @@ namespace Multiplayer
|
||||
{
|
||||
m_providerTicket = ticket;
|
||||
}
|
||||
|
||||
inline bool ClientToServerConnectionData::DidHandshake() const
|
||||
{
|
||||
return m_didHandshake;
|
||||
}
|
||||
|
||||
inline void ClientToServerConnectionData::SetDidHandshake(bool didHandshake)
|
||||
{
|
||||
m_didHandshake = didHandshake;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
*/
|
||||
|
||||
#include <Source/ConnectionData/ServerToClientConnectionData.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Multiplayer/Components/LocalPredictionPlayerInputComponent.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <AzNetworking/Utilities/EncryptionCommon.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -25,7 +28,7 @@ namespace Multiplayer
|
||||
)
|
||||
: m_connection(connection)
|
||||
, m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); })
|
||||
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, AzNetworking::ConnectionId connectionId) { OnControlledEntityMigration(entityHandle, remoteHostId, connectionId); })
|
||||
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId) { OnControlledEntityMigration(entityHandle, remoteHostId, connectionId); })
|
||||
, m_controlledEntity(controlledEntity)
|
||||
, m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient)
|
||||
{
|
||||
@@ -66,7 +69,7 @@ namespace Multiplayer
|
||||
return m_entityReplicationManager;
|
||||
}
|
||||
|
||||
void ServerToClientConnectionData::Update(AZ::TimeMs hostTimeMs)
|
||||
void ServerToClientConnectionData::Update()
|
||||
{
|
||||
m_entityReplicationManager.ActivatePendingEntities();
|
||||
|
||||
@@ -76,7 +79,7 @@ namespace Multiplayer
|
||||
// potentially false if we just migrated the player, if that is the case, don't send any more updates
|
||||
if (netBindComponent != nullptr && (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority))
|
||||
{
|
||||
m_entityReplicationManager.SendUpdates(hostTimeMs);
|
||||
m_entityReplicationManager.SendUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,40 +94,32 @@ namespace Multiplayer
|
||||
void ServerToClientConnectionData::OnControlledEntityMigration
|
||||
(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
|
||||
[[maybe_unused]] HostId remoteHostId,
|
||||
[[maybe_unused]] const HostId& remoteHostId,
|
||||
[[maybe_unused]] AzNetworking::ConnectionId connectionId
|
||||
)
|
||||
{
|
||||
//Multiplayer::ServerAddrInfo serverAddr;
|
||||
//if (gNovaGame->GetMultiplayerworkAgent().GetServerToServerNetwork().GetServerAddrInfoFromConnectionId(newConnectionId, serverAddr) == false)
|
||||
//{
|
||||
// AZLOG_WARN("MigrateClient::Failed to find servershard address, userID:%d", static_cast<uint32_t>(GetUserId()));
|
||||
// return;
|
||||
//}
|
||||
//
|
||||
//Multiplayer::GameTimePoint migratedClientGameTimePoint;
|
||||
//
|
||||
//if (m_ControlledEntity != nullptr)
|
||||
//{
|
||||
// if (const PlayerNetworkInputComponent::Authority* pComponent = Multiplayer::FindController<PlayerNetworkInputComponent::Authority>(m_ControlledEntity))
|
||||
// {
|
||||
// migratedClientGameTimePoint = pComponent->GetLastInputId().GetServerGameTimePoint();
|
||||
// }
|
||||
//}
|
||||
//
|
||||
// generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration
|
||||
//const uint64_t randomUserIdentifier = 0;
|
||||
//
|
||||
//// Tell the server a new client is about to join
|
||||
//MultiplayerPackets::NotifyClientMigration notifyClientMigration(randomUserIdentifier);
|
||||
//gNovaGame->GetMultiplayerworkAgent().GetServerToServerNetwork().SendReliablePacket(newConnectionId, notifyClientMigration);
|
||||
//
|
||||
//// Tell the client who to join
|
||||
//MultiplayerPackets::ClientMigration clientMigration(randomUserIdentifier, serverAddr, migratedClientGameTimePoint);
|
||||
//GetConnection()->SendReliablePacket(clientMigration);
|
||||
//
|
||||
//m_controlledEntity = nullptr;
|
||||
//m_canSendUpdates = false;
|
||||
ClientInputId migratedClientInputId = ClientInputId{ 0 };
|
||||
if (m_controlledEntity != nullptr)
|
||||
{
|
||||
auto controller = m_controlledEntity.FindController<LocalPredictionPlayerInputComponentController>();
|
||||
if (controller != nullptr)
|
||||
{
|
||||
migratedClientInputId = controller->GetLastInputId();
|
||||
}
|
||||
}
|
||||
|
||||
// Generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration
|
||||
const uint64_t randomUserIdentifier = AzNetworking::CryptoRand64();
|
||||
|
||||
// Tell the new host that a client is about to (re)join
|
||||
GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, randomUserIdentifier, migratedClientInputId);
|
||||
|
||||
// Tell the client who to join
|
||||
MultiplayerPackets::ClientMigration clientMigration(remoteHostId, randomUserIdentifier, migratedClientInputId);
|
||||
GetConnection()->SendReliablePacket(clientMigration);
|
||||
|
||||
m_controlledEntity = NetworkEntityHandle();
|
||||
m_canSendUpdates = false;
|
||||
}
|
||||
|
||||
void ServerToClientConnectionData::OnGameplayStarted()
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Multiplayer/ConnectionData/IConnectionData.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -30,9 +30,11 @@ namespace Multiplayer
|
||||
ConnectionDataType GetConnectionDataType() const override;
|
||||
AzNetworking::IConnection* GetConnection() const override;
|
||||
EntityReplicationManager& GetReplicationManager() override;
|
||||
void Update(AZ::TimeMs hostTimeMs) override;
|
||||
void Update() override;
|
||||
bool CanSendUpdates() const override;
|
||||
void SetCanSendUpdates(bool canSendUpdates) override;
|
||||
bool DidHandshake() const override;
|
||||
void SetDidHandshake(bool didHandshake) override;
|
||||
//! @}
|
||||
|
||||
NetworkEntityHandle GetPrimaryPlayerEntity();
|
||||
@@ -42,7 +44,7 @@ namespace Multiplayer
|
||||
|
||||
private:
|
||||
void OnControlledEntityRemove();
|
||||
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, AzNetworking::ConnectionId connectionId);
|
||||
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId);
|
||||
void OnGameplayStarted();
|
||||
|
||||
EntityReplicationManager m_entityReplicationManager;
|
||||
@@ -52,6 +54,7 @@ namespace Multiplayer
|
||||
AZStd::string m_providerTicket;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
bool m_canSendUpdates = false;
|
||||
bool m_didHandshake = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -38,4 +38,14 @@ namespace Multiplayer
|
||||
{
|
||||
m_providerTicket = ticket;
|
||||
}
|
||||
|
||||
inline bool ServerToClientConnectionData::DidHandshake() const
|
||||
{
|
||||
return m_didHandshake;
|
||||
}
|
||||
|
||||
inline void ServerToClientConnectionData::SetDidHandshake(bool didHandshake)
|
||||
{
|
||||
m_didHandshake = didHandshake;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Multiplayer
|
||||
{
|
||||
m_networkEditorInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(
|
||||
AZ::Name(MpEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this);
|
||||
m_networkEditorInterface->SetTimeoutEnabled(false);
|
||||
m_networkEditorInterface->SetTimeoutMs(AZ::TimeMs{ 0 }); // Disable timeouts on this network interface
|
||||
if (editorsv_isDedicated)
|
||||
{
|
||||
uint16_t editorServerPort = DefaultServerEditorPort;
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Multiplayer
|
||||
MultiplayerEditorConnection();
|
||||
~MultiplayerEditorConnection() = default;
|
||||
|
||||
bool IsHandshakeComplete() const { return true; };
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerInit& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReady& packet);
|
||||
|
||||
|
||||
@@ -10,6 +10,17 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
void FullOwnershipEntityDomain::SetAabb([[maybe_unused]] const AZ::Aabb& aabb)
|
||||
{
|
||||
; // Do nothing, by definition we own everything
|
||||
}
|
||||
|
||||
const AZ::Aabb& FullOwnershipEntityDomain::GetAabb() const
|
||||
{
|
||||
static AZ::Aabb nullAabb = AZ::Aabb::CreateNull();
|
||||
return nullAabb;
|
||||
}
|
||||
|
||||
bool FullOwnershipEntityDomain::IsInDomain([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const
|
||||
{
|
||||
return true;
|
||||
@@ -20,9 +31,9 @@ namespace Multiplayer
|
||||
;
|
||||
}
|
||||
|
||||
void FullOwnershipEntityDomain::RetrieveEntitiesNotInDomain([[maybe_unused]] EntitiesNotInDomain& outEntitiesNotInDomain) const
|
||||
const IEntityDomain::EntitiesNotInDomain& FullOwnershipEntityDomain::RetrieveEntitiesNotInDomain() const
|
||||
{
|
||||
;
|
||||
return m_entitiesNotInDomain;
|
||||
}
|
||||
|
||||
void FullOwnershipEntityDomain::DebugDraw() const
|
||||
|
||||
@@ -21,10 +21,15 @@ namespace Multiplayer
|
||||
|
||||
//! IEntityDomain overrides.
|
||||
//! @{
|
||||
void SetAabb(const AZ::Aabb& aabb) override;
|
||||
const AZ::Aabb& GetAabb() const override;
|
||||
bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override;
|
||||
void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) override;
|
||||
void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const override;
|
||||
const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const override;
|
||||
void DebugDraw() const override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
EntitiesNotInDomain m_entitiesNotInDomain;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzFramework/Components/CameraBus.h>
|
||||
#include <AzFramework/Session/ISessionRequests.h>
|
||||
#include <AzFramework/Session/SessionConfig.h>
|
||||
@@ -71,7 +72,7 @@ namespace Multiplayer
|
||||
|
||||
AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port");
|
||||
AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The address of the remote server or host to connect to");
|
||||
AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic");
|
||||
AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic");
|
||||
@@ -79,14 +80,13 @@ namespace Multiplayer
|
||||
AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking");
|
||||
AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server");
|
||||
AZ_CVAR(bool, sv_isTransient, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether a dedicated server shuts down if all existing connections disconnect.");
|
||||
AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
|
||||
AZ_CVAR(AZ::TimeMs, sv_serverSendRateMs, AZ::TimeMs{ 50 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of milliseconds between each network update");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The default spawnable to use when a new player connects");
|
||||
AZ_CVAR(float, cl_renderTickBlendBase, 0.15f, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"The base used for blending between network updates, 0.1 will be quite linear, 0.2 or 0.3 will "
|
||||
"slow down quicker and may be better suited to connections with highly variable latency");
|
||||
AZ_CVAR(bool, bg_multiplayerDebugDraw, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables debug draw for the multiplayer gem");
|
||||
|
||||
void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
@@ -94,9 +94,6 @@ namespace Multiplayer
|
||||
{
|
||||
serializeContext->Class<MultiplayerSystemComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
|
||||
serializeContext->Class<HostId>()
|
||||
->Version(1);
|
||||
serializeContext->Class<NetEntityId>()
|
||||
->Version(1);
|
||||
serializeContext->Class<NetComponentId>()
|
||||
@@ -112,7 +109,6 @@ namespace Multiplayer
|
||||
}
|
||||
else if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<HostId>();
|
||||
behaviorContext->Class<NetEntityId>();
|
||||
behaviorContext->Class<NetComponentId>();
|
||||
behaviorContext->Class<PropertyIndex>();
|
||||
@@ -173,7 +169,12 @@ namespace Multiplayer
|
||||
AZ::ConsoleInvokedFrom invokedFrom
|
||||
) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); })
|
||||
{
|
||||
;
|
||||
AZ::Interface<IMultiplayer>::Register(this);
|
||||
}
|
||||
|
||||
MultiplayerSystemComponent::~MultiplayerSystemComponent()
|
||||
{
|
||||
AZ::Interface<IMultiplayer>::Unregister(this);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::Activate()
|
||||
@@ -185,7 +186,6 @@ namespace Multiplayer
|
||||
{
|
||||
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
|
||||
}
|
||||
AZ::Interface<IMultiplayer>::Register(this);
|
||||
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Register(this);
|
||||
|
||||
//! Register our gems multiplayer components to assign NetComponentIds
|
||||
@@ -195,11 +195,12 @@ namespace Multiplayer
|
||||
void MultiplayerSystemComponent::Deactivate()
|
||||
{
|
||||
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Unregister(this);
|
||||
AZ::Interface<IMultiplayer>::Unregister(this);
|
||||
m_consoleCommandHandler.Disconnect();
|
||||
AZ::Interface<INetworking>::Get()->DestroyNetworkInterface(AZ::Name(MpNetworkInterfaceName));
|
||||
AzFramework::SessionNotificationBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
|
||||
m_networkEntityManager.Reset();
|
||||
}
|
||||
|
||||
bool MultiplayerSystemComponent::StartHosting(uint16_t port, bool isDedicated)
|
||||
@@ -208,7 +209,7 @@ namespace Multiplayer
|
||||
return m_networkInterface->Listen(port);
|
||||
}
|
||||
|
||||
bool MultiplayerSystemComponent::Connect(AZStd::string remoteAddress, uint16_t port)
|
||||
bool MultiplayerSystemComponent::Connect(const AZStd::string& remoteAddress, uint16_t port)
|
||||
{
|
||||
InitializeMultiplayer(MultiplayerAgentType::Client);
|
||||
const IpAddress address(remoteAddress.c_str(), port, m_networkInterface->GetType());
|
||||
@@ -310,7 +311,6 @@ namespace Multiplayer
|
||||
void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
const AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f));
|
||||
const AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs();
|
||||
const AZ::TimeMs serverRateMs = static_cast<AZ::TimeMs>(sv_serverSendRateMs);
|
||||
const float serverRateSeconds = static_cast<float>(serverRateMs) / 1000.0f;
|
||||
|
||||
@@ -347,12 +347,12 @@ namespace Multiplayer
|
||||
|
||||
// Send out the game state update to all connections
|
||||
{
|
||||
auto sendNetworkUpdates = [hostTimeMs, &stats](IConnection& connection)
|
||||
auto sendNetworkUpdates = [&stats](IConnection& connection)
|
||||
{
|
||||
if (connection.GetUserData() != nullptr)
|
||||
{
|
||||
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection.GetUserData());
|
||||
connectionData->Update(hostTimeMs);
|
||||
connectionData->Update();
|
||||
if (connectionData->GetConnectionDataType() == ConnectionDataType::ServerToClient)
|
||||
{
|
||||
stats.m_clientConnectionCount++;
|
||||
@@ -394,6 +394,11 @@ namespace Multiplayer
|
||||
{
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
}
|
||||
|
||||
if (bg_multiplayerDebugDraw)
|
||||
{
|
||||
m_networkEntityManager.DebugDraw();
|
||||
}
|
||||
}
|
||||
|
||||
int MultiplayerSystemComponent::GetTickOrder()
|
||||
@@ -441,9 +446,9 @@ namespace Multiplayer
|
||||
MultiplayerPackets::SyncConsole m_syncPacket;
|
||||
};
|
||||
|
||||
bool MultiplayerSystemComponent::IsHandshakeComplete() const
|
||||
bool MultiplayerSystemComponent::IsHandshakeComplete(AzNetworking::IConnection* connection) const
|
||||
{
|
||||
return m_didHandshake;
|
||||
return reinterpret_cast<IConnectionData*>(connection->GetUserData())->DidHandshake();
|
||||
}
|
||||
|
||||
bool MultiplayerSystemComponent::HandleRequest
|
||||
@@ -459,7 +464,7 @@ namespace Multiplayer
|
||||
AzFramework::PlayerConnectionConfig config;
|
||||
config.m_playerConnectionId = aznumeric_cast<uint32_t>(connection->GetConnectionId());
|
||||
config.m_playerSessionId = packet.GetTicket();
|
||||
if(!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get()->ValidatePlayerJoinSession(config))
|
||||
if (!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get()->ValidatePlayerJoinSession(config))
|
||||
{
|
||||
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); };
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
@@ -468,9 +473,9 @@ namespace Multiplayer
|
||||
}
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str());
|
||||
|
||||
if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map)))
|
||||
if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map)))
|
||||
{
|
||||
m_didHandshake = true;
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetDidHandshake(true);
|
||||
|
||||
// Sync our console
|
||||
ConsoleReplicator consoleReplicator(connection);
|
||||
@@ -487,11 +492,9 @@ namespace Multiplayer
|
||||
[[maybe_unused]] MultiplayerPackets::Accept& packet
|
||||
)
|
||||
{
|
||||
m_didHandshake = true;
|
||||
|
||||
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetDidHandshake(true);
|
||||
AZ::CVarFixedString commandString = "sv_map " + packet.GetMap();
|
||||
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(commandString.c_str());
|
||||
|
||||
AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap();
|
||||
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(loadLevelString.c_str());
|
||||
|
||||
@@ -608,7 +611,22 @@ namespace Multiplayer
|
||||
[[maybe_unused]] MultiplayerPackets::ClientMigration& packet
|
||||
)
|
||||
{
|
||||
return false;
|
||||
if (GetAgentType() != MultiplayerAgentType::Client)
|
||||
{
|
||||
// Only clients are allowed to migrate from one server to another
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the temporary user identifier so we can transmit it with our next Connect packet
|
||||
// The new server will use this to re-attach our set of autonomous entities
|
||||
|
||||
// Disconnect our existing server connection
|
||||
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); };
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
AZLOG_INFO("Migrating to new server shard");
|
||||
m_clientMigrationStartEvent.Signal(packet.GetLastClientInputId());
|
||||
m_networkInterface->Connect(packet.GetRemoteServerAddress());
|
||||
return true;
|
||||
}
|
||||
|
||||
ConnectResult MultiplayerSystemComponent::ValidateConnect
|
||||
@@ -642,7 +660,7 @@ namespace Multiplayer
|
||||
else
|
||||
{
|
||||
AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str());
|
||||
m_connAcquiredEvent.Signal(datum);
|
||||
m_connectionAcquiredEvent.Signal(datum);
|
||||
}
|
||||
|
||||
// Hosts will spawn a new default player prefab for the user that just connected
|
||||
@@ -656,27 +674,14 @@ namespace Multiplayer
|
||||
}
|
||||
controlledEntity.Activate();
|
||||
|
||||
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
|
||||
{
|
||||
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
|
||||
}
|
||||
|
||||
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
|
||||
{
|
||||
connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket));
|
||||
}
|
||||
else
|
||||
{
|
||||
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetProviderTicket(providerTicket);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
|
||||
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs);
|
||||
connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket));
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>(connection);
|
||||
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
|
||||
}
|
||||
}
|
||||
@@ -755,13 +760,21 @@ namespace Multiplayer
|
||||
|
||||
if (m_agentType == MultiplayerAgentType::Uninitialized)
|
||||
{
|
||||
m_spawnNetboundEntities = false;
|
||||
if (multiplayerType == MultiplayerAgentType::ClientServer || multiplayerType == MultiplayerAgentType::DedicatedServer)
|
||||
{
|
||||
m_spawnNetboundEntities = true;
|
||||
m_initEvent.Signal(m_networkInterface);
|
||||
|
||||
//const AZ::Aabb worldBounds = AZ::Interface<IPhysics>.Get()->GetWorldBounds();
|
||||
AZStd::unique_ptr<IEntityDomain> newDomain = AZStd::make_unique<FullOwnershipEntityDomain>();
|
||||
m_networkEntityManager.Initialize(InvalidHostId, AZStd::move(newDomain));
|
||||
if (!m_networkEntityManager.IsInitialized())
|
||||
{
|
||||
// Set up a full ownership domain if we didn't construct a domain during the initialize event
|
||||
const AZ::CVarFixedString serverAddr = cl_serveraddr;
|
||||
const uint16_t serverPort = cl_serverport;
|
||||
const AzNetworking::ProtocolType serverProtocol = sv_protocol;
|
||||
const AzNetworking::IpAddress hostId = AzNetworking::IpAddress(serverAddr.c_str(), serverPort, serverProtocol);
|
||||
m_networkEntityManager.Initialize(hostId, AZStd::make_unique<FullOwnershipEntityDomain>());
|
||||
}
|
||||
}
|
||||
}
|
||||
m_agentType = multiplayerType;
|
||||
@@ -780,14 +793,34 @@ namespace Multiplayer
|
||||
AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType));
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_clientMigrationStartEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_clientMigrationEndEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_clientDisconnectedEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_notifyClientMigrationEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddNotifyEntityMigrationEventHandler(NotifyEntityMigrationEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_notifyEntityMigrationEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_connAcquiredEvent);
|
||||
handler.Connect(m_connectionAcquiredEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler)
|
||||
@@ -805,6 +838,16 @@ namespace Multiplayer
|
||||
handler.Connect(m_shutdownEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId)
|
||||
{
|
||||
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
|
||||
{
|
||||
m_notifyEntityMigrationEvent.Signal(entityHandle, remoteHostId);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates)
|
||||
{
|
||||
IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet();
|
||||
@@ -851,6 +894,16 @@ namespace Multiplayer
|
||||
return m_filterEntityManager;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value)
|
||||
{
|
||||
m_spawnNetboundEntities = value;
|
||||
}
|
||||
|
||||
bool MultiplayerSystemComponent::GetShouldSpawnNetworkEntities() const
|
||||
{
|
||||
return m_spawnNetboundEntities;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
const MultiplayerStats& stats = GetStats();
|
||||
@@ -910,7 +963,7 @@ namespace Multiplayer
|
||||
// Unfortunately necessary, as NotifyPreRender can update transforms and thus cause a deadlock inside the vis system
|
||||
AZStd::vector<NetBindComponent*> gatheredEntities;
|
||||
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(viewFrustum,
|
||||
[&gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
|
||||
[this, &gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
|
||||
{
|
||||
gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size());
|
||||
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
|
||||
@@ -918,7 +971,7 @@ namespace Multiplayer
|
||||
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
|
||||
{
|
||||
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = m_networkEntityManager.GetNetworkEntityTracker()->GetNetBindComponent(entity);
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
gatheredEntities.push_back(netBindComponent);
|
||||
@@ -938,7 +991,7 @@ namespace Multiplayer
|
||||
for (auto& iter : *(m_networkEntityManager.GetNetworkEntityTracker()))
|
||||
{
|
||||
AZ::Entity* entity = iter.second;
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = m_networkEntityManager.GetNetworkEntityTracker()->GetNetBindComponent(entity);
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
netBindComponent->NotifyPreRender(deltaTime);
|
||||
@@ -997,7 +1050,10 @@ namespace Multiplayer
|
||||
|
||||
void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
AZ::Interface<IMultiplayer>::Get()->StartHosting(sv_port, sv_isDedicated);
|
||||
if (!AZ::Interface<IMultiplayer>::Get()->StartHosting(sv_port, sv_isDedicated))
|
||||
{
|
||||
AZLOG_ERROR("Failed to start listening on port %u, port is in use?", static_cast<uint32_t>(sv_port));
|
||||
}
|
||||
}
|
||||
AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to");
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Multiplayer
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
MultiplayerSystemComponent();
|
||||
~MultiplayerSystemComponent() override = default;
|
||||
~MultiplayerSystemComponent() override;
|
||||
|
||||
//! AZ::Component overrides.
|
||||
//! @{
|
||||
@@ -78,7 +78,7 @@ namespace Multiplayer
|
||||
int GetTickOrder() override;
|
||||
//! @}
|
||||
|
||||
bool IsHandshakeComplete() const;
|
||||
bool IsHandshakeComplete(AzNetworking::IConnection* connection) const;
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Connect& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Accept& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet);
|
||||
@@ -107,14 +107,20 @@ namespace Multiplayer
|
||||
//! @{
|
||||
MultiplayerAgentType GetAgentType() const override;
|
||||
void InitializeMultiplayer(MultiplayerAgentType state) override;
|
||||
bool StartHosting(uint16_t port, bool isDedicated = true) override;
|
||||
bool Connect(const AZStd::string& remoteAddress, uint16_t port) override;
|
||||
void Terminate(AzNetworking::DisconnectReason reason) override;
|
||||
void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) override;
|
||||
void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) override;
|
||||
void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) override;
|
||||
void AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler) override;
|
||||
void AddNotifyEntityMigrationEventHandler(NotifyEntityMigrationEvent::Handler& handler) override;
|
||||
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
|
||||
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
|
||||
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
|
||||
void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) override;
|
||||
bool StartHosting(uint16_t port, bool isDedicated = true) override;
|
||||
bool Connect(AZStd::string remoteAddress, uint16_t port) override;
|
||||
void Terminate(AzNetworking::DisconnectReason reason) override;
|
||||
void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override;
|
||||
void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override;
|
||||
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
|
||||
AZ::TimeMs GetCurrentHostTimeMs() const override;
|
||||
float GetCurrentBlendFactor() const override;
|
||||
@@ -122,6 +128,8 @@ namespace Multiplayer
|
||||
INetworkEntityManager* GetNetworkEntityManager() override;
|
||||
void SetFilterEntityManager(IFilterEntityManager* entityFilter) override;
|
||||
IFilterEntityManager* GetFilterEntityManager() override;
|
||||
void SetShouldSpawnNetworkEntities(bool value) override;
|
||||
bool GetShouldSpawnNetworkEntities() const override;
|
||||
//! @}
|
||||
|
||||
//! Console commands.
|
||||
@@ -151,9 +159,13 @@ namespace Multiplayer
|
||||
|
||||
SessionInitEvent m_initEvent;
|
||||
SessionShutdownEvent m_shutdownEvent;
|
||||
ConnectionAcquiredEvent m_connAcquiredEvent;
|
||||
ConnectionAcquiredEvent m_connectionAcquiredEvent;
|
||||
ServerAcceptanceReceivedEvent m_serverAcceptanceReceivedEvent;
|
||||
ClientDisconnectedEvent m_clientDisconnectedEvent;
|
||||
ClientMigrationStartEvent m_clientMigrationStartEvent;
|
||||
ClientMigrationEndEvent m_clientMigrationEndEvent;
|
||||
NotifyClientMigrationEvent m_notifyClientMigrationEvent;
|
||||
NotifyEntityMigrationEvent m_notifyEntityMigrationEvent;
|
||||
|
||||
AZStd::queue<AZStd::string> m_pendingConnectionTickets;
|
||||
|
||||
@@ -163,7 +175,7 @@ namespace Multiplayer
|
||||
double m_serverSendAccumulator = 0.0;
|
||||
float m_renderBlendFactor = 0.0f;
|
||||
float m_tickFactor = 0.0f;
|
||||
bool m_didHandshake = false;
|
||||
bool m_spawnNetboundEntities = true;
|
||||
|
||||
#if !defined(AZ_RELEASE_BUILD)
|
||||
MultiplayerEditorConnection m_editorConnectionListener;
|
||||
|
||||
+146
-133
@@ -6,11 +6,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/EntityDomains/IEntityDomain.h>
|
||||
@@ -46,6 +45,7 @@ namespace Multiplayer
|
||||
, m_clearRemovedReplicators([this]() { ClearRemovedReplicators(); }, AZ::Name("EntityReplicationManager::ClearRemovedReplicators"))
|
||||
, m_updateWindow([this]() { UpdateWindow(); }, AZ::Name("EntityReplicationManager::UpdateWindow"))
|
||||
, m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); })
|
||||
, m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); })
|
||||
{
|
||||
// Our max payload size is whatever is passed in, minus room for a udp packetheader
|
||||
m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead;
|
||||
@@ -61,14 +61,16 @@ namespace Multiplayer
|
||||
{
|
||||
networkEntityManager->AddEntityExitDomainHandler(m_entityExitDomainEventHandler);
|
||||
}
|
||||
|
||||
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler);
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SetRemoteHostId(HostId hostId)
|
||||
void EntityReplicationManager::SetRemoteHostId(const HostId& hostId)
|
||||
{
|
||||
m_remoteHostId = hostId;
|
||||
}
|
||||
|
||||
HostId EntityReplicationManager::GetRemoteHostId() const
|
||||
const HostId& EntityReplicationManager::GetRemoteHostId() const
|
||||
{
|
||||
return m_remoteHostId;
|
||||
}
|
||||
@@ -107,10 +109,34 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs)
|
||||
void EntityReplicationManager::SendUpdates()
|
||||
{
|
||||
m_frameTimeMs = AZ::GetElapsedTimeMs();
|
||||
SendEntityUpdates(hostTimeMs);
|
||||
|
||||
{
|
||||
EntityReplicatorList toSendList = GenerateEntityUpdateList();
|
||||
|
||||
AZLOG
|
||||
(
|
||||
NET_ReplicationInfo,
|
||||
"Sending %zd updates from %s to %s",
|
||||
toSendList.size(),
|
||||
GetNetworkEntityManager()->GetHostId().GetString().c_str(),
|
||||
GetRemoteHostId().GetString().c_str()
|
||||
);
|
||||
|
||||
// Prep a replication record for send, at this point, everything needs to be sent
|
||||
for (EntityReplicator* replicator : toSendList)
|
||||
{
|
||||
replicator->GetPropertyPublisher()->PrepareSerialization();
|
||||
}
|
||||
|
||||
// While our to send list is not empty, build up another packet to send
|
||||
do
|
||||
{
|
||||
SendEntityUpdateMessages(toSendList);
|
||||
} while (!toSendList.empty());
|
||||
}
|
||||
|
||||
SendEntityRpcs(m_deferredRpcMessagesReliable, true);
|
||||
SendEntityRpcs(m_deferredRpcMessagesUnreliable, false);
|
||||
@@ -120,9 +146,9 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_ReplicationInfo,
|
||||
"Sending from %u to %u, replicator count %u orphan count %u deferred reliable count %u deferred unreliable count %u",
|
||||
aznumeric_cast<uint32_t>(GetNetworkEntityManager()->GetHostId()),
|
||||
aznumeric_cast<uint32_t>(GetRemoteHostId()),
|
||||
"Sending from %s to %s, replicator count %u orphan count %u deferred reliable count %u deferred unreliable count %u",
|
||||
GetNetworkEntityManager()->GetHostId().GetString().c_str(),
|
||||
GetRemoteHostId().GetString().c_str(),
|
||||
aznumeric_cast<uint32_t>(m_entityReplicatorMap.size()),
|
||||
aznumeric_cast<uint32_t>(m_orphanedEntityRpcs.Size()),
|
||||
aznumeric_cast<uint32_t>(m_deferredRpcMessagesReliable.size()),
|
||||
@@ -130,65 +156,6 @@ namespace Multiplayer
|
||||
);
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SendEntityUpdatesPacketHelper
|
||||
(
|
||||
AZ::TimeMs hostTimeMs,
|
||||
EntityReplicatorList& toSendList,
|
||||
uint32_t maxPayloadSize,
|
||||
AzNetworking::IConnection& connection
|
||||
)
|
||||
{
|
||||
uint32_t pendingPacketSize = 0;
|
||||
EntityReplicatorList replicatorUpdatedList;
|
||||
MultiplayerPackets::EntityUpdates entityUpdatePacket;
|
||||
entityUpdatePacket.SetHostTimeMs(hostTimeMs);
|
||||
entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId());
|
||||
// Serialize everything
|
||||
while (!toSendList.empty())
|
||||
{
|
||||
EntityReplicator* replicator = toSendList.front();
|
||||
NetworkEntityUpdateMessage updateMessage(replicator->GenerateUpdatePacket());
|
||||
|
||||
const uint32_t nextMessageSize = updateMessage.GetEstimatedSerializeSize();
|
||||
|
||||
// Check if we are over our limits
|
||||
const bool payloadFull = (pendingPacketSize + nextMessageSize > maxPayloadSize);
|
||||
const bool capacityReached = (entityUpdatePacket.GetEntityMessages().size() >= entityUpdatePacket.GetEntityMessages().capacity());
|
||||
const bool largeEntityDetected = (payloadFull && replicatorUpdatedList.empty());
|
||||
if (capacityReached || (payloadFull && !largeEntityDetected))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pendingPacketSize += nextMessageSize;
|
||||
entityUpdatePacket.ModifyEntityMessages().push_back(updateMessage);
|
||||
replicatorUpdatedList.push_back(replicator);
|
||||
toSendList.pop_front();
|
||||
|
||||
if (largeEntityDetected)
|
||||
{
|
||||
AZLOG_WARN("\n\n*******************************");
|
||||
AZLOG_WARN
|
||||
(
|
||||
"Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d",
|
||||
aznumeric_cast<uint32_t>(replicator->GetEntityHandle().GetNetEntityId()),
|
||||
maxPayloadSize,
|
||||
nextMessageSize
|
||||
);
|
||||
AZLOG_WARN("*******************************");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const AzNetworking::PacketId sentId = connection.SendUnreliablePacket(entityUpdatePacket);
|
||||
|
||||
// Update the sent things with the packet id
|
||||
for (EntityReplicator* replicator : replicatorUpdatedList)
|
||||
{
|
||||
replicator->GetPropertyPublisher()->FinalizeSerialization(sentId);
|
||||
}
|
||||
}
|
||||
|
||||
EntityReplicationManager::EntityReplicatorList EntityReplicationManager::GenerateEntityUpdateList()
|
||||
{
|
||||
if (m_replicationWindow == nullptr)
|
||||
@@ -260,69 +227,92 @@ namespace Multiplayer
|
||||
return toSendList;
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs)
|
||||
void EntityReplicationManager::SendEntityUpdateMessages(EntityReplicatorList& replicatorList)
|
||||
{
|
||||
EntityReplicatorList toSendList = GenerateEntityUpdateList();
|
||||
|
||||
AZLOG(NET_ReplicationInfo, "Sending %zd updates from %d to %d", toSendList.size(), (uint8_t)GetNetworkEntityManager()->GetHostId(), (uint8_t)GetRemoteHostId());
|
||||
|
||||
// prep a replication record for send, at this point, everything needs to be sent
|
||||
for (EntityReplicator* replicator : toSendList)
|
||||
uint32_t pendingPacketSize = 0;
|
||||
EntityReplicatorList replicatorUpdatedList;
|
||||
NetworkEntityUpdateVector entityUpdates;
|
||||
// Serialize everything
|
||||
while (!replicatorList.empty())
|
||||
{
|
||||
replicator->GetPropertyPublisher()->PrepareSerialization();
|
||||
EntityReplicator* replicator = replicatorList.front();
|
||||
NetworkEntityUpdateMessage updateMessage(replicator->GenerateUpdatePacket());
|
||||
|
||||
const uint32_t nextMessageSize = updateMessage.GetEstimatedSerializeSize();
|
||||
|
||||
// Check if we are over our limits
|
||||
const bool payloadFull = (pendingPacketSize + nextMessageSize > m_maxPayloadSize);
|
||||
const bool capacityReached = (entityUpdates.size() >= entityUpdates.capacity());
|
||||
const bool largeEntityDetected = (payloadFull && replicatorUpdatedList.empty());
|
||||
if (capacityReached || (payloadFull && !largeEntityDetected))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pendingPacketSize += nextMessageSize;
|
||||
entityUpdates.push_back(updateMessage);
|
||||
replicatorUpdatedList.push_back(replicator);
|
||||
replicatorList.pop_front();
|
||||
|
||||
if (largeEntityDetected)
|
||||
{
|
||||
AZLOG_WARN
|
||||
(
|
||||
"Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d",
|
||||
aznumeric_cast<uint32_t>(replicator->GetEntityHandle().GetNetEntityId()),
|
||||
m_maxPayloadSize,
|
||||
nextMessageSize
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// While our to send list is not empty, build up another packet to send
|
||||
do
|
||||
const AzNetworking::PacketId sentId = m_replicationWindow->SendEntityUpdateMessages(entityUpdates);
|
||||
|
||||
// Update the sent things with the packet id
|
||||
for (EntityReplicator* replicator : replicatorUpdatedList)
|
||||
{
|
||||
SendEntityUpdatesPacketHelper(hostTimeMs, toSendList, m_maxPayloadSize, m_connection);
|
||||
} while (!toSendList.empty());
|
||||
replicator->FinalizeSerialization(sentId);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable)
|
||||
void EntityReplicationManager::SendEntityRpcs(RpcMessages& rpcMessages, bool reliable)
|
||||
{
|
||||
while (!deferredRpcs.empty())
|
||||
while (!rpcMessages.empty())
|
||||
{
|
||||
MultiplayerPackets::EntityRpcs entityRpcsPacket;
|
||||
NetworkEntityRpcVector entityRpcs;
|
||||
uint32_t pendingPacketSize = 0;
|
||||
|
||||
while (!deferredRpcs.empty())
|
||||
while (!rpcMessages.empty())
|
||||
{
|
||||
NetworkEntityRpcMessage& message = deferredRpcs.front();
|
||||
NetworkEntityRpcMessage& message = rpcMessages.front();
|
||||
|
||||
const uint32_t nextRpcSize = message.GetEstimatedSerializeSize();
|
||||
|
||||
if ((pendingPacketSize + nextRpcSize) > m_maxPayloadSize)
|
||||
{
|
||||
// We're over our limit, break and send an Rpc packet
|
||||
if (entityRpcsPacket.GetEntityRpcs().size() == 0)
|
||||
if (entityRpcs.size() == 0)
|
||||
{
|
||||
AZLOG(NET_Replicator, "Encountered an RPC that is above our MTU, message will be segmented (object size %u, max allowed size %u)", nextRpcSize, m_maxPayloadSize);
|
||||
entityRpcsPacket.ModifyEntityRpcs().push_back(message);
|
||||
deferredRpcs.pop_front();
|
||||
entityRpcs.push_back(message);
|
||||
rpcMessages.pop_front();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pendingPacketSize += nextRpcSize;
|
||||
if (entityRpcsPacket.GetEntityRpcs().full())
|
||||
if (entityRpcs.full())
|
||||
{
|
||||
// Packet was full, send what we've accumulated so far
|
||||
AZLOG(NET_Replicator, "We've hit our RPC message limit (RPC count %u, packet size %u)", aznumeric_cast<uint32_t>(entityRpcsPacket.GetEntityRpcs().size()), pendingPacketSize);
|
||||
AZLOG(NET_Replicator, "We've hit our RPC message limit (RPC count %u, packet size %u)", aznumeric_cast<uint32_t>(entityRpcs.size()), pendingPacketSize);
|
||||
break;
|
||||
}
|
||||
entityRpcsPacket.ModifyEntityRpcs().push_back(message);
|
||||
deferredRpcs.pop_front();
|
||||
entityRpcs.push_back(message);
|
||||
rpcMessages.pop_front();
|
||||
}
|
||||
|
||||
if (reliable)
|
||||
{
|
||||
m_connection.SendReliablePacket(entityRpcsPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connection.SendUnreliablePacket(entityRpcsPacket);
|
||||
}
|
||||
m_replicationWindow->SendEntityRpcs(entityRpcs, reliable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,10 +358,11 @@ namespace Multiplayer
|
||||
entityReplicator = GetEntityReplicator(entityHandle);
|
||||
if (entityReplicator)
|
||||
{
|
||||
// Check if we changed our remote role - this can happen during server entity migration. After we migrate ownership to the new server, we hold onto our entity replicator until we are sure
|
||||
// the other side has received all the packets (and we haven't had to do resends). At this point, it is possible hear back from the remote side we migrated to on the old replicator prior to the timeout and cleanup on the old one
|
||||
// Check if we changed our remote role - this can happen during server entity migration.
|
||||
// Retain our replicator after migration until we are sure the other side has received all the packets (and we haven't had to do resends).
|
||||
// At this point, the remote host should inform us we've migrated prior to the timeout and cleanup of the old replicator
|
||||
const bool changedRemoteRole = (remoteNetworkRole != entityReplicator->GetRemoteNetworkRole());
|
||||
// check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client
|
||||
// Check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client
|
||||
bool changedLocalRole(false);
|
||||
if (AZ::Entity* localEnt = entityReplicator->GetEntityHandle().GetEntity())
|
||||
{
|
||||
@@ -391,19 +382,33 @@ namespace Multiplayer
|
||||
// Reset our replicator, we are establishing a new one
|
||||
entityReplicator->Reset(remoteNetworkRole);
|
||||
}
|
||||
// else case is when an entity had left relevancy and come back (but it was still pending a removal)
|
||||
// Else case is when an entity had left relevancy and come back (but it was still pending a removal)
|
||||
entityReplicator->Initialize(entityHandle);
|
||||
AZLOG(NET_RepDeletes, "Reinited replicator for %u from remote manager id %d role %d", entityHandle.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()), aznumeric_cast<int32_t>(remoteNetworkRole));
|
||||
AZLOG
|
||||
(
|
||||
NET_RepDeletes,
|
||||
"Reinited replicator for %u from remote host %s role %d",
|
||||
entityHandle.GetNetEntityId(),
|
||||
GetRemoteHostId().GetString().c_str(),
|
||||
aznumeric_cast<int32_t>(remoteNetworkRole)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// haven't seen him before, let's add him
|
||||
// Haven't seen him before, let's add him
|
||||
AZ_Assert(entityHandle.GetNetBindComponent(), "No NetBindComponent");
|
||||
AZStd::unique_ptr<EntityReplicator> newEntityReplicator = AZStd::make_unique<EntityReplicator>(*this, &m_connection, remoteNetworkRole, entityHandle);
|
||||
newEntityReplicator->Initialize(entityHandle);
|
||||
entityReplicator = newEntityReplicator.get();
|
||||
m_entityReplicatorMap.emplace(entityHandle.GetNetEntityId(), AZStd::move(newEntityReplicator));
|
||||
AZLOG(NET_RepDeletes, "Added replicator for %u from remote manager id %d role %d", entityHandle.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()), aznumeric_cast<int32_t>(remoteNetworkRole));
|
||||
AZLOG
|
||||
(
|
||||
NET_RepDeletes,
|
||||
"Added replicator for %u from remote host %s role %d",
|
||||
entityHandle.GetNetEntityId(),
|
||||
GetRemoteHostId().GetString().c_str(),
|
||||
aznumeric_cast<int32_t>(remoteNetworkRole)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -453,11 +458,16 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
// @nt: TODO - delete once dropped RPC problem fixed
|
||||
void EntityReplicationManager::AddAutonomousEntityReplicatorCreatedHandle(AZ::Event<NetEntityId>::Handler& handler)
|
||||
void EntityReplicationManager::AddAutonomousEntityReplicatorCreatedHandler(AZ::Event<NetEntityId>::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_autonomousEntityReplicatorCreated);
|
||||
}
|
||||
|
||||
void EntityReplicationManager::AddSendMigrateEntityEventHandler(SendMigrateEntityEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_sendMigrateEntityEvent);
|
||||
}
|
||||
|
||||
const EntityReplicator* EntityReplicationManager::GetEntityReplicator(NetEntityId netEntityId) const
|
||||
{
|
||||
auto it = m_entityReplicatorMap.find(netEntityId);
|
||||
@@ -492,18 +502,18 @@ namespace Multiplayer
|
||||
{
|
||||
if (entityReplicator->IsMarkedForRemoval())
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %u remote manager id %d", updateMessage.GetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
else if (entityReplicator->OwnsReplicatorLifetime())
|
||||
{
|
||||
// This can occur if we migrate entities quickly - if this is a replicator from C to A, A migrates to B, B then migrates to C, and A's delete replicator has not arrived at C
|
||||
AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %u remote manager id %d", updateMessage.GetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldDeleteEntity = true;
|
||||
entityReplicator->MarkForRemoval();
|
||||
AZLOG(NET_RepDeletes, "Deleting replicater for entity id %u remote manager id %d", updateMessage.GetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepDeletes, "Deleting replicater for entity id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -519,17 +529,17 @@ namespace Multiplayer
|
||||
{
|
||||
if (updateMessage.GetWasMigrated())
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "Leaving id %u using timeout remote manager id %d", entity.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepDeletes, "Leaving id %u using timeout remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "Deleting entity id %u remote manager id %d", entity.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepDeletes, "Deleting entity id %u remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
GetNetworkEntityManager()->MarkForRemoval(entity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "Trying to delete entity id %u remote manager id %d, but it has been removed", entity.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepDeletes, "Trying to delete entity id %u remote host %s, but it has been removed", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,8 +708,8 @@ namespace Multiplayer
|
||||
AZLOG_WARN
|
||||
(
|
||||
"Dropping Packet and LocalServerToRemoteClient connection, unexpected packet "
|
||||
"LocalShard=%u EntityId=%u RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<uint32_t>(GetNetworkEntityManager()->GetHostId()),
|
||||
"LocalShard=%s EntityId=%u RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s",
|
||||
GetNetworkEntityManager()->GetHostId().GetString().c_str(),
|
||||
aznumeric_cast<uint32_t>(entityReplicator->GetEntityHandle().GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(entityReplicator->GetRemoteNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityReplicator->GetBoundLocalNetworkRole()),
|
||||
@@ -750,13 +760,13 @@ namespace Multiplayer
|
||||
result = UpdateValidationResult::DropMessage;
|
||||
if (updateMessage.GetIsDelete())
|
||||
{
|
||||
AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %u, sequence %d latest sequence %d from remote manager id %d",
|
||||
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %u, sequence %d latest sequence %d from remote host %s",
|
||||
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %u, sequence %d latest sequence %d from remote manager id %d",
|
||||
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %u, sequence %d latest sequence %d from remote host %s",
|
||||
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1062,12 +1072,12 @@ namespace Multiplayer
|
||||
return false;
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SetEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain)
|
||||
void EntityReplicationManager::SetRemoteEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain)
|
||||
{
|
||||
m_remoteEntityDomain = AZStd::move(entityDomain);
|
||||
}
|
||||
|
||||
IEntityDomain* EntityReplicationManager::GetEntityDomain()
|
||||
IEntityDomain* EntityReplicationManager::GetRemoteEntityDomain()
|
||||
{
|
||||
return m_remoteEntityDomain.get();
|
||||
}
|
||||
@@ -1108,7 +1118,7 @@ namespace Multiplayer
|
||||
|
||||
bool didSucceed = true;
|
||||
EntityMigrationMessage message;
|
||||
message.m_entityId = replicator->GetEntityHandle().GetNetEntityId();
|
||||
message.m_netEntityId = replicator->GetEntityHandle().GetNetEntityId();
|
||||
message.m_prefabEntityId = netBindComponent->GetPrefabEntityId();
|
||||
|
||||
if (localEnt->GetState() == AZ::Entity::State::Active)
|
||||
@@ -1133,9 +1143,12 @@ namespace Multiplayer
|
||||
message.m_propertyUpdateData.Resize(inputSerializer.GetSize());
|
||||
}
|
||||
AZ_Assert(didSucceed, "Failed to migrate entity from server");
|
||||
// TODO: Move this to an event
|
||||
//m_connection.SendReliablePacket(message);
|
||||
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote manager id %d", netEntityId, aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
|
||||
m_sendMigrateEntityEvent.Signal(m_connection, message);
|
||||
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote host %s", netEntityId, GetRemoteHostId().GetString().c_str());
|
||||
|
||||
// Notify all other EntityReplicationManagers that this entity has migrated so they can adjust their own replicators given our new proxy status
|
||||
GetMultiplayer()->SendNotifyEntityMigrationEvent(entityHandle, GetRemoteHostId());
|
||||
|
||||
// Immediately add a new replicator so that we catch RPC invocations, the remote side will make us a new one, and then remove us if needs be
|
||||
AddEntityReplicator(entityHandle, NetEntityRole::Authority);
|
||||
@@ -1144,7 +1157,7 @@ namespace Multiplayer
|
||||
|
||||
bool EntityReplicationManager::HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message)
|
||||
{
|
||||
EntityReplicator* replicator = GetEntityReplicator(message.m_entityId);
|
||||
EntityReplicator* replicator = GetEntityReplicator(message.m_netEntityId);
|
||||
{
|
||||
if (message.m_propertyUpdateData.GetSize() > 0)
|
||||
{
|
||||
@@ -1154,7 +1167,7 @@ namespace Multiplayer
|
||||
invokingConnection,
|
||||
replicator,
|
||||
AzNetworking::InvalidPacketId,
|
||||
message.m_entityId,
|
||||
message.m_netEntityId,
|
||||
NetEntityRole::Server,
|
||||
outputSerializer,
|
||||
message.m_prefabEntityId
|
||||
@@ -1168,7 +1181,7 @@ namespace Multiplayer
|
||||
// The HandlePropertyChangeMessage will have made a replicator if we didn't have one already
|
||||
if (!replicator)
|
||||
{
|
||||
replicator = GetEntityReplicator(message.m_entityId);
|
||||
replicator = GetEntityReplicator(message.m_netEntityId);
|
||||
}
|
||||
AZ_Assert(replicator, "Do not have replicator after handling migration message");
|
||||
|
||||
@@ -1188,7 +1201,7 @@ namespace Multiplayer
|
||||
// Change the role on the replicator
|
||||
AddEntityReplicator(entityHandle, NetEntityRole::Server);
|
||||
|
||||
AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote manager id %d", entityHandle.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));
|
||||
AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote host %s", entityHandle.GetNetEntityId(), GetRemoteHostId().GetString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1200,11 +1213,11 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void EntityReplicationManager::OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, [[maybe_unused]] AzNetworking::ConnectionId connectionId)
|
||||
void EntityReplicationManager::OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
|
||||
{
|
||||
if (remoteHostId == GetRemoteHostId())
|
||||
{
|
||||
// don't handle self sent messages
|
||||
// Don't handle self sent messages
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
-215
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
* 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 <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/EntityDomains/IEntityDomain.h>
|
||||
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
|
||||
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
|
||||
#include <AzNetworking/DataStructures/TimeoutQueue.h>
|
||||
#include <AzNetworking/PacketLayer/IPacketHeader.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzCore/std/limits.h>
|
||||
#include <AzCore/EBus/Event.h>
|
||||
#include <AzCore/EBus/ScheduledEvent.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
class IConnection;
|
||||
class IConnectionListener;
|
||||
}
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class IEntityDomain;
|
||||
class EntityReplicator;
|
||||
|
||||
//! @class EntityReplicationManager
|
||||
//! @brief Handles replication of relevant entities for one connection.
|
||||
class EntityReplicationManager final
|
||||
{
|
||||
public:
|
||||
using EntityReplicatorMap = AZStd::map<NetEntityId, AZStd::unique_ptr<EntityReplicator>>;
|
||||
|
||||
enum class Mode
|
||||
{
|
||||
Invalid,
|
||||
LocalServerToRemoteClient,
|
||||
LocalServerToRemoteServer,
|
||||
LocalClientToRemoteServer,
|
||||
};
|
||||
|
||||
EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode);
|
||||
~EntityReplicationManager() = default;
|
||||
|
||||
void SetRemoteHostId(HostId hostId);
|
||||
HostId GetRemoteHostId() const;
|
||||
|
||||
void ActivatePendingEntities();
|
||||
void SendUpdates(AZ::TimeMs hostTimeMs);
|
||||
void Clear(bool forMigration);
|
||||
|
||||
bool SetEntityRebasing(NetworkEntityHandle& entityHandle);
|
||||
|
||||
void MigrateAllEntities();
|
||||
void MigrateEntity(NetEntityId netEntityId);
|
||||
bool CanMigrateEntity(const ConstNetworkEntityHandle& entityHandle) const;
|
||||
|
||||
bool HasRemoteAuthority(const ConstNetworkEntityHandle& entityHandle) const;
|
||||
|
||||
void SetEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain);
|
||||
IEntityDomain* GetEntityDomain();
|
||||
void SetReplicationWindow(AZStd::unique_ptr<IReplicationWindow> replicationWindow);
|
||||
IReplicationWindow* GetReplicationWindow();
|
||||
|
||||
void GetEntityReplicatorIdList(AZStd::list<NetEntityId>& outList);
|
||||
uint32_t GetEntityReplicatorCount(NetEntityRole localNetworkRole);
|
||||
|
||||
void AddDeferredRpcMessage(NetworkEntityRpcMessage& rpcMessage);
|
||||
|
||||
void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event<NetEntityId>::Handler& handler);
|
||||
|
||||
bool HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message);
|
||||
bool HandleEntityDeleteMessage(EntityReplicator* entityReplicator, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
|
||||
bool HandleEntityUpdateMessage(AzNetworking::IConnection* invokingConnection, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
|
||||
bool HandleEntityRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& message);
|
||||
|
||||
AZ::TimeMs GetResendTimeoutTimeMs() const;
|
||||
|
||||
void SetMaxRemoteEntitiesPendingCreationCount(uint32_t maxPendingEntities);
|
||||
void SetEntityActivationTimeSliceMs(AZ::TimeMs timeSliceMs);
|
||||
void SetEntityPendingRemovalMs(AZ::TimeMs entityPendingRemovalMs);
|
||||
|
||||
AzNetworking::IConnection& GetConnection();
|
||||
AZ::TimeMs GetFrameTimeMs();
|
||||
|
||||
void AddReplicatorToPendingSend(const EntityReplicator& entityReplicator);
|
||||
|
||||
bool IsUpdateModeToServerClient();
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(EntityReplicationManager);
|
||||
|
||||
enum class UpdateValidationResult
|
||||
{
|
||||
HandleMessage, // Handle an entity update message
|
||||
DropMessage, // Do not handle an entity update message, but don't disconnect (could be out of order/date and isn't relevant)
|
||||
DropMessageAndDisconnect, // Do not handle the message, it is malformed and we should disconnect the connection
|
||||
};
|
||||
|
||||
UpdateValidationResult ValidateUpdate(const NetworkEntityUpdateMessage& updateMessage, AzNetworking::PacketId packetId, EntityReplicator* entityReplicator);
|
||||
|
||||
using RpcMessages = AZStd::list<NetworkEntityRpcMessage>;
|
||||
bool DispatchOrphanedRpc(NetworkEntityRpcMessage& message, EntityReplicator* entityReplicator);
|
||||
|
||||
using EntityReplicatorList = AZStd::deque<EntityReplicator*>;
|
||||
EntityReplicatorList GenerateEntityUpdateList();
|
||||
|
||||
void SendEntityUpdatesPacketHelper(AZ::TimeMs hostTimeMs, EntityReplicatorList& toSendList, uint32_t maxPayloadSize, AzNetworking::IConnection& connection);
|
||||
|
||||
void SendEntityUpdates(AZ::TimeMs hostTimeMs);
|
||||
void SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable);
|
||||
|
||||
void MigrateEntityInternal(NetEntityId entityId);
|
||||
void OnEntityExitDomain(const ConstNetworkEntityHandle& entityHandle);
|
||||
void OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, AzNetworking::ConnectionId connectionId);
|
||||
|
||||
EntityReplicator* AddEntityReplicator(const ConstNetworkEntityHandle& entityHandle, NetEntityRole netEntityRole);
|
||||
|
||||
const EntityReplicator* GetEntityReplicator(NetEntityId entityId) const;
|
||||
EntityReplicator* GetEntityReplicator(NetEntityId entityId);
|
||||
EntityReplicator* GetEntityReplicator(const ConstNetworkEntityHandle& entityHandle);
|
||||
|
||||
void UpdateWindow();
|
||||
|
||||
bool HandlePropertyChangeMessage
|
||||
(
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
EntityReplicator* entityReplicator,
|
||||
AzNetworking::PacketId packetId,
|
||||
NetEntityId netEntityId,
|
||||
NetEntityRole netEntityRole,
|
||||
AzNetworking::ISerializer& serializer,
|
||||
const PrefabEntityId& prefabEntityId
|
||||
);
|
||||
|
||||
void AddReplicatorToPendingRemoval(const EntityReplicator& replicator);
|
||||
void ClearRemovedReplicators();
|
||||
|
||||
class OrphanedEntityRpcs
|
||||
: public AzNetworking::ITimeoutHandler
|
||||
{
|
||||
public:
|
||||
OrphanedEntityRpcs(EntityReplicationManager& replicationManager);
|
||||
virtual ~OrphanedEntityRpcs() = default;
|
||||
void Update();
|
||||
bool DispatchOrphanedRpcs(EntityReplicator& entityReplicator);
|
||||
void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityRpcMessage);
|
||||
AZStd::size_t Size() const { return m_entityRpcMap.size(); }
|
||||
private:
|
||||
AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override;
|
||||
struct OrphanedRpcs
|
||||
{
|
||||
OrphanedRpcs() = default;
|
||||
OrphanedRpcs(OrphanedRpcs&& rhs)
|
||||
{
|
||||
m_rpcMessages.swap(rhs.m_rpcMessages);
|
||||
m_timeoutId = rhs.m_timeoutId;
|
||||
rhs.m_timeoutId = AzNetworking::TimeoutId{ 0 };
|
||||
}
|
||||
RpcMessages m_rpcMessages;
|
||||
AzNetworking::TimeoutId m_timeoutId = AzNetworking::TimeoutId{ 0 };
|
||||
};
|
||||
typedef AZStd::unordered_map<NetEntityId, OrphanedRpcs> EntityRpcMap;
|
||||
EntityRpcMap m_entityRpcMap;
|
||||
AzNetworking::TimeoutQueue m_timeoutQueue;
|
||||
EntityReplicationManager& m_replicationManager;
|
||||
};
|
||||
OrphanedEntityRpcs m_orphanedEntityRpcs;
|
||||
EntityReplicatorMap m_entityReplicatorMap;
|
||||
|
||||
//! The set of entities that we have sent creation messages for, but have not received confirmation back that the create has occurred
|
||||
AZStd::unordered_set<NetEntityId> m_remoteEntitiesPendingCreation;
|
||||
AZStd::deque<NetEntityId> m_entitiesPendingActivation;
|
||||
AZStd::set<NetEntityId> m_replicatorsPendingRemoval;
|
||||
AZStd::unordered_set<NetEntityId> m_replicatorsPendingSend;
|
||||
|
||||
// Deferred RPC Sends
|
||||
RpcMessages m_deferredRpcMessagesReliable;
|
||||
RpcMessages m_deferredRpcMessagesUnreliable;
|
||||
|
||||
AZ::Event<NetEntityId> m_autonomousEntityReplicatorCreated;
|
||||
EntityExitDomainEvent::Handler m_entityExitDomainEventHandler;
|
||||
|
||||
AZ::ScheduledEvent m_clearRemovedReplicators;
|
||||
AZ::ScheduledEvent m_updateWindow;
|
||||
|
||||
AzNetworking::IConnectionListener& m_connectionListener;
|
||||
AzNetworking::IConnection& m_connection;
|
||||
AZStd::unique_ptr<IReplicationWindow> m_replicationWindow;
|
||||
AZStd::unique_ptr<IEntityDomain> m_remoteEntityDomain;
|
||||
|
||||
AZ::TimeMs m_entityActivationTimeSliceMs = AZ::TimeMs{ 0 };
|
||||
AZ::TimeMs m_entityPendingRemovalMs = AZ::TimeMs{ 0 };
|
||||
AZ::TimeMs m_frameTimeMs = AZ::TimeMs{ 0 };
|
||||
HostId m_remoteHostId = InvalidHostId;
|
||||
uint32_t m_maxRemoteEntitiesPendingCreationCount = AZStd::numeric_limits<uint32_t>::max();
|
||||
uint32_t m_maxPayloadSize = 0;
|
||||
Mode m_updateMode = Mode::Invalid;
|
||||
|
||||
friend class EntityReplicator;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,11 +12,10 @@
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <Multiplayer/Components/NetworkTransformComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
|
||||
|
||||
@@ -55,7 +54,7 @@ namespace Multiplayer
|
||||
{
|
||||
if (auto localEnt = m_entityHandle.GetEntity())
|
||||
{
|
||||
m_netBindComponent = localEnt->FindComponent<NetBindComponent>();
|
||||
m_netBindComponent = m_entityHandle.GetNetBindComponent();
|
||||
m_boundLocalNetworkRole = m_netBindComponent->GetNetEntityRole();
|
||||
}
|
||||
}
|
||||
@@ -95,7 +94,7 @@ namespace Multiplayer
|
||||
m_entityHandle = entityHandle;
|
||||
if (auto localEntity = m_entityHandle.GetEntity())
|
||||
{
|
||||
m_netBindComponent = localEntity->FindComponent<NetBindComponent>();
|
||||
m_netBindComponent = m_entityHandle.GetNetBindComponent();
|
||||
AZ_Assert(m_netBindComponent, "No Multiplayer::NetBindComponent");
|
||||
m_boundLocalNetworkRole = m_netBindComponent->GetNetEntityRole();
|
||||
SetPrefabEntityId(m_netBindComponent->GetPrefabEntityId());
|
||||
@@ -126,7 +125,8 @@ namespace Multiplayer
|
||||
!RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False,
|
||||
m_netBindComponent,
|
||||
*m_connection
|
||||
);
|
||||
);
|
||||
m_onEntityDirtiedHandler.Disconnect();
|
||||
m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler);
|
||||
}
|
||||
else
|
||||
@@ -147,8 +147,9 @@ namespace Multiplayer
|
||||
// Prepare event handlers
|
||||
if (auto localEntity = m_entityHandle.GetEntity())
|
||||
{
|
||||
NetBindComponent* netBindComponent = localEntity->FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = m_entityHandle.GetNetBindComponent();
|
||||
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
|
||||
m_onEntityStopHandler.Disconnect();
|
||||
netBindComponent->AddEntityStopEventHandler(m_onEntityStopHandler);
|
||||
AttachRPCHandlers();
|
||||
}
|
||||
@@ -169,7 +170,7 @@ namespace Multiplayer
|
||||
|
||||
if (auto localEntity = m_entityHandle.GetEntity())
|
||||
{
|
||||
NetBindComponent* netBindComponent = localEntity->FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = m_entityHandle.GetNetBindComponent();
|
||||
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
|
||||
|
||||
switch (GetBoundLocalNetworkRole())
|
||||
@@ -471,19 +472,19 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_RepDeletes,
|
||||
"Sending delete replicator id %u migrated %d to remote manager id %d",
|
||||
"Sending delete replicator id %u migrated %d to remote host %s",
|
||||
aznumeric_cast<uint32_t>(GetEntityHandle().GetNetEntityId()),
|
||||
WasMigrated() ? 1 : 0,
|
||||
aznumeric_cast<int32_t>(m_replicationManager.GetRemoteHostId())
|
||||
m_replicationManager.GetRemoteHostId().GetString().c_str()
|
||||
);
|
||||
return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated(), m_propertyPublisher->IsRemoteReplicatorEstablished());
|
||||
}
|
||||
|
||||
NetBindComponent* netBindComponent = GetNetBindComponent();
|
||||
const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished();
|
||||
//const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished();
|
||||
|
||||
NetworkEntityUpdateMessage updateMessage(GetRemoteNetworkRole(), GetEntityHandle().GetNetEntityId());
|
||||
if (sendSliceName)
|
||||
//if (sendSliceName)
|
||||
{
|
||||
updateMessage.SetPrefabEntityId(netBindComponent->GetPrefabEntityId());
|
||||
}
|
||||
@@ -495,6 +496,11 @@ namespace Multiplayer
|
||||
return updateMessage;
|
||||
}
|
||||
|
||||
void EntityReplicator::FinalizeSerialization(AzNetworking::PacketId sentId)
|
||||
{
|
||||
m_propertyPublisher->FinalizeSerialization(sentId);
|
||||
}
|
||||
|
||||
void EntityReplicator::DeferRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
|
||||
{
|
||||
// Received rpc metrics, log rpc sent, number of bytes, and the componentId/rpcId for bandwidth metrics
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* 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 <AzCore/EBus/Event.h>
|
||||
#include <AzCore/EBus/ScheduledEvent.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/EntityBus.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/containers/ring_buffer.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class EntityReplicationManager;
|
||||
class NetworkEntityRpcMessage;
|
||||
class NetBindComponent;
|
||||
|
||||
class PropertyPublisher;
|
||||
class PropertySubscriber;
|
||||
|
||||
class EntityReplicator final
|
||||
: public AZ::EntityBus::Handler
|
||||
{
|
||||
public:
|
||||
EntityReplicator(EntityReplicationManager& replicationManager, AzNetworking::IConnection* connection, NetEntityRole remoteNetworkRole, const ConstNetworkEntityHandle& entityHandle);
|
||||
~EntityReplicator() override;
|
||||
|
||||
NetEntityRole GetBoundLocalNetworkRole() const;
|
||||
NetEntityRole GetRemoteNetworkRole() const;
|
||||
ConstNetworkEntityHandle GetEntityHandle() const;
|
||||
NetBindComponent* GetNetBindComponent();
|
||||
|
||||
void ActivateNetworkEntity();
|
||||
|
||||
const PrefabEntityId& GetPrefabEntityId() const;
|
||||
bool IsPrefabEntityIdSet() const;
|
||||
|
||||
bool OwnsReplicatorLifetime() const;
|
||||
bool RemoteManagerOwnsEntityLifetime() const;
|
||||
|
||||
// Interface for ReplicationManager to modify state of replication
|
||||
void Initialize(const ConstNetworkEntityHandle& entityHandle);
|
||||
void Reset(NetEntityRole remoteNetworkRole);
|
||||
void MarkForRemoval();
|
||||
bool IsMarkedForRemoval() const;
|
||||
void SetPendingRemoval(AZ::TimeMs pendingRemovalTimeMs);
|
||||
bool IsPendingRemoval() const;
|
||||
void ClearPendingRemoval();
|
||||
bool IsDeletionAcknowledged() const;
|
||||
bool WasMigrated() const;
|
||||
void SetWasMigrated(bool wasMigrated);
|
||||
// If an entity is part of a network hierarchy then it is only ready to activate when its direct parent entity is active.
|
||||
bool IsReadyToActivate() const;
|
||||
|
||||
NetworkEntityUpdateMessage GenerateUpdatePacket();
|
||||
|
||||
AZ::TimeMs GetResendTimeoutTimeMs() const;
|
||||
|
||||
PropertyPublisher* GetPropertyPublisher();
|
||||
const PropertyPublisher* GetPropertyPublisher() const;
|
||||
PropertySubscriber* GetPropertySubscriber();
|
||||
|
||||
// Handlers for Rpc messages
|
||||
bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& entityRpcMessage);
|
||||
|
||||
//! AZ::EntityBus overrides
|
||||
//! @{
|
||||
void OnEntityActivated(const AZ::EntityId&) override;
|
||||
void OnEntityDestroyed(const AZ::EntityId&) override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
enum class RpcValidationResult
|
||||
{
|
||||
HandleRpc, // Handle Rpc message
|
||||
DropRpc, // Do not handle Rpc
|
||||
DropRpcAndDisconnect, // Do not handle the Rpc, it is disallowed from this endpoint we should disconnect the connection
|
||||
ForwardToClient, // Forward this message to the Client
|
||||
ForwardToAutonomous, // Forward this message to the Autonomous
|
||||
ForwardToAuthority, // Forward this message to the Authority
|
||||
};
|
||||
RpcValidationResult ValidateRpcMessage(const NetworkEntityRpcMessage& entityRpcMessage) const;
|
||||
|
||||
// Internal state tracking
|
||||
bool CanSendUpdates();
|
||||
|
||||
void SetPrefabEntityId(const PrefabEntityId& prefabEntityId); // cache assetId so authority doesn't need to keep sending it
|
||||
|
||||
// Event processing
|
||||
void OnSendRpcEvent(NetworkEntityRpcMessage& message);
|
||||
void OnForwardRpcEvent(NetworkEntityRpcMessage& message);
|
||||
void OnEntityDirtiedEvent();
|
||||
void OnEntityRemovedEvent();
|
||||
void OnProxyRemovalTimedEvent();
|
||||
|
||||
void ActivateNetworkEntityInternal();
|
||||
|
||||
void AttachRPCHandlers();
|
||||
|
||||
void DeferRpcMessage(NetworkEntityRpcMessage& message);
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(EntityReplicator);
|
||||
|
||||
// Events
|
||||
RpcSendEvent::Handler m_onSendRpcHandler;
|
||||
RpcSendEvent::Handler m_onForwardRpcHandler;
|
||||
RpcSendEvent::Handler m_onSendAutonomousRpcHandler;
|
||||
RpcSendEvent::Handler m_onForwardAutonomousRpcHandler;
|
||||
EntityDirtiedEvent::Handler m_onEntityDirtiedHandler;
|
||||
EntityStopEvent::Handler m_onEntityStopHandler;
|
||||
AZ::ScheduledEvent m_proxyRemovalEvent;
|
||||
|
||||
ConstNetworkEntityHandle m_entityHandle;
|
||||
PrefabEntityId m_prefabEntityId;
|
||||
|
||||
AZStd::unique_ptr<PropertyPublisher> m_propertyPublisher;
|
||||
AZStd::unique_ptr<PropertySubscriber> m_propertySubscriber;
|
||||
|
||||
NetBindComponent* m_netBindComponent = nullptr;
|
||||
EntityReplicationManager& m_replicationManager;
|
||||
AzNetworking::IConnection* m_connection;
|
||||
NetEntityRole m_boundLocalNetworkRole;
|
||||
NetEntityRole m_remoteNetworkRole;
|
||||
|
||||
bool m_wasMigrated = false;
|
||||
bool m_isForwardingRpc = false;
|
||||
bool m_prefabEntityIdSet = false;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.inl>
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
inline NetEntityRole EntityReplicator::GetBoundLocalNetworkRole() const
|
||||
{
|
||||
return m_boundLocalNetworkRole;
|
||||
}
|
||||
|
||||
inline NetEntityRole EntityReplicator::GetRemoteNetworkRole() const
|
||||
{
|
||||
return m_remoteNetworkRole;
|
||||
}
|
||||
|
||||
inline ConstNetworkEntityHandle EntityReplicator::GetEntityHandle() const
|
||||
{
|
||||
return m_entityHandle;
|
||||
}
|
||||
|
||||
inline NetBindComponent* EntityReplicator::GetNetBindComponent()
|
||||
{
|
||||
return m_netBindComponent;
|
||||
}
|
||||
|
||||
inline const PrefabEntityId& EntityReplicator::GetPrefabEntityId() const
|
||||
{
|
||||
AZ_Assert(IsPrefabEntityIdSet(), "PrefabEntityId not set for Entity");
|
||||
return m_prefabEntityId;
|
||||
}
|
||||
|
||||
inline bool EntityReplicator::IsPrefabEntityIdSet() const
|
||||
{
|
||||
return m_prefabEntityIdSet;
|
||||
}
|
||||
|
||||
inline bool EntityReplicator::WasMigrated() const
|
||||
{
|
||||
return m_wasMigrated;
|
||||
}
|
||||
|
||||
inline void EntityReplicator::SetWasMigrated(bool wasMigrated)
|
||||
{
|
||||
m_wasMigrated = wasMigrated;
|
||||
}
|
||||
|
||||
inline PropertyPublisher* EntityReplicator::GetPropertyPublisher()
|
||||
{
|
||||
return m_propertyPublisher.get();
|
||||
}
|
||||
|
||||
inline const PropertyPublisher* EntityReplicator::GetPropertyPublisher() const
|
||||
{
|
||||
return m_propertyPublisher.get();
|
||||
}
|
||||
|
||||
inline PropertySubscriber* EntityReplicator::GetPropertySubscriber()
|
||||
{
|
||||
return m_propertySubscriber.get();
|
||||
}
|
||||
}
|
||||
@@ -336,7 +336,6 @@ namespace Multiplayer
|
||||
case PropertyPublisher::EntityReplicatorState::Deleting:
|
||||
{
|
||||
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Prepared, "Unexpected serialization phase");
|
||||
|
||||
FinalizeDeleteEntityRecord(sentId);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Multiplayer
|
||||
;
|
||||
}
|
||||
|
||||
bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId newOwner)
|
||||
bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner)
|
||||
{
|
||||
bool ret = false;
|
||||
auto timeoutData = m_timeoutDataMap.find(entityHandle.GetNetEntityId());
|
||||
@@ -33,10 +33,10 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_AuthTracker,
|
||||
"AuthTracker: Removing timeout for networkEntityId %u from %u, new owner is %u",
|
||||
"AuthTracker: Removing timeout for networkEntityId %u from %s, new owner is %s",
|
||||
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(timeoutData->second.m_previousOwner),
|
||||
aznumeric_cast<uint32_t>(newOwner)
|
||||
timeoutData->second.m_previousOwner.GetString().c_str(),
|
||||
newOwner.GetString().c_str()
|
||||
);
|
||||
m_timeoutDataMap.erase(timeoutData);
|
||||
ret = true;
|
||||
@@ -48,10 +48,10 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_AuthTracker,
|
||||
"AuthTracker: Assigning networkEntityId %u from %u to %u",
|
||||
"AuthTracker: Assigning networkEntityId %u from %s to %s",
|
||||
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(iter->second.back()),
|
||||
aznumeric_cast<uint32_t>(newOwner)
|
||||
iter->second.back().GetString().c_str(),
|
||||
newOwner.GetString().c_str()
|
||||
);
|
||||
}
|
||||
else
|
||||
@@ -59,9 +59,9 @@ namespace Multiplayer
|
||||
AZLOG
|
||||
(
|
||||
NET_AuthTracker,
|
||||
"AuthTracker: Assigning networkEntityId %u to %u",
|
||||
"AuthTracker: Assigning networkEntityId %u to %s",
|
||||
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(newOwner)
|
||||
newOwner.GetString().c_str()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Multiplayer
|
||||
return ret;
|
||||
}
|
||||
|
||||
void NetworkEntityAuthorityTracker::RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId previousOwner)
|
||||
void NetworkEntityAuthorityTracker::RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner)
|
||||
{
|
||||
auto mapIter = m_entityAuthorityMap.find(entityHandle.GetNetEntityId());
|
||||
if (mapIter != m_entityAuthorityMap.end())
|
||||
@@ -87,7 +87,7 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %u from %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), aznumeric_cast<uint32_t>(previousOwner));
|
||||
AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %u from %s", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), previousOwner.GetString().c_str());
|
||||
if (auto localEnt = entityHandle.GetEntity())
|
||||
{
|
||||
if (authorityStack.empty())
|
||||
@@ -167,7 +167,7 @@ namespace Multiplayer
|
||||
return InvalidHostId;
|
||||
}
|
||||
|
||||
NetworkEntityAuthorityTracker::TimeoutData::TimeoutData(ConstNetworkEntityHandle entityHandle, HostId previousOwner)
|
||||
NetworkEntityAuthorityTracker::TimeoutData::TimeoutData(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner)
|
||||
: m_entityHandle(entityHandle)
|
||||
, m_previousOwner(previousOwner)
|
||||
{
|
||||
@@ -205,9 +205,9 @@ namespace Multiplayer
|
||||
{
|
||||
AZLOG_ERROR
|
||||
(
|
||||
"Timed out entity id %u during migration previous owner %u, removing it",
|
||||
"Timed out entity id %u during migration previous owner %s, removing it",
|
||||
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(timeoutData->second.m_previousOwner)
|
||||
timeoutData->second.m_previousOwner.GetString().c_str()
|
||||
);
|
||||
m_networkEntityManager.MarkForRemoval(entityHandle);
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace Multiplayer
|
||||
NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager);
|
||||
|
||||
bool DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const;
|
||||
bool AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId newOwner);
|
||||
void RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId previousOwner);
|
||||
bool AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner);
|
||||
void RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner);
|
||||
HostId GetEntityAuthorityManager(ConstNetworkEntityHandle entityHandle) const;
|
||||
|
||||
private:
|
||||
@@ -37,7 +37,7 @@ namespace Multiplayer
|
||||
struct TimeoutData final
|
||||
{
|
||||
TimeoutData() = default;
|
||||
TimeoutData(ConstNetworkEntityHandle entityHandle, HostId previousOwner);
|
||||
TimeoutData(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner);
|
||||
ConstNetworkEntityHandle m_entityHandle;
|
||||
HostId m_previousOwner = InvalidHostId;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,11 @@ namespace Multiplayer
|
||||
: m_entity(entity)
|
||||
, m_networkEntityTracker(networkEntityTracker)
|
||||
{
|
||||
if (m_networkEntityTracker == nullptr)
|
||||
{
|
||||
m_networkEntityTracker = GetNetworkEntityTracker();
|
||||
}
|
||||
|
||||
if (m_networkEntityTracker)
|
||||
{
|
||||
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
|
||||
@@ -28,37 +33,18 @@ namespace Multiplayer
|
||||
if (entity)
|
||||
{
|
||||
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
|
||||
NetBindComponent* netBindComponent = m_entity->template FindComponent<NetBindComponent>();
|
||||
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
|
||||
m_netBindComponent = netBindComponent;
|
||||
m_netEntityId = netBindComponent->GetNetEntityId();
|
||||
m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity);
|
||||
if (m_netBindComponent != nullptr)
|
||||
{
|
||||
m_netEntityId = m_netBindComponent->GetNetEntityId();
|
||||
}
|
||||
else
|
||||
{
|
||||
*this = ConstNetworkEntityHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, NetEntityId netEntityId, const NetworkEntityTracker* networkEntityTracker)
|
||||
: m_entity(entity)
|
||||
, m_netEntityId(netEntityId)
|
||||
, m_networkEntityTracker(networkEntityTracker)
|
||||
{
|
||||
if (m_networkEntityTracker)
|
||||
{
|
||||
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
|
||||
}
|
||||
}
|
||||
|
||||
ConstNetworkEntityHandle::ConstNetworkEntityHandle(NetBindComponent* netBindComponent, const NetworkEntityTracker* networkEntityTracker)
|
||||
: m_entity(netBindComponent->GetEntity())
|
||||
, m_netBindComponent(netBindComponent)
|
||||
, m_networkEntityTracker(networkEntityTracker)
|
||||
, m_netEntityId(netBindComponent->GetNetEntityId())
|
||||
{
|
||||
if (m_networkEntityTracker)
|
||||
{
|
||||
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
|
||||
}
|
||||
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
|
||||
}
|
||||
|
||||
bool ConstNetworkEntityHandle::Exists() const
|
||||
{
|
||||
if (!m_networkEntityTracker)
|
||||
@@ -144,7 +130,7 @@ namespace Multiplayer
|
||||
}
|
||||
if (m_netBindComponent == nullptr)
|
||||
{
|
||||
m_netBindComponent = m_entity->template FindComponent<NetBindComponent>();
|
||||
m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(m_entity);
|
||||
}
|
||||
return m_netBindComponent;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
#include <AzFramework/Entity/GameEntityContextBus.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
@@ -41,11 +43,22 @@ namespace Multiplayer
|
||||
AZ::Interface<INetworkEntityManager>::Unregister(this);
|
||||
}
|
||||
|
||||
void NetworkEntityManager::Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
|
||||
void NetworkEntityManager::Initialize(const HostId& hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
|
||||
{
|
||||
m_hostId = hostId;
|
||||
m_entityDomain = AZStd::move(entityDomain);
|
||||
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
|
||||
m_entityDomain->ActivateTracking(m_ownedEntities);
|
||||
}
|
||||
|
||||
bool NetworkEntityManager::IsInitialized() const
|
||||
{
|
||||
return m_entityDomain != nullptr;
|
||||
}
|
||||
|
||||
IEntityDomain* NetworkEntityManager::GetEntityDomain() const
|
||||
{
|
||||
return m_entityDomain.get();
|
||||
}
|
||||
|
||||
NetworkEntityTracker* NetworkEntityManager::GetNetworkEntityTracker()
|
||||
@@ -63,7 +76,7 @@ namespace Multiplayer
|
||||
return &m_multiplayerComponentRegistry;
|
||||
}
|
||||
|
||||
HostId NetworkEntityManager::GetHostId() const
|
||||
const HostId& NetworkEntityManager::GetHostId() const
|
||||
{
|
||||
return m_hostId;
|
||||
}
|
||||
@@ -86,7 +99,7 @@ namespace Multiplayer
|
||||
NetworkEntityHandle NetworkEntityManager::AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity)
|
||||
{
|
||||
m_networkEntityTracker.Add(netEntityId, entity);
|
||||
return NetworkEntityHandle(entity, netEntityId, &m_networkEntityTracker);
|
||||
return NetworkEntityHandle(entity, &m_networkEntityTracker);
|
||||
}
|
||||
|
||||
void NetworkEntityManager::MarkForRemoval(const ConstNetworkEntityHandle& entityHandle)
|
||||
@@ -96,8 +109,6 @@ namespace Multiplayer
|
||||
if (net_DebugCheckNetworkEntityManager)
|
||||
{
|
||||
AZ_Assert(entityHandle.GetNetBindComponent(), "No NetBindComponent found on networked entity");
|
||||
[[maybe_unused]] const bool isClientOnlyEntity = false;// (ServerIdFromEntityId(it->first) == InvalidHostId);
|
||||
AZ_Assert(entityHandle.GetNetBindComponent()->IsNetEntityRoleAuthority() || isClientOnlyEntity, "Trying to delete a proxy entity, this will lead to issues deserializing entity updates");
|
||||
}
|
||||
m_removeList.push_back(entityHandle.GetNetEntityId());
|
||||
m_removeEntitiesEvent.Enqueue(AZ::TimeMs{ 0 });
|
||||
@@ -204,6 +215,29 @@ namespace Multiplayer
|
||||
m_localDeferredRpcMessages.emplace_back(AZStd::move(message));
|
||||
}
|
||||
|
||||
void NetworkEntityManager::DebugDraw() const
|
||||
{
|
||||
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
|
||||
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
|
||||
AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
|
||||
|
||||
for (NetworkEntityTracker::const_iterator it = m_networkEntityTracker.begin(); it != m_networkEntityTracker.end(); ++it)
|
||||
{
|
||||
AZ::Entity* entity = it->second;
|
||||
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
|
||||
if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
const AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId());
|
||||
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_entityDomain != nullptr)
|
||||
{
|
||||
m_entityDomain->DebugDraw();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEntityManager::DispatchLocalDeferredRpcMessages()
|
||||
{
|
||||
for (NetworkEntityRpcMessage& rpcMessage : m_localDeferredRpcMessages)
|
||||
@@ -211,7 +245,7 @@ namespace Multiplayer
|
||||
AZ::Entity* entity = m_networkEntityTracker.GetRaw(rpcMessage.GetEntityId());
|
||||
if (entity != nullptr)
|
||||
{
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
|
||||
AZ_Assert(netBindComponent != nullptr, "Attempting to send an RPC to an entity with no NetBindComponent");
|
||||
netBindComponent->HandleRpcMessage(nullptr, NetEntityRole::Server, rpcMessage);
|
||||
}
|
||||
@@ -226,9 +260,8 @@ namespace Multiplayer
|
||||
return;
|
||||
}
|
||||
|
||||
m_entitiesNotInDomain.clear();
|
||||
m_entityDomain->RetrieveEntitiesNotInDomain(m_entitiesNotInDomain);
|
||||
for (NetEntityId exitingId : m_entitiesNotInDomain)
|
||||
const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain = m_entityDomain->RetrieveEntitiesNotInDomain();
|
||||
for (NetEntityId exitingId : entitiesNotInDomain)
|
||||
{
|
||||
OnEntityExitDomain(exitingId);
|
||||
}
|
||||
@@ -239,18 +272,6 @@ namespace Multiplayer
|
||||
bool safeToExit = true;
|
||||
NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId);
|
||||
|
||||
// ClientAutonomous entities need special handling here. When we migrate a player's entity the player's client must tell the new server which
|
||||
// entity they were controlling. If we tell them to migrate before they know which entity they control it results in them requesting a new entity
|
||||
// from the new server, resulting in an orphaned PlayerChar. PlayerControllerComponentServerAuthority::PlayerClientHasControlledEntity()
|
||||
// will tell us whether the client sent an RPC acknowledging that they now know which entity is theirs.
|
||||
if (AZ::Entity* entity = entityHandle.GetEntity())
|
||||
{
|
||||
//if (PlayerComponent::Authority* playerController = FindController<PlayerComponent::Authority>(nonConstExitingEntityPtr))
|
||||
//{
|
||||
// safeToExit = playerController->PlayerClientHasControlledEntity();
|
||||
//}
|
||||
}
|
||||
|
||||
// We also need special handling for the EntityHierarchyComponent as well, since related entities need to be migrated together
|
||||
//auto* hierarchyController = FindController<EntityHierarchyComponent::Authority>(nonConstExitingEntityPtr);
|
||||
//if (hierarchyController)
|
||||
@@ -279,6 +300,21 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEntityManager::Reset()
|
||||
{
|
||||
m_multiplayerComponentRegistry.Reset();
|
||||
m_removeList.clear();
|
||||
m_entityDomain = nullptr;
|
||||
m_updateEntityDomainEvent.RemoveFromQueue();
|
||||
m_ownedEntities.clear();
|
||||
m_entityExitDomainEvent.DisconnectAllHandlers();
|
||||
m_onEntityMarkedDirty.DisconnectAllHandlers();
|
||||
m_onEntityNotifyChanges.DisconnectAllHandlers();
|
||||
m_controllersActivatedEvent.DisconnectAllHandlers();
|
||||
m_controllersDeactivatedEvent.DisconnectAllHandlers();
|
||||
m_localDeferredRpcMessages.clear();
|
||||
}
|
||||
|
||||
void NetworkEntityManager::RemoveEntities()
|
||||
{
|
||||
AZStd::vector<NetEntityId> removeList;
|
||||
@@ -330,6 +366,7 @@ namespace Multiplayer
|
||||
|
||||
originalToCloneIdMap[originalEntity->GetId()] = clone->GetId();
|
||||
|
||||
// Can't use NetworkEntityTracker to do the lookup since the entity has not activated yet
|
||||
NetBindComponent* netBindComponent = clone->FindComponent<NetBindComponent>();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
@@ -366,7 +403,6 @@ namespace Multiplayer
|
||||
&AzFramework::GameEntityContextRequestBus::Events::AddGameEntity, clone);
|
||||
|
||||
returnList.push_back(netBindComponent->GetEntityHandle());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -31,15 +31,15 @@ namespace Multiplayer
|
||||
NetworkEntityManager();
|
||||
~NetworkEntityManager();
|
||||
|
||||
//! Only invoked for authoritative hosts
|
||||
void Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain);
|
||||
|
||||
//! INetworkEntityManager overrides.
|
||||
//! @{
|
||||
void Initialize(const HostId& hostId, AZStd::unique_ptr<IEntityDomain> entityDomain) override;
|
||||
bool IsInitialized() const override;
|
||||
IEntityDomain* GetEntityDomain() const override;
|
||||
NetworkEntityTracker* GetNetworkEntityTracker() override;
|
||||
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override;
|
||||
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override;
|
||||
HostId GetHostId() const override;
|
||||
const HostId& GetHostId() const override;
|
||||
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override;
|
||||
NetEntityId GetNetEntityIdById(const AZ::EntityId& entityId) const override;
|
||||
|
||||
@@ -62,9 +62,7 @@ namespace Multiplayer
|
||||
|
||||
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
|
||||
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform) override;
|
||||
|
||||
void SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) override;
|
||||
|
||||
uint32_t GetEntityCount() const override;
|
||||
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override;
|
||||
void MarkForRemoval(const ConstNetworkEntityHandle& entityHandle) override;
|
||||
@@ -81,17 +79,22 @@ namespace Multiplayer
|
||||
void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
|
||||
void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
|
||||
void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) override;
|
||||
void DebugDraw() const override;
|
||||
//! @}
|
||||
|
||||
void DispatchLocalDeferredRpcMessages();
|
||||
void UpdateEntityDomain();
|
||||
void OnEntityExitDomain(NetEntityId entityId);
|
||||
|
||||
//! RootSpawnableNotificationBus
|
||||
//! @{
|
||||
void OnRootSpawnableAssigned(AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, uint32_t generation) override;
|
||||
void OnRootSpawnableReleased(uint32_t generation) override;
|
||||
//! @}
|
||||
|
||||
//! Used to release all memory prior to shutdown.
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
void RemoveEntities();
|
||||
NetEntityId NextId();
|
||||
@@ -105,7 +108,6 @@ namespace Multiplayer
|
||||
AZStd::unique_ptr<IEntityDomain> m_entityDomain;
|
||||
AZ::ScheduledEvent m_updateEntityDomainEvent;
|
||||
|
||||
IEntityDomain::EntitiesNotInDomain m_entitiesNotInDomain;
|
||||
OwnedEntitySet m_ownedEntities;
|
||||
|
||||
EntityExitDomainEvent m_entityExitDomainEvent;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Source/NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
|
||||
@@ -21,16 +22,26 @@ namespace Multiplayer
|
||||
m_netEntityIdMap[entity->GetId()] = netEntityId;
|
||||
}
|
||||
|
||||
void NetworkEntityTracker::RegisterNetBindComponent(AZ::Entity* entity, NetBindComponent* component)
|
||||
{
|
||||
m_netBindingMap[entity] = component;
|
||||
}
|
||||
|
||||
void NetworkEntityTracker::UnregisterNetBindComponent(NetBindComponent* component)
|
||||
{
|
||||
m_netBindingMap.erase(component->GetEntity());
|
||||
}
|
||||
|
||||
NetworkEntityHandle NetworkEntityTracker::Get(NetEntityId netEntityId)
|
||||
{
|
||||
AZ::Entity* entity = GetRaw(netEntityId);
|
||||
return NetworkEntityHandle(entity, netEntityId, this);
|
||||
return NetworkEntityHandle(entity, this);
|
||||
}
|
||||
|
||||
ConstNetworkEntityHandle NetworkEntityTracker::Get(NetEntityId netEntityId) const
|
||||
{
|
||||
AZ::Entity* entity = GetRaw(netEntityId);
|
||||
return ConstNetworkEntityHandle(entity, netEntityId, this);
|
||||
return ConstNetworkEntityHandle(entity, this);
|
||||
}
|
||||
|
||||
NetEntityId NetworkEntityTracker::Get(const AZ::EntityId& entityId) const
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class NetBindComponent;
|
||||
|
||||
//! @class NetworkEntityTracker
|
||||
//! @brief The responsibly of this class is to allow entity netEntityId's to be looked up.
|
||||
class NetworkEntityTracker
|
||||
@@ -23,16 +25,26 @@ namespace Multiplayer
|
||||
|
||||
using EntityMap = AZStd::unordered_map<NetEntityId, AZ::Entity*>;
|
||||
using NetEntityIdMap = AZStd::unordered_map<AZ::EntityId, NetEntityId>;
|
||||
using NetBindingMap = AZStd::unordered_map<AZ::Entity*, NetBindComponent*>;
|
||||
using iterator = EntityMap::iterator;
|
||||
using const_iterator = EntityMap::const_iterator;
|
||||
|
||||
NetworkEntityTracker() = default;
|
||||
|
||||
//! Adds a networked entity to the tracker
|
||||
//! Adds a networked entity to the tracker.
|
||||
//! @param netEntityId the networkId of the entity to add
|
||||
//! @param entity pointer to the entity corresponding to the networkId
|
||||
void Add(NetEntityId netEntityId, AZ::Entity* entity);
|
||||
|
||||
//! Registers a new NetBindComponent with the NetworkEntityTracker.
|
||||
//! @param entity pointer to the entity we are registering the NetBindComponent for
|
||||
//! @param component pointer to the NetBindComponent being registered
|
||||
void RegisterNetBindComponent(AZ::Entity* entity, NetBindComponent* component);
|
||||
|
||||
//! Unregisters a NetBindComponent from the NetworkEntityTracker.
|
||||
//! @param component pointer to the NetBindComponent being removed
|
||||
void UnregisterNetBindComponent(NetBindComponent* component);
|
||||
|
||||
//! Returns an entity handle which can validate entity existence.
|
||||
NetworkEntityHandle Get(NetEntityId netEntityId);
|
||||
ConstNetworkEntityHandle Get(NetEntityId netEntityId) const;
|
||||
@@ -46,9 +58,14 @@ namespace Multiplayer
|
||||
//! Get a raw pointer of an entity.
|
||||
AZ::Entity *GetRaw(NetEntityId netEntityId) const;
|
||||
|
||||
//! Moves the given iterator out of the entity holder and returns the ptr
|
||||
//! Moves the given iterator out of the entity holder and returns the ptr.
|
||||
AZ::Entity *Move(EntityMap::iterator iter);
|
||||
|
||||
//! Retrieves the NetBindComponent for the provided AZ::Entity, nullptr if the entity does not have netbinding.
|
||||
//! @param entity pointer to the entity to retrieve the NetBindComponent for
|
||||
//! @return pointer to the entities NetBindComponent, or nullptr if the entity doesn't exist or does not have netbinding
|
||||
NetBindComponent* GetNetBindComponent(AZ::Entity* rawEntity) const;
|
||||
|
||||
//! Container overloads
|
||||
//!@{
|
||||
iterator begin();
|
||||
@@ -79,6 +96,7 @@ namespace Multiplayer
|
||||
|
||||
EntityMap m_entityMap;
|
||||
NetEntityIdMap m_netEntityIdMap;
|
||||
NetBindingMap m_netBindingMap;
|
||||
uint32_t m_deleteChangeDirty = 0;
|
||||
uint32_t m_addChangeDirty = 0;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,16 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
inline NetBindComponent* NetworkEntityTracker::GetNetBindComponent(AZ::Entity* rawEntity) const
|
||||
{
|
||||
auto found = m_netBindingMap.find(rawEntity);
|
||||
if (found != m_netBindingMap.end())
|
||||
{
|
||||
return found->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline NetworkEntityTracker::iterator NetworkEntityTracker::begin()
|
||||
{
|
||||
return m_entityMap.begin();
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
#include <AzCore/Math/ShapeIntersection.h>
|
||||
#include <AzFramework/Visibility/IVisibilitySystem.h>
|
||||
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
AZ_CVAR(float, sv_RewindVolumeExtrudeDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The amount to increase rewind volume checks to account for fast moving entities");
|
||||
AZ_CVAR(bool, bg_RewindDebugDraw, false, nullptr, AZ::ConsoleFunctorFlags::Null, "If true enables debug draw of rewind operations");
|
||||
|
||||
NetworkTime::NetworkTime()
|
||||
{
|
||||
@@ -94,21 +96,46 @@ namespace Multiplayer
|
||||
// Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities
|
||||
const AZ::Aabb expandedVolume = rewindVolume.GetExpanded(AZ::Vector3(sv_RewindVolumeExtrudeDistance));
|
||||
|
||||
AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
|
||||
AZStd::vector<NetBindComponent*> gatheredEntities;
|
||||
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume,
|
||||
[entityBoundsUnion, rewindVolume, &gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
|
||||
AzFramework::DebugDisplayRequests* debugDisplay = nullptr;
|
||||
if (bg_RewindDebugDraw)
|
||||
{
|
||||
gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size());
|
||||
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
|
||||
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
|
||||
debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
|
||||
}
|
||||
|
||||
if (debugDisplay)
|
||||
{
|
||||
debugDisplay->SetColor(AZ::Colors::Red);
|
||||
debugDisplay->DrawWireBox(expandedVolume.GetMin(), expandedVolume.GetMax());
|
||||
}
|
||||
|
||||
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
|
||||
AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
|
||||
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume,
|
||||
[this, debugDisplay, networkEntityTracker, entityBoundsUnion, rewindVolume](const AzFramework::IVisibilityScene::NodeData& nodeData)
|
||||
{
|
||||
m_rewoundEntities.reserve(m_rewoundEntities.size() + nodeData.m_entries.size());
|
||||
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
|
||||
{
|
||||
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
|
||||
{
|
||||
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
|
||||
const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityLocalBoundsUnion(entity->GetId());
|
||||
const AZ::Vector3 currentCenter = currentBounds.GetCenter();
|
||||
NetworkEntityHandle entityHandle(entity, networkEntityTracker);
|
||||
if (entityHandle.GetNetBindComponent() == nullptr)
|
||||
{
|
||||
// Not a net-bound entity, terminate processing of this entity
|
||||
return;
|
||||
}
|
||||
|
||||
const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityWorldBoundsUnion(entity->GetId());
|
||||
const AZ::Vector3 currentCenter = currentBounds.GetCenter();
|
||||
NetworkTransformComponent* networkTransform = entity->template FindComponent<NetworkTransformComponent>();
|
||||
if (debugDisplay)
|
||||
{
|
||||
debugDisplay->SetColor(AZ::Colors::White);
|
||||
debugDisplay->DrawWireBox(currentBounds.GetMin(), currentBounds.GetMax());
|
||||
}
|
||||
|
||||
if (networkTransform != nullptr)
|
||||
{
|
||||
@@ -123,24 +150,20 @@ namespace Multiplayer
|
||||
}
|
||||
const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
|
||||
const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
|
||||
if (debugDisplay)
|
||||
{
|
||||
debugDisplay->SetColor(AZ::Colors::Grey);
|
||||
debugDisplay->DrawWireBox(rewoundAabb.GetMin(), rewoundAabb.GetMax());
|
||||
}
|
||||
|
||||
if (AZ::ShapeIntersection::Overlaps(rewoundAabb, rewindVolume)) // Validate the rewound aabb intersects our rewind volume
|
||||
{
|
||||
// Due to component constraints, netBindComponent must exist if networkTransform exists
|
||||
NetBindComponent* netBindComponent = entity->template FindComponent<NetBindComponent>();
|
||||
gatheredEntities.push_back(netBindComponent);
|
||||
m_rewoundEntities.push_back(entityHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
|
||||
for (NetBindComponent* netBindComponent : gatheredEntities)
|
||||
{
|
||||
netBindComponent->NotifySyncRewindState();
|
||||
m_rewoundEntities.push_back(NetworkEntityHandle(netBindComponent, networkEntityTracker));
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTime::ClearRewoundEntities()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
|
||||
@@ -37,20 +38,19 @@ namespace Multiplayer
|
||||
|
||||
void NetworkSpawnableHolderComponent::Activate()
|
||||
{
|
||||
const auto agentType = GetMultiplayer()->GetAgentType();
|
||||
const bool shouldSpawnNetEntities =
|
||||
(agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer);
|
||||
IMultiplayer* multiplayer = GetMultiplayer();
|
||||
const bool shouldSpawnNetEntities = multiplayer->GetShouldSpawnNetworkEntities();
|
||||
|
||||
if(shouldSpawnNetEntities)
|
||||
if (shouldSpawnNetEntities)
|
||||
{
|
||||
AZ::Transform rootEntityTransform = AZ::Transform::CreateIdentity();
|
||||
|
||||
if(auto* transformInterface = GetEntity()->GetTransform())
|
||||
if (auto* transformInterface = GetEntity()->GetTransform())
|
||||
{
|
||||
rootEntityTransform = transformInterface->GetWorldTM();
|
||||
}
|
||||
|
||||
INetworkEntityManager* networkEntityManager = GetNetworkEntityManager();
|
||||
INetworkEntityManager* networkEntityManager = multiplayer->GetNetworkEntityManager();
|
||||
AZ_Assert(networkEntityManager != nullptr,
|
||||
"Network Entity Manager must be initialized before NetworkSpawnableHolderComponent is activated");
|
||||
|
||||
|
||||
@@ -7,9 +7,16 @@
|
||||
*/
|
||||
|
||||
#include <Source/ReplicationWindows/NullReplicationWindow.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
NullReplicationWindow::NullReplicationWindow(AzNetworking::IConnection* connection)
|
||||
: m_connection(connection)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
bool NullReplicationWindow::ReplicationSetUpdateReady()
|
||||
{
|
||||
return true;
|
||||
@@ -36,6 +43,29 @@ namespace Multiplayer
|
||||
;
|
||||
}
|
||||
|
||||
AzNetworking::PacketId NullReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector)
|
||||
{
|
||||
MultiplayerPackets::EntityUpdates entityUpdatePacket;
|
||||
entityUpdatePacket.SetHostTimeMs(GetNetworkTime()->GetHostTimeMs());
|
||||
entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId());
|
||||
entityUpdatePacket.SetEntityMessages(entityUpdateVector);
|
||||
return m_connection->SendUnreliablePacket(entityUpdatePacket);
|
||||
}
|
||||
|
||||
void NullReplicationWindow::SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable)
|
||||
{
|
||||
MultiplayerPackets::EntityRpcs entityRpcsPacket;
|
||||
entityRpcsPacket.SetEntityRpcs(entityRpcVector);
|
||||
if (reliable)
|
||||
{
|
||||
m_connection->SendReliablePacket(entityRpcsPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connection->SendUnreliablePacket(entityRpcsPacket);
|
||||
}
|
||||
}
|
||||
|
||||
void NullReplicationWindow::DebugDraw() const
|
||||
{
|
||||
// Nothing to draw
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -16,7 +17,7 @@ namespace Multiplayer
|
||||
: public IReplicationWindow
|
||||
{
|
||||
public:
|
||||
NullReplicationWindow() = default;
|
||||
NullReplicationWindow(AzNetworking::IConnection* connection);
|
||||
|
||||
//! IReplicationWindow interface
|
||||
//! @{
|
||||
@@ -25,10 +26,13 @@ namespace Multiplayer
|
||||
uint32_t GetMaxProxyEntityReplicatorSendCount() const override;
|
||||
bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const override;
|
||||
void UpdateWindow() override;
|
||||
AzNetworking::PacketId SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) override;
|
||||
void SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) override;
|
||||
void DebugDraw() const override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
ReplicationSet m_emptySet;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
+44
-21
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <AzFramework/Visibility/IVisibilitySystem.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
@@ -50,7 +51,7 @@ namespace Multiplayer
|
||||
return m_priority < rhs.m_priority;
|
||||
}
|
||||
|
||||
ServerToClientReplicationWindow::ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, const AzNetworking::IConnection* connection)
|
||||
ServerToClientReplicationWindow::ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, AzNetworking::IConnection* connection)
|
||||
: m_controlledEntity(controlledEntity)
|
||||
, m_entityActivatedEventHandler([this](AZ::Entity* entity) { OnEntityActivated(entity); })
|
||||
, m_entityDeactivatedEventHandler([this](AZ::Entity* entity) { OnEntityDeactivated(entity); })
|
||||
@@ -106,7 +107,7 @@ namespace Multiplayer
|
||||
|
||||
void ServerToClientReplicationWindow::UpdateWindow()
|
||||
{
|
||||
// clear the candidate queue, we're going to rebuild it
|
||||
// Clear the candidate queue, we're going to rebuild it
|
||||
ReplicationCandidateQueue::container_type clearQueueContainer;
|
||||
clearQueueContainer.reserve(sv_MaxEntitiesToTrackReplication);
|
||||
// Move the clearQueueContainer into the ReplicationCandidateQueue to maintain the reserved memory
|
||||
@@ -117,7 +118,7 @@ namespace Multiplayer
|
||||
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
|
||||
if (!netBindComponent || !netBindComponent->HasController())
|
||||
{
|
||||
// if we don't have a controlled entity, or we no longer have control of the entity, don't run the update
|
||||
// If we don't have a controlled entity, or we no longer have control of the entity, don't run the update
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,24 +149,25 @@ namespace Multiplayer
|
||||
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
|
||||
{
|
||||
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
|
||||
NetworkEntityHandle entityHandle(entity, networkEntityTracker);
|
||||
if (entityHandle.GetNetBindComponent() == nullptr)
|
||||
{
|
||||
// Entity does not have netbinding, skip this entity
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filterEntityManager && filterEntityManager->IsEntityFiltered(entity, m_controlledEntity, m_connection->GetConnectionId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NetBindComponent* entryNetBindComponent = entity->template FindComponent<NetBindComponent>();
|
||||
if (entryNetBindComponent != nullptr)
|
||||
{
|
||||
// We want to find the closest extent to the player and prioritize using that distance
|
||||
const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
|
||||
const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
|
||||
const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
|
||||
const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
|
||||
|
||||
NetworkEntityHandle entityHandle(entryNetBindComponent, networkEntityTracker);
|
||||
AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
|
||||
}
|
||||
// We want to find the closest extent to the player and prioritize using that distance
|
||||
const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
|
||||
const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
|
||||
const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
|
||||
const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
|
||||
|
||||
AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
|
||||
}
|
||||
|
||||
// Add in Autonomous Entities
|
||||
@@ -179,6 +181,29 @@ namespace Multiplayer
|
||||
//}
|
||||
}
|
||||
|
||||
AzNetworking::PacketId ServerToClientReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector)
|
||||
{
|
||||
MultiplayerPackets::EntityUpdates entityUpdatePacket;
|
||||
entityUpdatePacket.SetHostTimeMs(GetNetworkTime()->GetHostTimeMs());
|
||||
entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId());
|
||||
entityUpdatePacket.SetEntityMessages(entityUpdateVector);
|
||||
return m_connection->SendUnreliablePacket(entityUpdatePacket);
|
||||
}
|
||||
|
||||
void ServerToClientReplicationWindow::SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable)
|
||||
{
|
||||
MultiplayerPackets::EntityRpcs entityRpcsPacket;
|
||||
entityRpcsPacket.SetEntityRpcs(entityRpcVector);
|
||||
if (reliable)
|
||||
{
|
||||
m_connection->SendReliablePacket(entityRpcsPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connection->SendUnreliablePacket(entityRpcsPacket);
|
||||
}
|
||||
}
|
||||
|
||||
void ServerToClientReplicationWindow::DebugDraw() const
|
||||
{
|
||||
//static const float BoundaryStripeHeight = 1.0f;
|
||||
@@ -204,11 +229,10 @@ namespace Multiplayer
|
||||
|
||||
void ServerToClientReplicationWindow::OnEntityActivated(AZ::Entity* entity)
|
||||
{
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
ConstNetworkEntityHandle entityHandle(entity);
|
||||
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
ConstNetworkEntityHandle entityHandle(netBindComponent, GetNetworkEntityTracker());
|
||||
|
||||
if (netBindComponent->HasController())
|
||||
{
|
||||
if (IFilterEntityManager* filter = GetMultiplayer()->GetFilterEntityManager())
|
||||
@@ -237,10 +261,9 @@ namespace Multiplayer
|
||||
|
||||
void ServerToClientReplicationWindow::OnEntityDeactivated(AZ::Entity* entity)
|
||||
{
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
if (netBindComponent != nullptr)
|
||||
ConstNetworkEntityHandle entityHandle(entity);
|
||||
if (entityHandle.GetNetBindComponent() != nullptr)
|
||||
{
|
||||
ConstNetworkEntityHandle entityHandle(netBindComponent, GetNetworkEntityTracker());
|
||||
m_replicationSet.erase(entityHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Multiplayer
|
||||
// we sort lowest priority first, so that we can easily keep the biggest N priorities
|
||||
using ReplicationCandidateQueue = AZStd::priority_queue<PrioritizedReplicationCandidate>;
|
||||
|
||||
ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, const AzNetworking::IConnection* connection);
|
||||
ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, AzNetworking::IConnection* connection);
|
||||
|
||||
//! IReplicationWindow interface
|
||||
//! @{
|
||||
@@ -47,6 +47,8 @@ namespace Multiplayer
|
||||
uint32_t GetMaxProxyEntityReplicatorSendCount() const override;
|
||||
bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const override;
|
||||
void UpdateWindow() override;
|
||||
AzNetworking::PacketId SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) override;
|
||||
void SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) override;
|
||||
void DebugDraw() const override;
|
||||
//! @}
|
||||
|
||||
@@ -75,7 +77,7 @@ namespace Multiplayer
|
||||
|
||||
//NetBindComponent* m_controlledNetBindComponent = nullptr;
|
||||
|
||||
const AzNetworking::IConnection* m_connection = nullptr;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
|
||||
// Cached values to detect a poor network connection
|
||||
uint32_t m_lastCheckedSentPackets = 0;
|
||||
|
||||
Reference in New Issue
Block a user