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 9, 2024
1 parent e65a237 commit 6bd1faf
Show file tree
Hide file tree
Showing 45 changed files with 1,399 additions and 118 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(bool p_enabled) {
embedded = p_enabled;
}

bool Engine::is_embedded() const {
return embedded;
}

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 = 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(bool p_enabled);
bool is_embedded() const;

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

void Engine::set_embedded(bool p_enabled) {
::Engine::get_singleton()->set_embedded(p_enabled);
}

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

String Engine::get_write_movie_path() const {
return ::Engine::get_singleton()->get_write_movie_path();
}
Expand Down Expand Up @@ -1941,6 +1949,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"), &Engine::is_embedded);

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

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

void set_embedded(bool p_enabled);
bool is_embedded() 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
9 changes: 7 additions & 2 deletions doc/classes/DisplayServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,12 @@
<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="FEATURE_WINDOW_HIDDEN" value="28" enum="Feature">
Display server supports hiding a window without destroying it. [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 Expand Up @@ -2110,8 +2116,7 @@
Window style is overridden, forcing sharp corners.
[b]Note:[/b] This flag is implemented only on Windows (11).
</constant>
<constant name="WINDOW_FLAG_MAX" value="9" enum="WindowFlags">
Max value of the [enum WindowFlags].
<constant name="WINDOW_FLAG_MAX" value="10" enum="WindowFlags">
</constant>
<constant name="WINDOW_EVENT_MOUSE_ENTER" value="0" enum="WindowEvent">
Sent when the mouse pointer enters the window.
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" 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
11 changes: 10 additions & 1 deletion doc/classes/Window.xml
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,11 @@
Emitted when the mouse cursor leaves the [Window]'s visible area, that is not occluded behind other [Control]s or windows, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not.
</description>
</signal>
<signal name="position_changed">
<description>
Emitted when the [Window] moved.
</description>
</signal>
<signal name="theme_changed">
<description>
Emitted when the [constant NOTIFICATION_THEME_CHANGED] notification is sent.
Expand Down Expand Up @@ -852,7 +857,11 @@
[b]Note:[/b] This flag has no effect in embedded windows.
[b]Note:[/b] This flag is implemented only on Windows (11).
</constant>
<constant name="FLAG_MAX" value="9" enum="Flags">
<constant name="FLAG_HIDDEN" value="9" enum="Flags">
Thie window is hidden but not destroyed.
[b]Note:[/b] This flag is implemented only on Windows and Linux (X11).
</constant>
<constant name="FLAG_MAX" value="10" enum="Flags">
Max value of the [enum Flags].
</constant>
<constant name="CONTENT_SCALE_MODE_DISABLED" value="0" enum="ContentScaleMode">
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 @@ -6697,6 +6699,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 @@ -787,6 +789,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
13 changes: 13 additions & 0 deletions editor/editor_run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/plugins/game_view_plugin.h"
#include "editor/run_instances_dialog.h"
#include "main/main.h"
#include "servers/display_server.h"

EditorRunInstanceStarting EditorRun::instance_starting_callback = nullptr;

EditorRun::Status EditorRun::get_status() const {
return status;
}
Expand Down Expand Up @@ -234,6 +237,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 @@ -286,6 +292,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:
static EditorRunInstanceStarting instance_starting_callback;

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
58 changes: 58 additions & 0 deletions editor/gui/editor_run_bar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,35 @@ void EditorRunBar::play_custom_scene(const String &p_custom) {
_run_scene(p_custom);
}

void EditorRunBar::restart() {
if (!is_playing()) {
return;
}

RunMode last_current_mode = current_mode;
String last_run_custom_filename = run_custom_filename;
String last_run_current_filename = run_current_filename;

stop_playing();

switch (last_current_mode) {
case RunMode::RUN_MAIN: {
_run_scene();
} break;
case RunMode::RUN_CUSTOM: {
play_custom_scene(last_run_custom_filename);
} break;
case RunMode::RUN_CURRENT: {
_run_scene(last_run_current_filename);
} break;
case RunMode::STOPPED: {
// Nothing to do.
} break;
}

current_mode = last_current_mode;
}

void EditorRunBar::stop_playing() {
if (editor_run.get_status() == EditorRun::STATUS_STOP) {
return;
Expand Down Expand Up @@ -344,6 +373,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 +389,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, "stack_dump")));
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
5 changes: 5 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 @@ -93,6 +96,7 @@ class EditorRunBar : public MarginContainer {
void play_main_scene(bool p_from_native = false);
void play_current_scene(bool p_reload = false);
void play_custom_scene(const String &p_custom);
void restart();

void stop_playing();
bool is_playing() const;
Expand All @@ -102,6 +106,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/AutoFocus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions editor/icons/EmbeddedProcess.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 6bd1faf

Please sign in to comment.