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, with a real-time collaborative backend (Elixir and Phoenix Channels) and a local backend for offline usage (Godot Custom Resources) (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.
Process discovery
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.