12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488 |
- /*
- * 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::OnDocumentationReleaseNotes()
- {
- QString webLink = tr("https://o3de.org/docs/release-notes/");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationGameDevBlog()
- {
- QString webLink = tr("https://o3de.org/news-blogs/");
- QDesktopServices::openUrl(QUrl(webLink));
- }
- void CCryEditApp::OnDocumentationForums()
- {
- QString webLink = tr("https://discord.com/invite/o3de");
- 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::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()));
- }
- 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>
|