12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorDefs.h"
- #include "PythonEditorFuncs.h"
- // Qt
- #include <QInputDialog>
- #include <QMessageBox>
- #include <QFileDialog>
- #include <AzCore/Utils/Utils.h>
- // AzToolsFramework
- #include <AzCore/RTTI/BehaviorContext.h>
- #include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
- #include <AzToolsFramework/Entity/EditorEntityContextBus.h>
- #include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
- // Editor
- #include "CryEdit.h"
- #include "GameEngine.h"
- #include "ViewManager.h"
- #include "GenericSelectItemDialog.h"
- #include "Commands/CommandManager.h"
- AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
- namespace
- {
- //////////////////////////////////////////////////////////////////////////
- const char* PyGetCVarAsString(const char* pName)
- {
- ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName);
- if (!pCVar)
- {
- AZ_Warning("editor", false, "PyGetCVar: Attempt to access non-existent CVar '%s'", pName ? pName : "(null)");
- return "(missing)";
- }
- return pCVar->GetString();
- }
- //////////////////////////////////////////////////////////////////////////
- // forward declarations
- void PySetCVarFromInt(const char* pName, int pValue);
- void PySetCVarFromFloat(const char* pName, float pValue);
- //////////////////////////////////////////////////////////////////////////
- void PySetCVarFromString(const char* pName, const char* pValue)
- {
- ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName);
- if (!pCVar)
- {
- AZ_Warning("editor", false, "Attempt to set non-existent string CVar '%s'", pName ? pName : "(null)");
- }
- else if (pCVar->GetType() == CVAR_INT)
- {
- PySetCVarFromInt(pName, static_cast<int>(std::stol(pValue)));
- }
- else if (pCVar->GetType() == CVAR_FLOAT)
- {
- PySetCVarFromFloat(pName, static_cast<float>(std::stod(pValue)));
- }
- else if (pCVar->GetType() != CVAR_STRING)
- {
- AZ_Warning("editor", false, "Type mismatch while assigning CVar '%s' as a string.", pName ? pName : "(null)");
- }
- else
- {
- pCVar->Set(pValue);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void PySetCVarFromInt(const char* pName, int pValue)
- {
- ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName);
- if (!pCVar)
- {
- AZ_Warning("editor", false, "Attempt to set non-existent integer CVar '%s'", pName ? pName : "(null)");
- }
- else if (pCVar->GetType() == CVAR_FLOAT)
- {
- PySetCVarFromFloat(pName, float(pValue));
- }
- else if (pCVar->GetType() == CVAR_STRING)
- {
- auto stringValue = AZStd::to_string(pValue);
- PySetCVarFromString(pName, stringValue.c_str());
- }
- else if (pCVar->GetType() != CVAR_INT)
- {
- AZ_Warning("editor", false, "Type mismatch while assigning CVar '%s' as an integer.", pName ? pName : "(null)");
- }
- else
- {
- pCVar->Set(pValue);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void PySetCVarFromFloat(const char* pName, float pValue)
- {
- ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName);
- if (!pCVar)
- {
- AZ_Warning("editor", false, "Attempt to set non-existent float CVar '%s'", pName ? pName : "(null)");
- }
- else if (pCVar->GetType() == CVAR_INT)
- {
- PySetCVarFromInt(pName, static_cast<int>(pValue));
- }
- else if (pCVar->GetType() == CVAR_STRING)
- {
- auto stringValue = AZStd::to_string(pValue);
- PySetCVarFromString(pName, stringValue.c_str());
- }
- else if (pCVar->GetType() != CVAR_FLOAT)
- {
- AZ_Warning("editor", false, "Type mismatch while assigning CVar '%s' as a float.", pName ? pName : "(null)");
- }
- else
- {
- pCVar->Set(pValue);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void PySetCVarFromAny(const char* pName, const AZStd::any& value)
- {
- ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName);
- if (!pCVar)
- {
- AZ_Warning("editor", false, "Attempt to set non-existent float CVar '%s'", pName ? pName : "(null)");
- }
- else if (pCVar->GetType() == CVAR_INT)
- {
- PySetCVarFromInt(pName, static_cast<int>(AZStd::any_cast<AZ::s64>(value)));
- }
- else if (pCVar->GetType() == CVAR_FLOAT)
- {
- PySetCVarFromFloat(pName, static_cast<float>(AZStd::any_cast<double>(value)));
- }
- else if (pCVar->GetType() == CVAR_STRING)
- {
- PySetCVarFromString(pName, AZStd::any_cast<AZStd::string_view>(value).data());
- }
- else
- {
- AZ_Warning("editor", false, "Type mismatch while assigning CVar '%s' as a float.", pName ? pName : "(null)");
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void PyEnterGameMode()
- {
- if (GetIEditor()->GetGameEngine())
- {
- GetIEditor()->GetGameEngine()->RequestSetGameMode(true);
- }
- }
- void PyEnterGameModeFullscreen()
- {
- ed_previewGameInFullscreen_once = true;
- PyEnterGameMode();
- }
- void PyExitGameMode()
- {
- if (GetIEditor()->GetGameEngine())
- {
- GetIEditor()->GetGameEngine()->RequestSetGameMode(false);
- }
- }
- bool PyIsInGameMode()
- {
- return GetIEditor()->IsInGameMode();
- }
- //////////////////////////////////////////////////////////////////////////
- void PyEnterSimulationMode()
- {
- if (!GetIEditor()->IsInSimulationMode())
- {
- CCryEditApp::instance()->OnSwitchPhysics();
- }
- }
- void PyExitSimulationMode()
- {
- if (GetIEditor()->IsInSimulationMode())
- {
- CCryEditApp::instance()->OnSwitchPhysics();
- }
- }
- bool PyIsInSimulationMode()
- {
- return GetIEditor()->IsInSimulationMode();
- }
- //////////////////////////////////////////////////////////////////////////
- void PyRunConsole(const char* text)
- {
- GetIEditor()->GetSystem()->GetIConsole()->ExecuteString(text);
- }
- //////////////////////////////////////////////////////////////////////////
- bool GetPythonScriptPath(const char* pFile, QString& path)
- {
- bool bRelativePath = true;
- char drive[_MAX_DRIVE];
- char fdir[_MAX_DIR];
- char fname[_MAX_FNAME];
- char fext[_MAX_EXT];
- drive[0] = '\0';
- _splitpath_s(pFile, drive, fdir, fname, fext);
- if (strlen(drive) != 0)
- {
- bRelativePath = false;
- }
- if (bRelativePath)
- {
- // Try to open from user folder
- QString userSandboxFolder = Path::GetResolvedUserSandboxFolder();
- Path::ConvertBackSlashToSlash(userSandboxFolder);
- path = userSandboxFolder + pFile;
- // If not found try editor folder
- if (!CFileUtil::FileExists(path))
- {
- AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
- QDir engineDir = !engineRoot.empty() ? QDir(QString(engineRoot.c_str())) : QDir::current();
- QString scriptFolder = engineDir.absoluteFilePath("Assets/Editor/Scripts/");
- Path::ConvertBackSlashToSlash(scriptFolder);
- path = scriptFolder + pFile;
- if (!CFileUtil::FileExists(path))
- {
- QString error = QString("Could not find '%1'\n in '%2'\n or '%3'\n").arg(pFile).arg(userSandboxFolder).arg(scriptFolder);
- AZ_Warning("python", false, error.toUtf8().data());
- return false;
- }
- }
- }
- else
- {
- path = pFile;
- if (!CFileUtil::FileExists(path))
- {
- QString error = QString("Could not find '") + pFile + "'\n";
- AZ_Warning("python", false, error.toUtf8().data());
- return false;
- }
- }
- Path::ConvertBackSlashToSlash(path);
- return true;
- }
- //////////////////////////////////////////////////////////////////////////
- void GetPythonArgumentsVector(const char* pArguments, QStringList& inputArguments)
- {
- if (pArguments == nullptr)
- {
- return;
- }
- inputArguments = QString(pArguments).split(" ", Qt::SkipEmptyParts);
- }
- //////////////////////////////////////////////////////////////////////////
- void PyRunFileWithParameters(const char* pFile, const char* pArguments)
- {
- QString path;
- QStringList inputArguments;
- GetPythonArgumentsVector(pArguments, inputArguments);
- if (GetPythonScriptPath(pFile, path))
- {
- AZStd::vector<AZStd::string_view> argList;
- argList.reserve(inputArguments.size() + 1);
- QByteArray p = path.toUtf8();
- QVector<QByteArray> argData;
- argData.reserve(inputArguments.count());
- for (auto iter = inputArguments.begin(); iter != inputArguments.end(); ++iter)
- {
- argData.push_back(iter->toLatin1());
- argList.push_back(argData.last().data());
- }
- using namespace AzToolsFramework;
- EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, p.data(), argList);
- }
- }
- void PyRunFile(const char* pFile)
- {
- PyRunFileWithParameters(pFile, nullptr);
- }
- //////////////////////////////////////////////////////////////////////////
- void PyExecuteCommand(const char* cmdline)
- {
- GetIEditor()->GetCommandManager()->Execute(cmdline);
- }
- //////////////////////////////////////////////////////////////////////////
- void PyLog(const char* pMessage)
- {
- if (strcmp(pMessage, "") != 0)
- {
- CryLogAlways("%s", pMessage);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- bool PyMessageBox(const char* pMessage)
- {
- return QMessageBox::information(QApplication::activeWindow(), QString(), pMessage, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok;
- }
- bool PyMessageBoxYesNo(const char* pMessage)
- {
- return QMessageBox::question(QApplication::activeWindow(), QString(), pMessage) == QMessageBox::Yes;
- }
- bool PyMessageBoxOK(const char* pMessage)
- {
- return QMessageBox::information(QApplication::activeWindow(), QString(), pMessage) == QMessageBox::Ok;
- }
- AZStd::string PyEditBox(AZStd::string_view pTitle)
- {
- QString stringValue = QInputDialog::getText(AzToolsFramework::GetActiveWindow(), pTitle.data(), QString());
- if (!stringValue.isEmpty())
- {
- return stringValue.toUtf8().constData();
- }
- return "";
- }
- AZStd::any PyEditBoxAndCheckProperty(const char* pTitle)
- {
- QString stringValue = QInputDialog::getText(AzToolsFramework::GetActiveWindow(), pTitle, QString());
- if (!stringValue.isEmpty())
- {
- // detect data type
- QString tempString = stringValue;
- int countComa = 0;
- int countOpenRoundBraket = 0;
- int countCloseRoundBraket = 0;
- int countDots = 0;
- int posDots = 0;
- int posComa = 0;
- int posOpenRoundBraket = 0;
- int posCloseRoundBraket = 0;
- for (int i = 0; i < 3; i++)
- {
- if (tempString.indexOf(".", posDots) > -1)
- {
- posDots = tempString.indexOf(".", posDots) + 1;
- countDots++;
- }
- if (tempString.indexOf(",", posComa) > -1)
- {
- posComa = tempString.indexOf(",", posComa) + 1;
- countComa++;
- }
- if (tempString.indexOf("(", posOpenRoundBraket) > -1)
- {
- posOpenRoundBraket = tempString.indexOf("(", posOpenRoundBraket) + 1;
- countOpenRoundBraket++;
- }
- if (tempString.indexOf(")", posCloseRoundBraket) > -1)
- {
- posCloseRoundBraket = tempString.indexOf(")", posCloseRoundBraket) + 1;
- countCloseRoundBraket++;
- }
- }
- if (countDots == 3 && countComa == 2 && countOpenRoundBraket == 1 && countCloseRoundBraket == 1)
- {
- // for example: (1.95, 2.75, 3.36)
- QString valueRed = stringValue;
- int iStart = valueRed.indexOf("(");
- valueRed.remove(0, iStart + 1);
- int iEnd = valueRed.indexOf(",");
- valueRed.remove(iEnd, valueRed.length());
- float fValueRed = valueRed.toFloat();
- QString valueGreen = stringValue;
- iStart = valueGreen.indexOf(",");
- valueGreen.remove(0, iStart + 1);
- iEnd = valueGreen.indexOf(",");
- valueGreen.remove(iEnd, valueGreen.length());
- float fValueGreen = valueGreen.toFloat();
- QString valueBlue = stringValue;
- valueBlue.remove(0, valueBlue.indexOf(",") + 1);
- valueBlue.remove(0, valueBlue.indexOf(",") + 1);
- valueBlue.remove(valueBlue.indexOf(")"), valueBlue.length());
- float fValueBlue = valueBlue.toFloat();
- return AZStd::make_any<AZ::Vector3>(fValueRed, fValueGreen, fValueBlue);
- }
- else if (countDots == 0 && countComa == 2 && countOpenRoundBraket == 1 && countCloseRoundBraket == 1)
- {
- // for example: (128, 32, 240)
- const AZ::u8 lowColorValue { 0 };
- const AZ::u8 highColorValue { 255 };
- QString valueRed = stringValue;
- int iStart = valueRed.indexOf("(");
- valueRed.remove(0, iStart + 1);
- int iEnd = valueRed.indexOf(",");
- valueRed.remove(iEnd, valueRed.length());
- AZ::u8 iValueRed = AZStd::clamp(aznumeric_cast<AZ::u8>(valueRed.toInt()), lowColorValue, highColorValue);
- QString valueGreen = stringValue;
- iStart = valueGreen.indexOf(",");
- valueGreen.remove(0, iStart + 1);
- iEnd = valueGreen.indexOf(",");
- valueGreen.remove(iEnd, valueGreen.length());
- AZ::u8 iValueGreen = AZStd::clamp(aznumeric_cast<AZ::u8>(valueGreen.toInt()), lowColorValue, highColorValue);
- QString valueBlue = stringValue;
- valueBlue.remove(0, valueBlue.indexOf(",") + 1);
- valueBlue.remove(0, valueBlue.indexOf(",") + 1);
- valueBlue.remove(valueBlue.indexOf(")"), valueBlue.length());
- AZ::u8 iValueBlue = AZStd::clamp(aznumeric_cast<AZ::u8>(valueBlue.toInt()), lowColorValue, highColorValue);
- return AZStd::make_any<AZ::Color>(iValueRed, iValueGreen, iValueBlue, highColorValue);
- }
- else if (countDots == 1 && countComa == 0 && countOpenRoundBraket == 0 && countCloseRoundBraket == 0)
- {
- // for example: 2.56
- return AZStd::make_any<double>(stringValue.toDouble());
- }
- else if (countDots == 0 && countComa == 0 && countOpenRoundBraket == 0 && countCloseRoundBraket == 0)
- {
- if (stringValue == "False" || stringValue == "True")
- {
- // for example: True
- return AZStd::make_any<bool>(stringValue == "True");
- }
- else
- {
- const bool anyNotDigits = AZStd::any_of(stringValue.begin(), stringValue.end(), [](const QChar& ch) { return !ch.isDigit(); });
- // looks like a string value?
- if (stringValue.isEmpty() || anyNotDigits)
- {
- // for example: Hello
- return AZStd::make_any<AZStd::string>(stringValue.toUtf8().data());
- }
- else // then it looks like an integer
- {
- // for example: 456
- return AZStd::make_any<AZ::s64>(stringValue.toInt());
- }
- }
- }
- }
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QObject::tr("Invalid Data"), QObject::tr("Invalid data type."));
- return {};
- }
- AZStd::string PyOpenFileBox()
- {
- QString path = QFileDialog::getOpenFileName();
- if (!path.isEmpty())
- {
- Path::ConvertBackSlashToSlash(path);
- }
- return path.toUtf8().constData();
- }
- AZStd::string PyComboBox(AZStd::string title, AZStd::vector<AZStd::string> values, int selectedIdx = 0)
- {
- AZStd::string result;
- if (title.empty())
- {
- throw std::runtime_error("Incorrect title argument passed in. ");
- }
- if (values.size() == 0)
- {
- throw std::runtime_error("Empty value list passed in. ");
- }
- QStringList list;
- for (const AZStd::string& str : values)
- {
- list.push_back(str.c_str());
- }
- CGenericSelectItemDialog pyDlg;
- pyDlg.setWindowTitle(title.c_str());
- pyDlg.SetMode(CGenericSelectItemDialog::eMODE_LIST);
- pyDlg.SetItems(list);
- pyDlg.PreSelectItem(list[selectedIdx]);
- if (pyDlg.exec() == QDialog::Accepted)
- {
- result = pyDlg.GetSelectedItem().toUtf8().constData();
- }
- return result;
- }
- void PyCrash()
- {
- AZ_Crash();
- }
- static void PyDrawLabel(int x, int y, float size, float r, float g, float b, float a, const char* pLabel)
- {
- if (!pLabel)
- {
- throw std::logic_error("No label given.");
- return;
- }
- if (!r || !g || !b || !a)
- {
- throw std::logic_error("Invalid color parameters given.");
- return;
- }
- if (!x || !y || !size)
- {
- throw std::logic_error("Invalid position or size parameters given.");
- }
- else
- {
- // ToDo: Remove function or update to work with Atom? LYN-3672
- // float color[] = {r, g, b, a};
- // ???->Draw2dLabel(x, y, size, color, false, pLabel);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Constrain
- //////////////////////////////////////////////////////////////////////////
- const char* PyGetAxisConstraint()
- {
- AxisConstrains actualConstrain = GetIEditor()->GetAxisConstrains();
- switch (actualConstrain)
- {
- case AXIS_X:
- return "X";
- case AXIS_Y:
- return "Y";
- case AXIS_Z:
- return "Z";
- case AXIS_XY:
- return "XY";
- case AXIS_XZ:
- return "XZ";
- case AXIS_YZ:
- return "YZ";
- case AXIS_XYZ:
- return "XYZ";
- case AXIS_TERRAIN:
- return (GetIEditor()->IsTerrainAxisIgnoreObjects()) ? "TERRAIN" : "TERRAINSNAP";
- default:
- throw std::logic_error("Invalid axes.");
- }
- }
- void PySetAxisConstraint(AZStd::string_view pConstrain)
- {
- if (pConstrain == "X")
- {
- GetIEditor()->SetAxisConstraints(AXIS_X);
- }
- else if (pConstrain == "Y")
- {
- GetIEditor()->SetAxisConstraints(AXIS_Y);
- }
- else if (pConstrain == "Z")
- {
- GetIEditor()->SetAxisConstraints(AXIS_Z);
- }
- else if (pConstrain == "XY")
- {
- GetIEditor()->SetAxisConstraints(AXIS_XY);
- }
- else if (pConstrain == "YZ")
- {
- GetIEditor()->SetAxisConstraints(AXIS_YZ);
- }
- else if (pConstrain == "XZ")
- {
- GetIEditor()->SetAxisConstraints(AXIS_XZ);
- }
- else if (pConstrain == "XYZ")
- {
- GetIEditor()->SetAxisConstraints(AXIS_XYZ);
- }
- else if (pConstrain == "TERRAIN")
- {
- GetIEditor()->SetAxisConstraints(AXIS_TERRAIN);
- GetIEditor()->SetTerrainAxisIgnoreObjects(true);
- }
- else if (pConstrain == "TERRAINSNAP")
- {
- GetIEditor()->SetAxisConstraints(AXIS_TERRAIN);
- GetIEditor()->SetTerrainAxisIgnoreObjects(false);
- }
- else
- {
- throw std::logic_error("Invalid axes.");
- }
- }
- //////////////////////////////////////////////////////////////////////////
- AZ::IO::Path PyGetPakFromFile(const char* filename)
- {
- auto pIPak = GetIEditor()->GetSystem()->GetIPak();
- AZ::IO::HandleType fileHandle = pIPak->FOpen(filename, "rb");
- if (fileHandle == AZ::IO::InvalidHandle)
- {
- throw std::logic_error("Invalid file name.");
- }
- AZ::IO::Path pArchPath = pIPak->GetFileArchivePath(fileHandle);
- pIPak->FClose(fileHandle);
- return pArchPath;
- }
- //////////////////////////////////////////////////////////////////////////
- void PyUndo()
- {
- GetIEditor()->Undo();
- }
- //////////////////////////////////////////////////////////////////////////
- void PyRedo()
- {
- GetIEditor()->Redo();
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Temporal, to be removed by LY-101149
- AZ::EntityId PyFindEditorEntity(const char* name)
- {
- AZ::EntityId foundEntityId;
- auto searchFunc = [name, &foundEntityId](AZ::Entity* e)
- {
- if (!foundEntityId.IsValid() && e->GetName() == name)
- {
- bool isEditorEntity = false;
- AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(isEditorEntity, &AzToolsFramework::EditorEntityContextRequests::IsEditorEntity, e->GetId());
- if (isEditorEntity)
- {
- foundEntityId = e->GetId();
- }
- }
- };
- AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::EnumerateEntities, searchFunc);
- return foundEntityId;
- }
- AZ::EntityId PyFindGameEntity(const char* name)
- {
- AZ::EntityId foundEntityId;
- auto searchFunc = [name, &foundEntityId](AZ::Entity* e)
- {
- if (!foundEntityId.IsValid() && e->GetName() == name)
- {
- bool isEditorEntity = true;
- AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(isEditorEntity, &AzToolsFramework::EditorEntityContextRequests::IsEditorEntity, e->GetId());
- if (!isEditorEntity)
- {
- foundEntityId = e->GetId();
- }
- }
- };
- AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::EnumerateEntities, searchFunc);
- return foundEntityId;
- }
- struct PyDumpBindings
- {
- static AZ_INLINE bool IsBehaviorFlaggedForEditor(const AZ::AttributeArray& attributes)
- {
- // defaults to Launcher
- AZ::Script::Attributes::ScopeFlags scopeType = AZ::Script::Attributes::ScopeFlags::Launcher;
- AZ::Attribute* scopeAttribute = AZ::FindAttribute(AZ::Script::Attributes::Scope, attributes);
- if (scopeAttribute)
- {
- AZ::AttributeReader scopeAttributeReader(nullptr, scopeAttribute);
- scopeAttributeReader.Read<AZ::Script::Attributes::ScopeFlags>(scopeType);
- }
- return (scopeType == AZ::Script::Attributes::ScopeFlags::Automation || scopeType == AZ::Script::Attributes::ScopeFlags::Common);
- }
- static AZ_INLINE AZStd::string GetModuleName(const AZ::AttributeArray& attributes)
- {
- AZStd::string moduleName;
- AZ::Attribute* moduleAttribute = AZ::FindAttribute(AZ::Script::Attributes::Module, attributes);
- if (moduleAttribute)
- {
- AZ::AttributeReader scopeAttributeReader(nullptr, moduleAttribute);
- scopeAttributeReader.Read<AZStd::string>(moduleName);
- }
- if (!moduleName.empty())
- {
- moduleName = "azlmbr." + moduleName;
- }
- else
- {
- moduleName = "azlmbr";
- }
- return moduleName;
- }
- static AZStd::string ParameterToString(const AZ::BehaviorMethod* method, size_t index)
- {
- const AZStd::string* argNameStr = method->GetArgumentName(index);
- const char* argName = (argNameStr && !argNameStr->empty()) ? argNameStr->c_str() : nullptr;
- if (argName)
- {
- return AZStd::string::format("%s %s", method->GetArgument(index)->m_name, argName);
- }
- else
- {
- return method->GetArgument(index)->m_name;
- }
- }
- static AZStd::string MethodArgumentsToString(const AZ::BehaviorMethod* method)
- {
- AZStd::string ret;
- AZStd::string argumentStr;
- for (size_t i = 0; i < method->GetNumArguments(); ++i)
- {
- argumentStr = ParameterToString(method, i);
- ret += argumentStr;
- if (i < method->GetNumArguments() - 1)
- {
- ret += ", ";
- }
- }
- return ret;
- }
- static AZStd::string MethodToString(const AZStd::string& methodName, const AZ::BehaviorMethod* method)
- {
- AZStd::string methodNameStrip = methodName.data() + methodName.rfind(':') + 1; // remove ClassName:: part as it is redundant
- return AZStd::string::format("%s %s(%s)%s", method->GetResult()->m_name, methodNameStrip.c_str(), MethodArgumentsToString(method).c_str(), method->m_isConst ? " const" : "");
- }
- static AZStd::string GetExposedPythonClasses()
- {
- AZ::BehaviorContext* behaviorContext(nullptr);
- AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
- AZStd::string output = "";
- output += "// Classes\n\n";
- for (auto elem : behaviorContext->m_classes)
- {
- AZ::BehaviorClass* cls = elem.second;
- bool exposedToPython = IsBehaviorFlaggedForEditor(cls->m_attributes);
- if (!exposedToPython)
- continue;
- output += AZStd::string::format("// Module: %s\n", GetModuleName(cls->m_attributes).c_str());
- output += AZStd::string::format("class %s\n", cls->m_name.c_str());
- output += "{\n";
- if (cls->m_methods.size() > 0)
- {
- output += " // Methods\n";
- for (auto method_elem : cls->m_methods)
- {
- output += AZStd::string::format(" %s;\n", MethodToString(method_elem.first, method_elem.second).c_str());
- }
- }
- if (cls->m_properties.size() > 0)
- {
- output += " // Properties\n";
- for (auto property_elem : cls->m_properties)
- {
- AZ::BehaviorProperty* bproperty = property_elem.second;
- output += AZStd::string::format(" %s %s;\n", bproperty->m_getter->GetResult()->m_name, bproperty->m_name.c_str());
- }
- }
- output += "}\n";
- }
- output += "\n\n// Ebuses\n\n";
- for (auto elem : behaviorContext->m_ebuses)
- {
- AZ::BehaviorEBus* ebus = elem.second;
- bool exposedToPython = IsBehaviorFlaggedForEditor(ebus->m_attributes);
- if (!exposedToPython)
- continue;
- output += AZStd::string::format("// Module: %s\n", GetModuleName(ebus->m_attributes).c_str());
- output += AZStd::string::format("ebus %s\n", ebus->m_name.c_str());
- output += "{\n";
- for (auto event_elem : ebus->m_events)
- {
- auto method = event_elem.second.m_event ? event_elem.second.m_event : event_elem.second.m_broadcast;
- if (method)
- {
- const char* comment = event_elem.second.m_event ? "/* event */" : "/* broadcast */";
- output += AZStd::string::format(" %s %s\n", comment, MethodToString(event_elem.first, method).c_str());
- }
- else
- {
- output += AZStd::string::format(" %s %s\n", "/* unknown */", event_elem.first.c_str());
- }
- }
- if (ebus->m_createHandler)
- {
- AZ::BehaviorEBusHandler* handler = nullptr;
- ebus->m_createHandler->InvokeResult(handler);
- if (handler)
- {
- const auto& notifications = handler->GetEvents();
- for (const auto& notification : notifications)
- {
- AZStd::string argsStr;
- const size_t paramCount = notification.m_parameters.size();
- for (size_t i = 0; i < notification.m_parameters.size(); ++i)
- {
- AZStd::string argName = notification.m_parameters[i].m_name;
- argsStr += argName;
- if (i != paramCount - 1)
- {
- argsStr += ", ";
- }
- }
- AZStd::string funcName = notification.m_name;
- output += AZStd::string::format(" /* notification */ %s(%s);\n", funcName.c_str(), argsStr.c_str());
- }
- ebus->m_destroyHandler->Invoke(handler);
- }
- }
- output += "}\n";
- }
- AzFramework::StringFunc::Replace(output, "AZStd::basic_string<char, AZStd::char_traits<char>, allocator>", "AZStd::string");
- return output;
- }
- };
- //////////////////////////////////////////////////////////////////////////
- namespace AzToolsFramework
- {
- void PythonEditorComponent::Reflect(AZ::ReflectContext* context)
- {
- if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
- {
- behaviorContext->EBus<EditorLayerPythonRequestBus>("PythonEditorBus")
- ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
- ->Attribute(AZ::Script::Attributes::Module, "python_editor_funcs")
- ->Event("GetCVar", &EditorLayerPythonRequestBus::Events::GetCVar)
- ->Event("SetCVar", &EditorLayerPythonRequestBus::Events::SetCVar)
- ->Event("SetCVarFromString", &EditorLayerPythonRequestBus::Events::SetCVarFromString)
- ->Event("SetCVarFromInteger", &EditorLayerPythonRequestBus::Events::SetCVarFromInteger)
- ->Event("SetCVarFromFloat", &EditorLayerPythonRequestBus::Events::SetCVarFromFloat)
- ->Event("RunConsole", &EditorLayerPythonRequestBus::Events::PyRunConsole)
- ->Event("EnterGameMode", &EditorLayerPythonRequestBus::Events::EnterGameMode)
- ->Event("IsInGameMode", &EditorLayerPythonRequestBus::Events::IsInGameMode)
- ->Event("ExitGameMode", &EditorLayerPythonRequestBus::Events::ExitGameMode)
- ->Event("EnterSimulationMode", &EditorLayerPythonRequestBus::Events::EnterSimulationMode)
- ->Event("IsInSimulationMode", &EditorLayerPythonRequestBus::Events::IsInSimulationMode)
- ->Event("ExitSimulationMode", &EditorLayerPythonRequestBus::Events::ExitSimulationMode)
- ->Event("RunFile", &EditorLayerPythonRequestBus::Events::RunFile)
- ->Event("RunFileParameters", &EditorLayerPythonRequestBus::Events::RunFileParameters)
- ->Event("ExecuteCommand", &EditorLayerPythonRequestBus::Events::ExecuteCommand)
- ->Event("MessageBoxOkCancel", &EditorLayerPythonRequestBus::Events::MessageBoxOkCancel)
- ->Event("MessageBoxYesNo", &EditorLayerPythonRequestBus::Events::MessageBoxYesNo)
- ->Event("MessageBoxOk", &EditorLayerPythonRequestBus::Events::MessageBoxOk)
- ->Event("EditBox", &EditorLayerPythonRequestBus::Events::EditBox)
- ->Event("EditBoxCheckDataType", &EditorLayerPythonRequestBus::Events::EditBoxCheckDataType)
- ->Event("OpenFileBox", &EditorLayerPythonRequestBus::Events::OpenFileBox)
- ->Event("GetAxisConstraint", &EditorLayerPythonRequestBus::Events::GetAxisConstraint)
- ->Event("SetAxisConstraint", &EditorLayerPythonRequestBus::Events::SetAxisConstraint)
- ->Event("GetPakFromFile", &EditorLayerPythonRequestBus::Events::GetPakFromFile)
- ->Event("Log", &EditorLayerPythonRequestBus::Events::Log)
- ->Event("Undo", &EditorLayerPythonRequestBus::Events::Undo)
- ->Event("Redo", &EditorLayerPythonRequestBus::Events::Redo)
- ->Event("DrawLabel", &EditorLayerPythonRequestBus::Events::DrawLabel)
- ->Event("ComboBox", &EditorLayerPythonRequestBus::Events::ComboBox)
- ;
- }
- }
- void PythonEditorComponent::Activate()
- {
- EditorLayerPythonRequestBus::Handler::BusConnect(GetEntityId());
- }
- void PythonEditorComponent::Deactivate()
- {
- EditorLayerPythonRequestBus::Handler::BusDisconnect();
- }
- const char* PythonEditorComponent::GetCVar(const char* pName)
- {
- return PyGetCVarAsString(pName);
- }
- void PythonEditorComponent::SetCVar(const char* pName, const AZStd::any& value)
- {
- return PySetCVarFromAny(pName, value);
- }
- void PythonEditorComponent::SetCVarFromString(const char* pName, const char* pValue)
- {
- return PySetCVarFromString(pName, pValue);
- }
- void PythonEditorComponent::SetCVarFromInteger(const char* pName, int pValue)
- {
- return PySetCVarFromInt(pName, pValue);
- }
- void PythonEditorComponent::SetCVarFromFloat(const char* pName, float pValue)
- {
- return PySetCVarFromFloat(pName, pValue);
- }
- void PythonEditorComponent::PyRunConsole(const char* text)
- {
- return ::PyRunConsole(text);
- }
- void PythonEditorComponent::EnterGameMode()
- {
- return PyEnterGameMode();
- }
- bool PythonEditorComponent::IsInGameMode()
- {
- return PyIsInGameMode();
- }
- void PythonEditorComponent::ExitGameMode()
- {
- return PyExitGameMode();
- }
- void PythonEditorComponent::EnterSimulationMode()
- {
- return PyEnterSimulationMode();
- }
- bool PythonEditorComponent::IsInSimulationMode()
- {
- return PyIsInSimulationMode();
- }
- void PythonEditorComponent::ExitSimulationMode()
- {
- return PyExitSimulationMode();
- }
- void PythonEditorComponent::RunFile(const char *pFile)
- {
- return PyRunFile(pFile);
- }
- void PythonEditorComponent::RunFileParameters(const char* pFile, const char* pArguments)
- {
- return PyRunFileWithParameters(pFile, pArguments);
- }
- void PythonEditorComponent::ExecuteCommand(const char* cmdline)
- {
- return PyExecuteCommand(cmdline);
- }
- bool PythonEditorComponent::MessageBoxOkCancel(const char* pMessage)
- {
- return PyMessageBox(pMessage);
- }
- bool PythonEditorComponent::MessageBoxYesNo(const char* pMessage)
- {
- return PyMessageBoxYesNo(pMessage);
- }
- bool PythonEditorComponent::MessageBoxOk(const char* pMessage)
- {
- return PyMessageBoxOK(pMessage);
- }
- AZStd::string PythonEditorComponent::EditBox(AZStd::string_view pTitle)
- {
- return PyEditBox(pTitle);
- }
- AZStd::any PythonEditorComponent::EditBoxCheckDataType(const char* pTitle)
- {
- return PyEditBoxAndCheckProperty(pTitle);
- }
- AZStd::string PythonEditorComponent::OpenFileBox()
- {
- return PyOpenFileBox();
- }
- const char* PythonEditorComponent::GetAxisConstraint()
- {
- return PyGetAxisConstraint();
- }
- void PythonEditorComponent::SetAxisConstraint(AZStd::string_view pConstrain)
- {
- return PySetAxisConstraint(pConstrain);
- }
- AZ::IO::Path PythonEditorComponent::GetPakFromFile(const char* filename)
- {
- return PyGetPakFromFile(filename);
- }
- void PythonEditorComponent::Log(const char* pMessage)
- {
- return PyLog(pMessage);
- }
- void PythonEditorComponent::Undo()
- {
- return PyUndo();
- }
- void PythonEditorComponent::Redo()
- {
- return PyRedo();
- }
- void PythonEditorComponent::DrawLabel(int x, int y, float size, float r, float g, float b, float a, const char* pLabel)
- {
- return PyDrawLabel(x, y, size, r, g, b, a, pLabel);
- }
- AZStd::string PythonEditorComponent::ComboBox(AZStd::string title, AZStd::vector<AZStd::string> values, int selectedIdx)
- {
- return PyComboBox(title, values, selectedIdx);
- }
- }
- namespace AzToolsFramework
- {
- void PythonEditorFuncsHandler::Reflect(AZ::ReflectContext* context)
- {
- if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
- {
- // this will put these methods into the 'azlmbr.legacy.general' module
- auto addLegacyGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
- {
- methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
- ->Attribute(AZ::Script::Attributes::Category, "Legacy/General")
- ->Attribute(AZ::Script::Attributes::Module, "legacy.general");
- };
- addLegacyGeneral(behaviorContext->Method("get_cvar", PyGetCVarAsString, nullptr, "Gets a CVar value as a string."));
- addLegacyGeneral(behaviorContext->Method("set_cvar", PySetCVarFromAny, nullptr, "Sets a CVar value from any simple value."));
- addLegacyGeneral(behaviorContext->Method("set_cvar_string", PySetCVarFromString, nullptr, "Sets a CVar value from a string."));
- addLegacyGeneral(behaviorContext->Method("set_cvar_integer", PySetCVarFromInt, nullptr, "Sets a CVar value from an integer."));
- addLegacyGeneral(behaviorContext->Method("set_cvar_float", PySetCVarFromFloat, nullptr, "Sets a CVar value from a float."));
- addLegacyGeneral(behaviorContext->Method("run_console", PyRunConsole, nullptr, "Runs a console command."));
- addLegacyGeneral(behaviorContext->Method("enter_game_mode", PyEnterGameMode, nullptr, "Enters the editor game mode."));
- addLegacyGeneral(behaviorContext->Method("enter_game_mode_fullscreen", PyEnterGameModeFullscreen, nullptr, "Enters the editor game mode in fullscreen."));
- addLegacyGeneral(behaviorContext->Method("is_in_game_mode", PyIsInGameMode, nullptr, "Queries if it's in the game mode or not."));
- addLegacyGeneral(behaviorContext->Method("exit_game_mode", PyExitGameMode, nullptr, "Exits the editor game mode."));
- addLegacyGeneral(behaviorContext->Method("enter_simulation_mode", PyEnterSimulationMode, nullptr, "Enters the editor AI/Physics simulation mode."));
- addLegacyGeneral(behaviorContext->Method("is_in_simulation_mode", PyIsInSimulationMode, nullptr, "Queries if the editor is currently in the AI/Physics simulation mode or not."));
- addLegacyGeneral(behaviorContext->Method("exit_simulation_mode", PyExitSimulationMode, nullptr, "Exits the editor AI/Physics simulation mode."));
- addLegacyGeneral(behaviorContext->Method("run_file", PyRunFile, nullptr, "Runs a script file. A relative path from the editor user folder or an absolute path should be given as an argument."));
- addLegacyGeneral(behaviorContext->Method("run_file_parameters", PyRunFileWithParameters, nullptr, "Runs a script file with parameters. A relative path from the editor user folder or an absolute path should be given as an argument. The arguments should be separated by whitespace."));
- addLegacyGeneral(behaviorContext->Method("execute_command", PyExecuteCommand, nullptr, "Executes a given string as an editor command."));
- addLegacyGeneral(behaviorContext->Method("message_box", PyMessageBox, nullptr, "Shows a confirmation message box with ok|cancel and shows a custom message."));
- addLegacyGeneral(behaviorContext->Method("message_box_yes_no", PyMessageBoxYesNo, nullptr, "Shows a confirmation message box with yes|no and shows a custom message."));
- addLegacyGeneral(behaviorContext->Method("message_box_ok", PyMessageBoxOK, nullptr, "Shows a confirmation message box with only ok and shows a custom message."));
- addLegacyGeneral(behaviorContext->Method("edit_box", PyEditBox, nullptr, "Shows an edit box and returns the value as string."));
- addLegacyGeneral(behaviorContext->Method("edit_box_check_data_type", PyEditBoxAndCheckProperty, nullptr, "Shows an edit box and checks the custom value to use the return value with other functions correctly."));
- addLegacyGeneral(behaviorContext->Method("open_file_box", PyOpenFileBox, nullptr, "Shows an open file box and returns the selected file path and name."));
- addLegacyGeneral(behaviorContext->Method("get_axis_constraint", PyGetAxisConstraint, nullptr, "Gets axis."));
- addLegacyGeneral(behaviorContext->Method("set_axis_constraint", PySetAxisConstraint, nullptr, "Sets axis."));
- addLegacyGeneral(behaviorContext->Method("get_pak_from_file", [](const char* filename) -> AZStd::string { return PyGetPakFromFile(filename).Native(); }, nullptr, "Finds a pak file name for a given file."));
- addLegacyGeneral(behaviorContext->Method("log", PyLog, nullptr, "Prints the message to the editor console window."));
- addLegacyGeneral(behaviorContext->Method("undo", PyUndo, nullptr, "Undoes the last operation."));
- addLegacyGeneral(behaviorContext->Method("redo", PyRedo, nullptr, "Redoes the last undone operation."));
- addLegacyGeneral(behaviorContext->Method("draw_label", PyDrawLabel, nullptr, "Shows a 2d label on the screen at the given position and given color."));
- addLegacyGeneral(behaviorContext->Method("combo_box", PyComboBox, nullptr, "Shows a combo box listing each value passed in, returns string value selected by the user."));
- addLegacyGeneral(behaviorContext->Method("crash", PyCrash, nullptr, "Crashes the application, useful for testing crash reporting and other automation tools."));
- /////////////////////////////////////////////////////////////////////////
- // Temporal, to be removed by LY-101149
- addLegacyGeneral(behaviorContext->Method("find_editor_entity", PyFindEditorEntity, nullptr, "Retrieves a editor entity id by name"));
- addLegacyGeneral(behaviorContext->Method("find_game_entity", PyFindGameEntity, nullptr, "Retrieves a game entity id by name"));
- //////////////////////////////////////////////////////////////////////////
- addLegacyGeneral(behaviorContext->Method("dump_exposed_classes", PyDumpBindings::GetExposedPythonClasses, nullptr, "Retrieves exposed classes"));
- }
- }
- }
|