Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editor: Allow to override CLI args on a per-instance basis #57975

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/config/project_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1221,6 +1221,10 @@ ProjectSettings::ProjectSettings() {
extensions.push_back("gdshader");

GLOBAL_DEF("editor/run/main_run_args", "");
GLOBAL_DEF("editor/run/instance_1_run_args", "");
GLOBAL_DEF("editor/run/instance_2_run_args", "");
GLOBAL_DEF("editor/run/instance_3_run_args", "");
GLOBAL_DEF("editor/run/instance_4_run_args", "");
Comment on lines +1224 to +1227
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
GLOBAL_DEF("editor/run/instance_1_run_args", "");
GLOBAL_DEF("editor/run/instance_2_run_args", "");
GLOBAL_DEF("editor/run/instance_3_run_args", "");
GLOBAL_DEF("editor/run/instance_4_run_args", "");
GLOBAL_DEF("editor/run/instance_run_args", PackedStringArray());


GLOBAL_DEF("editor/script/search_in_file_extensions", extensions);
custom_prop_info["editor/script/search_in_file_extensions"] = PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions");
Expand Down
19 changes: 19 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -536,13 +536,32 @@
<member name="editor/node_naming/name_num_separator" type="int" setter="" getter="" default="0">
What to use to separate node name from number. This is mostly an editor setting.
</member>
<member name="editor/run/instance_1_run_args" type="String" setter="" getter="" default="&quot;&quot;">
The command-line arguments to use for instance 1 when running the project. This overrides the arguments set in [member editor/run/main_run_args].
For further details, see [member editor/run/main_run_args].
Instance 1 is the default instance. The Editor can be set to start multiple instances by using the "Run Multiple Instances" setting in the Debug menu.
</member>
<member name="editor/run/instance_2_run_args" type="String" setter="" getter="" default="&quot;&quot;">
The command-line arguments to use for instance 2 when running the project. This overrides the arguments set in [member editor/run/main_run_args].
For further details, see [member editor/run/main_run_args]. The Editor can be set to start multiple instances by using the "Run Multiple Instances" setting in the Debug menu.
</member>
<member name="editor/run/instance_3_run_args" type="String" setter="" getter="" default="&quot;&quot;">
The command-line arguments to use for instance 3 when running the project. This overrides the arguments set in [member editor/run/main_run_args].
For further details, see [member editor/run/main_run_args]. The Editor can be set to start multiple instances by using the "Run Multiple Instances" setting in the Debug menu.
</member>
<member name="editor/run/instance_4_run_args" type="String" setter="" getter="" default="&quot;&quot;">
The command-line arguments to use for instance 3 when running the project. This overrides the arguments set in [member editor/run/main_run_args].
For further details, see [member editor/run/main_run_args]. The Editor can be set to start multiple instances by using the "Run Multiple Instances" setting in the Debug menu.
</member>
<member name="editor/run/main_run_args" type="String" setter="" getter="" default="&quot;&quot;">
The command-line arguments to append to Godot's own command line when running the project. This doesn't affect the editor itself.
It is possible to make another executable run Godot by using the [code]%command%[/code] placeholder. The placeholder will be replaced with Godot's own command line. Program-specific arguments should be placed [i]before[/i] the placeholder, whereas Godot-specific arguments should be placed [i]after[/i] the placeholder.
For example, this can be used to force the project to run on the dedicated GPU in a NVIDIA Optimus system on Linux:
[codeblock]
prime-run %command%
[/codeblock]
Note that the settings [member editor/run/instance_1_run_args] to [member editor/run/instance_4_run_args] allow to override these arguments on a per-instance basis.
Instance 1 is the default instance. Instances 2 to 4 can be enabled by using the "Run Multiple Instances" setting in the Debug menu.
</member>
<member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="" default="PackedStringArray(&quot;gd&quot;, &quot;gdshader&quot;)">
Text-based file extensions to include in the script editor's "Find in Files" feature. You can add e.g. [code]tscn[/code] if you wish to also parse your scene files, especially if you use built-in scripts which are serialized in the scene files.
Expand Down
70 changes: 42 additions & 28 deletions editor/editor_run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,44 @@ Error EditorRun::run(const String &p_scene) {
args.push_back(p_scene);
}

String exec = OS::get_singleton()->get_executable_path();
int instances = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_instances", 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int instances = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_instances", 1);
int instances = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_instances", 1);
PackedStringArray instance_arg_setting = ProjectSettings::get_singleton()->get("editor/run/instance_run_args");

