I have only just started looking into Nakama and I like very much what I have seen so far!
I have two questions about designing match loop logic: the documentation says for all three server-framework options that multithreading is not supported (and in the case of Go that goroutines are discouraged).
What is the best practice for longer running tasks in a server-authoritive environment? For example when npc pathfinding takes up most of the time for a single tick. Is there a way to carry such computations over multiple ticks (similar to Coroutines in Unity)?
In case of multiple active match-loops, do they share a single thread which executes them, or is every match loop in its own thread?
Any input on these issues would be greatly appreciated!
@yrucrem This is an interesting question, and I’ll address your points in reverse as that’s easier:
For point (2) every match handler, there is a dedicated goroutine (think green thread rather than OS thread) that drives it. Each of these goroutine is responsible for only one match at a time, they are scheduled independently, and do not observe when other matches may or may not be executing their loops.
This is intentional, and lets you focus on writing the match handler as if only one would ever execute. There’s no need to worry how resources like CPU time would be shared, it’s Nakama’s responsibility to make that work smoothly.
Point (1) is more complex, and largely depends on what work needs to be done. Generally we recommend avoiding goroutines as a pattern inside match handlers but there are of course exceptions. Expensive NPC pathfinding might be a good example.
To handle this type of work you will want to use the Go runtime, where you could use a channel and a goroutine pattern:
Create a channel that’s managed by the match handler and kept in the match state. Avoid channels shared between different matches.
When expensive work is needed create a goroutine to do it.
When the goroutine completes its work, put the result on the channel.
In your match loop function, check if there is any work being handed back via the channel. If the channel is empty just keep going with the rest of the match loop. If there is something on the channel read and use it however you need, then keep going with the rest of the match loop anyway.
Take care when writing code like this to avoid accidental shared access to resources not safe for concurrent use!