Skip to content

Commit

Permalink
Add support for command-line user arguments.
Browse files Browse the repository at this point in the history
Implements the standard Unix double dash (--) commandline argument:
* Arguments after a double dash (--) are ignored by Godot and stored for the user.
* User can access them via `OS.get_cmdline_user_args()`

Example:

`godot.exe scene_to_run.tscn --fullscreen -- --start-level 2`
  • Loading branch information
reduz committed Jul 31, 2022
1 parent 6d599ed commit 0dd6537
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 6 deletions.
11 changes: 11 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,16 @@ Vector<String> OS::get_cmdline_args() {
return cmdlinev;
}

Vector<String> OS::get_cmdline_user_args() {
List<String> cmdline = ::OS::get_singleton()->get_cmdline_user_args();
Vector<String> cmdlinev;
for (const String &E : cmdline) {
cmdlinev.push_back(E);
}

return cmdlinev;
}

String OS::get_locale() const {
return ::OS::get_singleton()->get_locale();
}
Expand Down Expand Up @@ -614,6 +624,7 @@ void OS::_bind_methods() {

ClassDB::bind_method(D_METHOD("get_name"), &OS::get_name);
ClassDB::bind_method(D_METHOD("get_cmdline_args"), &OS::get_cmdline_args);
ClassDB::bind_method(D_METHOD("get_cmdline_user_args"), &OS::get_cmdline_user_args);

ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &OS::delay_usec);
ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &OS::delay_msec);
Expand Down
1 change: 1 addition & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ class OS : public Object {

String get_name() const;
Vector<String> get_cmdline_args();
Vector<String> get_cmdline_user_args();

String get_locale() const;
String get_locale_language() const;
Expand Down
3 changes: 2 additions & 1 deletion core/os/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,10 @@ String OS::get_model_name() const {
return "GenericDevice";
}

void OS::set_cmdline(const char *p_execpath, const List<String> &p_args) {
void OS::set_cmdline(const char *p_execpath, const List<String> &p_args, const List<String> &p_user_args) {
_execpath = String::utf8(p_execpath);
_cmdline = p_args;
_user_args = p_user_args;
}

String OS::get_unique_id() const {
Expand Down
4 changes: 3 additions & 1 deletion core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class OS {
static uint64_t target_ticks;
String _execpath;
List<String> _cmdline;
List<String> _user_args;
bool _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0.
bool low_processor_usage_mode = false;
int low_processor_usage_mode_sleep_usec = 10000;
Expand Down Expand Up @@ -106,7 +107,7 @@ class OS {
virtual void finalize() = 0;
virtual void finalize_core() = 0;

virtual void set_cmdline(const char *p_execpath, const List<String> &p_args);
virtual void set_cmdline(const char *p_execpath, const List<String> &p_args, const List<String> &p_user_args);

virtual bool _check_internal_feature_support(const String &p_feature) = 0;

Expand Down Expand Up @@ -162,6 +163,7 @@ class OS {

virtual String get_name() const = 0;
virtual List<String> get_cmdline_args() const { return _cmdline; }
virtual List<String> get_cmdline_user_args() const { return _user_args; }
virtual List<String> get_cmdline_platform_args() const { return List<String>(); }
virtual String get_model_name() const;

Expand Down
11 changes: 11 additions & 0 deletions doc/classes/OS.xml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@
}
[/csharp]
[/codeblocks]
[b]Note:[/b] Passing custom user arguments directly is not recommended, as the engine may discard or modify them. Instead, the best way is to use the standard UNIX double dash ([code]--[/code]) and then pass custom arguments, which the engine itself will ignore. These can be read via [method get_cmdline_user_args].
</description>
</method>
<method name="get_cmdline_user_args">
<return type="PackedStringArray" />
<description>
Similar to [method get_cmdline_args], but this returns the user arguments (any argument passed after the double dash [code]--[/code] argument). These are left untouched by Godot for the user.
For example, in the command line below, [code]--fullscreen[/code] will not be returned in [method get_cmdline_user_args] and [code]--level 1[/code] will only be returned in [method get_cmdline_user_args]:
[codeblock]
godot --fullscreen -- --level 1
[/codeblock]
</description>
</method>
<method name="get_config_dir" qualifiers="const">
Expand Down
15 changes: 12 additions & 3 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print("\n");

OS::get_singleton()->print("Run options:\n");
OS::get_singleton()->print(" -- Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.\n");
#ifdef TOOLS_ENABLED
OS::get_singleton()->print(" -e, --editor Start the editor instead of running the scene.\n");
OS::get_singleton()->print(" -p, --project-manager Start the project manager, even if a project is auto-detected.\n");
Expand Down Expand Up @@ -623,6 +624,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
/* 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.
Expand Down Expand Up @@ -695,9 +698,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
continue;
}
#endif

List<String>::Element *N = I->next();

if (I->get() == "-h" || I->get() == "--help" || I->get() == "/?") { // display help
if (adding_user_args) {
user_args.push_back(I->get());
} else if (I->get() == "-h" || I->get() == "--help" || I->get() == "/?") { // display help

show_help = true;
exit_code = ERR_HELP; // Hack to force an early exit in `main()` with a success code.
Expand Down Expand Up @@ -1200,7 +1206,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->print("Missing --xr-mode argument, aborting.\n");
goto error;
}

} else if (I->get() == "--") {
adding_user_args = true;
} else {
main_args.push_back(I->get());
}
Expand Down Expand Up @@ -1377,7 +1384,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph

Logger::set_flush_stdout_on_print(ProjectSettings::get_singleton()->get("application/run/flush_stdout_on_print"));

OS::get_singleton()->set_cmdline(execpath, main_args);
OS::get_singleton()->set_cmdline(execpath, main_args, user_args);

// possibly be worth changing the default from vulkan to something lower spec,
// for the project manager, depending on how smooth the fallback is.
Expand Down Expand Up @@ -1670,6 +1677,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
unregister_core_types();

OS::get_singleton()->_cmdline.clear();
OS::get_singleton()->_user_args.clear();

if (message_queue) {
memdelete(message_queue);
Expand Down Expand Up @@ -3001,6 +3009,7 @@ void Main::cleanup(bool p_force) {
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 = "";

Expand Down
2 changes: 1 addition & 1 deletion tests/test_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ int test_main(int argc, char *argv[]) {
for (int i = 0; i < argc; i++) {
args.push_back(String::utf8(argv[i]));
}
OS::get_singleton()->set_cmdline("", args);
OS::get_singleton()->set_cmdline("", args, List<String>());

// Run custom test tools.
if (test_commands) {
Expand Down

0 comments on commit 0dd6537

Please sign in to comment.