<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jean Ekoé KOUSSAWO</title>
    <description>The latest articles on DEV Community by Jean Ekoé KOUSSAWO (@jeankouss).</description>
    <link>https://dev.to/jeankouss</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1707821%2Fdfd8c134-871c-4f06-b15b-fead274de08a.png</url>
      <title>DEV Community: Jean Ekoé KOUSSAWO</title>
      <link>https://dev.to/jeankouss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeankouss"/>
    <language>en</language>
    <item>
      <title>Seamless Inter-Process Communication with Godot's `execute_with_pipe`.</title>
      <dc:creator>Jean Ekoé KOUSSAWO</dc:creator>
      <pubDate>Sat, 28 Dec 2024 11:21:25 +0000</pubDate>
      <link>https://dev.to/jeankouss/seamless-inter-process-communication-with-godots-executewithpipe-537i</link>
      <guid>https://dev.to/jeankouss/seamless-inter-process-communication-with-godots-executewithpipe-537i</guid>
      <description>&lt;p&gt;Recently, I encountered some issues in Godot while attempting to communicate with another program through its stdin and stdout. After some days of research and testing various approaches, I finally understand what I believe to be the proper way to use &lt;code&gt;OS.execute_with_pipe&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some context
&lt;/h2&gt;

&lt;p&gt;Why might you need to use &lt;code&gt;OS.execute_with_pipe&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OS.execute_with_pipe&lt;/code&gt; lets you run external programs asynchronously and exchange data with them through input (stdin) and output (stdout) streams. &lt;a href="https://docs.godotengine.org/fr/4.x/classes/class_os.html#class-os-method-execute-with-pipe" rel="noopener noreferrer"&gt;See the official docs for more details&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You might need this when working with :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command-line tools&lt;/strong&gt; : Pinging a server, for instance,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI models or scripts&lt;/strong&gt; : Sending game state to an AI and receiving actions,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External program&lt;/strong&gt; : Communicating with a custom application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my case, I encountered this while developing a game where players can train and use their own AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with stdin and stdout
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;OS.execute_with_pipe&lt;/code&gt; returns a dictionary containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"stdio": A &lt;code&gt;FileAccess&lt;/code&gt; object for interacting with the process's stdin and stdout pipes (read/write),&lt;/li&gt;
&lt;li&gt;"stderr": A &lt;code&gt;FileAccess&lt;/code&gt; object for accessing the process's stderr pipe (read-only),&lt;/li&gt;
&lt;li&gt;"pid": Process ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The one that interests us is "stdio" &lt;code&gt;FileAccess&lt;/code&gt;object. We can send data to the external process by writing to that file, and get input from the process by reading it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending input to external program
&lt;/h3&gt;

&lt;p&gt;Sending input is straightforward. You write data to the "stdio" object, ensuring that the input ends with a newline character (\n). This is analogous to pressing &lt;em&gt;Enter&lt;/em&gt; on your keyboard. The easiest way to do this is with write_line():&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gdscript"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example: sending input to the external process&lt;/span&gt;
&lt;span class="n"&gt;process_io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"This is my input"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Automatically adds \n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The external program will pause if it needs input and none is available. Inputs are handled in the order they are sent : First In, First Out (FIFO).&lt;/p&gt;

&lt;h3&gt;
  
  
  Receiving outputs from the external program
&lt;/h3&gt;

&lt;p&gt;Retrieving output from the external program requires reading from "stdio".&lt;/p&gt;

&lt;p&gt;Here was the pain for me. &lt;/p&gt;

&lt;p&gt;Here's the catch: &lt;strong&gt;Read the &lt;code&gt;stdout&lt;/code&gt; in a dedicated thread&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you try to read &lt;code&gt;stdout&lt;/code&gt;, if data is available in stdout, it’s retrieved immediately. However, if stdout is empty, the reading thread will pause until data arrives. Attempting this in the main thread risks freezing your application if the external process takes time to produce output.&lt;/p&gt;

&lt;p&gt;Let me illustrate this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/JeanKouss/execute_with_pipe_using" rel="noopener noreferrer"&gt;Here is a link to the project&lt;/a&gt; I'll use for this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reading from main thread&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Were are going to look at what is happening in &lt;code&gt;main_thread_reading/MainTreadReading.tscn&lt;/code&gt; scene. The key element here is the &lt;code&gt;main_thread_reading/MainTreadReading.gd&lt;/code&gt; script attached to the scene's root node. Additionally, the &lt;code&gt;TextureRect&lt;/code&gt; node helps indicate if Godot is running smoothly: if it is, the Godot icon will fade in and out seamlessly.&lt;/p&gt;

&lt;p&gt;Here is the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gdscript"&gt;&lt;code&gt;&lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;process_io&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FileAccess&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;null&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;_ready&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_with_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"cmd.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/c'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ping 8.8.8.8 -t'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;process_io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stdio'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;process_io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_line&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script executes a continuous ping request to 8.8.8.8 and prints the output line by line.&lt;/p&gt;

&lt;p&gt;When you run the scene, you’ll notice that Godot occasionally freezes. This happens because the engine waits for output to be printed to the console, demonstrating why reading from &lt;code&gt;stdout&lt;/code&gt; in the main thread can disrupt smooth execution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A better way : reading from dedicated thread&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's look at the content of &lt;code&gt;dedicated_thread_reading/DedicatedThreadReading.gd&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gdscript"&gt;&lt;code&gt;&lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;process_io&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FileAccess&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;null&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;_ready&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_with_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"cmd.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/c'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ping 8.8.8.8 -t'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;process_io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stdio'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_process_output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;read_process_output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;process_io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;process_io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;OK&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;process_io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_line&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this version, we no longer read the output from the main thread. Instead, the output is read from a dedicated thread.&lt;/p&gt;

&lt;p&gt;When you run the &lt;code&gt;dedicated_thread_reading/DedicatedThreadReading.tscn&lt;/code&gt; scene, you’ll notice that Godot runs smoothly while continuously printing the ping request output to the console as it becomes available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this article has clarified how to use &lt;code&gt;execute_with_pipe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before wrapping up, it’s worth noting that using threads in Godot can introduce its own challenges, particularly when accessing shared data or objects across multiple threads. To handle these situations safely, I recommend reading about &lt;a href="https://docs.godotengine.org/en/stable/tutorials/performance/using_multiple_threads.html#mutexes" rel="noopener noreferrer"&gt;Mutexes and Semaphores in the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and have a great day!&lt;/p&gt;

</description>
      <category>godot</category>
      <category>gdscript</category>
      <category>ipc</category>
      <category>io</category>
    </item>
  </channel>
</rss>
