TickBusOrderViewerModule.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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/Memory/SystemAllocator.h>
  9. #include "TickBusOrderViewerSystemComponent.h"
  10. #include <IGem.h>
  11. #include <CryCommon/IConsole.h>
  12. #include <AzCore/Component/ComponentApplicationBus.h>
  13. #include <AzCore/Component/Entity.h>
  14. #include <AzCore/Component/TickBus.h>
  15. #include <AzCore/Module/ModuleManager.h>
  16. #include <AzCore/std/string/conversions.h>
  17. namespace TickBusOrderViewer
  18. {
  19. /**
  20. * Helper function for finding an entity by ID. Checks both the component application, and loaded modules.
  21. */
  22. AZ::Entity* FindEntity(const AZ::EntityId& entityId)
  23. {
  24. // First check the component application for the entity. This is where game, editor, and the root system entities live.
  25. AZ::Entity* entity = nullptr;
  26. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId);
  27. if (entity == nullptr)
  28. {
  29. // The entity was not in the component application's entity list, but it may be a system entity for a module.
  30. AZ::ModuleManagerRequestBus::Broadcast(&AZ::ModuleManagerRequestBus::Events::EnumerateModules, [&entity, entityId](const AZ::ModuleData& moduleData)
  31. {
  32. AZ::Entity* moduleEntity = moduleData.GetEntity();
  33. if (moduleEntity != nullptr && moduleEntity->GetId() == entityId)
  34. {
  35. entity = moduleEntity;
  36. // The entity was found, so stop the enumeration.
  37. return false;
  38. }
  39. // The matching entity is not in this module, keep looking through the rest of the modules.
  40. return true;
  41. });
  42. }
  43. return entity;
  44. }
  45. /**
  46. * Prints out all connected tickbus handlers, in the order they are ticked.
  47. * @param entityId An optional entity ID used to only display handlers for components on this entity.
  48. * displays all handlers if this is null.
  49. */
  50. void PrintTickbusHandlers(AZ::EntityId* entityId)
  51. {
  52. AZStd::string tickbusPrintoutTitle = "TickBus handlers in tick order";
  53. // If an entity ID was given, then update the printout title to contain information about that entity.
  54. if (entityId != nullptr)
  55. {
  56. // Search for the passed in entity, to print out its name.
  57. // Most people are going to think of their entities in terms of the name, and not the ID.
  58. AZ::Entity* entity = FindEntity(*entityId);
  59. if (entity != nullptr)
  60. {
  61. tickbusPrintoutTitle = AZStd::string::format("%s for entity \"%s\" %s",
  62. tickbusPrintoutTitle.c_str(),
  63. entity->GetName().c_str(),
  64. entityId->ToString().c_str());
  65. }
  66. else
  67. {
  68. // If the entity wasn't found, then print out the ID at least.
  69. tickbusPrintoutTitle = AZStd::string::format("%s for entity with id %s, entity could not be found",
  70. tickbusPrintoutTitle.c_str(),
  71. entityId->ToString().c_str());
  72. }
  73. }
  74. AZ_Printf("TickBusOrderViewer", tickbusPrintoutTitle.c_str());
  75. // Visit every tickbus handler. These are already sorted in the order they will be called.
  76. AZ::TickBus::EnumerateHandlers([entityId](AZ::TickEvents* handler)
  77. {
  78. // If this handler is a component, then it will have an associated entity.
  79. // This will allow printing additional, useful information for the user.
  80. AZ::Component* component = azrtti_cast<AZ::Component*>(handler);
  81. if (component && (entityId == nullptr || component->GetEntityId() == *entityId))
  82. {
  83. AZStd::string entityName = component->GetEntity() != nullptr ?
  84. component->GetEntity()->GetName() : "[No entity found]";
  85. // Print out everything about this tickbus listener that can help the user debug
  86. // their tick ordering issue that caused them to call this.
  87. // This includes:
  88. // * The component's type as a string and a UUID.
  89. // * The component's individual ID, which can be useful if the entity has duplicate components of the same type.
  90. // * The associated entity's name, so the user can trace this tick handler to the entity.
  91. // * The associated entity's ID, because entity names may not be unique.
  92. AZ_Printf("TickBusOrderViewer", "\t%d - Entity \"%s\" %s, component %s %s with ID %u",
  93. handler->GetTickOrder(),
  94. entityName.c_str(),
  95. component->GetEntityId().ToString().c_str(),
  96. component->RTTI_GetTypeName(),
  97. component->RTTI_GetType().ToString<AZStd::string>().c_str(),
  98. component->GetId());
  99. }
  100. else if(entityId == nullptr)
  101. {
  102. // This handler wasn't a component, so print out as much information as can be gathered.
  103. AZ_Printf("TickBusOrderViewer", "\t%d - Object with type %s %s",
  104. handler->GetTickOrder(),
  105. handler->RTTI_GetTypeName(),
  106. handler->RTTI_GetType().ToString<AZStd::string>().c_str());
  107. }
  108. // Return true so the enumeration continues, all handles need to be checked.
  109. return true;
  110. });
  111. }
  112. /**
  113. * Console command to print the handlers for the tickbus, in the order they are ticked.
  114. */
  115. void PrintTickbusHandlerOrder(IConsoleCmdArgs* args)
  116. {
  117. // If only the command was supplied with no entity ID, then print out information for
  118. // all tickbus handlers.
  119. if (args == nullptr || args->GetArgCount() == 1)
  120. {
  121. PrintTickbusHandlers(nullptr);
  122. return;
  123. }
  124. // If the passed in argument was not valid, print a warning and then the information for
  125. // all tickbus handlers.
  126. const char* entityIdString = args->GetArg(1);
  127. if (entityIdString == nullptr)
  128. {
  129. AZ_Warning("TickBusOrderViewer", false, "print_tickbus_handlers was called with an invalid parameter, printing out all handlers.");
  130. PrintTickbusHandlers(nullptr);
  131. return;
  132. }
  133. // Convert the passed in string to an entity ID. If this fails, then the user will need
  134. // to run the command again with a better formatted entity ID.
  135. AZ::EntityId entityId(AZStd::stoull(AZStd::string(entityIdString)));
  136. PrintTickbusHandlers(&entityId);
  137. }
  138. class TickBusOrderViewerModule
  139. : public CryHooksModule
  140. {
  141. public:
  142. AZ_RTTI(TickBusOrderViewerModule, "{DAE8B6D3-23ED-4547-9D0C-9F42CA812A06}", CryHooksModule);
  143. AZ_CLASS_ALLOCATOR(TickBusOrderViewerModule, AZ::SystemAllocator);
  144. TickBusOrderViewerModule()
  145. : CryHooksModule()
  146. {
  147. // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
  148. m_descriptors.insert(m_descriptors.end(), {
  149. TickBusOrderViewerSystemComponent::CreateDescriptor(),
  150. });
  151. }
  152. /**
  153. * Add required SystemComponents to the SystemEntity.
  154. */
  155. AZ::ComponentTypeList GetRequiredSystemComponents() const override
  156. {
  157. return AZ::ComponentTypeList{
  158. azrtti_typeid<TickBusOrderViewerSystemComponent>(),
  159. };
  160. }
  161. /**
  162. * Override for CryHooksModule::OnCrySystemInitialized to add the console commands
  163. * to print out tick bus information.
  164. */
  165. void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& initParams) override
  166. {
  167. CryHooksModule::OnCrySystemInitialized(system, initParams);
  168. // Register the command to print the tickbus handlers out.
  169. REGISTER_COMMAND("print_tickbus_handlers", &PrintTickbusHandlerOrder, 0, "Prints out the handlers for the tickbus in tick order. "
  170. "With zero parameters, prints all handlers. With one parameter, it converts that to an entity ID and only prints components for that entity.");
  171. }
  172. };
  173. }
  174. #if defined(O3DE_GEM_NAME)
  175. AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME), TickBusOrderViewer::TickBusOrderViewerModule)
  176. #else
  177. AZ_DECLARE_MODULE_CLASS(Gem_TickBusOrderViewer, TickBusOrderViewer::TickBusOrderViewerModule)
  178. #endif