Skip to content

Commit

Permalink
fix(InteractiveProcess): update to use new Pty node
Browse files Browse the repository at this point in the history
  • Loading branch information
ShadowApex committed Sep 30, 2024
1 parent 5a96942 commit 864954a
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 17 deletions.
44 changes: 35 additions & 9 deletions core/systems/launcher/interactive_process.gd
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
extends Resource
class_name InteractiveProcess

# DEPRECATED: Use the [Pty] node instead

## Class for starting an interacting with a process through a psuedo terminal
##
## Starts an interactive session
Expand All @@ -9,6 +11,9 @@ var pty: Pty
var cmd: String
var args: PackedStringArray = []
var pid: int
var registry := load("res://core/systems/resource/resource_registry.tres") as ResourceRegistry
var lines_mutex := Mutex.new()
var lines_buffer := PackedStringArray()
var logger := Log.get_logger("InteractiveProcess", Log.LEVEL.INFO)


Expand All @@ -20,45 +25,66 @@ func _init(command: String, cmd_args: PackedStringArray = []) -> void:
## Start the interactive process
# TODO: Fixme
func start() -> int:
# Create a new PTY instance and start the process
self.pty = Pty.new()
self.pty.exec(self.cmd, self.args)
self.pty.line_written.connect(_on_line_written)

# The new [Pty] is a node, so it must be added to the scene tree
self.registry.add_child(self.pty)

return OK


func _on_line_written(line: String):
logger.info("PTY:", line)
self.lines_mutex.lock()
self.lines_buffer.append(line)
self.lines_mutex.unlock()


## Send the given input to the running process
func send(input: String) -> void:
if not pty:
return
logger.debug("Writing input: " + input)
if pty.write(input.to_utf8_buffer()) < 0:
logger.info("Writing input: " + input)
if pty.write_line(input) < 0:
logger.debug("Unable to write to PTY")


## Read from the stdout of the running process
#TODO: Fixme
func read(chunk_size: int = 1024) -> String:
func read(_chunk_size: int = 1024) -> String:
if not pty:
logger.debug("Unable to read from closed PTY")
return ""

# Keep reading from the process until the buffer is empty
var output := ""
self.lines_mutex.lock()
var output := "\n".join(self.lines_buffer)
self.lines_buffer = PackedStringArray()
self.lines_mutex.unlock()

return output


## Stop the given process
func stop() -> void:
logger.debug("Stopping pid: " + str(pid))
OS.kill(pid)
pty = null
logger.debug("Stopping pid: " + str(self.pid))
self.pty.kill()
self.registry.remove_child(self.pty)
self.pty = null


## Returns whether or not the interactive process is still running
func is_running() -> bool:
return OS.is_process_running(pid)
if not pty:
return false
return pty.running


# TODO: Fixme
func output_to_log_file(log_file: FileAccess, chunk_size: int = 1024) -> int:
func output_to_log_file(log_file: FileAccess, _chunk_size: int = 1024) -> int:
if not log_file:
logger.warn("Unable to log output. Log file has not been opened.")
return ERR_FILE_CANT_OPEN
Expand Down
31 changes: 23 additions & 8 deletions extensions/core/src/resource/resource_processor.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
use godot::prelude::*;
use godot::{obj::WithBaseField, prelude::*};

use super::resource_registry::ResourceRegistry;

/// The [ResourceProcessor] allows Godot [Resource] objects to run a process
/// function every frame. Resources must register with the [ResourceRegistry]
/// associated with this [ResourceProcessor] in order to be processed from
/// the scene tree.
#[derive(GodotClass)]
#[class(init, base=Node)]
pub struct ResourceProcessor {
base: Base<Node>,
#[export]
registry: Gd<ResourceRegistry>,
initialized: bool,
}

#[godot_api]
impl INode for ResourceProcessor {
//fn init(base: Base<Self::Base>) -> Self {
// // Load the registry resource
// let mut resource_loader = ResourceLoader::singleton();
// if let Some(res) = resource_loader.load(res_path.clone().into()) {}
fn process(&mut self, delta: f64) {
if !self.initialized {
// Add any child nodes from the registry
let children = self.registry.bind().get_children();
for child in children.iter_shared() {
self.base_mut().add_child(child);
}

// Self { base, registry: () }
//}
// Add any future children that get added to the registry
let ptr = self.to_gd();
let method = Callable::from_object_method(&ptr, "add_child");
self.registry.connect("child_added".into(), method);

fn process(&mut self, delta: f64) {
// Remove any children that get removed from the registry
let method = Callable::from_object_method(&ptr, "remove_child");
self.registry.connect("child_removed".into(), method);

self.initialized = true;
}
self.registry.bind_mut().process(delta);
}
}
32 changes: 32 additions & 0 deletions extensions/core/src/resource/resource_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ use godot::prelude::*;
pub struct ResourceRegistry {
base: Base<Resource>,
resources: Array<Gd<Resource>>,
child_nodes: Array<Gd<Node>>,
}

#[godot_api]
impl ResourceRegistry {
#[signal]
fn child_added(child: Gd<Node>);
#[signal]
fn child_removed(child: Gd<Node>);

/// Register the given resource with the registry. The given resource will have its "process()" method called every frame by a [ResourceProcessor].
#[func]
pub fn register(&mut self, resource: Gd<Resource>) {
if !resource.has_method("process".into()) {
godot_error!(
Expand All @@ -24,14 +31,39 @@ impl ResourceRegistry {
}

/// Unregister the given resource from the registry.
#[func]
pub fn unregister(&mut self, resource: Gd<Resource>) {
self.resources.erase(&resource);
}

/// Calls the "process()" method on all registered resources. This should be called from a [Node] in the scene tree like the [ResourceProcessor].
#[func]
pub fn process(&mut self, delta: f64) {
for mut resource in self.resources.iter_shared() {
resource.call("process".into(), &[delta.to_variant()]);
}
}

/// Adds the given node to the [ResourceProcessor] node associated with this registry.
/// This provides a way for resources to add nodes into the scene tree.
#[func]
pub fn add_child(&mut self, child: Gd<Node>) {
self.child_nodes.push(child.clone());
self.base_mut()
.emit_signal("child_added".into(), &[child.to_variant()]);
}

/// Removes the given node from the scene tree
#[func]
pub fn remove_child(&mut self, child: Gd<Node>) {
self.child_nodes.erase(&child);
self.base_mut()
.emit_signal("child_removed".into(), &[child.to_variant()]);
}

/// Returns a list of all nodes that should be added as children to a [ResourceProcessor]
#[func]
pub fn get_children(&self) -> Array<Gd<Node>> {
self.child_nodes.clone()
}
}

0 comments on commit 864954a

Please sign in to comment.