Are there any good examples of elixir/erlang game engines that utilize (atleast) one process per object / creature / NPC ?
As far as I know, there isn’t really a game engine made in Elixir. I’m assuming that you mean game server.
If that is the case, there’s a couple of resources that might be of interest to you.
X-Plane is using Elixir for their multiplayer service - They implemented the RakNet protocol in Elixir (which they open sourced). This might be really useful for starting a backend for your game, or maybe it’s overkill.
Ricardo García Vega wrote a fun ant farm using GenServers and Phoenix - This is not specifically a game service, but it illustrates what you are looking for.
If you are just playing around, you could go far with Phoenix Channels and Phoenix PubSub. Keep in mind that you need a client library for Channels. For example, if you are using Unity you’d need a C# library. Here’s an unofficial client.
Another option is to use LiveView and make a web-based game. Someone posted about that in the forum this week.
The X-plane podcast looks really nice. Thanks!
Yes, I meant game server, thanks for clarifying.
Good to know I’m not the only one unable to find popular game servers i(besides x-plane) n Elixir/Erlang. I find this really surprising. Literature states:
3.1 Elixir delivers good low latency.
3.2 Elixir degrades nicely under stress.
3.3 Elixir has hot code reloading.
3.4 Elixir has light weight processes.
3.5 Elixir has hot code reloading.
This seems like a match made in heaven for simulating massive worlds with lots of NPCs. The possible outcomes seem to be:
4a. These servers exist, but they’re top secret non-open source tech.
4b. None of the above 5 points matters, since the higher order bit is ‘is this game fun’
4c. Even in a technical sense, the hardest problem isn’t any of the 5 listed above.
I know of someone that is writing a server emulator for an MMORPG, and what I’ve seen so far is quite impressive. Low latency, seems to have good performance under stress tests, gracefully handles player dis/reconnects (every player is a process), and so on. Good stuff. It’s not public code though, but they share their progress in the Elixir discord server.
If the game required the server to simulate math heavy stuff like physics simulation(think of a shooter maybe?) then it may Elixir is not be the best, but then the BEAM is an excellent orchestrator, so you can always delegate to some better suited language if that’s worth it.
I also know GitHub - alfredbaudisch/Godello: Trello inspired kanban board made with the Godot Engine and GDScript, powered by an online real-time collaborative backend (Elixir and Phoenix Channels) (the link description is self explanatory, thanks Discourse ) and Gemuboi(GitHub - Tuxified/gemuboi: Mirror of Gemuboi: https://gitlab.com/Tuxified/gemuboi/). The former is a gameboy emulator using Scenic.
I think that until recently, most of the people that came to the BEAM were trying to solve issues with backend servers that were very hard to solve in other languages/platform, but the BEAM was built from the ground up to (I’m generalizing here, but so far the only erlang example I found that went in a different direction was Wings3D). The other feeling I have is that developing an online game by splitting it into two different (and probably radically different) languages may be too costly, as you can’t easily reuse client code to run simulations on the server.
Interesting, why one process per object/creature/NPC? Why not just one key in your gamestate datastore per object/creature/NPC? This makes me very curious about your game
There are some example Elixir and Phoenix-based games/game dev tools in the Awesome Elixir Gaming repo. Have a look, and feel free to send a pull request There is an entity framework in there, although I think it is more for entity-based game architecture and may not handle any game logic
My team has done some preliminary work on an Elixir game server/engine for 2D games, and it became apparent pretty quickly why there aren’t any open-source production-ready game servers or collision engines written in Elixir: there are simply so many robust open-source solutions written in other languages, from C++ & C# to Rust.
We haven’t run into any performance issues with 2d collision/physics math calculations in Elixir, the issue is more taking the time to re-invent the game/physics engine wheel in Elixir, especially when every example out there is OOP. We’ve toyed with just plugging in DimForge, but this project is a low-priority for our team at the moment.
Would be interesting to start a new Elixir physics engine just for the heck of it, there is some abandonware out there (e.g. Collidex) but nothing particularly robust
Suppose we have designers who are not great programmers. Suppose we want these designers to be able to script NPCs. Suppose one of these designers writes an infinite loop / really slow code.
In such an environment, if it’s one beam process per NPC, one particular NPC freezes / lags, but the server / other NPCs runs fine.
If it’s not one beam process per NPC, it is possible that the script of one NPC hogs an entire OS thread / process, which makes everything on the server look laggy.
I’ve build a “game engine” like that as a means to learn more about Elixir. It implements a Factorio style world, where every machine (chest, inserter, assembler) is a GenServer process. Each machine is completely independent, and communicates with machines around it by sending messages.
The general principle seems to work, and I’m now working on multi-node scaling support. If you’re interested, the code can be found here. Do keep in mind that this was never meant to be production code, just a learning exercise. It’s also my first medium sized Elixir app.
@janj: This sounds very similar to what I had in mind, with Factorio replaced with World of Warcraft / Ultima Online / Runescape / …
If you don’t mind sharing, I would love to hear more about what worked well / what did not / unexpected obstacles, etc … (regarding “one process per interactive thing”).
Elixir seems to be a very good fit when modelling Factorio-like machines. As the machines operate completely independently, they map to GenServer almost 1:1.
I think the most challenging are dealing with process discovery (basically: naming things), updating restart state of individual processes and keeping track of time. There might be some bias here as these are also the things I’ve been working on recently
Some background: my implementation splits the world into tiles, each tile is responsible for n * m cells. The tiles are needed to be able to distribute the application over multiple nodes and are implemented using GenServer. The machines (assemblers, inserters, etc.) occupy one or more cells, which are located in one or more tiles.
Machines (processes) need to know about each other’s existence, otherwise they can’t interact. Once they know about the other machines, they need to remember those processes. You can remember them by their PID, name (for example a
:via tuple) or location. You start running into problems when machines crash or get replaced by other, potentially non-compatible, machines. For now, I’m relying on Syn for naming the machines, but I’ve yet to deal with more complicated replace scenarios.
Updating restart state
When you start an entity in your world, you will probably do that using a supervisor. When that entity crashes, it gets restarted with it’s original state. The problem here is that the original state might no longer make sense for that entity in the current game. The implementation I have now splits the GenServer state into a ‘valuable’ and ‘non-valuable’ part, and relies on a custom supervisor to remember and use the valuable state when restarting a crashed process. It took me quite a while to figure out how to do this.
Keeping track of time
I think many (most?) game engines rely on ‘ticks’ (constant units of time) to keep everything in sync. When you use a lot of processes, this might no longer work. I don’t use ticks but rely on timers to control machines, which works because what I’m trying to model is not very complicated. Once you have a more elaborate system and a UI to update this becomes a challenge.