diff --git a/Code/Framework/AzNetworking/AzNetworking/AutoGen/AutoPacketDispatcher_Header.jinja b/Code/Framework/AzNetworking/AzNetworking/AutoGen/AutoPacketDispatcher_Header.jinja index 63442ea1eb..1e9edc56bd 100644 --- a/Code/Framework/AzNetworking/AzNetworking/AutoGen/AutoPacketDispatcher_Header.jinja +++ b/Code/Framework/AzNetworking/AzNetworking/AutoGen/AutoPacketDispatcher_Header.jinja @@ -16,7 +16,7 @@ namespace {{ xml.attrib['Name'] }} //! @param handler the handler used to handle the received packet //! @return boolean true on successful dispatch, false if the request was not handled template - bool DispatchPacket(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer, HANDLER& handler); + AzNetworking::PacketDispatchResult DispatchPacket(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer, HANDLER& handler); } {% endfor %} diff --git a/Code/Framework/AzNetworking/AzNetworking/AutoGen/AutoPacketDispatcher_Inline.jinja b/Code/Framework/AzNetworking/AzNetworking/AutoGen/AutoPacketDispatcher_Inline.jinja index ac659820c3..9767828f3a 100644 --- a/Code/Framework/AzNetworking/AzNetworking/AutoGen/AutoPacketDispatcher_Inline.jinja +++ b/Code/Framework/AzNetworking/AzNetworking/AutoGen/AutoPacketDispatcher_Inline.jinja @@ -2,7 +2,7 @@ namespace {{ xml.attrib['Name'] }} { template - inline bool DispatchPacket(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer, HANDLER& handler) + inline AzNetworking::PacketDispatchResult DispatchPacket(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer, HANDLER& handler) { switch (aznumeric_cast(packetHeader.GetPacketType())) { @@ -10,16 +10,26 @@ namespace {{ xml.attrib['Name'] }} case aznumeric_cast({{ Packet.attrib['Name'] }}::Type): { AZLOG(Debug_DispatchPackets, "Received packet %s", "{{ Packet.attrib['Name'] }}"); +{% if ('HandshakePacket' not in Packet.attrib) or (Packet.attrib['HandshakePacket'] == 'false') %} + if (!handler.IsHandshakeComplete()) + { + return AzNetworking::PacketDispatchResult::Pending; + } +{% endif %} + {{ Packet.attrib['Name'] }} packet; if (!serializer.Serialize(packet, "Packet")) { - return false; + return AzNetworking::PacketDispatchResult::Failure; + } + if(handler.HandleRequest(connection, packetHeader, packet)) + { + return AzNetworking::PacketDispatchResult::Success; } - return handler.HandleRequest(connection, packetHeader, packet); } {% endfor %} } - return false; + return AzNetworking::PacketDispatchResult::Failure; } } {% endfor %} diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnectionListener.h b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnectionListener.h index 41e79e2207..f93e7d72f6 100644 --- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnectionListener.h +++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnectionListener.h @@ -45,8 +45,8 @@ namespace AzNetworking //! @param connection pointer to the connection instance generating the event //! @param packetHeader packet header of the associated payload //! @param serializer serializer instance containing the transmitted payload - //! @return boolean true to signal success, false to disconnect with a transport error - virtual bool OnPacketReceived(IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer) = 0; + //! @return PacketDispatchResult result of the packet handling attempt + virtual PacketDispatchResult OnPacketReceived(IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer) = 0; //! Called when a packet is deemed lost by the remote connection. //! @param connection pointer to the connection instance generating the event diff --git a/Code/Framework/AzNetworking/AzNetworking/PacketLayer/IPacketHeader.h b/Code/Framework/AzNetworking/AzNetworking/PacketLayer/IPacketHeader.h index ea9734a867..f7ca7f0166 100644 --- a/Code/Framework/AzNetworking/AzNetworking/PacketLayer/IPacketHeader.h +++ b/Code/Framework/AzNetworking/AzNetworking/PacketLayer/IPacketHeader.h @@ -15,6 +15,12 @@ namespace AzNetworking { + AZ_ENUM_CLASS(PacketDispatchResult + , Success + , Pending + , Failure + ); + AZ_ENUM_CLASS(PacketFlag , Compressed , MAX diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp index 3efc6a51a8..7b451865c4 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp @@ -236,14 +236,14 @@ namespace AzNetworking return true; } - bool UdpConnection::HandleCorePacket(IConnectionListener& connectionListener, UdpPacketHeader& header, ISerializer& serializer) + PacketDispatchResult UdpConnection::HandleCorePacket(IConnectionListener& connectionListener, UdpPacketHeader& header, ISerializer& serializer) { switch (static_cast(header.GetPacketType())) { case CorePackets::PacketType::InitiateConnectionPacket: { AZLOG(NET_CorePackets, "Received core packet %s", "InitiateConnection"); - return true; + return PacketDispatchResult::Success; } break; @@ -253,7 +253,7 @@ namespace AzNetworking CorePackets::ConnectionHandshakePacket packet; if (!serializer.Serialize(packet, "Packet")) { - return false; + return PacketDispatchResult::Failure; } if (m_state != ConnectionState::Connected) @@ -264,7 +264,7 @@ namespace AzNetworking } } - return true; + return PacketDispatchResult::Success; } break; @@ -274,10 +274,10 @@ namespace AzNetworking CorePackets::TerminateConnectionPacket packet; if (!serializer.Serialize(packet, "Packet")) { - return false; + return PacketDispatchResult::Failure; } Disconnect(packet.GetDisconnectReason(), TerminationEndpoint::Remote); - return true; + return PacketDispatchResult::Success; } break; @@ -287,10 +287,10 @@ namespace AzNetworking CorePackets::HeartbeatPacket packet; if (!serializer.Serialize(packet, "Packet")) { - return false; + return PacketDispatchResult::Failure; } // Do nothing, we've already processed our ack packets - return true; + return PacketDispatchResult::Success; } break; @@ -302,6 +302,6 @@ namespace AzNetworking AZ_Assert(false, "Unhandled core packet type!"); } - return false; + return PacketDispatchResult::Failure; } } diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h index 67d626d6cb..3d29cfcd23 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h @@ -131,7 +131,7 @@ namespace AzNetworking //! @param header the packet header received to process //! @param serializer the output serializer containing the transmitted packet data //! @return boolean true on successful handling of the received header - bool HandleCorePacket(IConnectionListener& listener, UdpPacketHeader& header, ISerializer& serializer); + PacketDispatchResult HandleCorePacket(IConnectionListener& listener, UdpPacketHeader& header, ISerializer& serializer); AZ_DISABLE_COPY_MOVE(UdpConnection); diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp index 66fe6f30eb..fa4ee78a92 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp @@ -37,14 +37,14 @@ namespace AzNetworking return m_sequenceGenerator.GetNextSequenceId(); } - bool UdpFragmentQueue::ProcessReceivedChunk(UdpConnection* connection, IConnectionListener& connectionListener, UdpPacketHeader& header, ISerializer& serializer) + PacketDispatchResult UdpFragmentQueue::ProcessReceivedChunk(UdpConnection* connection, IConnectionListener& connectionListener, UdpPacketHeader& header, ISerializer& serializer) { AZStd::unique_ptr packet = AZStd::make_unique(); if (!serializer.Serialize(*packet, "Packet")) { AZLOG(NET_FragmentQueue, "Fragment failed serialization"); - return false; + return PacketDispatchResult::Failure; } const bool isReliable = header.GetIsReliable(); @@ -63,14 +63,14 @@ namespace AzNetworking { // Too old to process AZLOG(NET_FragmentQueue, "Fragment sequence ID is outside our tracked window"); - return false; + return PacketDispatchResult::Failure; } if (m_deliveredFragments.GetBit(static_cast(sequenceDelta))) { // Received packet is a duplicate of one already forwarded to gameplay AZLOG(NET_FragmentQueue, "Received duplicate of fragmented packet %u, discarding", static_cast(fragmentSequence)); - return true; + return PacketDispatchResult::Success; } const uint32_t chunkCount = packet->GetChunkCount(); @@ -89,7 +89,7 @@ namespace AzNetworking { // Either we disagree on the number of chunks, or chunkIndex is bigger than the expected size, bail and disconnect AZLOG(NET_FragmentQueue, "Malformed chunk metadata in fragmented packet, chunkIndex %u, chunkCount %u, reservedSize %u", chunkIndex, chunkCount, static_cast(packetFragments.size())); - return false; + return PacketDispatchResult::Failure; } packetFragments[chunkIndex] = AZStd::move(packet); @@ -105,7 +105,7 @@ namespace AzNetworking } // We haven't received all chunks required to complete this packet yet - return true; + return PacketDispatchResult::Success; } totalPacketSize += static_cast(packetFragments[index]->GetChunkBuffer().GetSize()); @@ -119,7 +119,7 @@ namespace AzNetworking if (!buffer.Resize(totalPacketSize)) { AZLOG_ERROR("Fragmented packet is too large to fit in UdpPacketEncodingBuffer"); - return false; + return PacketDispatchResult::Failure; } uint8_t* bufferPointer = buffer.GetBuffer(); @@ -141,17 +141,17 @@ namespace AzNetworking if (!header.SerializePacketFlags(networkSerializer)) { AZLOG(NET_FragmentQueue, "Reconstructed fragmented packet failed packet flags serialization"); - return false; + return PacketDispatchResult::Failure; } if (!networkISerializer.Serialize(header, "Header")) { AZLOG(NET_FragmentQueue, "Reconstructed fragmented packet failed header serialization"); - return false; + return PacketDispatchResult::Failure; } } connection->GetPacketTracker().ProcessReceived(connection, header); - bool handledPacket = false; + PacketDispatchResult handledPacket; if (header.GetPacketType() < aznumeric_cast(CorePackets::PacketType::MAX)) { handledPacket = connection->HandleCorePacket(connectionListener, header, networkSerializer); diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h index 38c4df4f0e..15d4cfa10c 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -44,8 +45,8 @@ namespace AzNetworking //! @param connectionListener the connection listener for delivery of completed packets //! @param header the chunk packet header //! @param serializer the serializer containing the chunk body - //! @return boolean true if the chunk was processed, false if an error was encountered - bool ProcessReceivedChunk(UdpConnection* connection, IConnectionListener& connectionListener, UdpPacketHeader& header, ISerializer& serializer); + //! @return PacketDispatchResult result of processing the chunk + PacketDispatchResult ProcessReceivedChunk(UdpConnection* connection, IConnectionListener& connectionListener, UdpPacketHeader& header, ISerializer& serializer); private: diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp index d50b20831f..983b425b60 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp @@ -277,7 +277,7 @@ namespace AzNetworking timeoutItem->UpdateTimeoutTime(startTimeMs); - bool handledPacket = false; + PacketDispatchResult handledPacket; if (header.GetPacketType() < aznumeric_cast(CorePackets::PacketType::MAX)) { handledPacket = connection->HandleCorePacket(m_connectionListener, header, packetSerializer); @@ -287,7 +287,7 @@ namespace AzNetworking handledPacket = m_connectionListener.OnPacketReceived(connection, header, packetSerializer); } - if (handledPacket) + if (handledPacket == PacketDispatchResult::Success) { connection->UpdateHeartbeat(currentTimeMs); if (connection->GetConnectionState() == ConnectionState::Connecting && !connection->GetDtlsEndpoint().IsConnecting()) @@ -299,10 +299,16 @@ namespace AzNetworking else if (m_socket->IsEncrypted() && connection->GetDtlsEndpoint().IsConnecting() && !IsHandshakePacket(connection->GetDtlsEndpoint(), header.GetPacketType())) { - // It's possible for one side to finish its half of the handshake and start sending encrypted data + // It's possible for one side to finish its half of the encryption handshake and start sending encrypted data + // This will appear as a SerializationError due to the incomplete encryption handshake // If it's not an expected unencrypted type then skip it for now continue; } + else if (handledPacket == PacketDispatchResult::Pending) + { + // If we did not handle due to a handshake pending completion, defer it + continue; + } else if (connection->GetConnectionState() != ConnectionState::Disconnecting) { connection->Disconnect(DisconnectReason::StreamError, TerminationEndpoint::Local); diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index ce8931107f..203750d761 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -7,12 +7,12 @@ - + - + diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index fc398182ef..e634cb52fc 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -168,7 +168,7 @@ namespace Multiplayer ; } - bool MultiplayerEditorConnection::OnPacketReceived(AzNetworking::IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer) + AzNetworking::PacketDispatchResult MultiplayerEditorConnection::OnPacketReceived(AzNetworking::IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer) { return MultiplayerEditorPackets::DispatchPacket(connection, packetHeader, serializer, *this); } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h index 8c19848db7..8f1f90cc6b 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h @@ -33,6 +33,7 @@ namespace Multiplayer MultiplayerEditorConnection(); ~MultiplayerEditorConnection() = default; + bool IsHandshakeComplete(){ return true; }; bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerInit& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReady& packet); @@ -40,7 +41,7 @@ namespace Multiplayer //! @{ AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; void OnConnect(AzNetworking::IConnection* connection) override; - bool OnPacketReceived(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; + AzNetworking::PacketDispatchResult OnPacketReceived(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; void OnPacketLost(AzNetworking::IConnection* connection, AzNetworking::PacketId packetId) override; void OnDisconnect(AzNetworking::IConnection* connection, AzNetworking::DisconnectReason reason, AzNetworking::TerminationEndpoint endpoint) override; //! @} diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index b0a8350982..c36f2e0f11 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -441,6 +441,11 @@ namespace Multiplayer MultiplayerPackets::SyncConsole m_syncPacket; }; + bool MultiplayerSystemComponent::IsHandshakeComplete() + { + return m_didHandshake; + } + bool MultiplayerSystemComponent::HandleRequest ( [[maybe_unused]] AzNetworking::IConnection* connection, @@ -465,6 +470,8 @@ namespace Multiplayer if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map))) { + m_didHandshake = true; + // Sync our console ConsoleReplicator consoleReplicator(connection); AZ::Interface::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); }); @@ -480,6 +487,8 @@ namespace Multiplayer [[maybe_unused]] MultiplayerPackets::Accept& packet ) { + m_didHandshake = true; + AZ::CVarFixedString commandString = "sv_map " + packet.GetMap(); AZ::Interface::Get()->PerformCommand(commandString.c_str()); @@ -670,7 +679,7 @@ namespace Multiplayer } } - bool MultiplayerSystemComponent::OnPacketReceived(AzNetworking::IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer) + AzNetworking::PacketDispatchResult MultiplayerSystemComponent::OnPacketReceived(AzNetworking::IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer) { return MultiplayerPackets::DispatchPacket(connection, packetHeader, serializer, *this); } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index e2fb7deacc..74ba2ddf34 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -76,6 +76,7 @@ namespace Multiplayer int GetTickOrder() override; //! @} + bool IsHandshakeComplete(); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Connect& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Accept& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet); @@ -89,7 +90,7 @@ namespace Multiplayer //! @{ AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; void OnConnect(AzNetworking::IConnection* connection) override; - bool OnPacketReceived(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; + AzNetworking::PacketDispatchResult OnPacketReceived(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; void OnPacketLost(AzNetworking::IConnection* connection, AzNetworking::PacketId packetId) override; void OnDisconnect(AzNetworking::IConnection* connection, AzNetworking::DisconnectReason reason, AzNetworking::TerminationEndpoint endpoint) override; //! @} @@ -158,6 +159,7 @@ namespace Multiplayer double m_serverSendAccumulator = 0.0; float m_renderBlendFactor = 0.0f; float m_tickFactor = 0.0f; + bool m_didHandshake = false; #if !defined(AZ_RELEASE_BUILD) MultiplayerEditorConnection m_editorConnectionListener;