Integrating github/staging through commit ab87ed9

This commit is contained in:
alexpete
2021-04-09 11:27:37 -07:00
parent ae62a97894
commit 1044dc3da1
1582 changed files with 29374 additions and 519051 deletions
@@ -1,5 +1,13 @@
#pragma once
#include <AzCore/std/containers/list.h>
namespace AZ
{
class ComponentDescriptor;
class ReflectContext;
}
{% set Namespace = dataFiles[0].attrib['Namespace'] %}
namespace {{ Namespace }}
{
@@ -10,4 +18,6 @@ namespace {{ Namespace }}
{{ ComponentName }},
{% endfor %}
};
void CreateComponentDescriptors(AZStd::list<AZ::ComponentDescriptor*>& descriptors);
}
@@ -0,0 +1,23 @@
#include <AzCore/Component/Component.h>
{% for Component in dataFiles %}
{% set ComponentDerived = Component.attrib['OverrideComponent']|booleanTrue %}
{% set ControllerDerived = Component.attrib['OverrideController']|booleanTrue %}
{% if ComponentDerived or ControllerDerived %}
#include <{{ Component.attrib['OverrideInclude'] }}>
{% else %}
#include <Source/AutoGen/{{ Component.attrib['Name'] }}.AutoComponent.h>
{% endif %}
{% endfor %}
{% set Namespace = dataFiles[0].attrib['Namespace'] %}
namespace {{ Namespace }}
{
void CreateComponentDescriptors(AZStd::list<AZ::ComponentDescriptor*>& descriptors)
{
descriptors.insert(descriptors.end(), {
{% for Component in dataFiles %}
{{ Component.attrib['Name'] }}::CreateDescriptor(),
{% endfor %}
});
}
}
@@ -67,20 +67,26 @@
{#
#}
{%- macro GetNetPropertiesDirtyEnumName(Component, ClassType, ReplicateFrom, ReplicateTo) -%}
{{ ReplicateFrom }}To{{ ReplicateTo }}DirtyEnum
{%- macro GetNetPropertiesDirtyEnumName(ComponentName, ReplicateFrom, ReplicateTo) -%}
{{ UpperFirst(ComponentName) }}Internal::{{ ReplicateFrom }}To{{ ReplicateTo }}DirtyEnum
{%- endmacro -%}
{#
#}
{%- macro GetNetPropertiesPropertyDirtyEnum(Property) -%}
{{ Property.attrib['Name'] }}_DirtyFlag
{%- macro GetNetPropertiesPropertyDirtyEnum(Property, Extension = "none") -%}
{% if Extension == "none" %}
{{ Property.attrib['Name'] }}_DirtyFlag{% elif Extension.lower() == "size" %}
{{ Property.attrib['Name'] }}_Size_DirtyFlag{% elif Extension.lower() == "start" %}
{{ Property.attrib['Name'] }}_Start_DirtyFlag{% elif Extension.lower() == "end" %}
{{ Property.attrib['Name'] }}_End_DirtyFlag{% else %}
#error "Unknown extension ({{ Extension }}) passed to GetNetPropertiesPropertyDirtyEnum"
{% endif %}
{%- endmacro -%}
{#
#}
{%- macro GetNetPropertiesQualifiedPropertyDirtyEnum(Component, ClassType, ReplicateFrom, ReplicateTo, Property) -%}
{{ GetNetPropertiesDirtyEnumName(Component, ClassType, ReplicateFrom, ReplicateTo) }}::{{ GetNetPropertiesPropertyDirtyEnum(Property) }}
{%- macro GetNetPropertiesQualifiedPropertyDirtyEnum(ComponentName, ReplicateFrom, ReplicateTo, Property, Extension = "none") -%}
{{ GetNetPropertiesDirtyEnumName(ComponentName, ReplicateFrom, ReplicateTo) }}::{{ GetNetPropertiesPropertyDirtyEnum(Property, Extension) }}
{%- endmacro -%}
{#
@@ -138,54 +144,6 @@ AZ::Event<{{ Property.attrib['Type'] }}>
{%- endmacro -%}
{#
#}
{% macro DefineNetworkPropertyReflection(Component, ReplicateFrom, ReplicateTo, ClassName) %}
{% call(Property) ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %}
{% if Property.attrib['ExposeToEditor'] | booleanTrue %}
->Field("{{ Property.attrib['Name'] }}", &{{ ClassName }}::m_{{ LowerFirst(Property.attrib['Name']) }})
{% endif %}
{% endcall -%}
{% endmacro %}
{#
#}
{% macro DefineArchetypePropertyReflection(Component, ClassName) %}
{% call(Property) ParseArchetypeProperties(Component) %}
{% if Property.attrib['ExposeToEditor'] | booleanTrue %}
->Field("{{ Property.attrib['Name'] }}", &{{ ClassName }}::m_{{ LowerFirst(Property.attrib['Name']) }})
{% endif %}
{% endcall %}
{% endmacro %}
{#
#}
{% macro DefineNetworkPropertyConstructors(Component, ReplicateFrom, ReplicateTo, ClassType) %}
{% call(Property) ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %}
{% if Property.attrib['Container'] == 'Array' %}
, m_{{ LowerFirst(Property.attrib['Name']) }}({% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{{ Property.attrib['Type'] }}, Multiplayer::RewindHistorySize>({% endif %}{{ Property.attrib['Init'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, this){% endif %})
{% elif Property.attrib['Container'] == 'Vector' %}
, m_{{ LowerFirst(Property.attrib['Name']) }}({{ Property.attrib['Init'] }})
{% elif Property.attrib['IsRewindable']|booleanTrue %}
, m_{{ LowerFirst(Property.attrib['Name']) }}({{ Property.attrib['Init'] }}, this)
{% else %}
, m_{{ LowerFirst(Property.attrib['Name']) }}({{ Property.attrib['Init'] }})
{% endif %}
{% endcall %}
{% endmacro %}
{#
#}
{% macro DefineArchetypePropertyConstructors(Component) %}
{% call(Property) ParseArchetypeProperties(Component) %}
{% if Property.attrib['Container'] == 'Vector' %}
, m_{{ LowerFirst(Property.attrib['Name']) }}({{ Property.attrib['Init'] }}, {{ Property.attrib['Count'] }})
{% else %}
, m_{{ LowerFirst(Property.attrib['Name']) }}({{ Property.attrib['Init'] }})
{% endif %}
{% endcall %}
{% endmacro %}
{#
#}
{% macro ParseComponentServiceTypeAndName(Component) %}
{% for Service in Component.iter('ComponentRelation') %}
@@ -158,7 +158,7 @@ AZ::Event<{{ Property.attrib['Type'] }}> m_{{ LowerFirst(Property.attrib['Name']
{% macro DeclareNetworkPropertyVars(Component, ReplicateFrom, ReplicateTo) %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %}
{% if Property.attrib['Container'] == 'Array' %}
AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> m_{{ Property.attrib['Name'] }};
AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }};
{% elif Property.attrib['Container'] == 'Vector' %}
AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }};
{% elif Property.attrib['IsRewindable']|booleanTrue %}
@@ -170,6 +170,22 @@ Multiplayer::RewindableObject<{{ Property.attrib['Type'] }}, Multiplayer::Rewind
{% endmacro %}
{#
#}
{% macro DeclareNetworkPropertyReflectVars(Component, ReplicateFrom, ReplicateTo) %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %}
{% if Property.attrib['IsRewindable']|booleanTrue %}
{% if Property.attrib['Container'] == 'Array' %}
AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}Reflect;
{% elif Property.attrib['Container'] == 'Vector' %}
AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}Reflect;
{% else %}
{{ Property.attrib['Type'] }} m_{{ LowerFirst(Property.attrib['Name']) }}Reflect = {{ Property.attrib['Init'] }};
{% endif %}
{% endif %}
{% endcall %}
{% endmacro %}
{#
#}
{% macro DeclareArchetypePropertyVars(Component) %}
{% call(Property) AutoComponentMacros.ParseArchetypeProperties(Component) %}
@@ -262,6 +278,13 @@ namespace {{ Component.attrib['Namespace'] }}
//! Sets the bits in the attached record that correspond to predictable network properties.
void SetPredictableBits();
{% set networkPropertyCount = {'value' : 0} %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Authority') %}
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
{% endcall %}
{% if networkPropertyCount.value > 0 %}
AzNetworking::FixedSizeBitsetView m_authorityToAuthority;
{% endif %}
{% set networkPropertyCount = {'value' : 0} %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Client') %}
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
@@ -295,6 +318,7 @@ namespace {{ Component.attrib['Namespace'] }}
{{ RecordName }}
(
Multiplayer::ReplicationRecord& replicationRecord,
uint32_t authorityToAuthoritySimluationStartOffset,
uint32_t authorityToClientSimluationStartOffset,
uint32_t authorityToServerSimluationStartOffset,
uint32_t authorityToAutonomousStartOffset,
@@ -346,9 +370,14 @@ namespace {{ Component.attrib['Namespace'] }}
void ProcessInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) override {}
//! @}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Authority', false)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Authority', true)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Server', false)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Server', true)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', false)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', true)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', false)|indent(8) }}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', true)|indent(8) }}
{{ DeclareArchetypePropertyGetters(Component)|indent(8) -}}
{{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}}
{{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}}
@@ -380,7 +409,7 @@ namespace {{ Component.attrib['Namespace'] }}
public:
{% if ComponentDerived %}
AZ_CLASS_ALLOCATOR({{ ComponentBaseName }}, AZ::SystemAllocator, 0);
AZ_RTTI({{ ComponentBaseName }}, s_{{ LowerFirst(ComponentName) }}ConcreteUuid, AZ::Component);
AZ_RTTI({{ ComponentBaseName }}, "{{ (ComponentBaseName) | createHashGuid }}", Multiplayer::MultiplayerComponent);
{% else %}
AZ_MULTIPLAYER_COMPONENT({{ Component.attrib['Namespace'] }}::{{ ComponentBaseName }}, s_{{ LowerFirst(ComponentName) }}ConcreteUuid, Multiplayer::MultiplayerComponent);
{% endif %}
@@ -416,10 +445,10 @@ namespace {{ Component.attrib['Namespace'] }}
bool HandleRpcMessage(Multiplayer::NetEntityRole remoteRole, Multiplayer::NetworkEntityRpcMessage& rpcMessage) override;
bool SerializeStateDeltaMessage(Multiplayer::ReplicationRecord& replicationRecord, AzNetworking::ISerializer& serializer) override;
void NotifyStateDeltaChanges(Multiplayer::ReplicationRecord& replicationRecord) override;
protected:
bool HasController() const override;
MultiplayerController* GetController() override;
protected:
void ConstructController() override;
void DestructController() override;
void ActivateController(Multiplayer::EntityIsMigrating entityIsMigrating) override;
@@ -427,6 +456,7 @@ namespace {{ Component.attrib['Namespace'] }}
void NetworkAttach(Multiplayer::NetBindComponent* netBindComponent, Multiplayer::ReplicationRecord& currentEntityRecord, Multiplayer::ReplicationRecord& predictableEntityRecord) override;
//! @}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Authority', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}}
@@ -440,6 +470,10 @@ namespace {{ Component.attrib['Namespace'] }}
{% endif %}
{% endfor %}
private:
//! Authority To Authority serializers (hot backup in case of server failure)
bool SerializeAuthorityToAuthorityProperties({{ RecordName }}& replicationRecord, AzNetworking::ISerializer& serializer);
void NotifyChangesAuthorityToAuthorityProperties(const {{ RecordName }}& replicationRecord) const;
//! Authority to Client serializers
bool SerializeAuthorityToClientProperties({{ RecordName }}& replicationRecord, AzNetworking::ISerializer& serializer);
void NotifyChangesAuthorityToClientProperties(const {{ RecordName }}& replicationRecord) const;
@@ -460,15 +494,26 @@ namespace {{ Component.attrib['Namespace'] }}
AZStd::unique_ptr<{{ ControllerName }}> m_controller;
//! Network Properties
{{ DeclareNetworkPropertyVars(Component, 'Authority', 'Authority')|indent(8) -}}
{{ DeclareNetworkPropertyVars(Component, 'Authority', 'Server')|indent(8) -}}
{{ DeclareNetworkPropertyVars(Component, 'Authority', 'Client')|indent(8) -}}
{{ DeclareNetworkPropertyVars(Component, 'Authority', 'Autonomous')|indent(8) -}}
{{ DeclareNetworkPropertyVars(Component, 'Autonomous', 'Authority')|indent(8) }}
//! Network Properties for reflection and editor support
{{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Authority')|indent(8) -}}
{{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Server')|indent(8) -}}
{{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Client')|indent(8) -}}
{{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Autonomous')|indent(8) -}}
{{ DeclareNetworkPropertyReflectVars(Component, 'Autonomous', 'Authority')|indent(8) }}
//! NetworkProperty Events
{{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Authority')|indent(8) -}}
{{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Server')|indent(8) -}}
{{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Client')|indent(8) -}}
{{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Autonomous')|indent(8) -}}
{{ DeclareNetworkPropertyEvents(Component, 'Autonomous', 'Authority')|indent(8) }}
//! Archetype Properties
{{ DeclareArchetypePropertyVars(Component)|indent(8) }}
{% call(Type, Name) AutoComponentMacros.ParseComponentServiceTypeAndName(Component) %}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,35 @@
<?xml version="1.0"?>
<Component
Name="LocalPredictionPlayerInputComponent"
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="true"
OverrideInclude="Source/Components/LocalPredictionPlayerInputComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Weak" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Source/Components/NetworkTransformComponent.h" />
<Include File="Source/MultiplayerTypes.h"/>
<Include File="Source/NetworkInput/NetworkInput.h"/>
<Include File="Source/NetworkInput/NetworkInputHistory.h"/>
<Include File="Source/NetworkInput/NetworkInputVector.h"/>
<Include File="AzNetworking/DataStructures/ByteBuffer.h"/>
<NetworkProperty Type="Multiplayer::NetworkInputId" Name="LastInputId" Init="Multiplayer::NetworkInputId{0}" ReplicateFrom="Authority" ReplicateTo="Authority" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" GenerateEventBindings="false" />
<RemoteProcedure Name="SendClientInput" InvokeFrom="Autonomous" HandleOn="Authority" IsPublic="true" IsReliable="false" Description="Client to server move / input RPC">
<Param Type="Multiplayer::NetworkInputVector" Name="inputArray" />
<Param Type="uint32_t" Name="stateHash" />
<Param Type="AzNetworking::PacketEncodingBuffer" Name="clientState" Description="This is for debugging desyncs only; release games should not populate this parameter" />
</RemoteProcedure>
<RemoteProcedure Name="SendClientInputCorrection" InvokeFrom="Authority" HandleOn="Autonomous" IsPublic="true" IsReliable="false" Description="Autonomous proxy correction RPC">
<Param Type="Multiplayer::NetworkInputId" Name="inputId" />
<Param Type="AzNetworking::PacketEncodingBuffer" Name="correction" />
</RemoteProcedure>
<RemoteProcedure Name="SendMigrateClientInput" InvokeFrom="Autonomous" HandleOn="Authority" IsPublic="true" IsReliable="false" Description="Client to server migrate move / input RPC">
<Param Type="Multiplayer::MigrateNetworkInputVector" Name="inputArray" />
</RemoteProcedure>
</Component>
@@ -12,42 +12,15 @@
<Include File="Source/MultiplayerTypes.h"/>
<NetworkProperty Type="AZ::Quaternion" Name="rotation" Init="AZ::Quaternion::CreateIdentity()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="true" GenerateEventBindings="true" />
<NetworkProperty Type="AZ::Vector3" Name="translation" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="true" GenerateEventBindings="true" />
<NetworkProperty Type="AZ::Vector3" Name="scale" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="true" GenerateEventBindings="true" />
<NetworkProperty Type="AZ::Vector3" Name="velocity" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="true" GenerateEventBindings="true" />
<NetworkProperty Type="AZ::Quaternion" Name="rotation" Init="AZ::Quaternion::CreateIdentity()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" GenerateEventBindings="true" />
<NetworkProperty Type="AZ::Vector3" Name="translation" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" GenerateEventBindings="true" />
<NetworkProperty Type="AZ::Vector3" Name="scale" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" GenerateEventBindings="true" />
<NetworkProperty Type="uint8_t" Name="resetCount" Init="0" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" GenerateEventBindings="true" />
<NetworkProperty Type="NetEntityId" Name="parentEntityId" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" GenerateEventBindings="true" />
<NetworkProperty Type="int32_t" Name="parentAttachmentBoneId" Init="-1" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" GenerateEventBindings="true" />
<!--
<ArchetypeProperty Type="bool" Name="snapToGround" Init="false" ExposeToEditor="true" />
<ArchetypeProperty Type="bool" Name="useGroundNormal" Init="false" ExposeToEditor="true" />
<!--
This is reference only, will be deleted when we make a better test component that exercizes all aspects of component autogen
<RemoteProcedure Name="RPCServerToAuthorityPublic" InvokeFrom="Server" HandleOn="Authority" IsPublic="true" IsReliable="false" Description="Description" >
<Param Name="Test" Type="int" DefaultValue="0" Description="Description"/>
</RemoteProcedure>
<RemoteProcedure Name="RPCServerToAuthorityScriptProtected" InvokeFrom="Server" HandleOn="Authority" IsPublic="false" IsReliable="false" Description="Description" >
<Param Name="Test" Type="int" DefaultValue="0" Description="Description"/>
</RemoteProcedure>
<RemoteProcedure Name="RPCAutonomousToAuthorityPublic" InvokeFrom="Autonomous" HandleOn="Authority" IsPublic="true" IsReliable="false" Description="Description" >
<Param Name="Test" Type="int" DefaultValue="0" Description="Description"/>
</RemoteProcedure>
<RemoteProcedure Name="RPCAuthorityToAutonomousPublic" InvokeFrom="Authority" HandleOn="Autonomous" IsPublic="true" IsReliable="false" Description="Description" >
<Param Name="Test" Type="int" DefaultValue="0" Description="Description"/>
</RemoteProcedure>
<RemoteProcedure Name="RpcAuthorityToClientPublic" InvokeFrom="Authority" HandleOn="Client" IsPublic="true" IsReliable="false" Description="Description" >
<Param Name="Test" Type="int" DefaultValue="0" Description="Description"/>
</RemoteProcedure>
<RemoteProcedure Name="RpcAuthorityToClientProtected" InvokeFrom="Authority" HandleOn="Client" IsPublic="false" IsReliable="false" Description="Description" >
<Param Name="Test" Type="int" DefaultValue="0" Description="Description"/>
</RemoteProcedure>
<RemoteProcedure Name="RpcAuthorityToServerPublic" InvokeFrom="Authority" HandleOn="Server" IsPublic="true" IsReliable="true" Description="Description" >
<Param Name="Test" Type="int" DefaultValue="0" Description="Description"/>
</RemoteProcedure>
<RemoteProcedure Name="RpcAuthorityToServerProtected" InvokeFrom="Authority" HandleOn="Server" IsPublic="false" IsReliable="true" Description="Description" >
<Param Name="Test" Type="int" DefaultValue="0" Description="Description"/>
</RemoteProcedure>
-->
</Component>
@@ -0,0 +1,57 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Components/LocalPredictionPlayerInputComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
namespace Multiplayer
{
void LocalPredictionPlayerInputComponent::LocalPredictionPlayerInputComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<LocalPredictionPlayerInputComponent, LocalPredictionPlayerInputComponentBase>()
->Version(1);
}
LocalPredictionPlayerInputComponentBase::Reflect(context);
}
void LocalPredictionPlayerInputComponentController::HandleSendClientInput
(
[[maybe_unused]] const Multiplayer::NetworkInputVector& inputArray,
[[maybe_unused]] const uint32_t& stateHash,
[[maybe_unused]] const AzNetworking::PacketEncodingBuffer& clientState
)
{
;
}
void LocalPredictionPlayerInputComponentController::HandleSendMigrateClientInput
(
[[maybe_unused]] const Multiplayer::MigrateNetworkInputVector& inputArray
)
{
;
}
void LocalPredictionPlayerInputComponentController::HandleSendClientInputCorrection
(
[[maybe_unused]] const Multiplayer::NetworkInputId& inputId,
[[maybe_unused]] const AzNetworking::PacketEncodingBuffer& correction
)
{
;
}
}
@@ -0,0 +1,47 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.h>
namespace Multiplayer
{
class LocalPredictionPlayerInputComponent
: public LocalPredictionPlayerInputComponentBase
{
public:
AZ_MULTIPLAYER_COMPONENT(Multiplayer::LocalPredictionPlayerInputComponent, s_localPredictionPlayerInputComponentConcreteUuid, Multiplayer::LocalPredictionPlayerInputComponentBase);
static void Reflect([[maybe_unused]] AZ::ReflectContext* context);
void OnInit() override {}
void OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
void OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
};
class LocalPredictionPlayerInputComponentController
: public LocalPredictionPlayerInputComponentControllerBase
{
public:
LocalPredictionPlayerInputComponentController(LocalPredictionPlayerInputComponent& parent) : LocalPredictionPlayerInputComponentControllerBase(parent) {}
void OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
void OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
void HandleSendClientInput(const Multiplayer::NetworkInputVector& inputArray, const uint32_t& stateHash, const AzNetworking::PacketEncodingBuffer& clientState) override;
void HandleSendMigrateClientInput(const Multiplayer::MigrateNetworkInputVector& inputArray) override;
void HandleSendClientInputCorrection(const Multiplayer::NetworkInputId& inputId, const AzNetworking::PacketEncodingBuffer& correction) override;
};
}
@@ -16,9 +16,9 @@
namespace Multiplayer
{
void MultiplayerComponent::Reflect(AZ::ReflectContext* reflection)
void MultiplayerComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MultiplayerComponent, AZ::Component>()
@@ -17,10 +17,11 @@
#include <AzNetworking/DataStructures/FixedSizeBitsetView.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <Source/MultiplayerTypes.h>
#include <Include/IMultiplayer.h>
//! Macro to declare bindings for a multiplayer component inheriting from MultiplayerComponent
#define AZ_MULTIPLAYER_COMPONENT(ComponentClass, Guid, Base) \
AZ_RTTI(ComponentClass, Guid, AZ::Component) \
AZ_RTTI(ComponentClass, Guid, Base) \
AZ_COMPONENT_INTRUSIVE_DESCRIPTOR_TYPE(ComponentClass) \
AZ_COMPONENT_BASE(ComponentClass, Guid, Base)
@@ -38,7 +39,7 @@ namespace Multiplayer
AZ_CLASS_ALLOCATOR(MultiplayerComponent, AZ::SystemAllocator, 0);
AZ_RTTI(MultiplayerComponent, "{B7F5B743-CCD3-4981-8F1A-FC2B95CE22D7}", AZ::Component);
static void Reflect(AZ::ReflectContext* reflection);
static void Reflect(AZ::ReflectContext* context);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
MultiplayerComponent() = default;
@@ -70,10 +71,10 @@ namespace Multiplayer
virtual bool HandleRpcMessage(NetEntityRole netEntityRole, NetworkEntityRpcMessage& rpcMessage) = 0;
virtual bool SerializeStateDeltaMessage(ReplicationRecord& replicationRecord, AzNetworking::ISerializer& serializer) = 0;
virtual void NotifyStateDeltaChanges(ReplicationRecord& replicationRecord) = 0;
protected:
virtual bool HasController() const = 0;
virtual MultiplayerController* GetController() = 0;
protected:
virtual void ConstructController() = 0;
virtual void DestructController() = 0;
virtual void ActivateController(EntityIsMigrating entityIsMigrating) = 0;
@@ -113,27 +114,30 @@ namespace Multiplayer
{
if (bitset.GetBit(bitIndex))
{
//uint32_t prevUpdateSize = serializer.GetSize();
const uint32_t prevUpdateSize = serializer.GetSize();
serializer.ClearTrackedChangesFlag();
serializer.Serialize(value, name);
if (modifyRecord && !serializer.GetTrackedChangesFlag())
{
bitset.SetBit(bitIndex, false);
}
//uint32_t postUpdateSize = serializer.GetSize();
const uint32_t postUpdateSize = serializer.GetSize();
// Network Property metrics
// uint32_t updateSize = (postUpdateSize - prevUpdateSize);
// if (updateSize > 0)
// {
// if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)
// {
// GetPacketHandlerMetricsInstance().LogRecvNetworkPropertyUpdates(componentType, name, updateSize);
// }
// else
// {
// GetPacketHandlerMetricsInstance().LogSentNetworkPropertyUpdates(componentType, name, updateSize);
// }
// }
const uint32_t updateSize = (postUpdateSize - prevUpdateSize);
if (updateSize > 0)
{
MultiplayerStats& stats = AZ::Interface<IMultiplayer>::Get()->GetStats();
if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)
{
stats.m_propertyUpdatesRecv++;
stats.m_propertyUpdatesRecvBytes += updateSize;
}
else
{
stats.m_propertyUpdatesSent++;
stats.m_propertyUpdatesSentBytes += updateSize;
}
}
}
}
}
@@ -37,13 +37,13 @@ namespace Multiplayer
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<NetBindComponent>("NetBindComponent", "Required Component for binding an entity to the network")
editContext->Class<NetBindComponent>(
"Network Binding", "The Network Binding component marks an entity as able to be replicated across the network")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/NetBind.png")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/NetBind.png")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
;
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
}
}
}
@@ -69,7 +69,7 @@ namespace Multiplayer
void NetBindComponent::Init()
{
m_netEntityHandle = GetNetworkEntityManager()->AddEntityToEntityMap(m_netEntityId, GetEntity());
;
}
void NetBindComponent::Activate()
@@ -92,7 +92,7 @@ namespace Multiplayer
void NetBindComponent::Deactivate()
{
AZ_Assert(m_needsToBeStopped == false, "Entity appears to have been deleted with using the EntityManagerBase. Use MarkForRemoval to correctly clean up an entity");
AZ_Assert(m_needsToBeStopped == false, "Entity appears to have been improperly deleted. Use MarkForRemoval to correctly clean up a networked entity.");
m_handleLocalServerRpcMessageEventHandle.Disconnect();
if (NetworkRoleHasController(m_netEntityRole))
{
@@ -139,7 +139,8 @@ namespace Multiplayer
MultiplayerComponentInputVector NetBindComponent::AllocateComponentInputs()
{
MultiplayerComponentInputVector componentInputs;
for (MultiplayerComponent* multiplayerComponent : m_multiplayerInputComponentVector)
const size_t multiplayerComponentSize = m_multiplayerInputComponentVector.size();
for (size_t i = 0; i < multiplayerComponentSize; ++i)
{
// TODO: ComponentInput factory, needs multiplayer component architecture and autogen
AZStd::unique_ptr<IMultiplayerComponentInput> componentInput = nullptr; // ComponentInputFactory(multiplayerComponent->GetComponentId());
@@ -350,9 +351,10 @@ namespace Multiplayer
{
AZ_Assert(entity != nullptr, "AZ::Entity is null");
m_prefabEntityId = prefabEntityId;
m_netEntityId = netEntityId;
m_netEntityRole = netEntityRole;
m_prefabEntityId = prefabEntityId;
m_netEntityHandle = GetNetworkEntityManager()->AddEntityToEntityMap(m_netEntityId, entity);
for (AZ::Component* component : entity->GetComponents())
{
@@ -11,6 +11,8 @@
*/
#include <Source/Components/NetworkTransformComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
namespace Multiplayer
{
@@ -22,5 +24,7 @@ namespace Multiplayer
serializeContext->Class<NetworkTransformComponent, NetworkTransformComponentBase>()
->Version(1);
}
NetworkTransformComponentBase::Reflect(context);
}
}
@@ -67,6 +67,8 @@ namespace Multiplayer
void ServerToClientConnectionData::Update(AZ::TimeMs serverGameTimeMs)
{
m_entityReplicationManager.ActivatePendingEntities();
if (CanSendUpdates())
{
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
@@ -1,90 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/ConnectionData/ServerToServerConnectionData.h>
namespace Multiplayer
{
AZ_CVAR(AZ::TimeMs, sv_DefaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
AZ_CVAR(AZ::TimeMs, sv_ServerToServerReconnectDelayMs, AZ::TimeMs{ 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Number of milliseconds for delaying reconnecting that is based on sv_ServerNonceTimeoutMs");
AZ_CVAR(uint32_t, sv_ServerMaxRemoteEntitiesPendingCreationCount, 512, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of entities that we have sent to the remote server, but have not had a confirmation back from the remote server");
ServerToServerConnectionData::ServerToServerConnectionData
(
AzNetworking::IConnection* connection,
AzNetworking::IConnectionListener& connectionListener,
const AzNetworking::IpAddress& serverAddress
)
: m_connection(connection)
, m_serverAddress(serverAddress)
, m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteServer)
, m_connectEvent([this]() { OnConnectTimeout(); }, AZ::Name("Server to server connection timeout event"))
{
m_entityReplicationManager.SetRemoteHostId(InvalidHostId);// serverAddrInfo.GetServerAddrInfo().GetShardId());
m_entityReplicationManager.SetEntityActivationTimeSliceMs(sv_DefaultNetworkEntityActivationTimeSliceMs);
m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(sv_ServerMaxRemoteEntitiesPendingCreationCount);
if (connection->GetConnectionRole() == AzNetworking::ConnectionRole::Connector)
{
m_connectEvent.Enqueue(sv_ServerToServerReconnectDelayMs);
}
}
ServerToServerConnectionData::~ServerToServerConnectionData()
{
m_entityReplicationManager.Clear(false);
}
ConnectionDataType ServerToServerConnectionData::GetConnectionDataType() const
{
return ConnectionDataType::ServerToServer;
}
AzNetworking::IConnection* ServerToServerConnectionData::GetConnection() const
{
return m_connection;
}
EntityReplicationManager& ServerToServerConnectionData::GetReplicationManager()
{
return m_entityReplicationManager;
}
void ServerToServerConnectionData::Update(AZ::TimeMs serverGameTimeMs)
{
if (IsReady())
{
m_entityReplicationManager.SendUpdates(serverGameTimeMs);
}
}
HostId ServerToServerConnectionData::GetHostId() const
{
return InvalidHostId; // GetServerToServerAddrInfo().GetServerAddrInfo().GetShardId();
}
void ServerToServerConnectionData::OnConnectTimeout()
{
AZ_Assert(m_connection->GetConnectionRole() == AzNetworking::ConnectionRole::Connector, "Timeout should only be queued for connectors");
if (m_connection->GetConnectionState() == AzNetworking::ConnectionState::Connecting)
{
//NovaGameHubServer::RequestNewNoncesToReconnect::Request request;
//request.SetReconnectingServerShardId(GetShardId());
//gNovaGame->GetNovaServiceAgent().DispatchRequest(request, 0, &gNovaGame->GetNovaServiceAgent());
//AZLOG(Debug_UdpServerConnect, "Sent RequestNewNoncesToReconnect shardId:%u", static_cast<uint32_t>(GetShardId()));
//
//// Requeue in case we need to request additional nonces
//m_ConnectTimedEvent.Enqueue(TimeMs(sv_ServerToServerReconnectDelayMs + sv_ServerNonceTimeoutMs));
}
}
}
@@ -1,68 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Source/ConnectionData/IConnectionData.h>
#include <Source/NetworkEntity/INetworkEntityManager.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
namespace Multiplayer
{
class ServerToServerConnectionData
: public IConnectionData
{
public:
//! Constructor
//! @param connection connection to other server
//! @param connectionListener the connection listener interface for handling packets
//! @param serverAddress the address for the remote server
ServerToServerConnectionData
(
AzNetworking::IConnection* connection,
AzNetworking::IConnectionListener& connectionListener,
const AzNetworking::IpAddress& serverAddress
);
~ServerToServerConnectionData() override;
//! IConnectionData interface
//! @{
ConnectionDataType GetConnectionDataType() const override;
AzNetworking::IConnection* GetConnection() const override;
EntityReplicationManager& GetReplicationManager() override;
void Update(AZ::TimeMs serverGameTimeMs) override;
//! @}
const AzNetworking::IpAddress& GetServerAddress() const;
bool IsReady();
void SetIsReady(bool isReady);
//! Get my server shard Id
//! @return return shard Id
HostId GetHostId() const;
private:
void OnConnectTimeout();
AzNetworking::IpAddress m_serverAddress;
AzNetworking::IConnection* m_connection = nullptr;
EntityReplicationManager m_entityReplicationManager;
AZ::ScheduledEvent m_connectEvent; //< Connection timeout handler
bool m_isReady = false;
AZ_DISABLE_COPY_MOVE(ServerToServerConnectionData);
};
}
#include <Source/ConnectionData/ServerToServerConnectionData.inl>
@@ -1,29 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
namespace Multiplayer
{
inline const AzNetworking::IpAddress& ServerToServerConnectionData::GetServerAddress() const
{
return m_serverAddress;
}
inline bool ServerToServerConnectionData::IsReady()
{
return m_isReady;
}
inline void ServerToServerConnectionData::SetIsReady(bool isReady)
{
m_isReady = isReady;
}
}
@@ -1,66 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/EntityDomains/GlobalEntityDomain.h>
namespace Multiplayer
{
GlobalEntityDomain::GlobalEntityDomain()
: m_controllersActivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersActivated(entityHandle, entityIsMigrating); })
, m_controllersDeactivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersDeactivated(entityHandle, entityIsMigrating); })
{
;
}
bool GlobalEntityDomain::IsInDomain([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const
{
//if (const GlobalAccessComponent* globalAccessComp = entityHandle->FindComponent<GlobalAccessComponent>())
//{
// return globalAccessComp->GetPropagationMode() == PropagationMode::GlobalResidency;
//}
return false;
}
void GlobalEntityDomain::ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet)
{
for (auto& entityHandle : ownedEntitySet)
{
OnControllersActivated(entityHandle, EntityIsMigrating::False);
}
GetNetworkEntityManager()->AddControllersActivatedHandler(m_controllersActivatedHandler);
GetNetworkEntityManager()->AddControllersDeactivatedHandler(m_controllersDeactivatedHandler);
}
void GlobalEntityDomain::RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const
{
outEntitiesNotInDomain.insert(m_entitiesNotInDomain.begin(), m_entitiesNotInDomain.end());
}
void GlobalEntityDomain::DebugDraw() const
{
// Nothing to draw
}
void GlobalEntityDomain::OnControllersActivated(const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
if (!IsInDomain(entityHandle))
{
m_entitiesNotInDomain.insert(entityHandle.GetNetEntityId());
}
}
void GlobalEntityDomain::OnControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
m_entitiesNotInDomain.erase(entityHandle.GetNetEntityId());
}
}
@@ -1,41 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Source/EntityDomains/IEntityDomain.h>
namespace Multiplayer
{
class GlobalEntityDomain
: public IEntityDomain
{
public:
GlobalEntityDomain();
//! IEntityDomain overrides.
//! @{
bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override;
void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) override;
void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const override;
void DebugDraw() const override;
//! @}
private:
void OnControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating);
void OnControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating);
EntitiesNotInDomain m_entitiesNotInDomain;
ControllersActivatedEvent::Handler m_controllersActivatedHandler;
ControllersDeactivatedEvent::Handler m_controllersDeactivatedHandler;
};
}
@@ -1,80 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/EntityDomains/RoundRobinEntityDomain.h>
namespace Multiplayer
{
RoundRobinEntityDomain::RoundRobinEntityDomain(const RoundRobinEntityDomain& rhs)
: m_hostId(rhs.m_hostId)
, m_multiserverCount(rhs.m_multiserverCount)
, m_entitiesNotInDomain(rhs.m_entitiesNotInDomain)
, m_controllersActivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersActivated(entityHandle, entityIsMigrating); })
, m_controllersDeactivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersDeactivated(entityHandle, entityIsMigrating); })
{
;
}
RoundRobinEntityDomain::RoundRobinEntityDomain(HostId hostId, uint32_t multiserverCount)
: m_hostId(hostId)
, m_multiserverCount(multiserverCount)
, m_controllersActivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersActivated(entityHandle, entityIsMigrating); })
, m_controllersDeactivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersDeactivated(entityHandle, entityIsMigrating); })
{
;
}
bool RoundRobinEntityDomain::IsInDomain([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const
{
//const int desiredDomain = (entityHandle.GetNetEntityId() % m_ServerShardCount) + k_FirstGameShardId;
//const bool netIdInDomain = desiredDomain == m_ServerShardId;
//const bool isPlayer = entityHandle->FindComponent<PlayerComponent>() != nullptr;
//const bool isGlobalEnt = entityHandle->FindComponent<GlobalAccessComponent>() != nullptr;
//auto entityHierarchyComponent = entityHandle->FindComponent<EntityHierarchyComponent>();
//const bool isParented = entityHierarchyComponent ? entityHierarchyComponent->IsParented() : false;
//return netIdInDomain || isPlayer || isGlobalEnt || isParented;
return false;
}
void RoundRobinEntityDomain::ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet)
{
for (auto& entityHandle : ownedEntitySet)
{
OnControllersActivated(entityHandle, EntityIsMigrating::False);
}
GetNetworkEntityManager()->AddControllersActivatedHandler(m_controllersActivatedHandler);
GetNetworkEntityManager()->AddControllersDeactivatedHandler(m_controllersDeactivatedHandler);
}
void RoundRobinEntityDomain::DebugDraw() const
{
// Nothing to draw
}
void RoundRobinEntityDomain::RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const
{
outEntitiesNotInDomain.insert(m_entitiesNotInDomain.begin(), m_entitiesNotInDomain.end());
}
void RoundRobinEntityDomain::OnControllersActivated(const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
if (!IsInDomain(entityHandle))
{
m_entitiesNotInDomain.insert(entityHandle.GetNetEntityId());
}
}
void RoundRobinEntityDomain::OnControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
m_entitiesNotInDomain.erase(entityHandle.GetNetEntityId());
}
}
@@ -1,48 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Source/EntityDomains/IEntityDomain.h>
#include <AzCore/std/containers/unordered_set.h>
namespace Multiplayer
{
class RoundRobinEntityDomain
: public IEntityDomain
{
public:
RoundRobinEntityDomain() = delete;
RoundRobinEntityDomain(const RoundRobinEntityDomain& rhs);
RoundRobinEntityDomain(HostId hostId, uint32_t multiserverCount);
//! IEntityDomain overrides.
//! @{
bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override;
void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) override;
void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const override;
void DebugDraw() const override;
//! @}
private:
void UpdateEntityDomain();
void OnControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating);
void OnControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating);
HostId m_hostId = InvalidHostId;
uint32_t m_multiserverCount = 0;
EntitiesNotInDomain m_entitiesNotInDomain;
ControllersActivatedEvent::Handler m_controllersActivatedHandler;
ControllersDeactivatedEvent::Handler m_controllersDeactivatedHandler;
};
}
@@ -1,150 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/EntityDomains/SpatialEntityDomain.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
namespace Multiplayer
{
AZ_CVAR(float, sv_SpatialEntityDomainWidth, 20.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "This is the area around the non-overlapping map region over which the server is willing to control entities. This makes it so that if an entity is walking across the MapRegion boundry back and forth, they won't ping pong between servers.");
SpatialEntityDomain::SpatialEntityDomain(const AZ::Aabb& aabb)
: m_aabb(aabb)
, m_controllersActivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersActivated(entityHandle, entityIsMigrating); })
, m_controllersDeactivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersDeactivated(entityHandle, entityIsMigrating); })
{
// Slightly expand our Aabb to avoid entities rapidly toggling back and forth between domains
m_aabb.Expand(AZ::Vector3(sv_SpatialEntityDomainWidth, sv_SpatialEntityDomainWidth, sv_SpatialEntityDomainWidth));
}
bool SpatialEntityDomain::IsInDomain(const ConstNetworkEntityHandle& entityHandle) const
{
if (const AZ::Entity* entity = entityHandle.GetEntity())
{
const AZ::Transform transform = entity->GetTransform()->GetWorldTM();
return IsTransformInDomain(transform);
}
return false;
}
void SpatialEntityDomain::ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet)
{
for (auto& entityHandle : ownedEntitySet)
{
OnControllersActivated(entityHandle, EntityIsMigrating::False);
}
GetNetworkEntityManager()->AddControllersActivatedHandler(m_controllersActivatedHandler);
GetNetworkEntityManager()->AddControllersDeactivatedHandler(m_controllersDeactivatedHandler);
}
void SpatialEntityDomain::RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const
{
// validate all our entities did not come back into the domain
for (ConstNetworkEntityHandle& entityHandle : m_dirtyEntities)
{
AZ::Entity* entity = entityHandle.GetEntity();
// If the entity no longer exists, we can safely skip it
if (entity == nullptr)
{
continue;
}
// Turn back on tracking, we need this if the entity is in or out of domain (since entities can walk back into our domain prior to migrating)
AZ::TransformInterface* transformInterface = entity->GetTransform();
auto locationDataIter = m_ownedEntities.find(entityHandle);
AZ_Assert(locationDataIter != m_ownedEntities.end(), "This should always exist");
transformInterface->BindTransformChangedEventHandler(locationDataIter->second.m_updateEventHandler);
if (!IsInDomain(entityHandle))
{
m_entitiesNotInDomain.insert(entityHandle.GetNetEntityId());
}
}
m_dirtyEntities.clear();
outEntitiesNotInDomain.insert(m_entitiesNotInDomain.begin(), m_entitiesNotInDomain.end());
}
void SpatialEntityDomain::DebugDraw() const
{
static constexpr float BoundaryStripeHeight = 1.0f;
static constexpr float BoundaryStripeSpacing = 0.5f;
static constexpr int32_t BoundaryStripeCount = 10;
//auto* loc = draw.GetOwnerConst()->FindComponent<LocationComponent::Server>();
//if (loc == nullptr)
//{
// return;
//}
//
//Vec3 dmnMin = m_SpatialEntityDomainParams.GetMin();
//Vec3 dmnMax = m_SpatialEntityDomainParams.GetMax();
//
//dmnMin.z = loc->GetPosition().z;
//dmnMax.z = dmnMin.z + BoundaryStripeHeight;
//
//for (int i = 0; i < BoundaryStripeCount; ++i)
//{
// draw.AABB(dmnMin, dmnMax, color);
// dmnMin.z += BoundaryStripeSpacing;
// dmnMax.z += BoundaryStripeSpacing;
//}
}
const AZ::Aabb& SpatialEntityDomain::GetAabb() const
{
return m_aabb;
}
bool SpatialEntityDomain::IsTransformInDomain(const AZ::Transform& transform) const
{
return m_aabb.Contains(transform.GetTranslation());
}
void SpatialEntityDomain::EntityTransformUpdated(const ConstNetworkEntityHandle& entityHandle)
{
m_dirtyEntities.push_back(entityHandle);
LocationData& locationData = m_ownedEntities[entityHandle];
// we marked this entity as dirty, we don't need to be attached to the movement event anymore
locationData.m_updateEventHandler.Disconnect();
}
void SpatialEntityDomain::OnControllersActivated(const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
const AZ::Entity* entity = entityHandle.GetEntity();
// If the entity no longer exists, we can safely skip it
if (entity != nullptr)
{
LocationData& locationData = m_ownedEntities[entityHandle];
locationData.m_parent = this;
locationData.m_entityHandle = entityHandle;
// Turn back on tracking, we need this if the entity is in or out of domain (since entities can walk back into our domain prior to migrating)
AZ::TransformInterface* transformInterface = entity->GetTransform();
transformInterface->BindTransformChangedEventHandler(locationData.m_updateEventHandler);
}
if (!IsInDomain(entityHandle))
{
m_entitiesNotInDomain.insert(entityHandle.GetNetEntityId());
}
}
void SpatialEntityDomain::OnControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
m_entitiesNotInDomain.erase(entityHandle.GetNetEntityId());
m_ownedEntities.erase(entityHandle);
}
}
@@ -1,78 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Source/EntityDomains/IEntityDomain.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Aabb.h>
namespace Multiplayer
{
class MapRegion;
struct SpatialEntityDomainParams;
class SpatialEntityDomain
: public IEntityDomain
{
public:
SpatialEntityDomain(const AZ::Aabb& aabb);
//! IEntityDomain overrides.
//! @{
bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override;
void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) override;
void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const override;
void DebugDraw() const override;
//! @}
const AZ::Aabb& GetAabb() const;
private:
bool IsTransformInDomain(const AZ::Transform& transform) const;
void EntityTransformUpdated(const ConstNetworkEntityHandle& entityHandle);
void OnControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating);
void OnControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating);
struct LocationData
{
LocationData() = default;
LocationData(LocationData&& rhs)
: m_parent(rhs.m_parent)
, m_entityHandle(rhs.m_entityHandle)
{
;
}
SpatialEntityDomain* m_parent = nullptr;
ConstNetworkEntityHandle m_entityHandle;
AZ::TransformChangedEvent::Handler m_updateEventHandler = AZ::TransformChangedEvent::Handler
(
[this]([[maybe_unused]] const AZ::Transform& localTransform, [[maybe_unused]] const AZ::Transform& worldTransform)
{
m_parent->EntityTransformUpdated(m_entityHandle);
}
);
};
AZ::Aabb m_aabb;
// cached data
mutable EntitiesNotInDomain m_entitiesNotInDomain;
mutable AZStd::vector<ConstNetworkEntityHandle> m_dirtyEntities;
mutable AZStd::unordered_map<ConstNetworkEntityHandle, LocationData> m_ownedEntities;
ControllersActivatedEvent::Handler m_controllersActivatedHandler;
ControllersDeactivatedEvent::Handler m_controllersDeactivatedHandler;
};
}
@@ -1,17 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
namespace Multiplayer
{
}
@@ -1,176 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/EntityDomains/SpatialMapPartitioner.h>
#include <AzCore/std/containers/fixed_vector.h>
namespace Multiplayer
{
static constexpr uint32_t MaxFactors = 32;
using FactorSet = AZStd::fixed_vector<uint32_t, MaxFactors>;
// Computes factors of N, but does not return the factors 1 and N
void ComputeFactors(uint32_t integer, FactorSet& output)
{
output.clear();
while (integer > 2)
{
for (uint32_t test = 2; test <= (integer / 2); ++test)
{
if ((integer % test) == 0)
{
output.push_back(test);
integer /= test;
break;
}
}
}
if (integer > 1)
{
output.push_back(integer);
}
}
ServerMapPartitioner::ServerMapPartitioner()
: m_regionCount(0)
, m_shardCount(0)
{
;
}
void ServerMapPartitioner::PartitionMap(uint32_t totalRegions, uint32_t shardCount)
{
AZ_Assert(totalRegions > 0, "Total number of regions for map partitioner must be positive");
// Factor totalRegions
FactorSet factors;
ComputeFactors(totalRegions, factors);
// Compute map extents
AZ::Vector3 mapMinBounds, mapMaxBounds;
//AZ::Interface<IPhysics>::Get()->GetWorldBounds(mapMinBounds, mapMaxBounds);
//m_wholeMap = AZ::Aabb::CreateFromMinMax(mapMinBounds, mapMaxBounds);
// This part could use some work
// Basically we want to now distribute the factors in some way related to the extents of the map
// The ideal result I guess being that the output regions are as close to square as possible?
// For now distribute the factors evenly, and then bias for the larger map extent..
const AZ::Vector3 delta = mapMaxBounds - mapMinBounds;
uint32_t divisions[2] = { 1, 1 };
uint32_t divIndex = 0;
for (uint32_t i = 0; i < factors.size(); ++i)
{
divisions[divIndex] *= factors[i];
divIndex = 1 - divIndex;
}
// Sort.. since I don't know which came out greater
if (divisions[1] > divisions[0])
{
uint32_t temp = divisions[1];
divisions[1] = divisions[0];
divisions[0] = temp;
}
const bool xGreater = (delta.GetX() >= delta.GetY());
const uint32_t xAxisDiv = xGreater ? divisions[0] : divisions[1];
const uint32_t yAxisDiv = xGreater ? divisions[1] : divisions[0];
const float mapWidth = mapMaxBounds.GetX() - mapMinBounds.GetX();
const float mapHeight = mapMaxBounds.GetY() - mapMinBounds.GetY();
const float partitionWidth = mapWidth / xAxisDiv;
const float partitionHeight = mapHeight / yAxisDiv;
const uint32_t regionCount = xAxisDiv * yAxisDiv;
AZ_Assert(regionCount == totalRegions, "Was not able to partition the map into the requested region count, invalid region count specified");
m_regions.resize(regionCount);
m_shardCount = shardCount;
m_regionCount = totalRegions;
AZ::Vector3 regionMinBounds;
AZ::Vector3 regionMaxBounds;
uint32_t regionIndex = 0;
regionMaxBounds.SetY(mapMinBounds.GetY());
for (int32_t y = yAxisDiv - 1; y >= 0; --y)
{
regionMinBounds.SetY(regionMaxBounds.GetY());
if (y > 0)
{
regionMaxBounds.SetY(regionMaxBounds.GetY() + partitionHeight);
}
else
{
regionMaxBounds.SetY(mapMaxBounds.GetY());
}
regionMaxBounds.SetX(mapMinBounds.GetX());
for (int32_t x = xAxisDiv - 1; x >= 0; --x)
{
regionMinBounds.SetX(regionMaxBounds.GetX());
if (x > 0)
{
regionMaxBounds.SetX(regionMaxBounds.GetX() + partitionWidth);
}
else
{
regionMaxBounds.SetX(mapMaxBounds.GetX());
}
m_regions[regionIndex++] = AZ::Aabb::CreateFromMinMax(regionMinBounds, regionMaxBounds);
}
}
}
uint32_t ServerMapPartitioner::GetRegionCount() const
{
return static_cast<uint32_t>(m_regions.size());
}
AZ::Aabb ServerMapPartitioner::GetMapRegion(uint32_t index) const
{
if ((index >= 0) && (index < GetRegionCount()))
{
return m_regions[index];
}
return AZ::Aabb();
}
AZ::Aabb ServerMapPartitioner::GetMapRegionForHost(HostId hostId)
{
// TODO: This is not the best place for this logic, though I'm not sure where is yet -- potentially this should be a parameter returned by the game service?
// If there are global shards, those will be the lowest index, so we need to offset correctly to find the correct region for an EntityManagerId()
int offset = m_shardCount - m_regionCount;
int32_t index = aznumeric_cast<int32_t>(hostId) - 1 - offset;
AZ_Assert(index >= 0 && index < GetRegionCount(), "No region for Entity Manager");
return GetMapRegion(index);
}
const AZ::Aabb& ServerMapPartitioner::GetWholeMap() const
{
return m_wholeMap;
}
}
@@ -1,38 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Aabb.h>
#include <Source/MultiplayerTypes.h>
namespace Multiplayer
{
class ServerMapPartitioner
{
public:
ServerMapPartitioner();
void PartitionMap(uint32_t regionCount, uint32_t shardCount);
uint32_t GetRegionCount() const;
AZ::Aabb GetMapRegion(uint32_t index) const;
AZ::Aabb GetMapRegionForHost(HostId hostId);
const AZ::Aabb& GetWholeMap() const;
private:
AZ::Aabb m_wholeMap;
AZStd::vector<AZ::Aabb> m_regions;
uint32_t m_regionCount;
uint32_t m_shardCount;
};
}
@@ -13,6 +13,8 @@
#include <Source/Multiplayer_precompiled.h>
#include <Source/MultiplayerGem.h>
#include <Source/MultiplayerSystemComponent.h>
#include <Source/Components/NetBindComponent.h>
#include <Source/AutoGen/AutoComponentTypes.h>
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
namespace Multiplayer
@@ -23,7 +25,10 @@ namespace Multiplayer
m_descriptors.insert(m_descriptors.end(), {
AzNetworking::NetworkingSystemComponent::CreateDescriptor(),
MultiplayerSystemComponent::CreateDescriptor(),
NetBindComponent::CreateDescriptor(),
});
CreateComponentDescriptors(m_descriptors);
}
AZ::ComponentTypeList MultiplayerModule::GetRequiredSystemComponents() const
@@ -36,4 +41,4 @@ namespace Multiplayer
}
}
AZ_DECLARE_MODULE_CLASS(Gem_Multiplayer2, Multiplayer::MultiplayerModule);
AZ_DECLARE_MODULE_CLASS(Gem_Multiplayer, Multiplayer::MultiplayerModule);
@@ -11,11 +11,16 @@
*/
#include <Source/MultiplayerSystemComponent.h>
#include <Source/Components/MultiplayerComponent.h>
#include <Source/AutoGen/AutoComponentTypes.h>
#include <Source/ConnectionData/ServerToClientConnectionData.h>
#include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
#include <Source/EntityDomains/FullOwnershipEntityDomain.h>
#include <AzNetworking/Framework/INetworking.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzNetworking/Framework/INetworking.h>
namespace AZ::ConsoleTypeHelpers
{
@@ -69,6 +74,8 @@ namespace Multiplayer
serializeContext->Class<MultiplayerSystemComponent, AZ::Component>()
->Version(1);
}
MultiplayerComponent::Reflect(context);
}
void MultiplayerSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
@@ -103,16 +110,39 @@ namespace Multiplayer
AZ::TickBus::Handler::BusConnect();
m_networkInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(AZ::Name(s_networkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this);
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
AZ::Interface<IMultiplayer>::Register(this);
}
void MultiplayerSystemComponent::Deactivate()
{
AZ::Interface<IMultiplayer>::Unregister(this);
AZ::TickBus::Handler::BusDisconnect();
}
void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
void MultiplayerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
AZ::TimeMs elapsedMs = aznumeric_cast<AZ::TimeMs>(aznumeric_cast<int64_t>(deltaTime / 1000.0f));
AZ::TimeMs serverGameTimeMs = AZ::GetElapsedTimeMs();
// Handle deferred local rpc messages that were generated during the updates
m_networkEntityManager.DispatchLocalDeferredRpcMessages();
m_networkEntityManager.NotifyEntitiesChanged();
// Let the network system know the frame is done and we can collect dirty bits
m_networkEntityManager.NotifyEntitiesDirtied();
MultiplayerStats& stats = GetStats();
stats.m_entityCount = GetNetworkEntityManager()->GetEntityCount();
auto sendNetworkUpdates = [serverGameTimeMs](IConnection& connection)
{
if (connection.GetUserData() != nullptr)
{
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection.GetUserData());
connectionData->Update(serverGameTimeMs);
}
};
// Send out the game state update to all connections
m_networkInterface->GetConnectionSet().VisitConnections(sendNetworkUpdates);
MultiplayerPackets::SyncConsole packet;
AZ::ThreadSafeDeque<AZStd::string>::DequeType cvarUpdates;
@@ -190,9 +220,9 @@ namespace Multiplayer
bool MultiplayerSystemComponent::HandleRequest
(
IConnection* connection,
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
[[maybe_unused]] const MultiplayerPackets::Connect& packet
[[maybe_unused]] MultiplayerPackets::Connect& packet
)
{
if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map)))
@@ -207,9 +237,9 @@ namespace Multiplayer
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] IConnection* connection,
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
[[maybe_unused]] const MultiplayerPackets::Accept& packet
[[maybe_unused]] MultiplayerPackets::Accept& packet
)
{
AZ::CVarFixedString commandString = "sv_map " + packet.GetMap();
@@ -226,9 +256,9 @@ namespace Multiplayer
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] IConnection* connection,
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
const MultiplayerPackets::SyncConsole& packet
[[maybe_unused]] MultiplayerPackets::SyncConsole& packet
)
{
ExecuteConsoleCommandList(connection, packet.GetCommandSet());
@@ -237,9 +267,9 @@ namespace Multiplayer
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] IConnection* connection,
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
const MultiplayerPackets::ConsoleCommand& packet
[[maybe_unused]] MultiplayerPackets::ConsoleCommand& packet
)
{
const bool isAcceptor = (connection->GetConnectionRole() == ConnectionRole::Acceptor); // We're hosting if we accepted the connection
@@ -250,9 +280,9 @@ namespace Multiplayer
bool MultiplayerSystemComponent::HandleRequest
(
IConnection* connection,
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
const MultiplayerPackets::SyncConnectionCvars& packet
[[maybe_unused]] MultiplayerPackets::SyncConnectionCvars& packet
)
{
connection->SetConnectionQuality(ConnectionQuality(packet.GetLossPercent(), packet.GetLatencyMs(), packet.GetVarianceMs()));
@@ -261,29 +291,59 @@ namespace Multiplayer
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] IConnection* connection,
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
[[maybe_unused]] const MultiplayerPackets::EntityUpdates& packet
[[maybe_unused]] MultiplayerPackets::EntityUpdates& packet
)
{
return false;
bool handledAll = true;
if (connection->GetUserData() == nullptr)
{
AZLOG_WARN("Missing connection data, likely due to a connection in the process of closing, entity updates size %u", aznumeric_cast<uint32_t>(packet.GetEntityMessages().size()));
return handledAll;
}
EntityReplicationManager& replicationManager = reinterpret_cast<IConnectionData*>(connection->GetUserData())->GetReplicationManager();
// Ignore a_Request.GetServerGameTimePoint(), clients can't affect the server gametime
for (AZStd::size_t i = 0; i < packet.GetEntityMessages().size(); ++i)
{
const NetworkEntityUpdateMessage& updateMessage = packet.GetEntityMessages()[i];
handledAll &= replicationManager.HandleEntityUpdateMessage(connection, packetHeader, updateMessage);
AZ_Assert(handledAll, "GameServerToClientNetworkRequestHandler EntityUpdates Did not handle all updates");
}
return handledAll;
}
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] IConnection* connection,
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
[[maybe_unused]] const MultiplayerPackets::EntityRpcs& packet
[[maybe_unused]] MultiplayerPackets::EntityRpcs& packet
)
{
return false;
bool handledAll = true;
if (connection->GetUserData() == nullptr)
{
AZLOG_WARN("Missing connection data, likely due to a connection in the process of closing, entity updates size %u", aznumeric_cast<uint32_t>(packet.GetEntityRpcs().size()));
return handledAll;
}
EntityReplicationManager& replicationManager = reinterpret_cast<IConnectionData*>(connection->GetUserData())->GetReplicationManager();
for (AZStd::size_t i = 0; i < packet.GetEntityRpcs().size(); ++i)
{
handledAll &= replicationManager.HandleEntityRpcMessage(connection, packet.ModifyEntityRpcs()[i]);
}
return handledAll;
}
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] IConnection* connection,
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
[[maybe_unused]] const MultiplayerPackets::ClientMigration& packet
[[maybe_unused]] MultiplayerPackets::ClientMigration& packet
)
{
return false;
@@ -293,7 +353,7 @@ namespace Multiplayer
(
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader,
[[maybe_unused]] const MultiplayerPackets::NotifyClientMigration& packet
[[maybe_unused]] MultiplayerPackets::NotifyClientMigration& packet
)
{
return false;
@@ -303,7 +363,7 @@ namespace Multiplayer
(
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader,
[[maybe_unused]] const MultiplayerPackets::EntityMigration& packet
[[maybe_unused]] MultiplayerPackets::EntityMigration& packet
)
{
return false;
@@ -319,7 +379,7 @@ namespace Multiplayer
return ConnectResult::Accepted;
}
void MultiplayerSystemComponent::OnConnect(IConnection* connection)
void MultiplayerSystemComponent::OnConnect(AzNetworking::IConnection* connection)
{
if (connection->GetConnectionRole() == ConnectionRole::Connector)
{
@@ -335,9 +395,24 @@ namespace Multiplayer
datum.m_agentType = MultiplayerAgentType::Client;
m_connAcquiredEvent.Signal(datum);
}
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
{
// TODO: This needs to be set to the players autonomous proxy ------------v
NetworkEntityHandle controlledEntity = GetNetworkEntityTracker()->Get(NetEntityId{ 0 });
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
}
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
}
}
bool MultiplayerSystemComponent::OnPacketReceived(IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer)
bool MultiplayerSystemComponent::OnPacketReceived(AzNetworking::IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer)
{
return MultiplayerPackets::DispatchPacket(connection, packetHeader, serializer, *this);
}
@@ -347,7 +422,7 @@ namespace Multiplayer
;
}
void MultiplayerSystemComponent::OnDisconnect(IConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint)
void MultiplayerSystemComponent::OnDisconnect(AzNetworking::IConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint)
{
const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remote host disconnected";
AZStd::string reasonString = ToString(reason);
@@ -358,6 +433,14 @@ namespace Multiplayer
{
m_shutdownEvent.Signal(m_networkInterface);
}
// Clean up any multiplayer connection data we've bound to this connection instance
if (connection->GetUserData() != nullptr)
{
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
delete connectionData;
connection->SetUserData(nullptr);
}
}
MultiplayerAgentType MultiplayerSystemComponent::GetAgentType()
@@ -372,6 +455,11 @@ namespace Multiplayer
if (multiplayerType == MultiplayerAgentType::ClientServer || multiplayerType == MultiplayerAgentType::DedicatedServer)
{
m_initEvent.Signal(m_networkInterface);
const AZ::Aabb worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-16384.0f), AZ::Vector3(16384.0f));
//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));
}
}
m_agentType = multiplayerType;
@@ -392,6 +480,23 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent);
}
void MultiplayerSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
const MultiplayerStats& stats = GetStats();
AZLOG_INFO("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
AZLOG_INFO("Total client connections: %llu", aznumeric_cast<AZ::u64>(stats.m_clientConnectionCount));
AZLOG_INFO("Total server connections: %llu", aznumeric_cast<AZ::u64>(stats.m_serverConnectionCount));
AZLOG_INFO("Total property updates sent: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesSent));
AZLOG_INFO("Total property updates sent bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesSentBytes));
AZLOG_INFO("Total property updates received: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesRecv));
AZLOG_INFO("Total property updates received bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesRecvBytes));
AZLOG_INFO("Total RPCs sent: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsSent));
AZLOG_INFO("Total RPCs sent bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsSentBytes));
AZLOG_INFO("Total RPCs received: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsRecv));
AZLOG_INFO("Total RPCs received bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsRecvBytes));
}
void MultiplayerSystemComponent::OnConsoleCommandInvoked
(
AZStd::string_view command,
@@ -22,6 +22,7 @@
#include <Include/IMultiplayer.h>
#include <Source/NetworkTime/NetworkTime.h>
#include <Source/AutoGen/Multiplayer.AutoPacketDispatcher.h>
#include <Source/NetworkEntity/NetworkEntityManager.h>
namespace AzNetworking
{
@@ -60,16 +61,16 @@ namespace Multiplayer
int GetTickOrder() override;
//! @}
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::Connect& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::Accept& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::SyncConsole& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::ConsoleCommand& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::SyncConnectionCvars& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::EntityUpdates& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::EntityRpcs& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::ClientMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::NotifyClientMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const MultiplayerPackets::EntityMigration& packet);
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::SyncConsole& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ConsoleCommand& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::SyncConnectionCvars& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityUpdates& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityRpcs& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ClientMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::NotifyClientMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityMigration& packet);
//! IConnectionListener interface
//! @{
@@ -88,15 +89,24 @@ namespace Multiplayer
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
//! @}
//! Console commands.
//! @{
void DumpStats(const AZ::ConsoleCommandContainer& arguments);
//! @}
private:
void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom);
void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands);
AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session");
AzNetworking::INetworkInterface* m_networkInterface = nullptr;
AZ::ConsoleCommandInvokedEvent::Handler m_consoleCommandHandler;
AZ::ThreadSafeDeque<AZStd::string> m_cvarCommands;
NetworkEntityManager m_networkEntityManager;
NetworkTime m_networkTime;
MultiplayerAgentType m_agentType = MultiplayerAgentType::Uninitialized;
@@ -18,9 +18,9 @@
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/Components/NetBindComponent.h>
#include <Source/Components/NetworkTransformComponent.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
//#include "Generated/NovaGameCommon/Component/Multiplayer/LocationComponentCommon.AutoComponent.h"
#include <Include/IMultiplayer.h>
#include <AzNetworking/PacketLayer/IPacket.h>
#include <AzNetworking/Serialization/ISerializer.h>
@@ -447,7 +447,11 @@ namespace Multiplayer
void EntityReplicator::DeferRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
{
//Multiplayer::GetPacketHandlerMetricsInstance().LogSentRpc(entityRpcMessage.GetEntityComponentType(), entityRpcMessage.GetRpcMessageType(), entityRpcMessage.GetEstimatedSerializeSize());
// Received rpc metrics, log rpc sent, number of bytes, and the componentId/rpcId for bandwidth metrics
MultiplayerStats& stats = AZ::Interface<IMultiplayer>::Get()->GetStats();
stats.m_rpcsSent++;
stats.m_rpcsSentBytes += entityRpcMessage.GetEstimatedSerializeSize();
m_replicationManager.AddDeferredRpcMessage(entityRpcMessage);
}
@@ -627,8 +631,10 @@ namespace Multiplayer
bool EntityReplicator::HandleRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
{
// Received rpc metrics
//ScopedTimer processTimer(Multiplayer::GetPacketHandlerMetricsInstance().LogReceivedRpc(entityRpcMessage.GetEntityComponentType(), entityRpcMessage.GetRpcMessageType(), entityRpcMessage.GetEstimatedSerializeSize()));
// Received rpc metrics, log rpc received, time spent, number of bytes, and the componentId/rpcId for bandwidth metrics
MultiplayerStats& stats = AZ::Interface<IMultiplayer>::Get()->GetStats();
stats.m_rpcsRecv++;
stats.m_rpcsRecvBytes += entityRpcMessage.GetEstimatedSerializeSize();
if (!m_netBindComponent)
{
@@ -16,12 +16,14 @@ namespace Multiplayer
{
ReplicationRecordStats::ReplicationRecordStats
(
uint32_t authorityToAuthorityCount,
uint32_t authorityToClientCount,
uint32_t authorityToServerCount,
uint32_t authorityToAutonomousCount,
uint32_t autonomousToAuthorityCount
)
: m_authorityToClientCount(authorityToClientCount)
: m_authorityToAuthorityCount(authorityToAuthorityCount)
, m_authorityToClientCount(authorityToClientCount)
, m_authorityToServerCount(authorityToServerCount)
, m_authorityToAutonomousCount(authorityToAutonomousCount)
, m_autonomousToAuthorityCount(autonomousToAuthorityCount)
@@ -31,7 +33,8 @@ namespace Multiplayer
bool ReplicationRecordStats::operator ==(const ReplicationRecordStats& rhs) const
{
return (m_authorityToClientCount == rhs.m_authorityToClientCount)
return (m_authorityToAuthorityCount == rhs.m_authorityToAuthorityCount)
&& (m_authorityToClientCount == rhs.m_authorityToClientCount)
&& (m_authorityToServerCount == rhs.m_authorityToServerCount)
&& (m_authorityToAutonomousCount == rhs.m_authorityToAutonomousCount)
&& (m_autonomousToAuthorityCount == rhs.m_autonomousToAuthorityCount);
@@ -41,6 +44,7 @@ namespace Multiplayer
{
return ReplicationRecordStats
{
(m_authorityToAuthorityCount - rhs.m_authorityToAuthorityCount),
(m_authorityToClientCount - rhs.m_authorityToClientCount),
(m_authorityToServerCount - rhs.m_authorityToServerCount),
(m_authorityToAutonomousCount - rhs.m_authorityToAutonomousCount),
@@ -67,6 +71,7 @@ namespace Multiplayer
bool ReplicationRecord::AreAllBitsConsumed() const
{
bool ret = true;
ret &= m_authorityToAuthorityConsumedBits == m_authorityToAuthority.GetSize();
ret &= m_authorityToClientConsumedBits == m_authorityToClient.GetSize();
ret &= m_authorityToServerConsumedBits == m_authorityToServer.GetSize();
ret &= m_authorityToAutonomousConsumedBits == m_authorityToAutonomous.GetSize();
@@ -76,6 +81,7 @@ namespace Multiplayer
void ReplicationRecord::ResetConsumedBits()
{
m_authorityToAuthorityConsumedBits = 0;
m_authorityToClientConsumedBits = 0;
m_authorityToServerConsumedBits = 0;
m_authorityToAutonomousConsumedBits = 0;
@@ -86,7 +92,11 @@ namespace Multiplayer
{
ResetConsumedBits();
uint32_t recordSize = m_authorityToClient.GetSize();
uint32_t recordSize = m_authorityToAuthority.GetSize();
m_authorityToAuthority.Clear();
m_authorityToAuthority.Resize(recordSize);
recordSize = m_authorityToClient.GetSize();
m_authorityToClient.Clear();
m_authorityToClient.Resize(recordSize);
@@ -105,6 +115,7 @@ namespace Multiplayer
void ReplicationRecord::Append(const ReplicationRecord &rhs)
{
m_authorityToAuthority |= rhs.m_authorityToAuthority;
m_authorityToClient |= rhs.m_authorityToClient;
m_authorityToServer |= rhs.m_authorityToServer;
m_authorityToAutonomous |= rhs.m_authorityToAutonomous;
@@ -113,6 +124,7 @@ namespace Multiplayer
void ReplicationRecord::Subtract(const ReplicationRecord &rhs)
{
m_authorityToAuthority.Subtract(rhs.m_authorityToAuthority);
m_authorityToClient.Subtract(rhs.m_authorityToClient);
m_authorityToServer.Subtract(rhs.m_authorityToServer);
m_authorityToAutonomous.Subtract(rhs.m_authorityToAutonomous);
@@ -122,6 +134,10 @@ namespace Multiplayer
bool ReplicationRecord::HasChanges() const
{
bool hasChanges(false);
if (ContainsAuthorityToAuthorityBits())
{
hasChanges = hasChanges ? hasChanges : m_authorityToAuthority.AnySet();
}
if (ContainsAuthorityToClientBits())
{
hasChanges = hasChanges ? hasChanges : m_authorityToClient.AnySet();
@@ -143,25 +159,37 @@ namespace Multiplayer
bool ReplicationRecord::Serialize(AzNetworking::ISerializer& serializer)
{
if (ContainsAuthorityToAuthorityBits())
{
serializer.Serialize(m_authorityToAuthority, "AuthorityToAuthorityRecord");
}
if (ContainsAuthorityToClientBits())
{
serializer.Serialize(m_authorityToClient, "ServerToClientsRecord");
serializer.Serialize(m_authorityToClient, "AuthorityToClientRecord");
}
if (ContainsAuthorityToServerBits())
{
serializer.Serialize(m_authorityToServer, "ServerToServersRecord");
serializer.Serialize(m_authorityToServer, "AuthorityToServerRecord");
}
if (ContainsAuthorityToAutonomousBits())
{
serializer.Serialize(m_authorityToAutonomous, "ServerToAutonomousRecord");
serializer.Serialize(m_authorityToAutonomous, "AuthorityToAutonomousRecord");
}
if (ContainsAutonomousToAuthorityBits())
{
serializer.Serialize(m_autonomousToAuthority, "ClientToServersRecord");
serializer.Serialize(m_autonomousToAuthority, "AutonomousToAuthorityRecord");
}
return serializer.IsValid();
}
void ReplicationRecord::ConsumeAuthorityToAuthorityBits(uint32_t consumedBits)
{
if (ContainsAuthorityToAuthorityBits())
{
m_authorityToAuthorityConsumedBits += consumedBits;
}
}
void ReplicationRecord::ConsumeAuthorityToClientBits(uint32_t consumedBits)
{
if (ContainsAuthorityToClientBits())
@@ -194,6 +222,12 @@ namespace Multiplayer
}
}
bool ReplicationRecord::ContainsAuthorityToAuthorityBits() const
{
return (m_netEntityRole == NetEntityRole::Authority)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
}
bool ReplicationRecord::ContainsAuthorityToClientBits() const
{
return (m_netEntityRole != NetEntityRole::Authority)
@@ -218,6 +252,11 @@ namespace Multiplayer
|| (m_netEntityRole == NetEntityRole::InvalidRole);
}
uint32_t ReplicationRecord::GetRemainingAuthorityToAuthorityBits() const
{
return m_authorityToAuthorityConsumedBits < m_authorityToAuthority.GetValidBitCount() ? m_authorityToAuthority.GetValidBitCount() - m_authorityToAuthorityConsumedBits : 0;
}
uint32_t ReplicationRecord::GetRemainingAuthorityToClientBits() const
{
return m_authorityToClientConsumedBits < m_authorityToClient.GetValidBitCount() ? m_authorityToClient.GetValidBitCount() - m_authorityToClientConsumedBits : 0;
@@ -242,6 +281,7 @@ namespace Multiplayer
{
return ReplicationRecordStats
{
m_authorityToAuthorityConsumedBits,
m_authorityToClientConsumedBits,
m_authorityToServerConsumedBits,
m_authorityToAutonomousConsumedBits,
@@ -24,12 +24,14 @@ namespace Multiplayer
ReplicationRecordStats() = default;
ReplicationRecordStats
(
uint32_t authorityToAuthorityCount,
uint32_t authorityToClientCount,
uint32_t authorityToServerCount,
uint32_t authorityToAutonomousCount,
uint32_t autonomousToAuthorityCount
);
uint32_t m_authorityToAuthorityCount = 0;
uint32_t m_authorityToClientCount = 0;
uint32_t m_authorityToServerCount = 0;
uint32_t m_authorityToAutonomousCount = 0;
@@ -61,16 +63,19 @@ namespace Multiplayer
bool Serialize(AzNetworking::ISerializer& serializer);
void ConsumeAuthorityToAuthorityBits(uint32_t consumedBits);
void ConsumeAuthorityToClientBits(uint32_t consumedBits);
void ConsumeAuthorityToServerBits(uint32_t consumedBits);
void ConsumeAuthorityToAutonomousBits(uint32_t consumedBits);
void ConsumeAutonomousToAuthorityBits(uint32_t consumedBits);
bool ContainsAuthorityToAuthorityBits() const;
bool ContainsAuthorityToClientBits() const;
bool ContainsAuthorityToServerBits() const;
bool ContainsAuthorityToAutonomousBits() const;
bool ContainsAutonomousToAuthorityBits() const;
uint32_t GetRemainingAuthorityToAuthorityBits() const;
uint32_t GetRemainingAuthorityToClientBits() const;
uint32_t GetRemainingAuthorityToServerBits() const;
uint32_t GetRemainingAuthorityToAutonomousBits() const;
@@ -79,11 +84,13 @@ namespace Multiplayer
ReplicationRecordStats GetStats() const;
using RecordBitset = AzNetworking::FixedSizeVectorBitset<MaxRecordBits>;
RecordBitset m_authorityToAuthority;
RecordBitset m_authorityToClient;
RecordBitset m_authorityToServer;
RecordBitset m_authorityToAutonomous;
RecordBitset m_autonomousToAuthority;
uint32_t m_authorityToAuthorityConsumedBits = 0;
uint32_t m_authorityToClientConsumedBits = 0;
uint32_t m_authorityToServerConsumedBits = 0;
uint32_t m_authorityToAutonomousConsumedBits = 0;
@@ -24,10 +24,14 @@
namespace Multiplayer
{
AZ_CVAR(bool, net_DebugCheckNetworkEntityManager, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables extra debug checks inside the NetworkEntityManager");
AZ_CVAR(AZ::TimeMs, net_EntityDomainUpdateMs, AZ::TimeMs{ 500 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Frequency for updating the entity domain in ms");
NetworkEntityManager::NetworkEntityManager()
: m_networkEntityAuthorityTracker(*this)
, m_removeEntitiesEvent([this] { RemoveEntities(); }, AZ::Name("NetworkEntityManager remove entities event"))
, m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event"))
, m_entityAddedEventHandler([this](AZ::Entity* entity) { OnEntityAdded(entity); })
, m_entityRemovedEventHandler([this](AZ::Entity* entity) { OnEntityRemoved(entity); })
{
AZ::Interface<INetworkEntityManager>::Register(this);
}
@@ -37,6 +41,20 @@ namespace Multiplayer
AZ::Interface<INetworkEntityManager>::Unregister(this);
}
void NetworkEntityManager::Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
{
if (AZ::Interface<AZ::ComponentApplicationRequests>::Get() != nullptr)
{
// Null guard needed for unit tests
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityAddedEventHandler(m_entityAddedEventHandler);
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityRemovedEventHandler(m_entityRemovedEventHandler);
}
m_hostId = hostId;
m_entityDomain = AZStd::move(entityDomain);
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
}
NetworkEntityTracker* NetworkEntityManager::GetNetworkEntityTracker()
{
return &m_networkEntityTracker;
@@ -74,10 +92,9 @@ namespace Multiplayer
{
if (net_DebugCheckNetworkEntityManager)
{
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
AZ_Assert(netBindComponent, "No NetBindComponent found on networked entity");
const bool isClientOnlyEntity = false;// (ServerIdFromEntityId(it->first) == InvalidHostId);
AZ_Assert(netBindComponent->IsAuthority() || isClientOnlyEntity, "Trying to delete a proxy entity, this will lead to issues deserializing entity updates");
AZ_Assert(entityHandle.GetNetBindComponent(), "No NetBindComponent found on networked entity");
[[maybe_unused]] const bool isClientOnlyEntity = false;// (ServerIdFromEntityId(it->first) == InvalidHostId);
AZ_Assert(entityHandle.GetNetBindComponent()->IsAuthority() || 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 });
@@ -185,6 +202,100 @@ namespace Multiplayer
m_localDeferredRpcMessages.emplace_back(AZStd::move(message));
}
void NetworkEntityManager::DispatchLocalDeferredRpcMessages()
{
for (NetworkEntityRpcMessage& rpcMessage : m_localDeferredRpcMessages)
{
AZ::Entity* entity = m_networkEntityTracker.GetRaw(rpcMessage.GetEntityId());
if (entity != nullptr)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
AZ_Assert(netBindComponent != nullptr, "Attempting to send an RPC to an entity with no NetBindComponent");
netBindComponent->HandleRpcMessage(NetEntityRole::Server, rpcMessage);
}
}
m_localDeferredRpcMessages.clear();
}
void NetworkEntityManager::UpdateEntityDomain()
{
if (m_entityDomain == nullptr)
{
return;
}
m_entitiesNotInDomain.clear();
m_entityDomain->RetrieveEntitiesNotInDomain(m_entitiesNotInDomain);
for (NetEntityId exitingId : m_entitiesNotInDomain)
{
OnEntityExitDomain(exitingId);
}
}
void NetworkEntityManager::OnEntityExitDomain(NetEntityId entityId)
{
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)
//{
// if (hierarchyController->GetParentRelatedEntity())
// {
// safeToExit = false;
// }
//}
// Validate that we aren't already planning to remove this entity
if (safeToExit)
{
for (auto entityId : m_removeList)
{
if (entityId == entityId)
{
safeToExit = false;
}
}
}
if (safeToExit)
{
m_entityExitDomainEvent.Signal(entityHandle);
}
}
void NetworkEntityManager::OnEntityAdded(AZ::Entity* entity)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
{
const NetEntityId netEntityId = m_nextEntityId++;
netBindComponent->PreInit(entity, PrefabEntityId(), netEntityId, NetEntityRole::Authority);
}
}
void NetworkEntityManager::OnEntityRemoved(AZ::Entity* entity)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
{
MarkForRemoval(netBindComponent->GetEntityHandle());
}
}
void NetworkEntityManager::RemoveEntities()
{
//RewindableObjectState::ClearRewoundEntities();
@@ -213,10 +324,10 @@ namespace Multiplayer
// Delete Entity, method depends on how it was loaded
// Try slice removal first, then force delete
AZ::Entity* rawEntity = removeEntity.GetEntity();
//AZ::Entity* rawEntity = removeEntity.GetEntity();
//if (!rootSlice->RemoveEntity(rawEntity))
//{
delete rawEntity;
// delete rawEntity;
//}
}
@@ -13,10 +13,12 @@
#pragma once
#include <AzCore/EBus/ScheduledEvent.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <Source/NetworkEntity/INetworkEntityManager.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/EntityDomains/IEntityDomain.h>
namespace Multiplayer
{
@@ -29,6 +31,9 @@ namespace Multiplayer
NetworkEntityManager();
~NetworkEntityManager();
//! Only invoked for authoritative hosts
void Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain);
//! INetworkEntityManager overrides.
//! @{
NetworkEntityTracker* GetNetworkEntityTracker() override;
@@ -53,8 +58,14 @@ namespace Multiplayer
void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) override;
//! @}
void DispatchLocalDeferredRpcMessages();
void UpdateEntityDomain();
void OnEntityExitDomain(NetEntityId entityId);
private:
void OnEntityAdded(AZ::Entity* entity);
void OnEntityRemoved(AZ::Entity* entity);
void RemoveEntities();
NetworkEntityTracker m_networkEntityTracker;
@@ -63,14 +74,22 @@ namespace Multiplayer
AZStd::vector<NetEntityId> m_removeList;
AZStd::vector<AZ::Entity*> m_nonNetworkedEntities; // Contains entities that we've instantiated, but are not networked entities
AZStd::unique_ptr<IEntityDomain> m_entityDomain;
AZ::ScheduledEvent m_updateEntityDomainEvent;
IEntityDomain::EntitiesNotInDomain m_entitiesNotInDomain;
OwnedEntitySet m_ownedEntities;
EntityExitDomainEvent m_entityExitDomainEvent;
AZ::Event<> m_onEntityMarkedDirty;
AZ::Event<> m_onEntityNotifyChanges;
ControllersActivatedEvent m_controllersActivatedEvent;
ControllersDeactivatedEvent m_controllersDeactivatedEvent;
AZ::EntityAddedEvent::Handler m_entityAddedEventHandler;
AZ::EntityRemovedEvent::Handler m_entityRemovedEventHandler;
HostId m_hostId = InvalidHostId;
int32_t m_nextEntityIndex = 0;
NetEntityId m_nextEntityId = NetEntityId{ 0 };
// Local RPCs are buffered and dispatched at the end of the frame rather than processed immediately
// This is done to prevent local and network sent RPC's from having different dispatch behaviours
@@ -18,7 +18,7 @@
namespace Multiplayer
{
//! @class InputCommandArray
//! @class NetworkInputVector
//! @brief An array of network inputs. Used to mitigate loss of input packets on the server. Compresses subsequent elements.
class NetworkInputVector final
{
@@ -103,10 +103,10 @@ namespace Multiplayer
//! Helper method to compute clamped array index values accounting for the offset head index.
AZStd::size_t GetOffsetIndex(AZStd::size_t absoluteIndex) const;
mutable AzNetworking::ConnectionId m_owningConnectionId = AzNetworking::InvalidConnectionId;
AZStd::array<BASE_TYPE, REWIND_SIZE> m_history;
AzNetworking::ConnectionId m_owningConnectionId = AzNetworking::InvalidConnectionId;
ApplicationFrameId m_headTime = ApplicationFrameId{0};
uint32_t m_headIndex = 0;
AZStd::array<BASE_TYPE, REWIND_SIZE> m_history;
};
}
@@ -138,7 +138,7 @@ namespace Multiplayer
gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size());
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
{
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_NetEntity)
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
{
gatheredEntries.push_back(visEntry);
}
@@ -149,6 +149,8 @@ namespace Multiplayer
// Add all the neighbors
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
{
// TODO: Discard entities that don't have a NetBindComponent
//if (mp_ControlledFilteredEntityComponent && mp_ControlledFilteredEntityComponent->IsEntityFiltered(iterator.Get()))
//{
// continue;
@@ -1,141 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/ReplicationWindows/ServerToServerReplicationWindow.h>
#include <Source/Components/NetBindComponent.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Console/ILogger.h>
namespace Multiplayer
{
AZ_CVAR(float, sv_ReplicationWindowWidth, 100.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "This is the additional area around the non-overlapping map region over which the server should replicate entities");
AZ_CVAR(AZ::TimeMs, sv_ServerReplicationWindowUpdateMs, AZ::TimeMs{ 300 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Rate for replication window updates.");
ServerToServerReplicationWindow::ServerToServerReplicationWindow(const AZ::Aabb& aabb)
: m_aabb(aabb)
, m_updateWindowEvent([this]() { UpdateWindow(); }, AZ::Name("Server to server replication window update event"))
, m_controllersActivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersActivated(entityHandle, entityIsMigrating); })
, m_controllersDeactivatedHandler([this](const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) { OnControllersDeactivated(entityHandle, entityIsMigrating); })
{
m_aabb.Expand(AZ::Vector3(sv_ReplicationWindowWidth, sv_ReplicationWindowWidth, sv_ReplicationWindowWidth));
m_updateWindowEvent.Enqueue(sv_ServerReplicationWindowUpdateMs, true);
GetNetworkEntityManager()->AddControllersActivatedHandler(m_controllersActivatedHandler);
GetNetworkEntityManager()->AddControllersDeactivatedHandler(m_controllersDeactivatedHandler);
}
bool ServerToServerReplicationWindow::ReplicationSetUpdateReady()
{
return m_initialGatherComplete;
}
const ReplicationSet& ServerToServerReplicationWindow::GetReplicationSet() const
{
return m_replicationSet;
}
uint32_t ServerToServerReplicationWindow::GetMaxEntityReplicatorSendCount() const
{
return AZStd::numeric_limits<uint32_t>::max();
}
bool ServerToServerReplicationWindow::IsInWindow(const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const
{
outNetworkRole = NetEntityRole::InvalidRole;
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
if (netBindComponent != nullptr)
{
NetEntityRole networkRole = netBindComponent->GetNetEntityRole();
if (networkRole == NetEntityRole::Authority)
{
const AZ::Entity* entity = entityHandle.GetEntity();
AZ::TransformInterface* transformInterface = entity->GetTransform();
AZ::Vector3 entityPosition = transformInterface->GetWorldTranslation();
if (m_aabb.Contains(entityPosition))
{
outNetworkRole = Multiplayer::NetEntityRole::Server;
return true;
}
}
}
return false;
}
void ServerToServerReplicationWindow::UpdateWindow()
{
m_initialGatherComplete = true; // Auto updates bootstrapped
m_replicationSet.clear();
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->Enumerate(m_aabb, [this](const AzFramework::IVisibilitySystem::NodeData& nodeData)
{
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
{
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_NetEntity)
{
ConstNetworkEntityHandle entityHandle = ConstNetworkEntityHandle(static_cast<AZ::Entity*>(visEntry->m_userData), GetNetworkEntityTracker());
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
if (netBindComponent != nullptr)
{
NetEntityRole networkRole = netBindComponent->GetNetEntityRole();
if (networkRole == NetEntityRole::Authority)
{
m_replicationSet[entityHandle] = { NetEntityRole::Server, 0.0f }; // Note, server replication does not use priority
}
}
}
}
}
);
}
void ServerToServerReplicationWindow::DebugDraw() const
{
static const float BoundaryStripeHeight = 1.0f;
static const float BoundaryStripeSpacing = 0.5f;
static const int32_t BoundaryStripeCount = 10;
//auto* loc = draw.GetOwnerConst()->FindComponent<LocationComponent::Server>();
//if (loc == nullptr)
//{
// return;
//}
//
//Vec3 dmnMin = m_ReplicationWindowParams.GetMin();
//Vec3 dmnMax = m_ReplicationWindowParams.GetMax();
//
//dmnMin.z = loc->GetPosition().z;
//dmnMax.z = dmnMin.z + k_BoundaryStripeHeight;
//
//for (int i = 0; i < k_BoundaryStripeCount; ++i)
//{
// draw.AABB(dmnMin, dmnMax, DebugColors::green);
// dmnMin.z += k_BoundaryStripeSpacing;
// dmnMax.z += k_BoundaryStripeSpacing;
//}
}
void ServerToServerReplicationWindow::OnControllersActivated(const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
NetEntityRole networkRole = NetEntityRole::InvalidRole;
if (IsInWindow(entityHandle, networkRole))
{
m_replicationSet[entityHandle] = { NetEntityRole::Server, 0.0f };
}
}
void ServerToServerReplicationWindow::OnControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
m_replicationSet.erase(entityHandle);
}
}
@@ -1,57 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Source/ReplicationWindows/IReplicationWindow.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <Source/NetworkEntity/INetworkEntityManager.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/EBus/ScheduledEvent.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace Multiplayer
{
class ServerToServerReplicationWindow
: public IReplicationWindow
{
public:
ServerToServerReplicationWindow(const AZ::Aabb& aabb);
//! IReplicationWindow interface
//! @{
bool ReplicationSetUpdateReady() override;
const ReplicationSet& GetReplicationSet() const override;
uint32_t GetMaxEntityReplicatorSendCount() const override;
bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const override;
void UpdateWindow() override;
void DebugDraw() const override;
//! @}
private:
void OnControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating);
void OnControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating);
ServerToServerReplicationWindow& operator=(const ServerToServerReplicationWindow&) = delete;
ReplicationSet m_replicationSet;
AZ::ScheduledEvent m_updateWindowEvent;
ControllersActivatedEvent::Handler m_controllersActivatedHandler;
ControllersDeactivatedEvent::Handler m_controllersDeactivatedHandler;
AZ::Aabb m_aabb;
bool m_initialGatherComplete = false; // Replication window need to run an initial gather on connection
};
}