for (int instance_num = 1; instance_num <= instances; instance_num++) {
List<String> instance_args;
for (int i = 0; i < args.size(); i++) {
instance_args.push_back(args[i]);
}

String exec;
String instance_arg_setting = "editor/run/instance_" + itos(instance_num) + "_run_args";
String raw_custom_args = ProjectSettings::get_singleton()->get(instance_arg_setting);
Comment on lines +195 to +196
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like these should be in editor settings meta too (i.e. EditorSettings::get_singleton()->get_project_metadata("debug_options", instance_arg_setting)).

We don't really want to export them, they are debug/editor configurations, not something to store in the project settings.

I guess that raises the question of where to show those info, and maybe an hint to proceed towards an initial implementation of godotengine/godot-proposals#522 ?

Comment on lines +195 to +196
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
String instance_arg_setting = "editor/run/instance_" + itos(instance_num) + "_run_args";
String raw_custom_args = ProjectSettings::get_singleton()->get(instance_arg_setting);
String raw_custom_args;
if (instance_num >= instance_arg_setting.size()) {
raw_custom_args = instance_arg_setting[instance_num - 1];
}

if (raw_custom_args.is_empty()) {
// Fallback to main run args if no instance specific run args exist.
raw_custom_args = ProjectSettings::get_singleton()->get("editor/run/main_run_args");
}
parse_run_args(&exec, &instance_args, raw_custom_args);

printf("Running: %s", exec.utf8().get_data());
for (const String &E : instance_args) {
printf(" %s", E.utf8().get_data());
};
printf("\n");

OS::ProcessID pid = 0;
Error err = OS::get_singleton()->create_instance(instance_args, &pid);
ERR_FAIL_COND_V(err, err);
pids.push_back(pid);
}

status = STATUS_PLAY;
if (!p_scene.is_empty()) {
running_scene = p_scene;
}

return OK;
}

const String raw_custom_args = ProjectSettings::get_singleton()->get("editor/run/main_run_args");
void EditorRun::parse_run_args(String *exec_path, List<String> *args, const String raw_custom_args) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fits more the godot codebase:

Suggested change
void EditorRun::parse_run_args(String *exec_path, List<String> *args, const String raw_custom_args) {
void EditorRun::parse_run_args(String &r_exec_path, List<String> &r_args, const String p_raw_custom_args) {

*exec_path = OS::get_singleton()->get_executable_path();
if (!raw_custom_args.is_empty()) {
// Allow the user to specify a command to run, similar to Steam's launch options.
// In this case, Godot will no longer be run directly; it's up to the underlying command
Expand All @@ -202,53 +237,32 @@ Error EditorRun::run(const String &p_scene) {
// If nothing is placed before `%command%`, behave as if no placeholder was specified.
Vector<String> exec_args = raw_custom_args.substr(0, placeholder_pos).split(" ", false);
if (exec_args.size() >= 1) {
exec = exec_args[0];
*exec_path = exec_args[0];
exec_args.remove_at(0);

// Append the Godot executable name before we append executable arguments
// (since the order is reversed when using `push_front()`).
args.push_front(OS::get_singleton()->get_executable_path());
args->push_front(OS::get_singleton()->get_executable_path());
}

for (int i = exec_args.size() - 1; i >= 0; i--) {
// Iterate backwards as we're pushing items in the reverse order.
args.push_front(exec_args[i].replace(" ", "%20"));
args->push_front(exec_args[i].replace(" ", "%20"));
}

// Append Godot-specific custom arguments.
custom_args = raw_custom_args.substr(placeholder_pos + String("%command%").size()).split(" ", false);
for (int i = 0; i < custom_args.size(); i++) {
args.push_back(custom_args[i].replace(" ", "%20"));
args->push_back(custom_args[i].replace(" ", "%20"));
}
} else {
// Append Godot-specific custom arguments.
custom_args = raw_custom_args.split(" ", false);
for (int i = 0; i < custom_args.size(); i++) {
args.push_back(custom_args[i].replace(" ", "%20"));
args->push_back(custom_args[i].replace(" ", "%20"));
}
}
}

printf("Running: %s", exec.utf8().get_data());
for (const String &E : args) {
printf(" %s", E.utf8().get_data());
};
printf("\n");

int instances = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_instances", 1);
for (int i = 0; i < instances; i++) {
OS::ProcessID pid = 0;
Error err = OS::get_singleton()->create_instance(args, &pid);
ERR_FAIL_COND_V(err, err);
pids.push_back(pid);
}

status = STATUS_PLAY;
if (!p_scene.is_empty()) {
running_scene = p_scene;
}

return OK;
}

bool EditorRun::has_child_process(OS::ProcessID p_pid) const {
Expand Down
2 changes: 2 additions & 0 deletions editor/editor_run.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class EditorRun {
Status status;
String running_scene;

void parse_run_args(String *exec_path, List<String> *args, const String raw_custom_args);

public:
Status get_status() const;
String get_running_scene() const;
Expand Down