[HN Gopher] Show HN: Lyceum - An MMO game built with Zig and Erlang
       ___________________________________________________________________
        
       Show HN: Lyceum - An MMO game built with Zig and Erlang
        
       Hey HN, this is a small project myself and closer friends have been
       building on our free time (https://github.com/Dr-Nekoma/lyceum), it
       finally reached 0.1.0, we are open to feedback!  The original idea
       was to experiment with Zig + Raylib, eventually we wrote a small
       server in Erlang as well. We started by first interacting with
       Erlang via its C bindings, but this eventually led to some of us to
       prototyping our own tooling to better integrate Zig types with
       Erlang, we called such tool "zerl" as its avaliable here
       https://github.com/dont-rely-on-nulls/zerl.  Most of the developers
       are NixOS users, so the tooling heavily relies on Nix as well,
       including a Postgres running our devshell as well.
        
       Author : fluidwizard
       Score  : 125 points
       Date   : 2024-11-05 16:45 UTC (6 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | fluidwizard wrote:
       | Regarding Zerl, my friend just presented about it in Functional
       | Programming Sweden:
       | https://www.youtube.com/watch?v=5Cuv0WnbZtk&t=795s.
        
       | lionkor wrote:
       | > the client written in Zig (superchanged with raylib and Zerl).
       | 
       | What does "supercharged" mean here? I'd guess raylib simply does
       | _all_ the rendering and input handling, what is it
       | "supercharging" here?
        
         | meheleventyone wrote:
         | It's just a superlative to give praise to the two libraries
         | they are using to develop their game.
        
         | xyzzy4747 wrote:
         | It just means they like raylib and Zerl and used them.
        
       | lovegrenoble wrote:
       | Will the game evolve further, and what is its future?
        
         | DuLR10 wrote:
         | The main 3D part of the game is now on pause. We'll implement
         | minigames (2D ones) leveraging all the current game's Zig and
         | Erlang pieces. After getting that done, we may return to
         | developing the main game.
        
         | fluidwizard wrote:
         | Yeah, so the game is kinda in a weird state where a library was
         | born to make it work better, we are planning more features so
         | it stops being a multiplayer walking simulator.
        
       | canadiantim wrote:
       | Seems cool, would love to see a couple more videos of gameplay,
       | but otherwise looks really cool. I would definitely consider
       | playing
        
         | fluidwizard wrote:
         | This version is actually a walking simulator XD, us trying to
         | pull the crazy tooling together until it just works.
         | 
         | https://www.youtube.com/watch?v=ejYcWRERetM&t=2758s
         | 
         | Here's what the game consists rn
        
       | Closi wrote:
       | Pretty amazing effort, this looks like a great labour of love!
       | 
       | I can't give feedback on the code/technology, but on the writing
       | on the lore section, I would try to simplify the writing. For
       | instance the following:
       | 
       | > The reverberations of the trumpet stirred the knights from
       | their deep repose, igniting a tumultuous awakening. With swords
       | unsheathed and hearts ablaze, they clashed in a thunderous
       | symphony of war, each seeking to claim dominance over the waking
       | realm.
       | 
       | Feels too ornate (purple prose) and could be more directly put
       | as:
       | 
       | > The trumpet's call jolted the knights from their rest. Swords
       | drawn and hearts alight, they clashed in a fierce battle, each
       | striving for dominance.
       | 
       | I'm not an author or anything, but a little bit of copy writing
       | could help - although this might just be me as it's probably a
       | matter of personal taste!
        
         | skulk wrote:
         | It's definitely a matter of taste. The first version has
         | flavor, the second is flat. The first reminds me of the style
         | of writing that got me hooked on MUDs.
        
           | adamc wrote:
           | Yeah, agree. "Jolted" is not as evocative as "stirred".
        
           | inopinatus wrote:
           | LOUD MUSIC BROUGHT ALL THESE ANGRY SWORD GUYS TO THE YARD AND
           | YOU WON'T BELIEVE WHAT HAPPENED NEXT
        
           | montedeipaschi wrote:
           | We were actually considering a MUD at first, but then we
           | added raylib
        
         | dgfitz wrote:
         | The colorful version is better.
        
         | wyldfire wrote:
         | Maybe we could throw a "lurgid bee" in there somewhere?
        
         | nonethewiser wrote:
         | It's a style thing. Fantasy writing often does this
         | deliberately.
         | 
         | I've thought about this before when I revisited fantasy after
         | years of being in the CS domain which helped me abhor ornate
         | writing. I definitely think there is such thing as TOO ornate
         | but dead-simple language also feels bad. It feels wrong to just
         | say its an exception with fantasy - simplicity is good because
         | it conveys the same thing more clearly and with less effort. I
         | would think that transcends all domains. Still not sure how I
         | feel about this. I guess there is a baseline non-styled
         | language that is all about communicating raw info and then
         | there is style that can be applied to writing which makes it
         | feel more natural in different domains.
         | 
         | Having said all this, I actually do like your example more.
        
           | Closi wrote:
           | You are right - it's a balance and definitely a matter of
           | taste.
           | 
           | Although not-ornate doesn't necessarily mean dead-simple or
           | bad. For instance compare the following:
           | 
           | > The reverberations of the trumpet stirred the knights from
           | their deep repose, igniting a tumultuous awakening.
           | 
           | With a very similar sentence from Tolkein:
           | 
           | > At that moment, among the trees nearby, a horn rang out. It
           | rent the night like fire on a hill-top. Awake! Fear! Fire!
           | Foes! Awake!
           | 
           | This is much less ornate, with simpler language, yet easier
           | to parse and the image is much more vivid.
        
             | pandemic_region wrote:
             | So when there is less ornate and difficult language to
             | parse for the brain, there are more cycles available for
             | imagination? Indeed z a thin line to balance.
        
             | nonethewiser wrote:
             | That's a good example. I agree the imagery is strong with
             | the Tolkien example. It also struck me as how you might say
             | it if you are verbally telling the story. The "among the
             | trees nearby" interlude and the successive exclamations at
             | the end.
        
               | sigbottle wrote:
               | Exactly. Even though I understand every word of this
               | quote
               | 
               | > The reverberations of the trumpet stirred the knights
               | from their deep repose, igniting a tumultuous awakening.
               | With swords unsheathed and hearts ablaze, they clashed in
               | a thunderous symphony of war, each seeking to claim
               | dominance over the waking realm.
               | 
               | there's a distinct feeling of disconnect, I guess? That
               | language feels much more appropriate, when say, you're on
               | top of a mountain and admiring the beautiful landscape
               | around you. Tolkein's words capture the urgency and
               | adrenaline of war with his simpler sentences.
               | 
               | I suppose I'd need to see the context behind the original
               | quote; in a historical lore recap, I'm more happy with
               | that quote.
               | 
               | (Not going to pretend like I know precisely what's
               | different as it's all subjective, but I suspect the mood
               | you're trying to go for heavily impacts your writing
               | choice)
        
             | danenania wrote:
             | From a writing perspective, I think much of it is about
             | "weighting". If you make every line ornate and full of
             | adjectives, then nothing stands out.
             | 
             | Therefore for not-so-important details like "the knight
             | woke up" that are just about giving the reader necessary
             | info to follow along, it's generally better to put less
             | weight and emphasis by stating it plainly. This way when
             | you do add emphasis to make the reader visualize a crucial
             | scene or situation, or describe emotional states at these
             | moments etc, they will jump out as being special rather
             | than just more of the same.
             | 
             | In my experience, every great writer follows this pattern,
             | though they begin at different baselines. It's fundamental
             | to good writing, just like creating attention hierarchy is
             | fundamental to good graphic design.
        
           | inopinatus wrote:
           | True high fantasy would not reference swords and trumpets,
           | first spending a chapter or two defining a world with its own
           | musical instruments and bladed weapons, their names of course
           | also being in a language, sorry, _tongue_ , for which the
           | author must also first labour to invent a common version
           | spoken by everyone, an ancient/high version only remembered
           | by a few, and at least one alphabet.
           | 
           | In addition, each one must be described in detail, including
           | a potted life story of the blacksmith that created it; when,
           | why, and for whom; metallurgical observations; history of
           | actual use; any supernatural blessings whether apocryphal or
           | actual; the litany of families that have retained it as an
           | heirloom & their subsequent social or political fates;
           | details of any inscription or filigree; and a nickname. This
           | remains true for both the swords and the trumpet. Additional
           | remarks concerning a scabbard or case are optional but highly
           | regarded.
        
         | Kinrany wrote:
         | The short version is better.
        
         | bitterblotter wrote:
         | This definitely has a GPT smell, at no fault of the creator.
         | Reminds me of a PR i reviewed about a simple security patch,
         | where the description said "The following changes have been
         | implemented to strengthen our role-based access policies and
         | system security" - Like yeah, it's security patch. A small
         | tweak to his prompt would probably do the trick
        
           | pmarreck wrote:
           | i have "no purple prose" in my custom instructions
        
           | DuLR10 wrote:
           | No AI-based tools were used to make the lore. One of our
           | developers (not me) is more of an intense reader and retro
           | gamer; he was the one chosen to write it.
        
             | montedeipaschi wrote:
             | I hate AI I hate AI I hate AI I hate AI
        
               | nineteen999 wrote:
               | But we love Bulwer-Lytton!
        
           | montedeipaschi wrote:
           | I wrote that down as an inspiration of Paradise Lost by
           | Milton. The original idea was to appeal to the phonology of
           | English and structure it as a sonnet, but that got rushed
        
             | montedeipaschi wrote:
             | That is purple prose, however. I cannot deny it
        
       | mapcars wrote:
       | Its always nice to see people experimenting with different
       | technologies.
       | 
       | I'm curious about Erlang server, do you see any advantage or
       | features that Erlang provides, compared to for example if the
       | server was running in Python via multiple instances?
        
         | DuLR10 wrote:
         | We haven't touched the distributed part of the game, but our
         | understanding is that when that time comes, it will be easier
         | to use the BEAM approach given that it was made for this
         | purpose.
         | 
         | Given the experience so far, it seems that using Erlang was the
         | correct choice, not only because of the above, but also because
         | Erlang made the server implementation way easier than we
         | thought.
        
           | mapcars wrote:
           | I see now, you are sending messages directly to Erlang server
           | so you don't have to worry about network sockets.
           | 
           | In my experience the issues with Erlang come with working
           | with data structures, records are not flexible and there is
           | not much one can do to abstract the boilerplate.
        
             | DuLR10 wrote:
             | We are using maps all over the place in the server, and so
             | far nothing has been annoying.
             | 
             | I gotta say though that lack of infix custom operators for
             | the monadic bind is a pain.
        
               | klibertp wrote:
               | Take a look at Erlang's "parse transforms" which would
               | allow you to implement some syntactic sugar. That's what
               | is used to implement qlc, the query language for
               | ETS/Mnesia - that one adds new semantics to list
               | comprehensions, but you can modify any part of the syntax
               | you want.
               | 
               | Also, Elixir supports macros and infix operator
               | overloading - have you considered using it? If you know
               | Erlang's stdlib and BEAM, switching to Elixir (and back)
               | is almost painless. Not sure which monads you needed, but
               | `with` macro is built-in, and it's a monadic bind for
               | Option types (well, more or less). Adapting it for other
               | monads shouldn't be hard.
        
               | DuLR10 wrote:
               | Thanks, I will for sure take a look!
        
               | klibertp wrote:
               | For reference (for the "parse transform" approach in
               | Erlang): https://github.com/rabbitmq/erlando - it doesn't
               | look maintained, but it's probably still usable;
               | otherwise, you might get some inspiration from the code
               | :) This also (ab)uses list comprehension syntax:
               | write_file(Path, Data, Modes) ->             Modes1 =
               | [binary, write | (Modes -- [binary, write])],
               | do([error_m ||                 Bin <- make_binary(Data),
               | Hdl <- file:open(Path, Modes1),                 Result <-
               | return(do([error_m ||
               | file:write(Hdl, Bin),
               | file:sync(Hdl)])),                 file:close(Hdl),
               | Result]).
               | 
               | (if one of the calls returns `{error, Reason}` tuple, the
               | execution terminates and the tuple is returned;
               | otherwise, `{ok, Value}` tuple is unpacked)
        
             | jhgg wrote:
             | Having a potentially untrusted client connect to the erlang
             | node as a c_node (which seems to be what zerl does) is not
             | a good idea generally, as connecting that way essentially
             | allows the client to execute arbitrary code on the server.
        
               | DuLR10 wrote:
               | Please correct me if I say anything wrong.
               | 
               | As far as I can tell, this is not possible at all; the
               | serialization layer (Zerl) cannot send arbitrary code to
               | another node. Now, assuming we implement this, I also
               | think this is not possible due to how the server is
               | designed; based on supervisors and child processes for
               | user sessions.
               | 
               | We recently became aware that you can indeed send tuples
               | that have fixed effects when using the supervisor
               | behavior, so it may be totally possible and probable that
               | one could exploit this vulnerability to some degree in
               | our server. We plan to investigate more about it as we
               | continue to learn more about OTP and the BEAM.
        
               | toast0 wrote:
               | If you're using zerl on the client and plain dist on the
               | server; the question isn't what Zerl can serialize, but
               | what the server will process.
               | 
               | With stock OTP dist, there is _no_ barrier between nodes.
               | Stock OTP runs an rpc server that you can use to send
               | function calls to run, which can include BEAM code to
               | load (or file I /O to do); and even if that's disabled,
               | you can spawn a process to run a function with arguments
               | on a remote node without needing an rpc server at all.
        
               | DuLR10 wrote:
               | How can one protect the server then? Do we need some
               | special behavior and/or library?
        
               | toast0 wrote:
               | I'm not aware of anyone running a limited dist to allow
               | for untrusted dist clients. But here's an OTP response to
               | a proposal that's pretty clearly a no [1].
               | 
               | It'd be much simpler to put together a custom protocol to
               | communicate between the client and server. You could use
               | Erlang's External Term Format to exchange data if you
               | want, in which case you'd want to do
               | binary_to_term(Binary, [safe]) to prevent creation of new
               | atoms and new function references which can fill up
               | tables and also consider that just because deserializing
               | is safe for the runtime doesn't mean you can trust the
               | client.
               | 
               | Erlang makes it pretty easy to parse sensible things off
               | of network sockets, if you want to go more custom, too.
               | Binary pattern matching is lovely.
               | 
               | [1] https://erlangforums.com/t/rfc-erlang-dist-security-
               | filterin...
        
               | DuLR10 wrote:
               | Thanks for the ideas and references! I gotta say, though,
               | that I will be pretty sad if having to write a custom
               | protocol turns out to be the final solution. So much more
               | convenient to use OTP (especially now that we finally
               | have an infinitely extensible serialization library for
               | it; Zerl). I'm shocked such an oversight would exist in a
               | real commercial solution which is the BEAM.
        
               | toast0 wrote:
               | The original application of dist clustering was dual
               | computers in a single telecom switch. There's not really
               | a need for a security barrier in that case; anyone with
               | access to one computer would be expected to have access
               | to the other.
               | 
               | Additional applications for dist have been explored over
               | the years, but most of them involve clustering servers;
               | where a security barrier isn't necessary; although it
               | might be desirable --- I've used dist clusters where some
               | people had access to only certain types of nodes;
               | bypassing access control using dist clustering was
               | certainly a possibility. Bolting security onto something
               | designed without it often is pretty challenging.
               | Especially if you want to keep all the existing
               | applications working.
        
               | mostatik wrote:
               | There's a good (new) library in Elixir that may work for
               | this use case called Zigler
               | https://hexdocs.pm/zigler/Zig.html
        
               | fluidwizard wrote:
               | I think this behavior can be fixed by properly using
               | something like `lib_chan`, but we needed something that
               | worked first for our Func Prog Sweden demo.
               | 
               | Indeed a malicious client can craft an brutal kill
               | message as long as it knows the PID a process (either a
               | worker or a supervisor) for instance.
        
         | fluidwizard wrote:
         | Also, Erlang makes it bizarrely simple to have a single process
         | per user (and it's actually what we did for the demo).
        
       | mysterydip wrote:
       | I'm confused by the use of a 10,000 poly model for a tile instead
       | of a flat (2 poly) square. I didn't even realize there were
       | changes in its height from the animation on the page. Was it just
       | to test things out and you plan to go to a full terrain later?
        
         | DuLR10 wrote:
         | Former case. Just testing things out. We will for sure have to
         | either learn Blender and this ecosystem or find someone with
         | this skill set.
        
       | mtlynch wrote:
       | What's your experience been like with Nix?
       | 
       | How do you feel about devenv vs stock Nix? How are you getting
       | devenv to work, as I don't see a devenv.nix file. I'm still a Nix
       | beginner and would like to find ways of integrating it more into
       | my development and improving my current techniques.[0]
       | 
       | [0] https://mtlynch.io/notes/nix-dev-environment/
        
         | fluidwizard wrote:
         | So, we've been using devenv for some time now and it's useful
         | for describing a monorepo-like environment that zig-enjoyers
         | can quickly code and test their changes on the latest version
         | of the erlang server. Also, it's incredibly easy to also manage
         | postgres from there.
         | 
         | It's great if you like local-first development experience.
        
           | mtlynch wrote:
           | I looked harder at the code to figure out how you're using
           | devenv without a devenv.nix file.
           | 
           | I now see that you're using devenv from within your
           | flake.nix, which I didn't realize you could do.[0] Neat!
           | 
           | I'm going to give that a spin in my projects, as my current
           | solution for pinning versions of Go, Zig, etc. is to use
           | nixhub to look up which commit of nixpkgs corresponds to
           | which version of Go (e.g., Go 1.23.2 is nixpkgs version
           | 4ae2e647537bcdbb82265469442713d066675275). That's obviously a
           | pain to look up and performs poorly, so I'm curious to see
           | how devenv goes.
           | 
           | Thanks for sharing the source!
           | 
           | [0] https://github.com/Dr-
           | Nekoma/lyceum/blob/1b0acf2d4bf295135bb...
        
             | fluidwizard wrote:
             | Sure! I'm glad the source code helped you!
             | 
             | I've actually written about the process that led us to use
             | the following flake here:
             | https://mtrsk.github.io/blog/2024/experiments-with-erlang-
             | an...
        
       | pmarreck wrote:
       | Props for using Nix.
        
       | diath wrote:
       | The BEAM is made with fault tolerance, scalability, and
       | concurrency in mind at the cost of performance due to
       | immutability and message passing approach among other things,
       | which sounds like a terrible choice for a multiplayer video game
       | that's anything more than a walking simulator. Erlang sounds like
       | a good choice for auxiliary video game services, such as
       | chat/social, guild management, auction house and so on, not so
       | much for the game shard itself.
        
         | DuLR10 wrote:
         | Given the current state of the game (the one we keep passing
         | around between client and server), I don't think Erlang is a
         | bottleneck in performance for us, and it won't be in the near
         | future. Keep in mind this state is not that huge and it is yet
         | not distributed, so it is totally possible that at some point
         | Erlang will slow us down. I will keep your comment in mind,
         | thank you!
         | 
         | Edit: currently with the game running at 60fps, there is no
         | bottleneck from the server side. And we call it every 16ms! I
         | should also mention that Erlang's choice has a learning
         | purpose; we want to try to use the game as a way to learn more
         | about OTP and the BEAM.
        
         | toast0 wrote:
         | If it comes down to immutability and copies being a bottleneck,
         | a solution is probably to move more of the core game state into
         | native code (like with Zig), but fault tolerance, scalability,
         | and concurrency seem like important things for a MMO.
         | 
         | Hot loading is pretty nice too.
        
         | cmdrk wrote:
         | it really depends on the game. people have been writing large-
         | scale multiplayer games for over 25 years now, with MMOs
         | sporting 2,000 player+ shards on _significantly_ more primitive
         | hardware. There is always this assertion that high-level
         | languages of various flavors are too slow for games, but I
         | suspect that today 's hardware more than makes up for it for
         | the right kind of game.
        
       | newobj wrote:
       | is Lyceum a reference to Ultima?
        
       | racenis wrote:
       | Do you think that you could use that Erlang feature where you can
       | link up several server program instances running on separate
       | physical servers?
       | 
       | Maybe you could simulate different parts of the game world on
       | different physical servers.
       | 
       | I think this is something like what the Very Large MMOs do, but
       | with Erlang it might be easier.
        
         | DuLR10 wrote:
         | Sounds like an awesome idea for when we introduce the Server
         | Browser! Thank you!
        
       ___________________________________________________________________
       (page generated 2024-11-11 23:01 UTC)