-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
/
main.cpp
4770 lines (4031 loc) · 178 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**************************************************************************/
/* main.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "main.h"
#include "core/config/project_settings.h"
#include "core/core_globals.h"
#include "core/crypto/crypto.h"
#include "core/debugger/engine_debugger.h"
#include "core/extension/extension_api_dump.h"
#include "core/extension/gdextension_interface_dump.gen.h"
#include "core/extension/gdextension_manager.h"
#include "core/input/input.h"
#include "core/input/input_map.h"
#include "core/io/dir_access.h"
#include "core/io/file_access_pack.h"
#include "core/io/file_access_zip.h"
#include "core/io/image.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/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_server.h"
#include "servers/camera_server.h"
#include "servers/display_server.h"
#include "servers/movie_writer/movie_writer.h"
#include "servers/movie_writer/movie_writer_mjpeg.h"
#include "servers/navigation_server_3d.h"
#include "servers/navigation_server_3d_dummy.h"
#include "servers/register_server_types.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/text/text_server_dummy.h"
#include "servers/text_server.h"
// 2D
#include "servers/navigation_server_2d.h"
#include "servers/navigation_server_2d_dummy.h"
#include "servers/physics_server_2d.h"
#include "servers/physics_server_2d_dummy.h"
#ifndef _3D_DISABLED
#include "servers/physics_server_3d.h"
#include "servers/physics_server_3d_dummy.h"
#include "servers/xr_server.h"
#endif // _3D_DISABLED
#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_data_class_path.gen.h"
#include "editor/doc_tools.h"
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/editor_translation.h"
#include "editor/progress_dialog.h"
#include "editor/project_manager.h"
#include "editor/register_editor_types.h"
#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
#include "main/splash_editor.gen.h"
#endif
#ifndef DISABLE_DEPRECATED
#include "editor/project_converter_3_to_4.h"
#endif // DISABLE_DEPRECATED
#endif // TOOLS_ENABLED
#if defined(STEAMAPI_ENABLED)
#include "main/steam_tracker.h"
#endif
#include "modules/modules_enabled.gen.h" // For mono.
#if defined(MODULE_MONO_ENABLED) && defined(TOOLS_ENABLED)
#include "modules/mono/editor/bindings_generator.h"
#endif
#ifdef MODULE_GDSCRIPT_ENABLED
#include "modules/gdscript/gdscript.h"
#if defined(TOOLS_ENABLED) && !defined(GDSCRIPT_NO_LSP)
#include "modules/gdscript/language_server/gdscript_language_server.h"
#endif // TOOLS_ENABLED && !GDSCRIPT_NO_LSP
#endif // MODULE_GDSCRIPT_ENABLED
/* Static members */
// Singletons
// Initialized in setup()
static Engine *engine = nullptr;
static ProjectSettings *globals = nullptr;
static Input *input = nullptr;
static InputMap *input_map = nullptr;
static TranslationServer *translation_server = nullptr;
static Performance *performance = nullptr;
static PackedData *packed_data = nullptr;
#ifdef MINIZIP_ENABLED
static ZipArchive *zip_packed_data = nullptr;
#endif
static MessageQueue *message_queue = nullptr;
#if defined(STEAMAPI_ENABLED)
static SteamTracker *steam_tracker = nullptr;
#endif
// Initialized in setup2()
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;
static NavigationServer2D *navigation_server_2d = nullptr;
static PhysicsServer2DManager *physics_server_2d_manager = nullptr;
static PhysicsServer2D *physics_server_2d = nullptr;
static NavigationServer3D *navigation_server_3d = nullptr;
#ifndef _3D_DISABLED
static PhysicsServer3DManager *physics_server_3d_manager = nullptr;
static PhysicsServer3D *physics_server_3d = nullptr;
static XRServer *xr_server = nullptr;
#endif // _3D_DISABLED
// We error out if setup2() doesn't turn this true
static bool _start_success = false;
// Drivers
String display_driver = "";
String tablet_driver = "";
String text_driver = "";
String rendering_driver = "";
String rendering_method = "";
static int text_driver_idx = -1;
static int audio_driver_idx = -1;
// Engine config/tools
static bool single_window = false;
static bool editor = false;
static bool project_manager = false;
static bool cmdline_tool = false;
static String locale;
static String log_file;
static bool show_help = false;
static uint64_t quit_after = 0;
static OS::ProcessID editor_pid = 0;
#ifdef TOOLS_ENABLED
static bool found_project = false;
static bool auto_build_solutions = false;
static String debug_server_uri;
static bool wait_for_import = false;
static bool restore_editor_window_layout = true;
#ifndef DISABLE_DEPRECATED
static int converter_max_kb_file = 4 * 1024; // 4MB
static int converter_max_line_length = 100000;
#endif // DISABLE_DEPRECATED
HashMap<Main::CLIScope, Vector<String>> forwardable_cli_arguments;
#endif
static bool single_threaded_scene = false;
// Display
static DisplayServer::WindowMode window_mode = DisplayServer::WINDOW_MODE_WINDOWED;
static DisplayServer::ScreenOrientation window_orientation = DisplayServer::SCREEN_LANDSCAPE;
static DisplayServer::VSyncMode window_vsync_mode = DisplayServer::VSYNC_ENABLED;
static uint32_t window_flags = 0;
static Size2i window_size = Size2i(1152, 648);
static int init_screen = DisplayServer::SCREEN_PRIMARY;
static bool init_fullscreen = false;
static bool init_maximized = false;
static bool init_windowed = false;
static bool init_always_on_top = false;
static bool init_use_custom_pos = false;
static bool init_use_custom_screen = false;
static Vector2 init_custom_pos;
static int64_t init_embed_parent_window_id = 0;
// Debug
static bool use_debug_profiler = false;
#ifdef DEBUG_ENABLED
static bool debug_collisions = false;
static bool debug_paths = false;
static bool debug_navigation = false;
static bool debug_avoidance = false;
static bool debug_canvas_item_redraw = false;
#endif
static int max_fps = -1;
static int frame_delay = 0;
static int audio_output_latency = 0;
static bool disable_render_loop = false;
static int fixed_fps = -1;
static MovieWriter *movie_writer = nullptr;
static bool disable_vsync = false;
static bool print_fps = false;
#ifdef TOOLS_ENABLED
static bool editor_pseudolocalization = false;
static bool dump_gdextension_interface = false;
static bool dump_extension_api = false;
static bool include_docs_in_extension_api_dump = false;
static bool validate_extension_api = false;
static String validate_extension_api_file;
#endif
bool profile_gpu = false;
// Constants.
static const String NULL_DISPLAY_DRIVER("headless");
static const String NULL_AUDIO_DRIVER("Dummy");
// The length of the longest column in the command-line help we should align to
// (excluding the 2-space left and right margins).
// Currently, this is `--export-release <preset> <path>`.
static const int OPTION_COLUMN_LENGTH = 32;
/* Helper methods */
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(VERSION_HASH);
if (!hash.is_empty()) {
hash = "." + hash.left(9);
}
return String(VERSION_FULL_BUILD) + hash;
}
#if defined(TOOLS_ENABLED) && defined(MODULE_GDSCRIPT_ENABLED)
static Vector<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
// FIXME: Could maybe be moved to have less code in main.cpp.
void initialize_physics() {
#ifndef _3D_DISABLED
/// 3D Physics Server
physics_server_3d = PhysicsServer3DManager::get_singleton()->new_server(
GLOBAL_GET(PhysicsServer3DManager::setting_property_name));
if (!physics_server_3d) {
// Physics server not found, Use the default physics
physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server();
}
// Fall back to dummy if no default server has been registered.
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);
}
// Should be impossible, but make sure it's not null.
ERR_FAIL_NULL_MSG(physics_server_3d, "Failed to initialize PhysicsServer3D.");
physics_server_3d->init();
#endif // _3D_DISABLED
// 2D Physics server
physics_server_2d = PhysicsServer2DManager::get_singleton()->new_server(
GLOBAL_GET(PhysicsServer2DManager::get_singleton()->setting_property_name));
if (!physics_server_2d) {
// Physics server not found, Use the default physics
physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server();
}
// Fall back to dummy if no default server has been registered.
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);
}
// Should be impossible, but make sure it's not null.
ERR_FAIL_NULL_MSG(physics_server_2d, "Failed to initialize PhysicsServer2D.");
physics_server_2d->init();
}
void finalize_physics() {
#ifndef _3D_DISABLED
physics_server_3d->finish();
memdelete(physics_server_3d);
#endif // _3D_DISABLED
physics_server_2d->finish();
memdelete(physics_server_2d);
}
void finalize_display() {
rendering_server->finish();
memdelete(rendering_server);
memdelete(display_server);
}
void initialize_navigation_server() {
ERR_FAIL_COND(navigation_server_3d != nullptr);
ERR_FAIL_COND(navigation_server_2d != nullptr);
// Init 3D Navigation Server
navigation_server_3d = NavigationServer3DManager::new_default_server();
// Fall back to dummy if no default server has been registered.
if (!navigation_server_3d) {
navigation_server_3d = memnew(NavigationServer3DDummy);
}
// Should be impossible, but make sure it's not null.
ERR_FAIL_NULL_MSG(navigation_server_3d, "Failed to initialize NavigationServer3D.");
navigation_server_3d->init();
// Init 2D Navigation Server
navigation_server_2d = NavigationServer2DManager::new_default_server();
if (!navigation_server_2d) {
navigation_server_2d = memnew(NavigationServer2DDummy);
}
ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D.");
navigation_server_2d->init();
}
void finalize_navigation_server() {
ERR_FAIL_NULL(navigation_server_3d);
navigation_server_3d->finish();
memdelete(navigation_server_3d);
navigation_server_3d = nullptr;
ERR_FAIL_NULL(navigation_server_2d);
navigation_server_2d->finish();
memdelete(navigation_server_2d);
navigation_server_2d = nullptr;
}
void initialize_theme_db() {
theme_db = memnew(ThemeDB);
}
void finalize_theme_db() {
memdelete(theme_db);
theme_db = nullptr;
}
//#define DEBUG_INIT
#ifdef DEBUG_INIT
#define MAIN_PRINT(m_txt) print_line(m_txt)
#else
#define MAIN_PRINT(m_txt)
#endif
void Main::print_header(bool p_rich) {
if (VERSION_TIMESTAMP > 0) {
// Version timestamp available.
if (p_rich) {
Engine::get_singleton()->print_header_rich("\u001b[38;5;39m" + String(VERSION_NAME) + "\u001b[0m v" + get_full_version_string() + " (" + Time::get_singleton()->get_datetime_string_from_unix_time(VERSION_TIMESTAMP, true) + " UTC) - \u001b[4m" + String(VERSION_WEBSITE));
} else {
Engine::get_singleton()->print_header(String(VERSION_NAME) + " v" + get_full_version_string() + " (" + Time::get_singleton()->get_datetime_string_from_unix_time(VERSION_TIMESTAMP, true) + " UTC) - " + String(VERSION_WEBSITE));
}
} else {
if (p_rich) {
Engine::get_singleton()->print_header_rich("\u001b[38;5;39m" + String(VERSION_NAME) + "\u001b[0m v" + get_full_version_string() + " - \u001b[4m" + String(VERSION_WEBSITE));
} else {
Engine::get_singleton()->print_header(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
}
}
}
/**
* Prints a copyright notice in the command-line help with colored text. A newline is
* automatically added at the end.
*/
void Main::print_help_copyright(const char *p_notice) {
OS::get_singleton()->print("\u001b[90m%s\u001b[0m\n", p_notice);
}
/**
* Prints a title in the command-line help with colored text. A newline is
* automatically added at beginning and at the end.
*/
void Main::print_help_title(const char *p_title) {
OS::get_singleton()->print("\n\u001b[1;93m%s:\u001b[0m\n", p_title);
}
/**
* Returns the option string with required and optional arguments colored separately from the rest of the option.
* This color replacement must be done *after* calling `rpad()` for the length padding to be done correctly.
*/
String Main::format_help_option(const char *p_option) {
return (String(p_option)
.rpad(OPTION_COLUMN_LENGTH)
.replace("[", "\u001b[96m[")
.replace("]", "]\u001b[0m")
.replace("<", "\u001b[95m<")
.replace(">", ">\u001b[0m"));
}
/**
* Prints an option in the command-line help with colored text. No newline is
* added at the end. `p_availability` denotes which build types the argument is
* available in. Support in release export templates implies support in debug
* export templates and editor. Support in debug export templates implies
* support in editor.
*/
void Main::print_help_option(const char *p_option, const char *p_description, CLIOptionAvailability p_availability) {
const bool option_empty = (p_option && !p_option[0]);
if (!option_empty) {
const char *availability_badge = "";
switch (p_availability) {
case CLI_OPTION_AVAILABILITY_EDITOR:
availability_badge = "\u001b[1;91mE";
break;
case CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG:
availability_badge = "\u001b[1;94mD";
break;
case CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE:
availability_badge = "\u001b[1;92mR";
break;
case CLI_OPTION_AVAILABILITY_HIDDEN:
// Use for multiline option names (but not when the option name is empty).
availability_badge = " ";
break;
}
OS::get_singleton()->print(
" \u001b[92m%s %s\u001b[0m %s",
format_help_option(p_option).utf8().ptr(),
availability_badge,
p_description);
} else {
// Make continuation lines for descriptions faint if the option name is empty.
OS::get_singleton()->print(
" \u001b[92m%s \u001b[0m \u001b[90m%s",
format_help_option(p_option).utf8().ptr(),
p_description);
}
}
void Main::print_help(const char *p_binary) {
print_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 scene or \"project.godot\" file]\u001b[0m\n", p_binary);
#if defined(TOOLS_ENABLED)
print_help_title("Option legend (this build = editor)");
#elif defined(DEBUG_ENABLED)
print_help_title("Option legend (this build = debug export template)");
#else
print_help_title("Option legend (this build = release export template)");
#endif
OS::get_singleton()->print(" \u001b[1;92mR\u001b[0m Available in editor builds, debug export templates and release export templates.\n");
#ifdef DEBUG_ENABLED
OS::get_singleton()->print(" \u001b[1;94mD\u001b[0m Available in editor builds and debug export templates only.\n");
#endif
#ifdef TOOLS_ENABLED
OS::get_singleton()->print(" \u001b[1;91mE\u001b[0m Only available in editor builds.\n");
#endif
print_help_title("General options");
print_help_option("-h, --help", "Display this help message.\n");
print_help_option("--version", "Display the version string.\n");
print_help_option("-v, --verbose", "Use verbose stdout mode.\n");
print_help_option("--quiet", "Quiet mode, silences stdout messages. Errors are still displayed.\n");
print_help_option("--no-header", "Do not print engine version and rendering 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("--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 Debugger Adaptor 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 // MODULE_GDSCRIPT_ENABLED && !GDSCRIPT_NO_LSP
#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");
print_help_option("--path <directory>", "Path to a project (<directory> must contain a \"project.godot\" file).\n");
print_help_option("-u, --upwards", "Scan folders upwards for project.godot file.\n");
print_help_option("--main-pack <file>", "Path to a pack (.pck) file to load.\n");
#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
print_help_option("--remote-fs <address>", "Remote filesystem (<host/IP>[:<port>] address).\n");
print_help_option("--remote-fs-password <password>", "Password for remote filesystem.\n");
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_title("Debug options");
print_help_option("-d, --debug", "Debug (local stdout debugger).\n");
print_help_option("-b, --breakpoints", "Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
print_help_option("--profiling", "Enable profiling in the script debugger.\n");
print_help_option("--gpu-profile", "Show a GPU profile of the tasks that took the most time during frame rendering.\n");
print_help_option("--gpu-validation", "Enable graphics API validation layers for debugging.\n");
#ifdef DEBUG_ENABLED
print_help_option("--gpu-abort", "Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
#endif
print_help_option("--generate-spirv-debug-info", "Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
#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
print_help_option("--remote-debug <uri>", "Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
print_help_option("--single-threaded-scene", "Force scene tree to run in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
#if defined(DEBUG_ENABLED)
print_help_option("--debug-collisions", "Show collision shapes when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
print_help_option("--debug-paths", "Show path lines when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
print_help_option("--debug-navigation", "Show navigation polygons when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
print_help_option("--debug-avoidance", "Show navigation avoidance debug visuals when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
print_help_option("--debug-stringnames", "Print all StringName allocations to stdout when the engine quits.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
print_help_option("--debug-canvas-item-redraw", "Display a rectangle each time a canvas item requests a redraw (useful to troubleshoot low processor mode).\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG);
#endif
print_help_option("--max-fps <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
print_help_title("Standalone tools");
print_help_option("-s, --script <script>", "Run a script.\n");
print_help_option("--main-loop <main_loop_name>", "Run a MainLoop specified by its global class name.\n");
print_help_option("--check-only", "Only parse for errors and quit (use with --script).\n");
#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
// Commands are long; split the description to a second line.
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 // DISABLE_DEPRECATED
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-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);
#ifdef TESTS_ENABLED
print_help_option("--test [--help]", "Run unit tests. Use --test --help for more information.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
#endif
OS::get_singleton()->print("\n");
}
#ifdef TESTS_ENABLED
// The order is the same as in `Main::setup()`, only core and some editor types
// are initialized here. This also combines `Main::setup2()` initialization.
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(); // Here globals are present.
translation_server = memnew(TranslationServer);
tsman = memnew(TextServerManager);
if (tsman) {
Ref<TextServerDummy> ts;
ts.instantiate();
tsman->add_interface(ts);
}
#ifndef _3D_DISABLED
physics_server_3d_manager = memnew(PhysicsServer3DManager);
#endif // _3D_DISABLED
physics_server_2d_manager = memnew(PhysicsServer2DManager);
// From `Main::setup2()`.
register_early_core_singletons();
initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
register_core_extensions();
register_core_singletons();
/** INITIALIZE SERVERS **/
register_server_types();
#ifndef _3D_DISABLED
XRServer::set_xr_mode(XRServer::XRMODE_OFF); // Skip in tests.
#endif // _3D_DISABLED
initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
translation_server->setup(); //register translations, load them, etc.
if (!locale.is_empty()) {
translation_server->set_locale(locale);
}
translation_server->load_translations();
ResourceLoader::load_translation_remaps(); //load remaps for resources
ResourceLoader::load_path_remaps();
// Initialize ThemeDB early so that scene types can register their theme items.
// Default theme will be initialized later, after modules and ScriptServer are ready.
initialize_theme_db();
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 needs modules to be initialized so that sub-resources can be loaded.
theme_db->initialize_theme_noproject();
initialize_navigation_server();
ERR_FAIL_COND_V(TextServerManager::get_singleton()->get_interface_count() == 0, ERR_CANT_CREATE);
/* Use one with the most features available. */
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;
}
// The order is the same as in `Main::cleanup()`.
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();
finalize_navigation_server();
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 _3D_DISABLED
if (physics_server_3d_manager) {
memdelete(physics_server_3d_manager);
}
#endif // _3D_DISABLED
if (physics_server_2d_manager) {
memdelete(physics_server_2d_manager);
}
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 ((strncmp(argv[x], "--test", 6) == 0) && (strlen(argv[x]) == 6)) {
tests_need_run = true;
#ifdef TESTS_ENABLED
// TODO: need to come up with different test contexts.
// Not every test requires high-level functionality like `ClassDB`.
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;
}
/* Engine initialization
*
* Consists of several methods that are called by each platform's specific main(argc, argv).
* To fully understand engine init, one should therefore start from the platform's main and
* see how it calls into the Main class' methods.
*
* The initialization is typically done in 3 steps (with the setup2 step triggered either
* automatically by setup, or manually in the platform's main).
*
* - setup(execpath, argc, argv, p_second_phase) is the main entry point for all platforms,
* responsible for the initialization of all low level singletons and core types, and parsing
* command line arguments to configure things accordingly.
* If p_second_phase is true, it will chain into setup2() (default behavior). This is
* disabled on some platforms (Android, iOS) which trigger the second step in their own time.
*
* - setup2(p_main_tid_override) registers high level servers and singletons, displays the
* boot splash, then registers higher level types (scene, editor, etc.).
*
* - start() is the last step and that's where command line tools can run, or the main loop
* can be created eventually and the project settings put into action. That's also where
* the editor node is created, if relevant.
* start() does it own argument parsing for a subset of the command line arguments described
* in help, it's a bit messy and should be globalized with the setup() parsing somehow.
*/
Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_phase) {
Thread::make_main_thread();
set_current_thread_safe_for_nodes(true);
OS::get_singleton()->initialize();
// Benchmark tracking must be done after `OS::get_singleton()->initialize()` as on some
// platforms, it's used to set up the time utilities.
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(); //here globals are present
translation_server = memnew(TranslationServer);
performance = memnew(Performance);
GDREGISTER_CLASS(Performance);
engine->add_singleton(Engine::Singleton("Performance", performance));
// Only flush stdout in debug builds by default, as spamming `print()` will
// decrease performance if this is enabled.
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");
/* argument parsing and main creation */
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();
// Add command line arguments.
for (int i = 0; i < argc; i++) {
args.push_back(String::utf8(argv[i]));
}
// Add arguments received from macOS LaunchService (URL schemas, file associations).
for (const String &arg : platform_args) {
args.push_back(arg);
}
List<String>::Element *I = args.front();