PrefabProcessingTests.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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 <AzFramework/Components/TransformComponent.h>
  9. #include <AzFramework/Spawnable/Spawnable.h>
  10. #include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
  11. #include <AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.h>
  12. #include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
  13. #include <Prefab/PrefabDomTypes.h>
  14. #include <Prefab/Spawnable/PrefabProcessorContext.h>
  15. #include <Multiplayer/Components/NetBindComponent.h>
  16. #include <Multiplayer/MultiplayerConstants.h>
  17. #include <Source/NetworkEntity/NetworkEntityManager.h>
  18. #include <Source/Pipeline/NetworkPrefabProcessor.h>
  19. namespace UnitTest
  20. {
  21. class TestPrefabProcessorContext
  22. : public AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext
  23. {
  24. public:
  25. AZ_CLASS_ALLOCATOR(TestPrefabProcessorContext, AZ::SystemAllocator);
  26. AZ_RTTI(TestPrefabProcessorContext, "{2FFFAA06-BA78-4CB3-AE0E-6532822A9B69}",
  27. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext);
  28. explicit TestPrefabProcessorContext(const AZ::Uuid& sourceUuid)
  29. : PrefabProcessorContext(sourceUuid)
  30. {
  31. }
  32. const AZStd::vector<AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasStore>& GetEntityAliases() const
  33. {
  34. return m_entityAliases;
  35. }
  36. };
  37. class PrefabProcessingTestFixture : public ::testing::Test
  38. {
  39. public:
  40. void SetUp() override
  41. {
  42. using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext;
  43. // Create test entities: 1 networked and 1 static
  44. AZStd::vector<AZ::Entity*> entities;
  45. entities.emplace_back(CreateSourceEntity(m_staticEntityName.c_str(), false, AZ::Transform::CreateIdentity()));
  46. entities.emplace_back(CreateSourceEntity(m_netEntityName.c_str(), true, AZ::Transform::CreateIdentity()));
  47. // Convert the entities into prefab. Note: This will transfer the ownership of AZ::Entity* into Prefab
  48. ConvertEntitiesToPrefab(entities, m_networkPrefabDom, "test/path");
  49. // Create a non-networked prefab
  50. AZStd::vector<AZ::Entity*> nonNetworkEntities;
  51. nonNetworkEntities.emplace_back(CreateSourceEntity("NonNetEntity_1", false, AZ::Transform::CreateIdentity()));
  52. nonNetworkEntities.emplace_back(CreateSourceEntity("NonNetEntity_2", false, AZ::Transform::CreateIdentity()));
  53. ConvertEntitiesToPrefab(nonNetworkEntities, m_nonNetworkPrefabDom, "test/path_non_networked");
  54. }
  55. void TearDown() override
  56. {
  57. AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get()->RemoveAllTemplates();
  58. }
  59. static void ConvertEntitiesToPrefab(const AZStd::vector<AZ::Entity*>& entities, AzToolsFramework::Prefab::PrefabDom& prefabDom, AZ::IO::PathView filePath)
  60. {
  61. auto* prefabSystem = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
  62. AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(prefabSystem->CreatePrefab(entities, {}, filePath));
  63. ASSERT_TRUE(sourceInstance);
  64. auto& prefabTemplateDom = prefabSystem->FindTemplateDom(sourceInstance->GetTemplateId());
  65. prefabDom.CopyFrom(prefabTemplateDom, prefabDom.GetAllocator());
  66. }
  67. static AZ::Entity* CreateSourceEntity(const char* name, bool networked, const AZ::Transform& tm, AZ::Entity* parent = nullptr)
  68. {
  69. AZ::Entity* entity = aznew AZ::Entity(name);
  70. auto* transformComponent = entity->CreateComponent<AzFramework::TransformComponent>();
  71. if (parent)
  72. {
  73. transformComponent->SetParent(parent->GetId());
  74. transformComponent->SetLocalTM(tm);
  75. }
  76. else
  77. {
  78. transformComponent->SetWorldTM(tm);
  79. }
  80. if(networked)
  81. {
  82. entity->CreateComponent<Multiplayer::NetBindComponent>();
  83. }
  84. return entity;
  85. }
  86. static bool IsChildAfterParent(const AZStd::string& childName, const AZStd::string& parentName, const AzFramework::Spawnable::EntityList& entityList)
  87. {
  88. int parentIndex = -1;
  89. int childIndex = -1;
  90. for (int i = 0; i < entityList.size(); ++i)
  91. {
  92. if ((entityList[i]->GetName() == parentName) && (parentIndex == -1))
  93. {
  94. parentIndex = i;
  95. }
  96. if ((entityList[i]->GetName() == childName) && (childIndex == -1))
  97. {
  98. childIndex = i;
  99. }
  100. }
  101. EXPECT_NE(childIndex, -1);
  102. EXPECT_NE(parentIndex, -1);
  103. return childIndex > parentIndex;
  104. }
  105. static bool ContainsEntity(const AzFramework::Spawnable::EntityList& entityList, const AZStd::string& name)
  106. {
  107. for (const auto& entity : entityList)
  108. {
  109. if (entity->GetName() == name)
  110. {
  111. return true;
  112. }
  113. }
  114. return false;
  115. }
  116. const AZStd::string m_staticEntityName = "static_floor";
  117. const AZStd::string m_netEntityName = "networked_entity";
  118. AzToolsFramework::Prefab::PrefabDom m_networkPrefabDom;
  119. AzToolsFramework::Prefab::PrefabDom m_nonNetworkPrefabDom;
  120. };
  121. TEST_F(PrefabProcessingTestFixture, NetworkPrefabProcessor_ProcessPrefabTwoEntities_NetEntityGoesToNetSpawnable)
  122. {
  123. // Add the prefab into the Prefab Processor Context
  124. const AZStd::string prefabName = "testPrefab";
  125. TestPrefabProcessorContext prefabProcessorContext{AZ::Uuid::CreateRandom()};
  126. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument document(prefabName);
  127. ASSERT_TRUE(document.SetPrefabDom(AZStd::move(m_networkPrefabDom)));
  128. prefabProcessorContext.AddPrefab(AZStd::move(document));
  129. // Request NetworkPrefabProcessor and PrefabCatchmentProcessor to process the prefab
  130. Multiplayer::NetworkPrefabProcessor processor;
  131. processor.Process(prefabProcessorContext);
  132. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor prefabCatchmentProcessor;
  133. prefabCatchmentProcessor.Process(prefabProcessorContext);
  134. // Validate results
  135. EXPECT_TRUE(prefabProcessorContext.HasCompletedSuccessfully());
  136. // Should be 1 spawnable and 1 networked spawnable
  137. const auto& processedObjects = prefabProcessorContext.GetProcessedObjects();
  138. EXPECT_EQ(processedObjects.size(), 2);
  139. // Verify the name and the type of the spawnable asset
  140. const AZ::Data::AssetData& spawnableAsset = processedObjects[1].GetAsset();
  141. EXPECT_EQ(prefabName + Multiplayer::NetworkSpawnableFileExtension.data(), processedObjects[1].GetId());
  142. EXPECT_EQ(spawnableAsset.GetType(), azrtti_typeid<AzFramework::Spawnable>());
  143. // Verify we have only the networked entity in the network spawnable and not the static one
  144. const AzFramework::Spawnable* netSpawnable = azrtti_cast<const AzFramework::Spawnable*>(&spawnableAsset);
  145. const AzFramework::Spawnable::EntityList& networkSpawnableEntityList = netSpawnable->GetEntities();
  146. EXPECT_FALSE(ContainsEntity(networkSpawnableEntityList, m_staticEntityName));
  147. EXPECT_TRUE(ContainsEntity(networkSpawnableEntityList, m_netEntityName));
  148. }
  149. TEST_F(PrefabProcessingTestFixture, NetworkPrefabProcessor_ProcessPrefabTwoEntities_NonNetEntityDoesNotProduceNetSpawnable)
  150. {
  151. // Add the prefab into the Prefab Processor Context
  152. const AZStd::string prefabName = "testPrefab";
  153. TestPrefabProcessorContext prefabProcessorContext{ AZ::Uuid::CreateRandom() };
  154. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument document(prefabName);
  155. ASSERT_TRUE(document.SetPrefabDom(AZStd::move(m_nonNetworkPrefabDom)));
  156. prefabProcessorContext.AddPrefab(AZStd::move(document));
  157. // Request NetworkPrefabProcessor and PrefabCatchmentProcessor to process the prefab
  158. Multiplayer::NetworkPrefabProcessor processor;
  159. processor.Process(prefabProcessorContext);
  160. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor prefabCatchmentProcessor;
  161. prefabCatchmentProcessor.Process(prefabProcessorContext);
  162. // Validate results
  163. EXPECT_TRUE(prefabProcessorContext.HasCompletedSuccessfully());
  164. // Should be 1 spawnable and no networked spawnable
  165. const auto& processedObjects = prefabProcessorContext.GetProcessedObjects();
  166. EXPECT_EQ(processedObjects.size(), 1);
  167. // Verify the name and the type of the spawnable asset
  168. const AZ::Data::AssetData& spawnableAsset = processedObjects[0].GetAsset();
  169. EXPECT_EQ(prefabName + AzFramework::Spawnable::DotFileExtension, processedObjects[0].GetId());
  170. EXPECT_EQ(spawnableAsset.GetType(), azrtti_typeid<AzFramework::Spawnable>());
  171. }
  172. TEST_F(PrefabProcessingTestFixture, NetworkPrefabProcessor_ProcessPrefabTwoEntities_AliasesInsertedIntoContext)
  173. {
  174. using AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasStore;
  175. using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument;
  176. // Add the prefab into the Prefab Processor Context
  177. TestPrefabProcessorContext prefabProcessorContext{ AZ::Uuid::CreateRandom() };
  178. PrefabDocument prefabDocument("testPrefab");
  179. prefabDocument.SetPrefabDom(AZStd::move(m_networkPrefabDom));
  180. prefabProcessorContext.AddPrefab(AZStd::move(prefabDocument));
  181. // Request NetworkPrefabProcessor to process the prefab
  182. Multiplayer::NetworkPrefabProcessor processor;
  183. processor.Process(prefabProcessorContext);
  184. const AZStd::vector<EntityAliasStore>& aliases = prefabProcessorContext.GetEntityAliases();
  185. // Only 1 networked entity, so should be 1 alias inserted.
  186. EXPECT_EQ(aliases.size(), 1);
  187. // Verify alias metadata
  188. const EntityAliasStore& alias = aliases[0];
  189. EXPECT_EQ(alias.m_aliasType, AzFramework::Spawnable::EntityAliasType::Replace);
  190. EXPECT_EQ(alias.m_tag, Multiplayer::NetworkEntityManager::NetworkEntityTag);
  191. }
  192. TEST_F(PrefabProcessingTestFixture, NetworkPrefabProcessor_ProcessPrefabEntityHierarchy_EntitiesSorted)
  193. {
  194. using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext;
  195. const AZStd::string parentName = "static_parent";
  196. const AZStd::string childName = "networked_child";
  197. const AZStd::string childOfChildName = "networked_childOfChild";
  198. // Create test entities with the following hierarchy:
  199. // static parent
  200. // + networked child
  201. // + networked child
  202. AZStd::vector<AZ::Entity*> entities;
  203. AZ::Entity* parent = entities.emplace_back(CreateSourceEntity(parentName.c_str(), false, AZ::Transform::CreateIdentity()));
  204. AZ::Entity* child = entities.emplace_back(CreateSourceEntity(childName.c_str(), true, AZ::Transform::CreateIdentity(), parent));
  205. entities.emplace_back(CreateSourceEntity(childOfChildName.c_str(), true, AZ::Transform::CreateIdentity(), child));
  206. // Convert the entities into prefab. Note: This will transfer the ownership of AZ::Entity* into Prefab
  207. AzToolsFramework::Prefab::PrefabDom prefabDom;
  208. ConvertEntitiesToPrefab(entities, prefabDom, "test_entities_sorted/path");
  209. // Add the prefab into the Prefab Processor Context
  210. const AZStd::string prefabName = "testPrefab";
  211. TestPrefabProcessorContext prefabProcessorContext{ AZ::Uuid::CreateRandom() };
  212. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument prefabDocument(prefabName);
  213. ASSERT_TRUE(prefabDocument.SetPrefabDom(AZStd::move(prefabDom)));
  214. prefabProcessorContext.AddPrefab(AZStd::move(prefabDocument));
  215. // Request NetworkPrefabProcessor and PrefabCatchmentProcessor to process the prefab
  216. Multiplayer::NetworkPrefabProcessor processor;
  217. processor.Process(prefabProcessorContext);
  218. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor prefabCatchmentProcessor;
  219. prefabCatchmentProcessor.Process(prefabProcessorContext);
  220. EXPECT_TRUE(prefabProcessorContext.HasCompletedSuccessfully());
  221. // Verify entities are ordered by parent/child hierarchy
  222. const auto& processedObjects = prefabProcessorContext.GetProcessedObjects();
  223. // Static spawnable
  224. {
  225. const AZ::Data::AssetData& spawnableAsset = processedObjects[0].GetAsset();
  226. const AzFramework::Spawnable* spawnable = azrtti_cast<const AzFramework::Spawnable*>(&spawnableAsset);
  227. const AzFramework::Spawnable::EntityList& entityList = spawnable->GetEntities();
  228. EXPECT_TRUE(IsChildAfterParent(childName, parentName, entityList));
  229. EXPECT_TRUE(IsChildAfterParent(childOfChildName, childName, entityList));
  230. }
  231. // Network spawnable
  232. {
  233. const AZ::Data::AssetData& spawnableAsset = processedObjects[1].GetAsset();
  234. const AzFramework::Spawnable* spawnable = azrtti_cast<const AzFramework::Spawnable*>(&spawnableAsset);
  235. const AzFramework::Spawnable::EntityList& entityList = spawnable->GetEntities();
  236. EXPECT_TRUE(IsChildAfterParent(childOfChildName, childName, entityList));
  237. EXPECT_FALSE(ContainsEntity(entityList, parentName));
  238. }
  239. }
  240. TEST_F(PrefabProcessingTestFixture, NetworkPrefabProcessor_ProcessPrefabEntityHierarchy_GrandchildNetworkEntityProducesNetworkSpawnable)
  241. {
  242. using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext;
  243. const AZStd::string parentName = "static_parent";
  244. const AZStd::string childName = "static_child";
  245. const AZStd::string childOfChildName = "networked_childOfChild";
  246. // Create test entities with the following hierarchy:
  247. // static parent
  248. // + static child
  249. // + networked child
  250. AZStd::vector<AZ::Entity*> entities;
  251. AZ::Entity* parent = entities.emplace_back(CreateSourceEntity(parentName.c_str(), false, AZ::Transform::CreateIdentity()));
  252. AZ::Entity* child = entities.emplace_back(CreateSourceEntity(childName.c_str(), false, AZ::Transform::CreateIdentity(), parent));
  253. entities.emplace_back(CreateSourceEntity(childOfChildName.c_str(), true, AZ::Transform::CreateIdentity(), child));
  254. // Convert the entities into prefab. Note: This will transfer the ownership of AZ::Entity* into Prefab
  255. AzToolsFramework::Prefab::PrefabDom prefabDom;
  256. ConvertEntitiesToPrefab(entities, prefabDom, "test_entities_sorted/path");
  257. // Add the prefab into the Prefab Processor Context
  258. const AZStd::string prefabName = "testPrefab";
  259. TestPrefabProcessorContext prefabProcessorContext{ AZ::Uuid::CreateRandom() };
  260. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument prefabDocument(prefabName);
  261. ASSERT_TRUE(prefabDocument.SetPrefabDom(AZStd::move(prefabDom)));
  262. prefabProcessorContext.AddPrefab(AZStd::move(prefabDocument));
  263. // Request NetworkPrefabProcessor and PrefabCatchmentProcessor to process the prefab
  264. Multiplayer::NetworkPrefabProcessor processor;
  265. processor.Process(prefabProcessorContext);
  266. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor prefabCatchmentProcessor;
  267. prefabCatchmentProcessor.Process(prefabProcessorContext);
  268. EXPECT_TRUE(prefabProcessorContext.HasCompletedSuccessfully());
  269. // Verify entities are ordered by parent/child hierarchy
  270. const auto& processedObjects = prefabProcessorContext.GetProcessedObjects();
  271. // Verify that the nested network entity was discovered by the network prefab processor (NetworkPrefabProcessor::ProcessPrefab)
  272. // and 2 processed objects (spawnable and network.spawnable) were produced.
  273. EXPECT_EQ(processedObjects.size(), 2);
  274. // Static spawnable
  275. {
  276. const AZ::Data::AssetData& spawnableAsset = processedObjects[0].GetAsset();
  277. const AzFramework::Spawnable* spawnable = azrtti_cast<const AzFramework::Spawnable*>(&spawnableAsset);
  278. const AzFramework::Spawnable::EntityList& entityList = spawnable->GetEntities();
  279. EXPECT_TRUE(IsChildAfterParent(childName, parentName, entityList));
  280. EXPECT_TRUE(IsChildAfterParent(childOfChildName, childName, entityList));
  281. }
  282. // Network spawnable
  283. {
  284. const AZ::Data::AssetData& spawnableAsset = processedObjects[1].GetAsset();
  285. const AzFramework::Spawnable* spawnable = azrtti_cast<const AzFramework::Spawnable*>(&spawnableAsset);
  286. const AzFramework::Spawnable::EntityList& entityList = spawnable->GetEntities();
  287. EXPECT_FALSE(ContainsEntity(entityList, parentName));
  288. EXPECT_FALSE(ContainsEntity(entityList, childName));
  289. EXPECT_TRUE(ContainsEntity(entityList, childOfChildName));
  290. }
  291. }
  292. } // namespace UnitTest