Ported the local prediction player controller component
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 <AzNetworking/Serialization/StringifySerializer.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
StringifySerializer::StringifySerializer(char delimeter, bool outputFieldNames, const AZStd::string& seperator)
|
||||
: m_delimeter(delimeter)
|
||||
, m_outputFieldNames(outputFieldNames)
|
||||
, m_separator(seperator)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
const AZStd::string& StringifySerializer::GetString() const
|
||||
{
|
||||
return m_string;
|
||||
}
|
||||
|
||||
const StringifySerializer::StringMap& StringifySerializer::GetValueMap() const
|
||||
{
|
||||
return m_map;
|
||||
}
|
||||
|
||||
SerializerMode StringifySerializer::GetSerializerMode() const
|
||||
{
|
||||
return SerializerMode::ReadFromObject;
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(bool& value, const char* name)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(char& value, const char* name, char, char)
|
||||
{
|
||||
const int val = value; // Print chars as integers
|
||||
return ProcessData(name, val);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(int8_t& value, const char* name, int8_t, int8_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(int16_t& value, const char* name, int16_t, int16_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(int32_t& value, const char* name, int32_t, int32_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(int64_t& value, const char* name, int64_t, int64_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(uint8_t& value, const char* name, uint8_t, uint8_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(uint16_t& value, const char* name, uint16_t, uint16_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(uint32_t& value, const char* name, uint32_t, uint32_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(uint64_t& value, const char* name, uint64_t, uint64_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(float& value, const char* name, float, float)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(double& value, const char* name, double, double)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::SerializeBytes(uint8_t* buffer, uint32_t, bool isString, uint32_t&, const char* name)
|
||||
{
|
||||
if (isString)
|
||||
{
|
||||
AZ::CVarFixedString value = reinterpret_cast<char*>(buffer);
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StringifySerializer::BeginObject(const char* name, const char*)
|
||||
{
|
||||
m_prefixSizeStack.push_back(m_prefix.size());
|
||||
m_prefix += name;
|
||||
m_prefix += ".";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StringifySerializer::EndObject(const char*, const char*)
|
||||
{
|
||||
m_prefix.resize(m_prefixSizeStack.back());
|
||||
m_prefixSizeStack.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t* StringifySerializer::GetBuffer() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t StringifySerializer::GetCapacity() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t StringifySerializer::GetSize() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool StringifySerializer::ProcessData(const char* name, const T& value)
|
||||
{
|
||||
// Only add delimeters after we have processed at least one element
|
||||
if (!m_string.empty())
|
||||
{
|
||||
m_string += m_delimeter;
|
||||
}
|
||||
|
||||
if (m_outputFieldNames)
|
||||
{
|
||||
m_string += m_prefix;
|
||||
m_string += name;
|
||||
m_string += m_separator;
|
||||
}
|
||||
|
||||
AZ::CVarFixedString string = AZ::ConsoleTypeHelpers::ValueToString(value);
|
||||
m_string += string.c_str();
|
||||
m_map[m_prefix + name] = string.c_str();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
// StringifySerializer
|
||||
// Generate a debug string of a serializable object
|
||||
class StringifySerializer
|
||||
: public ISerializer
|
||||
{
|
||||
public:
|
||||
|
||||
using StringMap = AZStd::map<AZStd::string, AZStd::string>;
|
||||
|
||||
StringifySerializer(char delimeter = ' ', bool outputFieldNames = true, const AZStd::string& seperator = "=");
|
||||
|
||||
// GetString
|
||||
// After serializing objects, get the serialized values as a single string
|
||||
const AZStd::string& GetString() const;
|
||||
|
||||
// GetValueMap
|
||||
// After serializing objects, get the serialized values as key value pairs
|
||||
const StringMap& GetValueMap() const;
|
||||
|
||||
// ISerializer interfaces
|
||||
SerializerMode GetSerializerMode() const override;
|
||||
bool Serialize(bool& value, const char* name) override;
|
||||
bool Serialize(char& value, const char* name, char minValue, char maxValue) override;
|
||||
bool Serialize(int8_t& value, const char* name, int8_t minValue, int8_t maxValue) override;
|
||||
bool Serialize(int16_t& value, const char* name, int16_t minValue, int16_t maxValue) override;
|
||||
bool Serialize(int32_t& value, const char* name, int32_t minValue, int32_t maxValue) override;
|
||||
bool Serialize(int64_t& value, const char* name, int64_t minValue, int64_t maxValue) override;
|
||||
bool Serialize(uint8_t& value, const char* name, uint8_t minValue, uint8_t maxValue) override;
|
||||
bool Serialize(uint16_t& value, const char* name, uint16_t minValue, uint16_t maxValue) override;
|
||||
bool Serialize(uint32_t& value, const char* name, uint32_t minValue, uint32_t maxValue) override;
|
||||
bool Serialize(uint64_t& value, const char* name, uint64_t minValue, uint64_t maxValue) override;
|
||||
bool Serialize(float& value, const char* name, float minValue, float maxValue) override;
|
||||
bool Serialize(double& value, const char* name, double minValue, double maxValue) override;
|
||||
bool SerializeBytes(uint8_t* buffer, uint32_t bufferCapacity, bool isString, uint32_t& outSize, const char* name) override;
|
||||
bool BeginObject(const char* name, const char* typeName) override;
|
||||
bool EndObject(const char* name, const char* typeName) override;
|
||||
|
||||
const uint8_t* GetBuffer() const override;
|
||||
uint32_t GetCapacity() const override;
|
||||
uint32_t GetSize() const override;
|
||||
void ClearTrackedChangesFlag() override {}
|
||||
bool GetTrackedChangesFlag() const override { return false; }
|
||||
// ISerializer interfaces
|
||||
|
||||
private:
|
||||
|
||||
template <typename T>
|
||||
bool ProcessData(const char* name, const T& value);
|
||||
|
||||
private:
|
||||
|
||||
char m_delimeter;
|
||||
bool m_outputFieldNames = true;
|
||||
|
||||
StringMap m_map;
|
||||
AZStd::string m_string;
|
||||
AZStd::string m_prefix;
|
||||
AZStd::string m_separator;
|
||||
AZStd::deque<AZStd::size_t> m_prefixSizeStack;
|
||||
};
|
||||
}
|
||||
@@ -65,6 +65,8 @@ set(FILES
|
||||
Serialization/NetworkOutputSerializer.cpp
|
||||
Serialization/NetworkOutputSerializer.h
|
||||
Serialization/NetworkOutputSerializer.inl
|
||||
Serialization/StringifySerializer.cpp
|
||||
Serialization/StringifySerializer.h
|
||||
Serialization/TrackChangedSerializer.h
|
||||
Serialization/TrackChangedSerializer.inl
|
||||
TcpTransport/TcpConnection.cpp
|
||||
|
||||
+6
-4
@@ -12,11 +12,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class EntityReplicationManager;
|
||||
|
||||
enum class ConnectionDataType
|
||||
{
|
||||
ClientToServer,
|
||||
@@ -42,8 +44,8 @@ namespace Multiplayer
|
||||
virtual EntityReplicationManager& GetReplicationManager() = 0;
|
||||
|
||||
//! Creates and manages sending updates to the remote endpoint.
|
||||
//! @param serverGameTimeMs current server game time in milliseconds
|
||||
virtual void Update(AZ::TimeMs serverGameTimeMs) = 0;
|
||||
//! @param hostTimeMs current server game time in milliseconds
|
||||
virtual void Update(AZ::TimeMs hostTimeMs) = 0;
|
||||
|
||||
//! Returns whether update messages can be sent to the connection.
|
||||
//! @return true if update messages can be sent
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <AzNetworking/DataStructures/ByteBuffer.h>
|
||||
#include <Include/INetworkTime.h>
|
||||
#include <Include/MultiplayerStats.h>
|
||||
|
||||
namespace AzNetworking
|
||||
@@ -56,7 +57,7 @@ namespace Multiplayer
|
||||
|
||||
//! Gets the type of Agent this IMultiplayer impl represents
|
||||
//! @return The type of agents represented
|
||||
virtual MultiplayerAgentType GetAgentType() = 0;
|
||||
virtual MultiplayerAgentType GetAgentType() const = 0;
|
||||
|
||||
//! Sets the type of this Multiplayer connection and calls any related callback
|
||||
//! @param state The state of this connection
|
||||
@@ -78,6 +79,14 @@ namespace Multiplayer
|
||||
//! @param readyForEntityUpdates Ready for entity updates or not
|
||||
virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0;
|
||||
|
||||
//! Returns the current server time in milliseconds.
|
||||
//! This can be one of three possible values:
|
||||
//! 1. On the host outside of rewind scope, this will return the latest application elapsed time in ms.
|
||||
//! 2. On the host within rewind scope, this will return the rewound time in ms.
|
||||
//! 3. On the client, this will return the most recently replicated server time in ms.
|
||||
//! @return the current server time in milliseconds
|
||||
virtual AZ::TimeMs GetCurrentHostTimeMs() const = 0;
|
||||
|
||||
//! Returns the gem name associated with the provided component index.
|
||||
//! @param netComponentId the componentId to return the gem name of
|
||||
//! @return the name of the gem that contains the requested component
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Include/MultiplayerTypes.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/EBus/Event.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
+29
-37
@@ -14,13 +14,10 @@
|
||||
|
||||
#include <AzCore/Time/ITime.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <Include/MultiplayerTypes.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! This is a strong typedef for representing the number of application frames since application start.
|
||||
AZ_TYPE_SAFE_INTEGRAL(ApplicationFrameId, uint32_t);
|
||||
static constexpr ApplicationFrameId InvalidApplicationFrameId = ApplicationFrameId{0xFFFFFFFF};
|
||||
|
||||
//! @class INetworkTime
|
||||
//! @brief This is an AZ::Interface<> for managing multiplayer specific time related operations.
|
||||
class INetworkTime
|
||||
@@ -31,30 +28,24 @@ namespace Multiplayer
|
||||
INetworkTime() = default;
|
||||
virtual ~INetworkTime() = default;
|
||||
|
||||
//! Converts from an ApplicationFrameId to a corresponding TimeMs.
|
||||
//! @param frameId the ApplicationFrameId to convert to a TimeMs
|
||||
//! @return the TimeMs that corresponds to the provided ApplicationFrameId
|
||||
virtual AZ::TimeMs ConvertFrameIdToTimeMs(ApplicationFrameId frameId) const = 0;
|
||||
//! Returns true if the host timeMs and frameId has been temporarily altered.
|
||||
//! @return true if the host timeMs and frameId has been altered, false otherwise
|
||||
virtual bool IsTimeRewound() const = 0;
|
||||
|
||||
//! Converts from a TimeMs to an ApplicationFrameId.
|
||||
//! @param timeMs the TimeMs to convert to an ApplicationFrameId
|
||||
//! @return the ApplicationFrameId that corresponds to the provided TimeMs
|
||||
virtual ApplicationFrameId ConvertTimeMsToFrameId(AZ::TimeMs timeMs) const = 0;
|
||||
//! Retrieves the hosts current frameId (may be rewound on the server during backward reconciliation).
|
||||
//! @return the hosts current frameId
|
||||
virtual HostFrameId GetHostFrameId() const = 0;
|
||||
|
||||
//! Returns true if the application frameId has been temporarily altered.
|
||||
//! @return true if the application frameId has been altered, false otherwise
|
||||
virtual bool IsApplicationFrameIdRewound() const = 0;
|
||||
//! Retrieves the unaltered hosts current frameId.
|
||||
//! @return the hosts current frameId, unaltered by any scoped time instance
|
||||
virtual HostFrameId GetUnalteredHostFrameId() const = 0;
|
||||
|
||||
//! Retrieves the applications current frameId (may be rewound on the server during backward reconciliation).
|
||||
//! @return the applications current frameId
|
||||
virtual ApplicationFrameId GetApplicationFrameId() const = 0;
|
||||
//! Increments the hosts current frameId.
|
||||
virtual void IncrementHostFrameId() = 0;
|
||||
|
||||
//! Retrieves the unaltered applications current frameId.
|
||||
//! @return the applications current frameId, unaltered by any scoped time instance
|
||||
virtual ApplicationFrameId GetUnalteredApplicationFrameId() const = 0;
|
||||
|
||||
//! Increments the applications current frameId.
|
||||
virtual void IncrementApplicationFrameId() = 0;
|
||||
//! Retrieves the hosts current timeMs (may be rewound on the server during backward reconciliation).
|
||||
//! @return the hosts current timeMs
|
||||
virtual AZ::TimeMs GetHostTimeMs() const = 0;
|
||||
|
||||
//! Synchronizes rewindable entity state for the current application time.
|
||||
virtual void SyncRewindableEntityState() = 0;
|
||||
@@ -66,14 +57,15 @@ namespace Multiplayer
|
||||
|
||||
//! 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
|
||||
//! @param rewindConnectionId if this parameter matches the current rewindConnectionId, it will return the unaltered applicationFrameId
|
||||
//! @return the ApplicationFrameId taking into account the provided rewinding connectionId
|
||||
virtual ApplicationFrameId GetApplicationFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const = 0;
|
||||
//! @param rewindConnectionId if this parameter matches the current rewindConnectionId, it will return the unaltered hostFrameId
|
||||
//! @return the HostFrameId taking into account the provided rewinding connectionId
|
||||
virtual HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const = 0;
|
||||
|
||||
//! Alters the current ApplicationFrameId and binds that alteration to the provided ConnectionId.
|
||||
//! @param frameId the new ApplicationFrameId to use
|
||||
//! Alters the current HostFrameId and binds that alteration to the provided ConnectionId.
|
||||
//! @param frameId the new HostFrameId to use
|
||||
//! @param timeMs the new HostTimeMs to use
|
||||
//! @param rewindConnectionId the rewinding ConnectionId
|
||||
virtual void AlterApplicationFrameId(ApplicationFrameId frameId, AzNetworking::ConnectionId rewindConnectionId) = 0;
|
||||
virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) = 0;
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(INetworkTime);
|
||||
};
|
||||
@@ -93,22 +85,22 @@ namespace Multiplayer
|
||||
class ScopedAlterTime final
|
||||
{
|
||||
public:
|
||||
inline ScopedAlterTime(ApplicationFrameId frameId, AzNetworking::ConnectionId connectionId)
|
||||
inline ScopedAlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
INetworkTime* time = AZ::Interface<INetworkTime>::Get();
|
||||
m_previousApplicationFrameId = time->GetApplicationFrameId();
|
||||
m_previousHostFrameId = time->GetHostFrameId();
|
||||
m_previousHostTimeMs = time->GetHostTimeMs();
|
||||
m_previousRewindConnectionId = time->GetRewindingConnectionId();
|
||||
time->AlterApplicationFrameId(frameId, connectionId);
|
||||
time->AlterTime(frameId, timeMs, connectionId);
|
||||
}
|
||||
inline ~ScopedAlterTime()
|
||||
{
|
||||
INetworkTime* time = AZ::Interface<INetworkTime>::Get();
|
||||
time->AlterApplicationFrameId(m_previousApplicationFrameId, m_previousRewindConnectionId);
|
||||
time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId);
|
||||
}
|
||||
private:
|
||||
ApplicationFrameId m_previousApplicationFrameId = InvalidApplicationFrameId;
|
||||
HostFrameId m_previousHostFrameId = InvalidHostFrameId;
|
||||
AZ::TimeMs m_previousHostTimeMs = AZ::TimeMs{ 0 };
|
||||
AzNetworking::ConnectionId m_previousRewindConnectionId = AzNetworking::InvalidConnectionId;
|
||||
};
|
||||
}
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::ApplicationFrameId);
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Include/MultiplayerTypes.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
|
||||
namespace Multiplayer
|
||||
@@ -36,6 +36,12 @@ namespace Multiplayer
|
||||
AZ_TYPE_SAFE_INTEGRAL(PropertyIndex, uint16_t);
|
||||
AZ_TYPE_SAFE_INTEGRAL(RpcIndex, uint16_t);
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL(ClientInputId, uint16_t);
|
||||
|
||||
//! This is a strong typedef for representing the number of application frames since application start.
|
||||
AZ_TYPE_SAFE_INTEGRAL(HostFrameId, uint32_t);
|
||||
static constexpr HostFrameId InvalidHostFrameId = HostFrameId{ 0xFFFFFFFF };
|
||||
|
||||
using LongNetworkString = AZ::CVarFixedString;
|
||||
using ReliabilityType = AzNetworking::ReliabilityType;
|
||||
|
||||
@@ -122,3 +128,5 @@ AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetEntityId);
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetComponentId);
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::PropertyIndex);
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::RpcIndex);
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::ClientInputId);
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostFrameId);
|
||||
|
||||
+1
-1
@@ -138,4 +138,4 @@ namespace Multiplayer
|
||||
};
|
||||
}
|
||||
|
||||
#include "Source/NetworkEntity/NetworkEntityHandle.inl"
|
||||
#include <Include/NetworkEntityHandle.inl>
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <Source/Components/MultiplayerComponentRegistry.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
{% for Component in dataFiles %}
|
||||
{% set ComponentDerived = Component.attrib['OverrideComponent']|booleanTrue %}
|
||||
{% set ControllerDerived = Component.attrib['OverrideController']|booleanTrue %}
|
||||
|
||||
@@ -170,12 +170,12 @@ AZ::Event<{{ Property.attrib['Type'] }}>
|
||||
{% set PropertyName = UpperFirst(Property.attrib['Name']) %}
|
||||
{{ ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }}
|
||||
{% if IsOverride %}
|
||||
void Handle{{ PropertyName }}({{ ', '.join(paramDefines) }}) override {}
|
||||
void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ ', '.join(paramDefines) }}) override {}
|
||||
{% else %}
|
||||
//! {{ PropertyName }} Handler
|
||||
//! {{ Property.attrib['Description'] }}
|
||||
//! HandleOn {{ HandleOn }}
|
||||
virtual void Handle{{ PropertyName }}({{ ', '.join(paramDefines) }}) = 0;
|
||||
virtual void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ ', '.join(paramDefines) }}) = 0;
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{#
|
||||
|
||||
@@ -221,11 +221,11 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }
|
||||
#include <AzCore/EBus/Event.h>
|
||||
#include <AzCore/EBus/ScheduledEvent.h>
|
||||
#include <AzNetworking/DataStructures/FixedSizeBitsetView.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/IMultiplayerComponentInput.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/ReplicationRecord.h>
|
||||
#include <Source/Components/MultiplayerComponent.h>
|
||||
#include <Source/Components/MultiplayerController.h>
|
||||
#include <Source/NetworkInput/IMultiplayerComponentInput.h>
|
||||
#include <Source/NetworkTime/RewindableObject.h>
|
||||
#include <Include/MultiplayerTypes.h>
|
||||
{% call(Include) AutoComponentMacros.ParseIncludes(Component) %}
|
||||
@@ -435,7 +435,7 @@ namespace {{ Component.attrib['Namespace'] }}
|
||||
//! MultiplayerComponent interface
|
||||
//! @{
|
||||
NetComponentId GetNetComponentId() const override;
|
||||
bool HandleRpcMessage(Multiplayer::NetEntityRole remoteRole, Multiplayer::NetworkEntityRpcMessage& rpcMessage) override;
|
||||
bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, Multiplayer::NetEntityRole remoteRole, Multiplayer::NetworkEntityRpcMessage& rpcMessage) override;
|
||||
bool SerializeStateDeltaMessage(Multiplayer::ReplicationRecord& replicationRecord, AzNetworking::ISerializer& serializer) override;
|
||||
void NotifyStateDeltaChanges(Multiplayer::ReplicationRecord& replicationRecord) override;
|
||||
bool HasController() const override;
|
||||
|
||||
@@ -336,7 +336,7 @@ case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ Upp
|
||||
if (m_controller)
|
||||
{
|
||||
AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Authority, "Entity proxy does not have authority");
|
||||
m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }});
|
||||
m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }});
|
||||
}
|
||||
{% if Property.attrib['IsReliable']|booleanTrue %}
|
||||
{# if the rpc is not reliable we can simply drop it, also note message reliability type is default reliable in EntityRpcMessage #}
|
||||
@@ -350,7 +350,7 @@ case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ Upp
|
||||
if (m_controller)
|
||||
{
|
||||
AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Autonomous, "Entity proxy does not have autonomy");
|
||||
m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }});
|
||||
m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }});
|
||||
}
|
||||
{% else %}
|
||||
Handle{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }});
|
||||
@@ -1251,7 +1251,12 @@ namespace {{ Component.attrib['Namespace'] }}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4065) // switch statement contains 'default' but no 'case' labels
|
||||
bool {{ ComponentBaseName }}::HandleRpcMessage([[maybe_unused]] Multiplayer::NetEntityRole remoteRole, Multiplayer::NetworkEntityRpcMessage& message)
|
||||
bool {{ ComponentBaseName }}::HandleRpcMessage
|
||||
(
|
||||
[[maybe_unused]] AzNetworking::IConnection* invokingConnection,
|
||||
[[maybe_unused]] Multiplayer::NetEntityRole remoteRole,
|
||||
Multiplayer::NetworkEntityRpcMessage& message
|
||||
)
|
||||
{
|
||||
const {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure rpcType = static_cast<{{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure>(message.GetRpcIndex());
|
||||
switch (rpcType)
|
||||
|
||||
+2
-2
@@ -16,11 +16,11 @@
|
||||
<Include File="Source/NetworkInput/NetworkInputVector.h"/>
|
||||
<Include File="AzNetworking/DataStructures/ByteBuffer.h"/>
|
||||
|
||||
<NetworkProperty Type="Multiplayer::ClientInputId" Name="LastInputId" Init="Multiplayer::ClientInputId{0}" ReplicateFrom="Authority" ReplicateTo="Authority" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" GenerateEventBindings="false" />
|
||||
<NetworkProperty Type="Multiplayer::ClientInputId" Name="LastInputId" Init="Multiplayer::ClientInputId{ 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="AZ::HashValue64" Name="stateHash" />
|
||||
<Param Type="AzNetworking::PacketEncodingBuffer" Name="clientState" Description="This is for debugging desyncs only; release games should not populate this parameter" />
|
||||
</RemoteProcedure>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<PacketGroup Name="MultiplayerPackets" PacketStart="CorePackets::PacketType::MAX">
|
||||
<Include File="AzNetworking/AutoGen/CorePackets.AutoPackets.h" />
|
||||
<Include File="Include/MultiplayerTypes.h" />
|
||||
<Include File="Include/INetworkTime.h" />
|
||||
<Include File="Source/NetworkEntity/NetworkEntityRpcMessage.h" />
|
||||
<Include File="Source/NetworkEntity/NetworkEntityUpdateMessage.h" />
|
||||
|
||||
@@ -35,6 +36,7 @@
|
||||
|
||||
<Packet Name="EntityUpdates" Desc="A packet that contains multiple entity updates">
|
||||
<Member Type="AZ::TimeMs" Name="hostTimeMs" Init="AZ::TimeMs{ 0 }" />
|
||||
<Member Type="Multiplayer::HostFrameId" Name="hostFrameId" Init="Multiplayer::InvalidHostFrameId" />
|
||||
<Member Type="Multiplayer::NetworkEntityUpdateMessage" Name="entityMessages" Container="Vector" Count="Multiplayer::MaxAggregateEntityMessages" SuppressFromInitializerList="true" />
|
||||
</Packet>
|
||||
|
||||
|
||||
@@ -13,9 +13,41 @@
|
||||
#include <Source/Components/LocalPredictionPlayerInputComponent.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzNetworking/Serialization/HashSerializer.h>
|
||||
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
|
||||
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
|
||||
#include <AzNetworking/Serialization/StringifySerializer.h>
|
||||
#include <AzNetworking/Serialization/TrackChangedSerializer.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
AZ_CVAR(AZ::TimeMs, cl_InputRateMs, AZ::TimeMs{ 33 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Rate at which to sample and process client inputs");
|
||||
AZ_CVAR(AZ::TimeMs, cl_MaxRewindHistoryMs, AZ::TimeMs{ 2000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of milliseconds to keep for server correction rewind and replay");
|
||||
#ifndef _RELEASE
|
||||
AZ_CVAR(float, cl_DebugHackTimeMultiplier, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Scalar value used to simulate clock hacking cheats for validating bank time system and anticheat");
|
||||
#endif
|
||||
|
||||
AZ_CVAR(bool, sv_EnableCorrections, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables server corrections on autonomous proxy desyncs");
|
||||
AZ_CVAR(double, sv_MaxBankTimeWindowSec, 0.2, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum bank time we allow before we start rejecting autonomous proxy move inputs due to anticheat kicking in");
|
||||
AZ_CVAR(double, sv_BankTimeDecay, 0.025, nullptr, AZ::ConsoleFunctorFlags::Null, "Amount to decay bank time by, in case of more permanent shifts in client latency");
|
||||
AZ_CVAR(AZ::TimeMs, sv_MinCorrectionTimeMs, AZ::TimeMs{ 100 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum time to wait between sending out corrections in order to avoid flooding corrections on high-latency connections");
|
||||
AZ_CVAR(AZ::TimeMs, sv_InputUpdateTimeMs, AZ::TimeMs{ 5 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum time between component updates");
|
||||
|
||||
// Debug helper functions
|
||||
AZStd::string GetInputString(NetworkInput& input)
|
||||
{
|
||||
AzNetworking::StringifySerializer serializer(',', false);
|
||||
input.Serialize(serializer);
|
||||
return serializer.GetString();
|
||||
}
|
||||
|
||||
AZStd::string GetCorrectionDataString(NetBindComponent* netBindComponent)
|
||||
{
|
||||
AzNetworking::StringifySerializer serializer(',', false);
|
||||
netBindComponent->SerializeEntityCorrection(serializer);
|
||||
return serializer.GetString();
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponent::LocalPredictionPlayerInputComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
@@ -27,30 +59,544 @@ namespace Multiplayer
|
||||
LocalPredictionPlayerInputComponentBase::Reflect(context);
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponent::OnInit()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
LocalPredictionPlayerInputComponentController::LocalPredictionPlayerInputComponentController(LocalPredictionPlayerInputComponent& parent)
|
||||
: LocalPredictionPlayerInputComponentControllerBase(parent)
|
||||
, m_autonomousUpdateEvent([this]() { UpdateAutonomous(m_autonomousUpdateEvent.TimeInQueueMs()); }, AZ::Name("AutonomousUpdate Event"))
|
||||
, m_updateBankedTimeEvent([this]() { UpdateBankedTime(m_updateBankedTimeEvent.TimeInQueueMs()); }, AZ::Name("BankTimeUpdate Event"))
|
||||
, m_migrateStartHandler([this](ClientInputId migratedInputId) { OnMigrateStart(migratedInputId); })
|
||||
, m_migrateEndHandler([this]() { OnMigrateEnd(); })
|
||||
{
|
||||
if (GetNetEntityRole() == NetEntityRole::Autonomous)
|
||||
{
|
||||
m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true);
|
||||
parent.GetNetBindComponent()->AddEntityMigrationStartEventHandler(m_migrateStartHandler);
|
||||
parent.GetNetBindComponent()->AddEntityMigrationEndEventHandler(m_migrateEndHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
if (entityIsMigrating == EntityIsMigrating::True)
|
||||
{
|
||||
m_allowMigrateClientInput = true;
|
||||
m_serverMigrateFrameId = AZ::Interface<INetworkTime>::Get()->GetHostFrameId();
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::HandleSendClientInput
|
||||
(
|
||||
[[maybe_unused]] const Multiplayer::NetworkInputVector& inputArray,
|
||||
[[maybe_unused]] const uint32_t& stateHash,
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
const Multiplayer::NetworkInputVector& inputArray,
|
||||
const AZ::HashValue64& stateHash,
|
||||
[[maybe_unused]] const AzNetworking::PacketEncodingBuffer& clientState
|
||||
)
|
||||
{
|
||||
;
|
||||
// After receiving the first input from the client, start the update event to check for slow hacking
|
||||
if (!m_updateBankedTimeEvent.IsScheduled())
|
||||
{
|
||||
m_updateBankedTimeEvent.Enqueue(sv_InputUpdateTimeMs, true);
|
||||
}
|
||||
|
||||
if (invokingConnection == nullptr)
|
||||
{
|
||||
// Discard any input messages that were locally dispatched or sent by disconnected clients
|
||||
return;
|
||||
}
|
||||
|
||||
const AZ::TimeMs currentTimeMs = AZ::GetElapsedTimeMs();
|
||||
const double clientInputRateSec = static_cast<double>(static_cast<AZ::TimeMs>(cl_InputRateMs)) / 1000.0;
|
||||
m_lastInputReceivedTimeMs = currentTimeMs;
|
||||
|
||||
// Keep track of last inputs received, also allows us to update frame ids
|
||||
m_lastInputReceived = inputArray;
|
||||
|
||||
// Figure out which index from the input array we want
|
||||
// we start at the oldest input that has not been processed
|
||||
int32_t inputArrayIndex = -1;
|
||||
for (int32_t i = NetworkInputVector::MaxElements - 1; i >= 0; --i)
|
||||
{
|
||||
// Find an input that is newer than the last one we processed
|
||||
if (m_lastInputReceived[i].GetClientInputId() > GetLastInputId())
|
||||
{
|
||||
inputArrayIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputArrayIndex < 0)
|
||||
{
|
||||
AZLOG
|
||||
(
|
||||
NET_Prediction,
|
||||
"Discarding old or out of order move input (current: %u, received %u)",
|
||||
aznumeric_cast<uint32_t>(GetLastInputId()),
|
||||
aznumeric_cast<uint32_t>(m_lastInputReceived[0].GetClientInputId())
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
bool lostInput = false;
|
||||
if (GetLastInputId() < inputArray.GetPreviousInputId())
|
||||
{
|
||||
// last move id processed is older than the previous input id, we missed some input packets
|
||||
lostInput = true;
|
||||
}
|
||||
|
||||
SetLastInputId(m_lastInputReceived[0].GetClientInputId()); // Set this variable in case of migration
|
||||
|
||||
while (inputArrayIndex >= 0)
|
||||
{
|
||||
NetworkInput& input = m_lastInputReceived[inputArrayIndex];
|
||||
|
||||
// Anticheat, if we're receiving too many inputs, and fall outside our variable latency input window
|
||||
// Discard move input events, client may be speed hacking
|
||||
if (m_clientBankedTime < sv_MaxBankTimeWindowSec)
|
||||
{
|
||||
m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary
|
||||
|
||||
{
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId());
|
||||
GetNetBindComponent()->ProcessInput(input, static_cast<float>(clientInputRateSec));
|
||||
}
|
||||
if (lostInput)
|
||||
{
|
||||
AZLOG(NET_Prediction, "InputLost InputId=%u", input.GetClientInputId());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_Prediction, "Processed InputId=%u", input.GetClientInputId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_Prediction, "Dropped InputId=%u", input.GetClientInputId());
|
||||
}
|
||||
--inputArrayIndex;
|
||||
}
|
||||
|
||||
if (sv_EnableCorrections && (currentTimeMs - m_lastCorrectionSentTimeMs > sv_MinCorrectionTimeMs))
|
||||
{
|
||||
m_lastCorrectionSentTimeMs = currentTimeMs;
|
||||
|
||||
AzNetworking::HashSerializer hashSerializer;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(hashSerializer);
|
||||
|
||||
const AZ::HashValue64 localAuthorityHash = hashSerializer.GetHash();
|
||||
|
||||
if (stateHash != localAuthorityHash)
|
||||
{
|
||||
// Produce correction for client
|
||||
AzNetworking::PacketEncodingBuffer correction;
|
||||
correction.Resize(correction.GetCapacity());
|
||||
AzNetworking::NetworkInputSerializer serializer(correction.GetBuffer(), correction.GetCapacity());
|
||||
|
||||
// only deserialize if we have data (for client/server profile/debug mismatches)
|
||||
if (correction.GetSize() > 0)
|
||||
{
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serializer);
|
||||
}
|
||||
|
||||
correction.Resize(serializer.GetSize());
|
||||
|
||||
// Send correction
|
||||
SendClientInputCorrection(GetLastInputId(), correction);
|
||||
|
||||
#ifdef _DEBUG
|
||||
// In debug, show which states caused the correction
|
||||
AZStd::string clientStateString;
|
||||
AZStd::string serverStateString;
|
||||
{
|
||||
// Write in client state
|
||||
AzNetworking::NetworkOutputSerializer clientStateSerializer(clientState.GetBuffer(), clientState.GetSize());
|
||||
GetNetBindComponent()->SerializeEntityCorrection(clientStateSerializer);
|
||||
|
||||
// Read out state values
|
||||
AzNetworking::StringifySerializer clientValues;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(clientValues);
|
||||
|
||||
// Restore server state
|
||||
AzNetworking::NetworkOutputSerializer serverStateSerializer(correction.GetBuffer(), correction.GetSize());
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serverStateSerializer);
|
||||
|
||||
// Read out state values
|
||||
AzNetworking::StringifySerializer serverValues;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serverValues);
|
||||
|
||||
AZStd::map<AZStd::string, AZStd::pair<AZStd::string, AZStd::string>> mapComparison;
|
||||
// put the server value in the first part of the pair
|
||||
for (const auto& pair : serverValues.GetValueMap())
|
||||
{
|
||||
mapComparison[pair.first].first = pair.second;
|
||||
}
|
||||
// put the client value in the second part of the pair
|
||||
for (const auto& pair : clientValues.GetValueMap())
|
||||
{
|
||||
mapComparison[pair.first].second = pair.second;
|
||||
}
|
||||
|
||||
bool firstIt = true;
|
||||
for (const auto& mapPair : mapComparison)
|
||||
{
|
||||
if (mapPair.second.first != mapPair.second.second)
|
||||
{
|
||||
if (!firstIt)
|
||||
{
|
||||
clientStateString += ",";
|
||||
serverStateString += ",";
|
||||
}
|
||||
firstIt = false;
|
||||
|
||||
AZStd::string clientValue = mapPair.second.second.empty() ? "<no value>" : mapPair.second.second;
|
||||
AZStd::string serverValue = mapPair.second.first.empty() ? "<no value>" : mapPair.second.first;
|
||||
clientStateString += mapPair.first + "=" + clientValue;
|
||||
serverStateString += mapPair.first + "=" + serverValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
const AZStd::string clientStateString = "available in debug only";
|
||||
const AZStd::string serverStateString = "available in debug only";
|
||||
#endif
|
||||
|
||||
AZLOG_ERROR("** Autonomous proxy desync detected! ** clientState=[%s], serverState=[%s]", clientStateString.c_str(), serverStateString.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::HandleSendMigrateClientInput
|
||||
(
|
||||
[[maybe_unused]] const Multiplayer::MigrateNetworkInputVector& inputArray
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
const Multiplayer::MigrateNetworkInputVector& inputArray
|
||||
)
|
||||
{
|
||||
;
|
||||
if (!m_allowMigrateClientInput)
|
||||
{
|
||||
AZLOG_ERROR("Client attempting to SendMigrateClientInput message when server was not expecting it. This may be an attempt to cheat");
|
||||
return;
|
||||
}
|
||||
|
||||
// We only allow the client to send this message exactly once, when the component has been migrated
|
||||
// Any further processing of these messages from the client would be exploitable
|
||||
m_allowMigrateClientInput = false;
|
||||
|
||||
if (invokingConnection == nullptr)
|
||||
{
|
||||
// Discard any input migration messages that were locally dispatched or sent by disconnected clients
|
||||
return;
|
||||
}
|
||||
|
||||
const float clientInputRateSec = static_cast<float>(static_cast<AZ::TimeMs>(cl_InputRateMs)) / 1000.0;
|
||||
|
||||
// Copy array so we can modify input ids
|
||||
MigrateNetworkInputVector inputArrayCopy = inputArray;
|
||||
|
||||
for (uint32_t i = 0; i < inputArrayCopy.GetSize(); ++i)
|
||||
{
|
||||
NetworkInput& input = inputArrayCopy[i];
|
||||
|
||||
++ModifyLastInputId();
|
||||
input.SetClientInputId(GetLastInputId());
|
||||
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId());
|
||||
GetNetBindComponent()->ProcessInput(input, clientInputRateSec);
|
||||
|
||||
AZLOG
|
||||
(
|
||||
NET_Prediction,
|
||||
"Migrated InputId=%d - i=[%s] o=[%s]",
|
||||
aznumeric_cast<int32_t>(input.GetClientInputId()),
|
||||
GetInputString(input).c_str(),
|
||||
GetCorrectionDataString(GetNetBindComponent()).c_str()
|
||||
);
|
||||
|
||||
// Don't bother checking for corrections here, the next regular input will trigger any corrections if necessary
|
||||
// Also don't bother with any cheat detection here, because the input array is limited in size and at most and can only be sent once
|
||||
// So this highly constrains anything a malicious client can do
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::HandleSendClientInputCorrection
|
||||
(
|
||||
[[maybe_unused]] const Multiplayer::ClientInputId& inputId,
|
||||
[[maybe_unused]] const AzNetworking::PacketEncodingBuffer& correction
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
const Multiplayer::ClientInputId& inputId,
|
||||
const AzNetworking::PacketEncodingBuffer& correction
|
||||
)
|
||||
{
|
||||
;
|
||||
AZ_Assert(inputId <= m_clientInputId, "Invalid correction frame id, correction is for a move the client has not yet submitted to the server");
|
||||
if (inputId > m_clientInputId)
|
||||
{
|
||||
AZLOG_ERROR("Discarding correction for non-existent move, correction represents a move we haven't sent to the server yet");
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputId <= m_lastCorrectionInputId)
|
||||
{
|
||||
AZLOG(NET_Prediction, "Discarding old correction for client frame %u", aznumeric_cast<uint32_t>(inputId));
|
||||
return;
|
||||
}
|
||||
|
||||
m_lastCorrectionInputId = inputId;
|
||||
|
||||
// Apply the correction
|
||||
AzNetworking::TrackChangedSerializer<AzNetworking::NetworkOutputSerializer> serializer(correction.GetBuffer(), correction.GetSize());
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serializer);
|
||||
m_correctionEvent.Signal();
|
||||
|
||||
AZLOG
|
||||
(
|
||||
NET_Prediction,
|
||||
"Corrected InputId=%d - o=[%s]",
|
||||
aznumeric_cast<int32_t>(m_lastCorrectionInputId),
|
||||
GetCorrectionDataString(GetNetBindComponent()).c_str()
|
||||
);
|
||||
|
||||
const uint32_t inputHistorySize = m_inputHistory.Size();
|
||||
const uint32_t historicalDelta = aznumeric_cast<uint32_t>(m_clientInputId - inputId); // Do not replay the move we just corrected, that was already processed by the server
|
||||
|
||||
// If this correction is for a move outside our input history window, just start replaying from the oldest move we have available
|
||||
const uint32_t startReplayIndex = (inputHistorySize > historicalDelta) ? (inputHistorySize - historicalDelta) : 0;
|
||||
|
||||
// Flag that we are replaying inputs
|
||||
struct ScopedReplayingInput
|
||||
{
|
||||
ScopedReplayingInput(LocalPredictionPlayerInputComponentController* instance)
|
||||
: m_instance(instance)
|
||||
{
|
||||
m_instance->m_replayingInput = true;
|
||||
}
|
||||
~ScopedReplayingInput()
|
||||
{
|
||||
m_instance->m_replayingInput = false;
|
||||
}
|
||||
LocalPredictionPlayerInputComponentController* m_instance;
|
||||
};
|
||||
ScopedReplayingInput markReplayingInput(this);
|
||||
|
||||
const float clientInputRateSec = static_cast<float>(static_cast<AZ::TimeMs>(cl_InputRateMs)) / 1000.0;
|
||||
for (uint32_t replayIndex = startReplayIndex; replayIndex < inputHistorySize; ++replayIndex)
|
||||
{
|
||||
// Reprocess the input for this frame
|
||||
NetworkInput& input = m_inputHistory[replayIndex];
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId());
|
||||
GetNetBindComponent()->ProcessInput(input, clientInputRateSec);
|
||||
|
||||
AZLOG
|
||||
(
|
||||
NET_Prediction,
|
||||
"Replayed InputId=%d - i=[%s] o=[%s]",
|
||||
aznumeric_cast<int32_t>(input.GetClientInputId()),
|
||||
GetInputString(input).c_str(),
|
||||
GetCorrectionDataString(GetNetBindComponent()).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalPredictionPlayerInputComponentController::IsReplayingInput() const
|
||||
{
|
||||
return m_replayingInput;
|
||||
}
|
||||
|
||||
bool LocalPredictionPlayerInputComponentController::IsMigrating() const
|
||||
{
|
||||
return m_lastMigratedInputId != ClientInputId{ 0 };
|
||||
}
|
||||
|
||||
ClientInputId LocalPredictionPlayerInputComponentController::GetLastInputId() const
|
||||
{
|
||||
return m_clientInputId;
|
||||
}
|
||||
|
||||
HostFrameId LocalPredictionPlayerInputComponentController::GetInputFrameId(const NetworkInput& input) const
|
||||
{
|
||||
// If the client has sent us an invalid server frame id
|
||||
// this is because they are in the process of migrating from one server to another
|
||||
// In this situation, use whatever the server frame id was when this component was migrated
|
||||
// This will match the closest state to what the client sees
|
||||
return (input.GetHostFrameId() == InvalidHostFrameId) ? m_serverMigrateFrameId : input.GetHostFrameId();
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::CorrectionEventAddHandle(CorrectionEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_correctionEvent);
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::OnMigrateStart(ClientInputId migratedInputId)
|
||||
{
|
||||
m_lastMigratedInputId = migratedInputId;
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::OnMigrateEnd()
|
||||
{
|
||||
MigrateNetworkInputVector inputArray;
|
||||
|
||||
// Roll up all inputs that the new server doesn't have and send them now
|
||||
for (AZStd::size_t i = 0; i < m_inputHistory.Size(); ++i)
|
||||
{
|
||||
NetworkInput& input = m_inputHistory[i];
|
||||
|
||||
// New server already has these inputs
|
||||
if (input.GetClientInputId() <= m_lastMigratedInputId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Clear out the old server frame id
|
||||
// We don't know what server frame ids to use for the new server yet, but the new server will figure out how to deal with this
|
||||
input.SetHostFrameId(InvalidHostFrameId);
|
||||
|
||||
// New server doesn't have these inputs
|
||||
if (!inputArray.PushBack(input))
|
||||
{
|
||||
break; // Reached capacity
|
||||
}
|
||||
}
|
||||
|
||||
// Send these inputs to the server
|
||||
SendMigrateClientInput(inputArray);
|
||||
|
||||
// Done migrating
|
||||
m_lastMigratedInputId = ClientInputId{ 0 };
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::UpdateAutonomous(AZ::TimeMs deltaTimeMs)
|
||||
{
|
||||
const double deltaTime = static_cast<double>(deltaTimeMs) / 1000.0;
|
||||
const double inputRate = static_cast<double>(static_cast<AZ::TimeMs>(cl_InputRateMs)) / 1000.0;
|
||||
const double maxRewindHistory = static_cast<double>(static_cast<AZ::TimeMs>(cl_MaxRewindHistoryMs)) / 1000.0;
|
||||
|
||||
#ifndef _RELEASE
|
||||
m_moveAccumulator += deltaTime * cl_DebugHackTimeMultiplier;
|
||||
#else
|
||||
m_moveAccumulator += deltaTime;
|
||||
#endif
|
||||
|
||||
const uint32_t maxClientInputs = inputRate > 0.0 ? static_cast<uint32_t>(maxRewindHistory / inputRate) : 0;
|
||||
|
||||
INetworkTime* networkTime = AZ::Interface<INetworkTime>::Get();
|
||||
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
while (m_moveAccumulator >= inputRate)
|
||||
{
|
||||
m_moveAccumulator -= inputRate;
|
||||
++m_clientInputId;
|
||||
|
||||
NetworkInputVector inputArray(GetEntityHandle());
|
||||
NetworkInput& input = inputArray[0];
|
||||
|
||||
input.SetClientInputId(m_clientInputId);
|
||||
input.SetHostFrameId(networkTime->GetHostFrameId());
|
||||
input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs());
|
||||
|
||||
// Allow components to form the input for this frame
|
||||
GetNetBindComponent()->CreateInput(input, inputRate);
|
||||
|
||||
// Process the input for this frame
|
||||
GetNetBindComponent()->ProcessInput(input, inputRate);
|
||||
|
||||
AZLOG
|
||||
(
|
||||
NET_Prediction,
|
||||
"Processed InutId=%d - i=[%s] o=[%s]",
|
||||
aznumeric_cast<int32_t>(m_clientInputId),
|
||||
GetInputString(input).c_str(),
|
||||
GetCorrectionDataString(GetNetBindComponent()).c_str()
|
||||
);
|
||||
|
||||
// Generate a hash based on the current client predicted states
|
||||
AzNetworking::HashSerializer hashSerializer;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(hashSerializer);
|
||||
|
||||
// In debug, send the entire client output state to the server to make it easier to debug desync issues
|
||||
AzNetworking::PacketEncodingBuffer processInputResult;
|
||||
#ifdef _DEBUG
|
||||
AzNetworking::NetworkInputSerializer processInputResultSerializer(processInputResult.GetBuffer(), processInputResult.GetCapacity());
|
||||
GetNetBindComponent()->SerializeEntityCorrection(processInputResultSerializer);
|
||||
processInputResult.Resize(processInputResultSerializer.GetSize());
|
||||
#endif
|
||||
|
||||
// Save this input and discard move history outside our client rewind window
|
||||
m_inputHistory.PushBack(input);
|
||||
while (m_inputHistory.Size() > maxClientInputs)
|
||||
{
|
||||
m_inputHistory.PopFront();
|
||||
}
|
||||
|
||||
const size_t inputHistorySize = m_inputHistory.Size();
|
||||
|
||||
// Form the rest of the input array using the n most recent elements in the history buffer
|
||||
// NOTE: inputArray[0] has already been initialized hence start at i = 1
|
||||
for (uint32_t i = 1; i < NetworkInputVector::MaxElements; ++i)
|
||||
{
|
||||
if (i < inputHistorySize)
|
||||
{
|
||||
inputArray[i] = m_inputHistory[inputHistorySize - 1 - i];
|
||||
}
|
||||
else // History is too small?
|
||||
{
|
||||
// Plug in the most recent input
|
||||
inputArray[i] = input;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the input to server (only when we are not migrating)
|
||||
if (!IsMigrating())
|
||||
{
|
||||
SendClientInput(inputArray, hashSerializer.GetHash(), processInputResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::UpdateBankedTime(AZ::TimeMs deltaTimeMs)
|
||||
{
|
||||
const double deltaTime = static_cast<double>(deltaTimeMs) / 1000.0;
|
||||
const double inputRate = static_cast<double>(static_cast<AZ::TimeMs>(cl_InputRateMs)) / 1000.0;
|
||||
const double maxRewindHistory = static_cast<double>(static_cast<AZ::TimeMs>(cl_MaxRewindHistoryMs)) / 1000.0;
|
||||
|
||||
// Update banked time accumulator
|
||||
m_clientBankedTime -= deltaTime;
|
||||
|
||||
// Forcibly tick any clients who are too far behind our variable latency window
|
||||
// Client may be slow hacking
|
||||
if (m_clientBankedTime < -sv_MaxBankTimeWindowSec)
|
||||
{
|
||||
m_clientBankedTime = -sv_MaxBankTimeWindowSec; // clamp to boundary
|
||||
|
||||
NetworkInput& input = m_lastInputReceived[0];
|
||||
{
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), AzNetworking::InvalidConnectionId);
|
||||
GetNetBindComponent()->ProcessInput(input, inputRate);
|
||||
}
|
||||
|
||||
AZLOG
|
||||
(
|
||||
NET_Prediction,
|
||||
"Forced InputId=%d - i=[%s] o=[%s]",
|
||||
aznumeric_cast<int32_t>(input.GetClientInputId()),
|
||||
GetInputString(input).c_str(),
|
||||
GetCorrectionDataString(GetNetBindComponent()).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
// Decay our bank time window, in case the remote endpoint has suffered a more persistent shift in latency, this should cause the client to eventually recover
|
||||
m_clientBankedTime = m_clientBankedTime * (1.0 - sv_BankTimeDecay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.h>
|
||||
#include <Source/Components/NetBindComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
using CorrectionEvent = AZ::Event<>;
|
||||
|
||||
class LocalPredictionPlayerInputComponent
|
||||
: public LocalPredictionPlayerInputComponentBase
|
||||
{
|
||||
@@ -24,24 +27,87 @@ namespace Multiplayer
|
||||
|
||||
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 {}
|
||||
|
||||
|
||||
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) {}
|
||||
LocalPredictionPlayerInputComponentController(LocalPredictionPlayerInputComponent& parent);
|
||||
|
||||
void OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
|
||||
void OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
|
||||
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::ClientInputId& inputId, const AzNetworking::PacketEncodingBuffer& correction) override;
|
||||
void HandleSendClientInput
|
||||
(
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
const Multiplayer::NetworkInputVector& inputArray,
|
||||
const AZ::HashValue64& stateHash,
|
||||
const AzNetworking::PacketEncodingBuffer& clientState
|
||||
) override;
|
||||
|
||||
void HandleSendMigrateClientInput
|
||||
(
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
const Multiplayer::MigrateNetworkInputVector& inputArray
|
||||
) override;
|
||||
|
||||
void HandleSendClientInputCorrection
|
||||
(
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
const Multiplayer::ClientInputId& inputId,
|
||||
const AzNetworking::PacketEncodingBuffer& correction
|
||||
) override;
|
||||
|
||||
//! Return true if we're currently replaying inputs after a correction.
|
||||
//! If this value returns true, effects, audio, and other cosmetic triggers should be suppressed
|
||||
//! @return true if we're within correction scope and replaying inputs
|
||||
bool IsReplayingInput() const;
|
||||
|
||||
//! Return true if we're currently migrating from one host to another.
|
||||
//! @return boolean true if we're currently migrating from one host to another
|
||||
bool IsMigrating() const;
|
||||
|
||||
ClientInputId GetLastInputId() const;
|
||||
HostFrameId GetInputFrameId(const NetworkInput& input) const;
|
||||
|
||||
void CorrectionEventAddHandle(CorrectionEvent::Handler& handler);
|
||||
|
||||
private:
|
||||
|
||||
void OnMigrateStart(ClientInputId migratedInputId);
|
||||
void OnMigrateEnd();
|
||||
void UpdateAutonomous(AZ::TimeMs deltaTimeMs);
|
||||
void UpdateBankedTime(AZ::TimeMs deltaTimeMs);
|
||||
|
||||
// Implicitly sorted player input history, back() is the input that corresponds to the latest client input Id
|
||||
NetworkInputHistory m_inputHistory;
|
||||
|
||||
// Anti-cheat accumulator for clients who purposely mess with their clock rate
|
||||
NetworkInputVector m_lastInputReceived;
|
||||
|
||||
AZ::ScheduledEvent m_autonomousUpdateEvent; // Drives autonomous input collection
|
||||
AZ::ScheduledEvent m_updateBankedTimeEvent; // Drives authority bank time updates
|
||||
|
||||
CorrectionEvent m_correctionEvent;
|
||||
EntityMigrationStartEvent::Handler m_migrateStartHandler;
|
||||
EntityMigrationEndEvent::Handler m_migrateEndHandler;
|
||||
|
||||
double m_moveAccumulator = 0.0;
|
||||
double m_clientBankedTime = 0.0;
|
||||
|
||||
AZ::TimeMs m_lastInputReceivedTimeMs = AZ::TimeMs{ 0 };
|
||||
AZ::TimeMs m_lastCorrectionSentTimeMs = AZ::TimeMs{ 0 };
|
||||
|
||||
ClientInputId m_clientInputId = ClientInputId{ 0 };
|
||||
ClientInputId m_lastCorrectionInputId = ClientInputId{ 0 };
|
||||
ClientInputId m_lastMigratedInputId = ClientInputId{ 0 }; // Used to resend inputs that were queued during a migration event
|
||||
HostFrameId m_serverMigrateFrameId = InvalidHostFrameId;
|
||||
|
||||
bool m_replayingInput = false; // True if we're replaying inputs under a correction event (use this to suppress effects or audio)
|
||||
bool m_allowMigrateClientInput = false; // True if this component was migrated, we will allow the client to send us migrated inputs (one time only)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzNetworking/DataStructures/FixedSizeBitsetView.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <Include/MultiplayerTypes.h>
|
||||
#include <Include/IMultiplayer.h>
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Multiplayer
|
||||
|
||||
virtual NetComponentId GetNetComponentId() const = 0;
|
||||
|
||||
virtual bool HandleRpcMessage(NetEntityRole netEntityRole, NetworkEntityRpcMessage& rpcMessage) = 0;
|
||||
virtual bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetEntityRole netEntityRole, NetworkEntityRpcMessage& rpcMessage) = 0;
|
||||
virtual bool SerializeStateDeltaMessage(ReplicationRecord& replicationRecord, AzNetworking::ISerializer& serializer) = 0;
|
||||
virtual void NotifyStateDeltaChanges(ReplicationRecord& replicationRecord) = 0;
|
||||
virtual bool HasController() const = 0;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
|
||||
namespace Multiplayer
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
#include <Source/Components/NetBindComponent.h>
|
||||
#include <Source/Components/MultiplayerComponent.h>
|
||||
#include <Source/Components/MultiplayerController.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityUpdateMessage.h>
|
||||
#include <Source/NetworkInput/NetworkInput.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
@@ -192,12 +192,12 @@ namespace Multiplayer
|
||||
return bounds;
|
||||
}
|
||||
|
||||
bool NetBindComponent::HandleRpcMessage(NetEntityRole remoteRole, NetworkEntityRpcMessage& message)
|
||||
bool NetBindComponent::HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetEntityRole remoteRole, NetworkEntityRpcMessage& message)
|
||||
{
|
||||
auto findIt = m_multiplayerComponentMap.find(message.GetComponentId());
|
||||
if (findIt != m_multiplayerComponentMap.end())
|
||||
{
|
||||
return findIt->second->HandleRpcMessage(remoteRole, message);
|
||||
return findIt->second->HandleRpcMessage(invokingConnection, remoteRole, message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -274,9 +274,19 @@ namespace Multiplayer
|
||||
m_localNotificationRecord.Clear();
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyMigration(HostId remoteHostId, AzNetworking::ConnectionId connectionId)
|
||||
void NetBindComponent::NotifyMigrationStart(ClientInputId migratedInputId)
|
||||
{
|
||||
m_entityMigrationEvent.Signal(m_netEntityHandle, remoteHostId, connectionId);
|
||||
m_entityMigrationStartEvent.Signal(migratedInputId);
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyMigrationEnd()
|
||||
{
|
||||
m_entityMigrationEndEvent.Signal();
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId);
|
||||
}
|
||||
|
||||
void NetBindComponent::AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler)
|
||||
@@ -289,9 +299,19 @@ namespace Multiplayer
|
||||
eventHandler.Connect(m_dirtiedEvent);
|
||||
}
|
||||
|
||||
void NetBindComponent::AddEntityMigrationEventHandler(EntityMigrationEvent::Handler& eventHandler)
|
||||
void NetBindComponent::AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler)
|
||||
{
|
||||
eventHandler.Connect(m_entityMigrationEvent);
|
||||
eventHandler.Connect(m_entityMigrationStartEvent);
|
||||
}
|
||||
|
||||
void NetBindComponent::AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler)
|
||||
{
|
||||
eventHandler.Connect(m_entityMigrationEndEvent);
|
||||
}
|
||||
|
||||
void NetBindComponent::AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler)
|
||||
{
|
||||
eventHandler.Connect(m_entityServerMigrationEvent);
|
||||
}
|
||||
|
||||
bool NetBindComponent::SerializeEntityCorrection(AzNetworking::ISerializer& serializer)
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/ReplicationRecord.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Source/NetworkInput/IMultiplayerComponentInput.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <Include/IMultiplayerComponentInput.h>
|
||||
#include <Include/INetworkTime.h>
|
||||
#include <Include/MultiplayerTypes.h>
|
||||
#include <AzCore/EBus/Event.h>
|
||||
|
||||
@@ -34,7 +35,9 @@ namespace Multiplayer
|
||||
|
||||
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
|
||||
using EntityDirtiedEvent = AZ::Event<>;
|
||||
using EntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
|
||||
using EntityMigrationStartEvent = AZ::Event<ClientInputId>;
|
||||
using EntityMigrationEndEvent = AZ::Event<>;
|
||||
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
|
||||
|
||||
//! @class NetBindComponent
|
||||
//! @brief Component that provides net-binding to a networked entity.
|
||||
@@ -72,7 +75,7 @@ namespace Multiplayer
|
||||
void ProcessInput(NetworkInput& networkInput, float deltaTime);
|
||||
AZ::Aabb GetRewindBoundsForInput(const NetworkInput& networkInput, float deltaTime) const;
|
||||
|
||||
bool HandleRpcMessage(NetEntityRole remoteRole, NetworkEntityRpcMessage& message);
|
||||
bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetEntityRole remoteRole, NetworkEntityRpcMessage& message);
|
||||
bool HandlePropertyChangeMessage(AzNetworking::ISerializer& serializer, bool notifyChanges = true);
|
||||
|
||||
RpcSendEvent& GetSendAuthorityToClientRpcEvent();
|
||||
@@ -84,11 +87,15 @@ namespace Multiplayer
|
||||
|
||||
void MarkDirty();
|
||||
void NotifyLocalChanges();
|
||||
void NotifyMigration(HostId remoteHostId, AzNetworking::ConnectionId connectionId);
|
||||
void NotifyMigrationStart(ClientInputId migratedInputId);
|
||||
void NotifyMigrationEnd();
|
||||
void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId);
|
||||
|
||||
void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler);
|
||||
void AddEntityDirtiedEventHandler(EntityDirtiedEvent::Handler& eventHandler);
|
||||
void AddEntityMigrationEventHandler(EntityMigrationEvent::Handler& eventHandler);
|
||||
void AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler);
|
||||
void AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler);
|
||||
void AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler);
|
||||
|
||||
bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer);
|
||||
|
||||
@@ -133,7 +140,9 @@ namespace Multiplayer
|
||||
|
||||
EntityStopEvent m_entityStopEvent;
|
||||
EntityDirtiedEvent m_dirtiedEvent;
|
||||
EntityMigrationEvent m_entityMigrationEvent;
|
||||
EntityMigrationStartEvent m_entityMigrationStartEvent;
|
||||
EntityMigrationEndEvent m_entityMigrationEndEvent;
|
||||
EntityServerMigrationEvent m_entityServerMigrationEvent;
|
||||
AZ::Event<> m_onRemove;
|
||||
RpcSendEvent::Handler m_handleLocalServerRpcMessageEventHandle;
|
||||
AZ::Event<>::Handler m_handleMarkedDirty;
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Multiplayer
|
||||
return m_entityReplicationManager;
|
||||
}
|
||||
|
||||
void ClientToServerConnectionData::Update([[maybe_unused]] AZ::TimeMs serverGameTimeMs)
|
||||
void ClientToServerConnectionData::Update([[maybe_unused]] AZ::TimeMs hostTimeMs)
|
||||
{
|
||||
m_entityReplicationManager.ActivatePendingEntities();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/ConnectionData/IConnectionData.h>
|
||||
#include <Include/IConnectionData.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -32,7 +33,7 @@ namespace Multiplayer
|
||||
ConnectionDataType GetConnectionDataType() const override;
|
||||
AzNetworking::IConnection* GetConnection() const override;
|
||||
EntityReplicationManager& GetReplicationManager() override;
|
||||
void Update(AZ::TimeMs serverGameTimeMs) override;
|
||||
void Update(AZ::TimeMs hostTimeMs) override;
|
||||
bool CanSendUpdates() const override;
|
||||
void SetCanSendUpdates(bool canSendUpdates) override;
|
||||
//! @}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Multiplayer
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler);
|
||||
netBindComponent->AddEntityMigrationEventHandler(m_controlledEntityMigrationHandler);
|
||||
netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler);
|
||||
}
|
||||
|
||||
m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(sv_ClientMaxRemoteEntitiesPendingCreationCount);
|
||||
@@ -63,7 +63,7 @@ namespace Multiplayer
|
||||
return m_entityReplicationManager;
|
||||
}
|
||||
|
||||
void ServerToClientConnectionData::Update(AZ::TimeMs serverGameTimeMs)
|
||||
void ServerToClientConnectionData::Update(AZ::TimeMs hostTimeMs)
|
||||
{
|
||||
m_entityReplicationManager.ActivatePendingEntities();
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Multiplayer
|
||||
// potentially false if we just migrated the player, if that is the case, don't send any more updates
|
||||
if (netBindComponent != nullptr && (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority))
|
||||
{
|
||||
m_entityReplicationManager.SendUpdates(serverGameTimeMs);
|
||||
m_entityReplicationManager.SendUpdates(hostTimeMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/ConnectionData/IConnectionData.h>
|
||||
#include <Include/IConnectionData.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -33,7 +34,7 @@ namespace Multiplayer
|
||||
ConnectionDataType GetConnectionDataType() const override;
|
||||
AzNetworking::IConnection* GetConnection() const override;
|
||||
EntityReplicationManager& GetReplicationManager() override;
|
||||
void Update(AZ::TimeMs serverGameTimeMs) override;
|
||||
void Update(AZ::TimeMs hostTimeMs) override;
|
||||
bool CanSendUpdates() const override;
|
||||
void SetCanSendUpdates(bool canSendUpdates) override;
|
||||
//! @}
|
||||
@@ -49,7 +50,7 @@ namespace Multiplayer
|
||||
EntityReplicationManager m_entityReplicationManager;
|
||||
NetworkEntityHandle m_controlledEntity;
|
||||
EntityStopEvent::Handler m_controlledEntityRemovedHandler;
|
||||
EntityMigrationEvent::Handler m_controlledEntityMigrationHandler;
|
||||
EntityServerMigrationEvent::Handler m_controlledEntityMigrationHandler;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
bool m_canSendUpdates = false;
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/EntityDomains/IEntityDomain.h>
|
||||
#include <Include/IEntityDomain.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
AZ::TimeMs serverGameTimeMs = AZ::GetElapsedTimeMs();
|
||||
AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs();
|
||||
|
||||
// Handle deferred local rpc messages that were generated during the updates
|
||||
m_networkEntityManager.DispatchLocalDeferredRpcMessages();
|
||||
@@ -143,12 +143,12 @@ namespace Multiplayer
|
||||
|
||||
// Send out the game state update to all connections
|
||||
{
|
||||
auto sendNetworkUpdates = [serverGameTimeMs, &stats](IConnection& connection)
|
||||
auto sendNetworkUpdates = [hostTimeMs, &stats](IConnection& connection)
|
||||
{
|
||||
if (connection.GetUserData() != nullptr)
|
||||
{
|
||||
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection.GetUserData());
|
||||
connectionData->Update(serverGameTimeMs);
|
||||
connectionData->Update(hostTimeMs);
|
||||
if (connectionData->GetConnectionDataType() == ConnectionDataType::ServerToClient)
|
||||
{
|
||||
stats.m_clientConnectionCount++;
|
||||
@@ -481,7 +481,7 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
MultiplayerAgentType MultiplayerSystemComponent::GetAgentType()
|
||||
MultiplayerAgentType MultiplayerSystemComponent::GetAgentType() const
|
||||
{
|
||||
return m_agentType;
|
||||
}
|
||||
@@ -528,6 +528,18 @@ namespace Multiplayer
|
||||
});
|
||||
}
|
||||
|
||||
AZ::TimeMs MultiplayerSystemComponent::GetCurrentHostTimeMs() const
|
||||
{
|
||||
if (GetAgentType() == MultiplayerAgentType::Client)
|
||||
{
|
||||
return m_lastReplicatedHostTimeMs;
|
||||
}
|
||||
else // ClientServer or DedicatedServer
|
||||
{
|
||||
return m_networkTime.GetHostTimeMs();
|
||||
}
|
||||
}
|
||||
|
||||
const char* MultiplayerSystemComponent::GetComponentGemName(NetComponentId netComponentId) const
|
||||
{
|
||||
return GetMultiplayerComponentRegistry()->GetComponentGemName(netComponentId);
|
||||
|
||||
@@ -84,12 +84,13 @@ namespace Multiplayer
|
||||
|
||||
//! IMultiplayer interface
|
||||
//! @{
|
||||
MultiplayerAgentType GetAgentType() override;
|
||||
MultiplayerAgentType GetAgentType() const override;
|
||||
void InitializeMultiplayer(MultiplayerAgentType state) override;
|
||||
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
|
||||
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
|
||||
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
|
||||
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
|
||||
AZ::TimeMs GetCurrentHostTimeMs() const override;
|
||||
const char* GetComponentGemName(NetComponentId netComponentId) const override;
|
||||
const char* GetComponentName(NetComponentId netComponentId) const override;
|
||||
const char* GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const override;
|
||||
@@ -119,5 +120,8 @@ namespace Multiplayer
|
||||
SessionInitEvent m_initEvent;
|
||||
SessionShutdownEvent m_shutdownEvent;
|
||||
ConnectionAcquiredEvent m_connAcquiredEvent;
|
||||
|
||||
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
|
||||
HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId;
|
||||
};
|
||||
}
|
||||
|
||||
+16
-18
@@ -14,14 +14,14 @@
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
|
||||
#include <Source/ReplicationWindows/IReplicationWindow.h>
|
||||
#include <Source/EntityDomains/IEntityDomain.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityUpdateMessage.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Source/Components/NetBindComponent.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Include/IEntityDomain.h>
|
||||
#include <Include/IMultiplayer.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <Include/IReplicationWindow.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnectionListener.h>
|
||||
#include <AzNetworking/PacketLayer/IPacketHeader.h>
|
||||
@@ -93,10 +93,10 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SendUpdates(AZ::TimeMs serverGameTimeMs)
|
||||
void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs)
|
||||
{
|
||||
m_frameTimeMs = AZ::GetElapsedTimeMs();
|
||||
SendEntityUpdates(serverGameTimeMs);
|
||||
SendEntityUpdates(hostTimeMs);
|
||||
|
||||
SendEntityRpcs(m_deferredRpcMessagesReliable, true);
|
||||
SendEntityRpcs(m_deferredRpcMessagesUnreliable, false);
|
||||
@@ -118,7 +118,7 @@ namespace Multiplayer
|
||||
|
||||
void EntityReplicationManager::SendEntityUpdatesPacketHelper
|
||||
(
|
||||
AZ::TimeMs serverGameTimeMs,
|
||||
AZ::TimeMs hostTimeMs,
|
||||
EntityReplicatorList& toSendList,
|
||||
uint32_t maxPayloadSize,
|
||||
AzNetworking::IConnection& connection
|
||||
@@ -127,7 +127,8 @@ namespace Multiplayer
|
||||
uint32_t pendingPacketSize = 0;
|
||||
EntityReplicatorList replicatorUpdatedList;
|
||||
MultiplayerPackets::EntityUpdates entityUpdatePacket;
|
||||
entityUpdatePacket.SetHostTimeMs(serverGameTimeMs);
|
||||
entityUpdatePacket.SetHostTimeMs(hostTimeMs);
|
||||
entityUpdatePacket.SetHostFrameId(InvalidHostFrameId);
|
||||
// Serialize everything
|
||||
while (!toSendList.empty())
|
||||
{
|
||||
@@ -249,7 +250,7 @@ namespace Multiplayer
|
||||
return toSendList;
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs serverGameTimeMs)
|
||||
void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs)
|
||||
{
|
||||
EntityReplicatorList toSendList = GenerateEntityUpdateList();
|
||||
|
||||
@@ -264,7 +265,7 @@ namespace Multiplayer
|
||||
// While our to send list is not empty, build up another packet to send
|
||||
do
|
||||
{
|
||||
SendEntityUpdatesPacketHelper(serverGameTimeMs, toSendList, m_maxPayloadSize, m_connection);
|
||||
SendEntityUpdatesPacketHelper(hostTimeMs, toSendList, m_maxPayloadSize, m_connection);
|
||||
} while (!toSendList.empty());
|
||||
}
|
||||
|
||||
@@ -747,7 +748,7 @@ namespace Multiplayer
|
||||
|
||||
bool EntityReplicationManager::HandleEntityUpdateMessage
|
||||
(
|
||||
[[maybe_unused]] AzNetworking::IConnection* connection,
|
||||
[[maybe_unused]] AzNetworking::IConnection* invokingConnection,
|
||||
const AzNetworking::IPacketHeader& packetHeader,
|
||||
const NetworkEntityUpdateMessage& updateMessage
|
||||
)
|
||||
@@ -803,7 +804,7 @@ namespace Multiplayer
|
||||
return handled;
|
||||
}
|
||||
|
||||
bool EntityReplicationManager::HandleEntityRpcMessage([[maybe_unused]] AzNetworking::IConnection* connection, NetworkEntityRpcMessage& message)
|
||||
bool EntityReplicationManager::HandleEntityRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& message)
|
||||
{
|
||||
EntityReplicator* entityReplicator = GetEntityReplicator(message.GetEntityId());
|
||||
const bool isReplicatorValid = (entityReplicator != nullptr) && !entityReplicator->IsMarkedForRemoval();
|
||||
@@ -815,7 +816,7 @@ namespace Multiplayer
|
||||
}
|
||||
else
|
||||
{
|
||||
return entityReplicator->HandleRpcMessage(message);
|
||||
return entityReplicator->HandleRpcMessage(invokingConnection, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,8 +834,7 @@ namespace Multiplayer
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return entityReplicator->HandleRpcMessage(message);
|
||||
return entityReplicator->HandleRpcMessage(nullptr, message);
|
||||
}
|
||||
|
||||
AZ::TimeMs EntityReplicationManager::GetResendTimeoutTimeMs() const
|
||||
@@ -877,7 +877,6 @@ namespace Multiplayer
|
||||
AzNetworking::TimeoutResult EntityReplicationManager::OrphanedEntityRpcs::HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item)
|
||||
{
|
||||
NetEntityId timedOutEntityId = aznumeric_cast<NetEntityId>(item.m_userData);
|
||||
|
||||
auto entityRpcsIter = m_entityRpcMap.find(timedOutEntityId);
|
||||
if (entityRpcsIter != m_entityRpcMap.end())
|
||||
{
|
||||
@@ -887,7 +886,6 @@ namespace Multiplayer
|
||||
}
|
||||
m_entityRpcMap.erase(entityRpcsIter);
|
||||
}
|
||||
|
||||
return AzNetworking::TimeoutResult::Delete;
|
||||
}
|
||||
|
||||
@@ -1089,7 +1087,7 @@ namespace Multiplayer
|
||||
|
||||
if (m_updateMode == EntityReplicationManager::Mode::LocalServerToRemoteServer)
|
||||
{
|
||||
netBindComponent->NotifyMigration(GetRemoteHostId(), GetConnection().GetConnectionId());
|
||||
netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetConnection().GetConnectionId());
|
||||
}
|
||||
|
||||
bool didSucceed = true;
|
||||
@@ -1127,7 +1125,7 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityReplicationManager::HandleMessage([[maybe_unused]] AzNetworking::IConnection* connection, MultiplayerPackets::EntityMigration& message)
|
||||
bool EntityReplicationManager::HandleMessage([[maybe_unused]] AzNetworking::IConnection* invokingConnection, MultiplayerPackets::EntityMigration& message)
|
||||
{
|
||||
EntityReplicator* replicator = GetEntityReplicator(message.GetEntityId());
|
||||
{
|
||||
|
||||
+13
-17
@@ -13,11 +13,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Source/ReplicationWindows/IReplicationWindow.h>
|
||||
#include <Source/EntityDomains/IEntityDomain.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Source/Components/NetBindComponent.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <Include/IReplicationWindow.h>
|
||||
#include <Include/IEntityDomain.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzNetworking/DataStructures/TimeoutQueue.h>
|
||||
#include <AzNetworking/PacketLayer/IPacketHeader.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
@@ -59,7 +59,7 @@ namespace Multiplayer
|
||||
HostId GetRemoteHostId() const;
|
||||
|
||||
void ActivatePendingEntities();
|
||||
void SendUpdates(AZ::TimeMs serverGameTimeMs);
|
||||
void SendUpdates(AZ::TimeMs hostTimeMs);
|
||||
void Clear(bool forMigration);
|
||||
|
||||
bool SetEntityRebasing(NetworkEntityHandle& entityHandle);
|
||||
@@ -82,10 +82,10 @@ namespace Multiplayer
|
||||
|
||||
void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event<NetEntityId>::Handler& handler);
|
||||
|
||||
bool HandleMessage(AzNetworking::IConnection* connection, MultiplayerPackets::EntityMigration& message);
|
||||
bool HandleMessage(AzNetworking::IConnection* invokingConnection, MultiplayerPackets::EntityMigration& message);
|
||||
bool HandleEntityDeleteMessage(EntityReplicator* entityReplicator, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
|
||||
bool HandleEntityUpdateMessage(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
|
||||
bool HandleEntityRpcMessage(AzNetworking::IConnection* connection, NetworkEntityRpcMessage& message);
|
||||
bool HandleEntityUpdateMessage(AzNetworking::IConnection* invokingConnection, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
|
||||
bool HandleEntityRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& message);
|
||||
|
||||
AZ::TimeMs GetResendTimeoutTimeMs() const;
|
||||
|
||||
@@ -118,9 +118,9 @@ namespace Multiplayer
|
||||
using EntityReplicatorList = AZStd::deque<EntityReplicator*>;
|
||||
EntityReplicatorList GenerateEntityUpdateList();
|
||||
|
||||
void SendEntityUpdatesPacketHelper(AZ::TimeMs serverGameTimeMs, EntityReplicatorList& toSendList, uint32_t maxPayloadSize, AzNetworking::IConnection& connection);
|
||||
void SendEntityUpdatesPacketHelper(AZ::TimeMs hostTimeMs, EntityReplicatorList& toSendList, uint32_t maxPayloadSize, AzNetworking::IConnection& connection);
|
||||
|
||||
void SendEntityUpdates(AZ::TimeMs serverGameTimeMs);
|
||||
void SendEntityUpdates(AZ::TimeMs hostTimeMs);
|
||||
void SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable);
|
||||
|
||||
void MigrateEntityInternal(NetEntityId entityId);
|
||||
@@ -155,31 +155,27 @@ namespace Multiplayer
|
||||
OrphanedEntityRpcs(EntityReplicationManager& replicationManager);
|
||||
void Update();
|
||||
bool DispatchOrphanedRpcs(EntityReplicator& entityReplicator);
|
||||
void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityPrcMessage);
|
||||
void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityRpcMessage);
|
||||
AZStd::size_t Size() const { return m_entityRpcMap.size(); }
|
||||
private:
|
||||
AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override;
|
||||
|
||||
struct OrphanedRpcs
|
||||
{
|
||||
OrphanedRpcs() = default;
|
||||
OrphanedRpcs(OrphanedRpcs&& rhs)
|
||||
{
|
||||
m_rpcMessages.swap(rhs.m_rpcMessages);
|
||||
m_timeoutId = rhs.m_timeoutId;
|
||||
rhs.m_timeoutId = AzNetworking::TimeoutId{ 0 };
|
||||
m_rpcMessages.swap(rhs.m_rpcMessages);
|
||||
}
|
||||
|
||||
AzNetworking::TimeoutId m_timeoutId = AzNetworking::TimeoutId{ 0 };
|
||||
RpcMessages m_rpcMessages;
|
||||
AzNetworking::TimeoutId m_timeoutId = AzNetworking::TimeoutId{ 0 };
|
||||
};
|
||||
|
||||
typedef AZStd::unordered_map<NetEntityId, OrphanedRpcs> EntityRpcMap;
|
||||
EntityRpcMap m_entityRpcMap;
|
||||
AzNetworking::TimeoutQueue m_timeoutQueue;
|
||||
EntityReplicationManager& m_replicationManager;
|
||||
};
|
||||
|
||||
OrphanedEntityRpcs m_orphanedEntityRpcs;
|
||||
EntityReplicatorMap m_entityReplicatorMap;
|
||||
|
||||
|
||||
@@ -628,7 +628,7 @@ namespace Multiplayer
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntityReplicator::HandleRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
|
||||
bool EntityReplicator::HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& entityRpcMessage)
|
||||
{
|
||||
// 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();
|
||||
@@ -676,7 +676,7 @@ namespace Multiplayer
|
||||
switch (result)
|
||||
{
|
||||
case RpcValidationResult::HandleRpc:
|
||||
return m_netBindComponent->HandleRpcMessage(GetRemoteNetworkRole(), entityRpcMessage);
|
||||
return m_netBindComponent->HandleRpcMessage(invokingConnection, GetRemoteNetworkRole(), entityRpcMessage);
|
||||
case RpcValidationResult::DropRpc:
|
||||
return true;
|
||||
case RpcValidationResult::DropRpcAndDisconnect:
|
||||
@@ -703,7 +703,7 @@ namespace Multiplayer
|
||||
break;
|
||||
}
|
||||
|
||||
AZ_Assert(false, "Unhandled ERpcValidationResult %d", result);
|
||||
AZ_Assert(false, "Unhandled RpcValidationResult %d", result);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,8 @@ namespace Multiplayer
|
||||
const PropertyPublisher* GetPropertyPublisher() const;
|
||||
PropertySubscriber* GetPropertySubscriber();
|
||||
|
||||
// Handlers for messages
|
||||
bool HandleRpcMessage(NetworkEntityRpcMessage& entityRpcMessage);
|
||||
// Handlers for Rpc messages
|
||||
bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& entityRpcMessage);
|
||||
|
||||
//! AZ::EntityBus overrides
|
||||
//! @{
|
||||
|
||||
@@ -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/NetworkEntity/INetworkEntityManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! @class INetworkEntityDomain
|
||||
//! @brief A class that determines if an entity should belong to a particular EntityManager.
|
||||
class INetworkEntityDomain
|
||||
{
|
||||
public:
|
||||
using EntitiesNotInDomain = AZStd::unordered_set<NetEntityId>;
|
||||
|
||||
virtual ~INetworkEntityDomain() = default;
|
||||
|
||||
//! Enable Entity Domain Exit Tracking for entities on the server.
|
||||
//! @param ownedEntitySet
|
||||
virtual void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) = 0;
|
||||
|
||||
//! Return the set of entities not in this domain.
|
||||
//! @param outEntitiesNotInDomain
|
||||
virtual void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const = 0;
|
||||
|
||||
//! Returns whether or not an entity should be owned by an entity manager.
|
||||
//! @param entityHandle the handle of the entity to check for inclusion in the domain
|
||||
//! @return false if this entity should not belong to the entity manger, true if it could be owned by the entity manager
|
||||
virtual bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const = 0;
|
||||
};
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
*/
|
||||
|
||||
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Source/Components/NetBindComponent.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzNetworking/Utilities/NetworkCommon.h>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Source/Components/NetBindComponent.h>
|
||||
#include <Source/Components/MultiplayerController.h>
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace Multiplayer
|
||||
{
|
||||
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);
|
||||
netBindComponent->HandleRpcMessage(nullptr, NetEntityRole::Server, rpcMessage);
|
||||
}
|
||||
}
|
||||
m_localDeferredRpcMessages.clear();
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
#include <AzCore/EBus/ScheduledEvent.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzFramework/Spawnable/RootSpawnableInterface.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>
|
||||
#include <Include/IEntityDomain.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <Source/NetworkEntity/NetworkSpawnableLibrary.h>
|
||||
#include <Source/Components/MultiplayerComponentRegistry.h>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <Source/NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Include/MultiplayerTypes.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
|
||||
|
||||
@@ -48,19 +48,34 @@ namespace Multiplayer
|
||||
return m_inputId;
|
||||
}
|
||||
|
||||
void NetworkInput::SetServerTimeMs(AZ::TimeMs serverTimeMs)
|
||||
void NetworkInput::SetHostFrameId(HostFrameId hostFrameId)
|
||||
{
|
||||
m_serverTimeMs = serverTimeMs;
|
||||
m_hostFrameId = hostFrameId;
|
||||
}
|
||||
|
||||
AZ::TimeMs NetworkInput::GetServerTimeMs() const
|
||||
HostFrameId NetworkInput::GetHostFrameId() const
|
||||
{
|
||||
return m_serverTimeMs;
|
||||
return m_hostFrameId;
|
||||
}
|
||||
|
||||
AZ::TimeMs& NetworkInput::ModifyServerTimeMs()
|
||||
HostFrameId& NetworkInput::ModifyHostFrameId()
|
||||
{
|
||||
return m_serverTimeMs;
|
||||
return m_hostFrameId;
|
||||
}
|
||||
|
||||
void NetworkInput::SetHostTimeMs(AZ::TimeMs hostTimeMs)
|
||||
{
|
||||
m_hostTimeMs = hostTimeMs;
|
||||
}
|
||||
|
||||
AZ::TimeMs NetworkInput::GetHostTimeMs() const
|
||||
{
|
||||
return m_hostTimeMs;
|
||||
}
|
||||
|
||||
AZ::TimeMs& NetworkInput::ModifyHostTimeMs()
|
||||
{
|
||||
return m_hostTimeMs;
|
||||
}
|
||||
|
||||
void NetworkInput::AttachNetBindComponent(NetBindComponent* netBindComponent)
|
||||
@@ -76,17 +91,19 @@ namespace Multiplayer
|
||||
|
||||
bool NetworkInput::Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
if (!serializer.Serialize(m_inputId, "InputId"))
|
||||
if (!serializer.Serialize(m_inputId, "InputId")
|
||||
|| !serializer.Serialize(m_hostTimeMs, "HostTimeMs")
|
||||
|| !serializer.Serialize(m_hostFrameId, "HostFrameId"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t componentInputCount = static_cast<uint8_t>(m_componentInputs.size());
|
||||
uint16_t componentInputCount = static_cast<uint16_t>(m_componentInputs.size());
|
||||
serializer.Serialize(componentInputCount, "ComponentInputCount");
|
||||
m_componentInputs.resize(componentInputCount);
|
||||
if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)
|
||||
{
|
||||
for (uint8_t i = 0; i < componentInputCount; ++i)
|
||||
for (uint16_t i = 0; i < componentInputCount; ++i)
|
||||
{
|
||||
// We need to do a little extra work here, the delta serializer won't actually write out values if they were the same as the parent.
|
||||
// We need to make sure we don't lose state that is intrinsic to the underlying type
|
||||
@@ -148,7 +165,7 @@ namespace Multiplayer
|
||||
void NetworkInput::CopyInternal(const NetworkInput& rhs)
|
||||
{
|
||||
m_inputId = rhs.m_inputId;
|
||||
m_serverTimeMs = rhs.m_serverTimeMs;
|
||||
m_hostTimeMs = rhs.m_hostTimeMs;
|
||||
m_componentInputs.resize(rhs.m_componentInputs.size());
|
||||
for (int32_t i = 0; i < rhs.m_componentInputs.size(); ++i)
|
||||
{
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkInput/IMultiplayerComponentInput.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/IMultiplayerComponentInput.h>
|
||||
#include <Include/INetworkTime.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/RTTI/TypeSafeIntegral.h>
|
||||
|
||||
namespace Multiplayer
|
||||
@@ -21,8 +22,6 @@ namespace Multiplayer
|
||||
// Forwards
|
||||
class NetBindComponent;
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL(ClientInputId, uint16_t);
|
||||
|
||||
//! @class NetworkInput
|
||||
//! @brief A single networked client input command.
|
||||
class NetworkInput final
|
||||
@@ -42,9 +41,13 @@ namespace Multiplayer
|
||||
ClientInputId GetClientInputId() const;
|
||||
ClientInputId& ModifyClientInputId();
|
||||
|
||||
void SetServerTimeMs(AZ::TimeMs serverTimeMs);
|
||||
AZ::TimeMs GetServerTimeMs() const;
|
||||
AZ::TimeMs& ModifyServerTimeMs();
|
||||
void SetHostFrameId(HostFrameId hostFrameId);
|
||||
HostFrameId GetHostFrameId() const;
|
||||
HostFrameId& ModifyHostFrameId();
|
||||
|
||||
void SetHostTimeMs(AZ::TimeMs hostTimeMs);
|
||||
AZ::TimeMs GetHostTimeMs() const;
|
||||
AZ::TimeMs& ModifyHostTimeMs();
|
||||
|
||||
void AttachNetBindComponent(NetBindComponent* netBindComponent);
|
||||
|
||||
@@ -72,10 +75,9 @@ namespace Multiplayer
|
||||
|
||||
MultiplayerComponentInputVector m_componentInputs;
|
||||
ClientInputId m_inputId = ClientInputId{ 0 };
|
||||
AZ::TimeMs m_serverTimeMs = AZ::TimeMs{ 0 };
|
||||
HostFrameId m_hostFrameId = InvalidHostFrameId;
|
||||
AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 };
|
||||
ConstNetworkEntityHandle m_owner;
|
||||
bool m_wasAttached = false;
|
||||
};
|
||||
}
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::ClientInputId);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <Source/NetworkInput/NetworkInputChild.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
|
||||
namespace Multiplayer
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <Source/NetworkInput/NetworkInputVector.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzNetworking/Serialization/DeltaSerializer.h>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkInput/NetworkInput.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
|
||||
namespace Multiplayer
|
||||
|
||||
@@ -24,36 +24,31 @@ namespace Multiplayer
|
||||
AZ::Interface<INetworkTime>::Unregister(this);
|
||||
}
|
||||
|
||||
AZ::TimeMs NetworkTime::ConvertFrameIdToTimeMs([[maybe_unused]] ApplicationFrameId frameId) const
|
||||
{
|
||||
return AZ::TimeMs{0};
|
||||
}
|
||||
|
||||
ApplicationFrameId NetworkTime::ConvertTimeMsToFrameId([[maybe_unused]] AZ::TimeMs timeMs) const
|
||||
{
|
||||
return ApplicationFrameId{0};
|
||||
}
|
||||
|
||||
bool NetworkTime::IsApplicationFrameIdRewound() const
|
||||
bool NetworkTime::IsTimeRewound() const
|
||||
{
|
||||
return m_rewindingConnectionId != AzNetworking::InvalidConnectionId;
|
||||
}
|
||||
|
||||
ApplicationFrameId NetworkTime::GetApplicationFrameId() const
|
||||
HostFrameId NetworkTime::GetHostFrameId() const
|
||||
{
|
||||
return m_applicationFrameId;
|
||||
return m_hostFrameId;
|
||||
}
|
||||
|
||||
ApplicationFrameId NetworkTime::GetUnalteredApplicationFrameId() const
|
||||
HostFrameId NetworkTime::GetUnalteredHostFrameId() const
|
||||
{
|
||||
return m_unalteredFrameId;
|
||||
}
|
||||
|
||||
void NetworkTime::IncrementApplicationFrameId()
|
||||
void NetworkTime::IncrementHostFrameId()
|
||||
{
|
||||
AZ_Assert(!IsApplicationFrameIdRewound(), "Incrementing the global application frameId is unsupported under a rewound time scope");
|
||||
AZ_Assert(!IsTimeRewound(), "Incrementing the global application frameId is unsupported under a rewound time scope");
|
||||
++m_unalteredFrameId;
|
||||
m_applicationFrameId = m_unalteredFrameId;
|
||||
m_hostFrameId = m_unalteredFrameId;
|
||||
}
|
||||
|
||||
AZ::TimeMs NetworkTime::GetHostTimeMs() const
|
||||
{
|
||||
return m_hostTimeMs;
|
||||
}
|
||||
|
||||
void NetworkTime::SyncRewindableEntityState()
|
||||
@@ -66,14 +61,15 @@ namespace Multiplayer
|
||||
return m_rewindingConnectionId;
|
||||
}
|
||||
|
||||
ApplicationFrameId NetworkTime::GetApplicationFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const
|
||||
HostFrameId NetworkTime::GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const
|
||||
{
|
||||
return (IsApplicationFrameIdRewound() && (rewindConnectionId == m_rewindingConnectionId)) ? m_unalteredFrameId : m_applicationFrameId;
|
||||
return (IsTimeRewound() && (rewindConnectionId == m_rewindingConnectionId)) ? m_unalteredFrameId : m_hostFrameId;
|
||||
}
|
||||
|
||||
void NetworkTime::AlterApplicationFrameId(ApplicationFrameId frameId, AzNetworking::ConnectionId rewindConnectionId)
|
||||
void NetworkTime::AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId)
|
||||
{
|
||||
m_applicationFrameId = frameId;
|
||||
m_hostFrameId = frameId;
|
||||
m_hostTimeMs = timeMs;
|
||||
m_rewindingConnectionId = rewindConnectionId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkTime/INetworkTime.h>
|
||||
#include <Include/INetworkTime.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
|
||||
@@ -28,22 +28,22 @@ namespace Multiplayer
|
||||
|
||||
//! INetworkTime overrides.
|
||||
//! @{
|
||||
AZ::TimeMs ConvertFrameIdToTimeMs(ApplicationFrameId frameId) const override;
|
||||
ApplicationFrameId ConvertTimeMsToFrameId(AZ::TimeMs timeMs) const override;
|
||||
bool IsApplicationFrameIdRewound() const override;
|
||||
ApplicationFrameId GetApplicationFrameId() const override;
|
||||
ApplicationFrameId GetUnalteredApplicationFrameId() const override;
|
||||
void IncrementApplicationFrameId() override;
|
||||
bool IsTimeRewound() const override;
|
||||
HostFrameId GetHostFrameId() const override;
|
||||
HostFrameId GetUnalteredHostFrameId() const override;
|
||||
void IncrementHostFrameId() override;
|
||||
AZ::TimeMs GetHostTimeMs() const override;
|
||||
void SyncRewindableEntityState() override;
|
||||
AzNetworking::ConnectionId GetRewindingConnectionId() const override;
|
||||
ApplicationFrameId GetApplicationFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const override;
|
||||
void AlterApplicationFrameId(ApplicationFrameId frameId, AzNetworking::ConnectionId rewindConnectionId) override;
|
||||
HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const override;
|
||||
void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
|
||||
ApplicationFrameId m_applicationFrameId = ApplicationFrameId{0};
|
||||
ApplicationFrameId m_unalteredFrameId = ApplicationFrameId{0};
|
||||
HostFrameId m_hostFrameId = HostFrameId{ 0 };
|
||||
HostFrameId m_unalteredFrameId = HostFrameId{ 0 };
|
||||
AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 };
|
||||
AzNetworking::ConnectionId m_rewindingConnectionId = AzNetworking::InvalidConnectionId;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkTime/INetworkTime.h>
|
||||
#include <Include/INetworkTime.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <AzNetworking/Utilities/NetworkCommon.h>
|
||||
@@ -87,25 +87,25 @@ namespace Multiplayer
|
||||
|
||||
//! Returns what the appropriate current time is for this rewindable property.
|
||||
//! @return the appropriate current time is for this rewindable property
|
||||
ApplicationFrameId GetCurrentTimeForProperty() const;
|
||||
HostFrameId GetCurrentTimeForProperty() const;
|
||||
|
||||
//! Updates the latest value for this object instance, if frameTime represents a current or future time.
|
||||
//! Any attempts to set old values on the object will fail
|
||||
//! @param value the new value to set in the object history
|
||||
//! @param frameTime the time to set the value for
|
||||
void SetValueForTime(const BASE_TYPE& value, ApplicationFrameId frameTime);
|
||||
void SetValueForTime(const BASE_TYPE& value, HostFrameId frameTime);
|
||||
|
||||
//! Const value accessor, returns the correct value for the provided input time.
|
||||
//! @param frameTime the frame time to return the associated value for
|
||||
//! @return value given the current input time
|
||||
const BASE_TYPE& GetValueForTime(ApplicationFrameId frameTime) const;
|
||||
const BASE_TYPE& GetValueForTime(HostFrameId frameTime) const;
|
||||
|
||||
//! Helper method to compute clamped array index values accounting for the offset head index.
|
||||
AZStd::size_t GetOffsetIndex(AZStd::size_t absoluteIndex) const;
|
||||
|
||||
AZStd::array<BASE_TYPE, REWIND_SIZE> m_history;
|
||||
AzNetworking::ConnectionId m_owningConnectionId = AzNetworking::InvalidConnectionId;
|
||||
ApplicationFrameId m_headTime = ApplicationFrameId{0};
|
||||
HostFrameId m_headTime = HostFrameId{0};
|
||||
uint32_t m_headIndex = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Multiplayer
|
||||
inline RewindableObject<BASE_TYPE, REWIND_SIZE> &RewindableObject<BASE_TYPE, REWIND_SIZE>::operator =(const RewindableObject<BASE_TYPE, REWIND_SIZE>& rhs)
|
||||
{
|
||||
INetworkTime* networkTime = AZ::Interface<INetworkTime>::Get();
|
||||
SetValueForTime(rhs.GetValueForTime(networkTime->GetApplicationFrameId()), GetCurrentTimeForProperty());
|
||||
SetValueForTime(rhs.GetValueForTime(networkTime->GetHostFrameId()), GetCurrentTimeForProperty());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Multiplayer
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline BASE_TYPE& RewindableObject<BASE_TYPE, REWIND_SIZE>::Modify()
|
||||
{
|
||||
const ApplicationFrameId frameTime = GetCurrentTimeForProperty();
|
||||
const HostFrameId frameTime = GetCurrentTimeForProperty();
|
||||
if (frameTime < m_headTime)
|
||||
{
|
||||
AZ_Assert(false, "Trying to mutate a rewindable in the past");
|
||||
@@ -103,7 +103,7 @@ namespace Multiplayer
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline bool RewindableObject<BASE_TYPE, REWIND_SIZE>::Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
const ApplicationFrameId frameTime = GetCurrentTimeForProperty();
|
||||
const HostFrameId frameTime = GetCurrentTimeForProperty();
|
||||
BASE_TYPE value = GetValueForTime(frameTime);
|
||||
if (serializer.Serialize(value, "Element") && (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject))
|
||||
{
|
||||
@@ -113,14 +113,14 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline ApplicationFrameId RewindableObject<BASE_TYPE, REWIND_SIZE>::GetCurrentTimeForProperty() const
|
||||
inline HostFrameId RewindableObject<BASE_TYPE, REWIND_SIZE>::GetCurrentTimeForProperty() const
|
||||
{
|
||||
INetworkTime* networkTime = AZ::Interface<INetworkTime>::Get();
|
||||
return networkTime->GetApplicationFrameIdForRewindingConnection(m_owningConnectionId);
|
||||
return networkTime->GetHostFrameIdForRewindingConnection(m_owningConnectionId);
|
||||
}
|
||||
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline void RewindableObject<BASE_TYPE, REWIND_SIZE>::SetValueForTime(const BASE_TYPE& value, ApplicationFrameId frameTime)
|
||||
inline void RewindableObject<BASE_TYPE, REWIND_SIZE>::SetValueForTime(const BASE_TYPE& value, HostFrameId frameTime)
|
||||
{
|
||||
if (frameTime < m_headTime)
|
||||
{
|
||||
@@ -155,7 +155,7 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline const BASE_TYPE &RewindableObject<BASE_TYPE, REWIND_SIZE>::GetValueForTime(ApplicationFrameId frameTime) const
|
||||
inline const BASE_TYPE &RewindableObject<BASE_TYPE, REWIND_SIZE>::GetValueForTime(HostFrameId frameTime) const
|
||||
{
|
||||
if (frameTime > m_headTime)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/ReplicationWindows/IReplicationWindow.h>
|
||||
#include <Include/IReplicationWindow.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/ReplicationWindows/IReplicationWindow.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <Include/IReplicationWindow.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <AzCore/Component/EntityBus.h>
|
||||
#include <AzCore/EBus/ScheduledEvent.h>
|
||||
|
||||
@@ -10,10 +10,18 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Include/IConnectionData.h
|
||||
Include/IEntityDomain.h
|
||||
Include/IMultiplayer.h
|
||||
Include/IMultiplayerComponentInput.h
|
||||
Include/INetworkEntityManager.h
|
||||
Include/INetworkTime.h
|
||||
Include/IReplicationWindow.h
|
||||
Include/MultiplayerStats.cpp
|
||||
Include/MultiplayerStats.h
|
||||
Include/MultiplayerTypes.h
|
||||
Include/NetworkEntityHandle.h
|
||||
Include/NetworkEntityHandle.inl
|
||||
Source/Multiplayer_precompiled.cpp
|
||||
Source/Multiplayer_precompiled.h
|
||||
Source/MultiplayerSystemComponent.cpp
|
||||
@@ -41,13 +49,11 @@ set(FILES
|
||||
Source/ConnectionData/ClientToServerConnectionData.cpp
|
||||
Source/ConnectionData/ClientToServerConnectionData.h
|
||||
Source/ConnectionData/ClientToServerConnectionData.inl
|
||||
Source/ConnectionData/IConnectionData.h
|
||||
Source/ConnectionData/ServerToClientConnectionData.cpp
|
||||
Source/ConnectionData/ServerToClientConnectionData.h
|
||||
Source/ConnectionData/ServerToClientConnectionData.inl
|
||||
Source/EntityDomains/FullOwnershipEntityDomain.cpp
|
||||
Source/EntityDomains/FullOwnershipEntityDomain.h
|
||||
Source/EntityDomains/IEntityDomain.h
|
||||
Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp
|
||||
Source/NetworkEntity/EntityReplication/EntityReplicationManager.h
|
||||
Source/NetworkEntity/EntityReplication/EntityReplicator.cpp
|
||||
@@ -59,12 +65,9 @@ set(FILES
|
||||
Source/NetworkEntity/EntityReplication/PropertySubscriber.h
|
||||
Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp
|
||||
Source/NetworkEntity/EntityReplication/ReplicationRecord.h
|
||||
Source/NetworkEntity/INetworkEntityManager.h
|
||||
Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp
|
||||
Source/NetworkEntity/NetworkEntityAuthorityTracker.h
|
||||
Source/NetworkEntity/NetworkEntityHandle.cpp
|
||||
Source/NetworkEntity/NetworkEntityHandle.h
|
||||
Source/NetworkEntity/NetworkEntityHandle.inl
|
||||
Source/NetworkEntity/NetworkEntityManager.cpp
|
||||
Source/NetworkEntity/NetworkEntityManager.h
|
||||
Source/NetworkEntity/NetworkSpawnableLibrary.cpp
|
||||
@@ -76,7 +79,6 @@ set(FILES
|
||||
Source/NetworkEntity/NetworkEntityTracker.inl
|
||||
Source/NetworkEntity/NetworkEntityUpdateMessage.cpp
|
||||
Source/NetworkEntity/NetworkEntityUpdateMessage.h
|
||||
Source/NetworkInput/IMultiplayerComponentInput.h
|
||||
Source/NetworkInput/NetworkInput.cpp
|
||||
Source/NetworkInput/NetworkInput.h
|
||||
Source/NetworkInput/NetworkInputChild.cpp
|
||||
@@ -85,7 +87,6 @@ set(FILES
|
||||
Source/NetworkInput/NetworkInputHistory.h
|
||||
Source/NetworkInput/NetworkInputVector.cpp
|
||||
Source/NetworkInput/NetworkInputVector.h
|
||||
Source/NetworkTime/INetworkTime.h
|
||||
Source/NetworkTime/NetworkTime.cpp
|
||||
Source/NetworkTime/NetworkTime.h
|
||||
Source/NetworkTime/RewindableObject.h
|
||||
@@ -96,7 +97,6 @@ set(FILES
|
||||
Source/Pipeline/NetworkSpawnableHolderComponent.h
|
||||
Source/ReplicationWindows/NullReplicationWindow.cpp
|
||||
Source/ReplicationWindows/NullReplicationWindow.h
|
||||
Source/ReplicationWindows/IReplicationWindow.h
|
||||
Source/ReplicationWindows/ServerToClientReplicationWindow.cpp
|
||||
Source/ReplicationWindows/ServerToClientReplicationWindow.h
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user