Skip to content

Instantly share code, notes, and snippets.

@ViteFalcon
Created February 11, 2018 18:59
Show Gist options
  • Save ViteFalcon/324f09eea0b216549dd84cde13271a9e to your computer and use it in GitHub Desktop.
Save ViteFalcon/324f09eea0b216549dd84cde13271a9e to your computer and use it in GitHub Desktop.
Godot: Multithreaded Programming

Problem

I wanted to start building the GUI parts of the game, but create it in such a way that I will have a network interface that I can later replace with the actual implementation.

What I did

I created a LoginClient that provides an interface to the login server functions. This client works in an asynchronous manner. Login requests gets queued, which then gets processed by a thread. The fun part was in the details, which I will explain here.

Threads in Godot

I feel this is one of the things that has been least documented in Godot's documentation and few people in Godot's Discord channel have knowledge of how this works.

The general code to create and run a method in a thread looks like this:

func _method_run_by_thread(unused_args):
    # NOTE: `unused_args` aren't used, but is required for this
    # method to be able to call by the thread
    print("Ran this method")

func run_method_on_a_thread():
    var thread = Thread.new()
    thread.start(self, "_method_run_by_thread")

A few things to note here:

  • unused_args are still required for the method that will be run by the thread
  • Thread.start() can accept args that can be passed on to the function that gets run by the Thread instance

Asynchronous communication

When a request is made via our network client, we want that communication to be asynchronous, i.e., non-blocking, so that the main thread where the graphics is renders the GUI still remains responsive.

There are a few ways to achieve this, the simplest being that the main thread qeueus the requests to the server, while a separate thread is dedicated to removing items from the queue and sending it to the server. Yet another thread listens for responses from the server.

Challenges in Godot

There is no thread-safe queue in Godot and so creating one was my first task.

One of the interesting features of GDScript that I used in that was the yield method. Particularly, the yield with signal.

yield method allows you to 'relinquish' the control back to the caller, which then can be resumed by the caller like so:

func method():
  print(1)
  yield()
  print(2)

func method2():
  var y = method()
  print(3)
  y.resume()

Here, the output will be 132, because method yielded to method2 after printing 1, method2 then prints 3 and resumes method, which goes on to print 2.

When you yield to a signal from an object, the control gets passed back to the caller and the caller can continue with its operation, while the yielded method resumes once the signal has been triggered. I used this yield-with-signal mechanism to resume a push() when an element gets popped out, the the circular buffer is full. This allows for the circular buffer to not be strictly of a fixed size. You will still need to set a size for the queue such that the max size doesn't breach every time to cause yields to pile up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment