1/**************************************************************************/
2/* main.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "main.h"
32
33#include "core/config/project_settings.h"
34#include "core/core_globals.h"
35#include "core/core_string_names.h"
36#include "core/crypto/crypto.h"
37#include "core/debugger/engine_debugger.h"
38#include "core/extension/extension_api_dump.h"
39#include "core/extension/gdextension_interface_dump.gen.h"
40#include "core/extension/gdextension_manager.h"
41#include "core/input/input.h"
42#include "core/input/input_map.h"
43#include "core/io/dir_access.h"
44#include "core/io/file_access_pack.h"
45#include "core/io/file_access_zip.h"
46#include "core/io/image_loader.h"
47#include "core/io/ip.h"
48#include "core/io/resource_loader.h"
49#include "core/object/message_queue.h"
50#include "core/os/os.h"
51#include "core/os/time.h"
52#include "core/register_core_types.h"
53#include "core/string/translation.h"
54#include "core/version.h"
55#include "drivers/register_driver_types.h"
56#include "main/app_icon.gen.h"
57#include "main/main_timer_sync.h"
58#include "main/performance.h"
59#include "main/splash.gen.h"
60#include "modules/register_module_types.h"
61#include "platform/register_platform_apis.h"
62#include "scene/main/scene_tree.h"
63#include "scene/main/window.h"
64#include "scene/register_scene_types.h"
65#include "scene/resources/packed_scene.h"
66#include "scene/theme/theme_db.h"
67#include "servers/audio_server.h"
68#include "servers/camera_server.h"
69#include "servers/display_server.h"
70#include "servers/movie_writer/movie_writer.h"
71#include "servers/movie_writer/movie_writer_mjpeg.h"
72#include "servers/navigation_server_2d.h"
73#include "servers/navigation_server_3d.h"
74#include "servers/navigation_server_3d_dummy.h"
75#include "servers/physics_server_2d.h"
76#include "servers/physics_server_3d.h"
77#include "servers/register_server_types.h"
78#include "servers/rendering/rendering_server_default.h"
79#include "servers/text/text_server_dummy.h"
80#include "servers/text_server.h"
81#include "servers/xr_server.h"
82
83#ifdef TESTS_ENABLED
84#include "tests/test_main.h"
85#endif
86
87#ifdef TOOLS_ENABLED
88#include "editor/debugger/editor_debugger_node.h"
89#include "editor/doc_data_class_path.gen.h"
90#include "editor/doc_tools.h"
91#include "editor/editor_help.h"
92#include "editor/editor_node.h"
93#include "editor/editor_paths.h"
94#include "editor/editor_settings.h"
95#include "editor/editor_translation.h"
96#include "editor/progress_dialog.h"
97#include "editor/project_manager.h"
98#include "editor/register_editor_types.h"
99
100#ifndef NO_EDITOR_SPLASH
101#include "main/splash_editor.gen.h"
102#endif
103
104#ifndef DISABLE_DEPRECATED
105#include "editor/project_converter_3_to_4.h"
106#endif // DISABLE_DEPRECATED
107#endif // TOOLS_ENABLED
108
109#include "modules/modules_enabled.gen.h" // For mono.
110
111#if defined(MODULE_MONO_ENABLED) && defined(TOOLS_ENABLED)
112#include "modules/mono/editor/bindings_generator.h"
113#endif
114
115#ifdef MODULE_GDSCRIPT_ENABLED
116#include "modules/gdscript/gdscript.h"
117#endif
118
119/* Static members */
120
121// Singletons
122
123// Initialized in setup()
124static Engine *engine = nullptr;
125static ProjectSettings *globals = nullptr;
126static Input *input = nullptr;
127static InputMap *input_map = nullptr;
128static TranslationServer *translation_server = nullptr;
129static Performance *performance = nullptr;
130static PackedData *packed_data = nullptr;
131static Time *time_singleton = nullptr;
132#ifdef MINIZIP_ENABLED
133static ZipArchive *zip_packed_data = nullptr;
134#endif
135static MessageQueue *message_queue = nullptr;
136
137// Initialized in setup2()
138static AudioServer *audio_server = nullptr;
139static DisplayServer *display_server = nullptr;
140static RenderingServer *rendering_server = nullptr;
141static CameraServer *camera_server = nullptr;
142static XRServer *xr_server = nullptr;
143static TextServerManager *tsman = nullptr;
144static PhysicsServer3DManager *physics_server_3d_manager = nullptr;
145static PhysicsServer3D *physics_server_3d = nullptr;
146static PhysicsServer2DManager *physics_server_2d_manager = nullptr;
147static PhysicsServer2D *physics_server_2d = nullptr;
148static NavigationServer3D *navigation_server_3d = nullptr;
149static NavigationServer2D *navigation_server_2d = nullptr;
150static ThemeDB *theme_db = nullptr;
151// We error out if setup2() doesn't turn this true
152static bool _start_success = false;
153
154// Drivers
155
156String tablet_driver = "";
157String text_driver = "";
158String rendering_driver = "";
159String rendering_method = "";
160static int text_driver_idx = -1;
161static int display_driver_idx = -1;
162static int audio_driver_idx = -1;
163
164// Engine config/tools
165
166static bool single_window = false;
167static bool editor = false;
168static bool project_manager = false;
169static bool cmdline_tool = false;
170static String locale;
171static bool show_help = false;
172static uint64_t quit_after = 0;
173static OS::ProcessID editor_pid = 0;
174#ifdef TOOLS_ENABLED
175static bool found_project = false;
176static bool auto_build_solutions = false;
177static String debug_server_uri;
178#ifndef DISABLE_DEPRECATED
179static int converter_max_kb_file = 4 * 1024; // 4MB
180static int converter_max_line_length = 100000;
181#endif // DISABLE_DEPRECATED
182
183HashMap<Main::CLIScope, Vector<String>> forwardable_cli_arguments;
184#endif
185static bool single_threaded_scene = false;
186
187// Display
188
189static DisplayServer::WindowMode window_mode = DisplayServer::WINDOW_MODE_WINDOWED;
190static DisplayServer::ScreenOrientation window_orientation = DisplayServer::SCREEN_LANDSCAPE;
191static DisplayServer::VSyncMode window_vsync_mode = DisplayServer::VSYNC_ENABLED;
192static uint32_t window_flags = 0;
193static Size2i window_size = Size2i(1152, 648);
194
195static int init_screen = DisplayServer::SCREEN_PRIMARY;
196static bool init_fullscreen = false;
197static bool init_maximized = false;
198static bool init_windowed = false;
199static bool init_always_on_top = false;
200static bool init_use_custom_pos = false;
201static bool init_use_custom_screen = false;
202static Vector2 init_custom_pos;
203
204// Debug
205
206static bool use_debug_profiler = false;
207#ifdef DEBUG_ENABLED
208static bool debug_collisions = false;
209static bool debug_paths = false;
210static bool debug_navigation = false;
211static bool debug_avoidance = false;
212#endif
213static int max_fps = -1;
214static int frame_delay = 0;
215static int audio_output_latency = 0;
216static bool disable_render_loop = false;
217static int fixed_fps = -1;
218static MovieWriter *movie_writer = nullptr;
219static bool disable_vsync = false;
220static bool print_fps = false;
221#ifdef TOOLS_ENABLED
222static bool dump_gdextension_interface = false;
223static bool dump_extension_api = false;
224static bool validate_extension_api = false;
225static String validate_extension_api_file;
226#endif
227bool profile_gpu = false;
228
229// Constants.
230
231static const String NULL_DISPLAY_DRIVER("headless");
232static const String NULL_AUDIO_DRIVER("Dummy");
233
234/* Helper methods */
235
236bool Main::is_cmdline_tool() {
237 return cmdline_tool;
238}
239
240#ifdef TOOLS_ENABLED
241const Vector<String> &Main::get_forwardable_cli_arguments(Main::CLIScope p_scope) {
242 return forwardable_cli_arguments[p_scope];
243}
244#endif
245
246static String unescape_cmdline(const String &p_str) {
247 return p_str.replace("%20", " ");
248}
249
250static String get_full_version_string() {
251 String hash = String(VERSION_HASH);
252 if (!hash.is_empty()) {
253 hash = "." + hash.left(9);
254 }
255 return String(VERSION_FULL_BUILD) + hash;
256}
257
258#if defined(TOOLS_ENABLED) && defined(MODULE_GDSCRIPT_ENABLED)
259static Vector<String> get_files_with_extension(const String &p_root, const String &p_extension) {
260 Vector<String> paths;
261
262 Ref<DirAccess> dir = DirAccess::open(p_root);
263 if (dir.is_valid()) {
264 dir->list_dir_begin();
265 String fn = dir->get_next();
266 while (!fn.is_empty()) {
267 if (!dir->current_is_hidden() && fn != "." && fn != "..") {
268 if (dir->current_is_dir()) {
269 paths.append_array(get_files_with_extension(p_root.path_join(fn), p_extension));
270 } else if (fn.get_extension() == p_extension) {
271 paths.append(p_root.path_join(fn));
272 }
273 }
274 fn = dir->get_next();
275 }
276 dir->list_dir_end();
277 }
278
279 return paths;
280}
281#endif
282
283// FIXME: Could maybe be moved to have less code in main.cpp.
284void initialize_physics() {
285 /// 3D Physics Server
286 physics_server_3d = PhysicsServer3DManager::get_singleton()->new_server(
287 GLOBAL_GET(PhysicsServer3DManager::setting_property_name));
288 if (!physics_server_3d) {
289 // Physics server not found, Use the default physics
290 physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server();
291 }
292 ERR_FAIL_NULL(physics_server_3d);
293 physics_server_3d->init();
294
295 // 2D Physics server
296 physics_server_2d = PhysicsServer2DManager::get_singleton()->new_server(
297 GLOBAL_GET(PhysicsServer2DManager::get_singleton()->setting_property_name));
298 if (!physics_server_2d) {
299 // Physics server not found, Use the default physics
300 physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server();
301 }
302 ERR_FAIL_NULL(physics_server_2d);
303 physics_server_2d->init();
304}
305
306void finalize_physics() {
307 physics_server_3d->finish();
308 memdelete(physics_server_3d);
309
310 physics_server_2d->finish();
311 memdelete(physics_server_2d);
312}
313
314void finalize_display() {
315 rendering_server->finish();
316 memdelete(rendering_server);
317
318 memdelete(display_server);
319}
320
321void initialize_navigation_server() {
322 ERR_FAIL_COND(navigation_server_3d != nullptr);
323 ERR_FAIL_COND(navigation_server_2d != nullptr);
324
325 // Init 3D Navigation Server
326 navigation_server_3d = NavigationServer3DManager::new_default_server();
327
328 // Fall back to dummy if no default server has been registered.
329 if (!navigation_server_3d) {
330 WARN_PRINT_ONCE("No NavigationServer3D implementation has been registered! Falling back to a dummy implementation: navigation features will be unavailable.");
331 navigation_server_3d = memnew(NavigationServer3DDummy);
332 }
333
334 // Should be impossible, but make sure it's not null.
335 ERR_FAIL_NULL_MSG(navigation_server_3d, "Failed to initialize NavigationServer3D.");
336 navigation_server_3d->init();
337
338 // Init 2D Navigation Server
339 navigation_server_2d = memnew(NavigationServer2D);
340 ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D.");
341}
342
343void finalize_navigation_server() {
344 ERR_FAIL_NULL(navigation_server_3d);
345 navigation_server_3d->finish();
346 memdelete(navigation_server_3d);
347 navigation_server_3d = nullptr;
348
349 ERR_FAIL_NULL(navigation_server_2d);
350 memdelete(navigation_server_2d);
351 navigation_server_2d = nullptr;
352}
353
354void initialize_theme_db() {
355 theme_db = memnew(ThemeDB);
356}
357
358void finalize_theme_db() {
359 memdelete(theme_db);
360 theme_db = nullptr;
361}
362
363//#define DEBUG_INIT
364#ifdef DEBUG_INIT
365#define MAIN_PRINT(m_txt) print_line(m_txt)
366#else
367#define MAIN_PRINT(m_txt)
368#endif
369
370void Main::print_help(const char *p_binary) {
371 print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
372 OS::get_singleton()->print("Free and open source software under the terms of the MIT license.\n");
373 OS::get_singleton()->print("(c) 2014-present Godot Engine contributors.\n");
374 OS::get_singleton()->print("(c) 2007-2014 Juan Linietsky, Ariel Manzur.\n");
375 OS::get_singleton()->print("\n");
376 OS::get_singleton()->print("Usage: %s [options] [path to scene or 'project.godot' file]\n", p_binary);
377 OS::get_singleton()->print("\n");
378
379 OS::get_singleton()->print("General options:\n");
380 OS::get_singleton()->print(" -h, --help Display this help message.\n");
381 OS::get_singleton()->print(" --version Display the version string.\n");
382 OS::get_singleton()->print(" -v, --verbose Use verbose stdout mode.\n");
383 OS::get_singleton()->print(" -q, --quiet Quiet mode, silences stdout messages. Errors are still displayed.\n");
384 OS::get_singleton()->print("\n");
385
386 OS::get_singleton()->print("Run options:\n");
387 OS::get_singleton()->print(" --, ++ Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.\n");
388#ifdef TOOLS_ENABLED
389 OS::get_singleton()->print(" -e, --editor Start the editor instead of running the scene.\n");
390 OS::get_singleton()->print(" -p, --project-manager Start the project manager, even if a project is auto-detected.\n");
391 OS::get_singleton()->print(" --debug-server <uri> Start the editor debug server (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007)\n");
392#endif
393 OS::get_singleton()->print(" --quit Quit after the first iteration.\n");
394 OS::get_singleton()->print(" --quit-after <int> Quit after the given number of iterations. Set to 0 to disable.\n");
395 OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n");
396 OS::get_singleton()->print(" --path <directory> Path to a project (<directory> must contain a 'project.godot' file).\n");
397 OS::get_singleton()->print(" -u, --upwards Scan folders upwards for project.godot file.\n");
398 OS::get_singleton()->print(" --main-pack <file> Path to a pack (.pck) file to load.\n");
399 OS::get_singleton()->print(" --render-thread <mode> Render thread mode ['unsafe', 'safe', 'separate'].\n");
400 OS::get_singleton()->print(" --remote-fs <address> Remote filesystem (<host/IP>[:<port>] address).\n");
401 OS::get_singleton()->print(" --remote-fs-password <password> Password for remote filesystem.\n");
402
403 OS::get_singleton()->print(" --audio-driver <driver> Audio driver [");
404 for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
405 if (i > 0) {
406 OS::get_singleton()->print(", ");
407 }
408 OS::get_singleton()->print("'%s'", AudioDriverManager::get_driver(i)->get_name());
409 }
410 OS::get_singleton()->print("].\n");
411
412 OS::get_singleton()->print(" --display-driver <driver> Display driver (and rendering driver) [");
413 for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
414 if (i > 0) {
415 OS::get_singleton()->print(", ");
416 }
417 OS::get_singleton()->print("'%s' (", DisplayServer::get_create_function_name(i));
418 Vector<String> rd = DisplayServer::get_create_function_rendering_drivers(i);
419 for (int j = 0; j < rd.size(); j++) {
420 if (j > 0) {
421 OS::get_singleton()->print(", ");
422 }
423 OS::get_singleton()->print("'%s'", rd[j].utf8().get_data());
424 }
425 OS::get_singleton()->print(")");
426 }
427 OS::get_singleton()->print("].\n");
428 OS::get_singleton()->print(" --audio-output-latency <ms> Override audio output latency in milliseconds (default is 15 ms).\n");
429 OS::get_singleton()->print(" Lower values make sound playback more reactive but increase CPU usage, and may result in audio cracking if the CPU can't keep up.\n");
430
431 OS::get_singleton()->print(" --rendering-method <renderer> Renderer name. Requires driver support.\n");
432 OS::get_singleton()->print(" --rendering-driver <driver> Rendering driver (depends on display driver).\n");
433 OS::get_singleton()->print(" --gpu-index <device_index> Use a specific GPU (run with --verbose to get available device list).\n");
434 OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping).\n");
435 OS::get_singleton()->print(" --tablet-driver <driver> Pen tablet input driver.\n");
436 OS::get_singleton()->print(" --headless Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.\n");
437 OS::get_singleton()->print(" --write-movie <file> Writes a video to the specified path (usually with .avi or .png extension).\n");
438 OS::get_singleton()->print(" --fixed-fps is forced when enabled, but it can be used to change movie FPS.\n");
439 OS::get_singleton()->print(" --disable-vsync can speed up movie writing but makes interaction more difficult.\n");
440 OS::get_singleton()->print(" --quit-after can be used to specify the number of frames to write.\n");
441
442 OS::get_singleton()->print("\n");
443
444 OS::get_singleton()->print("Display options:\n");
445 OS::get_singleton()->print(" -f, --fullscreen Request fullscreen mode.\n");
446 OS::get_singleton()->print(" -m, --maximized Request a maximized window.\n");
447 OS::get_singleton()->print(" -w, --windowed Request windowed mode.\n");
448 OS::get_singleton()->print(" -t, --always-on-top Request an always-on-top window.\n");
449 OS::get_singleton()->print(" --resolution <W>x<H> Request window resolution.\n");
450 OS::get_singleton()->print(" --position <X>,<Y> Request window position (if set, screen argument is ignored).\n");
451 OS::get_singleton()->print(" --screen <N> Request window screen.\n");
452 OS::get_singleton()->print(" --single-window Use a single window (no separate subwindows).\n");
453 OS::get_singleton()->print(" --xr-mode <mode> Select XR (Extended Reality) mode ['default', 'off', 'on'].\n");
454 OS::get_singleton()->print("\n");
455
456 OS::get_singleton()->print("Debug options:\n");
457 OS::get_singleton()->print(" -d, --debug Debug (local stdout debugger).\n");
458 OS::get_singleton()->print(" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
459 OS::get_singleton()->print(" --profiling Enable profiling in the script debugger.\n");
460 OS::get_singleton()->print(" --gpu-profile Show a GPU profile of the tasks that took the most time during frame rendering.\n");
461 OS::get_singleton()->print(" --gpu-validation Enable graphics API validation layers for debugging.\n");
462#if DEBUG_ENABLED
463 OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n");
464#endif
465 OS::get_singleton()->print(" --generate-spirv-debug-info Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
466 OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
467 OS::get_singleton()->print(" --single-threaded-scene Scene tree runs in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
468#if defined(DEBUG_ENABLED)
469 OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n");
470 OS::get_singleton()->print(" --debug-paths Show path lines when running the scene.\n");
471 OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n");
472 OS::get_singleton()->print(" --debug-avoidance Show navigation avoidance debug visuals when running the scene.\n");
473 OS::get_singleton()->print(" --debug-stringnames Print all StringName allocations to stdout when the engine quits.\n");
474#endif
475 OS::get_singleton()->print(" --max-fps <fps> Set a maximum number of frames per second rendered (can be used to limit power usage). A value of 0 results in unlimited framerate.\n");
476 OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds). Do not use as a FPS limiter; use --max-fps instead.\n");
477 OS::get_singleton()->print(" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n");
478 OS::get_singleton()->print(" --disable-vsync Forces disabling of vertical synchronization, even if enabled in the project settings. Does not override driver-level V-Sync enforcement.\n");
479 OS::get_singleton()->print(" --disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.\n");
480 OS::get_singleton()->print(" --disable-crash-handler Disable crash handler when supported by the platform code.\n");
481 OS::get_singleton()->print(" --fixed-fps <fps> Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
482 OS::get_singleton()->print(" --delta-smoothing <enable> Enable or disable frame delta smoothing ['enable', 'disable'].\n");
483 OS::get_singleton()->print(" --print-fps Print the frames per second to the stdout.\n");
484 OS::get_singleton()->print("\n");
485
486 OS::get_singleton()->print("Standalone tools:\n");
487 OS::get_singleton()->print(" -s, --script <script> Run a script.\n");
488 OS::get_singleton()->print(" --main-loop <main_loop_name> Run a MainLoop specified by its global class name.\n");
489 OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n");
490#ifdef TOOLS_ENABLED
491 OS::get_singleton()->print(" --export-release <preset> <path> Export the project in release mode using the given preset and output path. The preset name should match one defined in export_presets.cfg.\n");
492 OS::get_singleton()->print(" <path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe').\n");
493 OS::get_singleton()->print(" The target directory must exist.\n");
494 OS::get_singleton()->print(" --export-debug <preset> <path> Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n");
495 OS::get_singleton()->print(" --export-pack <preset> <path> Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n");
496#ifndef DISABLE_DEPRECATED
497 OS::get_singleton()->print(" --convert-3to4 [<max_file_kb>] [<max_line_size>]\n");
498 OS::get_singleton()->print(" Converts project from Godot 3.x to Godot 4.x.\n");
499 OS::get_singleton()->print(" --validate-conversion-3to4 [<max_file_kb>] [<max_line_size>]\n");
500 OS::get_singleton()->print(" Shows what elements will be renamed when converting project from Godot 3.x to Godot 4.x.\n");
501#endif // DISABLE_DEPRECATED
502 OS::get_singleton()->print(" --doctool [<path>] Dump the engine API reference to the given <path> (defaults to current dir) in XML format, merging if existing files are found.\n");
503 OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
504#ifdef MODULE_GDSCRIPT_ENABLED
505 OS::get_singleton()->print(" --gdscript-docs <path> Rather than dumping the engine API, generate API reference from the inline documentation in the GDScript files found in <path> (used with --doctool).\n");
506#endif
507 OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
508 OS::get_singleton()->print(" --dump-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.\n");
509 OS::get_singleton()->print(" --dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.\n");
510 OS::get_singleton()->print(" --validate-extension-api <path> Validate an extension API file dumped (with the option above) from a previous version of the engine to ensure API compatibility. If incompatibilities or errors are detected, the return code will be non zero.\n");
511 OS::get_singleton()->print(" --benchmark Benchmark the run time and print it to console.\n");
512 OS::get_singleton()->print(" --benchmark-file <path> Benchmark the run time and save it to a given file in JSON format. The path should be absolute.\n");
513#ifdef TESTS_ENABLED
514 OS::get_singleton()->print(" --test [--help] Run unit tests. Use --test --help for more information.\n");
515#endif
516#endif
517 OS::get_singleton()->print("\n");
518}
519
520#ifdef TESTS_ENABLED
521// The order is the same as in `Main::setup()`, only core and some editor types
522// are initialized here. This also combines `Main::setup2()` initialization.
523Error Main::test_setup() {
524 Thread::make_main_thread();
525 set_current_thread_safe_for_nodes(true);
526
527 OS::get_singleton()->initialize();
528
529 engine = memnew(Engine);
530
531 register_core_types();
532 register_core_driver_types();
533
534 packed_data = memnew(PackedData);
535
536 globals = memnew(ProjectSettings);
537
538 register_core_settings(); // Here globals are present.
539
540 translation_server = memnew(TranslationServer);
541 tsman = memnew(TextServerManager);
542
543 if (tsman) {
544 Ref<TextServerDummy> ts;
545 ts.instantiate();
546 tsman->add_interface(ts);
547 }
548
549 physics_server_3d_manager = memnew(PhysicsServer3DManager);
550 physics_server_2d_manager = memnew(PhysicsServer2DManager);
551
552 // From `Main::setup2()`.
553 initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
554 register_core_extensions();
555
556 register_core_singletons();
557
558 /** INITIALIZE SERVERS **/
559 register_server_types();
560 XRServer::set_xr_mode(XRServer::XRMODE_OFF); // Skip in tests.
561 initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
562 GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
563
564 translation_server->setup(); //register translations, load them, etc.
565 if (!locale.is_empty()) {
566 translation_server->set_locale(locale);
567 }
568 translation_server->load_translations();
569 ResourceLoader::load_translation_remaps(); //load remaps for resources
570
571 ResourceLoader::load_path_remaps();
572
573 // Initialize ThemeDB early so that scene types can register their theme items.
574 // Default theme will be initialized later, after modules and ScriptServer are ready.
575 initialize_theme_db();
576
577 register_scene_types();
578 register_driver_types();
579
580 register_scene_singletons();
581
582 initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
583 GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
584
585#ifdef TOOLS_ENABLED
586 ClassDB::set_current_api(ClassDB::API_EDITOR);
587 register_editor_types();
588
589 initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
590 GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
591
592 ClassDB::set_current_api(ClassDB::API_CORE);
593#endif
594 register_platform_apis();
595
596 // Theme needs modules to be initialized so that sub-resources can be loaded.
597 theme_db->initialize_theme_noproject();
598
599 initialize_navigation_server();
600
601 ERR_FAIL_COND_V(TextServerManager::get_singleton()->get_interface_count() == 0, ERR_CANT_CREATE);
602
603 /* Use one with the most features available. */
604 int max_features = 0;
605 for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
606 uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features();
607 int feature_number = 0;
608 while (features) {
609 feature_number += features & 1;
610 features >>= 1;
611 }
612 if (feature_number >= max_features) {
613 max_features = feature_number;
614 text_driver_idx = i;
615 }
616 }
617 if (text_driver_idx >= 0) {
618 Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(text_driver_idx);
619 TextServerManager::get_singleton()->set_primary_interface(ts);
620 if (ts->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
621 ts->load_support_data("res://" + ts->get_support_data_filename());
622 }
623 } else {
624 ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface.");
625 }
626
627 ClassDB::set_current_api(ClassDB::API_NONE);
628
629 _start_success = true;
630
631 return OK;
632}
633// The order is the same as in `Main::cleanup()`.
634void Main::test_cleanup() {
635 ERR_FAIL_COND(!_start_success);
636
637 for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
638 TextServerManager::get_singleton()->get_interface(i)->cleanup();
639 }
640
641 ResourceLoader::remove_custom_loaders();
642 ResourceSaver::remove_custom_savers();
643
644#ifdef TOOLS_ENABLED
645 GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
646 uninitialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
647 unregister_editor_types();
648#endif
649
650 GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
651 uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
652
653 unregister_platform_apis();
654 unregister_driver_types();
655 unregister_scene_types();
656
657 finalize_theme_db();
658
659 finalize_navigation_server();
660
661 GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
662 uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
663 unregister_server_types();
664
665 EngineDebugger::deinitialize();
666 OS::get_singleton()->finalize();
667
668 if (packed_data) {
669 memdelete(packed_data);
670 }
671 if (translation_server) {
672 memdelete(translation_server);
673 }
674 if (tsman) {
675 memdelete(tsman);
676 }
677 if (physics_server_3d_manager) {
678 memdelete(physics_server_3d_manager);
679 }
680 if (physics_server_2d_manager) {
681 memdelete(physics_server_2d_manager);
682 }
683 if (globals) {
684 memdelete(globals);
685 }
686 if (engine) {
687 memdelete(engine);
688 }
689
690 unregister_core_driver_types();
691 unregister_core_extensions();
692 uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
693 unregister_core_types();
694
695 OS::get_singleton()->finalize_core();
696}
697#endif
698
699int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) {
700#ifdef TESTS_ENABLED
701 for (int x = 0; x < argc; x++) {
702 if ((strncmp(argv[x], "--test", 6) == 0) && (strlen(argv[x]) == 6)) {
703 tests_need_run = true;
704 // TODO: need to come up with different test contexts.
705 // Not every test requires high-level functionality like `ClassDB`.
706 test_setup();
707 int status = test_main(argc, argv);
708 test_cleanup();
709 return status;
710 }
711 }
712#endif
713 tests_need_run = false;
714 return 0;
715}
716
717/* Engine initialization
718 *
719 * Consists of several methods that are called by each platform's specific main(argc, argv).
720 * To fully understand engine init, one should therefore start from the platform's main and
721 * see how it calls into the Main class' methods.
722 *
723 * The initialization is typically done in 3 steps (with the setup2 step triggered either
724 * automatically by setup, or manually in the platform's main).
725 *
726 * - setup(execpath, argc, argv, p_second_phase) is the main entry point for all platforms,
727 * responsible for the initialization of all low level singletons and core types, and parsing
728 * command line arguments to configure things accordingly.
729 * If p_second_phase is true, it will chain into setup2() (default behavior). This is
730 * disabled on some platforms (Android, iOS) which trigger the second step in their own time.
731 *
732 * - setup2(p_main_tid_override) registers high level servers and singletons, displays the
733 * boot splash, then registers higher level types (scene, editor, etc.).
734 *
735 * - start() is the last step and that's where command line tools can run, or the main loop
736 * can be created eventually and the project settings put into action. That's also where
737 * the editor node is created, if relevant.
738 * start() does it own argument parsing for a subset of the command line arguments described
739 * in help, it's a bit messy and should be globalized with the setup() parsing somehow.
740 */
741
742Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_phase) {
743 Thread::make_main_thread();
744 set_current_thread_safe_for_nodes(true);
745
746 OS::get_singleton()->initialize();
747
748 // Benchmark tracking must be done after `OS::get_singleton()->initialize()` as on some
749 // platforms, it's used to set up the time utilities.
750 OS::get_singleton()->benchmark_begin_measure("startup_begin");
751
752 engine = memnew(Engine);
753
754 MAIN_PRINT("Main: Initialize CORE");
755 OS::get_singleton()->benchmark_begin_measure("core");
756
757 register_core_types();
758 register_core_driver_types();
759
760 MAIN_PRINT("Main: Initialize Globals");
761
762 input_map = memnew(InputMap);
763 time_singleton = memnew(Time);
764 globals = memnew(ProjectSettings);
765
766 register_core_settings(); //here globals are present
767
768 translation_server = memnew(TranslationServer);
769 performance = memnew(Performance);
770 GDREGISTER_CLASS(Performance);
771 engine->add_singleton(Engine::Singleton("Performance", performance));
772
773 // Only flush stdout in debug builds by default, as spamming `print()` will
774 // decrease performance if this is enabled.
775 GLOBAL_DEF_RST("application/run/flush_stdout_on_print", false);
776 GLOBAL_DEF_RST("application/run/flush_stdout_on_print.debug", true);
777
778 MAIN_PRINT("Main: Parse CMDLine");
779
780 /* argument parsing and main creation */
781 List<String> args;
782 List<String> main_args;
783 List<String> user_args;
784 bool adding_user_args = false;
785 List<String> platform_args = OS::get_singleton()->get_cmdline_platform_args();
786
787 // Add command line arguments.
788 for (int i = 0; i < argc; i++) {
789 args.push_back(String::utf8(argv[i]));
790 }
791
792 // Add arguments received from macOS LaunchService (URL schemas, file associations).
793 for (const String &arg : platform_args) {
794 args.push_back(arg);
795 }
796
797 List<String>::Element *I = args.front();
798
799 while (I) {
800 I->get() = unescape_cmdline(I->get().strip_edges());
801 I = I->next();
802 }
803
804 String display_driver = "";
805 String audio_driver = "";
806 String project_path = ".";
807 bool upwards = false;
808 String debug_uri = "";
809 bool skip_breakpoints = false;
810 String main_pack;
811 bool quiet_stdout = false;
812 int rtm = -1;
813
814 String remotefs;
815 String remotefs_pass;
816
817 Vector<String> breakpoints;
818 bool use_custom_res = true;
819 bool force_res = false;
820 bool delta_smoothing_override = false;
821
822 String default_renderer = "";
823 String default_renderer_mobile = "";
824 String renderer_hints = "";
825
826 packed_data = PackedData::get_singleton();
827 if (!packed_data) {
828 packed_data = memnew(PackedData);
829 }
830
831#ifdef MINIZIP_ENABLED
832
833 //XXX: always get_singleton() == 0x0
834 zip_packed_data = ZipArchive::get_singleton();
835 //TODO: remove this temporary fix
836 if (!zip_packed_data) {
837 zip_packed_data = memnew(ZipArchive);
838 }
839
840 packed_data->add_pack_source(zip_packed_data);
841#endif
842
843 // Default exit code, can be modified for certain errors.
844 Error exit_code = ERR_INVALID_PARAMETER;
845
846 I = args.front();
847 while (I) {
848#ifdef MACOS_ENABLED
849 // Ignore the process serial number argument passed by macOS Gatekeeper.
850 // Otherwise, Godot would try to open a non-existent project on the first start and abort.
851 if (I->get().begins_with("-psn_")) {
852 I = I->next();
853 continue;
854 }
855#endif
856
857 List<String>::Element *N = I->next();
858
859#ifdef TOOLS_ENABLED
860 if (I->get() == "--debug" ||
861 I->get() == "--verbose" ||
862 I->get() == "--disable-crash-handler") {
863 forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->get());
864 forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->get());
865 }
866 if (I->get() == "--single-window") {
867 forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->get());
868 }
869 if (I->get() == "--audio-driver" ||
870 I->get() == "--display-driver" ||
871 I->get() == "--rendering-method" ||
872 I->get() == "--rendering-driver") {
873 if (I->next()) {
874 forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->get());
875 forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get());
876 }
877 }
878 // If gpu is specified, both editor and debug instances started from editor will inherit.
879 if (I->get() == "--gpu-index") {
880 if (I->next()) {
881 forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->get());
882 forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get());
883 forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->get());
884 forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->next()->get());
885 }
886 }
887#endif
888
889 if (adding_user_args) {
890 user_args.push_back(I->get());
891 } else if (I->get() == "-h" || I->get() == "--help" || I->get() == "/?") { // display help
892
893 show_help = true;
894 exit_code = ERR_HELP; // Hack to force an early exit in `main()` with a success code.
895 goto error;
896
897 } else if (I->get() == "--version") {
898 print_line(get_full_version_string());
899 exit_code = ERR_HELP; // Hack to force an early exit in `main()` with a success code.
900 goto error;
901
902 } else if (I->get() == "-v" || I->get() == "--verbose") { // verbose output
903
904 OS::get_singleton()->_verbose_stdout = true;
905 } else if (I->get() == "-q" || I->get() == "--quiet") { // quieter output
906
907 quiet_stdout = true;
908
909 } else if (I->get() == "--audio-driver") { // audio driver
910
911 if (I->next()) {
912 audio_driver = I->next()->get();
913
914 bool found = false;
915 for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
916 if (audio_driver == AudioDriverManager::get_driver(i)->get_name()) {
917 found = true;
918 }
919 }
920
921 if (!found) {
922 OS::get_singleton()->print("Unknown audio driver '%s', aborting.\nValid options are ",
923 audio_driver.utf8().get_data());
924
925 for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
926 if (i == AudioDriverManager::get_driver_count() - 1) {
927 OS::get_singleton()->print(" and ");
928 } else if (i != 0) {
929 OS::get_singleton()->print(", ");
930 }
931
932 OS::get_singleton()->print("'%s'", AudioDriverManager::get_driver(i)->get_name());
933 }
934
935 OS::get_singleton()->print(".\n");
936
937 goto error;
938 }
939
940 N = I->next()->next();
941 } else {
942 OS::get_singleton()->print("Missing audio driver argument, aborting.\n");
943 goto error;
944 }
945 } else if (I->get() == "--audio-output-latency") {
946 if (I->next()) {
947 audio_output_latency = I->next()->get().to_int();
948 N = I->next()->next();
949 } else {
950 OS::get_singleton()->print("Missing audio output latency argument, aborting.\n");
951 goto error;
952 }
953 } else if (I->get() == "--text-driver") {
954 if (I->next()) {
955 text_driver = I->next()->get();
956 N = I->next()->next();
957 } else {
958 OS::get_singleton()->print("Missing text driver argument, aborting.\n");
959 goto error;
960 }
961
962 } else if (I->get() == "--display-driver") { // force video driver
963
964 if (I->next()) {
965 display_driver = I->next()->get();
966
967 bool found = false;
968 for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
969 if (display_driver == DisplayServer::get_create_function_name(i)) {
970 found = true;
971 }
972 }
973
974 if (!found) {
975 OS::get_singleton()->print("Unknown display driver '%s', aborting.\nValid options are ",
976 display_driver.utf8().get_data());
977
978 for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
979 if (i == DisplayServer::get_create_function_count() - 1) {
980 OS::get_singleton()->print(" and ");
981 } else if (i != 0) {
982 OS::get_singleton()->print(", ");
983 }
984
985 OS::get_singleton()->print("'%s'", DisplayServer::get_create_function_name(i));
986 }
987
988 OS::get_singleton()->print(".\n");
989
990 goto error;
991 }
992
993 N = I->next()->next();
994 } else {
995 OS::get_singleton()->print("Missing display driver argument, aborting.\n");
996 goto error;
997 }
998 } else if (I->get() == "--rendering-method") {
999 if (I->next()) {
1000 rendering_method = I->next()->get();
1001 N = I->next()->next();
1002 } else {
1003 OS::get_singleton()->print("Missing renderer name argument, aborting.\n");
1004 goto error;
1005 }
1006 } else if (I->get() == "--rendering-driver") {
1007 if (I->next()) {
1008 rendering_driver = I->next()->get();
1009 N = I->next()->next();
1010 } else {
1011 OS::get_singleton()->print("Missing rendering driver argument, aborting.\n");
1012 goto error;
1013 }
1014 } else if (I->get() == "-f" || I->get() == "--fullscreen") { // force fullscreen
1015
1016 init_fullscreen = true;
1017 } else if (I->get() == "-m" || I->get() == "--maximized") { // force maximized window
1018
1019 init_maximized = true;
1020 window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
1021
1022 } else if (I->get() == "-w" || I->get() == "--windowed") { // force windowed window
1023
1024 init_windowed = true;
1025 } else if (I->get() == "--gpu-index") {
1026 if (I->next()) {
1027 Engine::singleton->gpu_idx = I->next()->get().to_int();
1028 N = I->next()->next();
1029 } else {
1030 OS::get_singleton()->print("Missing GPU index argument, aborting.\n");
1031 goto error;
1032 }
1033 } else if (I->get() == "--gpu-validation") {
1034 Engine::singleton->use_validation_layers = true;
1035#ifdef DEBUG_ENABLED
1036 } else if (I->get() == "--gpu-abort") {
1037 Engine::singleton->abort_on_gpu_errors = true;
1038#endif
1039 } else if (I->get() == "--generate-spirv-debug-info") {
1040 Engine::singleton->generate_spirv_debug_info = true;
1041 } else if (I->get() == "--tablet-driver") {
1042 if (I->next()) {
1043 tablet_driver = I->next()->get();
1044 N = I->next()->next();
1045 } else {
1046 OS::get_singleton()->print("Missing tablet driver argument, aborting.\n");
1047 goto error;
1048 }
1049 } else if (I->get() == "--delta-smoothing") {
1050 if (I->next()) {
1051 String string = I->next()->get();
1052 bool recognized = false;
1053 if (string == "enable") {
1054 OS::get_singleton()->set_delta_smoothing(true);
1055 delta_smoothing_override = true;
1056 recognized = true;
1057 }
1058 if (string == "disable") {
1059 OS::get_singleton()->set_delta_smoothing(false);
1060 delta_smoothing_override = false;
1061 recognized = true;
1062 }
1063 if (!recognized) {
1064 OS::get_singleton()->print("Delta-smoothing argument not recognized, aborting.\n");
1065 goto error;
1066 }
1067 N = I->next()->next();
1068 } else {
1069 OS::get_singleton()->print("Missing delta-smoothing argument, aborting.\n");
1070 goto error;
1071 }
1072 } else if (I->get() == "--single-window") { // force single window
1073
1074 single_window = true;
1075 } else if (I->get() == "-t" || I->get() == "--always-on-top") { // force always-on-top window
1076
1077 init_always_on_top = true;
1078 } else if (I->get() == "--resolution") { // force resolution
1079
1080 if (I->next()) {
1081 String vm = I->next()->get();
1082
1083 if (!vm.contains("x")) { // invalid parameter format
1084
1085 OS::get_singleton()->print("Invalid resolution '%s', it should be e.g. '1280x720'.\n",
1086 vm.utf8().get_data());
1087 goto error;
1088 }
1089
1090 int w = vm.get_slice("x", 0).to_int();
1091 int h = vm.get_slice("x", 1).to_int();
1092
1093 if (w <= 0 || h <= 0) {
1094 OS::get_singleton()->print("Invalid resolution '%s', width and height must be above 0.\n",
1095 vm.utf8().get_data());
1096 goto error;
1097 }
1098
1099 window_size.width = w;
1100 window_size.height = h;
1101 force_res = true;
1102
1103 N = I->next()->next();
1104 } else {
1105 OS::get_singleton()->print("Missing resolution argument, aborting.\n");
1106 goto error;
1107 }
1108
1109 } else if (I->get() == "--screen") { // set window screen
1110
1111 if (I->next()) {
1112 init_screen = I->next()->get().to_int();
1113 init_use_custom_screen = true;
1114
1115 N = I->next()->next();
1116 } else {
1117 OS::get_singleton()->print("Missing screen argument, aborting.\n");
1118 goto error;
1119 }
1120
1121 } else if (I->get() == "--position") { // set window position
1122
1123 if (I->next()) {
1124 String vm = I->next()->get();
1125
1126 if (!vm.contains(",")) { // invalid parameter format
1127
1128 OS::get_singleton()->print("Invalid position '%s', it should be e.g. '80,128'.\n",
1129 vm.utf8().get_data());
1130 goto error;
1131 }
1132
1133 int x = vm.get_slice(",", 0).to_int();
1134 int y = vm.get_slice(",", 1).to_int();
1135
1136 init_custom_pos = Point2(x, y);
1137 init_use_custom_pos = true;
1138
1139 N = I->next()->next();
1140 } else {
1141 OS::get_singleton()->print("Missing position argument, aborting.\n");
1142 goto error;
1143 }
1144
1145 } else if (I->get() == "--headless") { // enable headless mode (no audio, no rendering).
1146
1147 audio_driver = NULL_AUDIO_DRIVER;
1148 display_driver = NULL_DISPLAY_DRIVER;
1149
1150 } else if (I->get() == "--profiling") { // enable profiling
1151
1152 use_debug_profiler = true;
1153
1154 } else if (I->get() == "-l" || I->get() == "--language") { // language
1155
1156 if (I->next()) {
1157 locale = I->next()->get();
1158 N = I->next()->next();
1159 } else {
1160 OS::get_singleton()->print("Missing language argument, aborting.\n");
1161 goto error;
1162 }
1163
1164 } else if (I->get() == "--remote-fs") { // remote filesystem
1165
1166 if (I->next()) {
1167 remotefs = I->next()->get();
1168 N = I->next()->next();
1169 } else {
1170 OS::get_singleton()->print("Missing remote filesystem address, aborting.\n");
1171 goto error;
1172 }
1173 } else if (I->get() == "--remote-fs-password") { // remote filesystem password
1174
1175 if (I->next()) {
1176 remotefs_pass = I->next()->get();
1177 N = I->next()->next();
1178 } else {
1179 OS::get_singleton()->print("Missing remote filesystem password, aborting.\n");
1180 goto error;
1181 }
1182 } else if (I->get() == "--render-thread") { // render thread mode
1183
1184 if (I->next()) {
1185 if (I->next()->get() == "safe") {
1186 rtm = OS::RENDER_THREAD_SAFE;
1187 } else if (I->next()->get() == "unsafe") {
1188 rtm = OS::RENDER_THREAD_UNSAFE;
1189 } else if (I->next()->get() == "separate") {
1190 rtm = OS::RENDER_SEPARATE_THREAD;
1191 } else {
1192 OS::get_singleton()->print("Unknown render thread mode, aborting.\nValid options are 'unsafe', 'safe' and 'separate'.\n");
1193 goto error;
1194 }
1195
1196 N = I->next()->next();
1197 } else {
1198 OS::get_singleton()->print("Missing render thread mode argument, aborting.\n");
1199 goto error;
1200 }
1201#ifdef TOOLS_ENABLED
1202 } else if (I->get() == "-e" || I->get() == "--editor") { // starts editor
1203
1204 editor = true;
1205 } else if (I->get() == "-p" || I->get() == "--project-manager") { // starts project manager
1206 project_manager = true;
1207 } else if (I->get() == "--debug-server") {
1208 if (I->next()) {
1209 debug_server_uri = I->next()->get();
1210 if (!debug_server_uri.contains("://")) { // wrong address
1211 OS::get_singleton()->print("Invalid debug server uri. It should be of the form <protocol>://<bind_address>:<port>.\n");
1212 goto error;
1213 }
1214 N = I->next()->next();
1215 } else {
1216 OS::get_singleton()->print("Missing remote debug server uri, aborting.\n");
1217 goto error;
1218 }
1219 } else if (I->get() == "--single-threaded-scene") {
1220 single_threaded_scene = true;
1221 } else if (I->get() == "--build-solutions") { // Build the scripting solution such C#
1222
1223 auto_build_solutions = true;
1224 editor = true;
1225 cmdline_tool = true;
1226 } else if (I->get() == "--dump-gdextension-interface") {
1227 // Register as an editor instance to use low-end fallback if relevant.
1228 editor = true;
1229 cmdline_tool = true;
1230 dump_gdextension_interface = true;
1231 print_line("Dumping GDExtension interface header file");
1232 // Hack. Not needed but otherwise we end up detecting that this should
1233 // run the project instead of a cmdline tool.
1234 // Needs full refactoring to fix properly.
1235 main_args.push_back(I->get());
1236 } else if (I->get() == "--dump-extension-api") {
1237 // Register as an editor instance to use low-end fallback if relevant.
1238 editor = true;
1239 cmdline_tool = true;
1240 dump_extension_api = true;
1241 print_line("Dumping Extension API");
1242 // Hack. Not needed but otherwise we end up detecting that this should
1243 // run the project instead of a cmdline tool.
1244 // Needs full refactoring to fix properly.
1245 main_args.push_back(I->get());
1246 } else if (I->get() == "--validate-extension-api") {
1247 // Register as an editor instance to use low-end fallback if relevant.
1248 editor = true;
1249 cmdline_tool = true;
1250 validate_extension_api = true;
1251 // Hack. Not needed but otherwise we end up detecting that this should
1252 // run the project instead of a cmdline tool.
1253 // Needs full refactoring to fix properly.
1254 main_args.push_back(I->get());
1255
1256 if (I->next()) {
1257 validate_extension_api_file = I->next()->get();
1258
1259 N = I->next()->next();
1260 } else {
1261 OS::get_singleton()->print("Missing file to load argument after --validate-extension-api, aborting.");
1262 goto error;
1263 }
1264
1265 } else if (I->get() == "--export-release" || I->get() == "--export-debug" ||
1266 I->get() == "--export-pack") { // Export project
1267 // Actually handling is done in start().
1268 editor = true;
1269 cmdline_tool = true;
1270 main_args.push_back(I->get());
1271#ifndef DISABLE_DEPRECATED
1272 } else if (I->get() == "--export") { // For users used to 3.x syntax.
1273 OS::get_singleton()->print("The Godot 3 --export option was changed to more explicit --export-release / --export-debug / --export-pack options.\nSee the --help output for details.\n");
1274 goto error;
1275 } else if (I->get() == "--convert-3to4") {
1276 // Actually handling is done in start().
1277 cmdline_tool = true;
1278 main_args.push_back(I->get());
1279
1280 if (I->next() && !I->next()->get().begins_with("-")) {
1281 if (itos(I->next()->get().to_int()) == I->next()->get()) {
1282 converter_max_kb_file = I->next()->get().to_int();
1283 }
1284 if (I->next()->next() && !I->next()->next()->get().begins_with("-")) {
1285 if (itos(I->next()->next()->get().to_int()) == I->next()->next()->get()) {
1286 converter_max_line_length = I->next()->next()->get().to_int();
1287 }
1288 }
1289 }
1290 } else if (I->get() == "--validate-conversion-3to4") {
1291 // Actually handling is done in start().
1292 cmdline_tool = true;
1293 main_args.push_back(I->get());
1294
1295 if (I->next() && !I->next()->get().begins_with("-")) {
1296 if (itos(I->next()->get().to_int()) == I->next()->get()) {
1297 converter_max_kb_file = I->next()->get().to_int();
1298 }
1299 if (I->next()->next() && !I->next()->next()->get().begins_with("-")) {
1300 if (itos(I->next()->next()->get().to_int()) == I->next()->next()->get()) {
1301 converter_max_line_length = I->next()->next()->get().to_int();
1302 }
1303 }
1304 }
1305#endif // DISABLE_DEPRECATED
1306 } else if (I->get() == "--doctool") {
1307 // Actually handling is done in start().
1308 cmdline_tool = true;
1309
1310 // `--doctool` implies `--headless` to avoid spawning an unnecessary window
1311 // and speed up class reference generation.
1312 audio_driver = NULL_AUDIO_DRIVER;
1313 display_driver = NULL_DISPLAY_DRIVER;
1314 main_args.push_back(I->get());
1315#endif // TOOLS_ENABLED
1316 } else if (I->get() == "--path") { // set path of project to start or edit
1317
1318 if (I->next()) {
1319 String p = I->next()->get();
1320 if (OS::get_singleton()->set_cwd(p) != OK) {
1321 OS::get_singleton()->print("Invalid project path specified: \"%s\", aborting.\n", p.utf8().get_data());
1322 goto error;
1323 }
1324 N = I->next()->next();
1325 } else {
1326 OS::get_singleton()->print("Missing relative or absolute path, aborting.\n");
1327 goto error;
1328 }
1329 } else if (I->get() == "-u" || I->get() == "--upwards") { // scan folders upwards
1330 upwards = true;
1331 } else if (I->get() == "--quit") { // Auto quit at the end of the first main loop iteration
1332 quit_after = 1;
1333 } else if (I->get() == "--quit-after") { // Quit after the given number of iterations
1334 if (I->next()) {
1335 quit_after = I->next()->get().to_int();
1336 N = I->next()->next();
1337 } else {
1338 OS::get_singleton()->print("Missing number of iterations, aborting.\n");
1339 goto error;
1340 }
1341 } else if (I->get().ends_with("project.godot")) {
1342 String path;
1343 String file = I->get();
1344 int sep = MAX(file.rfind("/"), file.rfind("\\"));
1345 if (sep == -1) {
1346 path = ".";
1347 } else {
1348 path = file.substr(0, sep);
1349 }
1350 if (OS::get_singleton()->set_cwd(path) == OK) {
1351 // path already specified, don't override
1352 } else {
1353 project_path = path;
1354 }
1355#ifdef TOOLS_ENABLED
1356 editor = true;
1357#endif
1358 } else if (I->get() == "-b" || I->get() == "--breakpoints") { // add breakpoints
1359
1360 if (I->next()) {
1361 String bplist = I->next()->get();
1362 breakpoints = bplist.split(",");
1363 N = I->next()->next();
1364 } else {
1365 OS::get_singleton()->print("Missing list of breakpoints, aborting.\n");
1366 goto error;
1367 }
1368
1369 } else if (I->get() == "--max-fps") { // set maximum rendered FPS
1370
1371 if (I->next()) {
1372 max_fps = I->next()->get().to_int();
1373 N = I->next()->next();
1374 } else {
1375 OS::get_singleton()->print("Missing maximum FPS argument, aborting.\n");
1376 goto error;
1377 }
1378
1379 } else if (I->get() == "--frame-delay") { // force frame delay
1380
1381 if (I->next()) {
1382 frame_delay = I->next()->get().to_int();
1383 N = I->next()->next();
1384 } else {
1385 OS::get_singleton()->print("Missing frame delay argument, aborting.\n");
1386 goto error;
1387 }
1388
1389 } else if (I->get() == "--time-scale") { // force time scale
1390
1391 if (I->next()) {
1392 Engine::get_singleton()->set_time_scale(I->next()->get().to_float());
1393 N = I->next()->next();
1394 } else {
1395 OS::get_singleton()->print("Missing time scale argument, aborting.\n");
1396 goto error;
1397 }
1398
1399 } else if (I->get() == "--main-pack") {
1400 if (I->next()) {
1401 main_pack = I->next()->get();
1402 N = I->next()->next();
1403 } else {
1404 OS::get_singleton()->print("Missing path to main pack file, aborting.\n");
1405 goto error;
1406 };
1407
1408 } else if (I->get() == "-d" || I->get() == "--debug") {
1409 debug_uri = "local://";
1410 OS::get_singleton()->_debug_stdout = true;
1411#if defined(DEBUG_ENABLED)
1412 } else if (I->get() == "--debug-collisions") {
1413 debug_collisions = true;
1414 } else if (I->get() == "--debug-paths") {
1415 debug_paths = true;
1416 } else if (I->get() == "--debug-navigation") {
1417 debug_navigation = true;
1418 } else if (I->get() == "--debug-avoidance") {
1419 debug_avoidance = true;
1420 } else if (I->get() == "--debug-stringnames") {
1421 StringName::set_debug_stringnames(true);
1422#endif
1423 } else if (I->get() == "--remote-debug") {
1424 if (I->next()) {
1425 debug_uri = I->next()->get();
1426 if (!debug_uri.contains("://")) { // wrong address
1427 OS::get_singleton()->print(
1428 "Invalid debug host address, it should be of the form <protocol>://<host/IP>:<port>.\n");
1429 goto error;
1430 }
1431 N = I->next()->next();
1432 } else {
1433 OS::get_singleton()->print("Missing remote debug host address, aborting.\n");
1434 goto error;
1435 }
1436 } else if (I->get() == "--editor-pid") { // not exposed to user
1437 if (I->next()) {
1438 editor_pid = I->next()->get().to_int();
1439 N = I->next()->next();
1440 } else {
1441 OS::get_singleton()->print("Missing editor PID argument, aborting.\n");
1442 goto error;
1443 }
1444 } else if (I->get() == "--disable-render-loop") {
1445 disable_render_loop = true;
1446 } else if (I->get() == "--fixed-fps") {
1447 if (I->next()) {
1448 fixed_fps = I->next()->get().to_int();
1449 N = I->next()->next();
1450 } else {
1451 OS::get_singleton()->print("Missing fixed-fps argument, aborting.\n");
1452 goto error;
1453 }
1454 } else if (I->get() == "--write-movie") {
1455 if (I->next()) {
1456 Engine::get_singleton()->set_write_movie_path(I->next()->get());
1457 N = I->next()->next();
1458 if (fixed_fps == -1) {
1459 fixed_fps = 60;
1460 }
1461 OS::get_singleton()->_writing_movie = true;
1462 } else {
1463 OS::get_singleton()->print("Missing write-movie argument, aborting.\n");
1464 goto error;
1465 }
1466 } else if (I->get() == "--disable-vsync") {
1467 disable_vsync = true;
1468 } else if (I->get() == "--print-fps") {
1469 print_fps = true;
1470 } else if (I->get() == "--profile-gpu") {
1471 profile_gpu = true;
1472 } else if (I->get() == "--disable-crash-handler") {
1473 OS::get_singleton()->disable_crash_handler();
1474 } else if (I->get() == "--skip-breakpoints") {
1475 skip_breakpoints = true;
1476 } else if (I->get() == "--xr-mode") {
1477 if (I->next()) {
1478 String xr_mode = I->next()->get().to_lower();
1479 N = I->next()->next();
1480 if (xr_mode == "default") {
1481 XRServer::set_xr_mode(XRServer::XRMODE_DEFAULT);
1482 } else if (xr_mode == "off") {
1483 XRServer::set_xr_mode(XRServer::XRMODE_OFF);
1484 } else if (xr_mode == "on") {
1485 XRServer::set_xr_mode(XRServer::XRMODE_ON);
1486 } else {
1487 OS::get_singleton()->print("Unknown --xr-mode argument \"%s\", aborting.\n", xr_mode.ascii().get_data());
1488 goto error;
1489 }
1490 } else {
1491 OS::get_singleton()->print("Missing --xr-mode argument, aborting.\n");
1492 goto error;
1493 }
1494
1495 } else if (I->get() == "--benchmark") {
1496 OS::get_singleton()->set_use_benchmark(true);
1497 } else if (I->get() == "--benchmark-file") {
1498 if (I->next()) {
1499 OS::get_singleton()->set_use_benchmark(true);
1500 String benchmark_file = I->next()->get();
1501 OS::get_singleton()->set_benchmark_file(benchmark_file);
1502 N = I->next()->next();
1503 } else {
1504 OS::get_singleton()->print("Missing <path> argument for --benchmark-file <path>.\n");
1505 goto error;
1506 }
1507
1508 } else if (I->get() == "--" || I->get() == "++") {
1509 adding_user_args = true;
1510 } else {
1511 main_args.push_back(I->get());
1512 }
1513
1514 I = N;
1515 }
1516
1517#ifdef TOOLS_ENABLED
1518 if (editor && project_manager) {
1519 OS::get_singleton()->print(
1520 "Error: Command line arguments implied opening both editor and project manager, which is not possible. Aborting.\n");
1521 goto error;
1522 }
1523#endif
1524
1525 // Network file system needs to be configured before globals, since globals are based on the
1526 // 'project.godot' file which will only be available through the network if this is enabled
1527 if (!remotefs.is_empty()) {
1528 int port;
1529 if (remotefs.contains(":")) {
1530 port = remotefs.get_slicec(':', 1).to_int();
1531 remotefs = remotefs.get_slicec(':', 0);
1532 } else {
1533 port = 6010;
1534 }
1535 Error err = OS::get_singleton()->setup_remote_filesystem(remotefs, port, remotefs_pass, project_path);
1536
1537 if (err) {
1538 OS::get_singleton()->printerr("Could not connect to remotefs: %s:%i.\n", remotefs.utf8().get_data(), port);
1539 goto error;
1540 }
1541 }
1542
1543 if (globals->setup(project_path, main_pack, upwards, editor) == OK) {
1544#ifdef TOOLS_ENABLED
1545 found_project = true;
1546#endif
1547 } else {
1548#ifdef TOOLS_ENABLED
1549 editor = false;
1550#else
1551 const String error_msg = "Error: Couldn't load project data at path \"" + project_path + "\". Is the .pck file missing?\nIf you've renamed the executable, the associated .pck file should also be renamed to match the executable's name (without the extension).\n";
1552 OS::get_singleton()->print("%s", error_msg.utf8().get_data());
1553 OS::get_singleton()->alert(error_msg);
1554
1555 goto error;
1556#endif
1557 }
1558
1559 // Initialize WorkerThreadPool.
1560 {
1561 int worker_threads = GLOBAL_GET("threading/worker_pool/max_threads");
1562 bool low_priority_use_system_threads = GLOBAL_GET("threading/worker_pool/use_system_threads_for_low_priority_tasks");
1563 float low_property_ratio = GLOBAL_GET("threading/worker_pool/low_priority_thread_ratio");
1564
1565 if (editor || project_manager) {
1566 WorkerThreadPool::get_singleton()->init();
1567 } else {
1568 WorkerThreadPool::get_singleton()->init(worker_threads, low_priority_use_system_threads, low_property_ratio);
1569 }
1570 }
1571
1572#ifdef TOOLS_ENABLED
1573 if (editor) {
1574 Engine::get_singleton()->set_editor_hint(true);
1575 }
1576#endif
1577
1578 // Initialize user data dir.
1579 OS::get_singleton()->ensure_user_data_dir();
1580
1581 initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
1582 register_core_extensions(); // core extensions must be registered after globals setup and before display
1583
1584 ResourceUID::get_singleton()->load_from_cache(); // load UUIDs from cache.
1585
1586 if (ProjectSettings::get_singleton()->has_custom_feature("dedicated_server")) {
1587 audio_driver = NULL_AUDIO_DRIVER;
1588 display_driver = NULL_DISPLAY_DRIVER;
1589 }
1590
1591 GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/debugger/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater"), 32768);
1592 GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/debugger/max_queued_messages", PROPERTY_HINT_RANGE, "0, 8192, 1, or_greater"), 2048);
1593 GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/debugger/max_errors_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"), 400);
1594 GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/debugger/max_warnings_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"), 400);
1595
1596 EngineDebugger::initialize(debug_uri, skip_breakpoints, breakpoints, []() {
1597 if (editor_pid) {
1598 DisplayServer::get_singleton()->enable_for_stealing_focus(editor_pid);
1599 }
1600 });
1601
1602#ifdef TOOLS_ENABLED
1603 if (editor) {
1604 packed_data->set_disabled(true);
1605 main_args.push_back("--editor");
1606 if (!init_windowed) {
1607 init_maximized = true;
1608 window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
1609 }
1610 }
1611
1612 if (!project_manager && !editor) {
1613 // If we didn't find a project, we fall back to the project manager.
1614 project_manager = !found_project && !cmdline_tool;
1615 }
1616
1617 if (project_manager) {
1618 Engine::get_singleton()->set_project_manager_hint(true);
1619 }
1620#endif
1621
1622 GLOBAL_DEF("debug/file_logging/enable_file_logging", false);
1623 // Only file logging by default on desktop platforms as logs can't be
1624 // accessed easily on mobile/Web platforms (if at all).
1625 // This also prevents logs from being created for the editor instance, as feature tags
1626 // are disabled while in the editor (even if they should logically apply).
1627 GLOBAL_DEF("debug/file_logging/enable_file_logging.pc", true);
1628 GLOBAL_DEF("debug/file_logging/log_path", "user://logs/godot.log");
1629 GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater"), 5);
1630
1631 if (!project_manager && !editor && FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) &&
1632 GLOBAL_GET("debug/file_logging/enable_file_logging")) {
1633 // Don't create logs for the project manager as they would be written to
1634 // the current working directory, which is inconvenient.
1635 String base_path = GLOBAL_GET("debug/file_logging/log_path");
1636 int max_files = GLOBAL_GET("debug/file_logging/max_log_files");
1637 OS::get_singleton()->add_logger(memnew(RotatedFileLogger(base_path, max_files)));
1638 }
1639
1640 if (main_args.size() == 0 && String(GLOBAL_GET("application/run/main_scene")) == "") {
1641#ifdef TOOLS_ENABLED
1642 if (!editor && !project_manager) {
1643#endif
1644 const String error_msg = "Error: Can't run project: no main scene defined in the project.\n";
1645 OS::get_singleton()->print("%s", error_msg.utf8().get_data());
1646 OS::get_singleton()->alert(error_msg);
1647 goto error;
1648#ifdef TOOLS_ENABLED
1649 }
1650#endif
1651 }
1652
1653 if (editor || project_manager) {
1654 Engine::get_singleton()->set_editor_hint(true);
1655 use_custom_res = false;
1656 input_map->load_default(); //keys for editor
1657 } else {
1658 input_map->load_from_project_settings(); //keys for game
1659 }
1660
1661 if (bool(GLOBAL_GET("application/run/disable_stdout"))) {
1662 quiet_stdout = true;
1663 }
1664 if (bool(GLOBAL_GET("application/run/disable_stderr"))) {
1665 CoreGlobals::print_error_enabled = false;
1666 };
1667
1668 if (quiet_stdout) {
1669 CoreGlobals::print_line_enabled = false;
1670 }
1671
1672 Logger::set_flush_stdout_on_print(GLOBAL_GET("application/run/flush_stdout_on_print"));
1673
1674 OS::get_singleton()->set_cmdline(execpath, main_args, user_args);
1675
1676 {
1677 String driver_hints = "";
1678#ifdef VULKAN_ENABLED
1679 driver_hints = "vulkan";
1680#endif
1681
1682 String default_driver = driver_hints.get_slice(",", 0);
1683
1684 // For now everything defaults to vulkan when available. This can change in future updates.
1685 GLOBAL_DEF("rendering/rendering_device/driver", default_driver);
1686 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.windows", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1687 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.linuxbsd", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1688 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.android", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1689 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.ios", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1690 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.macos", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1691
1692 driver_hints = "";
1693#ifdef GLES3_ENABLED
1694 driver_hints += "opengl3";
1695#endif
1696
1697 default_driver = driver_hints.get_slice(",", 0);
1698
1699 GLOBAL_DEF("rendering/gl_compatibility/driver", default_driver);
1700 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.windows", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1701 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.linuxbsd", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1702 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.web", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1703 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.android", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1704 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.ios", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1705 GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.macos", PROPERTY_HINT_ENUM, driver_hints), default_driver);
1706 GLOBAL_DEF_RST("rendering/gl_compatibility/nvidia_disable_threaded_optimization", true);
1707 }
1708
1709 // Start with RenderingDevice-based backends. Should be included if any RD driver present.
1710#ifdef VULKAN_ENABLED
1711 renderer_hints = "forward_plus,mobile";
1712 default_renderer_mobile = "mobile";
1713#endif
1714
1715 // And Compatibility next, or first if Vulkan is disabled.
1716#ifdef GLES3_ENABLED
1717 if (!renderer_hints.is_empty()) {
1718 renderer_hints += ",";
1719 }
1720 renderer_hints += "gl_compatibility";
1721 if (default_renderer_mobile.is_empty()) {
1722 default_renderer_mobile = "gl_compatibility";
1723 }
1724 // Default to Compatibility when using the project manager.
1725 if (rendering_driver.is_empty() && rendering_method.is_empty() && project_manager) {
1726 rendering_driver = "opengl3";
1727 rendering_method = "gl_compatibility";
1728 default_renderer_mobile = "gl_compatibility";
1729 }
1730#endif
1731 if (renderer_hints.is_empty()) {
1732 ERR_PRINT("No renderers available.");
1733 }
1734
1735 if (!rendering_method.is_empty()) {
1736 if (rendering_method != "forward_plus" &&
1737 rendering_method != "mobile" &&
1738 rendering_method != "gl_compatibility") {
1739 OS::get_singleton()->print("Unknown renderer name '%s', aborting. Valid options are: %s\n", rendering_method.utf8().get_data(), renderer_hints.utf8().get_data());
1740 goto error;
1741 }
1742 }
1743
1744 if (!rendering_driver.is_empty()) {
1745 // As the rendering drivers available may depend on the display driver and renderer
1746 // selected, we can't do an exhaustive check here, but we can look through all
1747 // the options in all the display drivers for a match.
1748
1749 bool found = false;
1750 for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
1751 Vector<String> r_drivers = DisplayServer::get_create_function_rendering_drivers(i);
1752
1753 for (int d = 0; d < r_drivers.size(); d++) {
1754 if (rendering_driver == r_drivers[d]) {
1755 found = true;
1756 break;
1757 }
1758 }
1759 }
1760
1761 if (!found) {
1762 OS::get_singleton()->print("Unknown rendering driver '%s', aborting.\nValid options are ",
1763 rendering_driver.utf8().get_data());
1764
1765 for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
1766 Vector<String> r_drivers = DisplayServer::get_create_function_rendering_drivers(i);
1767
1768 for (int d = 0; d < r_drivers.size(); d++) {
1769 OS::get_singleton()->print("'%s', ", r_drivers[d].utf8().get_data());
1770 }
1771 }
1772
1773 OS::get_singleton()->print(".\n");
1774
1775 goto error;
1776 }
1777
1778 // Set a default renderer if none selected. Try to choose one that matches the driver.
1779 if (rendering_method.is_empty()) {
1780 if (rendering_driver == "opengl3") {
1781 rendering_method = "gl_compatibility";
1782 } else {
1783 rendering_method = "forward_plus";
1784 }
1785 }
1786
1787 // Now validate whether the selected driver matches with the renderer.
1788 bool valid_combination = false;
1789 Vector<String> available_drivers;
1790#ifdef VULKAN_ENABLED
1791 if (rendering_method == "forward_plus" || rendering_method == "mobile") {
1792 available_drivers.push_back("vulkan");
1793 }
1794#endif
1795#ifdef GLES3_ENABLED
1796 if (rendering_method == "gl_compatibility") {
1797 available_drivers.push_back("opengl3");
1798 }
1799#endif
1800 if (available_drivers.is_empty()) {
1801 OS::get_singleton()->print("Unknown renderer name '%s', aborting.\n", rendering_method.utf8().get_data());
1802 goto error;
1803 }
1804
1805 for (int i = 0; i < available_drivers.size(); i++) {
1806 if (rendering_driver == available_drivers[i]) {
1807 valid_combination = true;
1808 break;
1809 }
1810 }
1811
1812 if (!valid_combination) {
1813 OS::get_singleton()->print("Invalid renderer/driver combination '%s' and '%s', aborting. %s only supports the following drivers ", rendering_method.utf8().get_data(), rendering_driver.utf8().get_data(), rendering_method.utf8().get_data());
1814
1815 for (int d = 0; d < available_drivers.size(); d++) {
1816 OS::get_singleton()->print("'%s', ", available_drivers[d].utf8().get_data());
1817 }
1818
1819 OS::get_singleton()->print(".\n");
1820
1821 goto error;
1822 }
1823 }
1824
1825 default_renderer = renderer_hints.get_slice(",", 0);
1826 GLOBAL_DEF_RST_BASIC(PropertyInfo(Variant::STRING, "rendering/renderer/rendering_method", PROPERTY_HINT_ENUM, renderer_hints), default_renderer);
1827 GLOBAL_DEF_RST_BASIC("rendering/renderer/rendering_method.mobile", default_renderer_mobile);
1828 GLOBAL_DEF_RST_BASIC("rendering/renderer/rendering_method.web", "gl_compatibility"); // This is a bit of a hack until we have WebGPU support.
1829
1830 // Default to ProjectSettings default if nothing set on the command line.
1831 if (rendering_method.is_empty()) {
1832 rendering_method = GLOBAL_GET("rendering/renderer/rendering_method");
1833 }
1834
1835 if (rendering_driver.is_empty()) {
1836 if (rendering_method == "gl_compatibility") {
1837 rendering_driver = GLOBAL_GET("rendering/gl_compatibility/driver");
1838 } else {
1839 rendering_driver = GLOBAL_GET("rendering/rendering_device/driver");
1840 }
1841 }
1842
1843 // note this is the desired rendering driver, it doesn't mean we will get it.
1844 // TODO - make sure this is updated in the case of fallbacks, so that the user interface
1845 // shows the correct driver string.
1846 OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
1847 OS::get_singleton()->set_current_rendering_method(rendering_method);
1848
1849 // always convert to lower case for consistency in the code
1850 rendering_driver = rendering_driver.to_lower();
1851
1852 if (use_custom_res) {
1853 if (!force_res) {
1854 window_size.width = GLOBAL_GET("display/window/size/viewport_width");
1855 window_size.height = GLOBAL_GET("display/window/size/viewport_height");
1856
1857 if (globals->has_setting("display/window/size/window_width_override") &&
1858 globals->has_setting("display/window/size/window_height_override")) {
1859 int desired_width = globals->get("display/window/size/window_width_override");
1860 if (desired_width > 0) {
1861 window_size.width = desired_width;
1862 }
1863 int desired_height = globals->get("display/window/size/window_height_override");
1864 if (desired_height > 0) {
1865 window_size.height = desired_height;
1866 }
1867 }
1868 }
1869
1870 if (!bool(GLOBAL_GET("display/window/size/resizable"))) {
1871 window_flags |= DisplayServer::WINDOW_FLAG_RESIZE_DISABLED_BIT;
1872 }
1873 if (bool(GLOBAL_GET("display/window/size/borderless"))) {
1874 window_flags |= DisplayServer::WINDOW_FLAG_BORDERLESS_BIT;
1875 }
1876 if (bool(GLOBAL_GET("display/window/size/always_on_top"))) {
1877 window_flags |= DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP_BIT;
1878 }
1879 if (bool(GLOBAL_GET("display/window/size/transparent"))) {
1880 window_flags |= DisplayServer::WINDOW_FLAG_TRANSPARENT_BIT;
1881 }
1882 if (bool(GLOBAL_GET("display/window/size/extend_to_title"))) {
1883 window_flags |= DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE_BIT;
1884 }
1885 if (bool(GLOBAL_GET("display/window/size/no_focus"))) {
1886 window_flags |= DisplayServer::WINDOW_FLAG_NO_FOCUS_BIT;
1887 }
1888 window_mode = (DisplayServer::WindowMode)(GLOBAL_GET("display/window/size/mode").operator int());
1889 int initial_position_type = GLOBAL_GET("display/window/size/initial_position_type").operator int();
1890 if (initial_position_type == 0) {
1891 if (!init_use_custom_pos) {
1892 init_custom_pos = GLOBAL_GET("display/window/size/initial_position").operator Vector2i();
1893 init_use_custom_pos = true;
1894 }
1895 } else if (initial_position_type == 1) {
1896 if (!init_use_custom_screen) {
1897 init_screen = DisplayServer::SCREEN_PRIMARY;
1898 init_use_custom_screen = true;
1899 }
1900 } else if (initial_position_type == 2) {
1901 if (!init_use_custom_screen) {
1902 init_screen = GLOBAL_GET("display/window/size/initial_screen").operator int();
1903 init_use_custom_screen = true;
1904 }
1905 }
1906 }
1907
1908 GLOBAL_DEF("internationalization/locale/include_text_server_data", false);
1909
1910 OS::get_singleton()->_allow_hidpi = GLOBAL_DEF("display/window/dpi/allow_hidpi", true);
1911 OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/per_pixel_transparency/allowed", false);
1912
1913#ifdef TOOLS_ENABLED
1914 if (editor || project_manager) {
1915 // The editor and project manager always detect and use hiDPI if needed.
1916 OS::get_singleton()->_allow_hidpi = true;
1917 // Disable Vulkan overlays in editor, they cause various issues.
1918 OS::get_singleton()->set_environment("DISABLE_MANGOHUD", "1"); // GH-57403.
1919 OS::get_singleton()->set_environment("DISABLE_RTSS_LAYER", "1"); // GH-57937.
1920 OS::get_singleton()->set_environment("DISABLE_VKBASALT", "1");
1921 } else {
1922 // Re-allow using Vulkan overlays, disabled while using the editor.
1923 OS::get_singleton()->unset_environment("DISABLE_MANGOHUD");
1924 OS::get_singleton()->unset_environment("DISABLE_RTSS_LAYER");
1925 OS::get_singleton()->unset_environment("DISABLE_VKBASALT");
1926 }
1927#endif
1928
1929 if (rtm == -1) {
1930 rtm = GLOBAL_DEF("rendering/driver/threads/thread_model", OS::RENDER_THREAD_SAFE);
1931 }
1932
1933 if (rtm >= 0 && rtm < 3) {
1934 if (editor || project_manager) {
1935 // Editor and project manager cannot run with rendering in a separate thread (they will crash on startup).
1936 rtm = OS::RENDER_THREAD_SAFE;
1937 }
1938 OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm);
1939 }
1940
1941 /* Determine audio and video drivers */
1942
1943 // Display driver, e.g. X11, Wayland.
1944 // Make sure that headless is the last one, which it is assumed to be by design.
1945 DEV_ASSERT(NULL_DISPLAY_DRIVER == DisplayServer::get_create_function_name(DisplayServer::get_create_function_count() - 1));
1946 for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
1947 String name = DisplayServer::get_create_function_name(i);
1948 if (display_driver == name) {
1949 display_driver_idx = i;
1950 break;
1951 }
1952 }
1953
1954 if (display_driver_idx < 0) {
1955 // If the requested driver wasn't found, pick the first entry.
1956 // If all else failed it would be the headless server.
1957 display_driver_idx = 0;
1958 }
1959
1960 // Store this in a globally accessible place, so we can retrieve the rendering drivers
1961 // list from the display driver for the editor UI.
1962 OS::get_singleton()->set_display_driver_id(display_driver_idx);
1963
1964 GLOBAL_DEF_RST_NOVAL("audio/driver/driver", AudioDriverManager::get_driver(0)->get_name());
1965 if (audio_driver.is_empty()) { // Specified in project.godot.
1966 audio_driver = GLOBAL_GET("audio/driver/driver");
1967 }
1968
1969 // Make sure that dummy is the last one, which it is assumed to be by design.
1970 DEV_ASSERT(NULL_AUDIO_DRIVER == AudioDriverManager::get_driver(AudioDriverManager::get_driver_count() - 1)->get_name());
1971 for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
1972 if (audio_driver == AudioDriverManager::get_driver(i)->get_name()) {
1973 audio_driver_idx = i;
1974 break;
1975 }
1976 }
1977
1978 if (audio_driver_idx < 0) {
1979 // If the requested driver wasn't found, pick the first entry.
1980 // If all else failed it would be the dummy driver (no sound).
1981 audio_driver_idx = 0;
1982 }
1983
1984 if (Engine::get_singleton()->get_write_movie_path() != String()) {
1985 // Always use dummy driver for audio driver (which is last), also in no threaded mode.
1986 audio_driver_idx = AudioDriverManager::get_driver_count() - 1;
1987 AudioDriverDummy::get_dummy_singleton()->set_use_threads(false);
1988 }
1989
1990 {
1991 window_orientation = DisplayServer::ScreenOrientation(int(GLOBAL_DEF_BASIC("display/window/handheld/orientation", DisplayServer::ScreenOrientation::SCREEN_LANDSCAPE)));
1992 }
1993 {
1994 window_vsync_mode = DisplayServer::VSyncMode(int(GLOBAL_DEF_BASIC("display/window/vsync/vsync_mode", DisplayServer::VSyncMode::VSYNC_ENABLED)));
1995 if (disable_vsync) {
1996 window_vsync_mode = DisplayServer::VSyncMode::VSYNC_DISABLED;
1997 }
1998 }
1999 Engine::get_singleton()->set_physics_ticks_per_second(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/physics_ticks_per_second", PROPERTY_HINT_RANGE, "1,1000,1"), 60));
2000 Engine::get_singleton()->set_max_physics_steps_per_frame(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/max_physics_steps_per_frame", PROPERTY_HINT_RANGE, "1,100,1"), 8));
2001 Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5));
2002 Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0));
2003 Engine::get_singleton()->set_audio_output_latency(GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15));
2004 // Use a safer default output_latency for web to avoid audio cracking on low-end devices, especially mobile.
2005 GLOBAL_DEF_RST("audio/driver/output_latency.web", 50);
2006
2007 GLOBAL_DEF("debug/settings/stdout/print_fps", false);
2008 GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false);
2009 GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false);
2010
2011 if (!OS::get_singleton()->_verbose_stdout) { // Not manually overridden.
2012 OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout");
2013 }
2014
2015#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
2016 OS::get_singleton()->set_environment("MVK_CONFIG_LOG_LEVEL", OS::get_singleton()->_verbose_stdout ? "3" : "1"); // 1 = Errors only, 3 = Info
2017#endif
2018
2019 if (max_fps >= 0) {
2020 Engine::get_singleton()->set_max_fps(max_fps);
2021 }
2022
2023 if (frame_delay == 0) {
2024 frame_delay = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), 0);
2025 }
2026
2027 if (audio_output_latency >= 1) {
2028 Engine::get_singleton()->set_audio_output_latency(audio_output_latency);
2029 }
2030
2031 OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false));
2032 OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(
2033 GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater"), 6900)); // Roughly 144 FPS
2034
2035 GLOBAL_DEF("application/run/delta_smoothing", true);
2036 if (!delta_smoothing_override) {
2037 OS::get_singleton()->set_delta_smoothing(GLOBAL_GET("application/run/delta_smoothing"));
2038 }
2039
2040 GLOBAL_DEF("display/window/ios/allow_high_refresh_rate", true);
2041 GLOBAL_DEF("display/window/ios/hide_home_indicator", true);
2042 GLOBAL_DEF("display/window/ios/hide_status_bar", true);
2043 GLOBAL_DEF("display/window/ios/suppress_ui_gesture", true);
2044
2045 // XR project settings.
2046 GLOBAL_DEF_RST_BASIC("xr/openxr/enabled", false);
2047 GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"), "res://openxr_action_map.tres");
2048 GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/form_factor", PROPERTY_HINT_ENUM, "Head Mounted,Handheld"), "0");
2049 GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1"); // "Mono,Stereo,Quad,Observer"
2050 GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/reference_space", PROPERTY_HINT_ENUM, "Local,Stage"), "1");
2051 GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/environment_blend_mode", PROPERTY_HINT_ENUM, "Opaque,Additive,Alpha"), "0");
2052
2053 GLOBAL_DEF_BASIC("xr/openxr/submit_depth_buffer", false);
2054 GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true);
2055
2056#ifdef TOOLS_ENABLED
2057 // Disabled for now, using XR inside of the editor we'll be working on during the coming months.
2058
2059 // editor settings (it seems we're too early in the process when setting up rendering, to access editor settings...)
2060 // EDITOR_DEF_RST("xr/openxr/in_editor", false);
2061 // GLOBAL_DEF("xr/openxr/in_editor", false);
2062#endif
2063
2064 Engine::get_singleton()->set_frame_delay(frame_delay);
2065
2066 message_queue = memnew(MessageQueue);
2067
2068 Thread::release_main_thread(); // If setup2() is called from another thread, that one will become main thread, so preventively release this one.
2069 set_current_thread_safe_for_nodes(false);
2070
2071 if (p_second_phase) {
2072 return setup2();
2073 }
2074
2075 OS::get_singleton()->benchmark_end_measure("core");
2076 return OK;
2077
2078error:
2079
2080 text_driver = "";
2081 display_driver = "";
2082 audio_driver = "";
2083 tablet_driver = "";
2084 Engine::get_singleton()->set_write_movie_path(String());
2085 project_path = "";
2086
2087 args.clear();
2088 main_args.clear();
2089
2090 if (show_help) {
2091 print_help(execpath);
2092 }
2093
2094 EngineDebugger::deinitialize();
2095
2096 if (performance) {
2097 memdelete(performance);
2098 }
2099 if (input_map) {
2100 memdelete(input_map);
2101 }
2102 if (time_singleton) {
2103 memdelete(time_singleton);
2104 }
2105 if (translation_server) {
2106 memdelete(translation_server);
2107 }
2108 if (globals) {
2109 memdelete(globals);
2110 }
2111 if (engine) {
2112 memdelete(engine);
2113 }
2114 if (packed_data) {
2115 memdelete(packed_data);
2116 }
2117
2118 unregister_core_driver_types();
2119 unregister_core_extensions();
2120 unregister_core_types();
2121
2122 OS::get_singleton()->_cmdline.clear();
2123 OS::get_singleton()->_user_args.clear();
2124
2125 if (message_queue) {
2126 memdelete(message_queue);
2127 }
2128
2129 OS::get_singleton()->benchmark_end_measure("core");
2130
2131 OS::get_singleton()->finalize_core();
2132 locale = String();
2133
2134 return exit_code;
2135}
2136
2137Error _parse_resource_dummy(void *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
2138 VariantParser::Token token;
2139 VariantParser::get_token(p_stream, token, line, r_err_str);
2140 if (token.type != VariantParser::TK_NUMBER && token.type != VariantParser::TK_STRING) {
2141 r_err_str = "Expected number (old style sub-resource index) or String (ext-resource ID)";
2142 return ERR_PARSE_ERROR;
2143 }
2144
2145 r_res.unref();
2146
2147 VariantParser::get_token(p_stream, token, line, r_err_str);
2148 if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
2149 r_err_str = "Expected ')'";
2150 return ERR_PARSE_ERROR;
2151 }
2152
2153 return OK;
2154}
2155
2156Error Main::setup2() {
2157 Thread::make_main_thread(); // Make whatever thread call this the main thread.
2158 set_current_thread_safe_for_nodes(true);
2159
2160 // Print engine name and version
2161 print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
2162
2163 OS::get_singleton()->benchmark_begin_measure("servers");
2164
2165 tsman = memnew(TextServerManager);
2166
2167 if (tsman) {
2168 Ref<TextServerDummy> ts;
2169 ts.instantiate();
2170 tsman->add_interface(ts);
2171 }
2172
2173 physics_server_3d_manager = memnew(PhysicsServer3DManager);
2174 physics_server_2d_manager = memnew(PhysicsServer2DManager);
2175
2176 register_server_types();
2177 initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
2178 GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
2179
2180#ifdef TOOLS_ENABLED
2181 if (editor || project_manager || cmdline_tool) {
2182 EditorPaths::create();
2183
2184 // Editor setting class is not available, load config directly.
2185 if (!init_use_custom_screen && (editor || project_manager) && EditorPaths::get_singleton()->are_paths_valid()) {
2186 Ref<DirAccess> dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir());
2187 String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres";
2188 String config_file_path = EditorPaths::get_singleton()->get_config_dir().path_join(config_file_name);
2189 if (dir->file_exists(config_file_name)) {
2190 Error err;
2191 Ref<FileAccess> f = FileAccess::open(config_file_path, FileAccess::READ, &err);
2192 if (f.is_valid()) {
2193 VariantParser::StreamFile stream;
2194 stream.f = f;
2195
2196 String assign;
2197 Variant value;
2198 VariantParser::Tag next_tag;
2199
2200 int lines = 0;
2201 String error_text;
2202
2203 VariantParser::ResourceParser rp_new;
2204 rp_new.ext_func = _parse_resource_dummy;
2205 rp_new.sub_func = _parse_resource_dummy;
2206
2207 while (true) {
2208 assign = Variant();
2209 next_tag.fields.clear();
2210 next_tag.name = String();
2211
2212 err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp_new, true);
2213 if (err == ERR_FILE_EOF) {
2214 break;
2215 }
2216 if (err == OK && !assign.is_empty()) {
2217 if (project_manager) {
2218 if (assign == "interface/editor/project_manager_screen") {
2219 init_screen = value;
2220 break;
2221 }
2222 } else if (editor) {
2223 if (assign == "interface/editor/editor_screen") {
2224 init_screen = value;
2225 break;
2226 }
2227 }
2228 }
2229 }
2230 }
2231 }
2232 }
2233
2234 if (found_project && EditorPaths::get_singleton()->is_self_contained()) {
2235 if (ProjectSettings::get_singleton()->get_resource_path() == OS::get_singleton()->get_executable_path().get_base_dir()) {
2236 ERR_PRINT("You are trying to run a self-contained editor at the same location as a project. This is not allowed, since editor files will mix with project files.");
2237 OS::get_singleton()->set_exit_code(EXIT_FAILURE);
2238 return FAILED;
2239 }
2240 }
2241 }
2242#endif
2243
2244 /* Initialize Input */
2245
2246 input = memnew(Input);
2247
2248 /* Initialize Display Server */
2249
2250 {
2251 String display_driver = DisplayServer::get_create_function_name(display_driver_idx);
2252
2253 Vector2i *window_position = nullptr;
2254 Vector2i position = init_custom_pos;
2255 if (init_use_custom_pos) {
2256 window_position = &position;
2257 }
2258
2259 Color boot_bg_color = GLOBAL_DEF_BASIC("application/boot_splash/bg_color", boot_splash_bg_color);
2260 DisplayServer::set_early_window_clear_color_override(true, boot_bg_color);
2261
2262 // rendering_driver now held in static global String in main and initialized in setup()
2263 Error err;
2264 display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, err);
2265 if (err != OK || display_server == nullptr) {
2266 // We can't use this display server, try other ones as fallback.
2267 // Skip headless (always last registered) because that's not what users
2268 // would expect if they didn't request it explicitly.
2269 for (int i = 0; i < DisplayServer::get_create_function_count() - 1; i++) {
2270 if (i == display_driver_idx) {
2271 continue; // Don't try the same twice.
2272 }
2273 display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, err);
2274 if (err == OK && display_server != nullptr) {
2275 break;
2276 }
2277 }
2278 }
2279
2280 if (err != OK || display_server == nullptr) {
2281 ERR_PRINT("Unable to create DisplayServer, all display drivers failed.");
2282 return err;
2283 }
2284 }
2285
2286 if (display_server->has_feature(DisplayServer::FEATURE_ORIENTATION)) {
2287 display_server->screen_set_orientation(window_orientation);
2288 }
2289
2290 if (GLOBAL_GET("debug/settings/stdout/print_fps") || print_fps) {
2291 // Print requested V-Sync mode at startup to diagnose the printed FPS not going above the monitor refresh rate.
2292 switch (window_vsync_mode) {
2293 case DisplayServer::VSyncMode::VSYNC_DISABLED:
2294 print_line("Requested V-Sync mode: Disabled");
2295 break;
2296 case DisplayServer::VSyncMode::VSYNC_ENABLED:
2297 print_line("Requested V-Sync mode: Enabled - FPS will likely be capped to the monitor refresh rate.");
2298 break;
2299 case DisplayServer::VSyncMode::VSYNC_ADAPTIVE:
2300 print_line("Requested V-Sync mode: Adaptive");
2301 break;
2302 case DisplayServer::VSyncMode::VSYNC_MAILBOX:
2303 print_line("Requested V-Sync mode: Mailbox");
2304 break;
2305 }
2306 }
2307
2308 if (OS::get_singleton()->_render_thread_mode == OS::RENDER_SEPARATE_THREAD) {
2309 WARN_PRINT("The Multi-Threaded rendering thread model is experimental, and has known issues which can lead to project crashes. Use the Single-Safe option in the project settings instead.");
2310 }
2311
2312 /* Initialize Pen Tablet Driver */
2313
2314 {
2315 GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", "");
2316 GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "wintab,winink"), "");
2317 }
2318
2319 if (tablet_driver.is_empty()) { // specified in project.godot
2320 tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver");
2321 if (tablet_driver.is_empty()) {
2322 tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(0);
2323 }
2324 }
2325
2326 for (int i = 0; i < DisplayServer::get_singleton()->tablet_get_driver_count(); i++) {
2327 if (tablet_driver == DisplayServer::get_singleton()->tablet_get_driver_name(i)) {
2328 DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(i));
2329 break;
2330 }
2331 }
2332
2333 if (DisplayServer::get_singleton()->tablet_get_current_driver().is_empty()) {
2334 DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(0));
2335 }
2336
2337 print_verbose("Using \"" + tablet_driver + "\" pen tablet driver...");
2338
2339 /* Initialize Rendering Server */
2340
2341 rendering_server = memnew(RenderingServerDefault(OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD));
2342
2343 rendering_server->init();
2344 //rendering_server->call_set_use_vsync(OS::get_singleton()->_use_vsync);
2345 rendering_server->set_render_loop_enabled(!disable_render_loop);
2346
2347 if (profile_gpu || (!editor && bool(GLOBAL_GET("debug/settings/stdout/print_gpu_profile")))) {
2348 rendering_server->set_print_gpu_profile(true);
2349 }
2350
2351 if (Engine::get_singleton()->get_write_movie_path() != String()) {
2352 movie_writer = MovieWriter::find_writer_for_file(Engine::get_singleton()->get_write_movie_path());
2353 if (movie_writer == nullptr) {
2354 ERR_PRINT("Can't find movie writer for file type, aborting: " + Engine::get_singleton()->get_write_movie_path());
2355 Engine::get_singleton()->set_write_movie_path(String());
2356 }
2357 }
2358
2359#ifdef UNIX_ENABLED
2360 // Print warning after initializing the renderer but before initializing audio.
2361 if (OS::get_singleton()->get_environment("USER") == "root" && !OS::get_singleton()->has_environment("GODOT_SILENCE_ROOT_WARNING")) {
2362 WARN_PRINT("Started the engine as `root`/superuser. This is a security risk, and subsystems like audio may not work correctly.\nSet the environment variable `GODOT_SILENCE_ROOT_WARNING` to 1 to silence this warning.");
2363 }
2364#endif
2365
2366 OS::get_singleton()->initialize_joypads();
2367
2368 /* Initialize Audio Driver */
2369
2370 AudioDriverManager::initialize(audio_driver_idx);
2371
2372 print_line(" "); //add a blank line for readability
2373
2374 // right moment to create and initialize the audio server
2375
2376 audio_server = memnew(AudioServer);
2377 audio_server->init();
2378
2379 // also init our xr_server from here
2380 xr_server = memnew(XRServer);
2381
2382 register_core_singletons();
2383
2384 MAIN_PRINT("Main: Setup Logo");
2385
2386#if !defined(TOOLS_ENABLED) && (defined(WEB_ENABLED) || defined(ANDROID_ENABLED))
2387 bool show_logo = false;
2388#else
2389 bool = true;
2390#endif
2391
2392 if (init_windowed) {
2393 //do none..
2394 } else if (init_maximized) {
2395 DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_MAXIMIZED);
2396 } else if (init_fullscreen) {
2397 DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_FULLSCREEN);
2398 }
2399 if (init_always_on_top) {
2400 DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, true);
2401 }
2402
2403 MAIN_PRINT("Main: Load Boot Image");
2404
2405 Color clear = GLOBAL_DEF_BASIC("rendering/environment/defaults/default_clear_color", Color(0.3, 0.3, 0.3));
2406 RenderingServer::get_singleton()->set_default_clear_color(clear);
2407
2408 if (show_logo) { //boot logo!
2409 const bool boot_logo_image = GLOBAL_DEF_BASIC("application/boot_splash/show_image", true);
2410 const String boot_logo_path = String(GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"), String())).strip_edges();
2411 const bool boot_logo_scale = GLOBAL_DEF_BASIC("application/boot_splash/fullsize", true);
2412 const bool boot_logo_filter = GLOBAL_DEF_BASIC("application/boot_splash/use_filter", true);
2413
2414 Ref<Image> ;
2415
2416 if (boot_logo_image) {
2417 if (!boot_logo_path.is_empty()) {
2418 boot_logo.instantiate();
2419 Error load_err = ImageLoader::load_image(boot_logo_path, boot_logo);
2420 if (load_err) {
2421 ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + "'. Loading default splash.");
2422 }
2423 }
2424 } else {
2425 // Create a 1×1 transparent image. This will effectively hide the splash image.
2426 boot_logo.instantiate();
2427 boot_logo->initialize_data(1, 1, false, Image::FORMAT_RGBA8);
2428 boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0));
2429 }
2430
2431 Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color");
2432
2433#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
2434 boot_bg_color =
2435 GLOBAL_DEF_BASIC("application/boot_splash/bg_color",
2436 (editor || project_manager) ? boot_splash_editor_bg_color : boot_splash_bg_color);
2437#endif
2438 if (boot_logo.is_valid()) {
2439 RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale,
2440 boot_logo_filter);
2441
2442 } else {
2443#ifndef NO_DEFAULT_BOOT_LOGO
2444 MAIN_PRINT("Main: Create bootsplash");
2445#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
2446 Ref<Image> splash = (editor || project_manager) ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png));
2447#else
2448 Ref<Image> splash = memnew(Image(boot_splash_png));
2449#endif
2450
2451 MAIN_PRINT("Main: ClearColor");
2452 RenderingServer::get_singleton()->set_default_clear_color(boot_bg_color);
2453 MAIN_PRINT("Main: Image");
2454 RenderingServer::get_singleton()->set_boot_image(splash, boot_bg_color, false);
2455#endif
2456 }
2457
2458#if defined(TOOLS_ENABLED) && defined(MACOS_ENABLED)
2459 if (OS::get_singleton()->get_bundle_icon_path().is_empty()) {
2460 Ref<Image> icon = memnew(Image(app_icon_png));
2461 DisplayServer::get_singleton()->set_icon(icon);
2462 }
2463#endif
2464 }
2465
2466 DisplayServer::set_early_window_clear_color_override(false);
2467
2468 MAIN_PRINT("Main: DCC");
2469 RenderingServer::get_singleton()->set_default_clear_color(
2470 GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
2471
2472 GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), String());
2473 GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"), String());
2474 GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/windows_native_icon", PROPERTY_HINT_FILE, "*.ico"), String());
2475
2476 Input *id = Input::get_singleton();
2477 if (id) {
2478 agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false);
2479
2480 if (bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_touch_from_mouse", false)) &&
2481 !(editor || project_manager)) {
2482 if (!DisplayServer::get_singleton()->is_touchscreen_available()) {
2483 //only if no touchscreen ui hint, set emulation
2484 id->set_emulate_touch_from_mouse(true);
2485 }
2486 }
2487
2488 id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
2489 }
2490
2491 MAIN_PRINT("Main: Load Translations and Remaps");
2492
2493 translation_server->setup(); //register translations, load them, etc.
2494 if (!locale.is_empty()) {
2495 translation_server->set_locale(locale);
2496 }
2497 translation_server->load_translations();
2498 ResourceLoader::load_translation_remaps(); //load remaps for resources
2499
2500 ResourceLoader::load_path_remaps();
2501
2502 MAIN_PRINT("Main: Load TextServer");
2503
2504 /* Enum text drivers */
2505 GLOBAL_DEF_RST("internationalization/rendering/text_driver", "");
2506 String text_driver_options;
2507 for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
2508 const String driver_name = TextServerManager::get_singleton()->get_interface(i)->get_name();
2509 if (driver_name == "Dummy") {
2510 // Dummy text driver cannot draw any text, making the editor unusable if selected.
2511 continue;
2512 }
2513 if (!text_driver_options.is_empty() && text_driver_options.find(",") == -1) {
2514 // Not the first option; add a comma before it as a separator for the property hint.
2515 text_driver_options += ",";
2516 }
2517 text_driver_options += driver_name;
2518 }
2519 ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/rendering/text_driver", PROPERTY_HINT_ENUM, text_driver_options));
2520
2521 /* Determine text driver */
2522 if (text_driver.is_empty()) {
2523 text_driver = GLOBAL_GET("internationalization/rendering/text_driver");
2524 }
2525
2526 if (!text_driver.is_empty()) {
2527 /* Load user selected text server. */
2528 for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
2529 if (TextServerManager::get_singleton()->get_interface(i)->get_name() == text_driver) {
2530 text_driver_idx = i;
2531 break;
2532 }
2533 }
2534 }
2535
2536 if (text_driver_idx < 0) {
2537 /* If not selected, use one with the most features available. */
2538 int max_features = 0;
2539 for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
2540 uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features();
2541 int feature_number = 0;
2542 while (features) {
2543 feature_number += features & 1;
2544 features >>= 1;
2545 }
2546 if (feature_number >= max_features) {
2547 max_features = feature_number;
2548 text_driver_idx = i;
2549 }
2550 }
2551 }
2552 if (text_driver_idx >= 0) {
2553 Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(text_driver_idx);
2554 TextServerManager::get_singleton()->set_primary_interface(ts);
2555 if (ts->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
2556 ts->load_support_data("res://" + ts->get_support_data_filename());
2557 }
2558 } else {
2559 ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface.");
2560 }
2561
2562 OS::get_singleton()->benchmark_end_measure("servers");
2563
2564 MAIN_PRINT("Main: Load Scene Types");
2565
2566 OS::get_singleton()->benchmark_begin_measure("scene");
2567
2568 // Initialize ThemeDB early so that scene types can register their theme items.
2569 // Default theme will be initialized later, after modules and ScriptServer are ready.
2570 initialize_theme_db();
2571
2572 register_scene_types();
2573 register_driver_types();
2574
2575 register_scene_singletons();
2576
2577 initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
2578 GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
2579
2580#ifdef TOOLS_ENABLED
2581 ClassDB::set_current_api(ClassDB::API_EDITOR);
2582 register_editor_types();
2583 initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
2584 GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
2585
2586 ClassDB::set_current_api(ClassDB::API_CORE);
2587
2588#endif
2589
2590 MAIN_PRINT("Main: Load Modules");
2591
2592 register_platform_apis();
2593
2594 GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/mouse_cursor/custom_image", PROPERTY_HINT_FILE, "*.png,*.webp"), String());
2595 GLOBAL_DEF_BASIC("display/mouse_cursor/custom_image_hotspot", Vector2());
2596 GLOBAL_DEF_BASIC("display/mouse_cursor/tooltip_position_offset", Point2(10, 10));
2597
2598 if (String(GLOBAL_GET("display/mouse_cursor/custom_image")) != String()) {
2599 Ref<Texture2D> cursor = ResourceLoader::load(
2600 GLOBAL_GET("display/mouse_cursor/custom_image"));
2601 if (cursor.is_valid()) {
2602 Vector2 hotspot = GLOBAL_GET("display/mouse_cursor/custom_image_hotspot");
2603 Input::get_singleton()->set_custom_mouse_cursor(cursor, Input::CURSOR_ARROW, hotspot);
2604 }
2605 }
2606
2607 camera_server = CameraServer::create();
2608
2609 MAIN_PRINT("Main: Load Physics");
2610
2611 initialize_physics();
2612 initialize_navigation_server();
2613 register_server_singletons();
2614
2615 // This loads global classes, so it must happen before custom loaders and savers are registered
2616 ScriptServer::init_languages();
2617
2618 theme_db->initialize_theme();
2619 audio_server->load_default_bus_layout();
2620
2621#if defined(MODULE_MONO_ENABLED) && defined(TOOLS_ENABLED)
2622 // Hacky to have it here, but we don't have good facility yet to let modules
2623 // register command line options to call at the right time. This needs to happen
2624 // after init'ing the ScriptServer, but also after init'ing the ThemeDB,
2625 // for the C# docs generation in the bindings.
2626 List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
2627 BindingsGenerator::handle_cmdline_args(cmdline_args);
2628#endif
2629
2630 if (use_debug_profiler && EngineDebugger::is_active()) {
2631 // Start the "scripts" profiler, used in local debugging.
2632 // We could add more, and make the CLI arg require a comma-separated list of profilers.
2633 EngineDebugger::get_singleton()->profiler_enable("scripts", true);
2634 }
2635
2636 if (!project_manager) {
2637 // If not running the project manager, and now that the engine is
2638 // able to load resources, load the global shader variables.
2639 // If running on editor, don't load the textures because the editor
2640 // may want to import them first. Editor will reload those later.
2641 rendering_server->global_shader_parameters_load_settings(!editor);
2642 }
2643
2644 _start_success = true;
2645
2646 ClassDB::set_current_api(ClassDB::API_NONE); //no more APIs are registered at this point
2647
2648 print_verbose("CORE API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_CORE)));
2649 print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR)));
2650 MAIN_PRINT("Main: Done");
2651
2652 OS::get_singleton()->benchmark_end_measure("scene");
2653
2654 return OK;
2655}
2656
2657String Main::get_rendering_driver_name() {
2658 return rendering_driver;
2659}
2660
2661// everything the main loop needs to know about frame timings
2662static MainTimerSync main_timer_sync;
2663
2664bool Main::start() {
2665 ERR_FAIL_COND_V(!_start_success, false);
2666
2667 bool has_icon = false;
2668 String positional_arg;
2669 String game_path;
2670 String script;
2671 String main_loop_type;
2672 bool check_only = false;
2673
2674#ifdef TOOLS_ENABLED
2675 String doc_tool_path;
2676 bool doc_base = true;
2677 String _export_preset;
2678 bool export_debug = false;
2679 bool export_pack_only = false;
2680#ifdef MODULE_GDSCRIPT_ENABLED
2681 String gdscript_docs_path;
2682#endif
2683#ifndef DISABLE_DEPRECATED
2684 bool converting_project = false;
2685 bool validating_converting_project = false;
2686#endif // DISABLE_DEPRECATED
2687#endif // TOOLS_ENABLED
2688
2689 main_timer_sync.init(OS::get_singleton()->get_ticks_usec());
2690 List<String> args = OS::get_singleton()->get_cmdline_args();
2691
2692 for (int i = 0; i < args.size(); i++) {
2693 // First check parameters that do not have an argument to the right.
2694
2695 // Doctest Unit Testing Handler
2696 // Designed to override and pass arguments to the unit test handler.
2697 if (args[i] == "--check-only") {
2698 check_only = true;
2699#ifdef TOOLS_ENABLED
2700 } else if (args[i] == "--no-docbase") {
2701 doc_base = false;
2702#ifndef DISABLE_DEPRECATED
2703 } else if (args[i] == "--convert-3to4") {
2704 converting_project = true;
2705 } else if (args[i] == "--validate-conversion-3to4") {
2706 validating_converting_project = true;
2707#endif // DISABLE_DEPRECATED
2708 } else if (args[i] == "-e" || args[i] == "--editor") {
2709 editor = true;
2710 } else if (args[i] == "-p" || args[i] == "--project-manager") {
2711 project_manager = true;
2712#endif // TOOLS_ENABLED
2713 } else if (args[i].length() && args[i][0] != '-' && positional_arg.is_empty()) {
2714 positional_arg = args[i];
2715
2716 if (args[i].ends_with(".scn") ||
2717 args[i].ends_with(".tscn") ||
2718 args[i].ends_with(".escn") ||
2719 args[i].ends_with(".res") ||
2720 args[i].ends_with(".tres")) {
2721 // Only consider the positional argument to be a scene path if it ends with
2722 // a file extension associated with Godot scenes. This makes it possible
2723 // for projects to parse command-line arguments for custom CLI arguments
2724 // or other file extensions without trouble. This can be used to implement
2725 // "drag-and-drop onto executable" logic, which can prove helpful
2726 // for non-game applications.
2727 game_path = args[i];
2728 }
2729 }
2730 // Then parameters that have an argument to the right.
2731 else if (i < (args.size() - 1)) {
2732 bool parsed_pair = true;
2733 if (args[i] == "-s" || args[i] == "--script") {
2734 script = args[i + 1];
2735 } else if (args[i] == "--main-loop") {
2736 main_loop_type = args[i + 1];
2737#ifdef TOOLS_ENABLED
2738 } else if (args[i] == "--doctool") {
2739 doc_tool_path = args[i + 1];
2740 if (doc_tool_path.begins_with("-")) {
2741 // Assuming other command line arg, so default to cwd.
2742 doc_tool_path = ".";
2743 parsed_pair = false;
2744 }
2745#ifdef MODULE_GDSCRIPT_ENABLED
2746 } else if (args[i] == "--gdscript-docs") {
2747 gdscript_docs_path = args[i + 1];
2748#endif
2749 } else if (args[i] == "--export-release") {
2750 editor = true; //needs editor
2751 _export_preset = args[i + 1];
2752 } else if (args[i] == "--export-debug") {
2753 editor = true; //needs editor
2754 _export_preset = args[i + 1];
2755 export_debug = true;
2756 } else if (args[i] == "--export-pack") {
2757 editor = true;
2758 _export_preset = args[i + 1];
2759 export_pack_only = true;
2760#endif
2761 } else {
2762 // The parameter does not match anything known, don't skip the next argument
2763 parsed_pair = false;
2764 }
2765 if (parsed_pair) {
2766 i++;
2767 }
2768 }
2769#ifdef TOOLS_ENABLED
2770 // Handle case where no path is given to --doctool.
2771 else if (args[i] == "--doctool") {
2772 doc_tool_path = ".";
2773 }
2774#endif
2775 }
2776
2777 uint64_t minimum_time_msec = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/boot_splash/minimum_display_time", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:ms"), 0);
2778 if (Engine::get_singleton()->is_editor_hint()) {
2779 minimum_time_msec = 0;
2780 }
2781
2782#ifdef TOOLS_ENABLED
2783#ifdef MODULE_GDSCRIPT_ENABLED
2784 if (!doc_tool_path.is_empty() && !gdscript_docs_path.is_empty()) {
2785 DocTools docs;
2786 Error err;
2787
2788 Vector<String> paths = get_files_with_extension(gdscript_docs_path, "gd");
2789 ERR_FAIL_COND_V_MSG(paths.size() == 0, false, "Couldn't find any GDScript files under the given directory: " + gdscript_docs_path);
2790
2791 for (const String &path : paths) {
2792 Ref<GDScript> gdscript = ResourceLoader::load(path);
2793 for (const DocData::ClassDoc &class_doc : gdscript->get_documentation()) {
2794 docs.add_doc(class_doc);
2795 }
2796 }
2797
2798 if (doc_tool_path == ".") {
2799 doc_tool_path = "./docs";
2800 }
2801
2802 Ref<DirAccess> da = DirAccess::create_for_path(doc_tool_path);
2803 err = da->make_dir_recursive(doc_tool_path);
2804 ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create GDScript docs directory: " + doc_tool_path + ": " + itos(err));
2805
2806 HashMap<String, String> doc_data_classes;
2807 err = docs.save_classes(doc_tool_path, doc_data_classes, false);
2808 ERR_FAIL_COND_V_MSG(err != OK, false, "Error saving GDScript docs:" + itos(err));
2809
2810 OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
2811 return false;
2812 }
2813#endif // MODULE_GDSCRIPT_ENABLED
2814
2815 if (!doc_tool_path.is_empty()) {
2816 // Needed to instance editor-only classes for their default values
2817 Engine::get_singleton()->set_editor_hint(true);
2818
2819 // Translate the class reference only when `-l LOCALE` parameter is given.
2820 if (!locale.is_empty() && locale != "en") {
2821 load_doc_translations(locale);
2822 }
2823
2824 {
2825 Ref<DirAccess> da = DirAccess::open(doc_tool_path);
2826 ERR_FAIL_COND_V_MSG(da.is_null(), false, "Argument supplied to --doctool must be a valid directory path.");
2827 }
2828
2829#ifndef MODULE_MONO_ENABLED
2830 // Hack to define .NET-specific project settings even on non-.NET builds,
2831 // so that we don't lose their descriptions and default values in DocTools.
2832 // Default values should be synced with mono_gd/gd_mono.cpp.
2833 GLOBAL_DEF("dotnet/project/assembly_name", "");
2834 GLOBAL_DEF("dotnet/project/solution_directory", "");
2835 GLOBAL_DEF(PropertyInfo(Variant::INT, "dotnet/project/assembly_reload_attempts", PROPERTY_HINT_RANGE, "1,16,1,or_greater"), 3);
2836#endif
2837
2838 Error err;
2839 DocTools doc;
2840 doc.generate(doc_base);
2841
2842 DocTools docsrc;
2843 HashMap<String, String> doc_data_classes;
2844 HashSet<String> checked_paths;
2845 print_line("Loading docs...");
2846
2847 for (int i = 0; i < _doc_data_class_path_count; i++) {
2848 // Custom modules are always located by absolute path.
2849 String path = _doc_data_class_paths[i].path;
2850 if (path.is_relative_path()) {
2851 path = doc_tool_path.path_join(path);
2852 }
2853 String name = _doc_data_class_paths[i].name;
2854 doc_data_classes[name] = path;
2855 if (!checked_paths.has(path)) {
2856 checked_paths.insert(path);
2857
2858 // Create the module documentation directory if it doesn't exist
2859 Ref<DirAccess> da = DirAccess::create_for_path(path);
2860 err = da->make_dir_recursive(path);
2861 ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create directory: " + path + ": " + itos(err));
2862
2863 print_line("Loading docs from: " + path);
2864 err = docsrc.load_classes(path);
2865 ERR_FAIL_COND_V_MSG(err != OK, false, "Error loading docs from: " + path + ": " + itos(err));
2866 }
2867 }
2868
2869 String index_path = doc_tool_path.path_join("doc/classes");
2870 // Create the main documentation directory if it doesn't exist
2871 Ref<DirAccess> da = DirAccess::create_for_path(index_path);
2872 err = da->make_dir_recursive(index_path);
2873 ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create index directory: " + index_path + ": " + itos(err));
2874
2875 print_line("Loading classes from: " + index_path);
2876 err = docsrc.load_classes(index_path);
2877 ERR_FAIL_COND_V_MSG(err != OK, false, "Error loading classes from: " + index_path + ": " + itos(err));
2878 checked_paths.insert(index_path);
2879
2880 print_line("Merging docs...");
2881 doc.merge_from(docsrc);
2882
2883 for (const String &E : checked_paths) {
2884 print_line("Erasing old docs at: " + E);
2885 err = DocTools::erase_classes(E);
2886 ERR_FAIL_COND_V_MSG(err != OK, false, "Error erasing old docs at: " + E + ": " + itos(err));
2887 }
2888
2889 print_line("Generating new docs...");
2890 err = doc.save_classes(index_path, doc_data_classes);
2891 ERR_FAIL_COND_V_MSG(err != OK, false, "Error saving new docs:" + itos(err));
2892
2893 print_line("Deleting docs cache...");
2894 if (FileAccess::exists(EditorHelp::get_cache_full_path())) {
2895 DirAccess::remove_file_or_error(EditorHelp::get_cache_full_path());
2896 }
2897
2898 OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
2899 return false;
2900 }
2901
2902 if (dump_gdextension_interface) {
2903 GDExtensionInterfaceDump::generate_gdextension_interface_file("gdextension_interface.h");
2904 }
2905
2906 if (dump_extension_api) {
2907 GDExtensionAPIDump::generate_extension_json_file("extension_api.json");
2908 }
2909
2910 if (dump_gdextension_interface || dump_extension_api) {
2911 OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
2912 return false;
2913 }
2914
2915 if (validate_extension_api) {
2916 bool valid = GDExtensionAPIDump::validate_extension_json_file(validate_extension_api_file) == OK;
2917 OS::get_singleton()->set_exit_code(valid ? EXIT_SUCCESS : EXIT_FAILURE);
2918 return false;
2919 }
2920
2921#ifndef DISABLE_DEPRECATED
2922 if (converting_project) {
2923 int ret = ProjectConverter3To4(converter_max_kb_file, converter_max_line_length).convert();
2924 if (ret) {
2925 OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
2926 }
2927 return false;
2928 }
2929 if (validating_converting_project) {
2930 bool ret = ProjectConverter3To4(converter_max_kb_file, converter_max_line_length).validate_conversion();
2931 if (ret) {
2932 OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
2933 }
2934 return false;
2935 }
2936#endif // DISABLE_DEPRECATED
2937
2938#endif // TOOLS_ENABLED
2939
2940 if (script.is_empty() && game_path.is_empty() && String(GLOBAL_GET("application/run/main_scene")) != "") {
2941 game_path = GLOBAL_GET("application/run/main_scene");
2942 }
2943
2944#ifdef TOOLS_ENABLED
2945 if (!editor && !project_manager && !cmdline_tool && script.is_empty() && game_path.is_empty()) {
2946 // If we end up here, it means we didn't manage to detect what we want to run.
2947 // Let's throw an error gently. The code leading to this is pretty brittle so
2948 // this might end up triggered by valid usage, in which case we'll have to
2949 // fine-tune further.
2950 OS::get_singleton()->alert("Couldn't detect whether to run the editor, the project manager or a specific project. Aborting.");
2951 ERR_FAIL_V_MSG(false, "Couldn't detect whether to run the editor, the project manager or a specific project. Aborting.");
2952 }
2953#endif
2954
2955 MainLoop *main_loop = nullptr;
2956 if (editor) {
2957 main_loop = memnew(SceneTree);
2958 }
2959 if (main_loop_type.is_empty()) {
2960 main_loop_type = GLOBAL_GET("application/run/main_loop_type");
2961 }
2962
2963 if (!script.is_empty()) {
2964 Ref<Script> script_res = ResourceLoader::load(script);
2965 ERR_FAIL_COND_V_MSG(script_res.is_null(), false, "Can't load script: " + script);
2966
2967 if (check_only) {
2968 if (!script_res->is_valid()) {
2969 OS::get_singleton()->set_exit_code(EXIT_FAILURE);
2970 } else {
2971 OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
2972 }
2973 return false;
2974 }
2975
2976 if (script_res->can_instantiate()) {
2977 StringName instance_type = script_res->get_instance_base_type();
2978 Object *obj = ClassDB::instantiate(instance_type);
2979 MainLoop *script_loop = Object::cast_to<MainLoop>(obj);
2980 if (!script_loop) {
2981 if (obj) {
2982 memdelete(obj);
2983 }
2984 OS::get_singleton()->alert(vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", script));
2985 ERR_FAIL_V_MSG(false, vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", script));
2986 }
2987
2988 script_loop->set_script(script_res);
2989 main_loop = script_loop;
2990 } else {
2991 return false;
2992 }
2993 } else { // Not based on script path.
2994 if (!editor && !ClassDB::class_exists(main_loop_type) && ScriptServer::is_global_class(main_loop_type)) {
2995 String script_path = ScriptServer::get_global_class_path(main_loop_type);
2996 Ref<Script> script_res = ResourceLoader::load(script_path);
2997 if (script_res.is_null()) {
2998 OS::get_singleton()->alert("Error: Could not load MainLoop script type: " + main_loop_type);
2999 ERR_FAIL_V_MSG(false, vformat("Could not load global class %s.", main_loop_type));
3000 }
3001 StringName script_base = script_res->get_instance_base_type();
3002 Object *obj = ClassDB::instantiate(script_base);
3003 MainLoop *script_loop = Object::cast_to<MainLoop>(obj);
3004 if (!script_loop) {
3005 if (obj) {
3006 memdelete(obj);
3007 }
3008 OS::get_singleton()->alert("Error: Invalid MainLoop script base type: " + script_base);
3009 ERR_FAIL_V_MSG(false, vformat("The global class %s does not inherit from SceneTree or MainLoop.", main_loop_type));
3010 }
3011 script_loop->set_script(script_res);
3012 main_loop = script_loop;
3013 }
3014 }
3015
3016 if (!main_loop && main_loop_type.is_empty()) {
3017 main_loop_type = "SceneTree";
3018 }
3019
3020 if (!main_loop) {
3021 if (!ClassDB::class_exists(main_loop_type)) {
3022 OS::get_singleton()->alert("Error: MainLoop type doesn't exist: " + main_loop_type);
3023 return false;
3024 } else {
3025 Object *ml = ClassDB::instantiate(main_loop_type);
3026 ERR_FAIL_NULL_V_MSG(ml, false, "Can't instance MainLoop type.");
3027
3028 main_loop = Object::cast_to<MainLoop>(ml);
3029 if (!main_loop) {
3030 memdelete(ml);
3031 ERR_FAIL_V_MSG(false, "Invalid MainLoop type.");
3032 }
3033 }
3034 }
3035
3036 OS::get_singleton()->set_main_loop(main_loop);
3037
3038 SceneTree *sml = Object::cast_to<SceneTree>(main_loop);
3039 if (sml) {
3040#ifdef DEBUG_ENABLED
3041 if (debug_collisions) {
3042 sml->set_debug_collisions_hint(true);
3043 }
3044 if (debug_paths) {
3045 sml->set_debug_paths_hint(true);
3046 }
3047 if (debug_navigation) {
3048 sml->set_debug_navigation_hint(true);
3049 NavigationServer3D::get_singleton()->set_debug_navigation_enabled(true);
3050 }
3051 if (debug_avoidance) {
3052 NavigationServer3D::get_singleton()->set_debug_avoidance_enabled(true);
3053 }
3054 if (debug_navigation || debug_avoidance) {
3055 NavigationServer3D::get_singleton()->set_active(true);
3056 NavigationServer3D::get_singleton()->set_debug_enabled(true);
3057 }
3058#endif
3059
3060 if (single_threaded_scene) {
3061 sml->set_disable_node_threading(true);
3062 }
3063
3064 bool embed_subwindows = GLOBAL_GET("display/window/subwindows/embed_subwindows");
3065
3066 if (single_window || (!project_manager && !editor && embed_subwindows) || !DisplayServer::get_singleton()->has_feature(DisplayServer::Feature::FEATURE_SUBWINDOWS)) {
3067 sml->get_root()->set_embedding_subwindows(true);
3068 }
3069
3070 ResourceLoader::add_custom_loaders();
3071 ResourceSaver::add_custom_savers();
3072
3073 if (!project_manager && !editor) { // game
3074 if (!game_path.is_empty() || !script.is_empty()) {
3075 //autoload
3076 OS::get_singleton()->benchmark_begin_measure("load_autoloads");
3077 HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
3078
3079 //first pass, add the constants so they exist before any script is loaded
3080 for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
3081 const ProjectSettings::AutoloadInfo &info = E.value;
3082
3083 if (info.is_singleton) {
3084 for (int i = 0; i < ScriptServer::get_language_count(); i++) {
3085 ScriptServer::get_language(i)->add_global_constant(info.name, Variant());
3086 }
3087 }
3088 }
3089
3090 //second pass, load into global constants
3091 List<Node *> to_add;
3092 for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
3093 const ProjectSettings::AutoloadInfo &info = E.value;
3094
3095 Node *n = nullptr;
3096 if (ResourceLoader::get_resource_type(info.path) == "PackedScene") {
3097 // Cache the scene reference before loading it (for cyclic references)
3098 Ref<PackedScene> scn;
3099 scn.instantiate();
3100 scn->set_path(info.path);
3101 scn->reload_from_file();
3102 ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Can't autoload: %s.", info.path));
3103
3104 if (scn.is_valid()) {
3105 n = scn->instantiate();
3106 }
3107 } else {
3108 Ref<Resource> res = ResourceLoader::load(info.path);
3109 ERR_CONTINUE_MSG(res.is_null(), vformat("Can't autoload: %s.", info.path));
3110
3111 Ref<Script> script_res = res;
3112 if (script_res.is_valid()) {
3113 StringName ibt = script_res->get_instance_base_type();
3114 bool valid_type = ClassDB::is_parent_class(ibt, "Node");
3115 ERR_CONTINUE_MSG(!valid_type, vformat("Script does not inherit from Node: %s.", info.path));
3116
3117 Object *obj = ClassDB::instantiate(ibt);
3118
3119 ERR_CONTINUE_MSG(!obj, vformat("Cannot instance script for autoload, expected 'Node' inheritance, got: %s."));
3120
3121 n = Object::cast_to<Node>(obj);
3122 n->set_script(script_res);
3123 }
3124 }
3125
3126 ERR_CONTINUE_MSG(!n, vformat("Path in autoload not a node or script: %s.", info.path));
3127 n->set_name(info.name);
3128
3129 //defer so references are all valid on _ready()
3130 to_add.push_back(n);
3131
3132 if (info.is_singleton) {
3133 for (int i = 0; i < ScriptServer::get_language_count(); i++) {
3134 ScriptServer::get_language(i)->add_global_constant(info.name, n);
3135 }
3136 }
3137 }
3138
3139 for (Node *E : to_add) {
3140 sml->get_root()->add_child(E);
3141 }
3142 OS::get_singleton()->benchmark_end_measure("load_autoloads");
3143 }
3144 }
3145
3146#ifdef TOOLS_ENABLED
3147 EditorNode *editor_node = nullptr;
3148 if (editor) {
3149 OS::get_singleton()->benchmark_begin_measure("editor");
3150 editor_node = memnew(EditorNode);
3151 sml->get_root()->add_child(editor_node);
3152
3153 if (!_export_preset.is_empty()) {
3154 editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only);
3155 game_path = ""; // Do not load anything.
3156 }
3157
3158 OS::get_singleton()->benchmark_end_measure("editor");
3159 }
3160#endif
3161 sml->set_auto_accept_quit(GLOBAL_GET("application/config/auto_accept_quit"));
3162 sml->set_quit_on_go_back(GLOBAL_GET("application/config/quit_on_go_back"));
3163
3164 if (!editor && !project_manager) {
3165 //standard helpers that can be changed from main config
3166
3167 String stretch_mode = GLOBAL_GET("display/window/stretch/mode");
3168 String stretch_aspect = GLOBAL_GET("display/window/stretch/aspect");
3169 Size2i stretch_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"),
3170 GLOBAL_GET("display/window/size/viewport_height"));
3171 real_t stretch_scale = GLOBAL_GET("display/window/stretch/scale");
3172 String stretch_scale_mode = GLOBAL_GET("display/window/stretch/scale_mode");
3173
3174 Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED;
3175 if (stretch_mode == "canvas_items") {
3176 cs_sm = Window::CONTENT_SCALE_MODE_CANVAS_ITEMS;
3177 } else if (stretch_mode == "viewport") {
3178 cs_sm = Window::CONTENT_SCALE_MODE_VIEWPORT;
3179 }
3180
3181 Window::ContentScaleAspect cs_aspect = Window::CONTENT_SCALE_ASPECT_IGNORE;
3182 if (stretch_aspect == "keep") {
3183 cs_aspect = Window::CONTENT_SCALE_ASPECT_KEEP;
3184 } else if (stretch_aspect == "keep_width") {
3185 cs_aspect = Window::CONTENT_SCALE_ASPECT_KEEP_WIDTH;
3186 } else if (stretch_aspect == "keep_height") {
3187 cs_aspect = Window::CONTENT_SCALE_ASPECT_KEEP_HEIGHT;
3188 } else if (stretch_aspect == "expand") {
3189 cs_aspect = Window::CONTENT_SCALE_ASPECT_EXPAND;
3190 }
3191
3192 Window::ContentScaleStretch cs_stretch = Window::CONTENT_SCALE_STRETCH_FRACTIONAL;
3193 if (stretch_scale_mode == "integer") {
3194 cs_stretch = Window::CONTENT_SCALE_STRETCH_INTEGER;
3195 }
3196
3197 sml->get_root()->set_content_scale_mode(cs_sm);
3198 sml->get_root()->set_content_scale_aspect(cs_aspect);
3199 sml->get_root()->set_content_scale_stretch(cs_stretch);
3200 sml->get_root()->set_content_scale_size(stretch_size);
3201 sml->get_root()->set_content_scale_factor(stretch_scale);
3202
3203 sml->set_auto_accept_quit(GLOBAL_GET("application/config/auto_accept_quit"));
3204 sml->set_quit_on_go_back(GLOBAL_GET("application/config/quit_on_go_back"));
3205 String appname = GLOBAL_GET("application/config/name");
3206 appname = TranslationServer::get_singleton()->translate(appname);
3207#ifdef DEBUG_ENABLED
3208 // Append a suffix to the window title to denote that the project is running
3209 // from a debug build (including the editor). Since this results in lower performance,
3210 // this should be clearly presented to the user.
3211 DisplayServer::get_singleton()->window_set_title(vformat("%s (DEBUG)", appname));
3212#else
3213 DisplayServer::get_singleton()->window_set_title(appname);
3214#endif
3215
3216 bool snap_controls = GLOBAL_GET("gui/common/snap_controls_to_pixels");
3217 sml->get_root()->set_snap_controls_to_pixels(snap_controls);
3218
3219 bool font_oversampling = GLOBAL_GET("gui/fonts/dynamic_fonts/use_oversampling");
3220 sml->get_root()->set_use_font_oversampling(font_oversampling);
3221
3222 int texture_filter = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_filter");
3223 int texture_repeat = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_repeat");
3224 sml->get_root()->set_default_canvas_item_texture_filter(
3225 Viewport::DefaultCanvasItemTextureFilter(texture_filter));
3226 sml->get_root()->set_default_canvas_item_texture_repeat(
3227 Viewport::DefaultCanvasItemTextureRepeat(texture_repeat));
3228 }
3229
3230#ifdef TOOLS_ENABLED
3231 if (editor) {
3232 bool editor_embed_subwindows = EditorSettings::get_singleton()->get_setting(
3233 "interface/editor/single_window_mode");
3234
3235 if (editor_embed_subwindows) {
3236 sml->get_root()->set_embedding_subwindows(true);
3237 }
3238 }
3239#endif
3240
3241 String local_game_path;
3242 if (!game_path.is_empty() && !project_manager) {
3243 local_game_path = game_path.replace("\\", "/");
3244
3245 if (!local_game_path.begins_with("res://")) {
3246 bool absolute =
3247 (local_game_path.size() > 1) && (local_game_path[0] == '/' || local_game_path[1] == ':');
3248
3249 if (!absolute) {
3250 if (ProjectSettings::get_singleton()->is_using_datapack()) {
3251 local_game_path = "res://" + local_game_path;
3252
3253 } else {
3254 int sep = local_game_path.rfind("/");
3255
3256 if (sep == -1) {
3257 Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
3258 local_game_path = da->get_current_dir().path_join(local_game_path);
3259 } else {
3260 Ref<DirAccess> da = DirAccess::open(local_game_path.substr(0, sep));
3261 if (da.is_valid()) {
3262 local_game_path = da->get_current_dir().path_join(
3263 local_game_path.substr(sep + 1, local_game_path.length()));
3264 }
3265 }
3266 }
3267 }
3268 }
3269
3270 local_game_path = ProjectSettings::get_singleton()->localize_path(local_game_path);
3271
3272#ifdef TOOLS_ENABLED
3273 if (editor) {
3274 if (game_path != String(GLOBAL_GET("application/run/main_scene")) || !editor_node->has_scenes_in_session()) {
3275 Error serr = editor_node->load_scene(local_game_path);
3276 if (serr != OK) {
3277 ERR_PRINT("Failed to load scene");
3278 }
3279 }
3280 DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_EDITOR);
3281 if (!debug_server_uri.is_empty()) {
3282 EditorDebuggerNode::get_singleton()->start(debug_server_uri);
3283 EditorDebuggerNode::get_singleton()->set_keep_open(true);
3284 }
3285 }
3286#endif
3287 if (!editor) {
3288 DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_ENGINE);
3289 }
3290 }
3291
3292 if (!project_manager && !editor) { // game
3293
3294 OS::get_singleton()->benchmark_begin_measure("game_load");
3295
3296 // Load SSL Certificates from Project Settings (or builtin).
3297 Crypto::load_default_certificates(GLOBAL_GET("network/tls/certificate_bundle_override"));
3298
3299 if (!game_path.is_empty()) {
3300 Node *scene = nullptr;
3301 Ref<PackedScene> scenedata = ResourceLoader::load(local_game_path);
3302 if (scenedata.is_valid()) {
3303 scene = scenedata->instantiate();
3304 }
3305
3306 ERR_FAIL_NULL_V_MSG(scene, false, "Failed loading scene: " + local_game_path + ".");
3307 sml->add_current_scene(scene);
3308
3309#ifdef MACOS_ENABLED
3310 String mac_icon_path = GLOBAL_GET("application/config/macos_native_icon");
3311 if (!mac_icon_path.is_empty()) {
3312 DisplayServer::get_singleton()->set_native_icon(mac_icon_path);
3313 has_icon = true;
3314 }
3315#endif
3316
3317#ifdef WINDOWS_ENABLED
3318 String win_icon_path = GLOBAL_GET("application/config/windows_native_icon");
3319 if (!win_icon_path.is_empty()) {
3320 DisplayServer::get_singleton()->set_native_icon(win_icon_path);
3321 has_icon = true;
3322 }
3323#endif
3324
3325 String icon_path = GLOBAL_GET("application/config/icon");
3326 if ((!icon_path.is_empty()) && (!has_icon)) {
3327 Ref<Image> icon;
3328 icon.instantiate();
3329 if (ImageLoader::load_image(icon_path, icon) == OK) {
3330 DisplayServer::get_singleton()->set_icon(icon);
3331 has_icon = true;
3332 }
3333 }
3334 }
3335
3336 OS::get_singleton()->benchmark_end_measure("game_load");
3337 }
3338
3339#ifdef TOOLS_ENABLED
3340 if (project_manager) {
3341 OS::get_singleton()->benchmark_begin_measure("project_manager");
3342 Engine::get_singleton()->set_editor_hint(true);
3343 ProjectManager *pmanager = memnew(ProjectManager);
3344 ProgressDialog *progress_dialog = memnew(ProgressDialog);
3345 pmanager->add_child(progress_dialog);
3346 sml->get_root()->add_child(pmanager);
3347 DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_PROJECTMAN);
3348 OS::get_singleton()->benchmark_end_measure("project_manager");
3349 }
3350
3351 if (project_manager || editor) {
3352 // Load SSL Certificates from Editor Settings (or builtin)
3353 Crypto::load_default_certificates(
3354 EditorSettings::get_singleton()->get_setting("network/tls/editor_tls_certificates").operator String());
3355 }
3356#endif
3357 }
3358
3359 if (!has_icon && OS::get_singleton()->get_bundle_icon_path().is_empty()) {
3360 Ref<Image> icon = memnew(Image(app_icon_png));
3361 DisplayServer::get_singleton()->set_icon(icon);
3362 }
3363
3364 if (movie_writer) {
3365 movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path());
3366 }
3367
3368 if (minimum_time_msec) {
3369 uint64_t minimum_time = 1000 * minimum_time_msec;
3370 uint64_t elapsed_time = OS::get_singleton()->get_ticks_usec();
3371 if (elapsed_time < minimum_time) {
3372 OS::get_singleton()->delay_usec(minimum_time - elapsed_time);
3373 }
3374 }
3375
3376 OS::get_singleton()->benchmark_end_measure("startup_begin");
3377 OS::get_singleton()->benchmark_dump();
3378
3379 return true;
3380}
3381
3382/* Main iteration
3383 *
3384 * This is the iteration of the engine's game loop, advancing the state of physics,
3385 * rendering and audio.
3386 * It's called directly by the platform's OS::run method, where the loop is created
3387 * and monitored.
3388 *
3389 * The OS implementation can impact its draw step with the Main::force_redraw() method.
3390 */
3391
3392uint64_t Main::last_ticks = 0;
3393uint32_t Main::frames = 0;
3394uint32_t Main::hide_print_fps_attempts = 3;
3395uint32_t Main::frame = 0;
3396bool Main::force_redraw_requested = false;
3397int Main::iterating = 0;
3398bool Main::agile_input_event_flushing = false;
3399
3400bool Main::is_iterating() {
3401 return iterating > 0;
3402}
3403
3404// For performance metrics.
3405static uint64_t physics_process_max = 0;
3406static uint64_t process_max = 0;
3407static uint64_t navigation_process_max = 0;
3408
3409bool Main::iteration() {
3410 //for now do not error on this
3411 //ERR_FAIL_COND_V(iterating, false);
3412
3413 iterating++;
3414
3415 const uint64_t ticks = OS::get_singleton()->get_ticks_usec();
3416 Engine::get_singleton()->_frame_ticks = ticks;
3417 main_timer_sync.set_cpu_ticks_usec(ticks);
3418 main_timer_sync.set_fixed_fps(fixed_fps);
3419
3420 const uint64_t ticks_elapsed = ticks - last_ticks;
3421
3422 const int physics_ticks_per_second = Engine::get_singleton()->get_physics_ticks_per_second();
3423 const double physics_step = 1.0 / physics_ticks_per_second;
3424
3425 const double time_scale = Engine::get_singleton()->get_time_scale();
3426
3427 MainFrameTime advance = main_timer_sync.advance(physics_step, physics_ticks_per_second);
3428 double process_step = advance.process_step;
3429 double scaled_step = process_step * time_scale;
3430
3431 Engine::get_singleton()->_process_step = process_step;
3432 Engine::get_singleton()->_physics_interpolation_fraction = advance.interpolation_fraction;
3433
3434 uint64_t physics_process_ticks = 0;
3435 uint64_t process_ticks = 0;
3436 uint64_t navigation_process_ticks = 0;
3437
3438 frame += ticks_elapsed;
3439
3440 last_ticks = ticks;
3441
3442 const int max_physics_steps = Engine::get_singleton()->get_max_physics_steps_per_frame();
3443 if (fixed_fps == -1 && advance.physics_steps > max_physics_steps) {
3444 process_step -= (advance.physics_steps - max_physics_steps) * physics_step;
3445 advance.physics_steps = max_physics_steps;
3446 }
3447
3448 bool exit = false;
3449
3450 // process all our active interfaces
3451 XRServer::get_singleton()->_process();
3452
3453 for (int iters = 0; iters < advance.physics_steps; ++iters) {
3454 if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) {
3455 Input::get_singleton()->flush_buffered_events();
3456 }
3457
3458 Engine::get_singleton()->_in_physics = true;
3459
3460 uint64_t physics_begin = OS::get_singleton()->get_ticks_usec();
3461
3462 PhysicsServer3D::get_singleton()->sync();
3463 PhysicsServer3D::get_singleton()->flush_queries();
3464
3465 PhysicsServer2D::get_singleton()->sync();
3466 PhysicsServer2D::get_singleton()->flush_queries();
3467
3468 if (OS::get_singleton()->get_main_loop()->physics_process(physics_step * time_scale)) {
3469 PhysicsServer3D::get_singleton()->end_sync();
3470 PhysicsServer2D::get_singleton()->end_sync();
3471
3472 exit = true;
3473 break;
3474 }
3475
3476 uint64_t navigation_begin = OS::get_singleton()->get_ticks_usec();
3477
3478 NavigationServer3D::get_singleton()->process(physics_step * time_scale);
3479
3480 navigation_process_ticks = MAX(navigation_process_ticks, OS::get_singleton()->get_ticks_usec() - navigation_begin); // keep the largest one for reference
3481 navigation_process_max = MAX(OS::get_singleton()->get_ticks_usec() - navigation_begin, navigation_process_max);
3482
3483 message_queue->flush();
3484
3485 PhysicsServer3D::get_singleton()->end_sync();
3486 PhysicsServer3D::get_singleton()->step(physics_step * time_scale);
3487
3488 PhysicsServer2D::get_singleton()->end_sync();
3489 PhysicsServer2D::get_singleton()->step(physics_step * time_scale);
3490
3491 message_queue->flush();
3492
3493 physics_process_ticks = MAX(physics_process_ticks, OS::get_singleton()->get_ticks_usec() - physics_begin); // keep the largest one for reference
3494 physics_process_max = MAX(OS::get_singleton()->get_ticks_usec() - physics_begin, physics_process_max);
3495 Engine::get_singleton()->_physics_frames++;
3496
3497 Engine::get_singleton()->_in_physics = false;
3498 }
3499
3500 if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) {
3501 Input::get_singleton()->flush_buffered_events();
3502 }
3503
3504 uint64_t process_begin = OS::get_singleton()->get_ticks_usec();
3505
3506 if (OS::get_singleton()->get_main_loop()->process(process_step * time_scale)) {
3507 exit = true;
3508 }
3509 message_queue->flush();
3510
3511 RenderingServer::get_singleton()->sync(); //sync if still drawing from previous frames.
3512
3513 if (DisplayServer::get_singleton()->can_any_window_draw() &&
3514 RenderingServer::get_singleton()->is_render_loop_enabled()) {
3515 if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) {
3516 if (RenderingServer::get_singleton()->has_changed()) {
3517 RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
3518 Engine::get_singleton()->frames_drawn++;
3519 }
3520 } else {
3521 RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
3522 Engine::get_singleton()->frames_drawn++;
3523 force_redraw_requested = false;
3524 }
3525 }
3526
3527 process_ticks = OS::get_singleton()->get_ticks_usec() - process_begin;
3528 process_max = MAX(process_ticks, process_max);
3529 uint64_t frame_time = OS::get_singleton()->get_ticks_usec() - ticks;
3530
3531 for (int i = 0; i < ScriptServer::get_language_count(); i++) {
3532 ScriptServer::get_language(i)->frame();
3533 }
3534
3535 AudioServer::get_singleton()->update();
3536
3537 if (EngineDebugger::is_active()) {
3538 EngineDebugger::get_singleton()->iteration(frame_time, process_ticks, physics_process_ticks, physics_step);
3539 }
3540
3541 frames++;
3542 Engine::get_singleton()->_process_frames++;
3543
3544 if (frame > 1000000) {
3545 // Wait a few seconds before printing FPS, as FPS reporting just after the engine has started is inaccurate.
3546 if (hide_print_fps_attempts == 0) {
3547 if (editor || project_manager) {
3548 if (print_fps) {
3549 print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
3550 }
3551 } else if (print_fps || GLOBAL_GET("debug/settings/stdout/print_fps")) {
3552 print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
3553 }
3554 } else {
3555 hide_print_fps_attempts--;
3556 }
3557
3558 Engine::get_singleton()->_fps = frames;
3559 performance->set_process_time(USEC_TO_SEC(process_max));
3560 performance->set_physics_process_time(USEC_TO_SEC(physics_process_max));
3561 performance->set_navigation_process_time(USEC_TO_SEC(navigation_process_max));
3562 process_max = 0;
3563 physics_process_max = 0;
3564 navigation_process_max = 0;
3565
3566 frame %= 1000000;
3567 frames = 0;
3568 }
3569
3570 iterating--;
3571
3572 // Needed for OSs using input buffering regardless accumulation (like Android)
3573 if (Input::get_singleton()->is_using_input_buffering() && !agile_input_event_flushing) {
3574 Input::get_singleton()->flush_buffered_events();
3575 }
3576
3577 if (movie_writer) {
3578 movie_writer->add_frame();
3579 }
3580
3581 if ((quit_after > 0) && (Engine::get_singleton()->_process_frames >= quit_after)) {
3582 exit = true;
3583 }
3584
3585 if (fixed_fps != -1) {
3586 return exit;
3587 }
3588
3589 OS::get_singleton()->add_frame_delay(DisplayServer::get_singleton()->window_can_draw());
3590
3591#ifdef TOOLS_ENABLED
3592 if (auto_build_solutions) {
3593 auto_build_solutions = false;
3594 // Only relevant when running the editor.
3595 if (!editor) {
3596 OS::get_singleton()->set_exit_code(EXIT_FAILURE);
3597 ERR_FAIL_V_MSG(true,
3598 "Command line option --build-solutions was passed, but no project is being edited. Aborting.");
3599 }
3600 if (!EditorNode::get_singleton()->call_build()) {
3601 OS::get_singleton()->set_exit_code(EXIT_FAILURE);
3602 ERR_FAIL_V_MSG(true,
3603 "Command line option --build-solutions was passed, but the build callback failed. Aborting.");
3604 }
3605 }
3606#endif
3607
3608 return exit;
3609}
3610
3611void Main::force_redraw() {
3612 force_redraw_requested = true;
3613}
3614
3615/* Engine deinitialization
3616 *
3617 * Responsible for freeing all the memory allocated by previous setup steps,
3618 * so that the engine closes cleanly without leaking memory or crashing.
3619 * The order matters as some of those steps are linked with each other.
3620 */
3621void Main::cleanup(bool p_force) {
3622 OS::get_singleton()->benchmark_begin_measure("Main::cleanup");
3623 if (!p_force) {
3624 ERR_FAIL_COND(!_start_success);
3625 }
3626
3627 for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
3628 TextServerManager::get_singleton()->get_interface(i)->cleanup();
3629 }
3630
3631 if (movie_writer) {
3632 movie_writer->end();
3633 }
3634
3635 ResourceLoader::clear_thread_load_tasks();
3636
3637 ResourceLoader::remove_custom_loaders();
3638 ResourceSaver::remove_custom_savers();
3639
3640 // Flush before uninitializing the scene, but delete the MessageQueue as late as possible.
3641 message_queue->flush();
3642
3643 OS::get_singleton()->delete_main_loop();
3644
3645 OS::get_singleton()->_cmdline.clear();
3646 OS::get_singleton()->_user_args.clear();
3647 OS::get_singleton()->_execpath = "";
3648 OS::get_singleton()->_local_clipboard = "";
3649
3650 ResourceLoader::clear_translation_remaps();
3651 ResourceLoader::clear_path_remaps();
3652
3653 ScriptServer::finish_languages();
3654
3655 // Sync pending commands that may have been queued from a different thread during ScriptServer finalization
3656 RenderingServer::get_singleton()->sync();
3657
3658 //clear global shader variables before scene and other graphics stuff are deinitialized.
3659 rendering_server->global_shader_parameters_clear();
3660
3661 if (xr_server) {
3662 // Now that we're unregistering properly in plugins we need to keep access to xr_server for a little longer
3663 // We do however unset our primary interface
3664 xr_server->set_primary_interface(Ref<XRInterface>());
3665 }
3666
3667#ifdef TOOLS_ENABLED
3668 GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
3669 uninitialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
3670 unregister_editor_types();
3671
3672#endif
3673
3674 ImageLoader::cleanup();
3675
3676 GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
3677 uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
3678
3679 unregister_platform_apis();
3680 unregister_driver_types();
3681 unregister_scene_types();
3682
3683 finalize_theme_db();
3684
3685 // Before deinitializing server extensions, finalize servers which may be loaded as extensions.
3686 finalize_navigation_server();
3687 finalize_physics();
3688
3689 GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
3690 uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
3691 unregister_server_types();
3692
3693 EngineDebugger::deinitialize();
3694
3695 if (xr_server) {
3696 memdelete(xr_server);
3697 }
3698
3699 if (audio_server) {
3700 audio_server->finish();
3701 memdelete(audio_server);
3702 }
3703
3704 if (camera_server) {
3705 memdelete(camera_server);
3706 }
3707
3708 OS::get_singleton()->finalize();
3709
3710 finalize_display();
3711
3712 if (input) {
3713 memdelete(input);
3714 }
3715
3716 if (packed_data) {
3717 memdelete(packed_data);
3718 }
3719 if (performance) {
3720 memdelete(performance);
3721 }
3722 if (input_map) {
3723 memdelete(input_map);
3724 }
3725 if (time_singleton) {
3726 memdelete(time_singleton);
3727 }
3728 if (translation_server) {
3729 memdelete(translation_server);
3730 }
3731 if (tsman) {
3732 memdelete(tsman);
3733 }
3734 if (physics_server_3d_manager) {
3735 memdelete(physics_server_3d_manager);
3736 }
3737 if (physics_server_2d_manager) {
3738 memdelete(physics_server_2d_manager);
3739 }
3740 if (globals) {
3741 memdelete(globals);
3742 }
3743 if (engine) {
3744 memdelete(engine);
3745 }
3746
3747 if (OS::get_singleton()->is_restart_on_exit_set()) {
3748 //attempt to restart with arguments
3749 List<String> args = OS::get_singleton()->get_restart_on_exit_arguments();
3750 OS::get_singleton()->create_instance(args);
3751 OS::get_singleton()->set_restart_on_exit(false, List<String>()); //clear list (uses memory)
3752 }
3753
3754 // Now should be safe to delete MessageQueue (famous last words).
3755 message_queue->flush();
3756 memdelete(message_queue);
3757
3758 unregister_core_driver_types();
3759 unregister_core_extensions();
3760 uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
3761 unregister_core_types();
3762
3763 OS::get_singleton()->benchmark_end_measure("Main::cleanup");
3764 OS::get_singleton()->benchmark_dump();
3765
3766 OS::get_singleton()->finalize_core();
3767}
3768