Skip to content

Commit

Permalink
Embedding game process in editor
Browse files Browse the repository at this point in the history
  • Loading branch information
Hilderin committed Nov 26, 2024
1 parent 98ddec4 commit 3ee8e5a
Show file tree
Hide file tree
Showing 44 changed files with 1,731 additions and 136 deletions.
8 changes: 8 additions & 0 deletions core/config/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,14 @@ void Engine::set_freeze_time_scale(bool p_frozen) {
freeze_time_scale = p_frozen;
}

void Engine::set_embedded_in_editor(bool p_enabled) {
embedded_in_editor = p_enabled;
}

bool Engine::is_embedded_in_editor() const {
return embedded_in_editor;
}

Engine::Engine() {
singleton = this;
}
Expand Down
3 changes: 3 additions & 0 deletions core/config/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class Engine {
bool editor_hint = false;
bool project_manager_hint = false;
bool extension_reloading = false;
bool embedded_in_editor = false;

bool _print_header = true;

Expand Down Expand Up @@ -201,6 +202,8 @@ class Engine {
bool notify_frame_server_synced();

void set_freeze_time_scale(bool p_frozen);
void set_embedded_in_editor(bool p_enabled);
bool is_embedded_in_editor() const;

Engine();
virtual ~Engine();
Expand Down
5 changes: 5 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1897,6 +1897,10 @@ bool Engine::is_editor_hint() const {
return ::Engine::get_singleton()->is_editor_hint();
}

bool Engine::is_embedded_in_editor() const {
return ::Engine::get_singleton()->is_embedded_in_editor();
}

String Engine::get_write_movie_path() const {
return ::Engine::get_singleton()->get_write_movie_path();
}
Expand Down Expand Up @@ -1974,6 +1978,7 @@ void Engine::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_script_language", "index"), &Engine::get_script_language);

ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint);
ClassDB::bind_method(D_METHOD("is_embedded_in_editor"), &Engine::is_embedded_in_editor);

ClassDB::bind_method(D_METHOD("get_write_movie_path"), &Engine::get_write_movie_path);

Expand Down
2 changes: 2 additions & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,8 @@ class Engine : public Object {
void set_editor_hint(bool p_enabled);
bool is_editor_hint() const;

bool is_embedded_in_editor() const;

// `set_write_movie_path()` is not exposed to the scripting API as changing it at run-time has no effect.
String get_write_movie_path() const;

Expand Down
2 changes: 2 additions & 0 deletions core/os/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ bool OS::has_feature(const String &p_feature) {
return _in_editor;
} else if (p_feature == "editor_runtime") {
return !_in_editor;
} else if (p_feature == "embedded_in_editor") {
return _embedded_in_editor;
}
#else
if (p_feature == "template") {
Expand Down
1 change: 1 addition & 0 deletions core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class OS {
bool _stderr_enabled = true;
bool _writing_movie = false;
bool _in_editor = false;
bool _embedded_in_editor = false;

CompositeLogger *_logger = nullptr;

Expand Down
3 changes: 3 additions & 0 deletions doc/classes/DisplayServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,9 @@
<constant name="FEATURE_NATIVE_DIALOG_FILE_EXTRA" value="26" enum="Feature">
The display server supports all features of [constant FEATURE_NATIVE_DIALOG_FILE], with the added functionality of Options and native dialog file access to [code]res://[/code] and [code]user://[/code] paths. See [method file_dialog_show] and [method file_dialog_with_options_show]. [b]Windows, macOS, Linux (X11/Wayland)[/b]
</constant>
<constant name="FEATURE_WINDOW_EMBEDDING" value="27" enum="Feature">
Display server supports embedding a window from another process. [b]Windows, Linux (X11)[/b]
</constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
Makes the mouse cursor visible if it is hidden.
</constant>
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/Engine.xml
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@
[b]Note:[/b] To detect whether the script is running on an editor [i]build[/i] (such as when pressing [kbd]F5[/kbd]), use [method OS.has_feature] with the [code]"editor"[/code] argument instead. [code]OS.has_feature("editor")[/code] evaluate to [code]true[/code] both when the script is running in the editor and when running the project from the editor, but returns [code]false[/code] when run from an exported project.
</description>
</method>
<method name="is_embedded_in_editor" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the game is running embedded in the editor, otherwise returns [code]false[/code]. This is useful to prevent attempting to update window mode or window flags that are not supported when running embedded in the editor.
</description>
</method>
<method name="is_in_physics_frame" qualifiers="const">
<return type="bool" />
<description>
Expand Down
8 changes: 7 additions & 1 deletion editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,9 @@ void EditorNode::_notification(int p_what) {
}

// Set a low FPS cap to decrease CPU/GPU usage while the editor is unfocused.
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/unfocused_low_processor_mode_sleep_usec")));
if (unfocused_low_processor_usage_mode_enabled) {
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/unfocused_low_processor_mode_sleep_usec")));
}
} break;

case NOTIFICATION_WM_ABOUT: {
Expand Down Expand Up @@ -6713,6 +6715,10 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p
return eta.exitcode;
}

void EditorNode::set_unfocused_low_processor_usage_mode_enabled(bool p_enabled) {
unfocused_low_processor_usage_mode_enabled = p_enabled;
}

EditorNode::EditorNode() {
DEV_ASSERT(!singleton);
singleton = this;
Expand Down
4 changes: 4 additions & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ class EditorNode : public Node {

bool was_window_windowed_last = false;

bool unfocused_low_processor_usage_mode_enabled = true;

static EditorBuildCallback build_callbacks[MAX_BUILD_CALLBACKS];
static EditorPluginInitializeCallback plugin_init_callbacks[MAX_INIT_CALLBACKS];
static int build_callback_count;
Expand Down Expand Up @@ -788,6 +790,8 @@ class EditorNode : public Node {
HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node, bool p_node_references_only);
HashMap<StringName, Variant> get_modified_properties_reference_to_nodes(Node *p_node, List<Node *> &p_nodes_referenced_by);

void set_unfocused_low_processor_usage_mode_enabled(bool p_enabled);

struct AdditiveNodeEntry {
Node *node = nullptr;
NodePath parent;
Expand Down
10 changes: 10 additions & 0 deletions editor/editor_run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
List<String> instance_args(args);
RunInstancesDialog::get_singleton()->get_argument_list_for_instance(i, instance_args);
RunInstancesDialog::get_singleton()->apply_custom_features(i);
if (instance_starting_callback) {
instance_starting_callback(i, instance_args);
}

if (OS::get_singleton()->is_stdout_verbose()) {
print_line(vformat("Running: %s", exec));
Expand Down Expand Up @@ -281,6 +284,13 @@ void EditorRun::stop() {
running_scene = "";
}

OS::ProcessID EditorRun::get_current_process() const {
if (pids.is_empty()) {
return 0;
}
return pids.get(0);
}

EditorRun::EditorRun() {
status = STATUS_STOP;
running_scene = "";
Expand Down
6 changes: 6 additions & 0 deletions editor/editor_run.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
#define EDITOR_RUN_H

#include "core/os/os.h"
#include "servers/display_server.h"

typedef void (*EditorRunInstanceStarting)(int p_index, List<String> &r_arguments);

class EditorRun {
public:
Expand All @@ -48,6 +51,8 @@ class EditorRun {
String running_scene;

public:
inline static EditorRunInstanceStarting instance_starting_callback = nullptr;

Status get_status() const;
String get_running_scene() const;

Expand All @@ -58,6 +63,7 @@ class EditorRun {
void stop_child_process(OS::ProcessID p_pid);
bool has_child_process(OS::ProcessID p_pid) const;
int get_child_process_count() const { return pids.size(); }
OS::ProcessID get_current_process() const;

EditorRun();
};
Expand Down
29 changes: 29 additions & 0 deletions editor/gui/editor_run_bar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,10 @@ void EditorRunBar::stop_child_process(OS::ProcessID p_pid) {
}
}

OS::ProcessID EditorRunBar::get_current_process() const {
return editor_run.get_current_process();
}

void EditorRunBar::set_movie_maker_enabled(bool p_enabled) {
write_movie_button->set_pressed(p_enabled);
}
Expand All @@ -356,14 +360,39 @@ HBoxContainer *EditorRunBar::get_buttons_container() {
return main_hbox;
}

void EditorRunBar::_instance_starting(int p_idx, List<String> &r_arguments) {
singleton->_instance_starting_internal(p_idx, r_arguments);
}

void EditorRunBar::_instance_starting_internal(int p_index, List<String> &r_arguments) {
Array arguments;

// We need to convert the an Array so it can be passed as parameter in a signal.
for (const String &arg : r_arguments) {
arguments.push_back(arg);
}

emit_signal(SNAME("instance_starting"), p_index, arguments);

// Copy back to a List.
r_arguments.clear();
for (const Variant &arg : arguments) {
r_arguments.push_back(arg);
}
}

void EditorRunBar::_bind_methods() {
ADD_SIGNAL(MethodInfo("play_pressed"));
ADD_SIGNAL(MethodInfo("instance_starting", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::ARRAY, "arguments")));
ADD_SIGNAL(MethodInfo("stop_pressed"));
}

EditorRunBar::EditorRunBar() {
singleton = this;

// Callback from EditorRun to propagate the instance_starting signal.
editor_run.instance_starting_callback = _instance_starting;

main_panel = memnew(PanelContainer);
add_child(main_panel);

Expand Down
4 changes: 4 additions & 0 deletions editor/gui/editor_run_bar.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class EditorRunBar : public MarginContainer {
void _run_scene(const String &p_scene_path = "");
void _run_native(const Ref<EditorExportPreset> &p_preset);

static void _instance_starting(int p_idx, List<String> &r_arguments);
void _instance_starting_internal(int p_index, List<String> &r_arguments);

protected:
void _notification(int p_what);
static void _bind_methods();
Expand All @@ -102,6 +105,7 @@ class EditorRunBar : public MarginContainer {

OS::ProcessID has_child_process(OS::ProcessID p_pid) const;
void stop_child_process(OS::ProcessID p_pid);
OS::ProcessID get_current_process() const;

void set_movie_maker_enabled(bool p_enabled);
bool is_movie_maker_enabled() const;
Expand Down
10 changes: 10 additions & 0 deletions editor/gui/editor_scene_tabs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@

#include "editor_scene_tabs.h"

#include "editor/editor_main_screen.h"
#include "editor/editor_node.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_run_bar.h"
#include "editor/inspector_dock.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
Expand Down Expand Up @@ -90,6 +92,14 @@ void EditorSceneTabs::_scene_tab_hovered(int p_tab) {
if (!bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover"))) {
return;
}

// Currently the tab previews are displayed under the running game process when embed.
// Right now, the easiest technique to fix that is to prevent displaying the tab preview
// when the user is in the Game View.
if (EditorNode::get_singleton()->get_editor_main_screen()->get_selected_index() == EditorMainScreen::EDITOR_GAME && EditorRunBar::get_singleton()->is_playing()) {
return;
}

int current_tab = scene_tabs->get_current_tab();

if (p_tab == current_tab || p_tab < 0) {
Expand Down
1 change: 1 addition & 0 deletions editor/icons/KeepAspect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 3ee8e5a

Please sign in to comment.