#include "main.h"
#include "core/config/project_settings.h"
#include "core/core_globals.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_interface_header_generator.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.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/object/script_language.h"
#include "core/os/os.h"
#include "core/os/time.h"
#include "core/profiling/profiling.h"
#include "core/register_core_types.h"
#include "core/string/translation_server.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/property_list_helper.h"
#include "scene/register_scene_types.h"
#include "scene/resources/packed_scene.h"
#include "scene/theme/theme_db.h"
#include "servers/audio/audio_driver_dummy.h"
#include "servers/audio/audio_server.h"
#include "servers/camera/camera_server.h"
#include "servers/display/display_server.h"
#include "servers/movie_writer/movie_writer.h"
#include "servers/register_server_types.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/text/text_server.h"
#include "servers/text/text_server_dummy.h"
#ifndef NAVIGATION_2D_DISABLED
#include "servers/navigation_2d/navigation_server_2d.h"
#include "servers/navigation_2d/navigation_server_2d_dummy.h"
#endif
#ifndef PHYSICS_2D_DISABLED
#include "servers/physics_2d/physics_server_2d.h"
#include "servers/physics_2d/physics_server_2d_dummy.h"
#endif
#ifndef NAVIGATION_3D_DISABLED
#include "servers/navigation_3d/navigation_server_3d.h"
#include "servers/navigation_3d/navigation_server_3d_dummy.h"
#endif
#ifndef PHYSICS_3D_DISABLED
#include "servers/physics_3d/physics_server_3d.h"
#include "servers/physics_3d/physics_server_3d_dummy.h"
#endif
#ifndef XR_DISABLED
#include "servers/xr/xr_server.h"
#endif
#ifdef TESTS_ENABLED
#include "tests/test_main.h"
#endif
#ifdef TOOLS_ENABLED
#include "editor/debugger/debug_adapter/debug_adapter_server.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/doc/doc_data_class_path.gen.h"
#include "editor/doc/doc_tools.h"
#include "editor/doc/editor_help.h"
#include "editor/editor_node.h"
#include "editor/file_system/editor_file_system.h"
#include "editor/file_system/editor_paths.h"
#include "editor/gui/progress_dialog.h"
#include "editor/project_manager/project_manager.h"
#include "editor/register_editor_types.h"
#include "editor/settings/editor_settings.h"
#include "editor/translations/editor_translation.h"
#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
#include "main/splash_editor.gen.h"
#endif
#ifndef DISABLE_DEPRECATED
#include "editor/project_upgrade/project_converter_3_to_4.h"
#endif
#endif
#if defined(STEAMAPI_ENABLED)
#include "main/steam_tracker.h"
#endif
#include "modules/modules_enabled.gen.h"
#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
#endif
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;
#if defined(STEAMAPI_ENABLED)
static SteamTracker *steam_tracker = nullptr;
#endif
static AudioServer *audio_server = nullptr;
static CameraServer *camera_server = nullptr;
static DisplayServer *display_server = nullptr;
static RenderingServer *rendering_server = nullptr;
static TextServerManager *tsman = nullptr;
static ThemeDB *theme_db = nullptr;
#ifndef PHYSICS_2D_DISABLED
static PhysicsServer2DManager *physics_server_2d_manager = nullptr;
static PhysicsServer2D *physics_server_2d = nullptr;
#endif
#ifndef PHYSICS_3D_DISABLED
static PhysicsServer3DManager *physics_server_3d_manager = nullptr;
static PhysicsServer3D *physics_server_3d = nullptr;
#endif
#ifndef XR_DISABLED
static XRServer *xr_server = nullptr;
#endif
static bool _start_success = false;
String display_driver = "";
String tablet_driver = "";
String text_driver = "";
static int text_driver_idx = -1;
static int audio_driver_idx = -1;
static DisplayServer::AccessibilityMode accessibility_mode = DisplayServer::AccessibilityMode::ACCESSIBILITY_AUTO;
static bool accessibility_mode_set = false;
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 recovery_mode = false;
static bool auto_build_solutions = false;
static String debug_server_uri;
static bool wait_for_import = false;
static bool restore_editor_window_layout = true;
#ifndef DISABLE_DEPRECATED
static int converter_max_kb_file = 4 * 1024;
static int converter_max_line_length = 100000;
#endif
HashMap<Main::CLIScope, Vector<String>> forwardable_cli_arguments;
#endif
static bool single_threaded_scene = false;
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;
static int64_t init_embed_parent_window_id = 0;
#ifdef TOOLS_ENABLED
static bool init_display_scale_found = false;
static int init_display_scale = 0;
static bool init_custom_scale_found = false;
static float init_custom_scale = 1.0;
static bool init_expand_to_title = false;
static bool init_expand_to_title_found = false;
#endif
static bool use_custom_res = true;
static bool force_res = false;
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;
static bool debug_mute_audio = 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 editor_pseudolocalization = false;
static bool dump_gdextension_interface = false;
static bool dump_gdextension_interface_header = 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;
static const String NULL_DISPLAY_DRIVER("headless");
static const String EMBEDDED_DISPLAY_DRIVER("embedded");
static const String NULL_AUDIO_DRIVER("Dummy");
static const int OPTION_COLUMN_LENGTH = 32;
bool Main::is_cmdline_tool() {
return cmdline_tool;
}
#ifdef TOOLS_ENABLED
const Vector<String> &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(GODOT_VERSION_HASH);
if (!hash.is_empty()) {
hash = "." + hash.left(9);
}
return String(GODOT_VERSION_FULL_BUILD) + hash;
}
#if defined(TOOLS_ENABLED) && defined(MODULE_GDSCRIPT_ENABLED)
static Vector<String> get_files_with_extension(const String &p_root, const String &p_extension) {
Vector<String> paths;
Ref<DirAccess> 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
void initialize_physics() {
#ifndef PHYSICS_3D_DISABLED
physics_server_3d = PhysicsServer3DManager::get_singleton()->new_server(
GLOBAL_GET(PhysicsServer3DManager::setting_property_name));
if (!physics_server_3d) {
physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server();
}
if (!physics_server_3d) {
WARN_PRINT(vformat("Falling back to dummy PhysicsServer3D; 3D physics functionality will be disabled. If this is intended, set the %s project setting to Dummy.", PhysicsServer3DManager::setting_property_name));
physics_server_3d = memnew(PhysicsServer3DDummy);
}
ERR_FAIL_NULL_MSG(physics_server_3d, "Failed to initialize PhysicsServer3D.");
physics_server_3d->init();
#endif
#ifndef PHYSICS_2D_DISABLED
physics_server_2d = PhysicsServer2DManager::get_singleton()->new_server(
GLOBAL_GET(PhysicsServer2DManager::get_singleton()->setting_property_name));
if (!physics_server_2d) {
physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server();
}
if (!physics_server_2d) {
WARN_PRINT(vformat("Falling back to dummy PhysicsServer2D; 2D physics functionality will be disabled. If this is intended, set the %s project setting to Dummy.", PhysicsServer2DManager::setting_property_name));
physics_server_2d = memnew(PhysicsServer2DDummy);
}
ERR_FAIL_NULL_MSG(physics_server_2d, "Failed to initialize PhysicsServer2D.");
physics_server_2d->init();
#endif
}
void finalize_physics() {
#ifndef PHYSICS_3D_DISABLED
physics_server_3d->finish();
memdelete(physics_server_3d);
#endif
#ifndef PHYSICS_2D_DISABLED
physics_server_2d->finish();
memdelete(physics_server_2d);
#endif
}
void finalize_display() {
rendering_server->finish();
memdelete(rendering_server);
memdelete(display_server);
}
void initialize_theme_db() {
theme_db = memnew(ThemeDB);
}
void finalize_theme_db() {
memdelete(theme_db);
theme_db = nullptr;
}
#ifdef DEBUG_INIT
#define MAIN_PRINT(m_txt) print_line(m_txt)
#else
#define MAIN_PRINT(m_txt)
#endif
void Main::print_header(bool p_rich) {
if (GODOT_VERSION_TIMESTAMP > 0) {
if (p_rich) {
Engine::get_singleton()->print_header_rich("\u001b[38;5;39m" + String(GODOT_VERSION_NAME) + "\u001b[0m v" + get_full_version_string() + " (" + Time::get_singleton()->get_datetime_string_from_unix_time(GODOT_VERSION_TIMESTAMP, true) + " UTC) - \u001b[4m" + String(GODOT_VERSION_WEBSITE));
} else {
Engine::get_singleton()->print_header(String(GODOT_VERSION_NAME) + " v" + get_full_version_string() + " (" + Time::get_singleton()->get_datetime_string_from_unix_time(GODOT_VERSION_TIMESTAMP, true) + " UTC) - " + String(GODOT_VERSION_WEBSITE));
}
} else {
if (p_rich) {
Engine::get_singleton()->print_header_rich("\u001b[38;5;39m" + String(GODOT_VERSION_NAME) + "\u001b[0m v" + get_full_version_string() + " - \u001b[4m" + String(GODOT_VERSION_WEBSITE));
} else {
Engine::get_singleton()->print_header(String(GODOT_VERSION_NAME) + " v" + get_full_version_string() + " - " + String(GODOT_VERSION_WEBSITE));
}
}
}
void Main::print_help_copyright(const char *p_notice) {
OS::get_singleton()->print("\u001b[90m%s\u001b[0m\n", p_notice);
}
void Main::print_help_title(const char *p_title) {
OS::get_singleton()->print("\n\u001b[1;93m%s:\u001b[0m\n", p_title);
}
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"));
}
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_UNSAFE:
availability_badge = "\u001b[1;93mX";
break;
case CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE:
availability_badge = "\u001b[1;92mR";
break;
case CLI_OPTION_AVAILABILITY_HIDDEN:
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 {
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_header(true);
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 \"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
#if defined(OVERRIDE_PATH_ENABLED)
OS::get_singleton()->print(" \u001b[1;93mX\u001b[0m Only available in editor builds, and export templates compiled with `disable_path_overrides=false`.\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_option("--no-header", "Do not print engine version and rendering driver/method header on startup.\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("--recovery-mode", "Start the editor in recovery mode, which disables features that can typically cause startup crashes, such as tool scripts, editor plugins, GDExtension addons, and others.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--debug-server <uri>", "Start the editor debug server (<protocol>://<host/IP>[:port], e.g. tcp://127.0.0.1:6007)\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--dap-port <port>", "Use the specified port for the GDScript Debug Adapter Protocol. Recommended port range [1024, 49151].\n", CLI_OPTION_AVAILABILITY_EDITOR);
#if defined(MODULE_GDSCRIPT_ENABLED) && !defined(GDSCRIPT_NO_LSP)
print_help_option("--lsp-port <port>", "Use the specified port for the GDScript Language Server Protocol. Recommended port range [1024, 49151].\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
#endif
print_help_option("--quit", "Quit after the first iteration.\n");
print_help_option("--quit-after <int>", "Quit after the given number of iterations. Set to 0 to disable.\n");
print_help_option("-l, --language <locale>", "Use a specific locale (<locale> being a two-letter code).\n");
#if defined(OVERRIDE_PATH_ENABLED)
print_help_option("--path <directory>", "Path to a project (<directory> must contain a \"project.godot\" file).\n", CLI_OPTION_AVAILABILITY_TEMPLATE_UNSAFE);
print_help_option("--scene <path>", "Path or UID of a scene in the project that should be started.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_UNSAFE);
print_help_option("--main-pack <file>", "Path to a pack (.pck) file to load.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_UNSAFE);
#endif
#ifdef DISABLE_DEPRECATED
print_help_option("--render-thread <mode>", "Render thread mode (\"safe\", \"separate\").\n");
#else
print_help_option("--render-thread <mode>", "Render thread mode (\"unsafe\" [deprecated], \"safe\", \"separate\").\n");
#endif
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
print_help_option("--remote-fs <address>", "Remote filesystem (<host/IP>[:<port>] address).\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
print_help_option("--remote-fs-password <password>", "Password for remote filesystem.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
#endif
print_help_option("--audio-driver <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 <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<String> 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 <ms>", "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>", "Renderer name. Requires driver support.\n");
print_help_option("--rendering-driver <driver>", "Rendering driver (depends on display driver).\n");
print_help_option("--gpu-index <device_index>", "Use a specific GPU (run with --verbose to get a list of available devices).\n");
print_help_option("--text-driver <driver>", "Text driver (used for font rendering, bidirectional support and shaping).\n");
print_help_option("--tablet-driver <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 <file>", "Write output/error log to the specified path instead of the default location defined by the project.\n");
print_help_option("", "<file> path should be absolute or relative to the project directory.\n");
print_help_option("--write-movie <file>", "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 <W>x<H>", "Request window resolution.\n");
print_help_option("--position <X>,<Y>", "Request window position.\n");
print_help_option("--screen <N>", "Request window screen.\n");
print_help_option("--single-window", "Use a single window (no separate subwindows).\n");
#ifndef _3D_DISABLED
print_help_option("--xr-mode <mode>", "Select XR (Extended Reality) mode [\"default\", \"off\", \"on\"].\n");
#endif
print_help_option("--wid <window_id>", "Request parented to window.\n");
print_help_option("--accessibility <mode>", "Select accessibility mode ['auto' (when screen reader is running, default), 'always', 'disabled'].\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("--ignore-error-breaks", "If debugger is connected, prevents sending error breakpoints.\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 (Vulkan only). This allows source-level shader debugging with RenderDoc.\n");
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
print_help_option("--extra-gpu-memory-tracking", "Enables additional memory tracking (see class reference for `RenderingDevice.get_driver_and_device_memory_report()` and linked methods). Currently only implemented for Vulkan. Enabling this feature may cause crashes on some systems due to buggy drivers or bugs in the Vulkan Loader. See https://github.com/godotengine/godot/issues/95967\n");
print_help_option("--accurate-breadcrumbs", "Force barriers between breadcrumbs. Useful for narrowing down a command causing GPU resets. Currently only implemented for Vulkan.\n");
#endif
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
print_help_option("--remote-debug <uri>", "Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
#endif
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");
#ifdef 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 <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 <ms>", "Simulate high CPU load (delay each frame by <ms> milliseconds). Do not use as a FPS limiter; use --max-fps instead.\n");
print_help_option("--time-scale <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 <fps>", "Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
print_help_option("--delta-smoothing <enable>", "Enable or disable frame delta smoothing [\"enable\", \"disable\"].\n");
print_help_option("--print-fps", "Print the frames per second to the stdout.\n");
#ifdef TOOLS_ENABLED
print_help_option("--editor-pseudolocalization", "Enable pseudolocalization for the editor and the project manager.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
#if defined(OVERRIDE_PATH_ENABLED) || defined(TESTS_ENABLED)
print_help_title("Standalone tools");
#endif
#if defined(OVERRIDE_PATH_ENABLED)
print_help_option("-s, --script <script>", "Run a script.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_UNSAFE);
print_help_option("--main-loop <main_loop_name>", "Run a MainLoop specified by its global class name.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_UNSAFE);
print_help_option("--check-only", "Only parse for errors and quit (use with --script).\n", CLI_OPTION_AVAILABILITY_TEMPLATE_UNSAFE);
#endif
#ifdef TOOLS_ENABLED
print_help_option("--import", "Starts the editor, waits for any resources to be imported, and then quits.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--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", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("", "<path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. \"builds/game.exe\").\n");
print_help_option("", "The target directory must exist.\n");
print_help_option("--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", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--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", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--export-patch <preset> <path>", "Export pack with changed files only. See --export-pack description for other considerations.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--patches <paths>", "List of patches to use with --export-patch. The list is comma-separated.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--install-android-build-template", "Install the Android build template. Used in conjunction with --export-release or --export-debug.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#ifndef DISABLE_DEPRECATED
print_help_option("--convert-3to4 ", "\n", CLI_OPTION_AVAILABILITY_HIDDEN);
print_help_option(" [max_file_kb] [max_line_size]", "Converts project from Godot 3.x to Godot 4.x.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--validate-conversion-3to4 ", "\n", CLI_OPTION_AVAILABILITY_HIDDEN);
print_help_option(" [max_file_kb] [max_line_size]", "Shows what elements will be renamed when converting project from Godot 3.x to Godot 4.x.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
print_help_option("--doctool [path]", "Dump the engine API reference to the given <path> (defaults to current directory) in XML format, merging if existing files are found.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--no-docbase", "Disallow dumping the base types (used with --doctool).\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--gdextension-docs", "Rather than dumping the engine API, generate API reference from all the GDExtensions loaded in the current project (used with --doctool).\n", CLI_OPTION_AVAILABILITY_EDITOR);
#ifdef MODULE_GDSCRIPT_ENABLED
print_help_option("--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", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
print_help_option("--build-solutions", "Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--dump-gdextension-interface", "Generate a GDExtension header file \"gdextension_interface.h\" in the current folder. This file is the base file required to implement a GDExtension.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--dump-gdextension-interface-json", "Generate a JSON dump of the GDExtension interface named \"gdextension_interface.json\" in the current folder.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--dump-extension-api", "Generate a JSON dump of the Godot API for GDExtension bindings named \"extension_api.json\" in the current folder.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--dump-extension-api-with-docs", "Generate JSON dump of the Godot API like the previous option, but including documentation.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--validate-extension-api <path>", "Validate an extension API file dumped (with one of the two previous options) from a previous version of the engine to ensure API compatibility.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("", "If incompatibilities or errors are detected, the exit code will be non-zero.\n");
print_help_option("--benchmark", "Benchmark the run time and print it to console.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--benchmark-file <path>", "Benchmark the run time and save it to a given file in JSON format. The path should be absolute.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
#ifdef TESTS_ENABLED
print_help_option("--test [--help]", "Run unit tests. Use --test --help for more information.\n");
#endif
OS::get_singleton()->print("\n");
}
#ifdef TESTS_ENABLED
Error Main::test_setup() {
Thread::make_main_thread();
set_current_thread_safe_for_nodes(true);
OS::get_singleton()->initialize();
engine = memnew(Engine);
register_core_types();
register_core_driver_types();
packed_data = memnew(PackedData);
globals = memnew(ProjectSettings);
register_core_settings();
translation_server = memnew(TranslationServer);
tsman = memnew(TextServerManager);
if (tsman) {
Ref<TextServerDummy> ts;
ts.instantiate();
tsman->add_interface(ts);
}
#ifndef PHYSICS_3D_DISABLED
physics_server_3d_manager = memnew(PhysicsServer3DManager);
#endif
#ifndef PHYSICS_2D_DISABLED
physics_server_2d_manager = memnew(PhysicsServer2DManager);
#endif
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2DManager::initialize_server_manager();
#endif
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3DManager::initialize_server_manager();
#endif
register_early_core_singletons();
initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
register_core_extensions();
register_core_singletons();
register_server_types();
#ifndef XR_DISABLED
XRServer::set_xr_mode(XRServer::XRMODE_OFF);
#endif
initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
translation_server->setup();
if (!locale.is_empty()) {
translation_server->set_locale(locale);
}
translation_server->load_project_translations(translation_server->get_main_domain());
ResourceLoader::load_translation_remaps();
initialize_theme_db();
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3DManager::initialize_server();
#endif
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2DManager::initialize_server();
#endif
register_scene_types();
register_driver_types();
register_scene_singletons();
initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
#ifdef TOOLS_ENABLED
ClassDB::set_current_api(ClassDB::API_EDITOR);
register_editor_types();
initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
ClassDB::set_current_api(ClassDB::API_CORE);
#endif
register_platform_apis();
theme_db->initialize_theme_noproject();
ERR_FAIL_COND_V(TextServerManager::get_singleton()->get_interface_count() == 0, ERR_CANT_CREATE);
int max_features = 0;
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features();
int feature_number = 0;
while (features) {
feature_number += features & 1;
features >>= 1;
}
if (feature_number >= max_features) {
max_features = feature_number;
text_driver_idx = i;
}
}
if (text_driver_idx >= 0) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(text_driver_idx);
TextServerManager::get_singleton()->set_primary_interface(ts);
if (ts->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
ts->load_support_data("res://" + ts->get_support_data_filename());
}
} else {
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface.");
}
ClassDB::set_current_api(ClassDB::API_NONE);
_start_success = true;
return OK;
}
void Main::test_cleanup() {
ERR_FAIL_COND(!_start_success);
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
TextServerManager::get_singleton()->get_interface(i)->cleanup();
}
ResourceLoader::remove_custom_loaders();
ResourceSaver::remove_custom_savers();
PropertyListHelper::clear_base_helpers();
#ifdef TOOLS_ENABLED
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
unregister_editor_types();
#endif
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
unregister_platform_apis();
unregister_driver_types();
unregister_scene_types();
finalize_theme_db();
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2DManager::finalize_server();
NavigationServer2DManager::finalize_server_manager();
#endif
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3DManager::finalize_server();
NavigationServer3DManager::finalize_server_manager();
#endif
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
unregister_server_types();
EngineDebugger::deinitialize();
OS::get_singleton()->finalize();
if (packed_data) {
memdelete(packed_data);
}
if (translation_server) {
memdelete(translation_server);
}
if (tsman) {
memdelete(tsman);
}
#ifndef PHYSICS_3D_DISABLED
if (physics_server_3d_manager) {
memdelete(physics_server_3d_manager);
}
#endif
#ifndef PHYSICS_2D_DISABLED
if (physics_server_2d_manager) {
memdelete(physics_server_2d_manager);
}
#endif
if (globals) {
memdelete(globals);
}
unregister_core_driver_types();
unregister_core_extensions();
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
if (engine) {
memdelete(engine);
}
unregister_core_types();
OS::get_singleton()->finalize_core();
}
#endif
int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) {
for (int x = 0; x < argc; x++) {
if ((strlen(argv[x]) == 2) && ((strncmp(argv[x], "--", 2) == 0) || (strncmp(argv[x], "++", 2) == 0))) {
tests_need_run = false;
return EXIT_SUCCESS;
}
if ((strncmp(argv[x], "--test", 6) == 0) && (strlen(argv[x]) == 6)) {
tests_need_run = true;
#ifdef TESTS_ENABLED
test_setup();
int status = test_main(argc, argv);
test_cleanup();
return status;
#else
ERR_PRINT(
"`--test` was specified on the command line, but this Godot binary was compiled without support for unit tests. Aborting.\n"
"To be able to run unit tests, use the `tests=yes` SCons option when compiling Godot.\n");
return EXIT_FAILURE;
#endif
}
}
tests_need_run = false;
return EXIT_SUCCESS;
}
Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_phase) {
GodotProfileZone("setup");
Thread::make_main_thread();
set_current_thread_safe_for_nodes(true);
OS::get_singleton()->initialize();
#if !defined(OVERRIDE_PATH_ENABLED) && !defined(TOOLS_ENABLED)
String old_cwd = OS::get_singleton()->get_cwd();
#if defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
String new_cwd = OS::get_singleton()->get_bundle_resource_dir();
if (new_cwd.is_empty() || !new_cwd.is_absolute_path()) {
new_cwd = OS::get_singleton()->get_executable_path().get_base_dir();
}
#else
String new_cwd = OS::get_singleton()->get_executable_path().get_base_dir();
#endif
if (!new_cwd.is_empty()) {
OS::get_singleton()->set_cwd(new_cwd);
}
#endif
OS::get_singleton()->benchmark_begin_measure("Startup", "Main::Setup");
engine = memnew(Engine);
MAIN_PRINT("Main: Initialize CORE");
register_core_types();
register_core_driver_types();
MAIN_PRINT("Main: Initialize Globals");
input_map = memnew(InputMap);
globals = memnew(ProjectSettings);
register_core_settings();
translation_server = memnew(TranslationServer);
performance = memnew(Performance);
GDREGISTER_CLASS(Performance);
engine->add_singleton(Engine::Singleton("Performance", performance));
GLOBAL_DEF_RST("application/run/flush_stdout_on_print", false);
GLOBAL_DEF_RST("application/run/flush_stdout_on_print.debug", true);
MAIN_PRINT("Main: Parse CMDLine");
List<String> args;
List<String> main_args;
List<String> user_args;
bool adding_user_args = false;
List<String> platform_args = OS::get_singleton()->get_cmdline_platform_args();
for (int i = 0; i < argc; i++) {
args.push_back(String::utf8(argv[i]));
}
for (const String &arg : platform_args) {
args.push_back(arg);
}
List<String>::Element *I = args.front();
while (I) {
I->get() = unescape_cmdline(I->get().strip_edges());
I = I->next();
}
String audio_driver = "";
String project_path = ".";
String debug_uri = "";
#if defined(TOOLS_ENABLED) && (defined(WINDOWS_ENABLED) || defined(LINUXBSD_ENABLED))
bool test_rd_creation = false;
bool test_rd_support = false;
#endif
bool skip_breakpoints = false;
bool ignore_error_breaks = false;
String main_pack;
bool quiet_stdout = false;
int separate_thread_render = -1;
String remotefs;
String remotefs_pass;
Vector<String> breakpoints;
bool delta_smoothing_override = false;
bool load_shell_env = false;
String rendering_driver = "";
String rendering_method = "";
OS::RenderingSource rendering_driver_source = OS::RenderingSource::RENDERING_SOURCE_DEFAULT;
OS::RenderingSource rendering_method_source = OS::RenderingSource::RENDERING_SOURCE_DEFAULT;
String default_renderer = "";
String default_renderer_mobile = "";
String renderer_hints = "";
packed_data = PackedData::get_singleton();
if (!packed_data) {
packed_data = memnew(PackedData);
}
#ifdef MINIZIP_ENABLED
zip_packed_data = ZipArchive::get_singleton();
if (!zip_packed_data) {
zip_packed_data = memnew(ZipArchive);
}
packed_data->add_pack_source(zip_packed_data);
#endif
Error exit_err = ERR_INVALID_PARAMETER;
I = args.front();
while (I) {
List<String>::Element *N = I->next();
const String &arg = I->get();
#ifdef MACOS_ENABLED
if (arg.begins_with("-psn_")) {
I = N;
continue;
}
#endif
#ifdef TOOLS_ENABLED
if (arg == "--debug" ||
arg == "--verbose" ||
arg == "--disable-crash-handler") {
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(arg);
forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(arg);
}
if (arg == "--single-window" || arg == "--editor-pseudolocalization") {
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(arg);
}
if (arg == "--audio-driver" ||
arg == "--display-driver" ||
arg == "--rendering-method" ||
arg == "--rendering-driver" ||
arg == "--xr-mode" ||
arg == "-l" ||
arg == "--language") {
if (N) {
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(arg);
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(N->get());
}
}
if (arg == "--gpu-index") {
if (N) {
const String &next_arg = N->get();
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(arg);
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(next_arg);
forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(arg);
forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(next_arg);
}
}
#endif
if (adding_user_args) {
user_args.push_back(arg);
} else if (arg == "-h" || arg == "--help" || arg == "/?") {
show_help = true;
exit_err = ERR_HELP;
goto error;
} else if (arg == "--version") {
print_line(get_full_version_string());
exit_err = ERR_HELP;
goto error;
} else if (arg == "-v" || arg == "--verbose") {
OS::get_singleton()->_verbose_stdout = true;
} else if (arg == "-q" || arg == "--quiet") {
quiet_stdout = true;
} else if (arg == "--no-header") {
Engine::get_singleton()->_print_header = false;
} else if (arg == "--audio-driver") {
if (N) {
audio_driver = N->get();
bool found = false;
for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
if (audio_driver == AudioDriverManager::get_driver(i)->get_name()) {
found = true;
}
}
if (!found) {
OS::get_singleton()->print("Unknown audio driver '%s', aborting.\nValid options are ",
audio_driver.utf8().get_data());
for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
if (i == AudioDriverManager::get_driver_count() - 1) {
OS::get_singleton()->print(" and ");
} else if (i != 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s'", AudioDriverManager::get_driver(i)->get_name());
}
OS::get_singleton()->print(".\n");
goto error;
}
N = N->next();
} else {
OS::get_singleton()->print("Missing audio driver argument, aborting.\n");
goto error;
}
} else if (arg == "--audio-output-latency") {
if (N) {
audio_output_latency = N->get().to_int();
N = N->next();
} else {
OS::get_singleton()->print("Missing audio output latency argument, aborting.\n");
goto error;
}
} else if (arg == "--text-driver") {
if (N) {
text_driver = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing text driver argument, aborting.\n");
goto error;
}
} else if (arg == "--display-driver") {
if (N) {
display_driver = N->get();
bool found = false;
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (display_driver == DisplayServer::get_create_function_name(i)) {
found = true;
}
}
if (!found) {
OS::get_singleton()->print("Unknown display driver '%s', aborting.\nValid options are ",
display_driver.utf8().get_data());
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (i == DisplayServer::get_create_function_count() - 1) {
OS::get_singleton()->print(" and ");
} else if (i != 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s'", DisplayServer::get_create_function_name(i));
}
OS::get_singleton()->print(".\n");
goto error;
}
N = N->next();
} else {
OS::get_singleton()->print("Missing display driver argument, aborting.\n");
goto error;
}
} else if (arg == "--rendering-method") {
if (N) {
rendering_method = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing renderer name argument, aborting.\n");
goto error;
}
} else if (arg == "--rendering-driver") {
if (N) {
rendering_driver = N->get();
rendering_driver_source = OS::RenderingSource::RENDERING_SOURCE_COMMANDLINE;
N = N->next();
} else {
OS::get_singleton()->print("Missing rendering driver argument, aborting.\n");
goto error;
}
} else if (arg == "-f" || arg == "--fullscreen") {
init_fullscreen = true;
window_mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
} else if (arg == "-m" || arg == "--maximized") {
init_maximized = true;
window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
} else if (arg == "-w" || arg == "--windowed") {
init_windowed = true;
} else if (arg == "--gpu-index") {
if (N) {
Engine::singleton->gpu_idx = N->get().to_int();
N = N->next();
} else {
OS::get_singleton()->print("Missing GPU index argument, aborting.\n");
goto error;
}
} else if (arg == "--gpu-validation") {
Engine::singleton->use_validation_layers = true;
#ifdef DEBUG_ENABLED
} else if (arg == "--gpu-abort") {
Engine::singleton->abort_on_gpu_errors = true;
#endif
} else if (arg == "--generate-spirv-debug-info") {
Engine::singleton->generate_spirv_debug_info = true;
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
} else if (arg == "--extra-gpu-memory-tracking") {
Engine::singleton->extra_gpu_memory_tracking = true;
} else if (arg == "--accurate-breadcrumbs") {
Engine::singleton->accurate_breadcrumbs = true;
#endif
} else if (arg == "--tablet-driver") {
if (N) {
tablet_driver = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing tablet driver argument, aborting.\n");
goto error;
}
} else if (arg == "--delta-smoothing") {
if (N) {
String string = N->get();
bool recognized = false;
if (string == "enable") {
OS::get_singleton()->set_delta_smoothing(true);
delta_smoothing_override = true;
recognized = true;
}
if (string == "disable") {
OS::get_singleton()->set_delta_smoothing(false);
delta_smoothing_override = false;
recognized = true;
}
if (!recognized) {
OS::get_singleton()->print("Delta-smoothing argument not recognized, aborting.\n");
goto error;
}
N = N->next();
} else {
OS::get_singleton()->print("Missing delta-smoothing argument, aborting.\n");
goto error;
}
} else if (arg == "--single-window") {
single_window = true;
} else if (arg == "--accessibility") {
if (N) {
String string = N->get();
if (string == "auto") {
accessibility_mode = DisplayServer::AccessibilityMode::ACCESSIBILITY_AUTO;
accessibility_mode_set = true;
} else if (string == "always") {
accessibility_mode = DisplayServer::AccessibilityMode::ACCESSIBILITY_ALWAYS;
accessibility_mode_set = true;
} else if (string == "disabled") {
accessibility_mode = DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED;
accessibility_mode_set = true;
} else {
OS::get_singleton()->print("Accessibility mode argument not recognized, aborting.\n");
goto error;
}
N = N->next();
} else {
OS::get_singleton()->print("Missing accessibility mode argument, aborting.\n");
goto error;
}
} else if (arg == "-t" || arg == "--always-on-top") {
init_always_on_top = true;
} else if (arg == "--resolution") {
if (N) {
String vm = N->get();
if (!vm.contains_char('x')) {
OS::get_singleton()->print("Invalid resolution '%s', it should be e.g. '1280x720'.\n",
vm.utf8().get_data());
goto error;
}
int w = vm.get_slicec('x', 0).to_int();
int h = vm.get_slicec('x', 1).to_int();
if (w <= 0 || h <= 0) {
OS::get_singleton()->print("Invalid resolution '%s', width and height must be above 0.\n",
vm.utf8().get_data());
goto error;
}
window_size.width = w;
window_size.height = h;
force_res = true;
N = N->next();
} else {
OS::get_singleton()->print("Missing resolution argument, aborting.\n");
goto error;
}
} else if (arg == "--screen") {
if (N) {
init_screen = N->get().to_int();
init_use_custom_screen = true;
N = N->next();
} else {
OS::get_singleton()->print("Missing screen argument, aborting.\n");
goto error;
}
} else if (arg == "--position") {
if (N) {
String vm = N->get();
if (!vm.contains_char(',')) {
OS::get_singleton()->print("Invalid position '%s', it should be e.g. '80,128'.\n",
vm.utf8().get_data());
goto error;
}
int x = vm.get_slicec(',', 0).to_int();
int y = vm.get_slicec(',', 1).to_int();
init_custom_pos = Point2(x, y);
init_use_custom_pos = true;
N = N->next();
} else {
OS::get_singleton()->print("Missing position argument, aborting.\n");
goto error;
}
} else if (arg == "--headless") {
audio_driver = NULL_AUDIO_DRIVER;
display_driver = NULL_DISPLAY_DRIVER;
} else if (arg == "--embedded") {
#ifdef MACOS_ENABLED
display_driver = EMBEDDED_DISPLAY_DRIVER;
#else
OS::get_singleton()->print("--embedded is only supported on macOS, aborting.\n");
goto error;
#endif
} else if (arg == "--log-file") {
if (N) {
log_file = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing log file path argument, aborting.\n");
goto error;
}
} else if (arg == "--profiling") {
use_debug_profiler = true;
} else if (arg == "-l" || arg == "--language") {
if (N) {
locale = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing language argument, aborting.\n");
goto error;
}
} else if (arg == "--remote-fs") {
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
if (N) {
remotefs = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing remote filesystem address, aborting.\n");
goto error;
}
#else
ERR_PRINT(
"`--remote-fs` was specified on the command line, but this Godot binary was compiled without debug. Aborting.\n"
"To be able to use it, use the `target=template_debug` SCons option when compiling Godot.\n");
#endif
} else if (arg == "--remote-fs-password") {
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
if (N) {
remotefs_pass = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing remote filesystem password, aborting.\n");
goto error;
}
#else
ERR_PRINT(
"`--remote-fs-password` was specified on the command line, but this Godot binary was compiled without debug. Aborting.\n"
"To be able to use it, use the `target=template_debug` SCons option when compiling Godot.\n");
goto error;
#endif
} else if (arg == "--render-thread") {
if (N) {
if (N->get() == "safe") {
separate_thread_render = 0;
#ifndef DISABLE_DEPRECATED
} else if (N->get() == "unsafe") {
OS::get_singleton()->print("The --render-thread unsafe option is unsupported in Godot 4 and will be removed.\n");
separate_thread_render = 0;
#endif
} else if (N->get() == "separate") {
separate_thread_render = 1;
} else {
OS::get_singleton()->print("Unknown render thread mode, aborting.\n");
#ifdef DISABLE_DEPRECATED
OS::get_singleton()->print("Valid options are 'safe' and 'separate'.\n");
#else
OS::get_singleton()->print("Valid options are 'unsafe', 'safe' and 'separate'.\n");
#endif
goto error;
}
N = N->next();
} else {
OS::get_singleton()->print("Missing render thread mode argument, aborting.\n");
goto error;
}
#ifdef TOOLS_ENABLED
} else if (arg == "-e" || arg == "--editor") {
editor = true;
} else if (arg == "-p" || arg == "--project-manager") {
project_manager = true;
} else if (arg == "--recovery-mode") {
recovery_mode = true;
} else if (arg == "--debug-server") {
if (N) {
debug_server_uri = N->get();
if (!debug_server_uri.contains("://")) {
OS::get_singleton()->print("Invalid debug server uri. It should be of the form <protocol>://<bind_address>:<port>.\n");
goto error;
}
N = N->next();
} else {
OS::get_singleton()->print("Missing remote debug server uri, aborting.\n");
goto error;
}
} else if (arg == "--single-threaded-scene") {
single_threaded_scene = true;
} else if (arg == "--build-solutions") {
auto_build_solutions = true;
editor = true;
cmdline_tool = true;
} else if (arg == "--dump-gdextension-interface") {
editor = true;
cmdline_tool = true;
dump_gdextension_interface_header = true;
print_line("Dumping GDExtension interface header file");
main_args.push_back(arg);
} else if (arg == "--dump-gdextension-interface-json") {
editor = true;
cmdline_tool = true;
dump_gdextension_interface = true;
print_line("Dumping GDExtension interface json file");
main_args.push_back(arg);
} else if (arg == "--dump-extension-api") {
editor = true;
cmdline_tool = true;
dump_extension_api = true;
print_line("Dumping Extension API");
main_args.push_back(arg);
} else if (arg == "--dump-extension-api-with-docs") {
editor = true;
cmdline_tool = true;
dump_extension_api = true;
include_docs_in_extension_api_dump = true;
print_line("Dumping Extension API including documentation");
main_args.push_back(arg);
} else if (arg == "--validate-extension-api") {
editor = true;
cmdline_tool = true;
validate_extension_api = true;
main_args.push_back(arg);
if (N) {
validate_extension_api_file = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing file to load argument after --validate-extension-api, aborting.");
goto error;
}
} else if (arg == "--import") {
editor = true;
cmdline_tool = true;
wait_for_import = true;
quit_after = 1;
} else if (arg == "--export-release" || arg == "--export-debug" ||
arg == "--export-pack" || arg == "--export-patch") {
editor = true;
cmdline_tool = true;
wait_for_import = true;
main_args.push_back(arg);
} else if (arg == "--patches") {
if (N) {
main_args.push_back(arg);
main_args.push_back(N->get());
N = N->next();
} else {
OS::get_singleton()->print("Missing comma-separated list of patches after --patches, aborting.\n");
goto error;
}
#ifndef DISABLE_DEPRECATED
} else if (arg == "--export") {
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");
goto error;
} else if (arg == "--convert-3to4") {
cmdline_tool = true;
main_args.push_back(arg);
if (N && !N->get().begins_with("-")) {
if (itos(N->get().to_int()) == N->get()) {
converter_max_kb_file = N->get().to_int();
}
if (N->next() && !N->next()->get().begins_with("-")) {
if (itos(N->next()->get().to_int()) == N->next()->get()) {
converter_max_line_length = N->next()->get().to_int();
}
}
}
} else if (arg == "--validate-conversion-3to4") {
cmdline_tool = true;
main_args.push_back(arg);
if (N && !N->get().begins_with("-")) {
if (itos(N->get().to_int()) == N->get()) {
converter_max_kb_file = N->get().to_int();
}
if (N->next() && !N->next()->get().begins_with("-")) {
if (itos(N->next()->get().to_int()) == N->next()->get()) {
converter_max_line_length = N->next()->get().to_int();
}
}
}
#endif
} else if (arg == "--doctool") {
cmdline_tool = true;
audio_driver = NULL_AUDIO_DRIVER;
display_driver = NULL_DISPLAY_DRIVER;
main_args.push_back(arg);
#ifdef MODULE_GDSCRIPT_ENABLED
} else if (arg == "--gdscript-docs") {
if (N) {
project_path = N->get();
main_args.push_back(arg);
main_args.push_back(N->get());
N = N->next();
quit_after = 1;
} else {
OS::get_singleton()->print("Missing relative or absolute path to project for --gdscript-docs, aborting.\n");
goto error;
}
#endif
#endif
} else if (arg == "--path") {
#if defined(OVERRIDE_PATH_ENABLED)
if (N) {
String p = N->get();
if (OS::get_singleton()->set_cwd(p) != OK) {
OS::get_singleton()->print("Invalid project path specified: \"%s\", aborting.\n", p.utf8().get_data());
goto error;
}
N = N->next();
} else {
OS::get_singleton()->print("Missing relative or absolute path, aborting.\n");
goto error;
}
#else
ERR_PRINT(
"`--path` was specified on the command line, but this Godot binary was compiled without support for path overrides. Aborting.\n"
"To be able to use it, use the `disable_path_overrides=no` SCons option when compiling Godot.\n");
goto error;
#endif
} else if (arg == "--quit") {
quit_after = 1;
#ifdef TOOLS_ENABLED
wait_for_import = true;
#endif
} else if (arg == "--quit-after") {
if (N) {
quit_after = N->get().to_int();
N = N->next();
} else {
OS::get_singleton()->print("Missing number of iterations, aborting.\n");
goto error;
}
} else if (arg.ends_with("project.godot")) {
#if defined(OVERRIDE_PATH_ENABLED)
String path;
String file = arg;
int sep = MAX(file.rfind_char('/'), file.rfind_char('\\'));
if (sep == -1) {
path = ".";
} else {
path = file.substr(0, sep);
}
if (OS::get_singleton()->set_cwd(path) == OK) {
} else {
project_path = path;
}
#ifdef TOOLS_ENABLED
editor = true;
#endif
#else
ERR_PRINT(
"`project.godot` path was specified on the command line, but this Godot binary was compiled without support for path overrides. Aborting.\n"
"To be able to use it, use the `disable_path_overrides=no` SCons option when compiling Godot.\n");
goto error;
#endif
} else if (arg == "-b" || arg == "--breakpoints") {
if (N) {
String bplist = N->get();
breakpoints = bplist.split(",");
N = N->next();
} else {
OS::get_singleton()->print("Missing list of breakpoints, aborting.\n");
goto error;
}
} else if (arg == "--max-fps") {
if (N) {
max_fps = N->get().to_int();
N = N->next();
} else {
OS::get_singleton()->print("Missing maximum FPS argument, aborting.\n");
goto error;
}
} else if (arg == "--frame-delay") {
if (N) {
frame_delay = N->get().to_int();
N = N->next();
} else {
OS::get_singleton()->print("Missing frame delay argument, aborting.\n");
goto error;
}
} else if (arg == "--time-scale") {
if (N) {
Engine::get_singleton()->set_time_scale(N->get().to_float());
N = N->next();
} else {
OS::get_singleton()->print("Missing time scale argument, aborting.\n");
goto error;
}
} else if (arg == "--main-pack") {
#if defined(OVERRIDE_PATH_ENABLED) || defined(WEB_ENABLED)
if (N) {
main_pack = N->get();
N = N->next();
} else {
OS::get_singleton()->print("Missing path to main pack file, aborting.\n");
goto error;
}
#else
ERR_PRINT(
"`--main-pack` was specified on the command line, but this Godot binary was compiled without support for path overrides. Aborting.\n"
"To be able to use it, use the `disable_path_overrides=no` SCons option when compiling Godot.\n");
goto error;
#endif
} else if (arg == "-d" || arg == "--debug") {
debug_uri = "local://";
OS::get_singleton()->_debug_stdout = true;
#if defined(DEBUG_ENABLED)
} else if (arg == "--debug-collisions") {
debug_collisions = true;
} else if (arg == "--debug-paths") {
debug_paths = true;
} else if (arg == "--debug-navigation") {
debug_navigation = true;
} else if (arg == "--debug-avoidance") {
debug_avoidance = true;
} else if (arg == "--debug-canvas-item-redraw") {
debug_canvas_item_redraw = true;
} else if (arg == "--debug-stringnames") {
StringName::set_debug_stringnames(true);
} else if (arg == "--debug-mute-audio") {
debug_mute_audio = true;
#endif
#if defined(TOOLS_ENABLED) && (defined(WINDOWS_ENABLED) || defined(LINUXBSD_ENABLED))
} else if (arg == "--test-rd-support") {
test_rd_support = true;
} else if (arg == "--test-rd-creation") {
test_rd_creation = true;
#endif
} else if (arg == "--remote-debug") {
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
if (N) {
debug_uri = N->get();
if (!debug_uri.contains("://")) {
OS::get_singleton()->print(
"Invalid debug host address, it should be of the form <protocol>://<host/IP>:<port>.\n");
goto error;
}
N = N->next();
} else {
OS::get_singleton()->print("Missing remote debug host address, aborting.\n");
goto error;
}
#else
ERR_PRINT(
"`--remote-debug` was specified on the command line, but this Godot binary was compiled without debug. Aborting.\n"
"To be able to use it, use the `target=template_debug` SCons option when compiling Godot.\n");
goto error;
#endif
} else if (arg == "--editor-pid") {
if (N) {
editor_pid = N->get().to_int();
N = N->next();
} else {
OS::get_singleton()->print("Missing editor PID argument, aborting.\n");
goto error;
}
} else if (arg == "--disable-render-loop") {
disable_render_loop = true;
} else if (arg == "--fixed-fps") {
if (N) {
fixed_fps = N->get().to_int();
N = N->next();
} else {
OS::get_singleton()->print("Missing fixed-fps argument, aborting.\n");
goto error;
}
} else if (arg == "--write-movie") {
if (N) {
Engine::get_singleton()->set_write_movie_path(N->get());
N = N->next();
if (fixed_fps == -1) {
fixed_fps = 60;
}
OS::get_singleton()->_writing_movie = true;
} else {
OS::get_singleton()->print("Missing write-movie argument, aborting.\n");
goto error;
}
} else if (arg == "--disable-vsync") {
disable_vsync = true;
} else if (arg == "--print-fps") {
print_fps = true;
#ifdef TOOLS_ENABLED
} else if (arg == "--editor-pseudolocalization") {
editor_pseudolocalization = true;
main_args.push_back(arg);
#endif
} else if (arg == "--gpu-profile") {
profile_gpu = true;
} else if (arg == "--disable-crash-handler") {
OS::get_singleton()->disable_crash_handler();
} else if (arg == "--skip-breakpoints") {
skip_breakpoints = true;
} else if (I->get() == "--ignore-error-breaks") {
ignore_error_breaks = true;
#ifndef XR_DISABLED
} else if (arg == "--xr-mode") {
if (N) {
String xr_mode = N->get().to_lower();
N = N->next();
if (xr_mode == "default") {
XRServer::set_xr_mode(XRServer::XRMODE_DEFAULT);
} else if (xr_mode == "off") {
XRServer::set_xr_mode(XRServer::XRMODE_OFF);
} else if (xr_mode == "on") {
XRServer::set_xr_mode(XRServer::XRMODE_ON);
} else {
OS::get_singleton()->print("Unknown --xr-mode argument \"%s\", aborting.\n", xr_mode.ascii().get_data());
goto error;
}
} else {
OS::get_singleton()->print("Missing --xr-mode argument, aborting.\n");
goto error;
}
#endif
} else if (arg == "--benchmark") {
OS::get_singleton()->set_use_benchmark(true);
} else if (arg == "--benchmark-file") {
if (N) {
OS::get_singleton()->set_use_benchmark(true);
String benchmark_file = N->get();
OS::get_singleton()->set_benchmark_file(benchmark_file);
N = N->next();
} else {
OS::get_singleton()->print("Missing <path> argument for --benchmark-file <path>.\n");
goto error;
}
#if defined(TOOLS_ENABLED) && defined(MODULE_GDSCRIPT_ENABLED) && !defined(GDSCRIPT_NO_LSP)
} else if (arg == "--lsp-port") {
if (N) {
int port_override = N->get().to_int();
if (port_override < 0 || port_override > 65535) {
OS::get_singleton()->print("<port> argument for --lsp-port <port> must be between 0 and 65535.\n");
goto error;
}
GDScriptLanguageServer::port_override = port_override;
N = N->next();
} else {
OS::get_singleton()->print("Missing <port> argument for --lsp-port <port>.\n");
goto error;
}
#endif
#if defined(TOOLS_ENABLED)
} else if (arg == "--dap-port") {
if (N) {
int port_override = N->get().to_int();
if (port_override < 0 || port_override > 65535) {
OS::get_singleton()->print("<port> argument for --dap-port <port> must be between 0 and 65535.\n");
goto error;
}
DebugAdapterServer::port_override = port_override;
N = N->next();
} else {
OS::get_singleton()->print("Missing <port> argument for --dap-port <port>.\n");
goto error;
}
#endif
} else if (arg == "--wid") {
if (N) {
init_embed_parent_window_id = N->get().to_int();
if (init_embed_parent_window_id == 0) {
OS::get_singleton()->print("<window_id> argument for --wid <window_id> must be different then 0.\n");
goto error;
}
OS::get_singleton()->_embedded_in_editor = true;
Engine::get_singleton()->set_embedded_in_editor(true);
N = N->next();
} else {
OS::get_singleton()->print("Missing <window_id> argument for --wid <window_id>.\n");
goto error;
}
} else if (arg == "--" || arg == "++") {
adding_user_args = true;
} else {
main_args.push_back(arg);
}
I = N;
}
#ifdef TOOLS_ENABLED
if (editor && project_manager) {
OS::get_singleton()->print(
"Error: Command line arguments implied opening both editor and project manager, which is not possible. Aborting.\n");
goto error;
}
#endif
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
if (!remotefs.is_empty()) {
int port;
if (remotefs.contains_char(':')) {
port = remotefs.get_slicec(':', 1).to_int();
remotefs = remotefs.get_slicec(':', 0);
} else {
port = 6010;
}
Error err = OS::get_singleton()->setup_remote_filesystem(remotefs, port, remotefs_pass, project_path);
if (err) {
OS::get_singleton()->printerr("Could not connect to remotefs: %s:%i.\n", remotefs.utf8().get_data(), port);
goto error;
}
}
#endif
OS::get_singleton()->_in_editor = editor;
if (globals->setup(project_path, main_pack, false, editor) == OK) {
#ifdef TOOLS_ENABLED
found_project = true;
#endif
} else {
#ifdef TOOLS_ENABLED
editor = false;
#else
String error_msg = "Error: Couldn't load project data at path \"" + (project_path == "." ? OS::get_singleton()->get_cwd() : project_path) + "\". Is the .pck file missing?\n\n";
#if !defined(OVERRIDE_PATH_ENABLED) && !defined(TOOLS_ENABLED)
String exec_path = OS::get_singleton()->get_executable_path();
String exec_basename = exec_path.get_file().get_basename();
if (FileAccess::exists(old_cwd.path_join(exec_basename + ".pck"))) {
error_msg += "\"" + exec_basename + ".pck\" was found in the current working directory. To be able to load a project from the CWD, use the `disable_path_overrides=no` SCons option when compiling Godot.\n";
} else if (FileAccess::exists(old_cwd.path_join("project.godot"))) {
error_msg += "\"project.godot\" was found in the current working directory. To be able to load a project from the CWD, use the `disable_path_overrides=no` SCons option when compiling Godot.\n";
} else {
error_msg += "If you've renamed the executable, the associated .pck file should also be renamed to match the executable's name (without the extension).\n";
}
#else
error_msg += "If you've renamed the executable, the associated .pck file should also be renamed to match the executable's name (without the extension).\n";
#endif
ERR_PRINT(error_msg);
OS::get_singleton()->alert(error_msg);
goto error;
#endif
}
{
#ifdef THREADS_ENABLED
if (editor || project_manager) {
WorkerThreadPool::get_singleton()->init(-1, 0.75);
} else {
int worker_threads = GLOBAL_GET("threading/worker_pool/max_threads");
float low_priority_ratio = GLOBAL_GET("threading/worker_pool/low_priority_thread_ratio");
WorkerThreadPool::get_singleton()->init(worker_threads, low_priority_ratio);
}
#else
WorkerThreadPool::get_singleton()->init(0, 0);
#endif
}
#ifdef TOOLS_ENABLED
if (!project_manager && !editor) {
project_manager = !found_project && !cmdline_tool;
}
{
LocalVector<String> layers_to_disable = {
"DISABLE_RTSS_LAYER",
"DISABLE_VULKAN_OBS_CAPTURE",
"DISABLE_VULKAN_OW_OBS_CAPTURE",
"DISABLE_SAMPLE_LAYER",
"DISABLE_GAMEPP_LAYER",
"DISABLE_VK_LAYER_TENCENT_wegame_cross_overlay_1",
"VK_LAYER_bandicam_helper_DEBUG_1",
"DISABLE_VK_LAYER_bandicam_helper_1",
"DISABLE_VK_LAYER_reshade_1",
"DISABLE_VK_LAYER_GPUOpen_GRS",
"DISABLE_LAYER",
"DISABLE_MANGOHUD",
"DISABLE_VKBASALT",
"DISABLE_FOSSILIZE",
};
#if defined(WINDOWS_ENABLED) || defined(LINUXBSD_ENABLED)
if (editor || project_manager || test_rd_support || test_rd_creation) {
#else
if (editor || project_manager) {
#endif
for (const String &layer_disable : layers_to_disable) {
OS::get_singleton()->set_environment(layer_disable, "1");
}
} else {
for (const String &layer_disable : layers_to_disable) {
OS::get_singleton()->unset_environment(layer_disable);
}
}
}
#endif
#if defined(TOOLS_ENABLED) && (defined(WINDOWS_ENABLED) || defined(LINUXBSD_ENABLED))
if (test_rd_support) {
OS::get_singleton()->set_crash_handler_silent();
if (OS::get_singleton()->_test_create_rendering_device(display_driver)) {
exit_err = ERR_HELP;
} else {
exit_err = ERR_UNAVAILABLE;
}
goto error;
} else if (test_rd_creation) {
OS::get_singleton()->set_crash_handler_silent();
if (OS::get_singleton()->_test_create_rendering_device_and_gl(display_driver)) {
exit_err = ERR_HELP;
} else {
exit_err = ERR_UNAVAILABLE;
}
goto error;
}
#endif
#ifdef TOOLS_ENABLED
if (editor) {
Engine::get_singleton()->set_editor_hint(true);
Engine::get_singleton()->set_extension_reloading_enabled(true);
OS::get_singleton()->create_lock_file();
main_args.push_back("--editor");
if (!init_windowed && !init_fullscreen) {
init_maximized = true;
window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
}
}
if (project_manager) {
Engine::get_singleton()->set_project_manager_hint(true);
}
if (recovery_mode) {
if (project_manager || !editor) {
OS::get_singleton()->print("Error: Recovery mode can only be used in the editor. Aborting.\n");
goto error;
}
Engine::get_singleton()->set_recovery_mode_hint(true);
}
#endif
OS::get_singleton()->set_cmdline(execpath, main_args, user_args);
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));
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));
Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/common/physics_jitter_fix", PROPERTY_HINT_RANGE, "0,2,0.001,or_greater"), 0.5));
Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0));
if (max_fps >= 0) {
Engine::get_singleton()->set_max_fps(max_fps);
}
OS::get_singleton()->ensure_user_data_dir();
OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false));
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(
GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater"), 6900));
GLOBAL_DEF("application/run/delta_smoothing", true);
if (!delta_smoothing_override) {
OS::get_singleton()->set_delta_smoothing(GLOBAL_GET("application/run/delta_smoothing"));
}
GLOBAL_DEF("debug/settings/stdout/print_fps", false);
GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false);
GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false);
GLOBAL_DEF("debug/settings/physics_interpolation/enable_warnings", true);
if (!OS::get_singleton()->_verbose_stdout) {
OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout");
}
register_early_core_singletons();
initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
register_core_extensions();
if (!editor) {
ResourceUID::get_singleton()->enable_reverse_cache();
}
ResourceUID::get_singleton()->load_from_cache(true);
ProjectSettings::get_singleton()->fix_autoload_paths();
if (ProjectSettings::get_singleton()->has_custom_feature("dedicated_server")) {
audio_driver = NULL_AUDIO_DRIVER;
display_driver = NULL_DISPLAY_DRIVER;
}
GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/debugger/max_chars_per_second", PROPERTY_HINT_RANGE, "256,4096,1,or_greater"), 32768);
GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/debugger/max_queued_messages", PROPERTY_HINT_RANGE, "128,8192,1,or_greater"), 2048);
GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/debugger/max_errors_per_second", PROPERTY_HINT_RANGE, "1,200,1,or_greater"), 400);
GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/debugger/max_warnings_per_second", PROPERTY_HINT_RANGE, "1,200,1,or_greater"), 400);
EngineDebugger::initialize(debug_uri, skip_breakpoints, ignore_error_breaks, breakpoints, []() {
if (editor_pid) {
DisplayServer::get_singleton()->enable_for_stealing_focus(editor_pid);
}
});
#ifdef TOOLS_ENABLED
if (editor) {
packed_data->set_disabled(true);
}
#endif
GLOBAL_DEF("debug/file_logging/enable_file_logging", false);
GLOBAL_DEF("debug/file_logging/enable_file_logging.pc", true);
GLOBAL_DEF("debug/file_logging/log_path", "user://logs/godot.log");
GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater"), 5);
if (FileAccess::get_create_func(!log_file.is_empty() ? FileAccess::ACCESS_FILESYSTEM : FileAccess::ACCESS_USERDATA) &&
(!log_file.is_empty() || (!project_manager && !editor && GLOBAL_GET("debug/file_logging/enable_file_logging")))) {
String base_path;
int max_files;
if (!log_file.is_empty()) {
base_path = log_file;
max_files = 1;
} else {
base_path = GLOBAL_GET("debug/file_logging/log_path");
max_files = GLOBAL_GET("debug/file_logging/max_log_files");
}
OS::get_singleton()->add_logger(memnew(RotatedFileLogger(base_path, max_files)));
}
if (main_args.is_empty() && String(GLOBAL_GET("application/run/main_scene")) == "") {
#ifdef TOOLS_ENABLED
if (!editor && !project_manager) {
#endif
const String error_msg = "Error: Can't run project: no main scene defined in the project.\n";
OS::get_singleton()->print("%s", error_msg.utf8().get_data());
OS::get_singleton()->alert(error_msg);
goto error;
#ifdef TOOLS_ENABLED
}
#endif
}
if (editor || project_manager) {
Engine::get_singleton()->set_editor_hint(true);
use_custom_res = false;
input_map->load_default();
} else {
input_map->load_from_project_settings();
}
if (bool(GLOBAL_GET("application/run/disable_stdout"))) {
quiet_stdout = true;
}
if (bool(GLOBAL_GET("application/run/disable_stderr"))) {
CoreGlobals::print_error_enabled = false;
}
if (!bool(GLOBAL_GET("application/run/print_header"))) {
Engine::get_singleton()->_print_header = false;
}
if (quiet_stdout) {
CoreGlobals::print_line_enabled = false;
}
Logger::set_flush_stdout_on_print(GLOBAL_GET("application/run/flush_stdout_on_print"));
{
GLOBAL_DEF_RST("rendering/rendering_device/driver", "vulkan");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.windows", PROPERTY_HINT_ENUM, "vulkan,d3d12"), "vulkan");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.linuxbsd", PROPERTY_HINT_ENUM, "vulkan"), "vulkan");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.android", PROPERTY_HINT_ENUM, "vulkan"), "vulkan");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.ios", PROPERTY_HINT_ENUM, "metal,vulkan"), "metal");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.visionos", PROPERTY_HINT_ENUM, "metal"), "metal");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.macos", PROPERTY_HINT_ENUM, "metal,vulkan"), "metal");
GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_vulkan", true);
GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_d3d12", true);
GLOBAL_DEF_RST("rendering/rendering_device/fallback_to_opengl3", true);
}
{
GLOBAL_DEF_RST("rendering/gl_compatibility/driver", "opengl3");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.windows", PROPERTY_HINT_ENUM, "opengl3,opengl3_angle"), "opengl3");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.linuxbsd", PROPERTY_HINT_ENUM, "opengl3,opengl3_es"), "opengl3");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.web", PROPERTY_HINT_ENUM, "opengl3"), "opengl3");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.android", PROPERTY_HINT_ENUM, "opengl3"), "opengl3");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.ios", PROPERTY_HINT_ENUM, "opengl3"), "opengl3");
GLOBAL_DEF_RST(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.macos", PROPERTY_HINT_ENUM, "opengl3,opengl3_angle"), "opengl3");
GLOBAL_DEF_RST("rendering/gl_compatibility/nvidia_disable_threaded_optimization", true);
GLOBAL_DEF_RST("rendering/gl_compatibility/fallback_to_angle", true);
GLOBAL_DEF_RST("rendering/gl_compatibility/fallback_to_native", true);
GLOBAL_DEF_RST("rendering/gl_compatibility/fallback_to_gles", true);
Array force_angle_list;
#define FORCE_ANGLE(m_vendor, m_name) \
{ \
Dictionary device; \
device["vendor"] = m_vendor; \
device["name"] = m_name; \
force_angle_list.push_back(device); \
}
FORCE_ANGLE("ATI", "Radeon 9");
FORCE_ANGLE("ATI", "Radeon X");
FORCE_ANGLE("ATI", "Radeon HD 2");
FORCE_ANGLE("ATI", "Radeon HD 3");
FORCE_ANGLE("ATI", "Radeon HD 4");
FORCE_ANGLE("ATI", "Radeon HD 5");
FORCE_ANGLE("ATI", "Radeon HD 6");
FORCE_ANGLE("ATI", "Radeon HD 7");
FORCE_ANGLE("ATI", "Radeon HD 8");
FORCE_ANGLE("ATI", "Radeon(TM) R2 Graphics");
FORCE_ANGLE("ATI", "Radeon(TM) R3 Graphics");
FORCE_ANGLE("ATI", "Radeon(TM) R4 Graphics");
FORCE_ANGLE("ATI", "Radeon(TM) R5 Graphics");
FORCE_ANGLE("ATI", "Radeon(TM) R6 Graphics");
FORCE_ANGLE("ATI", "Radeon(TM) R7 Graphics");
FORCE_ANGLE("AMD", "Radeon(TM) R7 Graphics");
FORCE_ANGLE("AMD", "Radeon(TM) R8 Graphics");
FORCE_ANGLE("ATI", "Radeon R5 Graphics");
FORCE_ANGLE("ATI", "Radeon R6 Graphics");
FORCE_ANGLE("ATI", "Radeon R7 Graphics");
FORCE_ANGLE("AMD", "Radeon R7 Graphics");
FORCE_ANGLE("AMD", "Radeon R8 Graphics");
FORCE_ANGLE("ATI", "Radeon R5 2");
FORCE_ANGLE("ATI", "Radeon R7 2");
FORCE_ANGLE("ATI", "Radeon R9 2");
FORCE_ANGLE("ATI", "Radeon R5 M2");
FORCE_ANGLE("ATI", "Radeon R7 M2");
FORCE_ANGLE("ATI", "Radeon R9 M2");
FORCE_ANGLE("ATI", "Radeon (TM) R9 Fury");
FORCE_ANGLE("ATI", "Radeon (TM) R5 3");
FORCE_ANGLE("AMD", "Radeon (TM) R5 3");
FORCE_ANGLE("ATI", "Radeon (TM) R7 3");
FORCE_ANGLE("AMD", "Radeon (TM) R7 3");
FORCE_ANGLE("ATI", "Radeon (TM) R9 3");
FORCE_ANGLE("AMD", "Radeon (TM) R9 3");
FORCE_ANGLE("ATI", "Radeon (TM) R5 M3");
FORCE_ANGLE("AMD", "Radeon (TM) R5 M3");
FORCE_ANGLE("ATI", "Radeon (TM) R7 M3");
FORCE_ANGLE("AMD", "Radeon (TM) R7 M3");
FORCE_ANGLE("ATI", "Radeon (TM) R9 M3");
FORCE_ANGLE("AMD", "Radeon (TM) R9 M3");
FORCE_ANGLE("Intel", "Intel(R) HD Graphics");
FORCE_ANGLE("Intel", "Intel HD Graphics");
FORCE_ANGLE("Intel", "Intel(R) Vallyview Graphics");
FORCE_ANGLE("Intel", "Intel(R) Iris(TM) Graphics 5100");
FORCE_ANGLE("Intel", "Intel(R) Iris(TM) Pro Graphics 5200");
FORCE_ANGLE("Intel", "Intel(R) Iris(TM) Graphics 6100");
FORCE_ANGLE("Intel", "Intel(R) Iris(TM) Pro Graphics 6200");
FORCE_ANGLE("Intel", "Intel(R) Iris(TM) Pro Graphics P6300");
FORCE_ANGLE("Intel", "Intel(R) Iris Graphics 540");
FORCE_ANGLE("Intel", "Intel(R) Iris Plus Graphics 640");
FORCE_ANGLE("Intel", "Intel(R) Iris Plus Graphics 650");
FORCE_ANGLE("Intel", "Intel(R) Iris Pro Graphics 580");
FORCE_ANGLE("Intel", "Intel(R) Iris Pro Graphics P580");
#undef FORCE_ANGLE
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::ARRAY, "rendering/gl_compatibility/force_angle_on_devices", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::DICTIONARY, PROPERTY_HINT_NONE, String())), force_angle_list);
}
#ifdef RD_ENABLED
renderer_hints = "forward_plus,mobile";
default_renderer_mobile = "mobile";
#endif
#ifdef GLES3_ENABLED
if (!renderer_hints.is_empty()) {
renderer_hints += ",";
}
renderer_hints += "gl_compatibility";
if (default_renderer_mobile.is_empty()) {
default_renderer_mobile = "gl_compatibility";
}
if (rendering_driver.is_empty() && rendering_method.is_empty() && project_manager) {
rendering_driver = "opengl3";
rendering_method = "gl_compatibility";
default_renderer_mobile = "gl_compatibility";
}
#endif
if (!rendering_method.is_empty()) {
if (rendering_method != "forward_plus" &&
rendering_method != "mobile" &&
rendering_method != "gl_compatibility" &&
rendering_method != "dummy") {
OS::get_singleton()->print("Unknown rendering method '%s', aborting.\nValid options are ",
rendering_method.utf8().get_data());
Vector<String> rendering_method_hints = renderer_hints.split(",");
rendering_method_hints.push_back("dummy");
for (int i = 0; i < rendering_method_hints.size(); i++) {
if (i == rendering_method_hints.size() - 1) {
OS::get_singleton()->print(" and ");
} else if (i != 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s'", rendering_method_hints[i].utf8().get_data());
}
OS::get_singleton()->print(".\n");
goto error;
}
}
if (renderer_hints.is_empty()) {
renderer_hints = "dummy";
}
if (!rendering_driver.is_empty()) {
bool found = false;
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
Vector<String> r_drivers = DisplayServer::get_create_function_rendering_drivers(i);
for (int d = 0; d < r_drivers.size(); d++) {
if (rendering_driver == r_drivers[d]) {
found = true;
break;
}
}
}
if (!found) {
OS::get_singleton()->print("Unknown rendering driver '%s', aborting.\nValid options are ",
rendering_driver.utf8().get_data());
Vector<String> unique_rendering_drivers;
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
Vector<String> r_drivers = DisplayServer::get_create_function_rendering_drivers(i);
for (int d = 0; d < r_drivers.size(); d++) {
if (!unique_rendering_drivers.has(r_drivers[d])) {
unique_rendering_drivers.append(r_drivers[d]);
}
}
}
for (int i = 0; i < unique_rendering_drivers.size(); i++) {
if (i == unique_rendering_drivers.size() - 1) {
OS::get_singleton()->print(" and ");
} else if (i != 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s'", unique_rendering_drivers[i].utf8().get_data());
}
OS::get_singleton()->print(".\n");
goto error;
}
if (rendering_method.is_empty()) {
if (rendering_driver == "dummy") {
rendering_method = "dummy";
} else if (rendering_driver == "opengl3" || rendering_driver == "opengl3_angle" || rendering_driver == "opengl3_es") {
rendering_method = "gl_compatibility";
} else {
rendering_method = "forward_plus";
}
}
bool valid_combination = false;
Vector<String> available_drivers;
if (rendering_method == "forward_plus" || rendering_method == "mobile") {
#ifdef VULKAN_ENABLED
available_drivers.push_back("vulkan");
#endif
#ifdef D3D12_ENABLED
available_drivers.push_back("d3d12");
#endif
#ifdef METAL_ENABLED
available_drivers.push_back("metal");
#endif
}
#ifdef GLES3_ENABLED
if (rendering_method == "gl_compatibility") {
available_drivers.push_back("opengl3");
available_drivers.push_back("opengl3_angle");
available_drivers.push_back("opengl3_es");
}
#endif
if (rendering_method == "dummy") {
available_drivers.push_back("dummy");
}
if (available_drivers.is_empty()) {
OS::get_singleton()->print("Unknown renderer name '%s', aborting.\n", rendering_method.utf8().get_data());
goto error;
}
for (int i = 0; i < available_drivers.size(); i++) {
if (rendering_driver == available_drivers[i]) {
valid_combination = true;
break;
}
}
if (!valid_combination) {
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());
for (int d = 0; d < available_drivers.size(); d++) {
OS::get_singleton()->print("'%s', ", available_drivers[d].utf8().get_data());
}
OS::get_singleton()->print(".\n");
goto error;
}
}
default_renderer = renderer_hints.get_slicec(',', 0);
GLOBAL_DEF_RST_BASIC(PropertyInfo(Variant::STRING, "rendering/renderer/rendering_method", PROPERTY_HINT_ENUM, renderer_hints), default_renderer);
GLOBAL_DEF_RST_BASIC("rendering/renderer/rendering_method.mobile", default_renderer_mobile);
GLOBAL_DEF_RST_BASIC(PropertyInfo(Variant::STRING, "rendering/renderer/rendering_method.web", PROPERTY_HINT_ENUM, "gl_compatibility"), "gl_compatibility");
if (rendering_method.is_empty()) {
rendering_method = GLOBAL_GET("rendering/renderer/rendering_method");
rendering_method_source = OS::RenderingSource::RENDERING_SOURCE_PROJECT_SETTING;
}
if (rendering_driver.is_empty()) {
if (rendering_method == "dummy") {
rendering_driver = "dummy";
} else if (rendering_method == "gl_compatibility") {
rendering_driver = GLOBAL_GET("rendering/gl_compatibility/driver");
rendering_driver_source = OS::RenderingSource::RENDERING_SOURCE_PROJECT_SETTING;
} else {
rendering_driver = GLOBAL_GET("rendering/rendering_device/driver");
rendering_driver_source = OS::RenderingSource::RENDERING_SOURCE_PROJECT_SETTING;
}
}
rendering_driver = rendering_driver.to_lower();
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, rendering_driver_source);
OS::get_singleton()->set_current_rendering_method(rendering_method, rendering_method_source);
#ifdef TOOLS_ENABLED
if (!force_res && project_manager) {
window_size.width = ProjectManager::DEFAULT_WINDOW_WIDTH;
window_size.height = ProjectManager::DEFAULT_WINDOW_HEIGHT;
}
#endif
if (use_custom_res) {
if (!force_res) {
window_size.width = GLOBAL_GET("display/window/size/viewport_width");
window_size.height = GLOBAL_GET("display/window/size/viewport_height");
if (globals->has_setting("display/window/size/window_width_override") &&
globals->has_setting("display/window/size/window_height_override")) {
int desired_width = GLOBAL_GET("display/window/size/window_width_override");
if (desired_width > 0) {
window_size.width = desired_width;
}
int desired_height = GLOBAL_GET("display/window/size/window_height_override");
if (desired_height > 0) {
window_size.height = desired_height;
}
}
}
if (!bool(GLOBAL_GET("display/window/size/resizable"))) {
window_flags |= DisplayServer::WINDOW_FLAG_RESIZE_DISABLED_BIT;
}
if (bool(GLOBAL_GET("display/window/size/minimize_disabled"))) {
window_flags |= DisplayServer::WINDOW_FLAG_MINIMIZE_DISABLED_BIT;
}
if (bool(GLOBAL_GET("display/window/size/maximize_disabled"))) {
window_flags |= DisplayServer::WINDOW_FLAG_MAXIMIZE_DISABLED_BIT;
}
if (bool(GLOBAL_GET("display/window/size/borderless"))) {
window_flags |= DisplayServer::WINDOW_FLAG_BORDERLESS_BIT;
}
if (bool(GLOBAL_GET("display/window/size/always_on_top"))) {
window_flags |= DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP_BIT;
}
if (bool(GLOBAL_GET("display/window/size/transparent"))) {
window_flags |= DisplayServer::WINDOW_FLAG_TRANSPARENT_BIT;
}
if (bool(GLOBAL_GET("display/window/size/extend_to_title"))) {
window_flags |= DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE_BIT;
}
if (bool(GLOBAL_GET("display/window/size/no_focus"))) {
window_flags |= DisplayServer::WINDOW_FLAG_NO_FOCUS_BIT;
}
if (bool(GLOBAL_GET("display/window/size/sharp_corners"))) {
window_flags |= DisplayServer::WINDOW_FLAG_SHARP_CORNERS_BIT;
}
window_mode = (DisplayServer::WindowMode)(GLOBAL_GET("display/window/size/mode").operator int());
int initial_position_type = GLOBAL_GET("display/window/size/initial_position_type").operator int();
if (initial_position_type == Window::WINDOW_INITIAL_POSITION_ABSOLUTE) {
if (!init_use_custom_pos) {
init_custom_pos = GLOBAL_GET("display/window/size/initial_position").operator Vector2i();
init_use_custom_pos = true;
}
} else if (initial_position_type == Window::WINDOW_INITIAL_POSITION_CENTER_PRIMARY_SCREEN || initial_position_type == Window::WINDOW_INITIAL_POSITION_CENTER_MAIN_WINDOW_SCREEN) {
if (!init_use_custom_screen) {
init_screen = DisplayServer::SCREEN_PRIMARY;
init_use_custom_screen = true;
}
} else if (initial_position_type == Window::WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN) {
if (!init_use_custom_screen) {
init_screen = GLOBAL_GET("display/window/size/initial_screen").operator int();
init_use_custom_screen = true;
}
} else if (initial_position_type == Window::WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_MOUSE_FOCUS) {
if (!init_use_custom_screen) {
init_screen = DisplayServer::SCREEN_WITH_MOUSE_FOCUS;
init_use_custom_screen = true;
}
} else if (initial_position_type == Window::WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS) {
if (!init_use_custom_screen) {
init_screen = DisplayServer::SCREEN_WITH_KEYBOARD_FOCUS;
init_use_custom_screen = true;
}
}
}
OS::get_singleton()->_allow_hidpi = GLOBAL_DEF("display/window/dpi/allow_hidpi", true);
OS::get_singleton()->_allow_layered = GLOBAL_DEF_RST("display/window/per_pixel_transparency/allowed", false);
load_shell_env = GLOBAL_DEF("application/run/load_shell_environment", false);
#ifdef TOOLS_ENABLED
if (editor || project_manager) {
OS::get_singleton()->_allow_hidpi = true;
load_shell_env = true;
}
#endif
if (load_shell_env) {
OS::get_singleton()->load_shell_environment();
}
if (separate_thread_render == -1) {
separate_thread_render = (int)GLOBAL_DEF("rendering/driver/threads/thread_model", OS::RENDER_THREAD_SAFE) == OS::RENDER_SEPARATE_THREAD;
}
if (editor || project_manager) {
separate_thread_render = 0;
}
#if !defined(THREADS_ENABLED)
separate_thread_render = 0;
#endif
OS::get_singleton()->_separate_thread_render = separate_thread_render;
DEV_ASSERT(NULL_DISPLAY_DRIVER == DisplayServer::get_create_function_name(DisplayServer::get_create_function_count() - 1));
GLOBAL_DEF_NOVAL("display/display_server/driver", "default");
GLOBAL_DEF_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.windows", PROPERTY_HINT_ENUM, "default,windows,headless"), "default");
GLOBAL_DEF_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.linuxbsd", PROPERTY_HINT_ENUM, "default,x11,wayland,headless"), "default");
GLOBAL_DEF_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.android", PROPERTY_HINT_ENUM, "default,android,headless"), "default");
GLOBAL_DEF_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.ios", PROPERTY_HINT_ENUM, "default,iOS,headless"), "default");
GLOBAL_DEF_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.visionos", PROPERTY_HINT_ENUM, "default,visionOS,headless"), "default");
GLOBAL_DEF_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.macos", PROPERTY_HINT_ENUM, "default,macos,headless"), "default");
GLOBAL_DEF_RST_NOVAL("audio/driver/driver", AudioDriverManager::get_driver(0)->get_name());
if (audio_driver.is_empty()) {
if (project_manager) {
audio_driver = "Dummy";
} else {
audio_driver = GLOBAL_GET("audio/driver/driver");
}
}
DEV_ASSERT(NULL_AUDIO_DRIVER == AudioDriverManager::get_driver(AudioDriverManager::get_driver_count() - 1)->get_name());
for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
if (audio_driver == AudioDriverManager::get_driver(i)->get_name()) {
audio_driver_idx = i;
break;
}
}
if (audio_driver_idx < 0) {
audio_driver_idx = 0;
}
if (Engine::get_singleton()->get_write_movie_path() != String()) {
audio_driver_idx = AudioDriverManager::get_driver_count() - 1;
AudioDriverDummy::get_dummy_singleton()->set_use_threads(false);
}
{
window_orientation = DisplayServer::ScreenOrientation(int(GLOBAL_DEF_BASIC("display/window/handheld/orientation", DisplayServer::ScreenOrientation::SCREEN_LANDSCAPE)));
}
{
window_vsync_mode = DisplayServer::VSyncMode(int(GLOBAL_DEF_BASIC("display/window/vsync/vsync_mode", DisplayServer::VSyncMode::VSYNC_ENABLED)));
if (disable_vsync) {
window_vsync_mode = DisplayServer::VSyncMode::VSYNC_DISABLED;
}
}
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15);
GLOBAL_DEF_RST("audio/driver/output_latency.web", 50);
Engine::get_singleton()->set_audio_output_latency(GLOBAL_GET("audio/driver/output_latency"));
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
OS::get_singleton()->set_environment("MVK_CONFIG_LOG_LEVEL", OS::get_singleton()->_verbose_stdout ? "3" : "1");
#endif
if (frame_delay == 0) {
frame_delay = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), 0);
if (Engine::get_singleton()->is_editor_hint()) {
frame_delay = 0;
}
}
if (audio_output_latency >= 1) {
Engine::get_singleton()->set_audio_output_latency(audio_output_latency);
}
GLOBAL_DEF("display/window/ios/allow_high_refresh_rate", true);
GLOBAL_DEF("display/window/ios/hide_home_indicator", true);
GLOBAL_DEF("display/window/ios/hide_status_bar", true);
GLOBAL_DEF("display/window/ios/suppress_ui_gesture", true);
#ifndef _3D_DISABLED
GLOBAL_DEF_RST_BASIC("xr/openxr/enabled", false);
GLOBAL_DEF(PropertyInfo(Variant::STRING, "xr/openxr/target_api_version"), "");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"), "res://openxr_action_map.tres");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/form_factor", PROPERTY_HINT_ENUM, "Head Mounted,Handheld"), "0");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/reference_space", PROPERTY_HINT_ENUM, "Local,Stage,Local Floor"), "1");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/environment_blend_mode", PROPERTY_HINT_ENUM, "Opaque,Additive,Alpha"), "0");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/foveation_level", PROPERTY_HINT_ENUM, "Off,Low,Medium,High"), "0");
GLOBAL_DEF_BASIC("xr/openxr/foveation_dynamic", false);
GLOBAL_DEF_BASIC("xr/openxr/submit_depth_buffer", false);
GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/extensions/debug_utils", PROPERTY_HINT_ENUM, "Disabled,Error,Warning,Info,Verbose"), "0");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/extensions/debug_message_types", PROPERTY_HINT_FLAGS, "General,Validation,Performance,Conformance"), "15");
GLOBAL_DEF_BASIC("xr/openxr/extensions/frame_synthesis", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_unobstructed_data_source", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_controller_data_source", false);
GLOBAL_DEF_RST_BASIC("xr/openxr/extensions/hand_interaction_profile", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/spatial_entity/enabled", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/spatial_entity/enable_spatial_anchors", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/spatial_entity/enable_persistent_anchors", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/spatial_entity/enable_builtin_anchor_detection", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/spatial_entity/enable_plane_tracking", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/spatial_entity/enable_builtin_plane_detection", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/spatial_entity/enable_marker_tracking", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/spatial_entity/enable_builtin_marker_tracking", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/extensions/spatial_entity/aruco_dict", PROPERTY_HINT_ENUM, "4x4 50 IDs,4x4 100 IDs,4x4 250 IDs,4x4 1000 IDs,5x5 50 IDs,5x5 100 IDs,5x5 250 IDs,5x5 1000 IDs,6x6 50 IDs,6x6 100 IDs,6x6 250 IDs,6x6 1000 IDs,7x7 50 IDs,7x7 100 IDs,7x7 250 IDs,7x7 1000 IDs"), "15");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/extensions/spatial_entity/april_tag_dict", PROPERTY_HINT_ENUM, "4x4H5,5x5H9,6x6H10,6x6H11"), "3");
GLOBAL_DEF_RST_BASIC("xr/openxr/extensions/eye_gaze_interaction", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/render_model", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/user_presence", false);
GLOBAL_DEF_BASIC("xr/openxr/binding_modifiers/analog_threshold", false);
GLOBAL_DEF_RST_BASIC("xr/openxr/binding_modifiers/dpad_binding", false);
#ifdef TOOLS_ENABLED
#endif
#endif
Engine::get_singleton()->set_frame_delay(frame_delay);
message_queue = memnew(MessageQueue);
Thread::release_main_thread();
set_current_thread_safe_for_nodes(false);
#if defined(STEAMAPI_ENABLED)
if (editor || project_manager) {
steam_tracker = memnew(SteamTracker);
}
#endif
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
if (p_second_phase) {
exit_err = setup2();
if (exit_err != OK) {
goto error;
}
}
return OK;
error:
text_driver = "";
display_driver = "";
audio_driver = "";
tablet_driver = "";
Engine::get_singleton()->set_write_movie_path(String());
project_path = "";
args.clear();
main_args.clear();
if (show_help) {
print_help(execpath);
}
if (editor) {
OS::get_singleton()->remove_lock_file();
}
EngineDebugger::deinitialize();
if (performance) {
memdelete(performance);
}
if (input_map) {
memdelete(input_map);
}
if (translation_server) {
memdelete(translation_server);
}
if (globals) {
memdelete(globals);
}
if (packed_data) {
memdelete(packed_data);
}
unregister_core_driver_types();
unregister_core_extensions();
if (engine) {
memdelete(engine);
}
unregister_core_types();
OS::get_singleton()->_cmdline.clear();
OS::get_singleton()->_user_args.clear();
if (message_queue) {
memdelete(message_queue);
}
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
#if defined(STEAMAPI_ENABLED)
if (steam_tracker) {
memdelete(steam_tracker);
}
#endif
OS::get_singleton()->finalize_core();
locale = String();
return exit_err;
}
Error _parse_resource_dummy(void *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
VariantParser::Token token;
VariantParser::get_token(p_stream, token, line, r_err_str);
if (token.type != VariantParser::TK_NUMBER && token.type != VariantParser::TK_STRING) {
r_err_str = "Expected number (old style sub-resource index) or String (ext-resource ID)";
return ERR_PARSE_ERROR;
}
r_res.unref();
VariantParser::get_token(p_stream, token, line, r_err_str);
if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
r_err_str = "Expected ')'";
return ERR_PARSE_ERROR;
}
return OK;
}
Error Main::setup2(bool p_show_boot_logo) {
GodotProfileZone("setup2");
OS::get_singleton()->benchmark_begin_measure("Startup", "Main::Setup2");
Thread::make_main_thread();
set_current_thread_safe_for_nodes(true);
print_header(false);
#ifdef TOOLS_ENABLED
int accessibility_mode_editor = 0;
int tablet_driver_editor = -1;
if (editor || project_manager || cmdline_tool) {
OS::get_singleton()->benchmark_begin_measure("Startup", "Initialize Early Settings");
EditorPaths::create();
if (!init_use_custom_screen && (editor || project_manager) && EditorPaths::get_singleton()->are_paths_valid()) {
ERR_FAIL_COND_V(!DirAccess::dir_exists_absolute(EditorPaths::get_singleton()->get_config_dir()), FAILED);
String config_file_path = EditorSettings::get_existing_settings_path();
if (FileAccess::exists(config_file_path)) {
Error err;
Ref<FileAccess> f = FileAccess::open(config_file_path, FileAccess::READ, &err);
if (f.is_valid()) {
VariantParser::StreamFile stream;
stream.f = f;
String assign;
Variant value;
VariantParser::Tag next_tag;
int lines = 0;
String error_text;
VariantParser::ResourceParser rp_new;
rp_new.ext_func = _parse_resource_dummy;
rp_new.sub_func = _parse_resource_dummy;
bool screen_found = false;
String screen_property;
bool prefer_wayland_found = false;
bool prefer_wayland = false;
bool tablet_found = false;
bool ac_found = false;
if (editor) {
screen_property = "interface/editor/editor_screen";
} else if (project_manager) {
screen_property = "interface/editor/project_manager_screen";
} else {
screen_found = true;
}
if (!display_driver.is_empty()) {
prefer_wayland_found = true;
}
while (!screen_found || !init_expand_to_title_found || !init_display_scale_found || !init_custom_scale_found || !prefer_wayland_found || !tablet_found || !ac_found) {
assign = Variant();
next_tag.fields.clear();
next_tag.name = String();
err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp_new, true);
if (err == ERR_FILE_EOF) {
break;
}
if (err == OK && !assign.is_empty()) {
if (!screen_found && assign == screen_property) {
init_screen = value;
screen_found = true;
if (editor) {
restore_editor_window_layout = value.operator int() == EditorSettings::InitialScreen::INITIAL_SCREEN_AUTO;
}
}
if (!ac_found && assign == "interface/accessibility/accessibility_support") {
accessibility_mode_editor = value;
ac_found = true;
} else if (!init_expand_to_title_found && assign == "interface/editor/expand_to_title") {
init_expand_to_title = value;
init_expand_to_title_found = true;
} else if (!init_display_scale_found && assign == "interface/editor/display_scale") {
init_display_scale = value;
init_display_scale_found = true;
} else if (!init_custom_scale_found && assign == "interface/editor/custom_display_scale") {
init_custom_scale = value;
init_custom_scale_found = true;
} else if (!prefer_wayland_found && assign == "run/platforms/linuxbsd/prefer_wayland") {
prefer_wayland = value;
prefer_wayland_found = true;
} else if (!tablet_found && assign == "interface/editor/tablet_driver") {
tablet_driver_editor = value;
tablet_found = true;
}
}
}
if (display_driver.is_empty()) {
if (prefer_wayland) {
display_driver = "wayland";
} else {
display_driver = "default";
}
}
}
}
}
if (found_project && EditorPaths::get_singleton()->is_self_contained()) {
if (ProjectSettings::get_singleton()->get_resource_path() == OS::get_singleton()->get_executable_path().get_base_dir()) {
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.");
OS::get_singleton()->set_exit_code(EXIT_FAILURE);
return FAILED;
}
}
bool has_command_line_window_override = init_use_custom_pos || init_use_custom_screen || init_windowed;
if (editor && !has_command_line_window_override && restore_editor_window_layout) {
Ref<ConfigFile> config;
config.instantiate();
Error err = config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
if (err == OK) {
init_screen = config->get_value("EditorWindow", "screen", init_screen);
String mode = config->get_value("EditorWindow", "mode", "maximized");
window_size = config->get_value("EditorWindow", "size", window_size);
if (mode == "windowed") {
window_mode = DisplayServer::WINDOW_MODE_WINDOWED;
init_windowed = true;
} else if (mode == "fullscreen") {
window_mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
init_fullscreen = true;
} else {
window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
init_maximized = true;
}
if (init_windowed) {
init_use_custom_pos = true;
init_custom_pos = config->get_value("EditorWindow", "position", Vector2i(0, 0));
}
}
}
if (init_screen == EditorSettings::InitialScreen::INITIAL_SCREEN_AUTO) {
init_screen = DisplayServer::SCREEN_PRIMARY;
}
OS::get_singleton()->benchmark_end_measure("Startup", "Initialize Early Settings");
}
#endif
OS::get_singleton()->benchmark_begin_measure("Startup", "Servers");
tsman = memnew(TextServerManager);
if (tsman) {
Ref<TextServerDummy> ts;
ts.instantiate();
tsman->add_interface(ts);
}
#ifndef PHYSICS_3D_DISABLED
physics_server_3d_manager = memnew(PhysicsServer3DManager);
#endif
#ifndef PHYSICS_2D_DISABLED
physics_server_2d_manager = memnew(PhysicsServer2DManager);
#endif
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2DManager::initialize_server_manager();
#endif
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3DManager::initialize_server_manager();
#endif
register_server_types();
{
OS::get_singleton()->benchmark_begin_measure("Servers", "Modules and Extensions");
initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
OS::get_singleton()->benchmark_end_measure("Servers", "Modules and Extensions");
}
{
OS::get_singleton()->benchmark_begin_measure("Servers", "Input");
input = memnew(Input);
OS::get_singleton()->initialize_joypads();
OS::get_singleton()->benchmark_end_measure("Servers", "Input");
}
{
OS::get_singleton()->benchmark_begin_measure("Servers", "Display");
if (display_driver.is_empty()) {
display_driver = GLOBAL_GET("display/display_server/driver");
}
int display_driver_idx = -1;
if (display_driver.is_empty() || display_driver == "default") {
display_driver_idx = 0;
} else {
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
String name = DisplayServer::get_create_function_name(i);
if (display_driver == name) {
display_driver_idx = i;
break;
}
}
if (display_driver_idx < 0) {
display_driver_idx = 0;
}
}
Vector2i *window_position = nullptr;
Vector2i position = init_custom_pos;
if (init_use_custom_pos) {
window_position = &position;
}
Color boot_bg_color = GLOBAL_DEF_BASIC("application/boot_splash/bg_color", boot_splash_bg_color);
DisplayServer::set_early_window_clear_color_override(true, boot_bg_color);
DisplayServer::Context context;
if (editor) {
context = DisplayServer::CONTEXT_EDITOR;
} else if (project_manager) {
context = DisplayServer::CONTEXT_PROJECTMAN;
} else {
context = DisplayServer::CONTEXT_ENGINE;
}
if (init_embed_parent_window_id) {
window_mode = DisplayServer::WINDOW_MODE_WINDOWED;
window_flags = DisplayServer::WINDOW_FLAG_BORDERLESS_BIT;
if (bool(GLOBAL_GET("display/window/size/transparent"))) {
window_flags |= DisplayServer::WINDOW_FLAG_TRANSPARENT_BIT;
}
}
#ifdef TOOLS_ENABLED
if ((project_manager || editor) && init_expand_to_title) {
window_flags |= DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE_BIT;
}
#endif
if (!accessibility_mode_set) {
#ifdef TOOLS_ENABLED
if (editor || project_manager || cmdline_tool) {
accessibility_mode = (DisplayServer::AccessibilityMode)accessibility_mode_editor;
} else {
#else
{
#endif
accessibility_mode = (DisplayServer::AccessibilityMode)GLOBAL_GET("accessibility/general/accessibility_support").operator int64_t();
}
}
DisplayServer::accessibility_set_mode(accessibility_mode);
String rendering_driver = OS::get_singleton()->get_current_rendering_driver_name();
Error err;
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, init_embed_parent_window_id, err);
if (err != OK || display_server == nullptr) {
String last_name = DisplayServer::get_create_function_name(display_driver_idx);
for (int i = 0; i < DisplayServer::get_create_function_count() - 1; i++) {
if (i == display_driver_idx) {
continue;
}
String name = DisplayServer::get_create_function_name(i);
WARN_PRINT(vformat("Display driver %s failed, falling back to %s.", last_name, name));
display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, init_embed_parent_window_id, err);
if (err == OK && display_server != nullptr) {
break;
}
}
}
if (err != OK || display_server == nullptr) {
ERR_PRINT("Unable to create DisplayServer, all display drivers failed.\nUse \"--headless\" command line argument to run the engine in headless mode if this is desired (e.g. for continuous integration).");
if (display_server) {
memdelete(display_server);
}
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
unregister_server_types();
if (input) {
memdelete(input);
}
if (tsman) {
memdelete(tsman);
}
#ifndef PHYSICS_3D_DISABLED
if (physics_server_3d_manager) {
memdelete(physics_server_3d_manager);
}
#endif
#ifndef PHYSICS_2D_DISABLED
if (physics_server_2d_manager) {
memdelete(physics_server_2d_manager);
}
#endif
return err;
}
#ifdef TOOLS_ENABLED
if (project_manager) {
float ui_scale = init_custom_scale;
switch (init_display_scale) {
case 0:
ui_scale = EditorSettings::get_auto_display_scale();
break;
case 1:
ui_scale = 0.75;
break;
case 2:
ui_scale = 1.0;
break;
case 3:
ui_scale = 1.25;
break;
case 4:
ui_scale = 1.5;
break;
case 5:
ui_scale = 1.75;
break;
case 6:
ui_scale = 2.0;
break;
default:
break;
}
if (!(force_res || use_custom_res)) {
display_server->window_set_size(Size2(window_size) * ui_scale, DisplayServer::MAIN_WINDOW_ID);
}
if (display_server->has_feature(DisplayServer::FEATURE_SUBWINDOWS) && !display_server->has_feature(DisplayServer::FEATURE_SELF_FITTING_WINDOWS)) {
Size2 real_size = DisplayServer::get_singleton()->window_get_size();
Rect2i scr_rect = display_server->screen_get_usable_rect(init_screen);
display_server->window_set_position(scr_rect.position + (scr_rect.size - real_size) / 2, DisplayServer::MAIN_WINDOW_ID);
}
}
#endif
if (display_server->has_feature(DisplayServer::FEATURE_SUBWINDOWS)) {
display_server->show_window(DisplayServer::MAIN_WINDOW_ID);
}
if (display_server->has_feature(DisplayServer::FEATURE_ORIENTATION)) {
display_server->screen_set_orientation(window_orientation);
}
OS::get_singleton()->benchmark_end_measure("Servers", "Display");
}
RenderingDevice *rd = RenderingDevice::get_singleton();
if (rd) {
rd->_set_max_fps(engine->get_max_fps());
}
#ifdef TOOLS_ENABLED
if (editor && init_windowed) {
if (DisplayServer::get_singleton()->window_get_mode() == DisplayServer::WINDOW_MODE_WINDOWED) {
Vector2i current_size = DisplayServer::get_singleton()->window_get_size();
Vector2i current_pos = DisplayServer::get_singleton()->window_get_position();
int screen = DisplayServer::get_singleton()->window_get_current_screen();
Rect2i screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
Vector2i adjusted_end = screen_rect.get_end().min(current_pos + current_size);
Vector2i adjusted_pos = screen_rect.get_position().max(adjusted_end - current_size);
Vector2i adjusted_size = DisplayServer::get_singleton()->window_get_min_size().max(adjusted_end - adjusted_pos);
if (current_pos != adjusted_end || current_size != adjusted_size) {
DisplayServer::get_singleton()->window_set_position(adjusted_pos);
DisplayServer::get_singleton()->window_set_size(adjusted_size);
}
}
}
#endif
if (GLOBAL_GET("debug/settings/stdout/print_fps") || print_fps) {
switch (window_vsync_mode) {
case DisplayServer::VSyncMode::VSYNC_DISABLED:
print_line("Requested V-Sync mode: Disabled");
break;
case DisplayServer::VSyncMode::VSYNC_ENABLED:
print_line("Requested V-Sync mode: Enabled - FPS will likely be capped to the monitor refresh rate.");
break;
case DisplayServer::VSyncMode::VSYNC_ADAPTIVE:
print_line("Requested V-Sync mode: Adaptive");
break;
case DisplayServer::VSyncMode::VSYNC_MAILBOX:
print_line("Requested V-Sync mode: Mailbox");
break;
}
}
if (OS::get_singleton()->_separate_thread_render) {
WARN_PRINT("The separate rendering thread feature is experimental. Feel free to try it since it will eventually become a stable feature.\n"
"However, bear in mind that at the moment it can lead to project crashes or instability.\n"
"So, unless you want to test the engine, set the \"rendering/driver/threads/thread_model\" project setting to 'Safe'.");
}
{
OS::get_singleton()->benchmark_begin_measure("Servers", "Tablet Driver");
GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", "");
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "auto,winink,wintab,dummy"), "");
#ifdef TOOLS_ENABLED
if (tablet_driver.is_empty() && tablet_driver_editor != -1) {
tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(tablet_driver_editor);
}
#endif
if (tablet_driver.is_empty()) {
tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver");
if (tablet_driver.is_empty()) {
tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(0);
}
}
for (int i = 0; i < DisplayServer::get_singleton()->tablet_get_driver_count(); i++) {
if (tablet_driver == DisplayServer::get_singleton()->tablet_get_driver_name(i)) {
DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(i));
break;
}
}
if (DisplayServer::get_singleton()->tablet_get_current_driver().is_empty()) {
DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(0));
}
print_verbose("Using \"" + DisplayServer::get_singleton()->tablet_get_current_driver() + "\" pen tablet driver...");
OS::get_singleton()->benchmark_end_measure("Servers", "Tablet Driver");
}
{
OS::get_singleton()->benchmark_begin_measure("Servers", "Rendering");
rendering_server = memnew(RenderingServerDefault(OS::get_singleton()->is_separate_thread_rendering_enabled()));
rendering_server->init();
rendering_server->set_render_loop_enabled(!disable_render_loop);
if (profile_gpu || (!editor && bool(GLOBAL_GET("debug/settings/stdout/print_gpu_profile")))) {
rendering_server->set_print_gpu_profile(true);
}
OS::get_singleton()->benchmark_end_measure("Servers", "Rendering");
}
#ifdef UNIX_ENABLED
if (OS::get_singleton()->get_environment("USER") == "root" && !OS::get_singleton()->has_environment("GODOT_SILENCE_ROOT_WARNING")) {
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.");
}
#endif
{
OS::get_singleton()->benchmark_begin_measure("Servers", "Audio");
AudioDriverManager::initialize(audio_driver_idx);
audio_server = memnew(AudioServer);
audio_server->init();
OS::get_singleton()->benchmark_end_measure("Servers", "Audio");
}
#ifndef XR_DISABLED
{
OS::get_singleton()->benchmark_begin_measure("Servers", "XR");
xr_server = memnew(XRServer);
OS::get_singleton()->benchmark_end_measure("Servers", "XR");
}
#endif
OS::get_singleton()->benchmark_end_measure("Startup", "Servers");
#ifndef WEB_ENABLED
Engine::get_singleton()->print_header("");
#endif
register_core_singletons();
{
OS::get_singleton()->benchmark_begin_measure("Startup", "Setup Window and Boot");
MAIN_PRINT("Main: Setup Logo");
if (!init_embed_parent_window_id) {
if (init_windowed) {
} else if (init_maximized) {
DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_MAXIMIZED);
} else if (init_fullscreen) {
DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_FULLSCREEN);
}
if (init_always_on_top) {
DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, true);
}
}
Color clear = GLOBAL_DEF_BASIC("rendering/environment/defaults/default_clear_color", Color(0.3, 0.3, 0.3));
RenderingServer::get_singleton()->set_default_clear_color(clear);
if (p_show_boot_logo) {
setup_boot_logo();
}
MAIN_PRINT("Main: Clear Color");
DisplayServer::set_early_window_clear_color_override(false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.bmp,*.hdr,*.jpg,*.jpeg,*.svg,*.tga,*.exr,*.webp"), String());
GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"), String());
GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/windows_native_icon", PROPERTY_HINT_FILE, "*.ico"), String());
MAIN_PRINT("Main: Touch Input");
Input *id = Input::get_singleton();
if (id) {
bool agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false);
id->set_agile_input_event_flushing(agile_input_event_flushing);
if (bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_touch_from_mouse", false)) &&
!(editor || project_manager)) {
if (!DisplayServer::get_singleton()->is_touchscreen_available()) {
id->set_emulate_touch_from_mouse(true);
}
}
id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
if (editor) {
id->set_emulate_mouse_from_touch(true);
}
}
OS::get_singleton()->benchmark_end_measure("Startup", "Setup Window and Boot");
}
MAIN_PRINT("Main: Load Translations and Remaps");
{
OS::get_singleton()->benchmark_begin_measure("Startup", "Translations and Remaps");
translation_server->setup();
if (!locale.is_empty()) {
translation_server->set_locale(locale);
}
translation_server->load_project_translations(translation_server->get_main_domain());
ResourceLoader::load_translation_remaps();
OS::get_singleton()->benchmark_end_measure("Startup", "Translations and Remaps");
}
MAIN_PRINT("Main: Load TextServer");
{
OS::get_singleton()->benchmark_begin_measure("Startup", "Text Server");
GLOBAL_DEF_RST("internationalization/rendering/text_driver", "");
String text_driver_options;
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
const String driver_name = TextServerManager::get_singleton()->get_interface(i)->get_name();
if (driver_name == "Dummy") {
continue;
}
if (!text_driver_options.is_empty() && !text_driver_options.contains_char(',')) {
text_driver_options += ",";
}
text_driver_options += driver_name;
}
ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/rendering/text_driver", PROPERTY_HINT_ENUM, text_driver_options));
if (text_driver.is_empty()) {
text_driver = GLOBAL_GET("internationalization/rendering/text_driver");
}
if (!text_driver.is_empty()) {
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
if (TextServerManager::get_singleton()->get_interface(i)->get_name() == text_driver) {
text_driver_idx = i;
break;
}
}
}
if (text_driver_idx < 0) {
int max_features = 0;
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features();
int feature_number = 0;
while (features) {
feature_number += features & 1;
features >>= 1;
}
if (feature_number >= max_features) {
max_features = feature_number;
text_driver_idx = i;
}
}
}
if (text_driver_idx >= 0) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(text_driver_idx);
TextServerManager::get_singleton()->set_primary_interface(ts);
if (ts->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
ts->load_support_data("res://" + ts->get_support_data_filename());
}
} else {
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface.");
}
OS::get_singleton()->benchmark_end_measure("Startup", "Text Server");
}
MAIN_PRINT("Main: Load Scene Types");
OS::get_singleton()->benchmark_begin_measure("Startup", "Scene");
initialize_theme_db();
#if !defined(NAVIGATION_2D_DISABLED) || !defined(NAVIGATION_3D_DISABLED)
MAIN_PRINT("Main: Load Navigation");
#endif
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3DManager::initialize_server();
#endif
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2DManager::initialize_server();
#endif
register_scene_types();
register_driver_types();
register_scene_singletons();
{
OS::get_singleton()->benchmark_begin_measure("Scene", "Modules and Extensions");
initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
OS::get_singleton()->benchmark_end_measure("Scene", "Modules and Extensions");
if (Engine::get_singleton()->get_write_movie_path() != String()) {
movie_writer = MovieWriter::find_writer_for_file(Engine::get_singleton()->get_write_movie_path());
if (movie_writer == nullptr) {
ERR_PRINT("Can't find movie writer for file type, aborting: " + Engine::get_singleton()->get_write_movie_path());
Engine::get_singleton()->set_write_movie_path(String());
}
}
}
PackedStringArray extensions;
extensions.push_back("gd");
if (ClassDB::class_exists("CSharpScript")) {
extensions.push_back("cs");
}
extensions.push_back("gdshader");
GLOBAL_DEF_NOVAL(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions);
OS::get_singleton()->benchmark_end_measure("Startup", "Scene");
#ifdef TOOLS_ENABLED
ClassDB::set_current_api(ClassDB::API_EDITOR);
register_editor_types();
{
OS::get_singleton()->benchmark_begin_measure("Editor", "Modules and Extensions");
initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
OS::get_singleton()->benchmark_end_measure("Editor", "Modules and Extensions");
}
ClassDB::set_current_api(ClassDB::API_CORE);
#endif
MAIN_PRINT("Main: Load Platforms");
OS::get_singleton()->benchmark_begin_measure("Startup", "Platforms");
register_platform_apis();
OS::get_singleton()->benchmark_end_measure("Startup", "Platforms");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/mouse_cursor/custom_image", PROPERTY_HINT_FILE, "*.png,*.bmp,*.hdr,*.jpg,*.jpeg,*.svg,*.tga,*.exr,*.webp"), String());
GLOBAL_DEF_BASIC("display/mouse_cursor/custom_image_hotspot", Vector2());
GLOBAL_DEF_BASIC("display/mouse_cursor/tooltip_position_offset", Point2(10, 10));
if (String(GLOBAL_GET("display/mouse_cursor/custom_image")) != String()) {
Ref<Texture2D> cursor = ResourceLoader::load(
GLOBAL_GET("display/mouse_cursor/custom_image"));
if (cursor.is_valid()) {
Vector2 hotspot = GLOBAL_GET("display/mouse_cursor/custom_image_hotspot");
Input::get_singleton()->set_custom_mouse_cursor(cursor, Input::CursorShape::CURSOR_ARROW, hotspot);
}
}
OS::get_singleton()->benchmark_begin_measure("Startup", "Finalize Setup");
camera_server = CameraServer::create();
MAIN_PRINT("Main: Load Physics");
initialize_physics();
register_server_singletons();
ScriptServer::init_languages();
#if TOOLS_ENABLED
if (editor) {
ResourceUID::scan_for_uid_on_startup = EditorFileSystem::scan_for_uid;
}
#endif
theme_db->initialize_theme();
audio_server->load_default_bus_layout();
#if defined(MODULE_MONO_ENABLED) && defined(TOOLS_ENABLED)
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
BindingsGenerator::handle_cmdline_args(cmdline_args);
#endif
if (use_debug_profiler && EngineDebugger::is_active()) {
EngineDebugger::get_singleton()->profiler_enable("scripts", true);
}
if (!project_manager) {
rendering_server->global_shader_parameters_load_settings(!editor);
}
OS::get_singleton()->benchmark_end_measure("Startup", "Finalize Setup");
_start_success = true;
ClassDB::set_current_api(ClassDB::API_NONE);
print_verbose("CORE API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_CORE)));
print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR)));
MAIN_PRINT("Main: Done");
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup2");
return OK;
}
void Main::setup_boot_logo() {
GodotProfileZone("setup_boot_logo");
MAIN_PRINT("Main: Load Boot Image");
#if !defined(TOOLS_ENABLED) && defined(WEB_ENABLED)
bool show_logo = false;
#else
bool show_logo = true;
#endif
if (show_logo) {
const bool boot_logo_image = GLOBAL_DEF_BASIC("application/boot_splash/show_image", true);
const RenderingServer::SplashStretchMode boot_stretch_mode = GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "application/boot_splash/stretch_mode", PROPERTY_HINT_ENUM, "Disabled,Keep,Keep Width,Keep Height,Cover,Ignore"), 1);
const bool boot_logo_filter = GLOBAL_DEF_BASIC("application/boot_splash/use_filter", true);
String boot_logo_path = GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"), String());
if (boot_logo_path.begins_with("uid://")) {
const ResourceUID::ID logo_id = ResourceUID::get_singleton()->text_to_id(boot_logo_path);
if (ResourceUID::get_singleton()->has_id(logo_id)) {
boot_logo_path = ResourceUID::get_singleton()->get_id_path(logo_id).strip_edges();
} else {
boot_logo_path = String();
}
}
Ref<Image> boot_logo;
if (boot_logo_image) {
if (!boot_logo_path.is_empty()) {
boot_logo.instantiate();
Error load_err = ImageLoader::load_image(boot_logo_path, boot_logo);
if (load_err) {
String msg = (boot_logo_path.ends_with(".png") ? "" : "The only supported format is PNG.");
ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + +"'. " + msg + " Loading default splash.");
}
}
} else {
boot_logo.instantiate();
boot_logo->initialize_data(1, 1, false, Image::FORMAT_RGBA8);
boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0));
}
Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color");
#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
boot_bg_color = GLOBAL_DEF_BASIC("application/boot_splash/bg_color", (editor || project_manager) ? boot_splash_editor_bg_color : boot_splash_bg_color);
#endif
if (boot_logo.is_valid()) {
RenderingServer::get_singleton()->set_boot_image_with_stretch(boot_logo, boot_bg_color, boot_stretch_mode, boot_logo_filter);
} else {
#ifndef NO_DEFAULT_BOOT_LOGO
MAIN_PRINT("Main: Create bootsplash");
#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
Ref<Image> splash = (editor || project_manager) ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png));
#else
Ref<Image> splash = memnew(Image(boot_splash_png));
#endif
MAIN_PRINT("Main: ClearColor");
RenderingServer::get_singleton()->set_default_clear_color(boot_bg_color);
MAIN_PRINT("Main: Image");
RenderingServer::get_singleton()->set_boot_image_with_stretch(splash, boot_bg_color, RenderingServer::SPLASH_STRETCH_MODE_DISABLED);
#endif
}
#if defined(TOOLS_ENABLED) && defined(MACOS_ENABLED)
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_ICON) && OS::get_singleton()->get_bundle_icon_path().is_empty()) {
Ref<Image> icon = memnew(Image(app_icon_png));
DisplayServer::get_singleton()->set_icon(icon);
}
#endif
}
RenderingServer::get_singleton()->set_default_clear_color(
GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
}
String Main::get_locale_override() {
return locale;
}
static MainTimerSync main_timer_sync;
int Main::start() {
GodotProfileZone("start");
OS::get_singleton()->benchmark_begin_measure("Startup", "Main::Start");
ERR_FAIL_COND_V(!_start_success, EXIT_FAILURE);
bool has_icon = false;
String positional_arg;
String game_path;
String script;
String main_loop_type;
bool check_only = false;
#ifdef TOOLS_ENABLED
String doc_tool_path;
bool doc_tool_implicit_cwd = false;
BitField<DocTools::GenerateFlags> gen_flags = {};
String _export_preset;
Vector<String> patches;
bool export_debug = false;
bool export_pack_only = false;
bool install_android_build_template = false;
bool export_patch = false;
#ifdef MODULE_GDSCRIPT_ENABLED
String gdscript_docs_path;
#endif
#ifndef DISABLE_DEPRECATED
bool converting_project = false;
bool validating_converting_project = false;
#endif
#endif
main_timer_sync.init(OS::get_singleton()->get_ticks_usec());
List<String> args = OS::get_singleton()->get_cmdline_args();
for (List<String>::Element *E = args.front(); E; E = E->next()) {
if (E->get() == "--check-only") {
check_only = true;
#ifdef TOOLS_ENABLED
} else if (E->get() == "--no-docbase") {
gen_flags.set_flag(DocTools::GENERATE_FLAG_SKIP_BASIC_TYPES);
} else if (E->get() == "--gdextension-docs") {
gen_flags.set_flag(DocTools::GENERATE_FLAG_SKIP_BASIC_TYPES);
gen_flags.set_flag(DocTools::GENERATE_FLAG_EXTENSION_CLASSES_ONLY);
#ifndef DISABLE_DEPRECATED
} else if (E->get() == "--convert-3to4") {
converting_project = true;
} else if (E->get() == "--validate-conversion-3to4") {
validating_converting_project = true;
#endif
} else if (E->get() == "-e" || E->get() == "--editor") {
editor = true;
} else if (E->get() == "-p" || E->get() == "--project-manager") {
project_manager = true;
} else if (E->get() == "--recovery-mode") {
recovery_mode = true;
} else if (E->get() == "--install-android-build-template") {
install_android_build_template = true;
#endif
} else if (E->get() == "--scene") {
#if defined(OVERRIDE_PATH_ENABLED)
E = E->next();
if (E) {
game_path = ResourceUID::ensure_path(E->get());
} else {
ERR_FAIL_V_MSG(EXIT_FAILURE, "Missing scene path, aborting.");
}
#else
ERR_PRINT(
"`--scene` was specified on the command line, but this Godot binary was compiled without support for path overrides. Aborting.\n"
"To be able to use it, use the `disable_path_overrides=no` SCons option when compiling Godot.\n");
return EXIT_FAILURE;
#endif
} else if (E->get().length() && E->get()[0] != '-' && positional_arg.is_empty() && game_path.is_empty()) {
positional_arg = E->get();
String scene_path = ResourceUID::ensure_path(E->get());
if (scene_path.ends_with(".scn") ||
scene_path.ends_with(".tscn") ||
scene_path.ends_with(".escn") ||
scene_path.ends_with(".res") ||
scene_path.ends_with(".tres")) {
#if defined(OVERRIDE_PATH_ENABLED)
game_path = scene_path;
#else
ERR_PRINT(
"Scene path was specified on the command line, but this Godot binary was compiled without support for path overrides. Aborting.\n"
"To be able to use it, use the `disable_path_overrides=no` SCons option when compiling Godot.\n");
return EXIT_FAILURE;
#endif
}
}
else if (E->next()) {
bool parsed_pair = true;
if (E->get() == "-s" || E->get() == "--script") {
script = E->next()->get();
} else if (E->get() == "--main-loop") {
main_loop_type = E->next()->get();
#ifdef TOOLS_ENABLED
} else if (E->get() == "--doctool") {
doc_tool_path = E->next()->get();
if (doc_tool_path.begins_with("-")) {
doc_tool_path = ".";
doc_tool_implicit_cwd = true;
parsed_pair = false;
}
#ifdef MODULE_GDSCRIPT_ENABLED
} else if (E->get() == "--gdscript-docs") {
gdscript_docs_path = E->next()->get();
#endif
} else if (E->get() == "--export-release") {
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
editor = true;
_export_preset = E->next()->get();
} else if (E->get() == "--export-debug") {
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
editor = true;
_export_preset = E->next()->get();
export_debug = true;
} else if (E->get() == "--export-pack") {
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
editor = true;
_export_preset = E->next()->get();
export_pack_only = true;
} else if (E->get() == "--export-patch") {
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
editor = true;
_export_preset = E->next()->get();
export_pack_only = true;
export_patch = true;
} else if (E->get() == "--patches") {
patches = E->next()->get().split(",", false);
#endif
} else {
parsed_pair = false;
}
if (parsed_pair) {
E = E->next();
}
} else if (E->get().begins_with("--export-")) {
ERR_FAIL_V_MSG(EXIT_FAILURE, "Missing export preset name, aborting.");
}
#ifdef TOOLS_ENABLED
else if (E->get() == "--doctool") {
doc_tool_path = ".";
doc_tool_implicit_cwd = true;
}
#endif
}
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);
if (Engine::get_singleton()->is_editor_hint()) {
minimum_time_msec = 0;
}
#ifdef TOOLS_ENABLED
#ifdef MODULE_GDSCRIPT_ENABLED
if (!doc_tool_path.is_empty() && gdscript_docs_path.is_empty()) {
#else
if (!doc_tool_path.is_empty()) {
#endif
Engine::get_singleton()->set_editor_hint(true);
if (!locale.is_empty() && locale != "en") {
load_doc_translations(locale);
}
{
Ref<DirAccess> da = DirAccess::open(doc_tool_path);
ERR_FAIL_COND_V_MSG(da.is_null(), EXIT_FAILURE, "Argument supplied to --doctool must be a valid directory path.");
if (doc_tool_implicit_cwd) {
ERR_FAIL_COND_V_MSG(!da->dir_exists("doc"), EXIT_FAILURE, "--doctool must be run from the Godot repository's root folder, or specify a path that points there.");
}
}
#ifndef MODULE_MONO_ENABLED
GLOBAL_DEF("dotnet/project/assembly_name", "");
GLOBAL_DEF("dotnet/project/solution_directory", "");
GLOBAL_DEF(PropertyInfo(Variant::INT, "dotnet/project/assembly_reload_attempts", PROPERTY_HINT_RANGE, "1,16,1,or_greater"), 3);
#endif
Error err;
DocTools doc;
doc.generate(gen_flags);
DocTools docsrc;
HashMap<String, String> doc_data_classes;
HashSet<String> checked_paths;
print_line("Loading docs...");
const bool gdextension_docs = gen_flags.has_flag(DocTools::GENERATE_FLAG_EXTENSION_CLASSES_ONLY);
if (!gdextension_docs) {
for (int i = 0; i < _doc_data_class_path_count; i++) {
String path = _doc_data_class_paths[i].path;
if (path.is_relative_path()) {
path = doc_tool_path.path_join(path);
}
String name = _doc_data_class_paths[i].name;
doc_data_classes[name] = path;
if (!checked_paths.has(path)) {
checked_paths.insert(path);
Ref<DirAccess> da = DirAccess::create_for_path(path);
err = da->make_dir_recursive(path);
ERR_FAIL_COND_V_MSG(err != OK, EXIT_FAILURE, "Error: Can't create directory: " + path + ": " + itos(err));
print_line("Loading docs from: " + path);
err = docsrc.load_classes(path);
ERR_FAIL_COND_V_MSG(err != OK, EXIT_FAILURE, "Error loading docs from: " + path + ": " + itos(err));
}
}
}
String index_path = gdextension_docs ? doc_tool_path.path_join("doc_classes") : doc_tool_path.path_join("doc/classes");
Ref<DirAccess> da = DirAccess::create_for_path(index_path);
err = da->make_dir_recursive(index_path);
ERR_FAIL_COND_V_MSG(err != OK, EXIT_FAILURE, "Error: Can't create index directory: " + index_path + ": " + itos(err));
print_line("Loading classes from: " + index_path);
err = docsrc.load_classes(index_path);
ERR_FAIL_COND_V_MSG(err != OK, EXIT_FAILURE, "Error loading classes from: " + index_path + ": " + itos(err));
checked_paths.insert(index_path);
print_line("Merging docs...");
doc.merge_from(docsrc);
for (const String &E : checked_paths) {
print_line("Erasing old docs at: " + E);
err = DocTools::erase_classes(E);
ERR_FAIL_COND_V_MSG(err != OK, EXIT_FAILURE, "Error erasing old docs at: " + E + ": " + itos(err));
}
print_line("Generating new docs...");
err = doc.save_classes(index_path, doc_data_classes, !gdextension_docs);
ERR_FAIL_COND_V_MSG(err != OK, EXIT_FAILURE, "Error saving new docs:" + itos(err));
print_line("Deleting docs cache...");
if (FileAccess::exists(EditorHelp::get_cache_full_path())) {
DirAccess::remove_file_or_error(EditorHelp::get_cache_full_path());
}
return EXIT_SUCCESS;
}
{
if (dump_gdextension_interface) {
GDExtensionInterfaceDump::generate_gdextension_interface_file("gdextension_interface.json");
}
if (dump_gdextension_interface_header) {
GDExtensionInterfaceHeaderGenerator::generate_gdextension_interface_header("gdextension_interface.h");
}
if (dump_extension_api) {
Engine::get_singleton()->set_editor_hint(true);
GDExtensionAPIDump::generate_extension_json_file("extension_api.json", include_docs_in_extension_api_dump);
}
if (dump_gdextension_interface || dump_gdextension_interface_header || dump_extension_api) {
return EXIT_SUCCESS;
}
if (validate_extension_api) {
Engine::get_singleton()->set_editor_hint(true);
bool valid = GDExtensionAPIDump::validate_extension_json_file(validate_extension_api_file) == OK;
return valid ? EXIT_SUCCESS : EXIT_FAILURE;
}
}
#ifndef DISABLE_DEPRECATED
if (converting_project) {
int ret = ProjectConverter3To4(converter_max_kb_file, converter_max_line_length).convert();
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}
if (validating_converting_project) {
bool ret = ProjectConverter3To4(converter_max_kb_file, converter_max_line_length).validate_conversion();
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}
#endif
#endif
#if defined(OVERRIDE_PATH_ENABLED)
bool disable_override = GLOBAL_GET("application/config/disable_project_settings_override");
if (disable_override) {
script = String();
game_path = String();
main_loop_type = String();
}
#else
script = String();
game_path = String();
main_loop_type = String();
#endif
if (script.is_empty() && game_path.is_empty()) {
const String main_scene = GLOBAL_GET("application/run/main_scene");
if (main_scene.begins_with("uid://")) {
ResourceUID::ID id = ResourceUID::get_singleton()->text_to_id(main_scene);
if (!editor && !ResourceUID::get_singleton()->has_id(id) && !FileAccess::exists(ResourceUID::get_singleton()->get_cache_file())) {
OS::get_singleton()->alert("Main scene's path could not be resolved from UID. Make sure the project is imported first. Aborting.");
ERR_FAIL_V_MSG(EXIT_FAILURE, "Main scene's path could not be resolved from UID. Make sure the project is imported first. Aborting.");
}
game_path = ResourceUID::get_singleton()->get_id_path(id);
} else {
game_path = main_scene;
}
}
#ifdef TOOLS_ENABLED
if (!editor && !project_manager && !cmdline_tool && script.is_empty() && game_path.is_empty()) {
OS::get_singleton()->alert("Couldn't detect whether to run the editor, the project manager or a specific project. Aborting.");
ERR_FAIL_V_MSG(EXIT_FAILURE, "Couldn't detect whether to run the editor, the project manager or a specific project. Aborting.");
}
#endif
MainLoop *main_loop = nullptr;
if (editor) {
main_loop = memnew(SceneTree);
}
if (main_loop_type.is_empty()) {
main_loop_type = GLOBAL_GET("application/run/main_loop_type");
}
if (!script.is_empty()) {
Ref<Script> script_res = ResourceLoader::load(script);
ERR_FAIL_COND_V_MSG(script_res.is_null(), EXIT_FAILURE, "Can't load script: " + script);
if (check_only) {
return script_res->is_valid() ? EXIT_SUCCESS : EXIT_FAILURE;
}
if (script_res->can_instantiate()) {
StringName instance_type = script_res->get_instance_base_type();
Object *obj = ClassDB::instantiate(instance_type);
MainLoop *script_loop = Object::cast_to<MainLoop>(obj);
if (!script_loop) {
if (obj) {
memdelete(obj);
}
OS::get_singleton()->alert(vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", script));
ERR_FAIL_V_MSG(EXIT_FAILURE, vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", script));
}
script_loop->set_script(script_res);
main_loop = script_loop;
} else {
return EXIT_FAILURE;
}
} else {
if (!editor && !ClassDB::class_exists(main_loop_type) && ScriptServer::is_global_class(main_loop_type)) {
String script_path = ScriptServer::get_global_class_path(main_loop_type);
Ref<Script> script_res = ResourceLoader::load(script_path);
if (script_res.is_null()) {
OS::get_singleton()->alert("Error: Could not load MainLoop script type: " + main_loop_type);
ERR_FAIL_V_MSG(EXIT_FAILURE, vformat("Could not load global class %s.", main_loop_type));
}
StringName script_base = script_res->get_instance_base_type();
Object *obj = ClassDB::instantiate(script_base);
MainLoop *script_loop = Object::cast_to<MainLoop>(obj);
if (!script_loop) {
if (obj) {
memdelete(obj);
}
OS::get_singleton()->alert("Error: Invalid MainLoop script base type: " + script_base);
ERR_FAIL_V_MSG(EXIT_FAILURE, vformat("The global class %s does not inherit from SceneTree or MainLoop.", main_loop_type));
}
script_loop->set_script(script_res);
main_loop = script_loop;
}
}
if (!main_loop && main_loop_type.is_empty()) {
main_loop_type = "SceneTree";
}
if (!main_loop) {
if (!ClassDB::class_exists(main_loop_type)) {
OS::get_singleton()->alert("Error: MainLoop type doesn't exist: " + main_loop_type);
return EXIT_FAILURE;
} else {
Object *ml = ClassDB::instantiate(main_loop_type);
ERR_FAIL_NULL_V_MSG(ml, EXIT_FAILURE, "Can't instance MainLoop type.");
main_loop = Object::cast_to<MainLoop>(ml);
if (!main_loop) {
memdelete(ml);
ERR_FAIL_V_MSG(EXIT_FAILURE, "Invalid MainLoop type.");
}
}
}
OS::get_singleton()->set_main_loop(main_loop);
SceneTree *sml = Object::cast_to<SceneTree>(main_loop);
if (sml) {
#ifdef DEBUG_ENABLED
if (debug_collisions) {
sml->set_debug_collisions_hint(true);
}
if (debug_paths) {
sml->set_debug_paths_hint(true);
}
if (debug_navigation) {
sml->set_debug_navigation_hint(true);
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2D::get_singleton()->set_debug_navigation_enabled(true);
#endif
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3D::get_singleton()->set_debug_navigation_enabled(true);
#endif
}
if (debug_avoidance) {
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2D::get_singleton()->set_debug_avoidance_enabled(true);
#endif
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3D::get_singleton()->set_debug_avoidance_enabled(true);
#endif
}
if (debug_navigation || debug_avoidance) {
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2D::get_singleton()->set_active(true);
NavigationServer2D::get_singleton()->set_debug_enabled(true);
#endif
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3D::get_singleton()->set_active(true);
NavigationServer3D::get_singleton()->set_debug_enabled(true);
#endif
}
if (debug_canvas_item_redraw) {
RenderingServer::get_singleton()->canvas_item_set_debug_redraw(true);
}
if (debug_mute_audio) {
AudioServer::get_singleton()->set_debug_mute(true);
}
#endif
if (single_threaded_scene) {
sml->set_disable_node_threading(true);
}
bool embed_subwindows = GLOBAL_GET("display/window/subwindows/embed_subwindows");
if (single_window || (!project_manager && !editor && embed_subwindows) || !DisplayServer::get_singleton()->has_feature(DisplayServer::Feature::FEATURE_SUBWINDOWS)) {
sml->get_root()->set_embedding_subwindows(true);
}
ResourceLoader::add_custom_loaders();
ResourceSaver::add_custom_savers();
if (!project_manager && !editor) {
if (!game_path.is_empty() || !script.is_empty()) {
OS::get_singleton()->benchmark_begin_measure("Startup", "Load Autoloads");
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads(ProjectSettings::get_singleton()->get_autoload_list());
for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
const ProjectSettings::AutoloadInfo &info = E.value;
if (info.is_singleton) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->add_global_constant(info.name, Variant());
}
}
}
List<Node *> to_add;
for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
const ProjectSettings::AutoloadInfo &info = E.value;
Node *n = nullptr;
if (ResourceLoader::get_resource_type(info.path) == "PackedScene") {
Ref<PackedScene> scn;
scn.instantiate();
scn->set_path(ResourceUID::ensure_path(info.path));
scn->reload_from_file();
ERR_CONTINUE_MSG(scn.is_null(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path));
if (scn.is_valid()) {
n = scn->instantiate();
}
} else {
Ref<Resource> res = ResourceLoader::load(info.path);
ERR_CONTINUE_MSG(res.is_null(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path));
Ref<Script> script_res = res;
if (script_res.is_valid()) {
StringName ibt = script_res->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "Node");
ERR_CONTINUE_MSG(!valid_type, vformat("Failed to instantiate an autoload, script '%s' does not inherit from 'Node'.", info.path));
Object *obj = ClassDB::instantiate(ibt);
ERR_CONTINUE_MSG(!obj, vformat("Failed to instantiate an autoload, cannot instantiate '%s'.", ibt));
n = Object::cast_to<Node>(obj);
n->set_script(script_res);
}
}
ERR_CONTINUE_MSG(!n, vformat("Failed to instantiate an autoload, path is not pointing to a scene or a script: %s.", info.path));
n->set_name(info.name);
to_add.push_back(n);
if (info.is_singleton) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->add_global_constant(info.name, n);
}
}
}
for (Node *E : to_add) {
sml->get_root()->add_child(E);
}
OS::get_singleton()->benchmark_end_measure("Startup", "Load Autoloads");
}
}
#ifdef TOOLS_ENABLED
#ifdef MODULE_GDSCRIPT_ENABLED
if (!doc_tool_path.is_empty() && !gdscript_docs_path.is_empty()) {
DocTools docs;
Error err;
Vector<String> paths = get_files_with_extension(gdscript_docs_path, "gd");
ERR_FAIL_COND_V_MSG(paths.is_empty(), EXIT_FAILURE, "Couldn't find any GDScript files under the given directory: " + gdscript_docs_path);
for (const String &path : paths) {
Ref<GDScript> gdscript = ResourceLoader::load(path);
for (const DocData::ClassDoc &class_doc : gdscript->get_documentation()) {
docs.add_doc(class_doc);
}
}
if (doc_tool_implicit_cwd) {
doc_tool_path = "./docs";
}
Ref<DirAccess> da = DirAccess::create_for_path(doc_tool_path);
err = da->make_dir_recursive(doc_tool_path);
ERR_FAIL_COND_V_MSG(err != OK, EXIT_FAILURE, "Error: Can't create GDScript docs directory: " + doc_tool_path + ": " + itos(err));
HashMap<String, String> doc_data_classes;
err = docs.save_classes(doc_tool_path, doc_data_classes, false);
ERR_FAIL_COND_V_MSG(err != OK, EXIT_FAILURE, "Error saving GDScript docs:" + itos(err));
return EXIT_SUCCESS;
}
#endif
EditorNode *editor_node = nullptr;
if (editor) {
OS::get_singleton()->benchmark_begin_measure("Startup", "Editor");
sml->get_root()->set_translation_domain("godot.editor");
if (editor_pseudolocalization) {
translation_server->get_editor_domain()->set_pseudolocalization_enabled(true);
}
editor_node = memnew(EditorNode);
sml->get_root()->add_child(editor_node);
if (!_export_preset.is_empty()) {
editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only, install_android_build_template, export_patch, patches);
game_path = "";
}
OS::get_singleton()->benchmark_end_measure("Startup", "Editor");
}
#endif
sml->set_auto_accept_quit(GLOBAL_GET("application/config/auto_accept_quit"));
sml->set_quit_on_go_back(GLOBAL_GET("application/config/quit_on_go_back"));
if (!editor && !project_manager) {
String stretch_mode = GLOBAL_GET("display/window/stretch/mode");
String stretch_aspect = GLOBAL_GET("display/window/stretch/aspect");
Size2i stretch_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"),
GLOBAL_GET("display/window/size/viewport_height"));
real_t stretch_scale = GLOBAL_GET("display/window/stretch/scale");
String stretch_scale_mode = GLOBAL_GET("display/window/stretch/scale_mode");
Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED;
if (stretch_mode == "canvas_items") {
cs_sm = Window::CONTENT_SCALE_MODE_CANVAS_ITEMS;
} else if (stretch_mode == "viewport") {
cs_sm = Window::CONTENT_SCALE_MODE_VIEWPORT;
}
Window::ContentScaleAspect cs_aspect = Window::CONTENT_SCALE_ASPECT_IGNORE;
if (stretch_aspect == "keep") {
cs_aspect = Window::CONTENT_SCALE_ASPECT_KEEP;
} else if (stretch_aspect == "keep_width") {
cs_aspect = Window::CONTENT_SCALE_ASPECT_KEEP_WIDTH;
} else if (stretch_aspect == "keep_height") {
cs_aspect = Window::CONTENT_SCALE_ASPECT_KEEP_HEIGHT;
} else if (stretch_aspect == "expand") {
cs_aspect = Window::CONTENT_SCALE_ASPECT_EXPAND;
}
Window::ContentScaleStretch cs_stretch = Window::CONTENT_SCALE_STRETCH_FRACTIONAL;
if (stretch_scale_mode == "integer") {
cs_stretch = Window::CONTENT_SCALE_STRETCH_INTEGER;
}
sml->get_root()->set_content_scale_mode(cs_sm);
sml->get_root()->set_content_scale_aspect(cs_aspect);
sml->get_root()->set_content_scale_stretch(cs_stretch);
sml->get_root()->set_content_scale_size(stretch_size);
sml->get_root()->set_content_scale_factor(stretch_scale);
sml->set_auto_accept_quit(GLOBAL_GET("application/config/auto_accept_quit"));
sml->set_quit_on_go_back(GLOBAL_GET("application/config/quit_on_go_back"));
String appname = GLOBAL_GET("application/config/name");
appname = TranslationServer::get_singleton()->translate(appname);
#ifdef DEBUG_ENABLED
DisplayServer::get_singleton()->window_set_title(vformat("%s (DEBUG)", appname));
#else
DisplayServer::get_singleton()->window_set_title(appname);
#endif
bool snap_controls = GLOBAL_GET("gui/common/snap_controls_to_pixels");
sml->get_root()->set_snap_controls_to_pixels(snap_controls);
int drag_threshold = GLOBAL_GET("gui/common/drag_threshold");
sml->get_root()->set_drag_threshold(drag_threshold);
bool font_oversampling = GLOBAL_GET("gui/fonts/dynamic_fonts/use_oversampling");
sml->get_root()->set_use_oversampling(font_oversampling);
int texture_filter = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_filter");
int texture_repeat = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_repeat");
sml->get_root()->set_default_canvas_item_texture_filter(
Viewport::DefaultCanvasItemTextureFilter(texture_filter));
sml->get_root()->set_default_canvas_item_texture_repeat(
Viewport::DefaultCanvasItemTextureRepeat(texture_repeat));
}
#ifdef TOOLS_ENABLED
if (editor) {
bool editor_embed_subwindows = EDITOR_GET("interface/editor/single_window_mode");
if (editor_embed_subwindows) {
sml->get_root()->set_embedding_subwindows(true);
}
restore_editor_window_layout = EDITOR_GET("interface/editor/editor_screen").operator int() == EditorSettings::InitialScreen::INITIAL_SCREEN_AUTO;
}
#endif
String local_game_path;
if (!game_path.is_empty() && !project_manager) {
local_game_path = game_path.replace_char('\\', '/');
if (!local_game_path.begins_with("res://")) {
bool absolute =
(local_game_path.size() > 1) && (local_game_path[0] == '/' || local_game_path[1] == ':');
if (!absolute) {
if (ProjectSettings::get_singleton()->is_using_datapack()) {
local_game_path = "res://" + local_game_path;
} else {
int sep = local_game_path.rfind_char('/');
if (sep == -1) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(da.is_null(), EXIT_FAILURE);
local_game_path = da->get_current_dir().path_join(local_game_path);
} else {
Ref<DirAccess> da = DirAccess::open(local_game_path.substr(0, sep));
if (da.is_valid()) {
local_game_path = da->get_current_dir().path_join(
local_game_path.substr(sep + 1));
}
}
}
}
}
local_game_path = ProjectSettings::get_singleton()->localize_path(local_game_path);
#ifdef TOOLS_ENABLED
if (editor) {
if (!recovery_mode && (game_path != ResourceUID::ensure_path(String(GLOBAL_GET("application/run/main_scene"))) || !editor_node->has_scenes_in_session())) {
Error serr = editor_node->load_scene(local_game_path);
if (serr != OK) {
ERR_PRINT("Failed to load scene");
}
}
if (!debug_server_uri.is_empty()) {
EditorDebuggerNode::get_singleton()->start(debug_server_uri);
EditorDebuggerNode::get_singleton()->set_keep_open(true);
}
}
#endif
}
if (!project_manager && !editor) {
OS::get_singleton()->benchmark_begin_measure("Startup", "Load Game");
Crypto::load_default_certificates(GLOBAL_GET("network/tls/certificate_bundle_override"));
if (!game_path.is_empty()) {
Node *scene = nullptr;
Ref<PackedScene> scenedata = ResourceLoader::load(local_game_path);
if (scenedata.is_valid()) {
scene = scenedata->instantiate();
}
ERR_FAIL_NULL_V_MSG(scene, EXIT_FAILURE, "Failed loading scene: " + local_game_path + ".");
sml->add_current_scene(scene);
#ifdef MACOS_ENABLED
#ifndef TOOLS_ENABLED
if ((FileAccess::exists(OS::get_singleton()->get_bundle_resource_dir().path_join("Assets.car")) && !OS::get_singleton()->get_bundle_icon_name().is_empty()) || (!OS::get_singleton()->get_bundle_icon_path().is_empty())) {
has_icon = true;
}
#endif
String mac_icon_path = GLOBAL_GET("application/config/macos_native_icon");
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_ICON) && !mac_icon_path.is_empty() && !has_icon) {
DisplayServer::get_singleton()->set_native_icon(mac_icon_path);
has_icon = true;
}
#endif
#ifdef WINDOWS_ENABLED
String win_icon_path = GLOBAL_GET("application/config/windows_native_icon");
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_ICON) && !win_icon_path.is_empty()) {
DisplayServer::get_singleton()->set_native_icon(win_icon_path);
has_icon = true;
}
#endif
String icon_path = GLOBAL_GET("application/config/icon");
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_ICON) && !icon_path.is_empty() && !has_icon) {
Ref<Image> icon;
icon.instantiate();
if (ImageLoader::load_image(icon_path, icon) == OK) {
DisplayServer::get_singleton()->set_icon(icon);
has_icon = true;
}
}
}
OS::get_singleton()->benchmark_end_measure("Startup", "Load Game");
}
#ifdef TOOLS_ENABLED
if (project_manager) {
OS::get_singleton()->benchmark_begin_measure("Startup", "Project Manager");
Engine::get_singleton()->set_editor_hint(true);
sml->get_root()->set_translation_domain("godot.editor");
if (editor_pseudolocalization) {
translation_server->get_editor_domain()->set_pseudolocalization_enabled(true);
}
ProjectManager *pmanager = memnew(ProjectManager);
ProgressDialog *progress_dialog = memnew(ProgressDialog);
pmanager->add_child(progress_dialog);
sml->get_root()->add_child(pmanager);
OS::get_singleton()->benchmark_end_measure("Startup", "Project Manager");
}
if (project_manager || editor) {
Crypto::load_default_certificates(
EditorSettings::get_singleton()->get_setting("network/tls/editor_tls_certificates").operator String());
}
if (recovery_mode) {
Engine::get_singleton()->set_recovery_mode_hint(true);
}
#endif
}
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_ICON) && !has_icon && OS::get_singleton()->get_bundle_icon_path().is_empty()) {
Ref<Image> icon = memnew(Image(app_icon_png));
DisplayServer::get_singleton()->set_icon(icon);
}
if (movie_writer) {
Size2i movie_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
String stretch_mode = GLOBAL_GET("display/window/stretch/mode");
if (stretch_mode != "viewport") {
const int window_width_override = GLOBAL_GET("display/window/size/window_width_override");
if (window_width_override > 0) {
movie_size.width = window_width_override;
}
const int window_height_override = GLOBAL_GET("display/window/size/window_height_override");
if (window_height_override > 0) {
movie_size.height = window_height_override;
}
}
movie_writer->begin(movie_size, fixed_fps, Engine::get_singleton()->get_write_movie_path());
}
GDExtensionManager::get_singleton()->startup();
#ifdef MACOS_ENABLED
if (minimum_time_msec) {
int64_t minimum_time = 1000 * minimum_time_msec;
uint64_t prev_time = OS::get_singleton()->get_ticks_usec();
while (minimum_time > 0) {
DisplayServer::get_singleton()->process_events();
OS::get_singleton()->delay_usec(100);
uint64_t next_time = OS::get_singleton()->get_ticks_usec();
minimum_time -= (next_time - prev_time);
prev_time = next_time;
}
} else {
DisplayServer::get_singleton()->process_events();
}
#else
if (minimum_time_msec) {
uint64_t minimum_time = 1000 * minimum_time_msec;
uint64_t elapsed_time = OS::get_singleton()->get_ticks_usec();
if (elapsed_time < minimum_time) {
OS::get_singleton()->delay_usec(minimum_time - elapsed_time);
}
}
#endif
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Start");
OS::get_singleton()->benchmark_dump();
return EXIT_SUCCESS;
}
uint64_t Main::last_ticks = 0;
uint32_t Main::frames = 0;
uint32_t Main::hide_print_fps_attempts = 3;
uint32_t Main::frame = 0;
bool Main::force_redraw_requested = false;
int Main::iterating = 0;
bool Main::is_iterating() {
return iterating > 0;
}
static uint64_t physics_process_max = 0;
static uint64_t process_max = 0;
static uint64_t navigation_process_max = 0;
bool Main::iteration() {
GodotProfileZone("Main::iteration");
GodotProfileZoneGroupedFirst(_profile_zone, "prepare");
iterating++;
const uint64_t ticks = OS::get_singleton()->get_ticks_usec();
Engine::get_singleton()->_frame_ticks = ticks;
main_timer_sync.set_cpu_ticks_usec(ticks);
main_timer_sync.set_fixed_fps(fixed_fps);
const uint64_t ticks_elapsed = ticks - last_ticks;
const int physics_ticks_per_second = Engine::get_singleton()->get_user_physics_ticks_per_second();
const double physics_step = 1.0 / physics_ticks_per_second;
const double time_scale = Engine::get_singleton()->get_effective_time_scale();
MainFrameTime advance = main_timer_sync.advance(physics_step, physics_ticks_per_second);
double process_step = advance.process_step;
double scaled_step = process_step * time_scale;
Engine::get_singleton()->_process_step = process_step;
Engine::get_singleton()->_physics_interpolation_fraction = advance.interpolation_fraction;
uint64_t physics_process_ticks = 0;
uint64_t process_ticks = 0;
#if !defined(NAVIGATION_2D_DISABLED) || !defined(NAVIGATION_3D_DISABLED)
uint64_t navigation_process_ticks = 0;
#endif
frame += ticks_elapsed;
last_ticks = ticks;
const int max_physics_steps = Engine::get_singleton()->get_user_max_physics_steps_per_frame();
if (fixed_fps == -1 && advance.physics_steps > max_physics_steps) {
process_step -= (advance.physics_steps - max_physics_steps) * physics_step;
advance.physics_steps = max_physics_steps;
}
bool exit = false;
#ifndef XR_DISABLED
GodotProfileZoneGrouped(_profile_zone, "xr_server->_process");
XRServer::get_singleton()->_process();
#endif
GodotProfileZoneGrouped(_profile_zone, "physics");
for (int iters = 0; iters < advance.physics_steps; ++iters) {
GodotProfileZone("Physics Step");
GodotProfileZoneGroupedFirst(_physics_zone, "setup");
if (Input::get_singleton()->is_agile_input_event_flushing()) {
Input::get_singleton()->flush_buffered_events();
}
Engine::get_singleton()->_in_physics = true;
Engine::get_singleton()->_physics_frames++;
uint64_t physics_begin = OS::get_singleton()->get_ticks_usec();
GodotProfileZoneGrouped(_physics_zone, "main loop iteration prepare");
OS::get_singleton()->get_main_loop()->iteration_prepare();
#ifndef PHYSICS_3D_DISABLED
GodotProfileZoneGrouped(_physics_zone, "PhysicsServer3D::sync");
PhysicsServer3D::get_singleton()->sync();
PhysicsServer3D::get_singleton()->flush_queries();
#endif
#ifndef PHYSICS_2D_DISABLED
GodotProfileZoneGrouped(_physics_zone, "PhysicsServer2D::sync");
PhysicsServer2D::get_singleton()->sync();
PhysicsServer2D::get_singleton()->flush_queries();
#endif
GodotProfileZoneGrouped(_physics_zone, "physics_process");
if (OS::get_singleton()->get_main_loop()->physics_process(physics_step * time_scale)) {
#ifndef PHYSICS_3D_DISABLED
PhysicsServer3D::get_singleton()->end_sync();
#endif
#ifndef PHYSICS_2D_DISABLED
PhysicsServer2D::get_singleton()->end_sync();
#endif
Engine::get_singleton()->_in_physics = false;
exit = true;
break;
}
#if !defined(NAVIGATION_2D_DISABLED) || !defined(NAVIGATION_3D_DISABLED)
uint64_t navigation_begin = OS::get_singleton()->get_ticks_usec();
#ifndef NAVIGATION_2D_DISABLED
GodotProfileZoneGrouped(_profile_zone, "NavigationServer2D::physics_process");
NavigationServer2D::get_singleton()->physics_process(physics_step * time_scale);
#endif
#ifndef NAVIGATION_3D_DISABLED
GodotProfileZoneGrouped(_profile_zone, "NavigationServer3D::physics_process");
NavigationServer3D::get_singleton()->physics_process(physics_step * time_scale);
#endif
navigation_process_ticks = MAX(navigation_process_ticks, OS::get_singleton()->get_ticks_usec() - navigation_begin);
navigation_process_max = MAX(OS::get_singleton()->get_ticks_usec() - navigation_begin, navigation_process_max);
message_queue->flush();
#endif
#ifndef PHYSICS_3D_DISABLED
GodotProfileZoneGrouped(_profile_zone, "3D physics");
PhysicsServer3D::get_singleton()->end_sync();
PhysicsServer3D::get_singleton()->step(physics_step * time_scale);
#endif
#ifndef PHYSICS_2D_DISABLED
GodotProfileZoneGrouped(_profile_zone, "2D physics");
PhysicsServer2D::get_singleton()->end_sync();
PhysicsServer2D::get_singleton()->step(physics_step * time_scale);
#endif
message_queue->flush();
GodotProfileZoneGrouped(_profile_zone, "main loop iteration end");
OS::get_singleton()->get_main_loop()->iteration_end();
physics_process_ticks = MAX(physics_process_ticks, OS::get_singleton()->get_ticks_usec() - physics_begin);
physics_process_max = MAX(OS::get_singleton()->get_ticks_usec() - physics_begin, physics_process_max);
Engine::get_singleton()->_in_physics = false;
}
if (Input::get_singleton()->is_agile_input_event_flushing()) {
Input::get_singleton()->flush_buffered_events();
}
uint64_t process_begin = OS::get_singleton()->get_ticks_usec();
GodotProfileZoneGrouped(_profile_zone, "process");
if (OS::get_singleton()->get_main_loop()->process(process_step * time_scale)) {
exit = true;
}
message_queue->flush();
#ifndef NAVIGATION_2D_DISABLED
GodotProfileZoneGrouped(_profile_zone, "process 2D navigation");
NavigationServer2D::get_singleton()->process(process_step * time_scale);
#endif
#ifndef NAVIGATION_3D_DISABLED
GodotProfileZoneGrouped(_profile_zone, "process 3D navigation");
NavigationServer3D::get_singleton()->process(process_step * time_scale);
#endif
GodotProfileZoneGrouped(_profile_zone, "RenderingServer::sync");
RenderingServer::get_singleton()->sync();
GodotProfileZoneGrouped(_profile_zone, "RenderingServer::draw");
const bool has_pending_resources_for_processing = RD::get_singleton() && RD::get_singleton()->has_pending_resources_for_processing();
bool wants_present = (DisplayServer::get_singleton()->can_any_window_draw() ||
DisplayServer::get_singleton()->has_additional_outputs()) &&
RenderingServer::get_singleton()->is_render_loop_enabled();
if (wants_present || has_pending_resources_for_processing) {
wants_present |= force_redraw_requested;
if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) {
if (RenderingServer::get_singleton()->has_changed()) {
RenderingServer::get_singleton()->draw(wants_present, scaled_step);
Engine::get_singleton()->increment_frames_drawn();
}
} else {
RenderingServer::get_singleton()->draw(wants_present, scaled_step);
Engine::get_singleton()->increment_frames_drawn();
force_redraw_requested = false;
}
}
process_ticks = OS::get_singleton()->get_ticks_usec() - process_begin;
process_max = MAX(process_ticks, process_max);
uint64_t frame_time = OS::get_singleton()->get_ticks_usec() - ticks;
GodotProfileZoneGrouped(_profile_zone, "GDExtensionManager::frame");
GDExtensionManager::get_singleton()->frame();
GodotProfileZoneGrouped(_profile_zone, "ScriptServer::frame");
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->frame();
}
GodotProfileZoneGrouped(_profile_zone, "AudioServer::update");
AudioServer::get_singleton()->update();
if (EngineDebugger::is_active()) {
EngineDebugger::get_singleton()->iteration(frame_time, process_ticks, physics_process_ticks, physics_step);
}
frames++;
Engine::get_singleton()->_process_frames++;
if (frame > 1000000) {
if (hide_print_fps_attempts == 0) {
if (editor || project_manager) {
if (print_fps) {
print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
}
} else if (print_fps || GLOBAL_GET("debug/settings/stdout/print_fps")) {
print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
}
} else {
hide_print_fps_attempts--;
}
Engine::get_singleton()->_fps = frames;
performance->set_process_time(USEC_TO_SEC(process_max));
performance->set_physics_process_time(USEC_TO_SEC(physics_process_max));
performance->set_navigation_process_time(USEC_TO_SEC(navigation_process_max));
process_max = 0;
physics_process_max = 0;
navigation_process_max = 0;
frame %= 1000000;
frames = 0;
}
iterating--;
if (movie_writer) {
GodotProfileZoneGrouped(_profile_zone, "movie_writer->add_frame");
movie_writer->add_frame();
}
#ifdef TOOLS_ENABLED
bool quit_after_timeout = false;
#endif
if ((quit_after > 0) && (Engine::get_singleton()->_process_frames >= quit_after)) {
#ifdef TOOLS_ENABLED
quit_after_timeout = true;
#endif
exit = true;
}
#ifdef TOOLS_ENABLED
if (wait_for_import && EditorFileSystem::get_singleton() && EditorFileSystem::get_singleton()->doing_first_scan()) {
exit = false;
}
#endif
if (fixed_fps != -1) {
return exit;
}
SceneTree *scene_tree = SceneTree::get_singleton();
bool wake_for_events = scene_tree && scene_tree->is_accessibility_enabled();
GodotProfileZoneGrouped(_profile_zone, "OS::add_frame_delay");
OS::get_singleton()->add_frame_delay(DisplayServer::get_singleton()->window_can_draw(), wake_for_events);
#ifdef TOOLS_ENABLED
if (auto_build_solutions) {
auto_build_solutions = false;
if (!editor) {
OS::get_singleton()->set_exit_code(EXIT_FAILURE);
ERR_FAIL_V_MSG(true,
"Command line option --build-solutions was passed, but no project is being edited. Aborting.");
}
if (!EditorNode::get_singleton()->call_build()) {
OS::get_singleton()->set_exit_code(EXIT_FAILURE);
ERR_FAIL_V_MSG(true,
"Command line option --build-solutions was passed, but the build callback failed. Aborting.");
}
}
#endif
#ifdef TOOLS_ENABLED
if (exit && quit_after_timeout && EditorNode::get_singleton()) {
EditorNode::get_singleton()->unload_editor_addons();
}
#endif
return exit;
}
void Main::force_redraw() {
force_redraw_requested = true;
}
void Main::cleanup(bool p_force) {
GodotProfileZone("cleanup");
OS::get_singleton()->benchmark_begin_measure("Shutdown", "Main::Cleanup");
if (!p_force) {
ERR_FAIL_COND(!_start_success);
}
#ifdef DEBUG_ENABLED
if (input) {
input->flush_frame_parsed_events();
}
#endif
GDExtensionManager::get_singleton()->shutdown();
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
TextServerManager::get_singleton()->get_interface(i)->cleanup();
}
if (movie_writer) {
movie_writer->end();
}
ResourceLoader::clear_thread_load_tasks();
ResourceLoader::remove_custom_loaders();
ResourceSaver::remove_custom_savers();
PropertyListHelper::clear_base_helpers();
if (OS::get_singleton()->get_exit_code() == EXIT_SUCCESS) {
OS::get_singleton()->remove_lock_file();
}
message_queue->flush();
OS::get_singleton()->delete_main_loop();
OS::get_singleton()->_cmdline.clear();
OS::get_singleton()->_user_args.clear();
OS::get_singleton()->_execpath = "";
OS::get_singleton()->_local_clipboard = "";
ResourceLoader::clear_translation_remaps();
WorkerThreadPool::get_singleton()->exit_languages_threads();
ScriptServer::finish_languages();
RenderingServer::get_singleton()->sync();
rendering_server->global_shader_parameters_clear();
#ifndef XR_DISABLED
if (xr_server) {
xr_server->set_primary_interface(Ref<XRInterface>());
}
#endif
#ifdef TOOLS_ENABLED
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
unregister_editor_types();
#endif
ImageLoader::cleanup();
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
unregister_platform_apis();
unregister_driver_types();
unregister_scene_types();
finalize_theme_db();
#ifndef NAVIGATION_2D_DISABLED
NavigationServer2DManager::finalize_server();
NavigationServer2DManager::finalize_server_manager();
#endif
#ifndef NAVIGATION_3D_DISABLED
NavigationServer3DManager::finalize_server();
NavigationServer3DManager::finalize_server_manager();
#endif
finalize_physics();
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
unregister_server_types();
EngineDebugger::deinitialize();
#ifndef XR_DISABLED
if (xr_server) {
memdelete(xr_server);
}
#endif
if (audio_server) {
audio_server->finish();
memdelete(audio_server);
}
if (camera_server) {
memdelete(camera_server);
}
OS::get_singleton()->finalize();
finalize_display();
if (input) {
memdelete(input);
}
if (packed_data) {
memdelete(packed_data);
}
if (performance) {
memdelete(performance);
}
if (input_map) {
memdelete(input_map);
}
if (translation_server) {
memdelete(translation_server);
}
if (tsman) {
memdelete(tsman);
}
#ifndef PHYSICS_3D_DISABLED
if (physics_server_3d_manager) {
memdelete(physics_server_3d_manager);
}
#endif
#ifndef PHYSICS_2D_DISABLED
if (physics_server_2d_manager) {
memdelete(physics_server_2d_manager);
}
#endif
if (globals) {
memdelete(globals);
}
if (OS::get_singleton()->is_restart_on_exit_set()) {
List<String> args = OS::get_singleton()->get_restart_on_exit_arguments();
OS::get_singleton()->create_instance(args);
OS::get_singleton()->set_restart_on_exit(false, List<String>());
}
message_queue->flush();
memdelete(message_queue);
#if defined(STEAMAPI_ENABLED)
if (steam_tracker) {
memdelete(steam_tracker);
}
#endif
unregister_core_driver_types();
unregister_core_extensions();
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
if (engine) {
memdelete(engine);
}
unregister_core_types();
OS::get_singleton()->benchmark_end_measure("Shutdown", "Main::Cleanup");
OS::get_singleton()->benchmark_dump();
OS::get_singleton()->finalize_core();
}