Merge branch 'main' into SPEC6476_ReflectNetworkProperties
This commit is contained in:
@@ -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) %}
|
||||
@@ -436,7 +436,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) }});
|
||||
@@ -1291,7 +1291,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)
|
||||
|
||||
+6
-5
@@ -12,15 +12,16 @@
|
||||
|
||||
<Include File="Include/MultiplayerTypes.h"/>
|
||||
<Include File="Source/NetworkInput/NetworkInput.h"/>
|
||||
<Include File="Source/NetworkInput/NetworkInputArray.h"/>
|
||||
<Include File="Source/NetworkInput/NetworkInputHistory.h"/>
|
||||
<Include File="Source/NetworkInput/NetworkInputVector.h"/>
|
||||
<Include File="Source/NetworkInput/NetworkInputMigrationVector.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="Multiplayer::NetworkInputArray" Name="inputArray" />
|
||||
<Param Type="AZ::HashValue32" Name="stateHash" />
|
||||
<Param Type="AzNetworking::PacketEncodingBuffer" Name="clientState" Description="This is for debugging desyncs only; release games should not populate this parameter" />
|
||||
</RemoteProcedure>
|
||||
|
||||
@@ -30,6 +31,6 @@
|
||||
</RemoteProcedure>
|
||||
|
||||
<RemoteProcedure Name="SendMigrateClientInput" InvokeFrom="Autonomous" HandleOn="Authority" IsPublic="true" IsReliable="false" Description="Client to server migrate move / input RPC">
|
||||
<Param Type="Multiplayer::MigrateNetworkInputVector" Name="inputArray" />
|
||||
<Param Type="Multiplayer::NetworkInputMigrationVector" Name="inputArray" />
|
||||
</RemoteProcedure>
|
||||
</Component>
|
||||
|
||||
@@ -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::NetworkInputArray& inputArray,
|
||||
const AZ::HashValue32& 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 = NetworkInputArray::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", aznumeric_cast<uint32_t>(input.GetClientInputId()));
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_Prediction, "Processed InputId=%u", aznumeric_cast<uint32_t>(input.GetClientInputId()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG(NET_Prediction, "Dropped InputId=%u", aznumeric_cast<uint32_t>(input.GetClientInputId()));
|
||||
}
|
||||
--inputArrayIndex;
|
||||
}
|
||||
|
||||
if (sv_EnableCorrections && (currentTimeMs - m_lastCorrectionSentTimeMs > sv_MinCorrectionTimeMs))
|
||||
{
|
||||
m_lastCorrectionSentTimeMs = currentTimeMs;
|
||||
|
||||
AzNetworking::HashSerializer hashSerializer;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(hashSerializer);
|
||||
|
||||
const AZ::HashValue32 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::NetworkInputMigrationVector& 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
|
||||
NetworkInputMigrationVector 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()
|
||||
{
|
||||
NetworkInputMigrationVector 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;
|
||||
|
||||
NetworkInputArray 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 < NetworkInputArray::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::NetworkInputArray& inputArray,
|
||||
const AZ::HashValue32& stateHash,
|
||||
const AzNetworking::PacketEncodingBuffer& clientState
|
||||
) override;
|
||||
|
||||
void HandleSendMigrateClientInput
|
||||
(
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
const Multiplayer::NetworkInputMigrationVector& 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
|
||||
NetworkInputArray 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);
|
||||
|
||||
@@ -119,7 +126,7 @@ namespace Multiplayer
|
||||
|
||||
ReplicationRecord m_currentRecord = NetEntityRole::InvalidRole;
|
||||
ReplicationRecord m_totalRecord = NetEntityRole::InvalidRole;
|
||||
ReplicationRecord m_predictableRecord = NetEntityRole::InvalidRole;
|
||||
ReplicationRecord m_predictableRecord = NetEntityRole::Autonomous;
|
||||
ReplicationRecord m_localNotificationRecord = NetEntityRole::InvalidRole;
|
||||
PrefabEntityId m_prefabEntityId;
|
||||
AZStd::unordered_map<NetComponentId, MultiplayerComponent*> m_multiplayerComponentMap;
|
||||
@@ -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,8 +50,9 @@ namespace Multiplayer
|
||||
return m_entityReplicationManager;
|
||||
}
|
||||
|
||||
void ClientToServerConnectionData::Update([[maybe_unused]] AZ::TimeMs serverGameTimeMs)
|
||||
void ClientToServerConnectionData::Update(AZ::TimeMs hostTimeMs)
|
||||
{
|
||||
m_entityReplicationManager.ActivatePendingEntities();
|
||||
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
|
||||
{
|
||||
@@ -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;
|
||||
//! @}
|
||||
|
||||
@@ -1,56 +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>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
enum class ConnectionDataType
|
||||
{
|
||||
ClientToServer,
|
||||
ServerToClient,
|
||||
ServerToServer
|
||||
};
|
||||
|
||||
class IConnectionData
|
||||
{
|
||||
public:
|
||||
virtual ~IConnectionData() = default;
|
||||
|
||||
//! Returns whether or not this is a ServerToClient or ServerToServer connection data instance.
|
||||
//! @return ConnectionDataType::ServerToClient or ConnectionDataType::ServerToServer
|
||||
virtual ConnectionDataType GetConnectionDataType() const = 0;
|
||||
|
||||
//! Returns the connection bound to this connection data instance.
|
||||
//! @return pointer to the connection bound to this connection data instance
|
||||
virtual AzNetworking::IConnection* GetConnection() const = 0;
|
||||
|
||||
//! Returns the EntityReplicationManager for this connection data instance.
|
||||
//! @return reference to the EntityReplicationManager for this connection data instance
|
||||
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;
|
||||
|
||||
//! Returns whether update messages can be sent to the connection.
|
||||
//! @return true if update messages can be sent
|
||||
virtual bool CanSendUpdates() const = 0;
|
||||
|
||||
//! Sets the state of connection whether update messages can be sent or not.
|
||||
//! @param canSendUpdates the state value
|
||||
virtual void SetCanSendUpdates(bool canSendUpdates) = 0;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -235,7 +235,7 @@ namespace Multiplayer
|
||||
|
||||
if (m_displayMultiplayerStats)
|
||||
{
|
||||
if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_HorizontalScrollbar))
|
||||
if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_None))
|
||||
{
|
||||
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
|
||||
@@ -254,7 +254,7 @@ namespace Multiplayer
|
||||
if (ImGui::BeginTable("", 5, flags))
|
||||
{
|
||||
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide, TEXT_BASE_WIDTH * 36.0f);
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/EntityDomains/IEntityDomain.h>
|
||||
#include <Include/IEntityDomain.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
@@ -1,44 +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 IEntityDomain
|
||||
//! @brief A class that determines if an entity should belong to a particular INetworkEntityManager.
|
||||
class IEntityDomain
|
||||
{
|
||||
public:
|
||||
using EntitiesNotInDomain = AZStd::unordered_set<NetEntityId>;
|
||||
|
||||
virtual ~IEntityDomain() = default;
|
||||
|
||||
//! Returns whether or not an entity should be owned by an entity manager.
|
||||
//! @param entityHandle the handle of the netbound entity to check
|
||||
//! @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;
|
||||
|
||||
//! Enable Entity Domain Exit Tracking for entities on the host.
|
||||
//! @param ownedEntitySet the set of entities to activate tracking for
|
||||
virtual void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) = 0;
|
||||
|
||||
//! Return the set of netbound entities not included in this domain.
|
||||
//! @param outEntitiesNotInDomain the set of known networked entities not included in this domain
|
||||
virtual void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const = 0;
|
||||
|
||||
//! Debug draw to visualize host entity domains.
|
||||
virtual void DebugDraw() const = 0;
|
||||
};
|
||||
}
|
||||
@@ -127,7 +127,8 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
AZ::TimeMs serverGameTimeMs = AZ::GetElapsedTimeMs();
|
||||
AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f));
|
||||
AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs();
|
||||
|
||||
// Handle deferred local rpc messages that were generated during the updates
|
||||
m_networkEntityManager.DispatchLocalDeferredRpcMessages();
|
||||
@@ -137,18 +138,19 @@ namespace Multiplayer
|
||||
m_networkEntityManager.NotifyEntitiesDirtied();
|
||||
|
||||
MultiplayerStats& stats = GetStats();
|
||||
stats.TickStats(deltaTimeMs);
|
||||
stats.m_entityCount = GetNetworkEntityManager()->GetEntityCount();
|
||||
stats.m_serverConnectionCount = 0;
|
||||
stats.m_clientConnectionCount = 0;
|
||||
|
||||
// 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 +483,7 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
MultiplayerAgentType MultiplayerSystemComponent::GetAgentType()
|
||||
MultiplayerAgentType MultiplayerSystemComponent::GetAgentType() const
|
||||
{
|
||||
return m_agentType;
|
||||
}
|
||||
@@ -528,6 +530,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;
|
||||
};
|
||||
}
|
||||
@@ -1,158 +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 <Include/MultiplayerTypes.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/EBus/Event.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class NetworkEntityTracker;
|
||||
class NetworkEntityAuthorityTracker;
|
||||
class NetworkEntityRpcMessage;
|
||||
class MultiplayerComponentRegistry;
|
||||
|
||||
using EntityExitDomainEvent = AZ::Event<const ConstNetworkEntityHandle&>;
|
||||
using ControllersActivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
|
||||
using ControllersDeactivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
|
||||
|
||||
//! @class INetworkEntityManager
|
||||
//! @brief The interface for managing all networked entities.
|
||||
class INetworkEntityManager
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(INetworkEntityManager, "{109759DE-9492-439C-A0B1-AE46E6FD029C}");
|
||||
|
||||
using OwnedEntitySet = AZStd::unordered_set<ConstNetworkEntityHandle>;
|
||||
using EntityList = AZStd::vector<NetworkEntityHandle>;
|
||||
|
||||
virtual ~INetworkEntityManager() = default;
|
||||
|
||||
//! Returns the NetworkEntityTracker for this INetworkEntityManager instance.
|
||||
//! @return the NetworkEntityTracker for this INetworkEntityManager instance
|
||||
virtual NetworkEntityTracker* GetNetworkEntityTracker() = 0;
|
||||
|
||||
//! Returns the NetworkEntityAuthorityTracker for this INetworkEntityManager instance.
|
||||
//! @return the NetworkEntityAuthorityTracker for this INetworkEntityManager instance
|
||||
virtual NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() = 0;
|
||||
|
||||
//! Returns the MultiplayerComponentRegistry for this INetworkEntityManager instance.
|
||||
//! @return the MultiplayerComponentRegistry for this INetworkEntityManager instance
|
||||
virtual MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() = 0;
|
||||
|
||||
//! Returns the HostId for this INetworkEntityManager instance.
|
||||
//! @return the HostId for this INetworkEntityManager instance
|
||||
virtual HostId GetHostId() const = 0;
|
||||
|
||||
//! Creates new entities of the given archetype
|
||||
//! @param prefabEntryId the name of the spawnable to spawn
|
||||
virtual EntityList CreateEntitiesImmediate(
|
||||
const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole, AutoActivate autoActivate,
|
||||
const AZ::Transform& transform) = 0;
|
||||
|
||||
//! Returns an ConstEntityPtr for the provided entityId.
|
||||
//! @param netEntityId the netEntityId to get an ConstEntityPtr for
|
||||
//! @return the requested ConstEntityPtr
|
||||
virtual ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const = 0;
|
||||
|
||||
//! Returns the total number of entities tracked by this INetworkEntityManager instance.
|
||||
//! @return the total number of entities tracked by this INetworkEntityManager instance
|
||||
virtual uint32_t GetEntityCount() const = 0;
|
||||
|
||||
//! Adds the provided entity to the internal entity map identified by the provided netEntityId.
|
||||
//! @param netEntityId the identifier to use for the added entity
|
||||
//! @param entity the entity to add to the internal entity map
|
||||
//! @return a NetworkEntityHandle for the newly added entity
|
||||
virtual NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) = 0;
|
||||
|
||||
//! Marks the specified entity for removal and deletion.
|
||||
//! @param entityHandle the entity to remove and delete
|
||||
virtual void MarkForRemoval(const ConstNetworkEntityHandle& entityHandle) = 0;
|
||||
|
||||
//! Returns true if the indicated entity is marked for removal.
|
||||
//! @param entityHandle the entity to test if marked for removal
|
||||
//! @return boolean true if the specified entity is marked for removal, false otherwise
|
||||
virtual bool IsMarkedForRemoval(const ConstNetworkEntityHandle& entityHandle) const = 0;
|
||||
|
||||
//! Unmarks the specified entity so it will no longer be removed and deleted.
|
||||
//! @param entityHandle the entity to unmark for removal and deletion
|
||||
virtual void ClearEntityFromRemovalList(const ConstNetworkEntityHandle& entityHandle) = 0;
|
||||
|
||||
//! Clears out and deletes all entities registered with the entity manager.
|
||||
virtual void ClearAllEntities() = 0;
|
||||
|
||||
//! Adds an event handler to be invoked when we notify which entities have been marked dirty.
|
||||
//! @param entityMarkedDirtyHandle event handler for the dirtied entity
|
||||
virtual void AddEntityMarkedDirtyHandler(AZ::Event<>::Handler& entityMarkedDirtyHandle) = 0;
|
||||
|
||||
//! Adds an event handler to be invoked when we notify entities to send their change notifications.
|
||||
//! @param entityNotifyChangesHandle event handler for the dirtied entity
|
||||
virtual void AddEntityNotifyChangesHandler(AZ::Event<>::Handler& entityNotifyChangesHandle) = 0;
|
||||
|
||||
//! Adds an event handler to be invoked when we notify entities to send their change notifications.
|
||||
//! @param entityNotifyChangesHandle event handler for the dirtied entity
|
||||
virtual void AddEntityExitDomainHandler(EntityExitDomainEvent::Handler& entityExitDomainHandler) = 0;
|
||||
|
||||
//! Adds an event handler to be invoked when an entities controllers have activated
|
||||
//! @param controllersActivatedHandler event handler for the entity
|
||||
virtual void AddControllersActivatedHandler(ControllersActivatedEvent::Handler& controllersActivatedHandler) = 0;
|
||||
|
||||
//! Adds an event handler to be invoked when an entities controllers have been deactivated
|
||||
//! @param controllersDeactivatedHandler event handler for the entity
|
||||
virtual void AddControllersDeactivatedHandler(ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) = 0;
|
||||
|
||||
//! Notifies entities that they should process their dirty state.
|
||||
virtual void NotifyEntitiesDirtied() = 0;
|
||||
|
||||
//! Notifies entities that they should process change notifications.
|
||||
virtual void NotifyEntitiesChanged() = 0;
|
||||
|
||||
//! Notifies that an entities controllers have activated.
|
||||
//! @param entityHandle handle to the entity whose controllers have activated
|
||||
//! @param entityIsMigrating true if the entity is activating after a migration
|
||||
virtual void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) = 0;
|
||||
|
||||
//! Notifies that an entities controllers have been deactivated.
|
||||
//! @param entityHandle handle to the entity whose controllers have been deactivated
|
||||
//! @param entityIsMigrating true if the entity is deactivating due to a migration
|
||||
virtual void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) = 0;
|
||||
|
||||
//! Handle a local rpc message.
|
||||
//! @param entityRpcMessage the local rpc message to handle
|
||||
virtual void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) = 0;
|
||||
};
|
||||
|
||||
// Convenience helpers
|
||||
inline INetworkEntityManager* GetNetworkEntityManager()
|
||||
{
|
||||
return AZ::Interface<INetworkEntityManager>::Get();
|
||||
}
|
||||
|
||||
inline NetworkEntityTracker* GetNetworkEntityTracker()
|
||||
{
|
||||
return GetNetworkEntityManager()->GetNetworkEntityTracker();
|
||||
}
|
||||
|
||||
inline NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker()
|
||||
{
|
||||
return GetNetworkEntityManager()->GetNetworkEntityAuthorityTracker();
|
||||
}
|
||||
|
||||
inline MultiplayerComponentRegistry* GetMultiplayerComponentRegistry()
|
||||
{
|
||||
return GetNetworkEntityManager()->GetMultiplayerComponentRegistry();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <Include/MultiplayerTypes.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class MultiplayerController;
|
||||
class NetworkEntityTracker;
|
||||
class NetBindComponent;
|
||||
|
||||
//! @class ConstNetworkEntityHandle
|
||||
//! @brief This class provides a wrapping around handle ids.
|
||||
//! It is optimized to avoid using the hashmap lookup unless the hashmap has had an item removed.
|
||||
class ConstNetworkEntityHandle
|
||||
{
|
||||
public:
|
||||
|
||||
//! Constructs a nullptr handle.
|
||||
ConstNetworkEntityHandle() = default;
|
||||
|
||||
//! Constructs a ConstNetworkEntityHandle given an entity, an entity tracker
|
||||
//! @param entity pointer to the entity to construct a ConstNetworkEntityHandle for
|
||||
//! @param entityTracker pointer to the entity tracker that tracks the entity
|
||||
ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* entityTracker);
|
||||
|
||||
//! Constructs a ConstNetworkEntityHandle given an entity, a networkEntityId, and an entity tracker
|
||||
//! @param entity pointer to the entity to construct a ConstNetworkEntityHandle for
|
||||
//! @param netEntityId the networkEntityId of the entity
|
||||
//! @param entityTracker pointer to the entity tracker that tracks the entity
|
||||
ConstNetworkEntityHandle(AZ::Entity* entity, NetEntityId netEntityId, const NetworkEntityTracker* entityTracker);
|
||||
|
||||
//! Constructs a ConstNetworkEntityHandle given an entity, a networked entityId, an entity tracker, and a dirty version
|
||||
//! @param netBindComponent pointer to the entities NetBindComponent
|
||||
//! @param entityTracker pointer to the entity tracker that tracks the entity
|
||||
ConstNetworkEntityHandle(NetBindComponent* netBindComponent, const NetworkEntityTracker* entityTracker);
|
||||
|
||||
ConstNetworkEntityHandle(const ConstNetworkEntityHandle&) = default;
|
||||
|
||||
//! Access the AZ::Entity if it safely exists, nullptr or false is returned if the entity does not exist.
|
||||
//! @{
|
||||
bool Exists() const;
|
||||
AZ::Entity* GetEntity();
|
||||
const AZ::Entity* GetEntity() const;
|
||||
//! @}
|
||||
|
||||
//! Operators providing pointer semantics.
|
||||
//! @{
|
||||
bool operator ==(const ConstNetworkEntityHandle& rhs) const;
|
||||
bool operator !=(const ConstNetworkEntityHandle& rhs) const;
|
||||
friend bool operator ==(const ConstNetworkEntityHandle& lhs, AZStd::nullptr_t);
|
||||
friend bool operator ==(AZStd::nullptr_t, const ConstNetworkEntityHandle& rhs);
|
||||
friend bool operator !=(const ConstNetworkEntityHandle& lhs, AZStd::nullptr_t);
|
||||
friend bool operator !=(AZStd::nullptr_t, const ConstNetworkEntityHandle& rhs);
|
||||
friend bool operator ==(const ConstNetworkEntityHandle& lhs, const AZ::Entity* rhs);
|
||||
friend bool operator ==(const AZ::Entity* lhs, const ConstNetworkEntityHandle& rhs);
|
||||
friend bool operator !=(const ConstNetworkEntityHandle& lhs, const AZ::Entity* rhs);
|
||||
friend bool operator !=(const AZ::Entity* lhs, const ConstNetworkEntityHandle& rhs);
|
||||
//! @}
|
||||
|
||||
bool operator <(const ConstNetworkEntityHandle& rhs) const;
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
//! Resets the handle to a nullptr state.
|
||||
void Reset();
|
||||
void Reset(const ConstNetworkEntityHandle& handle);
|
||||
|
||||
//! Returns the networkEntityId of the entity this handle points to.
|
||||
//! @return the networkEntityId of the entity this handle points to
|
||||
NetEntityId GetNetEntityId() const;
|
||||
|
||||
//! Returns the cached netBindComponent for this entity, or nullptr if it doesn't exist.
|
||||
//! @return the cached netBindComponent for this entity, or nullptr if it doesn't exist
|
||||
NetBindComponent* GetNetBindComponent() const;
|
||||
|
||||
//! Returns a specific component on of entity given a typeId.
|
||||
//! @param typeId the typeId of the component to find and return
|
||||
//! @return pointer to the requested component, or nullptr if it doesn't exist on the entity
|
||||
const AZ::Component* FindComponent(const AZ::TypeId& typeId) const;
|
||||
|
||||
//! Returns a specific component on of entity by class type.
|
||||
//! @return pointer to the requested component, or nullptr if it doesn't exist on the entity
|
||||
template <typename Component>
|
||||
const Component* FindComponent() const;
|
||||
|
||||
//! Helper function for sorting EntityHandles by netEntityId.
|
||||
static bool Compare(const ConstNetworkEntityHandle& lhs, const ConstNetworkEntityHandle& rhs);
|
||||
|
||||
protected:
|
||||
|
||||
mutable uint32_t m_changeDirty = 0; // Optimization so we don't need to recheck the hashmap
|
||||
mutable AZ::Entity* m_entity = nullptr;
|
||||
mutable NetBindComponent* m_netBindComponent = nullptr;
|
||||
const NetworkEntityTracker* m_networkEntityTracker = nullptr;
|
||||
NetEntityId m_netEntityId = InvalidNetEntityId;
|
||||
};
|
||||
|
||||
class NetworkEntityHandle
|
||||
: public ConstNetworkEntityHandle
|
||||
{
|
||||
public:
|
||||
|
||||
using ConstNetworkEntityHandle::ConstNetworkEntityHandle;
|
||||
|
||||
//! Initializes the underlying entity if possible.
|
||||
void Init();
|
||||
|
||||
//! Activates the underlying entity if possible.
|
||||
void Activate();
|
||||
|
||||
//! Deactivates the underlying entity if possible.
|
||||
void Deactivate();
|
||||
|
||||
//! Gets the BaseController from the first component on an Entity with the supplied typeId AND which inherits from Multiplayer::BaseComponent
|
||||
MultiplayerController* FindController(const AZ::TypeId& typeId);
|
||||
|
||||
template <typename Controller>
|
||||
Controller* FindController();
|
||||
|
||||
using ConstNetworkEntityHandle::FindComponent;
|
||||
AZ::Component* FindComponent(const AZ::TypeId& typeId);
|
||||
|
||||
template <typename ComponentType>
|
||||
ComponentType* FindComponent();
|
||||
};
|
||||
}
|
||||
|
||||
#include "Source/NetworkEntity/NetworkEntityHandle.inl"
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
inline bool operator ==(const ConstNetworkEntityHandle& lhs, AZStd::nullptr_t)
|
||||
{
|
||||
return !lhs.Exists();
|
||||
}
|
||||
|
||||
inline bool operator ==(AZStd::nullptr_t, const ConstNetworkEntityHandle& rhs)
|
||||
{
|
||||
return !rhs.Exists();
|
||||
}
|
||||
|
||||
inline bool operator !=(const ConstNetworkEntityHandle& lhs, AZStd::nullptr_t)
|
||||
{
|
||||
return lhs.Exists();
|
||||
}
|
||||
|
||||
inline bool operator !=(AZStd::nullptr_t, const ConstNetworkEntityHandle& rhs)
|
||||
{
|
||||
return rhs.Exists();
|
||||
}
|
||||
|
||||
inline bool operator==(const ConstNetworkEntityHandle& lhs, const AZ::Entity* rhs)
|
||||
{
|
||||
return lhs.m_entity == rhs;
|
||||
}
|
||||
|
||||
inline bool operator==(const AZ::Entity* lhs, const ConstNetworkEntityHandle& rhs)
|
||||
{
|
||||
return operator==(rhs, lhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const ConstNetworkEntityHandle& lhs, const AZ::Entity* rhs)
|
||||
{
|
||||
return lhs.m_entity != rhs;
|
||||
}
|
||||
|
||||
inline bool operator!=(const AZ::Entity* lhs, const ConstNetworkEntityHandle& rhs)
|
||||
{
|
||||
return operator!=(rhs, lhs);
|
||||
}
|
||||
|
||||
inline NetEntityId ConstNetworkEntityHandle::GetNetEntityId() const
|
||||
{
|
||||
return m_netEntityId;
|
||||
}
|
||||
|
||||
template <class ComponentType>
|
||||
inline const ComponentType* ConstNetworkEntityHandle::FindComponent() const
|
||||
{
|
||||
if (const AZ::Entity* entity{ GetEntity() })
|
||||
{
|
||||
return entity->template FindComponent<ComponentType>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline bool ConstNetworkEntityHandle::Compare(const ConstNetworkEntityHandle& lhs, const ConstNetworkEntityHandle& rhs)
|
||||
{
|
||||
return lhs.m_netEntityId < rhs.m_netEntityId;
|
||||
}
|
||||
|
||||
inline void NetworkEntityHandle::Init()
|
||||
{
|
||||
if (AZ::Entity* entity{ GetEntity() })
|
||||
{
|
||||
entity->Init();
|
||||
}
|
||||
}
|
||||
|
||||
inline void NetworkEntityHandle::Activate()
|
||||
{
|
||||
if (AZ::Entity* entity{ GetEntity() })
|
||||
{
|
||||
entity->Activate();
|
||||
}
|
||||
}
|
||||
|
||||
inline void NetworkEntityHandle::Deactivate()
|
||||
{
|
||||
if (AZ::Entity* entity{ GetEntity() })
|
||||
{
|
||||
entity->Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ControllerType>
|
||||
inline ControllerType* NetworkEntityHandle::FindController()
|
||||
{
|
||||
return static_cast<ControllerType*>(FindController(ControllerType::ComponentType::RTTI_Type()));
|
||||
}
|
||||
|
||||
template <typename ComponentType>
|
||||
inline ComponentType* NetworkEntityHandle::FindComponent()
|
||||
{
|
||||
if (AZ::Entity* entity{ GetEntity() })
|
||||
{
|
||||
return entity->template FindComponent<ComponentType>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//! AZStd::hash support.
|
||||
namespace AZStd
|
||||
{
|
||||
template <>
|
||||
class hash<Multiplayer::NetworkEntityHandle>
|
||||
{
|
||||
public:
|
||||
size_t operator()(const Multiplayer::NetworkEntityHandle &rhs) const
|
||||
{
|
||||
return hash<Multiplayer::NetEntityId>()(rhs.GetNetEntityId());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class hash<Multiplayer::ConstNetworkEntityHandle>
|
||||
{
|
||||
public:
|
||||
size_t operator()(const Multiplayer::ConstNetworkEntityHandle &rhs) const
|
||||
{
|
||||
return hash<Multiplayer::NetEntityId>()(rhs.GetNetEntityId());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -218,7 +218,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>
|
||||
|
||||
|
||||
@@ -1,36 +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 <Include/MultiplayerTypes.h>
|
||||
#include <AzNetworking/DataStructures/FixedSizeBitset.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
class ISerializer;
|
||||
}
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class IMultiplayerComponentInput
|
||||
{
|
||||
public:
|
||||
virtual ~IMultiplayerComponentInput() = default;
|
||||
virtual NetComponentId GetComponentId() const = 0;
|
||||
virtual bool Serialize(AzNetworking::ISerializer& serializer) = 0;
|
||||
};
|
||||
|
||||
using MultiplayerComponentInputVector = AZStd::vector<AZStd::unique_ptr<IMultiplayerComponentInput>>;
|
||||
}
|
||||
@@ -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
|
||||
@@ -30,8 +29,8 @@ namespace Multiplayer
|
||||
public:
|
||||
//! Intentionally restrict instancing of these objects to associated containers classes only
|
||||
//! This is a mechanism used to restrict calling autonomous client predicted setter functions to the ProcessInput call chain only
|
||||
friend class NetworkInputVector;
|
||||
friend class MigrateNetworkInputVector;
|
||||
friend class NetworkInputArray;
|
||||
friend class NetworkInputMigrationVector;
|
||||
friend class NetworkInputHistory;
|
||||
friend class NetworkInputChild;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+9
-72
@@ -10,21 +10,21 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkInput/NetworkInputVector.h>
|
||||
#include <Source/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Source/NetworkInput/NetworkInputArray.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzNetworking/Serialization/DeltaSerializer.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
NetworkInputVector::NetworkInputVector()
|
||||
NetworkInputArray::NetworkInputArray()
|
||||
: m_owner()
|
||||
, m_inputs()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
NetworkInputVector::NetworkInputVector(const ConstNetworkEntityHandle& entityHandle)
|
||||
NetworkInputArray::NetworkInputArray(const ConstNetworkEntityHandle& entityHandle)
|
||||
: m_owner(entityHandle)
|
||||
, m_inputs()
|
||||
{
|
||||
@@ -38,27 +38,27 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
NetworkInput& NetworkInputVector::operator[](uint32_t index)
|
||||
NetworkInput& NetworkInputArray::operator[](uint32_t index)
|
||||
{
|
||||
return m_inputs[index].m_networkInput;
|
||||
}
|
||||
|
||||
const NetworkInput& NetworkInputVector::operator[](uint32_t index) const
|
||||
const NetworkInput& NetworkInputArray::operator[](uint32_t index) const
|
||||
{
|
||||
return m_inputs[index].m_networkInput;
|
||||
}
|
||||
|
||||
void NetworkInputVector::SetPreviousInputId(ClientInputId previousInputId)
|
||||
void NetworkInputArray::SetPreviousInputId(ClientInputId previousInputId)
|
||||
{
|
||||
m_previousInputId = previousInputId;
|
||||
}
|
||||
|
||||
ClientInputId NetworkInputVector::GetPreviousInputId() const
|
||||
ClientInputId NetworkInputArray::GetPreviousInputId() const
|
||||
{
|
||||
return m_previousInputId;
|
||||
}
|
||||
|
||||
bool NetworkInputVector::Serialize(AzNetworking::ISerializer& serializer)
|
||||
bool NetworkInputArray::Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
// Always serialize the full first element
|
||||
if (!m_inputs[0].m_networkInput.Serialize(serializer))
|
||||
@@ -105,67 +105,4 @@ namespace Multiplayer
|
||||
serializer.Serialize(m_previousInputId, "PreviousInputId");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MigrateNetworkInputVector::MigrateNetworkInputVector()
|
||||
: m_owner()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
MigrateNetworkInputVector::MigrateNetworkInputVector(const ConstNetworkEntityHandle& entityHandle)
|
||||
: m_owner(entityHandle)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
uint32_t MigrateNetworkInputVector::GetSize() const
|
||||
{
|
||||
return aznumeric_cast<uint32_t>(m_inputs.size());
|
||||
}
|
||||
|
||||
NetworkInput& MigrateNetworkInputVector::operator[](uint32_t index)
|
||||
{
|
||||
return m_inputs[index].m_networkInput;
|
||||
}
|
||||
|
||||
const NetworkInput& MigrateNetworkInputVector::operator[](uint32_t index) const
|
||||
{
|
||||
return m_inputs[index].m_networkInput;
|
||||
}
|
||||
|
||||
bool MigrateNetworkInputVector::PushBack(const NetworkInput& networkInput)
|
||||
{
|
||||
if (m_inputs.size() < m_inputs.capacity())
|
||||
{
|
||||
m_inputs.push_back(networkInput);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MigrateNetworkInputVector::Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
NetEntityId ownerId = m_owner.GetNetEntityId();
|
||||
serializer.Serialize(ownerId, "OwnerId");
|
||||
|
||||
uint32_t inputCount = aznumeric_cast<uint32_t>(m_inputs.size());
|
||||
serializer.Serialize(inputCount, "InputCount");
|
||||
|
||||
if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)
|
||||
{
|
||||
// make sure all the possible NetworkInputs get attached prior to serialization, this double sends the size, but this message is only sent on server migration
|
||||
m_inputs.resize(inputCount);
|
||||
m_owner = GetNetworkEntityManager()->GetEntity(ownerId);
|
||||
NetBindComponent* netBindComponent = m_owner.GetNetBindComponent();
|
||||
if (netBindComponent)
|
||||
{
|
||||
for (uint32_t i = 0; i < m_inputs.size(); ++i)
|
||||
{
|
||||
m_inputs[i].m_networkInput.AttachNetBindComponent(netBindComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return serializer.Serialize(m_inputs, "Inputs");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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/NetworkInput/NetworkInput.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! @class NetworkInputArray
|
||||
//! @brief An array of network inputs. Used to mitigate loss of input packets on the server. Compresses subsequent elements.
|
||||
class NetworkInputArray final
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t MaxElements = 8; // Never try to replicate a list larger than this amount
|
||||
|
||||
NetworkInputArray();
|
||||
NetworkInputArray(const ConstNetworkEntityHandle& entityHandle);
|
||||
~NetworkInputArray() = default;
|
||||
|
||||
NetworkInput& operator[](uint32_t index);
|
||||
const NetworkInput& operator[](uint32_t index) const;
|
||||
|
||||
void SetPreviousInputId(ClientInputId previousInputId);
|
||||
ClientInputId GetPreviousInputId() const;
|
||||
|
||||
bool Serialize(AzNetworking::ISerializer& serializer);
|
||||
|
||||
private:
|
||||
|
||||
struct Wrapper // Strictly a workaround to deal with the private constructor of NetworkInput
|
||||
{
|
||||
Wrapper() : m_networkInput() {}
|
||||
Wrapper(const NetworkInput& networkInput) : m_networkInput(networkInput) {}
|
||||
NetworkInput m_networkInput;
|
||||
};
|
||||
|
||||
ConstNetworkEntityHandle m_owner;
|
||||
AZStd::array<Wrapper, MaxElements> m_inputs;
|
||||
ClientInputId m_previousInputId;
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkInput/NetworkInputMigrationVector.h>
|
||||
#include <Include/INetworkEntityManager.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
NetworkInputMigrationVector::NetworkInputMigrationVector()
|
||||
: m_owner()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
NetworkInputMigrationVector::NetworkInputMigrationVector(const ConstNetworkEntityHandle& entityHandle)
|
||||
: m_owner(entityHandle)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
uint32_t NetworkInputMigrationVector::GetSize() const
|
||||
{
|
||||
return aznumeric_cast<uint32_t>(m_inputs.size());
|
||||
}
|
||||
|
||||
NetworkInput& NetworkInputMigrationVector::operator[](uint32_t index)
|
||||
{
|
||||
return m_inputs[index].m_networkInput;
|
||||
}
|
||||
|
||||
const NetworkInput& NetworkInputMigrationVector::operator[](uint32_t index) const
|
||||
{
|
||||
return m_inputs[index].m_networkInput;
|
||||
}
|
||||
|
||||
bool NetworkInputMigrationVector::PushBack(const NetworkInput& networkInput)
|
||||
{
|
||||
if (m_inputs.size() < m_inputs.capacity())
|
||||
{
|
||||
m_inputs.push_back(networkInput);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkInputMigrationVector::Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
NetEntityId ownerId = m_owner.GetNetEntityId();
|
||||
serializer.Serialize(ownerId, "OwnerId");
|
||||
|
||||
uint32_t inputCount = aznumeric_cast<uint32_t>(m_inputs.size());
|
||||
serializer.Serialize(inputCount, "InputCount");
|
||||
|
||||
if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)
|
||||
{
|
||||
// make sure all the possible NetworkInputs get attached prior to serialization, this double sends the size, but this message is only sent on server migration
|
||||
m_inputs.resize(inputCount);
|
||||
m_owner = GetNetworkEntityManager()->GetEntity(ownerId);
|
||||
NetBindComponent* netBindComponent = m_owner.GetNetBindComponent();
|
||||
if (netBindComponent)
|
||||
{
|
||||
for (uint32_t i = 0; i < m_inputs.size(); ++i)
|
||||
{
|
||||
m_inputs[i].m_networkInput.AttachNetBindComponent(netBindComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return serializer.Serialize(m_inputs, "Inputs");
|
||||
}
|
||||
}
|
||||
+7
-39
@@ -13,54 +13,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <Source/NetworkInput/NetworkInput.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <Include/NetworkEntityHandle.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! @class NetworkInputVector
|
||||
//! @brief An array of network inputs. Used to mitigate loss of input packets on the server. Compresses subsequent elements.
|
||||
class NetworkInputVector final
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t MaxElements = 8; // Never try to replicate a list larger than this amount
|
||||
|
||||
NetworkInputVector();
|
||||
NetworkInputVector(const ConstNetworkEntityHandle& entityHandle);
|
||||
~NetworkInputVector() = default;
|
||||
|
||||
NetworkInput& operator[](uint32_t index);
|
||||
const NetworkInput& operator[](uint32_t index) const;
|
||||
|
||||
void SetPreviousInputId(ClientInputId previousInputId);
|
||||
ClientInputId GetPreviousInputId() const;
|
||||
|
||||
bool Serialize(AzNetworking::ISerializer& serializer);
|
||||
|
||||
private:
|
||||
|
||||
struct Wrapper // Strictly a workaround to deal with the private constructor of NetworkInput
|
||||
{
|
||||
Wrapper() : m_networkInput() {}
|
||||
Wrapper(const NetworkInput& networkInput) : m_networkInput(networkInput) {}
|
||||
NetworkInput m_networkInput;
|
||||
};
|
||||
|
||||
ConstNetworkEntityHandle m_owner;
|
||||
AZStd::fixed_vector<Wrapper, MaxElements> m_inputs;
|
||||
ClientInputId m_previousInputId;
|
||||
};
|
||||
|
||||
//! @class MigrateNetworkInputVector
|
||||
//! @class NetworkInputMigrationVector
|
||||
//! @brief A variable sized array of input commands, used specifically when migrate a clients inputs.
|
||||
class MigrateNetworkInputVector final
|
||||
class NetworkInputMigrationVector final
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t MaxElements = 90; // Never try to migrate a list larger than this amount, bumped up to handle DTLS connection time
|
||||
|
||||
MigrateNetworkInputVector();
|
||||
MigrateNetworkInputVector(const ConstNetworkEntityHandle& entityHandle);
|
||||
virtual ~MigrateNetworkInputVector() = default;
|
||||
NetworkInputMigrationVector();
|
||||
NetworkInputMigrationVector(const ConstNetworkEntityHandle& entityHandle);
|
||||
virtual ~NetworkInputMigrationVector() = default;
|
||||
|
||||
uint32_t GetSize() const;
|
||||
NetworkInput& operator[](uint32_t index);
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Time/ITime.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.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
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(INetworkTime, "{7D468063-255B-4FEE-86E1-6D750EEDD42A}");
|
||||
|
||||
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;
|
||||
|
||||
//! 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;
|
||||
|
||||
//! 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 applications current frameId (may be rewound on the server during backward reconciliation).
|
||||
//! @return the applications current frameId
|
||||
virtual ApplicationFrameId GetApplicationFrameId() const = 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;
|
||||
|
||||
//! Synchronizes rewindable entity state for the current application time.
|
||||
virtual void SyncRewindableEntityState() = 0;
|
||||
|
||||
//! Get the controlling connection that may be currently altering global game time.
|
||||
//! Note this abstraction is required at a relatively high level to allow for 'don't rewind the shooter' semantics
|
||||
//! @return the ConnectionId of the connection requesting the rewind operation
|
||||
virtual AzNetworking::ConnectionId GetRewindingConnectionId() const = 0;
|
||||
|
||||
//! Get the controlling connection that may be currently altering global game time.
|
||||
//! Note this abstraction is required at a relatively high level to allow for 'don't rewind the shooter' semantics
|
||||
//! @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;
|
||||
|
||||
//! Alters the current ApplicationFrameId and binds that alteration to the provided ConnectionId.
|
||||
//! @param frameId the new ApplicationFrameId to use
|
||||
//! @param rewindConnectionId the rewinding ConnectionId
|
||||
virtual void AlterApplicationFrameId(ApplicationFrameId frameId, AzNetworking::ConnectionId rewindConnectionId) = 0;
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(INetworkTime);
|
||||
};
|
||||
|
||||
// EBus wrapper for ScriptCanvas
|
||||
class INetworkTimeRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
};
|
||||
using INetworkTimeRequestBus = AZ::EBus<INetworkTime, INetworkTimeRequests>;
|
||||
|
||||
//! @class ScopedAlterTime
|
||||
//! @brief This is a wrapper that temporarily adjusts global program time for backward reconciliation purposes.
|
||||
class ScopedAlterTime final
|
||||
{
|
||||
public:
|
||||
inline ScopedAlterTime(ApplicationFrameId frameId, AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
INetworkTime* time = AZ::Interface<INetworkTime>::Get();
|
||||
m_previousApplicationFrameId = time->GetApplicationFrameId();
|
||||
m_previousRewindConnectionId = time->GetRewindingConnectionId();
|
||||
time->AlterApplicationFrameId(frameId, connectionId);
|
||||
}
|
||||
inline ~ScopedAlterTime()
|
||||
{
|
||||
INetworkTime* time = AZ::Interface<INetworkTime>::Get();
|
||||
time->AlterApplicationFrameId(m_previousApplicationFrameId, m_previousRewindConnectionId);
|
||||
}
|
||||
private:
|
||||
ApplicationFrameId m_previousApplicationFrameId = InvalidApplicationFrameId;
|
||||
AzNetworking::ConnectionId m_previousRewindConnectionId = AzNetworking::InvalidConnectionId;
|
||||
};
|
||||
}
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::ApplicationFrameId);
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,42 +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 <Include/MultiplayerTypes.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityHandle.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
struct EntityReplicationData
|
||||
{
|
||||
EntityReplicationData() = default;
|
||||
NetEntityRole m_netEntityRole = NetEntityRole::InvalidRole;
|
||||
float m_priority = 0.0f;
|
||||
};
|
||||
using ReplicationSet = AZStd::unordered_map<ConstNetworkEntityHandle, EntityReplicationData>;
|
||||
|
||||
class IReplicationWindow
|
||||
{
|
||||
public:
|
||||
virtual ~IReplicationWindow() = default;
|
||||
|
||||
virtual bool ReplicationSetUpdateReady() = 0;
|
||||
virtual const ReplicationSet& GetReplicationSet() const = 0;
|
||||
//! Max number of entities we can send updates for in one frame
|
||||
virtual uint32_t GetMaxEntityReplicatorSendCount() const = 0;
|
||||
virtual bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const = 0;
|
||||
virtual void UpdateWindow() = 0;
|
||||
virtual void DebugDraw() const = 0;
|
||||
};
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "NullReplicationWindow.h"
|
||||
#include <Source/ReplicationWindows/NullReplicationWindow.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/ReplicationWindows/IReplicationWindow.h>
|
||||
#include <Include/IReplicationWindow.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
+20
-15
@@ -56,6 +56,8 @@ namespace Multiplayer
|
||||
|
||||
ServerToClientReplicationWindow::ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, const AzNetworking::IConnection* connection)
|
||||
: m_controlledEntity(controlledEntity)
|
||||
, m_entityActivatedEventHandler([this](AZ::Entity* entity) { OnEntityActivated(entity); })
|
||||
, m_entityDeactivatedEventHandler([this](AZ::Entity* entity) { OnEntityDeactivated(entity); })
|
||||
, m_connection(connection)
|
||||
, m_lastCheckedSentPackets(connection->GetMetrics().m_packetsSent)
|
||||
, m_lastCheckedLostPackets(connection->GetMetrics().m_packetsLost)
|
||||
@@ -74,7 +76,9 @@ namespace Multiplayer
|
||||
//}
|
||||
|
||||
m_updateWindowEvent.Enqueue(sv_ClientReplicationWindowUpdateMs, true);
|
||||
AZ::EntitySystemBus::Handler::BusConnect();
|
||||
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityActivatedEventHandler(m_entityActivatedEventHandler);
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityDeactivatedEventHandler(m_entityDeactivatedEventHandler);
|
||||
}
|
||||
|
||||
bool ServerToClientReplicationWindow::ReplicationSetUpdateReady()
|
||||
@@ -146,24 +150,29 @@ namespace Multiplayer
|
||||
}
|
||||
);
|
||||
|
||||
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
|
||||
|
||||
// Add all the neighbors
|
||||
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
|
||||
{
|
||||
// TODO: Discard entities that don't have a NetBindComponent
|
||||
|
||||
//if (mp_ControlledFilteredEntityComponent && mp_ControlledFilteredEntityComponent->IsEntityFiltered(iterator.Get()))
|
||||
//{
|
||||
// continue;
|
||||
//}
|
||||
|
||||
// We want to find the closest extent to the player and prioritize using that distance
|
||||
const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
|
||||
const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
|
||||
const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
|
||||
const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
|
||||
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
|
||||
NetworkEntityHandle entityHandle(entity, GetNetworkEntityTracker());
|
||||
AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
|
||||
NetBindComponent* entryNetBindComponent = entity->template FindComponent<NetBindComponent>();
|
||||
if (entryNetBindComponent != nullptr)
|
||||
{
|
||||
const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
|
||||
const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
|
||||
const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
|
||||
const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
|
||||
|
||||
NetworkEntityHandle entityHandle(entryNetBindComponent, networkEntityTracker);
|
||||
AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
|
||||
}
|
||||
}
|
||||
|
||||
// Add in Autonomous Entities
|
||||
@@ -200,10 +209,8 @@ namespace Multiplayer
|
||||
//}
|
||||
}
|
||||
|
||||
void ServerToClientReplicationWindow::OnEntityActivated(const AZ::EntityId& entityId)
|
||||
void ServerToClientReplicationWindow::OnEntityActivated(AZ::Entity* entity)
|
||||
{
|
||||
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
|
||||
|
||||
ConstNetworkEntityHandle entityHandle(entity, GetNetworkEntityTracker());
|
||||
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
@@ -231,10 +238,8 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void ServerToClientReplicationWindow::OnEntityDeactivated(const AZ::EntityId& entityId)
|
||||
void ServerToClientReplicationWindow::OnEntityDeactivated(AZ::Entity* entity)
|
||||
{
|
||||
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
|
||||
|
||||
ConstNetworkEntityHandle entityHandle(entity, GetNetworkEntityTracker());
|
||||
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
|
||||
@@ -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>
|
||||
@@ -27,7 +27,6 @@ namespace Multiplayer
|
||||
|
||||
class ServerToClientReplicationWindow
|
||||
: public IReplicationWindow
|
||||
, public AZ::EntitySystemBus::Handler
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -56,11 +55,8 @@ namespace Multiplayer
|
||||
//! @}
|
||||
|
||||
private:
|
||||
//! EntitySystemBus interface
|
||||
//! @{
|
||||
void OnEntityActivated(const AZ::EntityId&) override;
|
||||
void OnEntityDeactivated(const AZ::EntityId&) override;
|
||||
//! @}
|
||||
void OnEntityActivated(AZ::Entity* entity);
|
||||
void OnEntityDeactivated(AZ::Entity* entity);
|
||||
|
||||
//void CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController);
|
||||
//void OnAddFilteredEntity(NetEntityId filteredEntityId);
|
||||
@@ -79,6 +75,9 @@ namespace Multiplayer
|
||||
NetworkEntityHandle m_controlledEntity;
|
||||
AZ::TransformInterface* m_controlledEntityTransform = nullptr;
|
||||
|
||||
AZ::EntityActivatedEvent::Handler m_entityActivatedEventHandler;
|
||||
AZ::EntityDeactivatedEvent::Handler m_entityDeactivatedEventHandler;
|
||||
|
||||
//FilteredEntityComponent::Authority* m_controlledFilteredEntityComponent = nullptr;
|
||||
//NetBindComponent* m_controlledNetBindComponent = nullptr;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user