SimplePlayerSpawnerComponent.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "AzCore/Component/TransformBus.h"
  9. #include <Multiplayer/IMultiplayer.h>
  10. #include <Multiplayer/Components/SimplePlayerSpawnerComponent.h>
  11. #include <AzCore/Console/ILogger.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <AzCore/Serialization/EditContext.h>
  14. namespace Multiplayer
  15. {
  16. void SimplePlayerSpawnerComponent::Activate()
  17. {
  18. AZ::Interface<IMultiplayerSpawner>::Register(this);
  19. AZ::Interface<ISimplePlayerSpawner>::Register(this);
  20. }
  21. void SimplePlayerSpawnerComponent::Deactivate()
  22. {
  23. AZ::Interface<ISimplePlayerSpawner>::Unregister(this);
  24. AZ::Interface<IMultiplayerSpawner>::Unregister(this);
  25. }
  26. void SimplePlayerSpawnerComponent::Reflect(AZ::ReflectContext* context)
  27. {
  28. if (const auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  29. {
  30. serializeContext->Class<SimplePlayerSpawnerComponent, AZ::Component>()
  31. ->Version(1)
  32. ->Field("PlayerSpawnable", &SimplePlayerSpawnerComponent::m_playerSpawnable)
  33. ->Field("SpawnPoints", &SimplePlayerSpawnerComponent::m_spawnPoints)
  34. ;
  35. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  36. {
  37. editContext->Class<SimplePlayerSpawnerComponent>(
  38. "Simple Network Player Spawner",
  39. "A simple player spawner that comes included with the Multiplayer gem. Attach this component to any level's root entity which needs to spawn a network player."
  40. "If no spawn points are provided the network players will be spawned at the world-space origin.")
  41. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  42. ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
  43. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/SimpleNetworkPlayerSpawner.svg")
  44. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/SimpleNetworkPlayerSpawner.svg")
  45. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Level"))
  46. ->DataElement(
  47. AZ::Edit::UIHandlers::Default,
  48. &SimplePlayerSpawnerComponent::m_playerSpawnable,
  49. "Player Spawnable Asset",
  50. "The network player spawnable asset which will be spawned for each player that joins.")
  51. ->DataElement(
  52. AZ::Edit::UIHandlers::Default,
  53. &SimplePlayerSpawnerComponent::m_spawnPoints,
  54. "Spawn Points",
  55. "Networked players will spawn at the spawn point locations in order. If there are more players than spawn points, the new players will round-robin back starting with the first spawn point.")
  56. ;
  57. }
  58. }
  59. }
  60. void SimplePlayerSpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  61. {
  62. provided.push_back(AZ_CRC_CE("MultiplayerSpawnerService"));
  63. }
  64. void SimplePlayerSpawnerComponent::GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  65. {
  66. incompatible.push_back(AZ_CRC_CE("MultiplayerSpawnerService"));
  67. }
  68. AZ::Transform SimplePlayerSpawnerComponent::GetNextSpawnPoint() const
  69. {
  70. if (m_spawnPoints.empty())
  71. {
  72. return AZ::Transform::Identity();
  73. }
  74. if (m_spawnIndex >= m_spawnPoints.size())
  75. {
  76. AZ_Assert(false, "SimplePlayerSpawnerComponent has an out-of-bounds m_spawnIndex %i. Please ensure spawn index is always valid.", m_spawnIndex);
  77. return AZ::Transform::Identity();
  78. }
  79. const AZ::EntityId spawnPointEntityId = m_spawnPoints[m_spawnIndex];
  80. if (!spawnPointEntityId.IsValid())
  81. {
  82. AZ_Assert(
  83. false,
  84. "Empty spawner entry at m_spawnIndex %i. Please ensure spawn index is always valid.",
  85. m_spawnIndex);
  86. return AZ::Transform::Identity();
  87. }
  88. AZ::Transform spawnPointTransform = AZ::Transform::Identity();
  89. AZ::TransformBus::EventResult(spawnPointTransform, spawnPointEntityId, &AZ::TransformInterface::GetWorldTM);
  90. return spawnPointTransform;
  91. }
  92. const AZStd::vector<AZ::EntityId>& SimplePlayerSpawnerComponent::GetSpawnPoints() const
  93. {
  94. return m_spawnPoints;
  95. }
  96. uint32_t SimplePlayerSpawnerComponent::GetSpawnPointCount() const
  97. {
  98. return aznumeric_cast<uint32_t>(m_spawnPoints.size());
  99. }
  100. uint32_t SimplePlayerSpawnerComponent::GetNextSpawnPointIndex() const
  101. {
  102. if (m_spawnPoints.empty())
  103. {
  104. return 0;
  105. }
  106. if (m_spawnIndex >= m_spawnPoints.size())
  107. {
  108. AZ_Assert(false, "SimplePlayerSpawnerComponent has an out-of-bounds m_spawnIndex %i. Please ensure spawn index is always valid.", m_spawnIndex);
  109. return static_cast<uint32_t>(-1);
  110. }
  111. return m_spawnIndex;
  112. }
  113. void SimplePlayerSpawnerComponent::SetNextSpawnPointIndex(uint32_t index)
  114. {
  115. if (index >= m_spawnPoints.size())
  116. {
  117. AZLOG_WARN("SetNextSpawnPointIndex called with out-of-bounds spawn index %i; total spawn points: %i", index, aznumeric_cast<uint32_t>(m_spawnPoints.size()));
  118. return;
  119. }
  120. m_spawnIndex = index;
  121. }
  122. NetworkEntityHandle SimplePlayerSpawnerComponent::OnPlayerJoin(
  123. [[maybe_unused]] uint64_t userId, [[maybe_unused]] const MultiplayerAgentDatum& agentDatum)
  124. {
  125. const PrefabEntityId prefabEntityId(AZ::Name(m_playerSpawnable.m_spawnableAsset.GetHint().c_str()));
  126. const AZ::Transform transform = GetNextSpawnPoint();
  127. m_spawnIndex = ++m_spawnIndex % m_spawnPoints.size();
  128. INetworkEntityManager::EntityList entityList =
  129. GetNetworkEntityManager()->CreateEntitiesImmediate(prefabEntityId, NetEntityRole::Authority, transform);
  130. NetworkEntityHandle controlledEntity;
  131. if (entityList.empty())
  132. {
  133. // Failure: The player prefab has no networked entities in it.
  134. AZLOG_ERROR(
  135. "Attempt to spawn prefab '%s' failed, no entities were spawned. Ensure that the prefab contains a single entity "
  136. "that is network enabled with a Network Binding component.",
  137. prefabEntityId.m_prefabName.GetCStr());
  138. }
  139. controlledEntity = entityList[0];
  140. return controlledEntity;
  141. }
  142. void SimplePlayerSpawnerComponent::OnPlayerLeave(ConstNetworkEntityHandle entityHandle, [[maybe_unused]] const ReplicationSet& replicationSet, [[maybe_unused]] AzNetworking::DisconnectReason reason)
  143. {
  144. // Walk hierarchy backwards to remove all children before parents
  145. AZStd::vector<AZ::EntityId> hierarchy = entityHandle.GetEntity()->GetTransform()->GetEntityAndAllDescendants();
  146. for (auto it = hierarchy.rbegin(); it != hierarchy.rend(); ++it)
  147. {
  148. const AZ::EntityId hierarchyEntityId = *it;
  149. ConstNetworkEntityHandle hierarchyEntityHandle = GetNetworkEntityManager()->GetEntity(GetNetworkEntityManager()->GetNetEntityIdById(hierarchyEntityId));
  150. if (hierarchyEntityHandle)
  151. {
  152. AZ::Interface<IMultiplayer>::Get()->GetNetworkEntityManager()->MarkForRemoval(hierarchyEntityHandle);
  153. }
  154. }
  155. }
  156. } // namespace Multiplayer