[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)