Reverts changes to component application and adds further client migration handling hookup

Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
kberg-amzn
2021-09-21 19:33:59 -07:00
parent ca7de715fd
commit d28bcbe027
8 changed files with 120 additions and 92 deletions
@@ -121,9 +121,9 @@ namespace AZ
return environment ? environment->Get() : nullptr; return environment ? environment->Get() : nullptr;
} }
ComponentApplication::EventLoggerDeleter::EventLoggerDeleter() noexcept= default; ComponentApplication::EventLoggerDeleter::EventLoggerDeleter() noexcept = default;
ComponentApplication::EventLoggerDeleter::EventLoggerDeleter(bool skipDelete) noexcept ComponentApplication::EventLoggerDeleter::EventLoggerDeleter(bool skipDelete) noexcept
: m_skipDelete{skipDelete} : m_skipDelete{ skipDelete }
{} {}
void ComponentApplication::EventLoggerDeleter::operator()(AZ::Debug::LocalFileEventLogger* ptr) void ComponentApplication::EventLoggerDeleter::operator()(AZ::Debug::LocalFileEventLogger* ptr)
{ {
@@ -332,27 +332,27 @@ namespace AZ
->Value("Stack trace always", Debug::AllocationRecords::RECORD_FULL); ->Value("Stack trace always", Debug::AllocationRecords::RECORD_FULL);
ec->Class<Descriptor>("System memory settings", "Settings for managing application memory usage") ec->Class<Descriptor>("System memory settings", "Settings for managing application memory usage")
->ClassElement(Edit::ClassElements::EditorData, "") ->ClassElement(Edit::ClassElements::EditorData, "")
->Attribute(Edit::Attributes::AutoExpand, true) ->Attribute(Edit::Attributes::AutoExpand, true)
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_grabAllMemory, "Allocate all memory at startup", "Allocate all system memory at startup if enabled, or allocate as needed if disabled") ->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_grabAllMemory, "Allocate all memory at startup", "Allocate all system memory at startup if enabled, or allocate as needed if disabled")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_allocationRecords, "Record allocations", "Collect information on each allocation made for debugging purposes (ignored in Release builds)") ->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_allocationRecords, "Record allocations", "Collect information on each allocation made for debugging purposes (ignored in Release builds)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_allocationRecordsSaveNames, "Record allocations with name saving", "Saves names/filenames information on each allocation made, useful for tracking down leaks in dynamic modules (ignored in Release builds)") ->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_allocationRecordsSaveNames, "Record allocations with name saving", "Saves names/filenames information on each allocation made, useful for tracking down leaks in dynamic modules (ignored in Release builds)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_allocationRecordsAttemptDecodeImmediately, "Record allocations and attempt immediate decode", "Decode callstacks for each allocation when they occur, used for tracking allocations that fail decoding. Very expensive. (ignored in Release builds)") ->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_allocationRecordsAttemptDecodeImmediately, "Record allocations and attempt immediate decode", "Decode callstacks for each allocation when they occur, used for tracking allocations that fail decoding. Very expensive. (ignored in Release builds)")
->DataElement(Edit::UIHandlers::ComboBox, &Descriptor::m_recordingMode, "Stack recording mode", "Stack record mode. (Ignored in final builds)") ->DataElement(Edit::UIHandlers::ComboBox, &Descriptor::m_recordingMode, "Stack recording mode", "Stack record mode. (Ignored in final builds)")
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_stackRecordLevels, "Stack entries to record", "Number of stack levels to record for each allocation (ignored in Release builds)") ->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_stackRecordLevels, "Stack entries to record", "Number of stack levels to record for each allocation (ignored in Release builds)")
->Attribute(Edit::Attributes::Step, 1) ->Attribute(Edit::Attributes::Step, 1)
->Attribute(Edit::Attributes::Max, 1024) ->Attribute(Edit::Attributes::Max, 1024)
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_autoIntegrityCheck, "Validate allocations", "Check allocations for integrity on each allocation/free (ignored in Release builds)") ->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_autoIntegrityCheck, "Validate allocations", "Check allocations for integrity on each allocation/free (ignored in Release builds)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_markUnallocatedMemory, "Mark freed memory", "Set memory to 0xcd when a block is freed for debugging (ignored in Release builds)") ->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_markUnallocatedMemory, "Mark freed memory", "Set memory to 0xcd when a block is freed for debugging (ignored in Release builds)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_doNotUsePools, "Don't pool allocations", "Pipe pool allocations in system/tree heap (ignored in Release builds)") ->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_doNotUsePools, "Don't pool allocations", "Pipe pool allocations in system/tree heap (ignored in Release builds)")
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_pageSize, "Page size", "Memory page size in bytes (must be OS page size aligned)") ->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_pageSize, "Page size", "Memory page size in bytes (must be OS page size aligned)")
->Attribute(Edit::Attributes::Step, 1024) ->Attribute(Edit::Attributes::Step, 1024)
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_poolPageSize, "Pool page size", "Memory pool page size in bytes (must be a multiple of page size)") ->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_poolPageSize, "Pool page size", "Memory pool page size in bytes (must be a multiple of page size)")
->Attribute(Edit::Attributes::Max, &Descriptor::m_pageSize) ->Attribute(Edit::Attributes::Max, &Descriptor::m_pageSize)
->Attribute(Edit::Attributes::Step, 1024) ->Attribute(Edit::Attributes::Step, 1024)
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_memoryBlockAlignment, "Block alignment", "Memory block alignment in bytes (must be multiple of the page size)") ->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_memoryBlockAlignment, "Block alignment", "Memory block alignment in bytes (must be multiple of the page size)")
->Attribute(Edit::Attributes::Step, &Descriptor::m_pageSize) ->Attribute(Edit::Attributes::Step, &Descriptor::m_pageSize)
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_memoryBlocksByteSize, "Block size", "Memory block size in bytes (must be multiple of the page size)") ->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_memoryBlocksByteSize, "Block size", "Memory block size in bytes (must be multiple of the page size)")
->Attribute(Edit::Attributes::Step, &Descriptor::m_pageSize) ->Attribute(Edit::Attributes::Step, &Descriptor::m_pageSize)
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_reservedOS, "OS reserved memory", "System memory reserved for OS (used only when 'Allocate all memory at startup' is true)") ->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_reservedOS, "OS reserved memory", "System memory reserved for OS (used only when 'Allocate all memory at startup' is true)")
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_reservedDebug, "Memory reserved for debugger", "System memory reserved for Debug allocator, like memory tracking (used only when 'Allocate all memory at startup' is true)") ->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_reservedDebug, "Memory reserved for debugger", "System memory reserved for Debug allocator, like memory tracking (used only when 'Allocate all memory at startup' is true)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_enableDrilling, "Enable Driller", "Enable Drilling support for the application (ignored in Release builds)") ->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_enableDrilling, "Enable Driller", "Enable Drilling support for the application (ignored in Release builds)")
@@ -419,11 +419,11 @@ namespace AZ
} }
else else
{ {
azstrcpy(m_commandLineBuffer, AZ_ARRAY_SIZE(m_commandLineBuffer), "no_argv_supplied"); azstrcpy(m_commandLineBuffer, AZ_ARRAY_SIZE(m_commandLineBuffer), "no_argv_supplied");
// use a "valid" value here. This is because Qt and potentially other third party libraries require // use a "valid" value here. This is because Qt and potentially other third party libraries require
// that ArgC be 'at least 1' and that (*argV)[0] be a valid pointer to a real null terminated string. // that ArgC be 'at least 1' and that (*argV)[0] be a valid pointer to a real null terminated string.
m_argC = 1; m_argC = 1;
m_argV = &m_commandLineBufferAddress; m_argV = &m_commandLineBufferAddress;
} }
// Create the Event logger if it doesn't exist, otherwise reuse the one registered // Create the Event logger if it doesn't exist, otherwise reuse the one registered
@@ -557,8 +557,8 @@ namespace AZ
void ReportBadEngineRoot() void ReportBadEngineRoot()
{ {
AZStd::string errorMessage = {"Unable to determine a valid path to the engine.\n" AZStd::string errorMessage = { "Unable to determine a valid path to the engine.\n"
"Check parameters such as --project-path and --engine-path and make sure they are valid.\n"}; "Check parameters such as --project-path and --engine-path and make sure they are valid.\n" };
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr) if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
{ {
AZ::SettingsRegistryInterface::FixedValueString filePathErrorStr; AZ::SettingsRegistryInterface::FixedValueString filePathErrorStr;
@@ -614,7 +614,6 @@ namespace AZ
AZ_Assert(m_systemEntity, "SystemEntity failed to initialize!"); AZ_Assert(m_systemEntity, "SystemEntity failed to initialize!");
AddRequiredSystemComponents(m_systemEntity.get()); AddRequiredSystemComponents(m_systemEntity.get());
//m_currentTime = GetElapsedTimeUs();
m_isStarted = true; m_isStarted = true;
return m_systemEntity.get(); return m_systemEntity.get();
} }
@@ -1372,38 +1371,44 @@ namespace AZ
void ComponentApplication::Tick(float deltaOverride /*= -1.f*/) void ComponentApplication::Tick(float deltaOverride /*= -1.f*/)
{ {
AZ_PROFILE_SCOPE(System, "Component application simulation tick");
AZStd::chrono::system_clock::time_point now = AZStd::chrono::system_clock::now();
m_deltaTime = 0.0f;
if (now >= m_currentTime)
{ {
AZStd::chrono::duration<float> delta = now - m_currentTime; AZ_PROFILE_SCOPE(System, "Component application simulation tick");
m_deltaTime = deltaOverride >= 0.f ? deltaOverride : delta.count();
}
{
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:ExecuteQueuedEvents");
TickBus::ExecuteQueuedEvents();
}
m_currentTime = now;
{
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
}
// If tick rate limiting is on, ensure (1 / g_simulation_tick_rate) ms has elapsed since the last frame, AZStd::chrono::system_clock::time_point now = AZStd::chrono::system_clock::now();
// sleeping if there's still time remaining.
if (g_simulation_tick_rate > 0.f)
{
now = AZStd::chrono::system_clock::now();
// Work in microsecond durations here as that's the native measurement time for time_point m_deltaTime = 0.0f;
constexpr float microsecondsPerSecond = 1000.f * 1000.f;
const AZStd::chrono::microseconds timeBudgetPerTick(static_cast<int>(microsecondsPerSecond / g_simulation_tick_rate));
AZStd::chrono::microseconds timeUntilNextTick = m_currentTime + timeBudgetPerTick - now;
if (timeUntilNextTick.count() > 0) if (now >= m_currentTime)
{ {
AZStd::this_thread::sleep_for(timeUntilNextTick); AZStd::chrono::duration<float> delta = now - m_currentTime;
m_deltaTime = deltaOverride >= 0.f ? deltaOverride : delta.count();
}
{
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:ExecuteQueuedEvents");
TickBus::ExecuteQueuedEvents();
}
m_currentTime = now;
{
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
}
// If tick rate limiting is on, ensure (1 / g_simulation_tick_rate) ms has elapsed since the last frame,
// sleeping if there's still time remaining.
if (g_simulation_tick_rate > 0.f)
{
now = AZStd::chrono::system_clock::now();
// Work in microsecond durations here as that's the native measurement time for time_point
constexpr float microsecondsPerSecond = 1000.f * 1000.f;
const AZStd::chrono::microseconds timeBudgetPerTick(static_cast<int>(microsecondsPerSecond / g_simulation_tick_rate));
AZStd::chrono::microseconds timeUntilNextTick = m_currentTime + timeBudgetPerTick - now;
if (timeUntilNextTick.count() > 0)
{
AZStd::this_thread::sleep_for(timeUntilNextTick);
}
} }
} }
} }
@@ -1555,5 +1560,4 @@ namespace AZ
AZ::SettingsRegistryScriptUtils::ReflectSettingsRegistryToBehaviorContext(*behaviorContext); AZ::SettingsRegistryScriptUtils::ReflectSettingsRegistryToBehaviorContext(*behaviorContext);
} }
} }
} // namespace AZ } // namespace AZ
@@ -5,13 +5,13 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT * SPDX-License-Identifier: Apache-2.0 OR MIT
* *
*/ */
#pragma once #pragma once
#include <AzCore/Component/ComponentApplicationBus.h> #include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Component.h> #include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h> #include <AzCore/Component/Entity.h>
#include <AzCore/Component/TickBus.h> #include <AzCore/Component/TickBus.h>
#include <AzCore/Time/ITime.h>
#include <AzCore/Memory/AllocationRecords.h> #include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Debug/BudgetTracker.h> #include <AzCore/Debug/BudgetTracker.h>
#include <AzCore/Memory/OSAllocator.h> #include <AzCore/Memory/OSAllocator.h>
@@ -377,14 +377,14 @@ namespace AZ
EntityRemovedEvent m_entityRemovedEvent; EntityRemovedEvent m_entityRemovedEvent;
EntityAddedEvent m_entityActivatedEvent; EntityAddedEvent m_entityActivatedEvent;
EntityRemovedEvent m_entityDeactivatedEvent; EntityRemovedEvent m_entityDeactivatedEvent;
AZ::IConsole* m_console{}; AZ::IConsole* m_console{};
Descriptor m_descriptor; Descriptor m_descriptor;
bool m_isStarted{ false }; bool m_isStarted{ false };
bool m_isSystemAllocatorOwner{ false }; bool m_isSystemAllocatorOwner{ false };
bool m_isOSAllocatorOwner{ false }; bool m_isOSAllocatorOwner{ false };
bool m_ownsConsole{}; bool m_ownsConsole{};
void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy. void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy.
IAllocatorAllocate* m_osAllocator{ nullptr }; IAllocatorAllocate* m_osAllocator{ nullptr };
EntitySetType m_entities; EntitySetType m_entities;
AZ::IO::FixedMaxPath m_exeDirectory; AZ::IO::FixedMaxPath m_exeDirectory;
AZ::IO::FixedMaxPath m_engineRoot; AZ::IO::FixedMaxPath m_engineRoot;
@@ -405,11 +405,11 @@ namespace AZ
// we create a buffer that can be written to (up to AZ_MAX_PATH_LEN) and then // we create a buffer that can be written to (up to AZ_MAX_PATH_LEN) and then
// pack it with a single param. // pack it with a single param.
char m_commandLineBuffer[AZ_MAX_PATH_LEN]; char m_commandLineBuffer[AZ_MAX_PATH_LEN];
char* m_commandLineBufferAddress{ m_commandLineBuffer }; char* m_commandLineBufferAddress{ m_commandLineBuffer };
StartupParameters m_startupParameters; StartupParameters m_startupParameters;
char** m_argV{ nullptr }; char** m_argV{ nullptr };
int m_argC{ 0 }; int m_argC{ 0 };
AZ::CommandLine m_commandLine; // < Stores parsed command line supplied to the constructor AZ::CommandLine m_commandLine; // < Stores parsed command line supplied to the constructor
@@ -45,6 +45,7 @@ namespace Multiplayer
using ClientMigrationStartEvent = AZ::Event<ClientInputId>; using ClientMigrationStartEvent = AZ::Event<ClientInputId>;
using ClientMigrationEndEvent = AZ::Event<>; using ClientMigrationEndEvent = AZ::Event<>;
using ClientDisconnectedEvent = AZ::Event<>; using ClientDisconnectedEvent = AZ::Event<>;
using NotifyClientMigrationEvent = AZ::Event<HostId, uint64_t, ClientInputId>;
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>; using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>; using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
using SessionShutdownEvent = AZ::Event<AzNetworking::INetworkInterface*>; using SessionShutdownEvent = AZ::Event<AzNetworking::INetworkInterface*>;
@@ -80,34 +81,38 @@ namespace Multiplayer
//! @param state The state of this connection //! @param state The state of this connection
virtual void InitializeMultiplayer(MultiplayerAgentType state) = 0; virtual void InitializeMultiplayer(MultiplayerAgentType state) = 0;
//! Starts hosting a server //! Starts hosting a server.
//! @param port The port to listen for connection on //! @param port The port to listen for connection on
//! @param isDedicated Whether the server is dedicated or client hosted //! @param isDedicated Whether the server is dedicated or client hosted
//! @return if the application successfully started hosting //! @return if the application successfully started hosting
virtual bool StartHosting(uint16_t port, bool isDedicated = true) = 0; virtual bool StartHosting(uint16_t port, bool isDedicated = true) = 0;
//! Connects to the specified IP as a Client //! Connects to the specified IP as a Client.
//! @param remoteAddress The domain or IP to connect to //! @param remoteAddress The domain or IP to connect to
//! @param port The port to connect to //! @param port The port to connect to
//! @result if a connection was successfully created //! @result if a connection was successfully created
virtual bool Connect(AZStd::string remoteAddress, uint16_t port) = 0; virtual bool Connect(AZStd::string remoteAddress, uint16_t port) = 0;
// Disconnects all multiplayer connections, stops listening on the server and invokes handlers appropriate to network context // Disconnects all multiplayer connections, stops listening on the server and invokes handlers appropriate to network context.
//! @param reason The reason for terminating connections //! @param reason The reason for terminating connections
virtual void Terminate(AzNetworking::DisconnectReason reason) = 0; virtual void Terminate(AzNetworking::DisconnectReason reason) = 0;
//! Adds a ClientMigrationStartEvent Handler which is invoked at the start of a client migration //! Adds a ClientMigrationStartEvent Handler which is invoked at the start of a client migration.
//! @param handler The ClientMigrationStartEvent Handler to add //! @param handler The ClientMigrationStartEvent Handler to add
virtual void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) = 0; virtual void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) = 0;
//! Adds a ClientMigrationEndEvent Handler which is invoked when a client completes migration //! Adds a ClientMigrationEndEvent Handler which is invoked when a client completes migration.
//! @param handler The ClientMigrationEndEvent Handler to add //! @param handler The ClientMigrationEndEvent Handler to add
virtual void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) = 0; virtual void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) = 0;
//! Adds a ClientDisconnectedEvent Handler which is invoked on the client when a disconnection occurs //! Adds a ClientDisconnectedEvent Handler which is invoked on the client when a disconnection occurs.
//! @param handler The ClientDisconnectedEvent Handler to add //! @param handler The ClientDisconnectedEvent Handler to add
virtual void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) = 0; virtual void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) = 0;
//! Adds a NotifyClientMigrationEvent Handler which is invoked when a client migrates from one host to another.
//! @param handler The NotifyClientMigrationEvent Handler to add
virtual void AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler) = 0;
//! Adds a ConnectionAcquiredEvent Handler which is invoked when a new endpoint connects to the session. //! Adds a ConnectionAcquiredEvent Handler which is invoked when a new endpoint connects to the session.
//! @param handler The ConnectionAcquiredEvent Handler to add //! @param handler The ConnectionAcquiredEvent Handler to add
virtual void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) = 0; virtual void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) = 0;
@@ -120,7 +125,13 @@ namespace Multiplayer
//! @param handler The SessionShutdownEvent handler to add //! @param handler The SessionShutdownEvent handler to add
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0; virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
//! Sends a packet telling if entity update messages can be sent //! Signals a NotifyClientMigrationEvent with the provided parameters.
//! @param hostId the host id of the host the client is migrating to
//! @param userIdentifier the user identifier the client will provide the new host to validate identity
//! @param lastClientInputId the last processed clientInputId by the current host
virtual void SendNotifyClientMigrationEvent(HostId hostId, uint64_t userIdentifier, ClientInputId lastClientInputId);
//! Sends a packet telling if entity update messages can be sent.
//! @param readyForEntityUpdates Ready for entity updates or not //! @param readyForEntityUpdates Ready for entity updates or not
virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0; virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0;
@@ -132,7 +143,7 @@ namespace Multiplayer
//! @return the current server time in milliseconds //! @return the current server time in milliseconds
virtual AZ::TimeMs GetCurrentHostTimeMs() const = 0; virtual AZ::TimeMs GetCurrentHostTimeMs() const = 0;
//! Returns the current blend factor for client side interpolation //! Returns the current blend factor for client side interpolation.
//! This value is only relevant on the client and is used to smooth between host frames //! This value is only relevant on the client and is used to smooth between host frames
//! @return the current blend factor //! @return the current blend factor
virtual float GetCurrentBlendFactor() const = 0; virtual float GetCurrentBlendFactor() const = 0;
@@ -403,6 +403,8 @@ namespace {{ Component.attrib['Namespace'] }}
: public Multiplayer::MultiplayerController : public Multiplayer::MultiplayerController
{ {
public: public:
using ComponentType = {{ ComponentName }};
{{ ControllerBaseName }}({{ ComponentName }}& owner); {{ ControllerBaseName }}({{ ComponentName }}& owner);
~{{ ControllerBaseName }}() override = default; ~{{ ControllerBaseName }}() override = default;
@@ -40,8 +40,8 @@
</Packet> </Packet>
<Packet Name="ClientMigration" Desc="Tell a client to migrate to a new server"> <Packet Name="ClientMigration" Desc="Tell a client to migrate to a new server">
<Member Type="uint64_t" Name="temporaryUserIdentifier" Init="0" /> <Member Type="AzNetworking::IpAddress" Name="remoteServerAddress" Init="AzNetworking::IpAddress()" />
<Member Type="AzNetworking::IpAddress" Name="remoteServerAddress" Init="AzNetworking::IpAddress()" /> <Member Type="uint64_t" Name="temporaryUserIdentifier" Init="0" />
<Member Type="AZ::TimeMs" Name="lastInputGameTimeMs" Init="AZ::TimeMs{ 0 }" /> <Member Type="Multiplayer::ClientInputId" Name="lastClientInputId" Init="Multiplayer::ClientInputId{ 0 }" />
</Packet> </Packet>
</PacketGroup> </PacketGroup>
@@ -7,7 +7,10 @@
*/ */
#include <Source/ConnectionData/ServerToClientConnectionData.h> #include <Source/ConnectionData/ServerToClientConnectionData.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Multiplayer/Components/LocalPredictionPlayerInputComponent.h>
#include <Multiplayer/IMultiplayer.h> #include <Multiplayer/IMultiplayer.h>
#include <AzNetworking/Utilities/EncryptionCommon.h>
namespace Multiplayer namespace Multiplayer
{ {
@@ -95,36 +98,31 @@ namespace Multiplayer
[[maybe_unused]] AzNetworking::ConnectionId connectionId [[maybe_unused]] AzNetworking::ConnectionId connectionId
) )
{ {
//Multiplayer::ServerAddrInfo serverAddr; AzNetworking::IpAddress serverAddress;
//if (gNovaGame->GetMultiplayerworkAgent().GetServerToServerNetwork().GetServerAddrInfoFromConnectionId(newConnectionId, serverAddr) == false) // serverAddress = GetHost(remoteHostId).GetAddress();
//{
// AZLOG_WARN("MigrateClient::Failed to find servershard address, userID:%d", static_cast<uint32_t>(GetUserId())); ClientInputId migratedClientInputId = ClientInputId{ 0 };
// return; if (m_controlledEntity != nullptr)
//} {
// auto controller = m_controlledEntity.FindController<LocalPredictionPlayerInputComponentController>();
//Multiplayer::GameTimePoint migratedClientGameTimePoint; if (controller != nullptr)
// {
//if (m_ControlledEntity != nullptr) migratedClientInputId = controller->GetLastInputId();
//{ }
// if (const PlayerNetworkInputComponent::Authority* pComponent = Multiplayer::FindController<PlayerNetworkInputComponent::Authority>(m_ControlledEntity)) }
// {
// migratedClientGameTimePoint = pComponent->GetLastInputId().GetServerGameTimePoint(); // Generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration
// } const uint64_t randomUserIdentifier = AzNetworking::CryptoRand64();
//}
// // Tell the new host that a client is about to (re)join
// generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, randomUserIdentifier, migratedClientInputId);
//const uint64_t randomUserIdentifier = 0;
// // Tell the client who to join
//// Tell the server a new client is about to join MultiplayerPackets::ClientMigration clientMigration(serverAddress, randomUserIdentifier, migratedClientInputId);
//MultiplayerPackets::NotifyClientMigration notifyClientMigration(randomUserIdentifier); GetConnection()->SendReliablePacket(clientMigration);
//gNovaGame->GetMultiplayerworkAgent().GetServerToServerNetwork().SendReliablePacket(newConnectionId, notifyClientMigration);
// m_controlledEntity = NetworkEntityHandle();
//// Tell the client who to join m_canSendUpdates = false;
//MultiplayerPackets::ClientMigration clientMigration(randomUserIdentifier, serverAddr, migratedClientGameTimePoint);
//GetConnection()->SendReliablePacket(clientMigration);
//
//m_controlledEntity = nullptr;
//m_canSendUpdates = false;
} }
void ServerToClientConnectionData::OnGameplayStarted() void ServerToClientConnectionData::OnGameplayStarted()
@@ -617,7 +617,7 @@ namespace Multiplayer
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); }; auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); };
m_networkInterface->GetConnectionSet().VisitConnections(visitor); m_networkInterface->GetConnectionSet().VisitConnections(visitor);
AZLOG_INFO("Migrating to new server shard"); AZLOG_INFO("Migrating to new server shard");
//m_clientMigrateStartEvent(packet.GetLastInputGameTimeMs()); m_clientMigrationStartEvent.Signal(ClientInputId{ 0 });
m_networkInterface->Connect(packet.GetRemoteServerAddress()); m_networkInterface->Connect(packet.GetRemoteServerAddress());
return true; return true;
} }
@@ -795,6 +795,11 @@ namespace Multiplayer
handler.Connect(m_clientDisconnectedEvent); handler.Connect(m_clientDisconnectedEvent);
} }
void MultiplayerSystemComponent::AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler)
{
handler.Connect(m_notifyClientMigrationEvent);
}
void MultiplayerSystemComponent::AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) void MultiplayerSystemComponent::AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler)
{ {
handler.Connect(m_connectionAcquiredEvent); handler.Connect(m_connectionAcquiredEvent);
@@ -810,6 +815,11 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent); handler.Connect(m_shutdownEvent);
} }
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(HostId hostId, uint64_t userIdentifier, ClientInputId lastClientInputId)
{
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId);
}
void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates) void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates)
{ {
IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet(); IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet();
@@ -111,9 +111,11 @@ namespace Multiplayer
void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) override; void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) override;
void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) override; void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) override;
void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) override; void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) override;
void AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler) override;
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override; void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override; void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override; void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
void SendNotifyClientMigrationEvent(HostId hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override;
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override; void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
AZ::TimeMs GetCurrentHostTimeMs() const override; AZ::TimeMs GetCurrentHostTimeMs() const override;
float GetCurrentBlendFactor() const override; float GetCurrentBlendFactor() const override;
@@ -154,6 +156,7 @@ namespace Multiplayer
ClientDisconnectedEvent m_clientDisconnectedEvent; ClientDisconnectedEvent m_clientDisconnectedEvent;
ClientMigrationStartEvent m_clientMigrationStartEvent; ClientMigrationStartEvent m_clientMigrationStartEvent;
ClientMigrationEndEvent m_clientMigrationEndEvent; ClientMigrationEndEvent m_clientMigrationEndEvent;
NotifyClientMigrationEvent m_notifyClientMigrationEvent;
AZStd::queue<AZStd::string> m_pendingConnectionTickets; AZStd::queue<AZStd::string> m_pendingConnectionTickets;