123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519 |
- /*
- * 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"
- #ifdef WIN32
- AZ_PUSH_DISABLE_WARNING(4458, "-Wunknown-warning-option")
- #include <gdiplus.h>
- AZ_POP_DISABLE_WARNING
- #pragma comment (lib, "Gdiplus.lib")
- #include <WinUser.h> // needed for MessageBoxW in the assert handler
- #endif
- #include <array>
- #include <string>
- #include <iostream>
- #include <fstream>
- #include "CryEdit.h"
- // Qt
- #include <QCommandLineParser>
- #include <QSharedMemory>
- #include <QSystemSemaphore>
- #include <QDesktopServices>
- #include <QElapsedTimer>
- #include <QProcess>
- #include <QScopedValueRollback>
- #include <QClipboard>
- #include <QMenuBar>
- #include <QMessageBox>
- #include <QDialogButtonBox>
- #include <QUrlQuery>
- // AzCore
- #include <AzCore/Casting/numeric_cast.h>
- #include <AzCore/Component/ComponentApplicationBus.h>
- #include <AzCore/Component/ComponentApplicationLifecycle.h>
- #include <AzCore/Module/Environment.h>
- #include <AzCore/RTTI/BehaviorContext.h>
- #include <AzCore/std/smart_ptr/make_shared.h>
- #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
- #include <AzCore/StringFunc/StringFunc.h>
- #include <AzCore/Utils/Utils.h>
- #include <AzCore/Console/IConsole.h>
- #include <AzCore/EBus/IEventScheduler.h>
- #include <AzCore/Name/Name.h>
- #include <AzCore/IO/SystemFile.h>
- // AzFramework
- #include <AzFramework/Components/CameraBus.h>
- #include <AzFramework/Process/ProcessWatcher.h>
- #include <AzFramework/ProjectManager/ProjectManager.h>
- #include <AzFramework/Spawnable/RootSpawnableInterface.h>
- // AzToolsFramework
- #include <AzToolsFramework/ActionManager/ActionManagerSystemComponent.h>
- #include <AzToolsFramework/Component/EditorComponentAPIBus.h>
- #include <AzToolsFramework/Component/EditorLevelComponentAPIBus.h>
- #include <AzToolsFramework/Editor/ActionManagerUtils.h>
- #include <AzToolsFramework/UI/UICore/ProgressShield.hxx>
- #include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
- #include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
- #include <AzToolsFramework/API/EditorPythonConsoleBus.h>
- #include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
- #include <AzToolsFramework/API/ToolsApplicationAPI.h>
- #include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
- #include <AzToolsFramework/PythonTerminal/ScriptHelpDialog.h>
- #include <AzToolsFramework/Viewport/LocalViewBookmarkLoader.h>
- // AzQtComponents
- #include <AzQtComponents/Components/StyleManager.h>
- #include <AzQtComponents/Utilities/HandleDpiAwareness.h>
- #include <AzQtComponents/Components/WindowDecorationWrapper.h>
- #include <AzQtComponents/Utilities/QtPluginPaths.h>
- // CryCommon
- #include <CryCommon/ILevelSystem.h>
- // Editor
- #include "Settings.h"
- #include "GameResourcesExporter.h"
- #include "MainWindow.h"
- #include "Core/QtEditorApplication.h"
- #include "NewLevelDialog.h"
- #include "LayoutConfigDialog.h"
- #include "ViewManager.h"
- #include "FileTypeUtils.h"
- #include "PluginManager.h"
- #include "IEditorImpl.h"
- #include "StartupLogoDialog.h"
- #include "DisplaySettings.h"
- #include "GameEngine.h"
- #include "StartupTraceHandler.h"
- #include "ToolsConfigPage.h"
- #include "WaitProgress.h"
- #include "ToolBox.h"
- #include "EditorPreferencesDialog.h"
- #include "AnimationContext.h"
- #include "GotoPositionDlg.h"
- #include "ConsoleDialog.h"
- #include "Controls/ConsoleSCB.h"
- #include "ScopedVariableSetter.h"
- #include "Util/3DConnexionDriver.h"
- #include "Util/AutoDirectoryRestoreFileDialog.h"
- #include "Util/EditorAutoLevelLoadTest.h"
- #include <AzToolsFramework/PythonTerminal/ScriptHelpDialog.h>
- #include "LevelFileDialog.h"
- #include "LevelIndependentFileMan.h"
- #include "WelcomeScreen/WelcomeScreenDialog.h"
- #include "Controls/ReflectedPropertyControl/PropertyCtrl.h"
- #include "Controls/ReflectedPropertyControl/ReflectedVar.h"
- #include "EditorToolsApplication.h"
- #include <AzToolsFramework/Undo/UndoSystem.h>
- #if defined(AZ_PLATFORM_WINDOWS)
- #include <AzFramework/API/ApplicationAPI_Platform.h>
- #endif
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- #include "WindowObserver_mac.h"
- #endif
- #include <AzCore/RTTI/BehaviorContext.h>
- #include <AzFramework/Render/Intersector.h>
- #include <AzCore/std/smart_ptr/make_shared.h>
- static const char O3DEEditorClassName[] = "O3DEEditorClass";
- static const char O3DEApplicationName[] = "O3DEApplication";
- static AZ::EnvironmentVariable<bool> inEditorBatchMode = nullptr;
- namespace Platform
- {
- bool OpenUri(const QUrl& uri);
- }
- RecentFileList::RecentFileList()
- {
- m_settings.beginGroup(QStringLiteral("Application"));
- m_settings.beginGroup(QStringLiteral("Recent File List"));
- ReadList();
- }
- void RecentFileList::Remove(int index)
- {
- m_arrNames.removeAt(index);
- }
- void RecentFileList::Add(const QString& f)
- {
- QString filename = QDir::toNativeSeparators(f);
- m_arrNames.removeAll(filename);
- m_arrNames.push_front(filename);
- while (m_arrNames.count() > Max)
- {
- m_arrNames.removeAt(Max);
- }
- }
- int RecentFileList::GetSize()
- {
- return m_arrNames.count();
- }
- void RecentFileList::GetDisplayName(QString& name, int index, const QString& curDir)
- {
- name = m_arrNames[index];
- const QDir cur(curDir);
- QDir fileDir(name); // actually pointing at file, first cdUp() gets us the parent dir
- while (fileDir.cdUp())
- {
- if (fileDir == cur)
- {
- name = cur.relativeFilePath(name);
- break;
- }
- }
- name = QDir::toNativeSeparators(name);
- }
- QString& RecentFileList::operator[](int index)
- {
- return m_arrNames[index];
- }
- void RecentFileList::ReadList()
- {
- m_arrNames.clear();
- for (int i = 1; i <= Max; ++i)
- {
- QString f = m_settings.value(QStringLiteral("File%1").arg(i)).toString();
- if (!f.isEmpty())
- {
- m_arrNames.push_back(f);
- }
- }
- }
- void RecentFileList::WriteList()
- {
- m_settings.remove(QString());
- int i = 1;
- for (auto f : m_arrNames)
- {
- m_settings.setValue(QStringLiteral("File%1").arg(i++), f);
- }
- }
- #define ERROR_LEN 256
- CCryDocManager::CCryDocManager()
- {
- }
- CCrySingleDocTemplate* CCryDocManager::SetDefaultTemplate(CCrySingleDocTemplate* pNew)
- {
- CCrySingleDocTemplate* pOld = m_pDefTemplate;
- m_pDefTemplate = pNew;
- m_templateList.clear();
- m_templateList.push_back(m_pDefTemplate);
- return pOld;
- }
- // Copied from MFC to get rid of the silly ugly unoverridable doc-type pick dialog
- void CCryDocManager::OnFileNew()
- {
- assert(m_pDefTemplate != nullptr);
- m_pDefTemplate->OpenDocumentFile(nullptr);
- // if returns NULL, the user has already been alerted
- }
- bool CCryDocManager::DoPromptFileName(QString& fileName, [[maybe_unused]] UINT nIDSTitle,
- [[maybe_unused]] DWORD lFlags, bool bOpenFileDialog, [[maybe_unused]] CDocTemplate* pTemplate)
- {
- CLevelFileDialog levelFileDialog(bOpenFileDialog);
- levelFileDialog.show();
- levelFileDialog.adjustSize();
- if (levelFileDialog.exec() == QDialog::Accepted)
- {
- fileName = levelFileDialog.GetFileName();
- return true;
- }
- return false;
- }
- CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions)
- {
- assert(filename != nullptr);
- const bool reopenIfSame = openSameLevelOptions == COpenSameLevelOptions::ReopenLevelIfSame;
- // find the highest confidence
- auto pos = m_templateList.begin();
- CCrySingleDocTemplate::Confidence bestMatch = CCrySingleDocTemplate::noAttempt;
- CCrySingleDocTemplate* pBestTemplate = nullptr;
- CCryEditDoc* pOpenDocument = nullptr;
- if (filename[0] == '\"')
- {
- ++filename;
- }
- QString szPath = QString::fromUtf8(filename);
- if (szPath.endsWith('"'))
- {
- szPath.remove(szPath.length() - 1, 1);
- }
- while (pos != m_templateList.end())
- {
- auto pTemplate = *(pos++);
- CCrySingleDocTemplate::Confidence match;
- assert(pOpenDocument == nullptr);
- match = pTemplate->MatchDocType(szPath.toUtf8().data(), pOpenDocument);
- if (match > bestMatch)
- {
- bestMatch = match;
- pBestTemplate = pTemplate;
- }
- if (match == CCrySingleDocTemplate::yesAlreadyOpen)
- {
- break; // stop here
- }
- }
- if (!reopenIfSame && pOpenDocument != nullptr)
- {
- return pOpenDocument;
- }
- if (pBestTemplate == nullptr)
- {
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QString(), QObject::tr("Failed to open document."));
- return nullptr;
- }
- return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), addToMostRecentFileList, false);
- }
- //////////////////////////////////////////////////////////////////////////////
- // CCryEditApp
- #undef ON_COMMAND
- #define ON_COMMAND(id, method) \
- MainWindow::instance()->GetActionManager()->RegisterActionHandler(id, this, &CCryEditApp::method);
- #undef ON_COMMAND_RANGE
- #define ON_COMMAND_RANGE(idStart, idEnd, method) \
- for (int i = idStart; i <= idEnd; ++i) \
- ON_COMMAND(i, method);
- AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
- CCryEditApp* CCryEditApp::s_currentInstance = nullptr;
- /////////////////////////////////////////////////////////////////////////////
- // CCryEditApp construction
- CCryEditApp::CCryEditApp()
- {
- s_currentInstance = this;
- m_sPreviewFile[0] = 0;
- AzFramework::AssetSystemInfoBus::Handler::BusConnect();
- AzFramework::AssetSystemStatusBus::Handler::BusConnect();
- m_disableIdleProcessingCounter = 0;
- EditorIdleProcessingBus::Handler::BusConnect();
- }
- //////////////////////////////////////////////////////////////////////////
- CCryEditApp::~CCryEditApp()
- {
- EditorIdleProcessingBus::Handler::BusDisconnect();
- AzFramework::AssetSystemStatusBus::Handler::BusDisconnect();
- AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
- s_currentInstance = nullptr;
- }
- CCryEditApp* CCryEditApp::instance()
- {
- return s_currentInstance;
- }
- class CEditCommandLineInfo
- {
- public:
- bool m_bTest = false;
- bool m_bAutoLoadLevel = false;
- bool m_bExport = false;
- bool m_bExportTexture = false;
- bool m_bConsoleMode = false;
- bool m_bNullRenderer = false;
- bool m_bDeveloperMode = false;
- bool m_bRunPythonScript = false;
- bool m_bRunPythonTestScript = false;
- bool m_bShowVersionInfo = false;
- QString m_exportFile;
- QString m_strFileName;
- QString m_appRoot;
- QString m_logFile;
- QString m_pythonArgs;
- QString m_pythonTestCase;
- QString m_execFile;
- QString m_execLineCmd;
- bool m_bSkipWelcomeScreenDialog = false;
- bool m_bAutotestMode = false;
- struct CommandLineStringOption
- {
- QString name;
- QString description;
- QString valueName;
- };
- CEditCommandLineInfo()
- {
- bool dummy;
- QCommandLineParser parser;
- parser.addHelpOption();
- parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
- parser.setApplicationDescription(QObject::tr("O3DE Editor"));
- // nsDocumentRevisionDebugMode is an argument that the macOS system passed into an App bundle that is being debugged.
- // Need to include it here so that Qt argument parser does not error out.
- bool nsDocumentRevisionsDebugMode = false;
- const std::vector<std::pair<QString, bool&> > options = {
- { "export", m_bExport },
- { "exportTexture", m_bExportTexture },
- { "test", m_bTest },
- { "auto_level_load", m_bAutoLoadLevel },
- { "BatchMode", m_bConsoleMode },
- { "NullRenderer", m_bNullRenderer },
- { "devmode", m_bDeveloperMode },
- { "runpython", m_bRunPythonScript },
- { "runpythontest", m_bRunPythonTestScript },
- { "version", m_bShowVersionInfo },
- { "NSDocumentRevisionsDebugMode", nsDocumentRevisionsDebugMode},
- { "skipWelcomeScreenDialog", m_bSkipWelcomeScreenDialog},
- { "autotest_mode", m_bAutotestMode},
- { "regdumpall", dummy },
- { "attach-debugger", dummy }, // Attaches a debugger for the current application
- { "wait-for-debugger", dummy }, // Waits until a debugger is attached to the current application
- };
- QString dummyString;
- const std::vector<std::pair<CommandLineStringOption, QString&> > stringOptions = {
- {{"logfile", "File name of the log file to write out to.", "logfile"}, m_logFile},
- {{"runpythonargs", "Command-line argument string to pass to the python script if --runpython or --runpythontest was used.", "runpythonargs"}, m_pythonArgs},
- {{"pythontestcase", "Test case name of python test script if --runpythontest was used.", "pythontestcase"}, m_pythonTestCase},
- {{"exec", "cfg file to run on startup, used for systems like automation", "exec"}, m_execFile},
- {{"rhi", "Command-line argument to force which rhi to use", "rhi"}, dummyString },
- {{"rhi-device-validation", "Command-line argument to configure rhi validation", "rhi-device-validation"}, dummyString },
- {{"exec_line", "command to run on startup, used for systems like automation", "exec_line"}, m_execLineCmd},
- {{"regset", "Command-line argument to override settings registry values", "regset"}, dummyString},
- {{"regremove", "Deletes a value within the global settings registry at the JSON pointer path @key", "regremove"}, dummyString},
- {{"regdump", "Sets a value within the global settings registry at the JSON pointer path @key with value of @value", "regdump"}, dummyString},
- {{"project-path", "Supplies the path to the project that the Editor should use", "project-path"}, dummyString},
- {{"engine-path", "Supplies the path to the engine", "engine-path"}, dummyString},
- {{"project-cache-path", "Path to the project cache", "project-cache-path"}, dummyString},
- {{"project-user-path", "Path to the project user path", "project-user-path"}, dummyString},
- {{"project-log-path", "Path to the project log path", "project-log-path"}, dummyString}
- // add dummy entries here to prevent QCommandLineParser error-ing out on cmd line args that will be parsed later
- };
- parser.addPositionalArgument("file", QCoreApplication::translate("main", "file to open"));
- for (const auto& option : options)
- {
- parser.addOption(QCommandLineOption(option.first));
- }
- for (const auto& option : stringOptions)
- {
- parser.addOption(QCommandLineOption(option.first.name, option.first.description, option.first.valueName));
- }
- QStringList args = qApp->arguments();
- #ifdef Q_OS_WIN32
- for (QString& arg : args)
- {
- if (!arg.isEmpty() && arg[0] == '/')
- {
- arg[0] = '-'; // QCommandLineParser only supports - and -- prefixes
- }
- }
- #endif
- if (!parser.parse(args))
- {
- AZ_TracePrintf("QT CommandLine Parser", "QT command line parsing warned with message %s."
- " Has the QCommandLineParser had these options added to it", parser.errorText().toUtf8().constData());
- }
- // Get boolean options
- const int numOptions = static_cast<int>(options.size());
- for (int i = 0; i < numOptions; ++i)
- {
- options[i].second = parser.isSet(options[i].first);
- }
- // Get string options
- for (auto& option : stringOptions)
- {
- option.second = parser.value(option.first.valueName);
- }
- m_bExport = m_bExport || m_bExportTexture;
- const QStringList positionalArgs = parser.positionalArguments();
- if (!positionalArgs.isEmpty())
- {
- m_strFileName = positionalArgs.first();
- if (positionalArgs.first().at(0) != '[')
- {
- m_exportFile = positionalArgs.first();
- }
- }
- }
- };
- struct SharedData
- {
- bool raise = false;
- char text[_MAX_PATH];
- };
- /////////////////////////////////////////////////////////////////////////////
- // CTheApp::FirstInstance
- // FirstInstance checks for an existing instance of the application.
- // If one is found, it is activated.
- //
- // This function uses a technique similar to that described in KB
- // article Q141752 to locate the previous instance of the application. .
- bool CCryEditApp::FirstInstance(bool bForceNewInstance)
- {
- QSystemSemaphore sem(QString(O3DEApplicationName) + "_sem", 1);
- sem.acquire();
- {
- FixDanglingSharedMemory(O3DEEditorClassName);
- }
- sem.release();
- m_mutexApplication = new QSharedMemory(O3DEEditorClassName);
- if (!m_mutexApplication->create(sizeof(SharedData)) && !bForceNewInstance)
- {
- m_mutexApplication->attach();
- // another instance is already running - activate it
- sem.acquire();
- SharedData* data = reinterpret_cast<SharedData*>(m_mutexApplication->data());
- data->raise = true;
- if (m_bPreviewMode)
- {
- // IF in preview mode send this window copy data message to load new preview file.
- azstrcpy(data->text, MAX_PATH, m_sPreviewFile);
- }
- return false;
- }
- else
- {
- m_mutexApplication->attach();
- // this is the first instance
- sem.acquire();
- ::memset(m_mutexApplication->data(), 0, m_mutexApplication->size());
- sem.release();
- QTimer* t = new QTimer(this);
- connect(t, &QTimer::timeout, this, [this]() {
- QSystemSemaphore sem(QString(O3DEApplicationName) + "_sem", 1);
- sem.acquire();
- SharedData* data = reinterpret_cast<SharedData*>(m_mutexApplication->data());
- QString preview = QString::fromLatin1(data->text);
- if (data->raise)
- {
- QWidget* w = MainWindow::instance();
- w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
- w->raise();
- w->activateWindow();
- data->raise = false;
- }
- if (!preview.isEmpty())
- {
- // Load this file
- LoadFile(preview);
- data->text[0] = 0;
- }
- sem.release();
- });
- t->start(1000);
- return true;
- }
- return true;
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnFileSave()
- {
- if (m_savingLevel)
- {
- return;
- }
- const QScopedValueRollback<bool> rollback(m_savingLevel, true);
- auto* prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
- AZ_Assert(prefabIntegrationInterface != nullptr, "PrefabIntegrationInterface is not found.");
- prefabIntegrationInterface->SaveCurrentPrefab();
- // when attempting to save, update the last known location using the active camera transform
- AzToolsFramework::StoreViewBookmarkLastKnownLocationFromActiveCamera();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnUpdateDocumentReady(QAction* action)
- {
- action->setEnabled(GetIEditor()
- && GetIEditor()->GetDocument()
- && GetIEditor()->GetDocument()->IsDocumentReady()
- && !m_bIsExportingLegacyData
- && !m_creatingNewLevel
- && !m_openingLevel
- && !m_savingLevel);
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnUpdateFileOpen(QAction* action)
- {
- action->setEnabled(!m_bIsExportingLegacyData && !m_creatingNewLevel && !m_openingLevel && !m_savingLevel);
- }
- bool CCryEditApp::ShowEnableDisableGemDialog(const QString& title, const QString& message)
- {
- const QString informativeMessage = QObject::tr("Please follow the instructions <a href=\"https://www.o3de.org/docs/user-guide/project-config/add-remove-gems/\">here</a>, after which the Editor will be re-launched automatically.");
- QMessageBox box(AzToolsFramework::GetActiveWindow());
- box.addButton(QObject::tr("Continue"), QMessageBox::AcceptRole);
- box.addButton(QObject::tr("Back"), QMessageBox::RejectRole);
- box.setWindowTitle(title);
- box.setText(message);
- box.setInformativeText(informativeMessage);
- box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
- if (box.exec() == QMessageBox::AcceptRole)
- {
- // Called from a modal dialog with the main window as its parent. Best not to close the main window while the dialog is still active.
- QTimer::singleShot(0, MainWindow::instance(), &MainWindow::close);
- return true;
- }
- return false;
- }
- QString CCryEditApp::ShowWelcomeDialog()
- {
- WelcomeScreenDialog wsDlg(MainWindow::instance());
- wsDlg.SetRecentFileList(GetRecentFileList());
- wsDlg.exec();
- QString levelName = wsDlg.GetLevelPath();
- return levelName;
- }
- //////////////////////////////////////////////////////////////////////////
- // Needed to work with custom memory manager.
- //////////////////////////////////////////////////////////////////////////
- CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool bMakeVisible /*= true*/)
- {
- return OpenDocumentFile(lpszPathName, true, bMakeVisible);
- }
- CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, [[maybe_unused]] bool bMakeVisible)
- {
- CCryEditDoc* pCurDoc = GetIEditor()->GetDocument();
- if (pCurDoc)
- {
- if (!pCurDoc->SaveModified())
- {
- return nullptr;
- }
- }
- if (!pCurDoc)
- {
- pCurDoc = qobject_cast<CCryEditDoc*>(m_documentClass->newInstance());
- if (pCurDoc == nullptr)
- return nullptr;
- pCurDoc->setParent(this);
- }
- pCurDoc->SetModifiedFlag(false);
- if (lpszPathName == nullptr)
- {
- pCurDoc->SetTitle(tr("Untitled"));
- pCurDoc->OnNewDocument();
- }
- else
- {
- pCurDoc->OnOpenDocument(lpszPathName);
- pCurDoc->SetPathName(lpszPathName);
- if (addToMostRecentFileList)
- {
- CCryEditApp::instance()->AddToRecentFileList(lpszPathName);
- }
- }
- return pCurDoc;
- }
- CCrySingleDocTemplate::Confidence CCrySingleDocTemplate::MatchDocType(const char* lpszPathName, CCryEditDoc*& rpDocMatch)
- {
- assert(lpszPathName != nullptr);
- rpDocMatch = nullptr;
- // go through all documents
- CCryEditDoc* pDoc = GetIEditor()->GetDocument();
- if (pDoc)
- {
- QString prevPathName = pDoc->GetLevelPathName();
- // all we need to know here is whether it is the same file as before.
- if (!prevPathName.isEmpty())
- {
- // QFileInfo is guaranteed to return true iff the two paths refer to the same path.
- if (QFileInfo(prevPathName) == QFileInfo(QString::fromUtf8(lpszPathName)))
- {
- // already open
- rpDocMatch = pDoc;
- return yesAlreadyOpen;
- }
- }
- }
- // see if it matches our default suffix
- const QString strFilterExt = EditorUtils::LevelFile::GetDefaultFileExtension();
- const QString strOldFilterExt = EditorUtils::LevelFile::GetOldCryFileExtension();
- // see if extension matches
- assert(strFilterExt[0] == '.');
- QString strDot = "." + Path::GetExt(lpszPathName);
- if (!strDot.isEmpty())
- {
- if(strDot == strFilterExt || strDot == strOldFilterExt)
- {
- return yesAttemptNative; // extension matches, looks like ours
- }
- }
- // otherwise we will guess it may work
- return yesAttemptForeign;
- }
- /////////////////////////////////////////////////////////////////////////////
- namespace
- {
- AZStd::mutex g_splashScreenStateLock;
- enum ESplashScreenState
- {
- eSplashScreenState_Init, eSplashScreenState_Started, eSplashScreenState_Destroy
- };
- ESplashScreenState g_splashScreenState = eSplashScreenState_Init;
- IInitializeUIInfo* g_pInitializeUIInfo = nullptr;
- QWidget* g_splashScreen = nullptr;
- }
- QString FormatVersion([[maybe_unused]] const SFileVersion& v)
- {
- if (QObject::tr("%1").arg(O3DE_BUILD_VERSION) == "0")
- {
- return QObject::tr("Development Build");
- }
- return QObject::tr("Version %1").arg(O3DE_BUILD_VERSION);
- }
- QString FormatRichTextCopyrightNotice()
- {
- // copyright symbol is HTML Entity = ©
- QString copyrightHtmlSymbol = "©";
- QString copyrightString = QObject::tr("Copyright %1 Contributors to the Open 3D Engine Project");
- return copyrightString.arg(copyrightHtmlSymbol);
- }
- void CCryEditApp::AssetSystemWaiting()
- {
- QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
- }
- /////////////////////////////////////////////////////////////////////////////
- void CCryEditApp::ShowSplashScreen(CCryEditApp* app)
- {
- g_splashScreenStateLock.lock();
- CStartupLogoDialog* splashScreen = new CStartupLogoDialog(CStartupLogoDialog::Loading, FormatVersion(app->m_pEditor->GetFileVersion()), FormatRichTextCopyrightNotice());
- g_pInitializeUIInfo = splashScreen;
- g_splashScreen = splashScreen;
- g_splashScreenState = eSplashScreenState_Started;
- g_splashScreenStateLock.unlock();
- splashScreen->show();
- QObject::connect(splashScreen, &QObject::destroyed, splashScreen, [=]
- {
- AZStd::scoped_lock lock(g_splashScreenStateLock);
- g_pInitializeUIInfo = nullptr;
- g_splashScreen = nullptr;
- });
- }
- /////////////////////////////////////////////////////////////////////////////
- void CCryEditApp::CreateSplashScreen()
- {
- if (!m_bConsoleMode && !IsInAutotestMode())
- {
- // Create startup output splash
- ShowSplashScreen(this);
- GetIEditor()->Notify(eNotify_OnSplashScreenCreated);
- }
- else
- {
- // Create console
- m_pConsoleDialog = new CConsoleDialog();
- m_pConsoleDialog->show();
- g_pInitializeUIInfo = m_pConsoleDialog;
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- void CCryEditApp::CloseSplashScreen()
- {
- if (CStartupLogoDialog::instance())
- {
- delete CStartupLogoDialog::instance();
- g_splashScreenStateLock.lock();
- g_splashScreenState = eSplashScreenState_Destroy;
- g_splashScreenStateLock.unlock();
- }
- GetIEditor()->Notify(eNotify_OnSplashScreenDestroyed);
- }
- /////////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OutputStartupMessage(QString str)
- {
- g_splashScreenStateLock.lock();
- if (g_pInitializeUIInfo)
- {
- g_pInitializeUIInfo->SetInfoText(str.toUtf8().data());
- }
- g_splashScreenStateLock.unlock();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::InitFromCommandLine(CEditCommandLineInfo& cmdInfo)
- {
- m_bConsoleMode |= cmdInfo.m_bConsoleMode;
- inEditorBatchMode = AZ::Environment::CreateVariable<bool>("InEditorBatchMode", m_bConsoleMode);
- m_bTestMode |= cmdInfo.m_bTest;
- m_bSkipWelcomeScreenDialog = cmdInfo.m_bSkipWelcomeScreenDialog || !cmdInfo.m_execFile.isEmpty() || !cmdInfo.m_execLineCmd.isEmpty() || cmdInfo.m_bAutotestMode;
- m_bExportMode = cmdInfo.m_bExport;
- m_bRunPythonTestScript = cmdInfo.m_bRunPythonTestScript;
- m_bRunPythonScript = cmdInfo.m_bRunPythonScript || cmdInfo.m_bRunPythonTestScript;
- m_execFile = cmdInfo.m_execFile;
- m_execLineCmd = cmdInfo.m_execLineCmd;
- m_bAutotestMode = cmdInfo.m_bAutotestMode || cmdInfo.m_bConsoleMode;
- if (m_bExportMode)
- {
- m_exportFile = cmdInfo.m_exportFile;
- }
- // Do we have a passed filename ?
- if (!cmdInfo.m_strFileName.isEmpty())
- {
- if (!m_bRunPythonScript && IsPreviewableFileType(cmdInfo.m_strFileName.toUtf8().constData()))
- {
- m_bPreviewMode = true;
- azstrncpy(m_sPreviewFile, _MAX_PATH, cmdInfo.m_strFileName.toUtf8().constData(), _MAX_PATH);
- }
- }
- if (cmdInfo.m_bAutoLoadLevel)
- {
- m_bLevelLoadTestMode = true;
- gEnv->bNoAssertDialog = true;
- CEditorAutoLevelLoadTest::Instance();
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- AZ::Outcome<void, AZStd::string> CCryEditApp::InitGameSystem(HWND hwndForInputSystem)
- {
- CGameEngine* pGameEngine = new CGameEngine;
- AZ::Outcome<void, AZStd::string> initOutcome = pGameEngine->Init(m_bPreviewMode, m_bTestMode, qApp->arguments().join(" ").toUtf8().data(), g_pInitializeUIInfo, hwndForInputSystem);
- if (!initOutcome.IsSuccess())
- {
- return initOutcome;
- }
- AZ_Assert(pGameEngine, "Game engine initialization failed, but initOutcome returned success.");
- m_pEditor->SetGameEngine(pGameEngine);
- // needs to be called after CrySystem has been loaded.
- gSettings.LoadDefaultGamePaths();
- return AZ::Success();
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CCryEditApp::CheckIfAlreadyRunning()
- {
- bool bForceNewInstance = false;
- if (!m_bPreviewMode)
- {
- FixDanglingSharedMemory(O3DEApplicationName);
- m_mutexApplication = new QSharedMemory(O3DEApplicationName);
- if (!m_mutexApplication->create(16))
- {
- // Don't prompt the user in non-interactive export mode. Instead, default to allowing multiple instances to
- // run simultaneously, so that multiple level exports can be run in parallel on the same machine.
- // NOTE: If you choose to do this, be sure to export *different* levels, since nothing prevents multiple runs
- // from trying to write to the same level at the same time.
- // If we're running interactively, let's ask and make sure the user actually intended to do this.
- if (!m_bExportMode && QMessageBox::question(AzToolsFramework::GetActiveWindow(), QObject::tr("Too many apps"), QObject::tr("There is already an Open 3D Engine application running\nDo you want to start another one?")) != QMessageBox::Yes)
- {
- return false;
- }
- bForceNewInstance = true;
- }
- }
- if (!FirstInstance(bForceNewInstance))
- {
- return false;
- }
- return true;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CCryEditApp::InitGame()
- {
- if (!m_bPreviewMode)
- {
- AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
- Log((QString("project_path = %1").arg(!projectPath.empty() ? projectPath.c_str() : "<not set>")).toUtf8().data());
- ICVar* pVar = gEnv->pConsole->GetCVar("sys_localization_folder");
- const char* sLocalizationFolder = pVar ? pVar->GetString() : nullptr;
- Log((QString("sys_localization_folder = ") + (sLocalizationFolder && sLocalizationFolder[0] ? sLocalizationFolder : "<not set>")).toUtf8().data());
- OutputStartupMessage("Starting Game...");
- if (!GetIEditor()->GetGameEngine()->InitGame(nullptr))
- {
- return false;
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Apply settings post engine initialization.
- GetIEditor()->GetDisplaySettings()->PostInitApply();
- gSettings.PostInitApply();
- return true;
- }
- /////////////////////////////////////////////////////////////////////////////
- void CCryEditApp::InitPlugins()
- {
- OutputStartupMessage("Loading Plugins...");
- // Load the plugins
- {
- GetIEditor()->LoadPlugins();
- #if defined(AZ_PLATFORM_WINDOWS)
- C3DConnexionDriver* p3DConnexionDriver = new C3DConnexionDriver;
- GetIEditor()->GetPluginManager()->RegisterPlugin(0, p3DConnexionDriver);
- #endif
- }
- }
- ////////////////////////////////////////////////////////////////////////////
- // Be careful when calling this function: it should be called after
- // everything else has finished initializing, otherwise, certain things
- // aren't set up yet. If in doubt, wrap it in a QTimer::singleShot(0ms);
- void CCryEditApp::InitLevel(const CEditCommandLineInfo& cmdInfo)
- {
- const char* defaultExtension = EditorUtils::LevelFile::GetDefaultFileExtension();
- const char* oldExtension = EditorUtils::LevelFile::GetOldCryFileExtension();
- if (m_bPreviewMode)
- {
- // Load geometry object.
- if (!cmdInfo.m_strFileName.isEmpty())
- {
- LoadFile(cmdInfo.m_strFileName);
- }
- }
- else if (m_bExportMode && !m_exportFile.isEmpty())
- {
- GetIEditor()->SetModifiedFlag(false);
- GetIEditor()->SetModifiedModule(eModifiedNothing);
- auto pDocument = OpenDocumentFile(m_exportFile.toUtf8().constData());
- if (pDocument)
- {
- GetIEditor()->SetModifiedFlag(false);
- GetIEditor()->SetModifiedModule(eModifiedNothing);
- ExportLevel(cmdInfo.m_bExport, cmdInfo.m_bExportTexture, true);
- // Terminate process.
- CLogFile::WriteLine("Editor: Terminate Process after export");
- }
- // the call to quit() must be posted to the event queue because the app is currently not yet running.
- // if we were to call quit() right now directly, the app would ignore it.
- QTimer::singleShot(0, QCoreApplication::instance(), &QCoreApplication::quit);
- return;
- }
- else if ((cmdInfo.m_strFileName.endsWith(defaultExtension, Qt::CaseInsensitive))
- || (cmdInfo.m_strFileName.endsWith(oldExtension, Qt::CaseInsensitive)))
- {
- auto pDocument = OpenDocumentFile(cmdInfo.m_strFileName.toUtf8().constData());
- if (pDocument)
- {
- GetIEditor()->SetModifiedFlag(false);
- GetIEditor()->SetModifiedModule(eModifiedNothing);
- }
- }
- else
- {
- //////////////////////////////////////////////////////////////////////////
- //It can happen that if you are switching between projects and you have auto load set that
- //you could inadvertently load the wrong project and not know it, you would think you are editing
- //one level when in fact you are editing the old one. This can happen if both projects have the same
- //relative path... which is often the case when branching.
- // Ex. D:\cryengine\dev\ gets branched to D:\cryengine\branch\dev
- // Now you have gamesdk in both roots and therefore GameSDK\Levels\Singleplayer\Forest in both
- // If you execute the branch the m_pRecentFileList will be an absolute path to the old gamesdk,
- // then if auto load is set simply takes the old level and loads it in the new branch...
- //I would question ever trying to load a level not in our gamesdk, what happens when there are things that
- //an not exist in the level when built in a different gamesdk.. does it erase them, most likely, then you
- //just screwed up the level for everyone in the other gamesdk...
- //So if we are auto loading a level outside our current gamesdk we should act as though the flag
- //was unset and pop the dialog which should be in the correct location. This is not fool proof, but at
- //least this its a compromise that doesn't automatically do something you probably shouldn't.
- bool autoloadLastLevel = gSettings.bAutoloadLastLevelAtStartup;
- if (autoloadLastLevel
- && GetRecentFileList()
- && GetRecentFileList()->GetSize())
- {
- QString gamePath = Path::GetEditingGameDataFolder().c_str();
- Path::ConvertSlashToBackSlash(gamePath);
- gamePath = Path::ToUnixPath(gamePath.toLower());
- gamePath = Path::AddSlash(gamePath);
- QString fullPath = GetRecentFileList()->m_arrNames[0];
- Path::ConvertSlashToBackSlash(fullPath);
- fullPath = Path::ToUnixPath(fullPath.toLower());
- fullPath = Path::AddSlash(fullPath);
- if (fullPath.indexOf(gamePath, 0) != 0)
- {
- autoloadLastLevel = false;
- }
- }
- //////////////////////////////////////////////////////////////////////////
- QString levelName;
- bool isLevelNameValid = false;
- bool doLevelNeedLoading = true;
- const bool runningPythonScript = cmdInfo.m_bRunPythonScript || cmdInfo.m_bRunPythonTestScript;
- AZ::EBusLogicalResult<bool, AZStd::logical_or<bool> > skipStartupUIProcess(false);
- AzToolsFramework::EditorEvents::Bus::BroadcastResult(
- skipStartupUIProcess, &AzToolsFramework::EditorEvents::Bus::Events::SkipEditorStartupUI);
- if (!skipStartupUIProcess.value)
- {
- do
- {
- isLevelNameValid = false;
- doLevelNeedLoading = true;
- if (gSettings.bShowDashboardAtStartup
- && !runningPythonScript
- && !m_bConsoleMode
- && !m_bSkipWelcomeScreenDialog
- && !m_bPreviewMode
- && !autoloadLastLevel)
- {
- levelName = ShowWelcomeDialog();
- }
- else if (autoloadLastLevel
- && GetRecentFileList()
- && GetRecentFileList()->GetSize())
- {
- levelName = GetRecentFileList()->m_arrNames[0];
- }
- if (levelName.isEmpty())
- {
- break;
- }
- if (levelName == "new")
- {
- //implies that the user has clicked the create new level option
- bool wasCreateLevelOperationCancelled = false;
- bool isNewLevelCreationSuccess = false;
- // This will show the new level dialog until a valid input has been entered by the user or until the user click cancel
- while (!isNewLevelCreationSuccess && !wasCreateLevelOperationCancelled)
- {
- isNewLevelCreationSuccess = CreateLevel(wasCreateLevelOperationCancelled);
- if (isNewLevelCreationSuccess == true)
- {
- doLevelNeedLoading = false;
- isLevelNameValid = true;
- }
- }
- ;
- }
- else
- {
- //implies that the user wants to open an existing level
- doLevelNeedLoading = true;
- isLevelNameValid = true;
- }
- } while (!isLevelNameValid);// if we reach here and levelName is not valid ,it implies that the user has clicked cancel on the create new level dialog
- // load level
- if (doLevelNeedLoading && !levelName.isEmpty())
- {
- if (!CFileUtil::Exists(levelName, false))
- {
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QObject::tr("Missing level"), QObject::tr("Failed to auto-load last opened level. Level file does not exist:\n\n%1").arg(levelName));
- return;
- }
- QString str;
- str = tr("Loading level %1 ...").arg(levelName);
- OutputStartupMessage(str);
- OpenDocumentFile(levelName.toUtf8().data());
- }
- }
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CCryEditApp::InitConsole()
- {
- // Execute command from cmdline -exec_line if applicable
- if (!m_execLineCmd.isEmpty())
- {
- gEnv->pConsole->ExecuteString(QString("%1").arg(m_execLineCmd).toLocal8Bit());
- }
- // Execute cfg from cmdline -exec if applicable
- if (!m_execFile.isEmpty())
- {
- gEnv->pConsole->ExecuteString(QString("exec %1").arg(m_execFile).toLocal8Bit());
- }
- // Execute special configs.
- gEnv->pConsole->ExecuteString("exec editor_autoexec.cfg");
- gEnv->pConsole->ExecuteString("exec editor.cfg");
- gEnv->pConsole->ExecuteString("exec user.cfg");
- GetISystem()->ExecuteCommandLine();
- return true;
- }
- /////////////////////////////////////////////////////////////////////////////
- void CCryEditApp::CompileCriticalAssets() const
- {
- // regardless of what is set in the bootstrap wait for AP to be ready
- // wait a maximum of 100 milliseconds and pump the system event loop until empty
- struct AssetsInQueueNotification
- : public AzFramework::AssetSystemInfoBus::Handler
- {
- void CountOfAssetsInQueue(const int& count) override
- {
- CCryEditApp::OutputStartupMessage(QString("Asset Processor working... %1 jobs remaining.").arg(count));
- }
- };
- AssetsInQueueNotification assetsInQueueNotifcation;
- assetsInQueueNotifcation.BusConnect();
- bool ready{};
- while (!ready)
- {
- AzFramework::AssetSystemRequestBus::BroadcastResult(ready, &AzFramework::AssetSystemRequestBus::Events::WaitUntilAssetProcessorReady, AZStd::chrono::milliseconds(100));
- if (!ready)
- {
- AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
- }
- }
- assetsInQueueNotifcation.BusDisconnect();
- AZ_TracePrintf("Editor", "CriticalAssetsCompiled\n");
- // Signal the "CriticalAssetsCompiled" lifecycle event
- // Also reload the "assetcatalog.xml" if it exists
- if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
- {
- // Reload the assetcatalog.xml at this point again
- // Start Monitoring Asset changes over the network and load the AssetCatalog
- auto LoadCatalog = [settingsRegistry](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
- {
- if (AZ::IO::FixedMaxPath assetCatalogPath;
- settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
- {
- assetCatalogPath /= "assetcatalog.xml";
- assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str());
- }
- };
- CCryEditApp::OutputStartupMessage(QString("Loading Asset Catalog..."));
- AZ::Data::AssetCatalogRequestBus::Broadcast(AZStd::move(LoadCatalog));
- // Only signal the event *after* the asset catalog has been loaded.
- AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "CriticalAssetsCompiled", R"({})");
- }
- CCryEditApp::OutputStartupMessage(QString("Asset Processor is now ready."));
- }
- bool CCryEditApp::ConnectToAssetProcessor() const
- {
- bool connectedToAssetProcessor = false;
- // When the AssetProcessor is already launched it should take less than a second to perform a connection
- // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize
- // and able to negotiate a connection when running a debug build
- // and to negotiate a connection
- // Setting the connectTimeout to 3 seconds if not set within the settings registry
- AZStd::chrono::seconds connectTimeout(3);
- // Initialize the launchAssetProcessorTimeout to 15 seconds by default and check the settings registry for an override
- AZStd::chrono::seconds launchAssetProcessorTimeout(15);
- AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
- if (settingsRegistry)
- {
- AZ::s64 timeoutValue{};
- if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, timeoutValue,
- AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "connect_ap_timeout"))
- {
- connectTimeout = AZStd::chrono::seconds(timeoutValue);
- }
- // Reset timeout integer
- timeoutValue = {};
- if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, timeoutValue,
- AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "launch_ap_timeout"))
- {
- launchAssetProcessorTimeout = AZStd::chrono::seconds(timeoutValue);
- }
- }
- CCryEditApp::OutputStartupMessage(QString("Connecting to Asset Processor... "));
- AzFramework::AssetSystem::ConnectionSettings connectionSettings;
- AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
- connectionSettings.m_launchAssetProcessorOnFailedConnection = true;
- connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
- connectionSettings.m_connectionIdentifier = AzFramework::AssetSystem::ConnectionIdentifiers::Editor;
- connectionSettings.m_loggingCallback = [](AZStd::string_view logData)
- {
- CCryEditApp::OutputStartupMessage(QString::fromUtf8(logData.data(), aznumeric_cast<int>(logData.size())));
- };
- AzFramework::AssetSystemRequestBus::BroadcastResult(connectedToAssetProcessor, &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings);
- if (connectedToAssetProcessor)
- {
- AZ_TracePrintf("Editor", "Connected to Asset Processor\n");
- CCryEditApp::OutputStartupMessage(QString("Connected to Asset Processor"));
- CompileCriticalAssets();
- return true;
- }
- AZ_TracePrintf("Editor", "Failed to connect to Asset Processor\n");
- CCryEditApp::OutputStartupMessage(QString("Failed to connect to Asset Processor"));
- return false;
- }
- //! This handles the normal logging of Python output in the Editor by outputting
- //! the data to both the Editor Console and the Editor.log file
- struct CCryEditApp::PythonOutputHandler
- : public AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
- {
- PythonOutputHandler()
- {
- AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusConnect();
- }
- ~PythonOutputHandler() override
- {
- AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
- }
- int GetOrder() override
- {
- return 0;
- }
- void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override
- {
- AZ_TracePrintf("python_test", "%.*s", static_cast<int>(message.size()), message.data());
- }
- void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override
- {
- AZ_Error("python_test", false, "%.*s", static_cast<int>(message.size()), message.data());
- }
- void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override
- {
- AZ_Error("python_test", false, "EXCEPTION: %.*s", static_cast<int>(message.size()), message.data());
- }
- };
- //! Outputs Python test script print() to stdout
- //! If an exception happens in a Python test script, the process terminates
- struct PythonTestOutputHandler final
- : public CCryEditApp::PythonOutputHandler
- {
- PythonTestOutputHandler() = default;
- ~PythonTestOutputHandler() override = default;
- void OnTraceMessage(AZStd::string_view message) override
- {
- PythonOutputHandler::OnTraceMessage(message);
- printf("%.*s\n", static_cast<int>(message.size()), message.data());
- }
- void OnErrorMessage(AZStd::string_view message) override
- {
- PythonOutputHandler::OnErrorMessage(message);
- printf("ERROR: %.*s\n", static_cast<int>(message.size()), message.data());
- }
- void OnExceptionMessage(AZStd::string_view message) override
- {
- PythonOutputHandler::OnExceptionMessage(message);
- printf("EXCEPTION: %.*s\n", static_cast<int>(message.size()), message.data());
- }
- };
- void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
- {
- if (cmdInfo.m_bRunPythonTestScript)
- {
- m_pythonOutputHandler = AZStd::make_shared<PythonTestOutputHandler>();
- }
- else
- {
- m_pythonOutputHandler = AZStd::make_shared<PythonOutputHandler>();
- }
- using namespace AzToolsFramework;
- if (cmdInfo.m_bRunPythonScript || cmdInfo.m_bRunPythonTestScript)
- {
- // Separates the compound string of semicolon separated values into a vector of values
- const auto extractSeparatedValues = [](const AZStd::string& compoundValues)
- {
- AZStd::vector<AZStd::string_view> values;
- AZ::StringFunc::TokenizeVisitor(
- compoundValues.c_str(),
- [&values](AZStd::string_view elem)
- {
- values.push_back(elem);
- },
- ';',
- false /* keepEmptyStrings */
- );
- return values;
- };
- // Reads the contents of the specified file and returns a string of said contents
- const auto readFileContents = [](const AZStd::string& path) -> AZStd::string
- {
- const auto fileSize = AZ::IO::SystemFile::Length(path.c_str());
- if (fileSize == 0)
- {
- return "";
- }
- AZStd::vector<char> buffer(fileSize + 1);
- buffer[fileSize] = '\0';
- if (!AZ::IO::SystemFile::Read(path.c_str(), buffer.data()))
- {
- return "";
- }
- return AZStd::string(buffer.begin(), buffer.end());
- };
- // We support specifying multiple files in the cmdline by separating them with ';'
- // If a semicolon list of .py files is provided we look at the arg string
- AZStd::string scriptFileStr;
- if (cmdInfo.m_strFileName.endsWith(".py"))
- {
- // cmdInfo data is only available on startup, copy it
- scriptFileStr = cmdInfo.m_strFileName.toUtf8().constData();
- }
- else if (cmdInfo.m_strFileName.endsWith(".txt"))
- {
- // Otherwise, we look to see if we can read the file for test modules
- // The file is expected to contain a single semicolon separated string of Editor pytest modules
- if (scriptFileStr = readFileContents(cmdInfo.m_strFileName.toUtf8().data()); scriptFileStr.empty())
- {
- AZ_Error(
- "RunInitPythonScript",
- false, "Failed to read the file containing a semi colon separated list of python modules");
- return;
- }
- }
- else
- {
- AZ_Error("RunInitPythonScript", false, "Failed to read Python files from --runpythontest arg. "
- "Expects a semi colon separated list of python modules or a file containing a semi colon separated list of python modules");
- return;
- }
- // Extract the discrete python script files
- const auto fileList = extractSeparatedValues(scriptFileStr);
- if (cmdInfo.m_pythonArgs.length() > 0 || cmdInfo.m_bRunPythonTestScript)
- {
- QByteArray pythonArgsStr = cmdInfo.m_pythonArgs.toUtf8();
- AZStd::vector<AZStd::string_view> pythonArgs;
- AZ::StringFunc::TokenizeVisitor(pythonArgsStr.constData(),
- [&pythonArgs](AZStd::string_view elem)
- {
- pythonArgs.push_back(elem);
- }, ' '
- );
- if (cmdInfo.m_bRunPythonTestScript)
- {
- // We support specifying multiple test case names in the cmdline by separating them
- // with ';', either in a text file or as a string
- AZStd::string testCaseStr;
- if (cmdInfo.m_pythonTestCase.endsWith(".txt"))
- {
- // A path to the file containing the test case names has been provided as the argument
- if (testCaseStr = readFileContents(cmdInfo.m_pythonTestCase.toUtf8().data()); testCaseStr.empty())
- {
- AZ_Error(
- "RunInitPythonScript",
- false,
- "Failed to read Python files from --pythontestcase arg. "
- "Expects a semi colon separated list of python test case names or a file containing a semi colon separated list of "
- "python test case names");
- return;
- }
- }
- else
- {
- // Test case names have been passed as the argument
- testCaseStr = cmdInfo.m_pythonTestCase.toUtf8().data();
- }
- // Extract the discrete python test case names
- const auto testCaseList = extractSeparatedValues(testCaseStr);
- // The number of python script files must match the number of test case names for the test case names
- // to properly correlate with their invoking scripts
- if (fileList.size() != testCaseList.size())
- {
- AZ_Error(
- "RunInitPythonScript",
- false,
- "The number of supplied test scripts (%zu) did not match the number of supplied test case names (%zu)",
- fileList.size(), testCaseList.size());
- return;
- }
- bool success = true;
- auto ExecuteByFilenamesTests = [&pythonArgs, &fileList, &testCaseList, &success](EditorPythonRunnerRequests* pythonRunnerRequests)
- {
- for (int i = 0; i < fileList.size(); ++i)
- {
- bool cur_success = pythonRunnerRequests->ExecuteByFilenameAsTest(fileList[i], testCaseList[i], pythonArgs);
- success = success && cur_success;
- }
- };
- EditorPythonRunnerRequestBus::Broadcast(ExecuteByFilenamesTests);
- if (success)
- {
- // Close the editor gracefully as the test has completed
- GetIEditor()->GetDocument()->SetModifiedFlag(false);
- QTimer::singleShot(0, qApp, &QApplication::closeAllWindows);
- }
- else
- {
- // Close down the application with 0xF exit code indicating failure of the test
- AZ::Debug::Trace::Terminate(0xF);
- }
- }
- else
- {
- auto ExecuteByFilenamesWithArgs = [&pythonArgs, &fileList](EditorPythonRunnerRequests* pythonRunnerRequests)
- {
- for (AZStd::string_view filename : fileList)
- {
- pythonRunnerRequests->ExecuteByFilenameWithArgs(filename, pythonArgs);
- }
- };
- EditorPythonRunnerRequestBus::Broadcast(ExecuteByFilenamesWithArgs);
- }
- }
- else
- {
- auto ExecuteByFilenames = [&fileList](EditorPythonRunnerRequests* pythonRunnerRequests)
- {
- for (AZStd::string_view filename : fileList)
- {
- pythonRunnerRequests->ExecuteByFilename(filename);
- }
- };
- EditorPythonRunnerRequestBus::Broadcast(ExecuteByFilenames);
- }
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // CCryEditApp initialization
- bool CCryEditApp::InitInstance()
- {
- QElapsedTimer startupTimer;
- startupTimer.start();
- m_pEditor = new CEditorImpl();
- // parameters must be parsed early to capture arguments for test bootstrap
- CEditCommandLineInfo cmdInfo;
- InitFromCommandLine(cmdInfo);
- qobject_cast<Editor::EditorQtApplication*>(qApp)->Initialize(); // Must be done after CEditorImpl() is created
- m_pEditor->Initialize();
- // let anything listening know that they can use the IEditor now
- AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::NotifyIEditorAvailable, m_pEditor);
- if (cmdInfo.m_bShowVersionInfo)
- {
- CStartupLogoDialog startupDlg(CStartupLogoDialog::About, FormatVersion(m_pEditor->GetFileVersion()), FormatRichTextCopyrightNotice());
- startupDlg.exec();
- return false;
- }
- RegisterReflectedVarHandlers();
- CreateSplashScreen();
- // Register the application's document templates. Document templates
- // serve as the connection between documents, frame windows and views
- CCrySingleDocTemplate* pDocTemplate = CCrySingleDocTemplate::create<CCryEditDoc>();
- m_pDocManager = new CCryDocManager;
- ((CCryDocManager*)m_pDocManager)->SetDefaultTemplate(pDocTemplate);
- auto mainWindow = new MainWindow();
- #ifdef Q_OS_MACOS
- auto mainWindowWrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionDisabled);
- #else
- auto mainWindowWrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionAutoTitleBarButtons);
- #endif
- mainWindowWrapper->setGuest(mainWindow);
- HWND mainWindowWrapperHwnd = (HWND)mainWindowWrapper->winId();
- AZ::IO::FixedMaxPath engineRootPath;
- if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
- {
- settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
- }
- QDir engineRoot = QString::fromUtf8(engineRootPath.c_str(), aznumeric_cast<int>(engineRootPath.Native().size()));
- AzQtComponents::StyleManager::addSearchPaths(
- QStringLiteral("style"),
- engineRoot.filePath(QStringLiteral("Code/Editor/Style")),
- QStringLiteral(":/Assets/Editor/Style"),
- engineRootPath);
- AzQtComponents::StyleManager::setStyleSheet(mainWindow, QStringLiteral("style:Editor.qss"));
- // Note: we should use getNativeHandle to get the HWND from the widget, but
- // it returns an invalid handle unless the widget has been shown and polished and even then
- // it sometimes returns an invalid handle.
- // So instead, we use winId(), which does consistently work
- //mainWindowWrapperHwnd = QtUtil::getNativeHandle(mainWindowWrapper);
- // Connect to the AssetProcessor at this point
- // It will be launched if not running
- ConnectToAssetProcessor();
- CCryEditApp::OutputStartupMessage(QString("Initializing Game System..."));
- auto initGameSystemOutcome = InitGameSystem(mainWindowWrapperHwnd);
- if (!initGameSystemOutcome.IsSuccess())
- {
- return false;
- }
- if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
- {
- AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})");
- }
- // Process some queued events come from system init
- // Such as asset catalog loaded notification.
- // There are some systems need to load configurations from assets for post initialization but before loading level
- AZ::TickBus::ExecuteQueuedEvents();
- qobject_cast<Editor::EditorQtApplication*>(qApp)->LoadSettings();
- // Create Sandbox user folder if necessary
- AZ::IO::FileIOBase::GetDirectInstance()->CreatePath(Path::GetUserSandboxFolder().toUtf8().data());
- if (!InitGame())
- {
- if (gEnv && gEnv->pLog)
- {
- gEnv->pLog->LogError("Game can not be initialized, InitGame() failed.");
- }
- if (!cmdInfo.m_bExport)
- {
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QString(), QObject::tr("Game can not be initialized, please refer to the editor log file"));
- }
- return false;
- }
- // Meant to be called before MainWindow::Initialize
- InitPlugins();
- CCryEditApp::OutputStartupMessage(QString("Initializing Main Window..."));
- mainWindow->Initialize();
- GetIEditor()->GetCommandManager()->RegisterAutoCommands();
- mainWindowWrapper->enableSaveRestoreGeometry("O3DE", "O3DE", "mainWindowGeometry");
- m_pDocManager->OnFileNew();
- if (MainWindow::instance())
- {
- if (m_bConsoleMode || IsInAutotestMode())
- {
- AZ::Environment::FindVariable<int>("assertVerbosityLevel").Set(1);
- m_pConsoleDialog->raise();
- }
- else
- {
- MainWindow::instance()->show();
- MainWindow::instance()->raise();
- MainWindow::instance()->update();
- MainWindow::instance()->setFocus();
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- QWindow* window = mainWindowWrapper->windowHandle();
- if (window)
- {
- Editor::WindowObserver* observer = new Editor::WindowObserver(window, this);
- connect(observer, &Editor::WindowObserver::windowIsMovingOrResizingChanged, Editor::EditorQtApplication::instance(), &Editor::EditorQtApplication::setIsMovingOrResizing);
- }
- #endif
- }
- }
- if (m_bAutotestMode)
- {
- ICVar* const noErrorReportWindowCVar = gEnv && gEnv->pConsole ? gEnv->pConsole->GetCVar("sys_no_error_report_window") : nullptr;
- if (noErrorReportWindowCVar)
- {
- noErrorReportWindowCVar->Set(true);
- }
- ICVar* const showErrorDialogOnLoadCVar = gEnv && gEnv->pConsole ? gEnv->pConsole->GetCVar("ed_showErrorDialogOnLoad") : nullptr;
- if (showErrorDialogOnLoadCVar)
- {
- showErrorDialogOnLoadCVar->Set(false);
- }
- }
- SetEditorWindowTitle(nullptr, AZ::Utils::GetProjectDisplayName().c_str(), nullptr);
- m_pEditor->InitFinished();
- CCryEditApp::OutputStartupMessage(QString("Activating Python..."));
- // Make sure Python is started before we attempt to restore the Editor layout, since the user
- // might have custom view panes in the saved layout that will need to be registered.
- auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
- if (editorPythonEventsInterface)
- {
- editorPythonEventsInterface->StartPython();
- }
- CCryEditApp::OutputStartupMessage(QString("")); // add a blank line so that python is not blamed for anything that happens here
- if (!GetIEditor()->IsInConsolewMode())
- {
- bool restoreDefaults = !mainWindowWrapper->restoreGeometryFromSettings();
- QtViewPaneManager::instance()->RestoreLayout(restoreDefaults);
- }
- // Trigger the Action Manager registration hooks once all systems and Gems are initialized and listening.
- AzToolsFramework::ActionManagerSystemComponent::TriggerRegistrationNotifications();
- CloseSplashScreen();
- // DON'T CHANGE ME!
- // Test scripts listen for this line, so please don't touch this without updating them.
- // We consider ourselves "initialized enough" at this stage because all further initialization may be blocked by the modal welcome screen.
- CLogFile::WriteLine(QString("Engine initialized, took %1s.").arg(startupTimer.elapsed() / 1000.0, 0, 'f', 2));
- // Init the level after everything else is finished initializing, otherwise, certain things aren't set up yet
- QTimer::singleShot(0, this, [this, cmdInfo] {
- InitLevel(cmdInfo);
- });
- if (!m_bConsoleMode && !m_bPreviewMode)
- {
- GetIEditor()->UpdateViews();
- if (MainWindow::instance())
- {
- MainWindow::instance()->setFocus();
- }
- }
- if (!InitConsole())
- {
- return true;
- }
- if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
- {
- AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacyCommandLineProcessed", R"({})");
- }
- if (IsInRegularEditorMode())
- {
- int startUpMacroIndex = GetIEditor()->GetToolBoxManager()->GetMacroIndex("startup", true);
- if (startUpMacroIndex >= 0)
- {
- CryLogAlways("Executing the startup macro");
- GetIEditor()->GetToolBoxManager()->ExecuteMacro(startUpMacroIndex, true);
- }
- }
- if (GetIEditor()->GetCommandManager()->IsRegistered("editor.open_lnm_editor"))
- {
- CCommand0::SUIInfo uiInfo;
- [[maybe_unused]] bool ok = GetIEditor()->GetCommandManager()->GetUIInfo("editor.open_lnm_editor", uiInfo);
- assert(ok);
- }
- RunInitPythonScript(cmdInfo);
- return true;
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::LoadFile(QString fileName)
- {
- if (GetIEditor()->GetViewManager()->GetViewCount() == 0)
- {
- return;
- }
- if (MainWindow::instance() || m_pConsoleDialog)
- {
- SetEditorWindowTitle(nullptr, AZ::Utils::GetProjectDisplayName().c_str(), GetIEditor()->GetGameEngine()->GetLevelName());
- }
- GetIEditor()->SetModifiedFlag(false);
- GetIEditor()->SetModifiedModule(eModifiedNothing);
- }
- //////////////////////////////////////////////////////////////////////////
- inline void ExtractMenuName(QString& str)
- {
- // eliminate &
- int pos = str.indexOf('&');
- if (pos >= 0)
- {
- str = str.left(pos) + str.right(str.length() - pos - 1);
- }
- // cut the string
- for (int i = 0; i < str.length(); i++)
- {
- if (str[i] == 9)
- {
- str = str.left(i);
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::EnableAccelerator([[maybe_unused]] bool bEnable)
- {
- /*
- if (bEnable)
- {
- //LoadAccelTable( MAKEINTRESOURCE(IDR_MAINFRAME) );
- m_AccelManager.UpdateWndTable();
- CLogFile::WriteLine( "Enable Accelerators" );
- }
- else
- {
- CMainFrame *mainFrame = (CMainFrame*)m_pMainWnd;
- if (mainFrame->m_hAccelTable)
- DestroyAcceleratorTable( mainFrame->m_hAccelTable );
- mainFrame->m_hAccelTable = nullptr;
- mainFrame->LoadAccelTable( MAKEINTRESOURCE(IDR_GAMEACCELERATOR) );
- CLogFile::WriteLine( "Disable Accelerators" );
- }
- */
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::SaveAutoRemind()
- {
- // Added a static variable here to avoid multiple messageboxes to
- // remind the user of saving the file. Many message boxes would appear as this
- // is triggered by a timer even which does not stop when the message box is called.
- // Used a static variable instead of a member variable because this value is not
- // Needed anywhere else.
- static bool boIsShowingWarning(false);
- // Ingore in game mode, or if no level created, or level not modified
- if (GetIEditor()->IsInGameMode() || !GetIEditor()->GetGameEngine()->IsLevelLoaded() || !GetIEditor()->GetDocument()->IsModified())
- {
- return;
- }
- if (boIsShowingWarning)
- {
- return;
- }
- boIsShowingWarning = true;
- if (QMessageBox::question(AzToolsFramework::GetActiveWindow(), QString(), QObject::tr("Auto Reminder: You did not save level for at least %1 minute(s)\r\nDo you want to save it now?").arg(gSettings.autoRemindTime), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
- {
- // Save now.
- GetIEditor()->SaveDocument();
- }
- boIsShowingWarning = false;
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::WriteConfig()
- {
- IEditor* pEditor = GetIEditor();
- if (pEditor && pEditor->GetDisplaySettings())
- {
- pEditor->GetDisplaySettings()->SaveRegistry();
- }
- }
- // App command to run the dialog
- void CCryEditApp::OnAppAbout()
- {
- auto* dialog = new CStartupLogoDialog(
- CStartupLogoDialog::About, FormatVersion(m_pEditor->GetFileVersion()), FormatRichTextCopyrightNotice());
- auto mainWindow = MainWindow::instance();
- auto geometry = dialog->geometry();
- geometry.moveCenter(mainWindow->mapToGlobal(mainWindow->geometry().center()));
- dialog->setGeometry(geometry);
- dialog->setAttribute(Qt::WA_DeleteOnClose);
- dialog->show();
- }
- // App command to run the Welcome to Open 3D Engine dialog
- void CCryEditApp::OnAppShowWelcomeScreen()
- {
- // This logic is a simplified version of the startup
- // flow that also shows the Welcome dialog
- if (m_bIsExportingLegacyData
- || m_creatingNewLevel
- || m_openingLevel
- || m_savingLevel)
- {
- QMessageBox::warning(AzToolsFramework::GetActiveWindow(), QString(), "The Welcome screen cannot be displayed because a level load/save is in progress.");
- return;
- }
- QString levelName;
- bool showWelcomeDialog = true;
- while (showWelcomeDialog)
- {
- // Keep showing the Welcome dialog as long as the user cancels
- // a level creation/load triggered from the Welcome dialog
- levelName = ShowWelcomeDialog();
- if (levelName == "new")
- {
- // The user has clicked on the create new level option
- bool wasCreateLevelOperationCancelled = false;
- bool isNewLevelCreationSuccess = false;
- // This will show the new level dialog until a valid input has been entered by the user or until the user click cancel
- while (!isNewLevelCreationSuccess && !wasCreateLevelOperationCancelled)
- {
- isNewLevelCreationSuccess = CreateLevel(wasCreateLevelOperationCancelled);
- }
- if (isNewLevelCreationSuccess)
- {
- showWelcomeDialog = false;
- levelName.clear();
- }
- }
- else
- {
- // The user has selected an existing level to open
- showWelcomeDialog = false;
- }
- }
- if (!levelName.isEmpty())
- {
- // load level
- if (!CFileUtil::Exists(levelName, false))
- {
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QObject::tr("Missing level"), QObject::tr("Failed to auto-load last opened level. Level file does not exist:\n\n%1").arg(levelName));
- }
- else
- {
- OpenDocumentFile(levelName.toUtf8().data());
- }
- }
- }
- void CCryEditApp::OnUpdateShowWelcomeScreen(QAction* action)
- {
- action->setEnabled(!m_bIsExportingLegacyData
- && !m_creatingNewLevel
- && !m_openingLevel
- && !m_savingLevel);
- }
- void CCryEditApp::OnDocumentationTutorials()
- {
- QString webLink = tr("https://o3de.org/docs/learning-guide/");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationGlossary()
- {
- QString webLink = tr("https://o3de.org/docs/user-guide/appendix/glossary/");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationO3DE()
- {
- QString webLink = tr("https://o3de.org/docs/");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationGamelift()
- {
- QString webLink = tr("https://docs.aws.amazon.com/gamelift/");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationReleaseNotes()
- {
- QString webLink = tr("https://o3de.org/docs/release-notes/");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationGameDevBlog()
- {
- QString webLink = tr("https://aws.amazon.com/blogs/gamedev");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationForums()
- {
- QString webLink = tr("https://o3de.org/community/");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationAWSSupport()
- {
- QString webLink = tr("https://aws.amazon.com/contact-us");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- bool CCryEditApp::FixDanglingSharedMemory(const QString& sharedMemName) const
- {
- QSystemSemaphore sem(sharedMemName + "_sem", 1);
- sem.acquire();
- {
- QSharedMemory fix(sharedMemName);
- if (!fix.attach())
- {
- if (fix.error() != QSharedMemory::NotFound)
- {
- sem.release();
- return false;
- }
- }
- // fix.detach() when destructed, taking out any dangling shared memory
- // on unix
- }
- sem.release();
- return true;
- }
- /////////////////////////////////////////////////////////////////////////////
- // CCryEditApp message handlers
- int CCryEditApp::ExitInstance(int exitCode)
- {
- if (m_pEditor)
- {
- m_pEditor->OnBeginShutdownSequence();
- }
- qobject_cast<Editor::EditorQtApplication*>(qApp)->UnloadSettings();
- if (IsInRegularEditorMode())
- {
- if (GetIEditor())
- {
- int shutDownMacroIndex = GetIEditor()->GetToolBoxManager()->GetMacroIndex("shutdown", true);
- if (shutDownMacroIndex >= 0)
- {
- CryLogAlways("Executing the shutdown macro");
- GetIEditor()->GetToolBoxManager()->ExecuteMacro(shutDownMacroIndex, true);
- }
- }
- }
- if (GetIEditor())
- {
- //Nobody seems to know in what case that kind of exit can happen so instrumented to see if it happens at all
- if (m_pEditor)
- {
- m_pEditor->OnEarlyExitShutdownSequence();
- }
- gEnv->pLog->Flush();
- // note: the intention here is to quit immediately without processing anything further
- // on linux and mac, _exit has that effect
- // however, on windows, _exit() still invokes CRT functions, unloads, and destructors
- // so on windows, we need to use TerminateProcess
- #if defined(AZ_PLATFORM_WINDOWS)
- TerminateProcess(GetCurrentProcess(), exitCode);
- #else
- _exit(exitCode);
- #endif
- }
- SAFE_DELETE(m_pConsoleDialog);
- if (GetIEditor())
- {
- GetIEditor()->Notify(eNotify_OnQuit);
- }
- // if we're aborting due to an unexpected shutdown then don't call into objects that don't exist yet.
- if ((gEnv) && (gEnv->pSystem) && (gEnv->pSystem->GetILevelSystem()))
- {
- gEnv->pSystem->GetILevelSystem()->UnloadLevel();
- }
- if (GetIEditor())
- {
- GetIEditor()->GetDocument()->DeleteTemporaryLevel();
- }
- m_bExiting = true;
- HEAP_CHECK
- ////////////////////////////////////////////////////////////////////////
- // Executed directly before termination of the editor, just write a
- // quick note to the log so that we can later see that the editor
- // terminated flawlessly. Also delete temporary files.
- ////////////////////////////////////////////////////////////////////////
- WriteConfig();
- if (m_pEditor)
- {
- // Ensure component entities are wiped prior to unloading plugins,
- // since components may be implemented in those plugins.
- AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
- &AzToolsFramework::EditorEntityContextRequestBus::Events::ResetEditorContext);
- // vital, so that the Qt integration can unhook itself!
- m_pEditor->UnloadPlugins();
- m_pEditor->Uninitialize();
- }
- //////////////////////////////////////////////////////////////////////////
- // Quick end for editor.
- if (gEnv && gEnv->pSystem)
- {
- gEnv->pSystem->Quit();
- SAFE_RELEASE(gEnv->pSystem);
- }
- //////////////////////////////////////////////////////////////////////////
- if (m_pEditor)
- {
- m_pEditor->DeleteThis();
- m_pEditor = nullptr;
- }
- // save accelerator manager configuration.
- //m_AccelManager.SaveOnExit();
- #ifdef WIN32
- Gdiplus::GdiplusShutdown(m_gdiplusToken);
- #endif
- if (m_mutexApplication)
- {
- delete m_mutexApplication;
- }
- return 0;
- }
- bool CCryEditApp::IsWindowInForeground()
- {
- return Editor::EditorQtApplication::instance()->IsActive();
- }
- void CCryEditApp::DisableIdleProcessing()
- {
- m_disableIdleProcessingCounter++;
- }
- void CCryEditApp::EnableIdleProcessing()
- {
- m_disableIdleProcessingCounter--;
- AZ_Assert(m_disableIdleProcessingCounter >= 0, "m_disableIdleProcessingCounter must be nonnegative");
- }
- bool CCryEditApp::OnIdle([[maybe_unused]] LONG lCount)
- {
- if (0 == m_disableIdleProcessingCounter)
- {
- return IdleProcessing(gSettings.backgroundUpdatePeriod == -1);
- }
- else
- {
- return false;
- }
- }
- int CCryEditApp::IdleProcessing(bool bBackgroundUpdate)
- {
- AZ_Assert(m_disableIdleProcessingCounter == 0, "We should not be in IdleProcessing()");
- //HEAP_CHECK
- if (!MainWindow::instance())
- {
- return 0;
- }
- if (!GetIEditor()->GetSystem())
- {
- return 0;
- }
- // Ensure we don't get called re-entrantly
- // This can occur when a nested Qt event loop fires (e.g. by way of a modal dialog calling exec)
- if (m_idleProcessingRunning)
- {
- return 0;
- }
- QScopedValueRollback<bool> guard(m_idleProcessingRunning, true);
- ////////////////////////////////////////////////////////////////////////
- // Call the update function of the engine
- ////////////////////////////////////////////////////////////////////////
- if (m_bTestMode && !bBackgroundUpdate)
- {
- // Terminate process.
- CLogFile::WriteLine("Editor: Terminate Process");
- exit(0);
- }
- bool bIsAppWindow = IsWindowInForeground();
- bool bActive = false;
- int res = 0;
- if (bIsAppWindow || m_bForceProcessIdle || m_bKeepEditorActive
- // Automated tests must always keep the editor active, or they can get stuck
- || m_bAutotestMode || m_bRunPythonTestScript)
- {
- res = 1;
- bActive = true;
- }
- if (m_bForceProcessIdle && bIsAppWindow)
- {
- m_bForceProcessIdle = false;
- }
- // focus changed
- if (m_bPrevActive != bActive)
- {
- GetIEditor()->GetSystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_CHANGE_FOCUS, bActive, 0);
- #if defined(AZ_PLATFORM_WINDOWS)
- // This is required for the audio system to be notified of focus changes in the editor. After discussing it
- // with the macOS team, they are working on unifying the system events between the editor and standalone
- // launcher so this is only needed on windows.
- if (bActive)
- {
- AzFramework::WindowsLifecycleEvents::Bus::Broadcast(&AzFramework::WindowsLifecycleEvents::Bus::Events::OnSetFocus);
- }
- else
- {
- AzFramework::WindowsLifecycleEvents::Bus::Broadcast(&AzFramework::WindowsLifecycleEvents::Bus::Events::OnKillFocus);
- }
- #endif
- }
- m_bPrevActive = bActive;
- // Tick System Events, even in the background
- AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
- if (componentApplicationRequests)
- {
- AZ::ComponentApplication* componentApplication = componentApplicationRequests->GetApplication();
- if (componentApplication)
- {
- componentApplication->TickSystem();
- }
- }
- // Don't tick application if we're doing idle processing during an assert.
- const bool isErrorWindowVisible = (gEnv && gEnv->pSystem->IsAssertDialogVisible());
- if (isErrorWindowVisible)
- {
- if (m_pEditor)
- {
- m_pEditor->Update();
- }
- }
- else if (bActive || (bBackgroundUpdate && !bIsAppWindow))
- {
- // Update Game
- GetIEditor()->GetGameEngine()->Update();
- if (!GetIEditor()->IsInGameMode())
- {
- if (m_pEditor)
- {
- m_pEditor->Update();
- }
- GetIEditor()->Notify(eNotify_OnIdleUpdate);
- }
- }
- else
- {
- if (GetIEditor()->GetSystem() && GetIEditor()->GetSystem()->GetILog())
- {
- GetIEditor()->GetSystem()->GetILog()->Update(); // print messages from other threads
- }
- // If we're backgrounded and not fully background updating, idle to rate limit SystemTick
- static AZ::TimeMs sTimeLastMs = AZ::GetRealElapsedTimeMs();
- const int64_t maxFrameTimeMs = ed_backgroundSystemTickCap;
- if (maxFrameTimeMs > 0)
- {
- const int64_t maxElapsedTimeMs = maxFrameTimeMs + static_cast<int64_t>(sTimeLastMs);
- const int64_t realElapsedTimeMs = static_cast<int64_t>(AZ::GetRealElapsedTimeMs());
- if (maxElapsedTimeMs > realElapsedTimeMs)
- {
- CrySleep(aznumeric_cast<unsigned int>(maxElapsedTimeMs - realElapsedTimeMs));
- }
- }
- sTimeLastMs = AZ::GetRealElapsedTimeMs();
- }
- DisplayLevelLoadErrors();
- if (CConsoleSCB::GetCreatedInstance())
- {
- CConsoleSCB::GetCreatedInstance()->FlushText();
- }
- return res;
- }
- void CCryEditApp::DisplayLevelLoadErrors()
- {
- CCryEditDoc* currentLevel = GetIEditor()->GetDocument();
- if (currentLevel && currentLevel->IsDocumentReady() && !m_levelErrorsHaveBeenDisplayed)
- {
- // Generally it takes a few idle updates for meshes to load and be processed by their components. This value
- // was picked based on examining when mesh components are updated and their materials are checked for
- // errors (2 updates) plus one more for good luck.
- const int IDLE_FRAMES_TO_WAIT = 3;
- ++m_numBeforeDisplayErrorFrames;
- if (m_numBeforeDisplayErrorFrames > IDLE_FRAMES_TO_WAIT)
- {
- GetIEditor()->CommitLevelErrorReport();
- GetIEditor()->GetErrorReport()->Display();
- m_numBeforeDisplayErrorFrames = 0;
- m_levelErrorsHaveBeenDisplayed = true;
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::ExportLevel(bool /* bExportToGame */, bool /* bExportTexture */, bool /* bAutoExport */)
- {
- AZ_Assert(false, "Prefab system doesn't require level exports.");
- return;
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnEditHold()
- {
- GetIEditor()->GetDocument()->Hold(HOLD_FETCH_FILE);
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnEditFetch()
- {
- GetIEditor()->GetDocument()->Fetch(HOLD_FETCH_FILE);
- }
- //////////////////////////////////////////////////////////////////////////
- bool CCryEditApp::UserExportToGame(bool /* bNoMsgBox */)
- {
- AZ_Assert(false, "Export Level should no longer exist.");
- return false;
- }
- void CCryEditApp::ExportToGame(bool /* bNoMsgBox */)
- {
- AZ_Assert(false, "Prefab system no longer exports levels.");
- return;
- }
- void CCryEditApp::OnFileExportToGameNoSurfaceTexture()
- {
- UserExportToGame(false);
- }
- void CCryEditApp::OnMoveObject()
- {
- ////////////////////////////////////////////////////////////////////////
- // Move the selected object to the marker position
- ////////////////////////////////////////////////////////////////////////
- }
- void CCryEditApp::OnRenameObj()
- {
- }
- void CCryEditApp::OnViewSwitchToGame()
- {
- if (IsInPreviewMode())
- {
- return;
- }
- // If switching on game mode...
- if (!GetIEditor()->IsInGameMode())
- {
- // If simulation mode is enabled...
- uint32 flags = GetIEditor()->GetDisplaySettings()->GetSettings();
- if (flags & SETTINGS_PHYSICS)
- {
- // Disable simulation mode
- OnSwitchPhysics();
- // Schedule for next frame to enable game mode
- AZ::Interface<AZ::IEventScheduler>::Get()->AddCallback(
- [this] { OnViewSwitchToGame(); },
- AZ::Name("Enable Game Mode"), AZ::Time::ZeroTimeMs);
- return;
- }
- }
- // close all open menus
- auto activePopup = qApp->activePopupWidget();
- if (qobject_cast<QMenu*>(activePopup))
- {
- activePopup->hide();
- }
- // TODO: Add your command handler code here
- bool inGame = !GetIEditor()->IsInGameMode();
- GetIEditor()->SetInGameMode(inGame);
- }
- void CCryEditApp::OnViewSwitchToGameFullScreen()
- {
- ed_previewGameInFullscreen_once = true;
- OnViewSwitchToGame();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnEditLevelData()
- {
- auto dir = QFileInfo(GetIEditor()->GetDocument()->GetLevelPathName()).dir();
- CFileUtil::EditTextFile(dir.absoluteFilePath("leveldata.xml").toUtf8().data());
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnFileEditLogFile()
- {
- QString file = CLogFile::GetLogFileName();
- QString fullPathName = Path::GamePathToFullPath(file);
- QDesktopServices::openUrl(QUrl::fromLocalFile(fullPathName));
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnFileEditEditorini()
- {
- CFileUtil::EditTextFile(EDITOR_CFG_FILE);
- }
- void CCryEditApp::OnPreferences()
- {
- /*
- //////////////////////////////////////////////////////////////////////////////
- // Accels edit by CPropertyPage
- CAcceleratorManager tmpAccelManager;
- tmpAccelManager = m_AccelManager;
- CAccelMapPage page(&tmpAccelManager);
- CPropertySheet sheet;
- sheet.SetTitle( _T("Preferences") );
- sheet.AddPage(&page);
- if (sheet.DoModal() == IDOK) {
- m_AccelManager = tmpAccelManager;
- m_AccelManager.UpdateWndTable();
- }
- */
- }
- void CCryEditApp::OnOpenProjectManagerSettings()
- {
- OpenProjectManager("UpdateProject");
- }
- void CCryEditApp::OnOpenProjectManagerNew()
- {
- OpenProjectManager("CreateProject");
- }
- void CCryEditApp::OnOpenProjectManager()
- {
- OpenProjectManager("Projects");
- }
- void CCryEditApp::OpenProjectManager(const AZStd::string& screen)
- {
- // provide the current project path for in case we want to update the project
- AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
- const AZStd::vector<AZStd::string> commandLineOptions {
- "--screen", screen,
- "--project-path", AZStd::string::format(R"("%s")", projectPath.c_str()) };
- bool launchSuccess = AzFramework::ProjectManager::LaunchProjectManager(commandLineOptions);
- if (!launchSuccess)
- {
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QObject::tr("Failed to launch O3DE Project Manager"), QObject::tr("Failed to find or start the O3dE Project Manager"));
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnUndo()
- {
- //GetIEditor()->GetObjectManager()->UndoLastOp();
- GetIEditor()->Undo();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnRedo()
- {
- GetIEditor()->Redo();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnUpdateRedo(QAction* action)
- {
- if (GetIEditor()->GetUndoManager()->IsHaveRedo())
- {
- action->setEnabled(true);
- }
- else
- {
- action->setEnabled(false);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnUpdateUndo(QAction* action)
- {
- if (GetIEditor()->GetUndoManager()->IsHaveUndo())
- {
- action->setEnabled(true);
- }
- else
- {
- action->setEnabled(false);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnSwitchPhysics()
- {
- if (GetIEditor()->GetGameEngine() && !GetIEditor()->GetGameEngine()->GetSimulationMode() && !GetIEditor()->GetGameEngine()->IsLevelLoaded())
- {
- // Don't allow physics to be toggled on if we haven't loaded a level yet
- return;
- }
- QWaitCursor wait;
- GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_EDITOR_SIMULATION_MODE_SWITCH_START, 0, 0);
- uint32 flags = GetIEditor()->GetDisplaySettings()->GetSettings();
- if (flags & SETTINGS_PHYSICS)
- {
- flags &= ~SETTINGS_PHYSICS;
- }
- else
- {
- flags |= SETTINGS_PHYSICS;
- }
- GetIEditor()->GetDisplaySettings()->SetSettings(flags);
- if ((flags & SETTINGS_PHYSICS) == 0)
- {
- GetIEditor()->GetGameEngine()->SetSimulationMode(false);
- GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_EDITOR_SIMULATION_MODE_CHANGED, 0, 0);
- }
- else
- {
- GetIEditor()->GetGameEngine()->SetSimulationMode(true);
- GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_EDITOR_SIMULATION_MODE_CHANGED, 1, 0);
- }
- GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_EDITOR_SIMULATION_MODE_SWITCH_END, 0, 0);
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnSwitchPhysicsUpdate(QAction* action)
- {
- Q_ASSERT(action->isCheckable());
- action->setChecked(!m_bIsExportingLegacyData && GetIEditor()->GetGameEngine()->GetSimulationMode());
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnSyncPlayer()
- {
- GetIEditor()->GetGameEngine()->SyncPlayerPosition(!GetIEditor()->GetGameEngine()->IsSyncPlayerPosition());
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnSyncPlayerUpdate(QAction* action)
- {
- Q_ASSERT(action->isCheckable());
- action->setChecked(!GetIEditor()->GetGameEngine()->IsSyncPlayerPosition());
- }
- void CCryEditApp::OnUpdateNonGameMode(QAction* action)
- {
- action->setEnabled(!GetIEditor()->IsInGameMode());
- }
- void CCryEditApp::OnUpdateNewLevel(QAction* action)
- {
- action->setEnabled(!m_bIsExportingLegacyData);
- }
- void CCryEditApp::OnUpdatePlayGame(QAction* action)
- {
- action->setEnabled(!m_bIsExportingLegacyData && GetIEditor()->IsLevelLoaded());
- }
- //////////////////////////////////////////////////////////////////////////
- CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& templateName, const QString& levelName, QString& fullyQualifiedLevelName /* ={} */)
- {
- // If we are creating a new level and we're in simulate mode, then switch it off before we do anything else
- if (GetIEditor()->GetGameEngine() && GetIEditor()->GetGameEngine()->GetSimulationMode())
- {
- // Preserve the modified flag, we don't want this switch of physics to change that flag
- bool bIsDocModified = GetIEditor()->GetDocument()->IsModified();
- OnSwitchPhysics();
- GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified);
- auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get();
- if (rootSpawnableInterface)
- {
- rootSpawnableInterface->ProcessSpawnableQueue();
- }
- }
- const QScopedValueRollback<bool> rollback(m_creatingNewLevel);
- m_creatingNewLevel = true;
- GetIEditor()->Notify(eNotify_OnBeginCreate);
- CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorBeginCreate);
- QString currentLevel = GetIEditor()->GetLevelFolder();
- if (!currentLevel.isEmpty())
- {
- GetIEditor()->GetSystem()->GetIPak()->ClosePacks(currentLevel.toUtf8().data());
- }
- QString cryFileName = levelName.mid(levelName.lastIndexOf('/') + 1, levelName.length() - levelName.lastIndexOf('/') + 1);
- QString levelPath = QStringLiteral("%1/Levels/%2/").arg(Path::GetEditingGameDataFolder().c_str(), levelName);
- fullyQualifiedLevelName = levelPath + cryFileName + EditorUtils::LevelFile::GetDefaultFileExtension();
- //_MAX_PATH includes null terminator, so we actually want to cap at _MAX_PATH-1
- if (fullyQualifiedLevelName.length() >= _MAX_PATH-1)
- {
- GetIEditor()->Notify(eNotify_OnEndCreate);
- return ECLR_MAX_PATH_EXCEEDED;
- }
- // Does the directory already exist ?
- if (QFileInfo(levelPath).exists())
- {
- GetIEditor()->Notify(eNotify_OnEndCreate);
- return ECLR_ALREADY_EXISTS;
- }
- // Create the directory
- CLogFile::WriteLine("Creating level directory");
- if (!CFileUtil::CreatePath(levelPath))
- {
- GetIEditor()->Notify(eNotify_OnEndCreate);
- return ECLR_DIR_CREATION_FAILED;
- }
- if (GetIEditor()->GetDocument()->IsDocumentReady())
- {
- m_pDocManager->OnFileNew();
- }
- ICVar* sv_map = gEnv->pConsole->GetCVar("sv_map");
- if (sv_map)
- {
- sv_map->Set(levelName.toUtf8().data());
- }
- GetIEditor()->GetDocument()->InitEmptyLevel(128, 1);
- GetIEditor()->SetStatusText("Creating Level...");
- // Save the document to this folder
- GetIEditor()->GetDocument()->SetPathName(fullyQualifiedLevelName);
- GetIEditor()->GetGameEngine()->SetLevelPath(levelPath);
- auto* service = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
- if (service)
- {
- const AZStd::string templateNameString(templateName.toUtf8().constData());
- service->CreateNewLevelPrefab(fullyQualifiedLevelName.toUtf8().constData(), templateNameString);
- }
- if (GetIEditor()->GetDocument()->Save())
- {
- GetIEditor()->GetGameEngine()->LoadLevel(true, true);
- GetIEditor()->GetSystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0);
- GetIEditor()->GetSystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_END, 0, 0);
- }
- GetIEditor()->GetDocument()->CreateDefaultLevelAssets(128, 1);
- GetIEditor()->GetDocument()->SetDocumentReady(true);
- GetIEditor()->SetStatusText("Ready");
- // At the end of the creating level process, add this level to the MRU list
- CCryEditApp::instance()->AddToRecentFileList(fullyQualifiedLevelName);
- GetIEditor()->Notify(eNotify_OnEndCreate);
- CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorEndCreate);
- return ECLR_OK;
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnCreateLevel()
- {
- if (m_creatingNewLevel)
- {
- return;
- }
- bool wasCreateLevelOperationCancelled = false;
- bool isNewLevelCreationSuccess = false;
- // This will show the new level dialog until a valid input has been entered by the user or until the user click cancel
- while (!isNewLevelCreationSuccess && !wasCreateLevelOperationCancelled)
- {
- wasCreateLevelOperationCancelled = false;
- isNewLevelCreationSuccess = CreateLevel(wasCreateLevelOperationCancelled);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- bool CCryEditApp::CreateLevel(bool& wasCreateLevelOperationCancelled)
- {
- bool bIsDocModified = GetIEditor()->GetDocument()->IsModified();
- if (GetIEditor()->GetDocument()->IsDocumentReady() && bIsDocModified)
- {
- auto* prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
- auto* prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
- AZ_Assert(prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface is not found.");
- AZ_Assert(prefabIntegrationInterface != nullptr, "PrefabIntegrationInterface is not found.");
- if (prefabEditorEntityOwnershipInterface == nullptr || prefabIntegrationInterface == nullptr)
- {
- return false;
- }
- AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId = prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId();
- int prefabSaveSelection = prefabIntegrationInterface->HandleRootPrefabClosure(rootPrefabTemplateId);
- // In order to get the accept and reject codes of QDialog and QDialogButtonBox aligned, we do (1-prefabSaveSelection) here.
- // For example, QDialog::Rejected(0) is emitted when dialog is closed. But the int value corresponds to
- // QDialogButtonBox::AcceptRole(0).
- switch (1 - prefabSaveSelection)
- {
- case QDialogButtonBox::AcceptRole:
- bIsDocModified = false;
- break;
- case QDialogButtonBox::RejectRole:
- wasCreateLevelOperationCancelled = true;
- return false;
- case QDialogButtonBox::InvalidRole:
- // Set Modified flag to false to prevent show Save unchanged dialog again
- GetIEditor()->GetDocument()->SetModifiedFlag(false);
- break;
- }
- }
- const char* temporaryLevelName = GetIEditor()->GetDocument()->GetTemporaryLevelName();
- CNewLevelDialog dlg;
- dlg.m_level = "";
- if (dlg.exec() != QDialog::Accepted)
- {
- wasCreateLevelOperationCancelled = true;
- GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified);
- return false;
- }
- if (!GetIEditor()->GetLevelIndependentFileMan()->PromptChangedFiles())
- {
- return false;
- }
- QString levelNameWithPath = dlg.GetLevel();
- QString levelName = levelNameWithPath.mid(levelNameWithPath.lastIndexOf('/') + 1);
- if (levelName == temporaryLevelName && GetIEditor()->GetLevelName() != temporaryLevelName)
- {
- GetIEditor()->GetDocument()->DeleteTemporaryLevel();
- }
- if (levelName.length() == 0 || !AZ::StringFunc::Path::IsValid(levelName.toUtf8().data()))
- {
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QString(), QObject::tr("Level name is invalid, please choose another name."));
- return false;
- }
- //Verify that we are not using the temporary level name
- if (QString::compare(levelName, temporaryLevelName) == 0)
- {
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QString(), QObject::tr("Please enter a level name that is different from the temporary name."));
- return false;
- }
- // We're about to start creating a level, so start recording errors to display at the end.
- GetIEditor()->StartLevelErrorReportRecording();
- QString fullyQualifiedLevelName;
- ECreateLevelResult result = CreateLevel(dlg.GetTemplateName(), levelNameWithPath, fullyQualifiedLevelName);
- if (result == ECLR_ALREADY_EXISTS)
- {
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QString(), QObject::tr("Level with this name already exists, please choose another name."));
- return false;
- }
- else if (result == ECLR_DIR_CREATION_FAILED)
- {
- QString szLevelRoot = QStringLiteral("%1\\Levels\\%2").arg(Path::GetEditingGameDataFolder().c_str(), levelName);
- QByteArray windowsErrorMessage(ERROR_LEN, 0);
- QByteArray cwd(ERROR_LEN, 0);
- DWORD dw = GetLastError();
- #ifdef WIN32
- wchar_t windowsErrorMessageW[ERROR_LEN];
- windowsErrorMessageW[0] = L'\0';
- FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr,
- dw,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- windowsErrorMessageW,
- ERROR_LEN, nullptr);
- _getcwd(cwd.data(), cwd.length());
- AZStd::to_string(windowsErrorMessage.data(), ERROR_LEN, windowsErrorMessageW);
- #else
- windowsErrorMessage = strerror(dw);
- cwd = QDir::currentPath().toUtf8();
- #endif
- QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QString(), QObject::tr("Failed to create level directory: %1\n Error: %2\nCurrent Path: %3").arg(szLevelRoot, windowsErrorMessage, cwd));
- return false;
- }
- else if (result == ECLR_MAX_PATH_EXCEEDED)
- {
- QFileInfo info(fullyQualifiedLevelName);
- const AZStd::string rawProjectDirectory = Path::GetEditingGameDataFolder();
- const QString projectDirectory = QDir::toNativeSeparators(QString::fromUtf8(rawProjectDirectory.data(), static_cast<int>(rawProjectDirectory.size())));
- const QString elidedLevelName = QStringLiteral("%1...%2").arg(levelName.left(10)).arg(levelName.right(10));
- const QString elidedLevelFileName = QStringLiteral("%1...%2").arg(info.fileName().left(10)).arg(info.fileName().right(10));
- const QString message = QObject::tr(
- "The fully-qualified path for the new level exceeds the maximum supported path length of %1 characters (it's %2 characters long). Please choose a smaller name.\n\n"
- "The fully-qualified path is made up of the project folder (\"%3\", %4 characters), the \"Levels\" sub-folder, a folder named for the level (\"%5\", %6 characters) and the level file (\"%7\", %8 characters), plus necessary separators.\n\n"
- "Please also note that on most platforms, individual components of the path (folder/file names can't exceed approximately 255 characters)\n\n"
- "Click \"Copy to Clipboard\" to copy the fully-qualified name and close this message.")
- .arg(_MAX_PATH - 1).arg(fullyQualifiedLevelName.size())
- .arg(projectDirectory).arg(projectDirectory.size())
- .arg(elidedLevelName).arg(levelName.size())
- .arg(elidedLevelFileName).arg(info.fileName().size());
- QMessageBox messageBox(QMessageBox::Critical, QString(), message, QMessageBox::Ok, AzToolsFramework::GetActiveWindow());
- QPushButton* copyButton = messageBox.addButton(QObject::tr("Copy to Clipboard"), QMessageBox::ActionRole);
- QObject::connect(copyButton, &QPushButton::pressed, this, [fullyQualifiedLevelName]() { QGuiApplication::clipboard()->setText(fullyQualifiedLevelName); });
- messageBox.exec();
- return false;
- }
- // force the level being rendered at least once
- m_bForceProcessIdle = true;
- m_levelErrorsHaveBeenDisplayed = false;
- return true;
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnOpenLevel()
- {
- CLevelFileDialog levelFileDialog(true);
- levelFileDialog.show();
- levelFileDialog.adjustSize();
- if (levelFileDialog.exec() == QDialog::Accepted)
- {
- OpenDocumentFile(levelFileDialog.GetFileName().toUtf8().data(), true, COpenSameLevelOptions::ReopenLevelIfSame);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions)
- {
- if (m_openingLevel)
- {
- return GetIEditor()->GetDocument();
- }
- // If we are loading and we're in simulate mode, then switch it off before we do anything else
- if (GetIEditor()->GetGameEngine() && GetIEditor()->GetGameEngine()->GetSimulationMode())
- {
- // Preserve the modified flag, we don't want this switch of physics to change that flag
- bool bIsDocModified = GetIEditor()->GetDocument()->IsModified();
- OnSwitchPhysics();
- GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified);
- auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get();
- if (rootSpawnableInterface)
- {
- rootSpawnableInterface->ProcessSpawnableQueue();
- }
- }
- // We're about to start loading a level, so start recording errors to display at the end.
- GetIEditor()->StartLevelErrorReportRecording();
- const QScopedValueRollback<bool> rollback(m_openingLevel, true);
- MainWindow::instance()->menuBar()->setEnabled(false);
- CCryEditDoc* doc = nullptr;
- bool bVisible = false;
- bool bTriggerConsole = false;
- doc = GetIEditor()->GetDocument();
- bVisible = GetIEditor()->ShowConsole(true);
- bTriggerConsole = true;
- if (GetIEditor()->GetLevelIndependentFileMan()->PromptChangedFiles())
- {
- SandboxEditor::StartupTraceHandler openDocTraceHandler;
- openDocTraceHandler.StartCollection();
- if (m_bAutotestMode)
- {
- openDocTraceHandler.SetShowWindow(false);
- }
- // in this case, we set addToMostRecentFileList to always be true because adding files to the MRU list
- // automatically culls duplicate and normalizes paths anyway
- m_pDocManager->OpenDocumentFile(filename, addToMostRecentFileList, openSameLevelOptions);
- if (openDocTraceHandler.HasAnyErrors())
- {
- doc->SetHasErrors();
- }
- }
- if (bTriggerConsole)
- {
- GetIEditor()->ShowConsole(bVisible);
- }
- MainWindow::instance()->menuBar()->setEnabled(true);
- m_levelErrorsHaveBeenDisplayed = false;
- return doc; // the API wants a CDocument* to be returned. It seems not to be used, though, in our current state.
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnResourcesReduceworkingset()
- {
- #ifdef WIN32 // no such thing on macOS
- SetProcessWorkingSetSize(GetCurrentProcess(), std::numeric_limits<SIZE_T>::max(), std::numeric_limits<SIZE_T>::max());
- #endif
- }
- void CCryEditApp::OnUpdateWireframe(QAction* action)
- {
- Q_ASSERT(action->isCheckable());
- int nWireframe(R_SOLID_MODE);
- ICVar* r_wireframe(gEnv->pConsole->GetCVar("r_wireframe"));
- if (r_wireframe)
- {
- nWireframe = r_wireframe->GetIVal();
- }
- action->setChecked(nWireframe == R_WIREFRAME_MODE);
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnViewConfigureLayout()
- {
- if (GetIEditor()->IsInGameMode())
- {
- // you may not change your viewports while game mode is running.
- CryLog("You may not change viewport configuration while in game mode.");
- return;
- }
- CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout();
- if (layout)
- {
- CLayoutConfigDialog dlg;
- dlg.SetLayout(layout->GetLayout());
- if (dlg.exec() == QDialog::Accepted)
- {
- // Will kill this Pane. so must be last line in this function.
- layout->CreateLayout(dlg.GetLayout());
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnToolsLogMemoryUsage()
- {
- gEnv->pConsole->ExecuteString("SaveLevelStats");
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnCustomizeKeyboard()
- {
- MainWindow::instance()->OnCustomizeToolbar();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnToolsScriptHelp()
- {
- AzToolsFramework::CScriptHelpDialog::GetInstance()->show();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnViewCycle2dviewport()
- {
- GetIEditor()->GetViewManager()->Cycle2DViewport();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnDisplayGotoPosition()
- {
- GotoPositionDialog dialog;
- dialog.exec();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnFileSavelevelresources()
- {
- CGameResourcesExporter saver;
- saver.GatherAllLoadedResources();
- saver.ChooseDirectoryAndSave();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnClearRegistryData()
- {
- if (QMessageBox::warning(AzToolsFramework::GetActiveWindow(), QString(), QObject::tr("Clear all sandbox registry data ?"),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
- {
- QSettings settings;
- settings.clear();
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnToolsPreferences()
- {
- EditorPreferencesDialog dlg(MainWindow::instance());
- dlg.exec();
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnSwitchToDefaultCamera()
- {
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnUpdateSwitchToDefaultCamera(QAction* action)
- {
- Q_ASSERT(action->isCheckable());
- {
- action->setEnabled(false);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnSwitchToSequenceCamera()
- {
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnUpdateSwitchToSequenceCamera(QAction* action)
- {
- Q_ASSERT(action->isCheckable());
- {
- action->setEnabled(false);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnSwitchToSelectedcamera()
- {
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnUpdateSwitchToSelectedCamera(QAction* action)
- {
- Q_ASSERT(action->isCheckable());
- {
- action->setEnabled(false);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnSwitchcameraNext()
- {
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnOpenAssetBrowserView()
- {
- QtViewPaneManager::instance()->OpenPane(LyViewPane::AssetBrowser);
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnOpenTrackView()
- {
- QtViewPaneManager::instance()->OpenPane(LyViewPane::TrackView);
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnOpenAudioControlsEditor()
- {
- QtViewPaneManager::instance()->OpenPane(LyViewPane::AudioControlsEditor);
- }
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::OnOpenUICanvasEditor()
- {
- QtViewPaneManager::instance()->OpenPane(LyViewPane::UiEditor);
- }
- //////////////////////////////////////////////////////////////////////////
- RecentFileList* CCryEditApp::GetRecentFileList()
- {
- static RecentFileList list;
- return &list;
- };
- //////////////////////////////////////////////////////////////////////////
- void CCryEditApp::AddToRecentFileList(const QString& lpszPathName)
- {
- // In later MFC implementations (WINVER >= 0x0601) files must exist before they can be added to the recent files list.
- // Here we override the new CWinApp::AddToRecentFileList code with the old implementation to remove this requirement.
- if (IsInAutotestMode())
- {
- // Never add to the recent file list when in auto test mode
- // This would cause issues for devs running tests locally impacting their normal workflows/setups
- return;
- }
- if (GetRecentFileList())
- {
- GetRecentFileList()->Add(lpszPathName);
- }
- // write the list immediately so it will be remembered even after a crash
- if (GetRecentFileList())
- {
- GetRecentFileList()->WriteList();
- }
- else
- {
- CLogFile::WriteLine("ERROR: Recent File List is NULL!");
- }
- }
- //////////////////////////////////////////////////////////////////////////
- bool CCryEditApp::IsInRegularEditorMode()
- {
- return !IsInTestMode() && !IsInPreviewMode()
- && !IsInExportMode() && !IsInConsoleMode() && !IsInLevelLoadTestMode();
- }
- void CCryEditApp::SetEditorWindowTitle(QString sTitleStr, QString sPreTitleStr, QString sPostTitleStr)
- {
- if (MainWindow::instance() || m_pConsoleDialog)
- {
- if (sTitleStr.isEmpty())
- {
- sTitleStr = QObject::tr("O3DE Editor [%1]").arg(FormatVersion(m_pEditor->GetFileVersion()));
- }
- if (!sPreTitleStr.isEmpty())
- {
- sTitleStr.insert(sTitleStr.length(), QStringLiteral(" - %1").arg(sPreTitleStr));
- }
- if (!sPostTitleStr.isEmpty())
- {
- sTitleStr.insert(sTitleStr.length(), QStringLiteral(" - %1").arg(sPostTitleStr));
- }
- MainWindow::instance()->setWindowTitle(sTitleStr);
- if (m_pConsoleDialog)
- {
- m_pConsoleDialog->setWindowTitle(sTitleStr);
- }
- }
- }
- bool CCryEditApp::Command_ExportToEngine()
- {
- return CCryEditApp::instance()->UserExportToGame(true);
- }
- CMainFrame * CCryEditApp::GetMainFrame() const
- {
- return MainWindow::instance()->GetOldMainFrame();
- }
- void CCryEditApp::OpenExternalLuaDebugger(AZStd::string_view luaDebuggerUri, AZStd::string_view projectPath, AZStd::string_view enginePath, const char* files)
- {
- // Put together the whole Url Query String:
- QUrlQuery query;
- query.addQueryItem("projectPath", QString::fromUtf8(projectPath.data(), aznumeric_cast<int>(projectPath.size())));
- if (!enginePath.empty())
- {
- query.addQueryItem("enginePath", QString::fromUtf8(enginePath.data(), aznumeric_cast<int>(enginePath.size())));
- }
- auto ParseFilesList = [&](AZStd::string_view filePath)
- {
- bool fullPathFound = false;
- auto GetFullSourcePath = [&]
- (AzToolsFramework::AssetSystem::AssetSystemRequest* assetSystemRequests)
- {
- AZ::IO::Path assetFullPath;
- if(assetSystemRequests->GetFullSourcePathFromRelativeProductPath(filePath, assetFullPath.Native()))
- {
- fullPathFound = true;
- query.addQueryItem("files[]", QString::fromUtf8(assetFullPath.c_str()));
- }
- };
- AzToolsFramework::AssetSystemRequestBus::Broadcast(AZStd::move(GetFullSourcePath));
- // If the full source path could be found through the Asset System, then
- // attempt to resolve the path using the FileIO instance
- if (!fullPathFound)
- {
- AZ::IO::FixedMaxPath resolvedFilePath;
- if (auto fileIo = AZ::IO::FileIOBase::GetInstance();
- fileIo != nullptr && fileIo->ResolvePath(resolvedFilePath, filePath)
- && fileIo->Exists(resolvedFilePath.c_str()))
- {
- query.addQueryItem("files[]", QString::fromUtf8(resolvedFilePath.c_str()));
- }
- }
- };
- AZ::StringFunc::TokenizeVisitor(files, ParseFilesList, "|");
- QUrl luaDebuggerUrl(QString::fromUtf8(luaDebuggerUri.data(), aznumeric_cast<int>(luaDebuggerUri.size())));
- luaDebuggerUrl.setQuery(query);
- AZ_VerifyError("CCryEditApp", Platform::OpenUri(luaDebuggerUrl),
- "Failed to start external lua debugger with URI: %s", luaDebuggerUrl.toString().toUtf8().constData());
- }
- void CCryEditApp::OpenLUAEditor(const char* files)
- {
- AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath();
- AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
- auto registry = AZ::SettingsRegistry::Get();
- if (registry)
- {
- AZStd::string luaDebuggerUri;
- if (registry->Get(luaDebuggerUri, LuaDebuggerUriRegistryKey))
- {
- OpenExternalLuaDebugger(luaDebuggerUri, projectPath, enginePath, files);
- return;
- }
- }
- AZStd::string filename = "LuaIDE";
- AZ::IO::FixedMaxPath executablePath = AZ::Utils::GetExecutableDirectory();
- executablePath /= filename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION;
- if (!AZ::IO::SystemFile::Exists(executablePath.c_str()))
- {
- AZ_Error("LuaIDE", false, "%s not found", executablePath.c_str());
- return;
- }
- AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
- AZStd::vector<AZStd::string> launchCmd = { executablePath.String() };
- launchCmd.emplace_back("--engine-path");
- launchCmd.emplace_back(AZStd::string_view{ enginePath });
- launchCmd.emplace_back("--project-path");
- launchCmd.emplace_back(AZStd::string_view{ projectPath });
- launchCmd.emplace_back("--launch");
- launchCmd.emplace_back("lua");
- auto ParseFilesList = [&launchCmd](AZStd::string_view filePath)
- {
- bool fullPathFound = false;
- auto GetFullSourcePath = [&launchCmd, &filePath, &fullPathFound]
- (AzToolsFramework::AssetSystem::AssetSystemRequest* assetSystemRequests)
- {
- AZ::IO::Path assetFullPath;
- if(assetSystemRequests->GetFullSourcePathFromRelativeProductPath(filePath, assetFullPath.Native()))
- {
- fullPathFound = true;
- launchCmd.emplace_back("--files");
- launchCmd.emplace_back(AZStd::move(assetFullPath.Native()));
- }
- };
- AzToolsFramework::AssetSystemRequestBus::Broadcast(AZStd::move(GetFullSourcePath));
- // If the full source path could be found through the Asset System, then
- // attempt to resolve the path using the FileIO instance
- if (!fullPathFound)
- {
- AZ::IO::FixedMaxPath resolvedFilePath;
- if (auto fileIo = AZ::IO::FileIOBase::GetInstance();
- fileIo != nullptr && fileIo->ResolvePath(resolvedFilePath, filePath)
- && fileIo->Exists(resolvedFilePath.c_str()))
- {
- launchCmd.emplace_back("--files");
- launchCmd.emplace_back(resolvedFilePath.String());
- }
- }
- };
- AZ::StringFunc::TokenizeVisitor(files, ParseFilesList, "|");
- processLaunchInfo.m_commandlineParameters = AZStd::move(launchCmd);
- AZ_VerifyError("LuaIDE", AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo),
- "Lua IDE has failed to launch at path %s", executablePath.c_str());
- }
- void CCryEditApp::PrintAlways(const AZStd::string& output)
- {
- m_stdoutRedirection.WriteBypassingRedirect(output.c_str(), static_cast<unsigned int>(output.size()));
- }
- QString CCryEditApp::GetRootEnginePath() const
- {
- return m_rootEnginePath;
- }
- void CCryEditApp::RedirectStdoutToNull()
- {
- m_stdoutRedirection.RedirectTo(AZ::IO::SystemFile::GetNullFilename());
- }
- void CCryEditApp::OnError(AzFramework::AssetSystem::AssetSystemErrors error)
- {
- AZStd::string errorMessage = "";
- switch (error)
- {
- case AzFramework::AssetSystem::ASSETSYSTEM_FAILED_TO_LAUNCH_ASSETPROCESSOR:
- errorMessage = AZStd::string::format("Failed to start the Asset Processor.\r\nPlease make sure that AssetProcessor is available in the same folder the Editor is in.\r\n");
- break;
- case AzFramework::AssetSystem::ASSETSYSTEM_FAILED_TO_CONNECT_TO_ASSETPROCESSOR:
- errorMessage = AZStd::string::format("Failed to connect to the Asset Processor.\r\nPlease make sure that AssetProcessor is available in the same folder the Editor is in and another copy is not already running somewhere else.\r\n");
- break;
- }
- QMessageBox::critical(nullptr,"Error",errorMessage.c_str());
- }
- void CCryEditApp::OnOpenProceduralMaterialEditor()
- {
- QtViewPaneManager::instance()->OpenPane(LyViewPane::SubstanceEditor);
- }
- namespace Editor
- {
- //! This function returns the build system target name
- AZStd::string_view GetBuildTargetName()
- {
- #if !defined (LY_CMAKE_TARGET)
- #error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target"
- #endif
- return AZStd::string_view{ LY_CMAKE_TARGET };
- }
- }
- #if defined(AZ_PLATFORM_WINDOWS)
- //Due to some laptops not autoswitching to the discrete gpu correctly we are adding these
- //dllspecs as defined in the amd and nvidia white papers to 'force on' the use of the
- //discrete chips. This will be overriden by users setting application profiles
- //and may not work on older drivers or bios. In theory this should be enough to always force on
- //the discrete chips.
- //http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf
- //https://community.amd.com/thread/169965
- // It is unclear if this is also needed for linux or osx at this time(22/02/2017)
- extern "C"
- {
- __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
- __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
- }
- #endif
- #ifdef Q_OS_WIN
- #pragma comment(lib, "Shell32.lib")
- #endif
- extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
- {
- // Debugging utilities
- for (int i = 1; i < argc; ++i)
- {
- if (azstricmp(argv[i], "--attach-debugger") == 0)
- {
- AZ::Debug::Trace::AttachDebugger();
- }
- else if (azstricmp(argv[i], "--wait-for-debugger") == 0)
- {
- AZ::Debug::Trace::WaitForDebugger();
- }
- }
- // ensure the EditorEventsBus context gets created inside EditorLib
- [[maybe_unused]] const auto& editorEventsContext = AzToolsFramework::EditorEvents::Bus::GetOrCreateContext();
- // connect relevant buses to global settings
- gSettings.Connect();
- auto theApp = AZStd::make_unique<CCryEditApp>();
- // Must be set before QApplication is initialized, so that we support HighDpi monitors, like the Retina displays
- // on Windows 10
- QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
- QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
- QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
- // QtOpenGL attributes and surface format setup.
- QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
- QSurfaceFormat format = QSurfaceFormat::defaultFormat();
- format.setDepthBufferSize(24);
- format.setStencilBufferSize(8);
- format.setVersion(2, 1);
- format.setProfile(QSurfaceFormat::CoreProfile);
- format.setSamples(8);
- format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
- format.setRenderableType(QSurfaceFormat::OpenGL);
- format.setSwapInterval(0);
- #ifdef AZ_DEBUG_BUILD
- format.setOption(QSurfaceFormat::DebugContext);
- #endif
- QSurfaceFormat::setDefaultFormat(format);
- Editor::EditorQtApplication::InstallQtLogHandler();
- AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
- Editor::EditorQtApplication* app = Editor::EditorQtApplication::newInstance(argc, argv);
- QStringList qArgs = app->arguments();
- const bool is_automated_test = AZStd::any_of(qArgs.begin(), qArgs.end(),
- [](const QString& elem)
- {
- return elem.endsWith("autotest_mode") || elem.endsWith("runpythontest");
- }
- );
- if (is_automated_test)
- {
- // Nullroute all stdout to null for automated tests, this way we make sure
- // that the test result output is not polluted with unrelated output data.
- theApp->RedirectStdoutToNull();
- }
- // Hook the trace bus to catch errors, boot the AZ app after the QApplication is up
- int ret = 0;
- // open a scope to contain the AZToolsApp instance;
- {
- EditorInternal::EditorToolsApplication AZToolsApp(&argc, &argv);
- {
- CEditCommandLineInfo cmdInfo;
- if (!cmdInfo.m_bAutotestMode && !cmdInfo.m_bConsoleMode && !cmdInfo.m_bExport && !cmdInfo.m_bExportTexture &&
- !cmdInfo.m_bNullRenderer && !cmdInfo.m_bTest)
- {
- if (auto nativeUI = AZ::Interface<AZ::NativeUI::NativeUIRequests>::Get(); nativeUI != nullptr)
- {
- nativeUI->SetMode(AZ::NativeUI::Mode::ENABLED);
- }
- }
- }
- // The settings registry has been created by the AZ::ComponentApplication constructor at this point
- AZ::SettingsRegistryInterface& registry = *AZ::SettingsRegistry::Get();
- AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
- registry, Editor::GetBuildTargetName());
- AZ::Interface<AZ::IConsole>::Get()->PerformCommand("sv_isDedicated false");
- if (!AZToolsApp.Start())
- {
- return -1;
- }
- AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::NotifyQtApplicationAvailable, app);
- int exitCode = 0;
- bool didCryEditStart = CCryEditApp::instance()->InitInstance();
- AZ_Error("Editor", didCryEditStart, "O3DE Editor did not initialize correctly, and will close."
- "\nThis could be because of incorrectly configured components, or missing required gems."
- "\nSee other errors for more details.");
- AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEvents::NotifyEditorInitialized);
- if (didCryEditStart)
- {
- app->EnableOnIdle();
- ret = app->exec();
- }
- else
- {
- exitCode = 1;
- }
- CCryEditApp::instance()->ExitInstance(exitCode);
- }
- delete app;
- gSettings.Disconnect();
- return ret;
- }
- AZ_DECLARE_MODULE_INITIALIZATION
- #include <moc_CryEdit.cpp>
|