From e02daac0ffba3e264cf8363d2c198dec6ca084ff Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Mon, 2 Mar 2026 02:00:30 -0600 Subject: [PATCH] Created Plugin Custom Runner Created plugin Custom Runner, but is not specifically needed for what it was originally created for. Still checking it in, since it might be useful later. --- addons/custom_runner/plugin.cfg | 7 ++ addons/custom_runner/plugin.gd | 151 +++++++++++++++++++++++++++++ addons/custom_runner/plugin.gd.uid | 1 + 3 files changed, 159 insertions(+) create mode 100644 addons/custom_runner/plugin.cfg create mode 100644 addons/custom_runner/plugin.gd create mode 100644 addons/custom_runner/plugin.gd.uid diff --git a/addons/custom_runner/plugin.cfg b/addons/custom_runner/plugin.cfg new file mode 100644 index 00000000..1df3bc8e --- /dev/null +++ b/addons/custom_runner/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Custom Runner" +description="A plugin that runs the Godot Engine with a specific tool, such as obs-gamecapture, to capture the game window." +author="Mario Steele" +version="0.1" +script="plugin.gd" diff --git a/addons/custom_runner/plugin.gd b/addons/custom_runner/plugin.gd new file mode 100644 index 00000000..3a54ce04 --- /dev/null +++ b/addons/custom_runner/plugin.gd @@ -0,0 +1,151 @@ +@tool +extends EditorPlugin + +var _runner_btn: RunnerButton +var _play_icon: Texture2D +var _stop_icon: Texture2D + +func _enter_tree() -> void: + ProjectSettings.set("application/run/custom_runner", "") + ProjectSettings.add_property_info({ + "name": "application/run/custom_runner", + "type": TYPE_STRING, + "hint": PROPERTY_HINT_GLOBAL_FILE, + "hint_string": "" + }) + _runner_btn = RunnerButton.new() + add_control_to_container(EditorPlugin.CONTAINER_TOOLBAR, _runner_btn) + + var cnt := _runner_btn.get_parent() + cnt.move_child(_runner_btn, cnt.get_child_count() - 3) + + +func _exit_tree() -> void: + remove_control_from_container(EditorPlugin.CONTAINER_TOOLBAR, _runner_btn) + _runner_btn.queue_free() + _runner_btn = null + + +class RunnerButton: + extends Button + + var _play: Texture2D + var _stop: Texture2D + var _running_pid: int = -1 + var _io: FileAccess = null + var _err: FileAccess = null + + func _init() -> void: + _play = EditorInterface.get_editor_theme().get_icon(&"Play", &"EditorIcons") + _stop = EditorInterface.get_editor_theme().get_icon(&"Stop", &"EditorIcons") + shortcut = Shortcut.new() + var key := InputEventKey.new() + key.keycode = KEY_F5 + key.shift_pressed = true + shortcut.events = [key] + modulate = Color.GREEN + var esb := StyleBoxEmpty.new() + add_theme_stylebox_override(&"normal", esb) + + func _ready() -> void: + icon = _play + + func _process(_d: float) -> void: + if _running_pid == -1: return + if not OS.is_process_running(_running_pid): + _running_pid = -1 + icon = _play + modulate = Color.GREEN + + func _pressed() -> void: + if _running_pid != -1: + OS.kill(_running_pid) + _running_pid = -1 + icon = _play + modulate = Color.GREEN + return + + var runner := ProjectSettings.get_setting("application/run/custom_runner", "") + if runner == "": + EditorInterface.play_main_scene() + return + + if not FileAccess.file_exists(runner): + EditorInterface.get_editor_toaster().push_toast("Custom runner does not exist!", + EditorToaster.SEVERITY_ERROR, + "Provide the full path to the custom runner!") + return + + var godot = OS.get_executable_path() + var project = ProjectSettings.globalize_path("res://").rstrip("/") + var host: String = EditorInterface.get_editor_settings().get_setting("network/debug/remote_host") + var port: int = EditorInterface.get_editor_settings().get_setting("network/debug/remote_port") + var pid: int = OS.get_process_id() + var main_scene: String = ProjectSettings.get_setting("application/run/main_scene") + var run_arguments := [ + godot, + "--path", project, + "-d" + #"--remote-debug", "tcp://%s:%s" % [host, port], + #"--editor-pid", "%s" % pid, + #"--position", "320,-24", + #"--scene", main_scene + ] + # --remote-debug tcp://127.0.0.1:6007 --editor-pid 3480813 --position 320,-24 --scene uid://2t2hslaaewee + print("Executing project with the following Command Line: %s %s" % [runner, run_arguments]) + + + #_running_pid = OS.create_process(runner, run_arguments) + var res := OS.execute_with_pipe(runner, run_arguments, false) + + if res.is_empty(): + EditorInterface.get_editor_toaster().push_toast("Failed to Run custom runner.",EditorToaster.SEVERITY_ERROR) + + _running_pid = res.pid + _io = res.stdio + _err = res.stderr + + icon = _stop + modulate = Color.RED + _monitor_input_output() + + func _monitor_input_output() -> void: + var output: PackedByteArray = [] + var err: PackedByteArray = [] + + while OS.is_process_running(_running_pid): + await get_tree().process_frame + + var res := _read_io(_io, output) + if res != "": + print_rich(res) + + res = _read_io(_err, err) + if res != "": + print_rich(res) + + await get_tree().process_frame + var str := _read_io(_io, output) + if str != "": + print(str) + + str = _read_io(_err, err) + if str != "": + print(str) + _io.close() + _err.close() + _io = null + _err = null + + func _read_io(io: FileAccess, buffer: PackedByteArray) -> String: + if io.get_position() < io.get_length(): + buffer.append_array(io.get_buffer(io.get_length() - io.get_position())) + var nl = buffer.rfind(10) + if nl == -1: + return "" + + var part := buffer.slice(0,nl) + var leftover := buffer.slice(nl+1) + buffer.clear() + buffer.append_array(leftover) + return part.get_string_from_utf8() diff --git a/addons/custom_runner/plugin.gd.uid b/addons/custom_runner/plugin.gd.uid new file mode 100644 index 00000000..6f22a159 --- /dev/null +++ b/addons/custom_runner/plugin.gd.uid @@ -0,0 +1 @@ +uid://bsdgmd1nimt6n