Merged development
Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com>
This commit is contained in:
@@ -36,6 +36,8 @@ namespace Multiplayer
|
||||
void OnScaleChangedEvent(float scale);
|
||||
void OnResetCountChangedEvent();
|
||||
|
||||
void UpdateTargetHostFrameId();
|
||||
|
||||
AZ::Transform m_previousTransform = AZ::Transform::CreateIdentity();
|
||||
AZ::Transform m_targetTransform = AZ::Transform::CreateIdentity();
|
||||
|
||||
@@ -45,6 +47,8 @@ namespace Multiplayer
|
||||
AZ::Event<uint8_t>::Handler m_resetCountEventHandler;
|
||||
|
||||
EntityPreRenderEvent::Handler m_entityPreRenderEventHandler;
|
||||
|
||||
Multiplayer::HostFrameId m_targetHostFrameId = HostFrameId(0);
|
||||
};
|
||||
|
||||
class NetworkTransformComponentController
|
||||
|
||||
@@ -122,6 +122,11 @@ namespace Multiplayer
|
||||
//! @return the current server time in milliseconds
|
||||
virtual AZ::TimeMs GetCurrentHostTimeMs() const = 0;
|
||||
|
||||
//! Returns the current blend factor for client side interpolation
|
||||
//! This value is only relevant on the client and is used to smooth between host frames
|
||||
//! @return the current blend factor
|
||||
virtual float GetCurrentBlendFactor() const = 0;
|
||||
|
||||
//! Returns the network time instance bound to this multiplayer instance.
|
||||
//! @return pointer to the network time instance bound to this multiplayer instance
|
||||
virtual INetworkTime* GetNetworkTime() = 0;
|
||||
@@ -182,23 +187,27 @@ namespace Multiplayer
|
||||
class ScopedAlterTime final
|
||||
{
|
||||
public:
|
||||
inline ScopedAlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId connectionId)
|
||||
inline ScopedAlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
INetworkTime* time = GetNetworkTime();
|
||||
m_previousHostFrameId = time->GetHostFrameId();
|
||||
m_previousHostTimeMs = time->GetHostTimeMs();
|
||||
m_previousRewindConnectionId = time->GetRewindingConnectionId();
|
||||
time->AlterTime(frameId, timeMs, connectionId);
|
||||
m_previousBlendFactor = time->GetHostBlendFactor();
|
||||
time->AlterBlendFactor(blendFactor);
|
||||
}
|
||||
inline ~ScopedAlterTime()
|
||||
{
|
||||
INetworkTime* time = GetNetworkTime();
|
||||
time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId);
|
||||
time->AlterBlendFactor(m_previousBlendFactor);
|
||||
}
|
||||
private:
|
||||
HostFrameId m_previousHostFrameId = InvalidHostFrameId;
|
||||
AZ::TimeMs m_previousHostTimeMs = AZ::TimeMs{ 0 };
|
||||
AzNetworking::ConnectionId m_previousRewindConnectionId = AzNetworking::InvalidConnectionId;
|
||||
float m_previousBlendFactor = DefaultBlendFactor;
|
||||
};
|
||||
|
||||
inline const char* GetEnumString(MultiplayerAgentType value)
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace Multiplayer
|
||||
//! The default number of rewindable samples for us to store.
|
||||
static constexpr uint32_t RewindHistorySize = 128;
|
||||
|
||||
//! The default blend factor for ScopedAlterTime
|
||||
static constexpr float DefaultBlendFactor = 1.f;
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL(HostId, uint32_t);
|
||||
static constexpr HostId InvalidHostId = static_cast<HostId>(-1);
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ namespace Multiplayer
|
||||
AZ::TimeMs GetHostTimeMs() const;
|
||||
AZ::TimeMs& ModifyHostTimeMs();
|
||||
|
||||
void SetHostBlendFactor(float hostBlendFactor);
|
||||
float GetHostBlendFactor() const;
|
||||
|
||||
void AttachNetBindComponent(NetBindComponent* netBindComponent);
|
||||
|
||||
bool Serialize(AzNetworking::ISerializer& serializer);
|
||||
@@ -73,6 +76,7 @@ namespace Multiplayer
|
||||
ClientInputId m_inputId = ClientInputId{ 0 };
|
||||
HostFrameId m_hostFrameId = InvalidHostFrameId;
|
||||
AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 };
|
||||
float m_hostBlendFactor = 0.f;
|
||||
ConstNetworkEntityHandle m_owner;
|
||||
bool m_wasAttached = false;
|
||||
};
|
||||
|
||||
@@ -43,6 +43,10 @@ namespace Multiplayer
|
||||
//! @return the hosts current timeMs
|
||||
virtual AZ::TimeMs GetHostTimeMs() const = 0;
|
||||
|
||||
//! Retrieves the hosts current blend factor (may be rewound on the server during backward reconciliation).
|
||||
//! @return the hosts current blend factor
|
||||
virtual float GetHostBlendFactor() const = 0;
|
||||
|
||||
//! Get the controlling connection that may be currently altering global game time.
|
||||
//! Note this abstraction is required at a relatively high level to allow for 'don't rewind the shooter' semantics
|
||||
//! @return the ConnectionId of the connection requesting the rewind operation
|
||||
@@ -60,6 +64,10 @@ namespace Multiplayer
|
||||
//! @param rewindConnectionId the rewinding ConnectionId
|
||||
virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) = 0;
|
||||
|
||||
//! Alters the current Host blend factor. Used to drive interpolation in rewound states.
|
||||
//! @param blendFactor the blend factor to use
|
||||
virtual void AlterBlendFactor(float blendFactor) = 0;
|
||||
|
||||
//! Syncs all entities contained within a volume to the current rewind state.
|
||||
//! @param rewindVolume the volume to rewind entities within (needed for physics entities)
|
||||
virtual void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) = 0;
|
||||
|
||||
@@ -60,6 +60,10 @@ namespace Multiplayer
|
||||
//! @return value in const base type form
|
||||
const BASE_TYPE& Get() const;
|
||||
|
||||
//! Const base type retriever for one host frame behind Get(). Only intended for use in SyncRewind contexts.
|
||||
//! @return value in const base type form
|
||||
const BASE_TYPE& GetPrevious() const;
|
||||
|
||||
//! Base type retriever.
|
||||
//! @return value in base type form
|
||||
BASE_TYPE& Modify();
|
||||
|
||||
@@ -66,6 +66,12 @@ namespace Multiplayer
|
||||
return GetValueForTime(GetCurrentTimeForProperty());
|
||||
}
|
||||
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline const BASE_TYPE& RewindableObject<BASE_TYPE, REWIND_SIZE>::GetPrevious() const
|
||||
{
|
||||
return GetValueForTime(GetCurrentTimeForProperty() - HostFrameId(1));
|
||||
}
|
||||
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline BASE_TYPE& RewindableObject<BASE_TYPE, REWIND_SIZE>::Modify()
|
||||
{
|
||||
|
||||
@@ -323,10 +323,12 @@ namespace {{ Component.attrib['Namespace'] }}
|
||||
/// Place in your .cpp
|
||||
#include <{{ Component.attrib['OverrideInclude'] }}>
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace {{ Component.attrib['Namespace'] }}
|
||||
{
|
||||
{% if ComponentDerived %}
|
||||
void {{ ComponentName }}::{{ ComponentName }}::Reflect(AZ::ReflectContext* context)
|
||||
void {{ ComponentName }}::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
|
||||
@@ -970,7 +970,7 @@ enum class NetworkProperties
|
||||
{% macro DefineComponentServiceProxyGrabs(Component, ClassType, ComponentType) %}
|
||||
{% for Service in Component.iter('ComponentRelation') %}
|
||||
{% if Service.attrib['Constraint'] != 'Incompatible' %}
|
||||
m_{{ LowerFirst(Service.attrib['Name']) }} = FindComponent<{{ UpperFirst(Service.attrib['Namespace']) }}::{{ UpperFirst(Service.attrib['Name']) }}>();
|
||||
m_{{ LowerFirst(Service.attrib['Name']) }} = FindComponent<{{ Service.attrib['Namespace'] }}::{{ UpperFirst(Service.attrib['Name']) }}>();
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
@@ -1709,12 +1709,12 @@ namespace {{ Component.attrib['Namespace'] }}
|
||||
|
||||
{% for Service in Component.iter('ComponentRelation') %}
|
||||
{% if Service.attrib['Constraint'] != 'Incompatible' %}
|
||||
const {{ UpperFirst(Service.attrib['Namespace']) }}::{{ UpperFirst(Service.attrib['Name']) }}* {{ ComponentBaseName }}::Get{{ UpperFirst(Service.attrib['Name']) }}() const
|
||||
const {{ Service.attrib['Namespace'] }}::{{ UpperFirst(Service.attrib['Name']) }}* {{ ComponentBaseName }}::Get{{ UpperFirst(Service.attrib['Name']) }}() const
|
||||
{
|
||||
return m_{{ LowerFirst(Service.attrib['Name']) }};
|
||||
}
|
||||
|
||||
{{ UpperFirst(Service.attrib['Namespace']) }}::{{ UpperFirst(Service.attrib['Name']) }}* {{ ComponentBaseName }}::Get{{ UpperFirst(Service.attrib['Name']) }}()
|
||||
{{ Service.attrib['Namespace'] }}::{{ UpperFirst(Service.attrib['Name']) }}* {{ ComponentBaseName }}::Get{{ UpperFirst(Service.attrib['Name']) }}()
|
||||
{
|
||||
return m_{{ LowerFirst(Service.attrib['Name']) }};
|
||||
}
|
||||
|
||||
@@ -155,9 +155,12 @@ namespace Multiplayer
|
||||
// Discard move input events, client may be speed hacking
|
||||
if (m_clientBankedTime < sv_MaxBankTimeWindowSec)
|
||||
{
|
||||
// Client blends from previous frame to target so here we subtract blend factor to get to that state
|
||||
const float blendFactor = AZStd::min(AZStd::max(0.f, input.GetHostBlendFactor()), 1.f);
|
||||
const AZ::TimeMs blendMs = AZ::TimeMs(static_cast<float>(static_cast<AZ::TimeMs>(cl_InputRateMs)) * (1.f - blendFactor));
|
||||
m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary
|
||||
{
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId());
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, input.GetHostBlendFactor(), invokingConnection->GetConnectionId());
|
||||
GetNetBindComponent()->ProcessInput(input, static_cast<float>(clientInputRateSec));
|
||||
}
|
||||
|
||||
@@ -311,7 +314,7 @@ namespace Multiplayer
|
||||
++ModifyLastInputId();
|
||||
input.SetClientInputId(GetLastInputId());
|
||||
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId());
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), input.GetHostBlendFactor(), invokingConnection->GetConnectionId());
|
||||
GetNetBindComponent()->ProcessInput(input, clientInputRateSec);
|
||||
|
||||
AZLOG
|
||||
@@ -391,7 +394,7 @@ namespace Multiplayer
|
||||
{
|
||||
// Reprocess the input for this frame
|
||||
NetworkInput& input = m_inputHistory[replayIndex];
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId());
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), input.GetHostBlendFactor(), invokingConnection->GetConnectionId());
|
||||
GetNetBindComponent()->ProcessInput(input, clientInputRateSec);
|
||||
|
||||
AZLOG
|
||||
@@ -499,6 +502,7 @@ namespace Multiplayer
|
||||
input.SetClientInputId(m_clientInputId);
|
||||
input.SetHostFrameId(networkTime->GetHostFrameId());
|
||||
input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs());
|
||||
input.SetHostBlendFactor(multiplayer->GetCurrentBlendFactor());
|
||||
|
||||
// Allow components to form the input for this frame
|
||||
GetNetBindComponent()->CreateInput(input, inputRate);
|
||||
@@ -573,7 +577,7 @@ namespace Multiplayer
|
||||
|
||||
NetworkInput& input = m_lastInputReceived[0];
|
||||
{
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), AzNetworking::InvalidConnectionId);
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), DefaultBlendFactor, AzNetworking::InvalidConnectionId);
|
||||
GetNetBindComponent()->ProcessInput(input, inputRate);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,18 +61,21 @@ namespace Multiplayer
|
||||
{
|
||||
m_previousTransform.SetRotation(m_targetTransform.GetRotation());
|
||||
m_targetTransform.SetRotation(rotation);
|
||||
UpdateTargetHostFrameId();
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnTranslationChangedEvent(const AZ::Vector3& translation)
|
||||
{
|
||||
m_previousTransform.SetTranslation(m_targetTransform.GetTranslation());
|
||||
m_targetTransform.SetTranslation(translation);
|
||||
UpdateTargetHostFrameId();
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnScaleChangedEvent(float scale)
|
||||
{
|
||||
m_previousTransform.SetUniformScale(m_targetTransform.GetUniformScale());
|
||||
m_targetTransform.SetUniformScale(scale);
|
||||
UpdateTargetHostFrameId();
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnResetCountChangedEvent()
|
||||
@@ -83,14 +86,31 @@ namespace Multiplayer
|
||||
m_previousTransform = m_targetTransform;
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::UpdateTargetHostFrameId()
|
||||
{
|
||||
HostFrameId currentHostFrameId = Multiplayer::GetNetworkTime()->GetHostFrameId();
|
||||
if (currentHostFrameId > m_targetHostFrameId)
|
||||
{
|
||||
m_targetHostFrameId = currentHostFrameId;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime, float blendFactor)
|
||||
{
|
||||
if (!HasController())
|
||||
{
|
||||
AZ::Transform blendTransform;
|
||||
blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor));
|
||||
blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor));
|
||||
blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor));
|
||||
if (Multiplayer::GetNetworkTime() && Multiplayer::GetNetworkTime()->GetHostFrameId() > m_targetHostFrameId)
|
||||
{
|
||||
m_previousTransform = m_targetTransform;
|
||||
blendTransform = m_targetTransform;
|
||||
}
|
||||
else
|
||||
{
|
||||
blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor));
|
||||
blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor));
|
||||
blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor));
|
||||
}
|
||||
|
||||
if (!GetTransformComponent()->GetWorldTM().IsClose(blendTransform))
|
||||
{
|
||||
|
||||
@@ -28,29 +28,24 @@ namespace Multiplayer
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent"));
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatbile)
|
||||
{
|
||||
incompatbile.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent"));
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::Activate()
|
||||
{
|
||||
#ifdef IMGUI_ENABLED
|
||||
ImGui::ImGuiUpdateListenerBus::Handler::BusConnect();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::Deactivate()
|
||||
{
|
||||
#ifdef IMGUI_ENABLED
|
||||
@@ -83,7 +78,6 @@ namespace Multiplayer
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond)
|
||||
{
|
||||
uint64_t summedCalls = 0;
|
||||
@@ -100,8 +94,9 @@ namespace Multiplayer
|
||||
|
||||
bool DrawMetricsRow(const char* name, bool expandable, uint64_t totalCalls, uint64_t totalBytes, float callsPerSecond, float bytesPerSecond)
|
||||
{
|
||||
const ImGuiTreeNodeFlags flags = expandable ? ImGuiTreeNodeFlags_SpanFullWidth
|
||||
: (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
const ImGuiTreeNodeFlags flags = expandable
|
||||
? ImGuiTreeNodeFlags_SpanFullWidth
|
||||
: (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
const bool open = ImGui::TreeNodeEx(name, flags);
|
||||
@@ -115,14 +110,12 @@ namespace Multiplayer
|
||||
ImGui::Text("%11.2f", bytesPerSecond);
|
||||
return open;
|
||||
}
|
||||
|
||||
bool DrawSummaryRow(const char* name, const MultiplayerStats& stats)
|
||||
{
|
||||
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics();
|
||||
const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateTotalPropertyUpdateRecvMetrics();
|
||||
const MultiplayerStats::Metric rpcsSent = stats.CalculateTotalRpcsSentMetrics();
|
||||
const MultiplayerStats::Metric rpcsRecv = stats.CalculateTotalRpcsRecvMetrics();
|
||||
|
||||
const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls;
|
||||
const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes;
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -131,17 +124,14 @@ namespace Multiplayer
|
||||
AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond);
|
||||
AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond);
|
||||
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
|
||||
|
||||
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
|
||||
}
|
||||
|
||||
bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId)
|
||||
{
|
||||
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
|
||||
const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId);
|
||||
const MultiplayerStats::Metric rpcsSent = stats.CalculateComponentRpcsSentMetrics(netComponentId);
|
||||
const MultiplayerStats::Metric rpcsRecv = stats.CalculateComponentRpcsRecvMetrics(netComponentId);
|
||||
|
||||
const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls;
|
||||
const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes;
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -150,10 +140,8 @@ namespace Multiplayer
|
||||
AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond);
|
||||
AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond);
|
||||
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
|
||||
|
||||
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
|
||||
}
|
||||
|
||||
void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId)
|
||||
{
|
||||
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
|
||||
@@ -178,7 +166,6 @@ namespace Multiplayer
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const MultiplayerStats::Metric metric = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId);
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -200,7 +187,6 @@ namespace Multiplayer
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsSentMetrics(netComponentId);
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -222,7 +208,6 @@ namespace Multiplayer
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsRecvMetrics(netComponentId);
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -246,45 +231,218 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::OnImGuiUpdate()
|
||||
void DrawNetworkingStats()
|
||||
{
|
||||
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
const ImGuiTableFlags flags = ImGuiTableFlags_BordersV
|
||||
| ImGuiTableFlags_BordersOuterH
|
||||
| ImGuiTableFlags_Resizable
|
||||
| ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_NoBordersInBody;
|
||||
|
||||
const ImGuiTreeNodeFlags nodeFlags = (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
|
||||
AzNetworking::INetworking* networking = AZ::Interface<AzNetworking::INetworking>::Get();
|
||||
|
||||
ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount());
|
||||
ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast<AZ::s64>(networking->GetTcpListenThreadUpdateTime()));
|
||||
ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount());
|
||||
ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast<AZ::s64>(networking->GetUdpReaderThreadUpdateTime()));
|
||||
ImGui::NewLine();
|
||||
|
||||
for (auto& networkInterface : networking->GetNetworkInterfaces())
|
||||
{
|
||||
if (ImGui::CollapsingHeader(networkInterface.second->GetName().GetCStr()))
|
||||
{
|
||||
const char* protocol = networkInterface.second->GetType() == AzNetworking::ProtocolType::Tcp ? "Tcp" : "Udp";
|
||||
const char* trustZone = networkInterface.second->GetTrustZone() == AzNetworking::TrustZone::ExternalClientToServer ? "ExternalClientToServer" : "InternalServerToServer";
|
||||
const uint32_t port = aznumeric_cast<uint32_t>(networkInterface.second->GetPort());
|
||||
ImGui::Text("%sNetworkInterface open to %s on port %u", protocol, trustZone, port);
|
||||
|
||||
if (ImGui::BeginTable("", 2, flags))
|
||||
{
|
||||
const AzNetworking::NetworkInterfaceMetrics& metrics = networkInterface.second->GetMetrics();
|
||||
ImGui::TableSetupColumn("Stat", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total time spent updating (ms)");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%lld", aznumeric_cast<AZ::s64>(metrics.m_updateTimeMs));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total number of connections");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_connectionCount));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total send time (ms)");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%lld", aznumeric_cast<AZ::s64>(metrics.m_sendTimeMs));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total sent packets");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::s64>(metrics.m_sendPackets));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total sent bytes after compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytes));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total sent bytes before compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytesUncompressed));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total sent compressed packets without benefit");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_sendCompressedPacketsNoGain));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total gain from packet compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%lld", aznumeric_cast<AZ::s64>(metrics.m_sendBytesCompressedDelta));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total packets resent");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_resentPackets));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total receive time (ms)");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%lld", aznumeric_cast<AZ::s64>(metrics.m_recvTimeMs));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total received packets");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_recvPackets));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total received bytes after compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytes));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total received bytes before compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytesUncompressed));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total packets discarded due to load");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_discardedPackets));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("", 7, flags))
|
||||
{
|
||||
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
|
||||
ImGui::TableSetupColumn("RemoteAddr", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Conn. Id", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 6.0f);
|
||||
ImGui::TableSetupColumn("Send (Bps)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 10.0f);
|
||||
ImGui::TableSetupColumn("Recv (Bps)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 10.0f);
|
||||
ImGui::TableSetupColumn("RTT (ms)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 8.0f);
|
||||
ImGui::TableSetupColumn("% Lost", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 8.0f);
|
||||
ImGui::TableSetupColumn("Debug Settings", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 32.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto displayConnectionRow = [](AzNetworking::IConnection& connection)
|
||||
{
|
||||
ImGui::PushID(&connection);
|
||||
|
||||
const AzNetworking::ConnectionMetrics& metrics = connection.GetMetrics();
|
||||
const AzNetworking::IpAddress::IpString remoteAddr = connection.GetRemoteAddress().GetString();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TreeNodeEx(remoteAddr.c_str(), nodeFlags);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%5llu", aznumeric_cast<AZ::u64>(connection.GetConnectionId()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%9.2f", metrics.m_sendDatarate.GetBytesPerSecond());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%9.2f", metrics.m_recvDatarate.GetBytesPerSecond());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%7.2f", metrics.m_connectionRtt.GetRoundTripTimeSeconds() * 1000.0f);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%7.2f", metrics.m_sendDatarate.GetLossRatePercent());
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
{
|
||||
AzNetworking::ConnectionQuality& quality = connection.GetConnectionQuality();
|
||||
int32_t latency = aznumeric_cast<int32_t>(quality.m_latencyMs);
|
||||
int32_t variance = aznumeric_cast<int32_t>(quality.m_varianceMs);
|
||||
ImGui::SliderInt("Loss %", &quality.m_lossPercentage, 0, 100);
|
||||
if (ImGui::SliderInt("Latency(ms)", &latency, 0, 3000))
|
||||
{
|
||||
quality.m_latencyMs = AZ::TimeMs{ latency };
|
||||
}
|
||||
if (ImGui::SliderInt("Jitter(ms)", &variance, 0, 1000))
|
||||
{
|
||||
quality.m_varianceMs = AZ::TimeMs{ variance };
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
};
|
||||
networkInterface.second->GetConnectionSet().VisitConnections(displayConnectionRow);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::NewLine();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void DrawMultiplayerStats()
|
||||
{
|
||||
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
|
||||
const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
|
||||
ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType()));
|
||||
ImGui::Text("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
|
||||
ImGui::Text("Total client connections: %llu", aznumeric_cast<AZ::u64>(stats.m_clientConnectionCount));
|
||||
ImGui::Text("Total server connections: %llu", aznumeric_cast<AZ::u64>(stats.m_serverConnectionCount));
|
||||
ImGui::NewLine();
|
||||
|
||||
static ImGuiTableFlags flags = ImGuiTableFlags_BordersV
|
||||
| ImGuiTableFlags_BordersOuterH
|
||||
| ImGuiTableFlags_Resizable
|
||||
| ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_NoBordersInBody;
|
||||
|
||||
if (ImGui::BeginTable("", 5, flags))
|
||||
{
|
||||
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Bytes/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
if (DrawSummaryRow("Totals", stats))
|
||||
{
|
||||
for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index)
|
||||
{
|
||||
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
|
||||
using StringLabel = AZStd::fixed_string<128>;
|
||||
const StringLabel gemName = componentRegistry->GetComponentGemName(netComponentId);
|
||||
const StringLabel componentName = componentRegistry->GetComponentName(netComponentId);
|
||||
const StringLabel label = gemName + "::" + componentName;
|
||||
if (DrawComponentRow(label.c_str(), stats, netComponentId))
|
||||
{
|
||||
DrawComponentDetails(stats, netComponentId);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
ImGui::NewLine();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::OnImGuiUpdate()
|
||||
{
|
||||
if (m_displayNetworkingStats)
|
||||
{
|
||||
if (ImGui::Begin("Networking Stats", &m_displayNetworkingStats, ImGuiWindowFlags_None))
|
||||
{
|
||||
AzNetworking::INetworking* networking = AZ::Interface<AzNetworking::INetworking>::Get();
|
||||
|
||||
ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount());
|
||||
ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast<AZ::s64>(networking->GetTcpListenThreadUpdateTime()));
|
||||
ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount());
|
||||
ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast<AZ::s64>(networking->GetUdpReaderThreadUpdateTime()));
|
||||
|
||||
for (auto& networkInterface : networking->GetNetworkInterfaces())
|
||||
{
|
||||
const char* protocol = networkInterface.second->GetType() == AzNetworking::ProtocolType::Tcp ? "Tcp" : "Udp";
|
||||
const char* trustZone = networkInterface.second->GetTrustZone() == AzNetworking::TrustZone::ExternalClientToServer ? "ExternalClientToServer" : "InternalServerToServer";
|
||||
const uint32_t port = aznumeric_cast<uint32_t>(networkInterface.second->GetPort());
|
||||
ImGui::Text("%sNetworkInterface: %s - open to %s on port %u", protocol, networkInterface.second->GetName().GetCStr(), trustZone, port);
|
||||
|
||||
const AzNetworking::NetworkInterfaceMetrics& metrics = networkInterface.second->GetMetrics();
|
||||
ImGui::Text(" - Total time spent updating in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_updateTimeMs));
|
||||
ImGui::Text(" - Total number of connections: %llu", aznumeric_cast<AZ::u64>(metrics.m_connectionCount));
|
||||
ImGui::Text(" - Total send time in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_sendTimeMs));
|
||||
ImGui::Text(" - Total sent packets: %llu", aznumeric_cast<AZ::s64>(metrics.m_sendPackets));
|
||||
ImGui::Text(" - Total sent bytes after compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytes));
|
||||
ImGui::Text(" - Total sent bytes before compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytesUncompressed));
|
||||
ImGui::Text(" - Total sent compressed packets without benefit: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendCompressedPacketsNoGain));
|
||||
ImGui::Text(" - Total gain from packet compression: %lld", aznumeric_cast<AZ::s64>(metrics.m_sendBytesCompressedDelta));
|
||||
ImGui::Text(" - Total packets resent: %llu", aznumeric_cast<AZ::u64>(metrics.m_resentPackets));
|
||||
ImGui::Text(" - Total receive time in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_recvTimeMs));
|
||||
ImGui::Text(" - Total received packets: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvPackets));
|
||||
ImGui::Text(" - Total received bytes after compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytes));
|
||||
ImGui::Text(" - Total received bytes before compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytesUncompressed));
|
||||
ImGui::Text(" - Total packets discarded due to load: %llu", aznumeric_cast<AZ::u64>(metrics.m_discardedPackets));
|
||||
}
|
||||
DrawNetworkingStats();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,50 +450,7 @@ namespace Multiplayer
|
||||
{
|
||||
if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_None))
|
||||
{
|
||||
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
|
||||
const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
|
||||
ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType()));
|
||||
ImGui::Text("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
|
||||
ImGui::Text("Total client connections: %llu", aznumeric_cast<AZ::u64>(stats.m_clientConnectionCount));
|
||||
ImGui::Text("Total server connections: %llu", aznumeric_cast<AZ::u64>(stats.m_serverConnectionCount));
|
||||
ImGui::NewLine();
|
||||
|
||||
static ImGuiTableFlags flags = ImGuiTableFlags_BordersV
|
||||
| ImGuiTableFlags_BordersOuterH
|
||||
| ImGuiTableFlags_Resizable
|
||||
| ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_NoBordersInBody;
|
||||
|
||||
if (ImGui::BeginTable("", 5, flags))
|
||||
{
|
||||
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Bytes/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
if (DrawSummaryRow("Totals", stats))
|
||||
{
|
||||
for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index)
|
||||
{
|
||||
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
|
||||
using StringLabel = AZStd::fixed_string<128>;
|
||||
const StringLabel gemName = componentRegistry->GetComponentGemName(netComponentId);
|
||||
const StringLabel componentName = componentRegistry->GetComponentName(netComponentId);
|
||||
const StringLabel label = gemName + "::" + componentName;
|
||||
if (DrawComponentRow(label.c_str(), stats, netComponentId))
|
||||
{
|
||||
DrawComponentDetails(stats, netComponentId);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
DrawMultiplayerStats();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,19 +69,24 @@ namespace Multiplayer
|
||||
{
|
||||
using namespace AzNetworking;
|
||||
|
||||
AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port");
|
||||
AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to");
|
||||
AZ_CVAR(AZ::CVarFixedString, cl_serverpassword, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Optional server password");
|
||||
AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port");
|
||||
AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The address of the remote server or host to connect to");
|
||||
AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic");
|
||||
AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_map, "nolevel", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The map the server should load");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_gamerules, "norules", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "GameRules server works with");
|
||||
AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking");
|
||||
AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server");
|
||||
AZ_CVAR(bool, sv_isTransient, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether a dedicated server shuts down if all existing connections disconnect.");
|
||||
AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
|
||||
AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
|
||||
AZ_CVAR(AZ::TimeMs, sv_serverSendRateMs, AZ::TimeMs{ 50 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of milliseconds between each network update");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The default spawnable to use when a new player connects");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The default spawnable to use when a new player connects");
|
||||
AZ_CVAR(float, cl_renderTickBlendBase, 0.15f, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"The base used for blending between network updates, 0.1 will be quite linear, 0.2 or 0.3 will "
|
||||
"slow down quicker and may be better suited to connections with highly variable latency");
|
||||
|
||||
void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
@@ -546,7 +551,7 @@ namespace Multiplayer
|
||||
if ((GetAgentType() == MultiplayerAgentType::Client) && (packet.GetHostFrameId() > m_lastReplicatedHostFrameId))
|
||||
{
|
||||
// Update client to latest server time
|
||||
m_renderBlendFactor = 0.0f;
|
||||
m_tickFactor = 0.0f;
|
||||
m_lastReplicatedHostTimeMs = packet.GetHostTimeMs();
|
||||
m_lastReplicatedHostFrameId = packet.GetHostFrameId();
|
||||
m_networkTime.AlterTime(m_lastReplicatedHostFrameId, m_lastReplicatedHostTimeMs, AzNetworking::InvalidConnectionId);
|
||||
@@ -804,6 +809,11 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
float MultiplayerSystemComponent::GetCurrentBlendFactor() const
|
||||
{
|
||||
return m_renderBlendFactor;
|
||||
}
|
||||
|
||||
INetworkTime* MultiplayerSystemComponent::GetNetworkTime()
|
||||
{
|
||||
return &m_networkTime;
|
||||
@@ -849,10 +859,18 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds)
|
||||
{
|
||||
m_tickFactor += deltaTime / serverRateSeconds;
|
||||
// Linear close to the origin, but asymptote at y = 1
|
||||
const float targetAdjustBlend = AZStd::clamp(deltaTime / serverRateSeconds, 0.0f, 1.0f);
|
||||
m_renderBlendFactor = 1.0f - (std::pow(0.2f, m_renderBlendFactor + targetAdjustBlend));
|
||||
AZLOG(NET_Blending, "Computed blend factor of %0.2f using a frametime of %0.2f and a serverTickRate of %0.2f", m_renderBlendFactor, deltaTime, serverRateSeconds);
|
||||
m_renderBlendFactor = AZStd::clamp(1.0f - (std::pow(cl_renderTickBlendBase, m_tickFactor)), 0.0f, 1.0f);
|
||||
AZLOG
|
||||
(
|
||||
NET_Blending,
|
||||
"Computed blend factor of %0.3f using a tick factor of %0.3f, a frametime of %0.3f and a serverTickRate of %0.3f",
|
||||
m_renderBlendFactor,
|
||||
m_tickFactor,
|
||||
deltaTime,
|
||||
serverRateSeconds
|
||||
);
|
||||
|
||||
if (Camera::ActiveCameraRequestBus::HasHandlers())
|
||||
{
|
||||
|
||||
@@ -113,6 +113,7 @@ namespace Multiplayer
|
||||
void Terminate(AzNetworking::DisconnectReason reason) override;
|
||||
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
|
||||
AZ::TimeMs GetCurrentHostTimeMs() const override;
|
||||
float GetCurrentBlendFactor() const override;
|
||||
INetworkTime* GetNetworkTime() override;
|
||||
INetworkEntityManager* GetNetworkEntityManager() override;
|
||||
void SetFilterEntityManager(IFilterEntityManager* entityFilter) override;
|
||||
@@ -156,6 +157,7 @@ namespace Multiplayer
|
||||
|
||||
double m_serverSendAccumulator = 0.0;
|
||||
float m_renderBlendFactor = 0.0f;
|
||||
float m_tickFactor = 0.0f;
|
||||
|
||||
#if !defined(AZ_RELEASE_BUILD)
|
||||
MultiplayerEditorConnection m_editorConnectionListener;
|
||||
|
||||
@@ -76,6 +76,16 @@ namespace Multiplayer
|
||||
return m_hostTimeMs;
|
||||
}
|
||||
|
||||
void NetworkInput::SetHostBlendFactor(float hostBlendFactor)
|
||||
{
|
||||
m_hostBlendFactor = hostBlendFactor;
|
||||
}
|
||||
|
||||
float NetworkInput::GetHostBlendFactor() const
|
||||
{
|
||||
return m_hostBlendFactor;
|
||||
}
|
||||
|
||||
void NetworkInput::AttachNetBindComponent(NetBindComponent* netBindComponent)
|
||||
{
|
||||
m_wasAttached = true;
|
||||
@@ -91,7 +101,8 @@ namespace Multiplayer
|
||||
{
|
||||
if (!serializer.Serialize(m_inputId, "InputId")
|
||||
|| !serializer.Serialize(m_hostTimeMs, "HostTimeMs")
|
||||
|| !serializer.Serialize(m_hostFrameId, "HostFrameId"))
|
||||
|| !serializer.Serialize(m_hostFrameId, "HostFrameId")
|
||||
|| !serializer.Serialize(m_hostBlendFactor, "HostBlendFactor"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -164,6 +175,7 @@ namespace Multiplayer
|
||||
m_inputId = rhs.m_inputId;
|
||||
m_hostFrameId = rhs.m_hostFrameId;
|
||||
m_hostTimeMs = rhs.m_hostTimeMs;
|
||||
m_hostBlendFactor = rhs.m_hostBlendFactor;
|
||||
m_componentInputs.resize(rhs.m_componentInputs.size());
|
||||
for (int32_t i = 0; i < rhs.m_componentInputs.size(); ++i)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,11 @@ namespace Multiplayer
|
||||
return m_hostTimeMs;
|
||||
}
|
||||
|
||||
float NetworkTime::GetHostBlendFactor() const
|
||||
{
|
||||
return m_hostBlendFactor;
|
||||
}
|
||||
|
||||
AzNetworking::ConnectionId NetworkTime::GetRewindingConnectionId() const
|
||||
{
|
||||
return m_rewindingConnectionId;
|
||||
@@ -72,6 +77,11 @@ namespace Multiplayer
|
||||
m_rewindingConnectionId = rewindConnectionId;
|
||||
}
|
||||
|
||||
void NetworkTime::AlterBlendFactor(float blendFactor)
|
||||
{
|
||||
m_hostBlendFactor = blendFactor;
|
||||
}
|
||||
|
||||
void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume)
|
||||
{
|
||||
// Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities
|
||||
@@ -95,6 +105,7 @@ namespace Multiplayer
|
||||
|
||||
if (networkTransform != nullptr)
|
||||
{
|
||||
// We're not presently factoring in interpolated position here
|
||||
const AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); // Get the rewound position
|
||||
const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
|
||||
const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
|
||||
|
||||
@@ -30,9 +30,11 @@ namespace Multiplayer
|
||||
HostFrameId GetUnalteredHostFrameId() const override;
|
||||
void IncrementHostFrameId() override;
|
||||
AZ::TimeMs GetHostTimeMs() const override;
|
||||
float GetHostBlendFactor() const override;
|
||||
AzNetworking::ConnectionId GetRewindingConnectionId() const override;
|
||||
HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const override;
|
||||
void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) override;
|
||||
void AlterBlendFactor(float blendFactor) override;
|
||||
void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) override;
|
||||
void ClearRewoundEntities() override;
|
||||
//! @}
|
||||
@@ -44,6 +46,7 @@ namespace Multiplayer
|
||||
HostFrameId m_hostFrameId = HostFrameId{ 0 };
|
||||
HostFrameId m_unalteredFrameId = HostFrameId{ 0 };
|
||||
AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 };
|
||||
float m_hostBlendFactor = DefaultBlendFactor;
|
||||
AzNetworking::ConnectionId m_rewindingConnectionId = AzNetworking::InvalidConnectionId;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,8 +107,10 @@ namespace Multiplayer
|
||||
void ServerToClientReplicationWindow::UpdateWindow()
|
||||
{
|
||||
// clear the candidate queue, we're going to rebuild it
|
||||
ReplicationCandidateQueue clearQueue;
|
||||
clearQueue.get_container().reserve(sv_MaxEntitiesToTrackReplication);
|
||||
ReplicationCandidateQueue::container_type clearQueueContainer;
|
||||
clearQueueContainer.reserve(sv_MaxEntitiesToTrackReplication);
|
||||
// Move the clearQueueContainer into the ReplicationCandidateQueue to maintain the reserved memory
|
||||
ReplicationCandidateQueue clearQueue(ReplicationCandidateQueue::value_compare{}, AZStd::move(clearQueueContainer));
|
||||
m_candidateQueue.swap(clearQueue);
|
||||
m_replicationSet.clear();
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace UnitTest
|
||||
// Test rewind for all pushed values and overall size
|
||||
for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx)
|
||||
{
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(idx), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(idx + 1, test.size());
|
||||
EXPECT_EQ(idx, test.back());
|
||||
}
|
||||
@@ -70,9 +70,9 @@ namespace UnitTest
|
||||
EXPECT_TRUE(test.empty());
|
||||
|
||||
// Test rewind for pop_back and clear
|
||||
Multiplayer::ScopedAlterTime pop_time(static_cast<Multiplayer::HostFrameId>(RewindableContainerSize), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime pop_time(static_cast<Multiplayer::HostFrameId>(RewindableContainerSize), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(RewindableContainerSize - 1, test.size());
|
||||
Multiplayer::ScopedAlterTime clear_time(static_cast<Multiplayer::HostFrameId>(RewindableContainerSize + 1), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime clear_time(static_cast<Multiplayer::HostFrameId>(RewindableContainerSize + 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(0, test.size());
|
||||
|
||||
// Test copy_values and resize_no_construct
|
||||
@@ -100,7 +100,7 @@ namespace UnitTest
|
||||
// Test rewind for all values and overall size
|
||||
for (uint32_t idx = 1; idx <= RewindableContainerSize; ++idx)
|
||||
{
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(idx), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
for (uint32_t testIdx = 0; testIdx < RewindableContainerSize; ++testIdx)
|
||||
{
|
||||
if (testIdx < idx)
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace UnitTest
|
||||
|
||||
for (uint32_t i = 0; i < 16; ++i)
|
||||
{
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(i, test);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace UnitTest
|
||||
|
||||
for (uint32_t i = 16; i < 48; ++i)
|
||||
{
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(i, test);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ namespace UnitTest
|
||||
|
||||
{
|
||||
// Note that we didn't actually set any value for time rewindableBufferFrames, so we're testing fetching a value past the last time set
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(RewindableBufferFrames), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(RewindableBufferFrames), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(RewindableBufferFrames - 1, test);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ namespace UnitTest
|
||||
|
||||
for (uint32_t i = 0; i < RewindableBufferFrames; ++i)
|
||||
{
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
const Object& value = test;
|
||||
EXPECT_EQ(value.value, i);
|
||||
}
|
||||
@@ -102,19 +102,19 @@ namespace UnitTest
|
||||
TEST_F(RewindableObjectTests, TestBackfillOnLargeTimestep)
|
||||
{
|
||||
Multiplayer::RewindableObject<uint32_t, RewindableBufferFrames> test(0);
|
||||
Multiplayer::ScopedAlterTime time1(static_cast<Multiplayer::HostFrameId>(0), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time1(static_cast<Multiplayer::HostFrameId>(0), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
test = 1;
|
||||
|
||||
Multiplayer::ScopedAlterTime time2(static_cast<Multiplayer::HostFrameId>(31), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time2(static_cast<Multiplayer::HostFrameId>(31), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
test = 2;
|
||||
|
||||
for (uint32_t i = 0; i < 31; ++i)
|
||||
{
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(1, test);
|
||||
}
|
||||
|
||||
Multiplayer::ScopedAlterTime time3(static_cast<Multiplayer::HostFrameId>(31), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time3(static_cast<Multiplayer::HostFrameId>(31), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(2, test);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace UnitTest
|
||||
|
||||
for (uint32_t i = 0; i < 1000; ++i)
|
||||
{
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(1000 - i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(1000 - i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(1000, test);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user