NodeListDumpAction.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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/ComponentApplicationBus.h>
  9. #include <AzCore/RTTI/BehaviorContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/std/string/string_view.h>
  13. #include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
  14. #include <ScriptCanvas/Bus/ScriptCanvasBus.h>
  15. #include <ScriptCanvasDeveloperEditor/NodeListDumpAction.h>
  16. #include <QAction>
  17. #include <QApplication>
  18. #include <QClipboard>
  19. #include <QMimeData>
  20. #include <QMenu>
  21. namespace ScriptCanvasDeveloperEditor
  22. {
  23. namespace NodeListDumpAction
  24. {
  25. void DumpBehaviorContextNodes();
  26. void DumpBehaviorContextMethods(AZStd::string& dumpStr);
  27. void DumpBehaviorContextEbuses(AZStd::string& dumpStr);
  28. QAction* CreateNodeListDumpAction(QMenu* mainMenu)
  29. {
  30. QAction* nodeDumpAction = nullptr;
  31. if (mainMenu)
  32. {
  33. nodeDumpAction = mainMenu->addAction(QAction::tr("Dump EBus Nodes"));
  34. nodeDumpAction->setAutoRepeat(false);
  35. nodeDumpAction->setToolTip("Dumps a list of all EBus nodes(their inputs and outputs) to the clipboard");
  36. nodeDumpAction->setShortcut(QKeySequence(QAction::tr("Ctrl+Alt+N", "Debug|Dump EBus Nodes")));
  37. QObject::connect(nodeDumpAction, &QAction::triggered, &DumpBehaviorContextNodes);
  38. }
  39. return nodeDumpAction;
  40. }
  41. namespace DumpInternal
  42. {
  43. const size_t maxInputParameters = 10;
  44. }
  45. void DumpBehaviorContextNodes()
  46. {
  47. AZStd::string nodeList = "Group Name,Class Name/Ebus Name,Event Name/Method Name,Output Type";
  48. for (size_t i = 0; i < DumpInternal::maxInputParameters; ++i)
  49. {
  50. nodeList += ",Input Type " + AZStd::to_string(aznumeric_cast<int>(i) + 1);
  51. }
  52. nodeList += "\n";
  53. DumpBehaviorContextMethods(nodeList);
  54. DumpBehaviorContextEbuses(nodeList);
  55. QMimeData* mime = new QMimeData;
  56. mime->setData("text/plain", QByteArray(nodeList.cbegin(), static_cast<int>(nodeList.size())));
  57. QClipboard* clipboard = QApplication::clipboard();
  58. clipboard->setMimeData(mime);
  59. }
  60. void DumpBehaviorContextMethods(AZStd::string& dumpStr)
  61. {
  62. AZ::SerializeContext* serializeContext{};
  63. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  64. AZ::BehaviorContext* behaviorContext{};
  65. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  66. if (serializeContext == nullptr || behaviorContext == nullptr)
  67. {
  68. return;
  69. }
  70. for (const auto& classIter : behaviorContext->m_classes)
  71. {
  72. const AZ::BehaviorClass* behaviorClass = classIter.second;
  73. // Check for "ignore" attribute for ScriptCanvas
  74. auto excludeClassAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes));
  75. const bool excludeClass = excludeClassAttributeData && static_cast<AZ::u64>(excludeClassAttributeData->Get(nullptr)) & static_cast<AZ::u64>(AZ::Script::Attributes::ExcludeFlags::Documentation);
  76. if (excludeClass)
  77. {
  78. continue; // skip this class
  79. }
  80. AZStd::string_view categoryName;
  81. if (auto categoryAttribute = azrtti_cast<AZ::AttributeData<const char*>*>(AZ::FindAttribute(AZ::Script::Attributes::Category, behaviorClass->m_attributes)))
  82. {
  83. categoryName = categoryAttribute->Get(nullptr);
  84. }
  85. for (auto methodPair : behaviorClass->m_methods)
  86. {
  87. // Check for "ignore" attribute for ScriptCanvas
  88. auto excludeMethodAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, methodPair.second->m_attributes));
  89. const bool excludeMethod = excludeMethodAttributeData && static_cast<AZ::u64>(excludeMethodAttributeData->Get(nullptr)) & static_cast<AZ::u64>(AZ::Script::Attributes::ExcludeFlags::Documentation);
  90. if (excludeMethod)
  91. {
  92. continue; // skip this method
  93. }
  94. dumpStr += AZStd::string::format(R"("%s","%s","%s",)", !categoryName.empty() ? categoryName.data() : "", classIter.first.c_str(), methodPair.first.c_str());
  95. AZ::BehaviorMethod* method = methodPair.second;
  96. const auto result = method->HasResult() ? method->GetResult() : nullptr;
  97. if (result)
  98. {
  99. AZStd::string resultType = result->m_name;
  100. dumpStr += "\"" + resultType + "\"";
  101. }
  102. else
  103. {
  104. dumpStr += "\"void\"";
  105. }
  106. for (size_t i = 0; i < method->GetNumArguments(); ++i)
  107. {
  108. dumpStr += ",";
  109. if (const AZ::BehaviorParameter* argument = method->GetArgument(i))
  110. {
  111. AZStd::string argType = argument->m_name;
  112. dumpStr += "\"" + argType;
  113. const AZStd::string* argName = method->GetArgumentName(i);
  114. if (argName && !argName->empty())
  115. {
  116. dumpStr += "(" + *argName + ")";
  117. const AZStd::string* argToolTip = method->GetArgumentToolTip(i);
  118. if (argToolTip && !argToolTip->empty())
  119. {
  120. dumpStr += ": " + *argToolTip;
  121. }
  122. }
  123. else if (i == 0 && method->IsMember())
  124. {
  125. dumpStr += "(This Pointer)";
  126. }
  127. dumpStr += "\"";
  128. }
  129. }
  130. for (size_t i = method->GetNumArguments(); i < DumpInternal::maxInputParameters; ++i)
  131. {
  132. dumpStr += ",";
  133. }
  134. dumpStr += "\n";
  135. }
  136. }
  137. }
  138. void DumpBehaviorContextEBusHandlers(AZStd::string& dumpStr, AZ::BehaviorEBus* ebus, AZStd::string_view categoryName)
  139. {
  140. if (!ebus)
  141. {
  142. return;
  143. }
  144. if (!ebus->m_createHandler || !ebus->m_destroyHandler)
  145. {
  146. return;
  147. }
  148. AZ::BehaviorContext* behaviorContext{};
  149. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  150. AZ::BehaviorEBusHandler* handler;
  151. if (ebus->m_createHandler->InvokeResult(handler))
  152. {
  153. for (const AZ::BehaviorEBusHandler::BusForwarderEvent& event : handler->GetEvents())
  154. {
  155. dumpStr += AZStd::string::format(R"("%s","%s","%s",)", !categoryName.empty() ? categoryName.data() : "", ebus->m_name.c_str(), event.m_name);
  156. if (event.HasResult())
  157. {
  158. const AZ::BehaviorParameter& resultParam = event.m_parameters[AZ::eBehaviorBusForwarderEventIndices::Result];
  159. AZStd::string resultType = resultParam.m_name;
  160. dumpStr += "\"" + resultType + "\"";
  161. }
  162. else
  163. {
  164. dumpStr += "\"void\"";
  165. }
  166. for (size_t i = AZ::eBehaviorBusForwarderEventIndices::ParameterFirst; i < event.m_parameters.size(); ++i)
  167. {
  168. const AZ::BehaviorParameter& argParam = event.m_parameters[i];
  169. dumpStr += ",";
  170. AZStd::string argType = argParam.m_name;
  171. dumpStr += "\"" + argType;
  172. AZStd::string argName = argType;
  173. AZStd::string argToolTip = argType;
  174. if (event.m_metadataParameters.size() > i)
  175. {
  176. argName = event.m_metadataParameters[i].m_name;
  177. argToolTip = event.m_metadataParameters[i].m_toolTip;
  178. }
  179. if (!argName.empty())
  180. {
  181. dumpStr += "(" + argName + ")";
  182. if (!argToolTip.empty())
  183. {
  184. dumpStr += ": " + argToolTip;
  185. }
  186. }
  187. dumpStr += "\"";
  188. }
  189. for (size_t i = event.m_parameters.size(); i < DumpInternal::maxInputParameters; ++i)
  190. {
  191. dumpStr += ",";
  192. }
  193. dumpStr += "\n";
  194. }
  195. ebus->m_destroyHandler->Invoke(handler); // Destroys the Created EbusHandler
  196. }
  197. }
  198. void DumpBehaviorContextEbuses(AZStd::string& dumpStr)
  199. {
  200. AZ::SerializeContext* serializeContext{};
  201. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  202. AZ::BehaviorContext* behaviorContext{};
  203. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  204. if (serializeContext == nullptr || behaviorContext == nullptr)
  205. {
  206. return;
  207. }
  208. // We will skip buses that are ONLY registered on classes that derive from EditorComponentBase,
  209. // because they don't have a runtime implementation. Buses such as the TransformComponent which
  210. // is implemented by both an EditorComponentBase derived class and a Component derived class
  211. // will still appear
  212. AZStd::unordered_set<AZ::Crc32> skipBuses;
  213. AZStd::unordered_set<AZ::Crc32> potentialSkipBuses;
  214. AZStd::unordered_set<AZ::Crc32> nonSkipBuses;
  215. for (const auto& classIter : behaviorContext->m_classes)
  216. {
  217. const AZ::BehaviorClass* behaviorClass = classIter.second;
  218. // Check for "ignore" attribute for ScriptCanvas
  219. auto excludeClassAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes));
  220. const bool excludeClass = excludeClassAttributeData && static_cast<AZ::u64>(excludeClassAttributeData->Get(nullptr)) & static_cast<AZ::u64>(AZ::Script::Attributes::ExcludeFlags::Documentation);
  221. if (excludeClass)
  222. {
  223. for (const auto& requestBus : behaviorClass->m_requestBuses)
  224. {
  225. skipBuses.insert(AZ::Crc32(requestBus.c_str()));
  226. }
  227. continue; // skip this class
  228. }
  229. auto baseClass = AZStd::find(behaviorClass->m_baseClasses.begin(),
  230. behaviorClass->m_baseClasses.end(),
  231. AzToolsFramework::Components::EditorComponentBase::TYPEINFO_Uuid());
  232. if (baseClass != behaviorClass->m_baseClasses.end())
  233. {
  234. for (const auto& requestBus : behaviorClass->m_requestBuses)
  235. {
  236. potentialSkipBuses.insert(AZ::Crc32(requestBus.c_str()));
  237. }
  238. }
  239. // If the Ebus does not inherit from EditorComponentBase then do not skip it
  240. else
  241. {
  242. for (const auto& requestBus : behaviorClass->m_requestBuses)
  243. {
  244. nonSkipBuses.insert(AZ::Crc32(requestBus.c_str()));
  245. }
  246. }
  247. }
  248. // Add buses which are not on the non-skip list to the skipBuses set
  249. for (auto potentialSkipBus : potentialSkipBuses)
  250. {
  251. if (nonSkipBuses.find(potentialSkipBus) == nonSkipBuses.end())
  252. {
  253. skipBuses.insert(potentialSkipBus);
  254. }
  255. }
  256. for (const auto& ebusIter : behaviorContext->m_ebuses)
  257. {
  258. AZ::BehaviorEBus* ebus = ebusIter.second;
  259. if (ebus == nullptr)
  260. {
  261. continue;
  262. }
  263. auto excludeEbusAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, ebusIter.second->m_attributes));
  264. const bool excludeBus = excludeEbusAttributeData && static_cast<AZ::u64>(excludeEbusAttributeData->Get(nullptr)) & static_cast<AZ::u64>(AZ::Script::Attributes::ExcludeFlags::Documentation);
  265. auto skipBusIterator = skipBuses.find(AZ::Crc32(ebusIter.first.c_str()));
  266. if (skipBusIterator != skipBuses.end() || excludeBus)
  267. {
  268. continue;
  269. }
  270. AZStd::string_view categoryName;
  271. if (auto categoryAttribute = azrtti_cast<AZ::AttributeData<const char*>*>(AZ::FindAttribute(AZ::Script::Attributes::Category, ebus->m_attributes)))
  272. {
  273. categoryName = categoryAttribute->Get(nullptr);
  274. }
  275. DumpBehaviorContextEBusHandlers(dumpStr, ebus, categoryName);
  276. for (const auto& eventIter : ebus->m_events)
  277. {
  278. auto excludeEventAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, eventIter.second.m_attributes));
  279. const bool excludeEvent = excludeEventAttributeData && static_cast<AZ::u64>(excludeEventAttributeData->Get(nullptr)) & static_cast<AZ::u64>(AZ::Script::Attributes::ExcludeFlags::Documentation);
  280. if (excludeEvent)
  281. {
  282. continue;
  283. }
  284. const AZ::BehaviorMethod* const method = eventIter.second.m_event ? eventIter.second.m_event : eventIter.second.m_broadcast;
  285. if (!method)
  286. {
  287. continue;
  288. }
  289. dumpStr += AZStd::string::format(R"("%s","%s","%s",)", !categoryName.empty() ? categoryName.data() : "", ebusIter.first.c_str(), eventIter.first.c_str());
  290. const auto& resultParam = method->HasResult() ? method->GetResult() : nullptr;
  291. if (resultParam)
  292. {
  293. AZStd::string resultType = resultParam->m_name;
  294. dumpStr += "\"" + resultType + "\"";
  295. }
  296. else
  297. {
  298. dumpStr += "\"void\"";
  299. }
  300. for (size_t i = 0; i < method->GetNumArguments(); ++i)
  301. {
  302. dumpStr += ",";
  303. if (const auto& argParam = method->GetArgument(i))
  304. {
  305. AZStd::string argType = argParam->m_name;
  306. dumpStr += "\"" + argType;
  307. const AZStd::string* argName = method->GetArgumentName(i);
  308. if (argName && !argName->empty())
  309. {
  310. dumpStr += "(" + *argName + ")";
  311. const AZStd::string* argToolTip = method->GetArgumentToolTip(i);
  312. if (argToolTip && !argToolTip->empty())
  313. {
  314. dumpStr += ": " + *argToolTip;
  315. }
  316. }
  317. else if (i == 0 && method->HasBusId())
  318. {
  319. dumpStr += "(EBus ID)";
  320. }
  321. dumpStr += "\"";
  322. }
  323. }
  324. for (size_t i = method->GetNumArguments(); i < DumpInternal::maxInputParameters; ++i)
  325. {
  326. dumpStr += ",";
  327. }
  328. dumpStr += "\n";
  329. }
  330. }
  331. }
  332. }
  333. }