/**************************************************************************/ /* main.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ #include "main.h" #include "core/config/project_settings.h" #include "core/core_globals.h" #include "core/core_string_names.h" #include "core/crypto/crypto.h" #include "core/debugger/engine_debugger.h" #include "core/extension/extension_api_dump.h" #include "core/extension/gdextension_interface_dump.gen.h" #include "core/extension/gdextension_manager.h" #include "core/input/input.h" #include "core/input/input_map.h" #include "core/io/dir_access.h" #include "core/io/file_access_pack.h" #include "core/io/file_access_zip.h" #include "core/io/image_loader.h" #include "core/io/ip.h" #include "core/io/resource_loader.h" #include "core/object/message_queue.h" #include "core/os/os.h" #include "core/os/time.h" #include "core/register_core_types.h" #include "core/string/translation.h" #include "core/version.h" #include "drivers/register_driver_types.h" #include "main/app_icon.gen.h" #include "main/main_timer_sync.h" #include "main/performance.h" #include "main/splash.gen.h" #include "modules/register_module_types.h" #include "platform/register_platform_apis.h" #include "scene/main/scene_tree.h" #include "scene/main/window.h" #include "scene/register_scene_types.h" #include "scene/resources/packed_scene.h" #include "scene/theme/theme_db.h" #include "servers/audio_server.h" #include "servers/camera_server.h" #include "servers/display_server.h" #include "servers/movie_writer/movie_writer.h" #include "servers/movie_writer/movie_writer_mjpeg.h" #include "servers/navigation_server_2d.h" #include "servers/navigation_server_2d_dummy.h" #include "servers/navigation_server_3d.h" #include "servers/navigation_server_3d_dummy.h" #include "servers/physics_server_2d.h" #include "servers/physics_server_3d.h" #include "servers/register_server_types.h" #include "servers/rendering/rendering_server_default.h" #include "servers/text/text_server_dummy.h" #include "servers/text_server.h" #include "servers/xr_server.h" #ifdef TESTS_ENABLED #include "tests/test_main.h" #endif #ifdef TOOLS_ENABLED #include "editor/debugger/editor_debugger_node.h" #include "editor/doc_data_class_path.gen.h" #include "editor/doc_tools.h" #include "editor/editor_help.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_settings.h" #include "editor/editor_translation.h" #include "editor/progress_dialog.h" #include "editor/project_manager.h" #include "editor/register_editor_types.h" #ifndef NO_EDITOR_SPLASH #include "main/splash_editor.gen.h" #endif #ifndef DISABLE_DEPRECATED #include "editor/project_converter_3_to_4.h" #endif // DISABLE_DEPRECATED #endif // TOOLS_ENABLED #include "modules/modules_enabled.gen.h" // For mono. #if defined(MODULE_MONO_ENABLED) && defined(TOOLS_ENABLED) #include "modules/mono/editor/bindings_generator.h" #endif #ifdef MODULE_GDSCRIPT_ENABLED #include "modules/gdscript/gdscript.h" #if defined(TOOLS_ENABLED) && !defined(GDSCRIPT_NO_LSP) #include "modules/gdscript/language_server/gdscript_language_server.h" #endif // TOOLS_ENABLED && !GDSCRIPT_NO_LSP #endif // MODULE_GDSCRIPT_ENABLED /* Static members */ // Singletons // Initialized in setup() static Engine *engine = nullptr; static ProjectSettings *globals = nullptr; static Input *input = nullptr; static InputMap *input_map = nullptr; static TranslationServer *translation_server = nullptr; static Performance *performance = nullptr; static PackedData *packed_data = nullptr; #ifdef MINIZIP_ENABLED static ZipArchive *zip_packed_data = nullptr; #endif static MessageQueue *message_queue = nullptr; // Initialized in setup2() static AudioServer *audio_server = nullptr; static DisplayServer *display_server = nullptr; static RenderingServer *rendering_server = nullptr; static CameraServer *camera_server = nullptr; static XRServer *xr_server = nullptr; static TextServerManager *tsman = nullptr; static PhysicsServer3DManager *physics_server_3d_manager = nullptr; static PhysicsServer3D *physics_server_3d = nullptr; static PhysicsServer2DManager *physics_server_2d_manager = nullptr; static PhysicsServer2D *physics_server_2d = nullptr; static NavigationServer3D *navigation_server_3d = nullptr; static NavigationServer2D *navigation_server_2d = nullptr; static ThemeDB *theme_db = nullptr; // We error out if setup2() doesn't turn this true static bool _start_success = false; // Drivers String display_driver = ""; String tablet_driver = ""; String text_driver = ""; String rendering_driver = ""; String rendering_method = ""; static int text_driver_idx = -1; static int audio_driver_idx = -1; // Engine config/tools static bool single_window = false; static bool editor = false; static bool project_manager = false; static bool cmdline_tool = false; static String locale; static String log_file; static bool show_help = false; static uint64_t quit_after = 0; static OS::ProcessID editor_pid = 0; #ifdef TOOLS_ENABLED static bool found_project = false; static bool auto_build_solutions = false; static String debug_server_uri; #ifndef DISABLE_DEPRECATED static int converter_max_kb_file = 4 * 1024; // 4MB static int converter_max_line_length = 100000; #endif // DISABLE_DEPRECATED HashMap> forwardable_cli_arguments; #endif static bool single_threaded_scene = false; // Display static DisplayServer::WindowMode window_mode = DisplayServer::WINDOW_MODE_WINDOWED; static DisplayServer::ScreenOrientation window_orientation = DisplayServer::SCREEN_LANDSCAPE; static DisplayServer::VSyncMode window_vsync_mode = DisplayServer::VSYNC_ENABLED; static uint32_t window_flags = 0; static Size2i window_size = Size2i(1152, 648); static int init_screen = DisplayServer::SCREEN_PRIMARY; static bool init_fullscreen = false; static bool init_maximized = false; static bool init_windowed = false; static bool init_always_on_top = false; static bool init_use_custom_pos = false; static bool init_use_custom_screen = false; static Vector2 init_custom_pos; // Debug static bool use_debug_profiler = false; #ifdef DEBUG_ENABLED static bool debug_collisions = false; static bool debug_paths = false; static bool debug_navigation = false; static bool debug_avoidance = false; static bool debug_canvas_item_redraw = false; #endif static int max_fps = -1; static int frame_delay = 0; static int audio_output_latency = 0; static bool disable_render_loop = false; static int fixed_fps = -1; static MovieWriter *movie_writer = nullptr; static bool disable_vsync = false; static bool print_fps = false; #ifdef TOOLS_ENABLED static bool dump_gdextension_interface = false; static bool dump_extension_api = false; static bool include_docs_in_extension_api_dump = false; static bool validate_extension_api = false; static String validate_extension_api_file; #endif bool profile_gpu = false; // Constants. static const String NULL_DISPLAY_DRIVER("headless"); static const String NULL_AUDIO_DRIVER("Dummy"); // The length of the longest column in the command-line help we should align to // (excluding the 2-space left and right margins). // Currently, this is `--export-release `. static const int OPTION_COLUMN_LENGTH = 32; /* Helper methods */ bool Main::is_cmdline_tool() { return cmdline_tool; } #ifdef TOOLS_ENABLED const Vector &Main::get_forwardable_cli_arguments(Main::CLIScope p_scope) { return forwardable_cli_arguments[p_scope]; } #endif static String unescape_cmdline(const String &p_str) { return p_str.replace("%20", " "); } static String get_full_version_string() { String hash = String(VERSION_HASH); if (!hash.is_empty()) { hash = "." + hash.left(9); } return String(VERSION_FULL_BUILD) + hash; } #if defined(TOOLS_ENABLED) && defined(MODULE_GDSCRIPT_ENABLED) static Vector get_files_with_extension(const String &p_root, const String &p_extension) { Vector paths; Ref dir = DirAccess::open(p_root); if (dir.is_valid()) { dir->list_dir_begin(); String fn = dir->get_next(); while (!fn.is_empty()) { if (!dir->current_is_hidden() && fn != "." && fn != "..") { if (dir->current_is_dir()) { paths.append_array(get_files_with_extension(p_root.path_join(fn), p_extension)); } else if (fn.get_extension() == p_extension) { paths.append(p_root.path_join(fn)); } } fn = dir->get_next(); } dir->list_dir_end(); } return paths; } #endif // FIXME: Could maybe be moved to have less code in main.cpp. void initialize_physics() { /// 3D Physics Server physics_server_3d = PhysicsServer3DManager::get_singleton()->new_server( GLOBAL_GET(PhysicsServer3DManager::setting_property_name)); if (!physics_server_3d) { // Physics server not found, Use the default physics physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server(); } ERR_FAIL_NULL(physics_server_3d); physics_server_3d->init(); // 2D Physics server physics_server_2d = PhysicsServer2DManager::get_singleton()->new_server( GLOBAL_GET(PhysicsServer2DManager::get_singleton()->setting_property_name)); if (!physics_server_2d) { // Physics server not found, Use the default physics physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server(); } ERR_FAIL_NULL(physics_server_2d); physics_server_2d->init(); } void finalize_physics() { physics_server_3d->finish(); memdelete(physics_server_3d); physics_server_2d->finish(); memdelete(physics_server_2d); } void finalize_display() { rendering_server->finish(); memdelete(rendering_server); memdelete(display_server); } void initialize_navigation_server() { ERR_FAIL_COND(navigation_server_3d != nullptr); ERR_FAIL_COND(navigation_server_2d != nullptr); // Init 3D Navigation Server navigation_server_3d = NavigationServer3DManager::new_default_server(); // Fall back to dummy if no default server has been registered. if (!navigation_server_3d) { WARN_PRINT_ONCE("No NavigationServer3D implementation has been registered! Falling back to a dummy implementation: navigation features will be unavailable."); navigation_server_3d = memnew(NavigationServer3DDummy); } // Should be impossible, but make sure it's not null. ERR_FAIL_NULL_MSG(navigation_server_3d, "Failed to initialize NavigationServer3D."); navigation_server_3d->init(); // Init 2D Navigation Server navigation_server_2d = NavigationServer2DManager::new_default_server(); if (!navigation_server_2d) { WARN_PRINT_ONCE("No NavigationServer2D implementation has been registered! Falling back to a dummy implementation: navigation features will be unavailable."); navigation_server_2d = memnew(NavigationServer2DDummy); } ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D."); navigation_server_2d->init(); } void finalize_navigation_server() { ERR_FAIL_NULL(navigation_server_3d); navigation_server_3d->finish(); memdelete(navigation_server_3d); navigation_server_3d = nullptr; ERR_FAIL_NULL(navigation_server_2d); navigation_server_2d->finish(); memdelete(navigation_server_2d); navigation_server_2d = nullptr; } void initialize_theme_db() { theme_db = memnew(ThemeDB); } void finalize_theme_db() { memdelete(theme_db); theme_db = nullptr; } //#define DEBUG_INIT #ifdef DEBUG_INIT #define MAIN_PRINT(m_txt) print_line(m_txt) #else #define MAIN_PRINT(m_txt) #endif /** * Prints a copyright notice in the command-line help with colored text. A newline is * automatically added at the end. */ void Main::print_help_copyright(const char *p_notice) { OS::get_singleton()->print("\u001b[90m%s\u001b[0m\n", p_notice); } /** * Prints a title in the command-line help with colored text. A newline is * automatically added at beginning and at the end. */ void Main::print_help_title(const char *p_title) { OS::get_singleton()->print("\n\u001b[1;93m%s:\u001b[0m\n", p_title); } /** * Returns the option string with required and optional arguments colored separately from the rest of the option. * This color replacement must be done *after* calling `rpad()` for the length padding to be done correctly. */ String Main::format_help_option(const char *p_option) { return (String(p_option) .rpad(OPTION_COLUMN_LENGTH) .replace("[", "\u001b[96m[") .replace("]", "]\u001b[0m") .replace("<", "\u001b[95m<") .replace(">", ">\u001b[0m")); } /** * Prints an option in the command-line help with colored text. No newline is * added at the end. `p_availability` denotes which build types the argument is * available in. Support in release export templates implies support in debug * export templates and editor. Support in debug export templates implies * support in editor. */ void Main::print_help_option(const char *p_option, const char *p_description, CLIOptionAvailability p_availability) { const bool option_empty = (p_option && !p_option[0]); if (!option_empty) { const char *availability_badge = ""; switch (p_availability) { case CLI_OPTION_AVAILABILITY_EDITOR: availability_badge = "\u001b[1;91mE"; break; case CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG: availability_badge = "\u001b[1;94mD"; break; case CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE: availability_badge = "\u001b[1;92mR"; break; case CLI_OPTION_AVAILABILITY_HIDDEN: // Use for multiline option names (but not when the option name is empty). availability_badge = " "; break; } OS::get_singleton()->print( " \u001b[92m%s %s\u001b[0m %s", format_help_option(p_option).utf8().ptr(), availability_badge, p_description); } else { // Make continuation lines for descriptions faint if the option name is empty. OS::get_singleton()->print( " \u001b[92m%s \u001b[0m \u001b[90m%s", format_help_option(p_option).utf8().ptr(), p_description); } } void Main::print_help(const char *p_binary) { print_line("\u001b[38;5;39m" + String(VERSION_NAME) + "\u001b[0m v" + get_full_version_string() + " - \u001b[4m" + String(VERSION_WEBSITE) + "\u001b[0m"); print_help_copyright("Free and open source software under the terms of the MIT license."); print_help_copyright("(c) 2014-present Godot Engine contributors. (c) 2007-present Juan Linietsky, Ariel Manzur."); print_help_title("Usage"); OS::get_singleton()->print(" %s \u001b[96m[options] [path to scene or \"project.godot\" file]\u001b[0m\n", p_binary); #if defined(TOOLS_ENABLED) print_help_title("Option legend (this build = editor)"); #elif defined(DEBUG_ENABLED) print_help_title("Option legend (this build = debug export template)"); #else print_help_title("Option legend (this build = release export template)"); #endif OS::get_singleton()->print(" \u001b[1;92mR\u001b[0m Available in editor builds, debug export templates and release export templates.\n"); #ifdef DEBUG_ENABLED OS::get_singleton()->print(" \u001b[1;94mD\u001b[0m Available in editor builds and debug export templates only.\n"); #endif #ifdef TOOLS_ENABLED OS::get_singleton()->print(" \u001b[1;91mE\u001b[0m Only available in editor builds.\n"); #endif print_help_title("General options"); print_help_option("-h, --help", "Display this help message.\n"); print_help_option("--version", "Display the version string.\n"); print_help_option("-v, --verbose", "Use verbose stdout mode.\n"); print_help_option("--quiet", "Quiet mode, silences stdout messages. Errors are still displayed.\n"); print_help_title("Run options"); print_help_option("--, ++", "Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.\n"); #ifdef TOOLS_ENABLED print_help_option("-e, --editor", "Start the editor instead of running the scene.\n", CLI_OPTION_AVAILABILITY_EDITOR); print_help_option("-p, --project-manager", "Start the project manager, even if a project is auto-detected.\n", CLI_OPTION_AVAILABILITY_EDITOR); print_help_option("--debug-server ", "Start the editor debug server (://[:port], e.g. tcp://127.0.0.1:6007)\n", CLI_OPTION_AVAILABILITY_EDITOR); #if defined(MODULE_GDSCRIPT_ENABLED) && !defined(GDSCRIPT_NO_LSP) print_help_option("--lsp-port ", "Use the specified port for the GDScript language server protocol. The port should be between 1025 and 49150.\n", CLI_OPTION_AVAILABILITY_EDITOR); #endif // MODULE_GDSCRIPT_ENABLED && !GDSCRIPT_NO_LSP #endif print_help_option("--quit", "Quit after the first iteration.\n"); print_help_option("--quit-after ", "Quit after the given number of iterations. Set to 0 to disable.\n"); print_help_option("-l, --language ", "Use a specific locale ( being a two-letter code).\n"); print_help_option("--path ", "Path to a project ( must contain a \"project.godot\" file).\n"); print_help_option("-u, --upwards", "Scan folders upwards for project.godot file.\n"); print_help_option("--main-pack ", "Path to a pack (.pck) file to load.\n"); print_help_option("--render-thread ", "Render thread mode (\"unsafe\", \"safe\", \"separate\").\n"); print_help_option("--remote-fs
", "Remote filesystem ([:] address).\n"); print_help_option("--remote-fs-password ", "Password for remote filesystem.\n"); print_help_option("--audio-driver ", "Audio driver ["); for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) { if (i > 0) { OS::get_singleton()->print(", "); } OS::get_singleton()->print("\"%s\"", AudioDriverManager::get_driver(i)->get_name()); } OS::get_singleton()->print("].\n"); print_help_option("--display-driver ", "Display driver (and rendering driver) ["); for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { if (i > 0) { OS::get_singleton()->print(", "); } OS::get_singleton()->print("\"%s\" (", DisplayServer::get_create_function_name(i)); Vector rd = DisplayServer::get_create_function_rendering_drivers(i); for (int j = 0; j < rd.size(); j++) { if (j > 0) { OS::get_singleton()->print(", "); } OS::get_singleton()->print("\"%s\"", rd[j].utf8().get_data()); } OS::get_singleton()->print(")"); } OS::get_singleton()->print("].\n"); print_help_option("--audio-output-latency ", "Override audio output latency in milliseconds (default is 15 ms).\n"); print_help_option("", "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"); print_help_option("--rendering-method ", "Renderer name. Requires driver support.\n"); print_help_option("--rendering-driver ", "Rendering driver (depends on display driver).\n"); print_help_option("--gpu-index ", "Use a specific GPU (run with --verbose to get a list of available devices).\n"); print_help_option("--text-driver ", "Text driver (used for font rendering, bidirectional support and shaping).\n"); print_help_option("--tablet-driver ", "Pen tablet input driver.\n"); print_help_option("--headless", "Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.\n"); print_help_option("--log-file ", "Write output/error log to the specified path instead of the default location defined by the project.\n"); print_help_option("", " path should be absolute or relative to the project directory.\n"); print_help_option("--write-movie ", "Write a video to the specified path (usually with .avi or .png extension).\n"); print_help_option("", "--fixed-fps is forced when enabled, but it can be used to change movie FPS.\n"); print_help_option("", "--disable-vsync can speed up movie writing but makes interaction more difficult.\n"); print_help_option("", "--quit-after can be used to specify the number of frames to write.\n"); print_help_title("Display options"); print_help_option("-f, --fullscreen", "Request fullscreen mode.\n"); print_help_option("-m, --maximized", "Request a maximized window.\n"); print_help_option("-w, --windowed", "Request windowed mode.\n"); print_help_option("-t, --always-on-top", "Request an always-on-top window.\n"); print_help_option("--resolution x", "Request window resolution.\n"); print_help_option("--position ,", "Request window position.\n"); print_help_option("--screen ", "Request window screen.\n"); print_help_option("--single-window", "Use a single window (no separate subwindows).\n"); print_help_option("--xr-mode ", "Select XR (Extended Reality) mode [\"default\", \"off\", \"on\"].\n"); print_help_title("Debug options"); print_help_option("-d, --debug", "Debug (local stdout debugger).\n"); print_help_option("-b, --breakpoints", "Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n"); print_help_option("--profiling", "Enable profiling in the script debugger.\n"); print_help_option("--gpu-profile", "Show a GPU profile of the tasks that took the most time during frame rendering.\n"); print_help_option("--gpu-validation", "Enable graphics API validation layers for debugging.\n"); #ifdef DEBUG_ENABLED print_help_option("--gpu-abort", "Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); #endif print_help_option("--generate-spirv-debug-info", "Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n"); print_help_option("--remote-debug ", "Remote debug (://[:], e.g. tcp://127.0.0.1:6007).\n"); print_help_option("--single-threaded-scene", "Force scene tree to run in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n"); #if defined(DEBUG_ENABLED) print_help_option("--debug-collisions", "Show collision shapes when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); print_help_option("--debug-paths", "Show path lines when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); print_help_option("--debug-navigation", "Show navigation polygons when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); print_help_option("--debug-avoidance", "Show navigation avoidance debug visuals when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); print_help_option("--debug-stringnames", "Print all StringName allocations to stdout when the engine quits.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); print_help_option("--debug-canvas-item-redraw", "Display a rectangle each time a canvas item requests a redraw (useful to troubleshoot low processor mode).\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); #endif print_help_option("--max-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"); print_help_option("--frame-delay ", "Simulate high CPU load (delay each frame by milliseconds). Do not use as a FPS limiter; use --max-fps instead.\n"); print_help_option("--time-scale ", "Force time scale (higher values are faster, 1.0 is normal speed).\n"); print_help_option("--disable-vsync", "Forces disabling of vertical synchronization, even if enabled in the project settings. Does not override driver-level V-Sync enforcement.\n"); print_help_option("--disable-render-loop", "Disable render loop so rendering only occurs when called explicitly from script.\n"); print_help_option("--disable-crash-handler", "Disable crash handler when supported by the platform code.\n"); print_help_option("--fixed-fps ", "Force a fixed number of frames per second. This setting disables real-time synchronization.\n"); print_help_option("--delta-smoothing ", "Enable or disable frame delta smoothing [\"enable\", \"disable\"].\n"); print_help_option("--print-fps", "Print the frames per second to the stdout.\n"); print_help_title("Standalone tools"); print_help_option("-s, --script