Merge pull request #255 from aws-lumberyard-dev/MultiplayerPipeline

Added delayed activation for client networked entities. Added ability to specify additional .cfg file to use the same launcher for server & client
This commit is contained in:
SergeyAMZN
2021-04-28 16:19:34 +01:00
committed by GitHub
16 changed files with 116 additions and 22 deletions
+22 -2
View File
@@ -26,7 +26,6 @@
#include <AzGameFramework/Application/GameApplication.h>
#include <CryLibrary.h>
#include <IConsole.h>
#include <ISystem.h>
#include <ITimer.h>
#include <LegacyAllocator.h>
@@ -46,6 +45,23 @@ extern "C" void CreateStaticModules(AZStd::vector<AZ::Module*>& modulesOut);
namespace
{
void ExecuteConsoleCommandFile(AzFramework::Application& application)
{
const AZStd::string_view customConCmdKey = "console-command-file";
const AZ::CommandLine* commandLine = application.GetCommandLine();
AZStd::size_t numSwitchValues = commandLine->GetNumSwitchValues(customConCmdKey);
if (numSwitchValues > 0)
{
// The expectations for command line parameters is that the "last one wins"
// That way it allows users and test scripts to override previous command line options by just listing them later on the invocation line
const AZStd::string& consoleCmd = commandLine->GetSwitchValue(customConCmdKey, numSwitchValues - 1);
if (!consoleCmd.empty())
{
AZ::Interface<AZ::IConsole>::Get()->ExecuteConfigFile(consoleCmd.c_str());
}
}
}
#if AZ_TRAIT_LAUNCHER_USE_CRY_DYNAMIC_MODULE_HANDLE
// mimics AZ::DynamicModuleHandle but uses CryLibrary under the hood,
// which is necessary to properly load legacy Cry libraries on some platforms
@@ -637,7 +653,11 @@ namespace O3DELauncher
if (gEnv && gEnv->pConsole)
{
// Execute autoexec.cfg to load the initial level
gEnv->pConsole->ExecuteString("exec autoexec.cfg");
AZ::Interface<AZ::IConsole>::Get()->ExecuteConfigFile("autoexec.cfg");
// Find out if console command file was passed
// via --console-command-file=%filename% and execute it
ExecuteConsoleCommandFile(gameApplication);
gEnv->pSystem->ExecuteCommandLine(false);
@@ -74,6 +74,10 @@ namespace Multiplayer
//! @param handler The SessionShutdownEvent handler to add
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
//! Sends a packet telling if entity update messages can be sent
//! @param readyForEntityUpdates Ready for entity updates or not
virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0;
//! Returns the gem name associated with the provided component index.
//! @param netComponentId the componentId to return the gem name of
//! @return the name of the gem that contains the requested component
@@ -73,6 +73,12 @@ namespace Multiplayer
True
};
enum class AutoActivate : uint8_t
{
DoNotActivate,
Activate
};
// This is just a placeholder
// The level/prefab cooking will devise the actual solution for identifying a dynamically spawnable entity within a prefab
struct PrefabEntityId
@@ -14,6 +14,10 @@
<Member Type="Multiplayer::HostId" Name="hostId" Init="Multiplayer::InvalidHostId" />
<Member Type="Multiplayer::LongNetworkString" Name="map" />
</Packet>
<Packet Name="ReadyForEntityUpdates" Desc="Client confirming it is ready to receive entity updates">
<Member Type="bool" Name="readyForEntityUpdates" />
</Packet>
<Packet Name="SyncConsole" Desc="Packet for synchornizing cvars between hosts">
<Member Type="Multiplayer::LongNetworkString" Name="commandSet" Container="Vector" Count="32" />
@@ -33,10 +33,10 @@ namespace Multiplayer
AzNetworking::IConnection* GetConnection() const override;
EntityReplicationManager& GetReplicationManager() override;
void Update(AZ::TimeMs serverGameTimeMs) override;
bool CanSendUpdates() const override;
void SetCanSendUpdates(bool canSendUpdates) override;
//! @}
bool CanSendUpdates();
private:
EntityReplicationManager m_entityReplicationManager;
AzNetworking::IConnection* m_connection = nullptr;
@@ -12,8 +12,13 @@
namespace Multiplayer
{
inline bool ClientToServerConnectionData::CanSendUpdates()
inline bool ClientToServerConnectionData::CanSendUpdates() const
{
return m_canSendUpdates;
}
inline void ClientToServerConnectionData::SetCanSendUpdates(bool canSendUpdates)
{
m_canSendUpdates = canSendUpdates;
}
}
@@ -44,5 +44,13 @@ namespace Multiplayer
//! 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;
};
}
@@ -34,10 +34,10 @@ namespace Multiplayer
AzNetworking::IConnection* GetConnection() const override;
EntityReplicationManager& GetReplicationManager() override;
void Update(AZ::TimeMs serverGameTimeMs) override;
bool CanSendUpdates() const override;
void SetCanSendUpdates(bool canSendUpdates) override;
//! @}
bool CanSendUpdates();
NetworkEntityHandle GetPrimaryPlayerEntity();
const NetworkEntityHandle& GetPrimaryPlayerEntity() const;
@@ -51,7 +51,7 @@ namespace Multiplayer
EntityStopEvent::Handler m_controlledEntityRemovedHandler;
EntityMigrationEvent::Handler m_controlledEntityMigrationHandler;
AzNetworking::IConnection* m_connection = nullptr;
bool m_canSendUpdates = true;
bool m_canSendUpdates = false;
};
}
@@ -12,11 +12,17 @@
namespace Multiplayer
{
inline bool ServerToClientConnectionData::CanSendUpdates()
inline bool ServerToClientConnectionData::CanSendUpdates() const
{
return m_canSendUpdates;
}
inline void ServerToClientConnectionData::SetCanSendUpdates(bool canSendUpdates)
{
m_canSendUpdates = canSendUpdates;
}
inline NetworkEntityHandle ServerToClientConnectionData::GetPrimaryPlayerEntity()
{
return m_controlledEntity;
@@ -384,6 +384,19 @@ namespace Multiplayer
return false;
}
bool MultiplayerSystemComponent::HandleRequest( AzNetworking::IConnection* connection,
[[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet)
{
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
if (connectionData)
{
connectionData->SetCanSendUpdates(packet.GetReadyForEntityUpdates());
return true;
}
return false;
}
ConnectResult MultiplayerSystemComponent::ValidateConnect
(
[[maybe_unused]] const IpAddress& remoteAddress,
@@ -506,6 +519,15 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent);
}
void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates)
{
IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet();
connectionSet.VisitConnections([readyForEntityUpdates](IConnection& connection)
{
connection.SendReliablePacket(MultiplayerPackets::ReadyForEntityUpdates(readyForEntityUpdates));
});
}
const char* MultiplayerSystemComponent::GetComponentGemName(NetComponentId netComponentId) const
{
return GetMultiplayerComponentRegistry()->GetComponentGemName(netComponentId);
@@ -71,6 +71,7 @@ namespace Multiplayer
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ClientMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::NotifyClientMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet);
//! IConnectionListener interface
//! @{
@@ -88,6 +89,7 @@ namespace Multiplayer
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
const char* GetComponentGemName(NetComponentId netComponentId) const override;
const char* GetComponentName(NetComponentId netComponentId) const override;
const char* GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const override;
@@ -73,6 +73,12 @@ namespace Multiplayer
True
};
enum class AutoActivate : uint8_t
{
DoNotActivate,
Activate
};
// This is just a placeholder
// The level/prefab cooking will devise the actual solution for identifying a dynamically spawnable entity within a prefab
struct PrefabEntityId
@@ -543,11 +543,8 @@ namespace Multiplayer
// Create an entity if we don't have one
if (createEntity)
{
// @pereslav
//replicatorEntity = GetNetworkEntityManager()->CreateSingleEntityImmediateInternal(prefabEntityId, EntitySpawnType::Replicate, AutoActivate::DoNotActivate, netEntityId, localNetworkRole, AZ::Transform::Identity());
INetworkEntityManager::EntityList entityList = GetNetworkEntityManager()->CreateEntitiesImmediate(
prefabEntityId, netEntityId, localNetworkRole,
AZ::Transform::Identity());
prefabEntityId, netEntityId, localNetworkRole, AutoActivate::DoNotActivate, AZ::Transform::Identity());
if (entityList.size() == 1)
{
@@ -60,7 +60,8 @@ namespace Multiplayer
//! 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, const AZ::Transform& transform) = 0;
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
@@ -11,19 +11,19 @@
*/
#include <Source/NetworkEntity/NetworkEntityManager.h>
#include <Source/Components/NetBindComponent.h>
#include <Include/IMultiplayer.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Slice/SliceMetadataInfoComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Include/IMultiplayer.h>
#include <Pipeline/NetworkSpawnableHolderComponent.h>
#include <AzCore/Asset/AssetManager.h>
#include <Source/Components/NetBindComponent.h>
namespace Multiplayer
{
@@ -361,7 +361,7 @@ namespace Multiplayer
INetworkEntityManager::EntityList NetworkEntityManager::CreateEntitiesImmediate(
const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole,
const AZ::Transform& transform)
AutoActivate autoActivate, const AZ::Transform& transform)
{
INetworkEntityManager::EntityList returnList;
@@ -407,6 +407,11 @@ namespace Multiplayer
transformComponent->SetWorldTM(transform);
}
if (autoActivate == AutoActivate::DoNotActivate)
{
clone->SetRuntimeActiveByDefault(false);
}
AzFramework::GameEntityContextRequestBus::Broadcast(
&AzFramework::GameEntityContextRequestBus::Events::AddGameEntity, clone);
@@ -462,7 +467,9 @@ namespace Multiplayer
m_rootSpawnableAsset = netSpawnableAsset;
const auto agentType = AZ::Interface<IMultiplayer>::Get()->GetAgentType();
auto* multiplayer = AZ::Interface<IMultiplayer>::Get();
const auto agentType = multiplayer->GetAgentType();
const bool spawnImmediately =
(agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer);
@@ -470,6 +477,12 @@ namespace Multiplayer
{
CreateEntitiesImmediate(*netSpawnable, NetEntityRole::Authority);
}
else
{
// If we don't spawn net entities immediately (i.e. it is a client),
// tell the server/host it can start sending updates that will instantiate entities.
multiplayer->SendReadyForEntityUpdates(true);
}
}
void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)
@@ -50,7 +50,7 @@ namespace Multiplayer
EntityList CreateEntitiesImmediate(
const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole,
const AZ::Transform& transform) override;
AutoActivate autoActivate, const AZ::Transform& transform) override;
uint32_t GetEntityCount() const override;
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override;