←back to thread

93 points BSTRhino | 2 comments | | HN request time: 0.001s | source

For the past 3 years, I've been creating a new 2D game programming language where the multiplayer is completely automatic. The idea is that someone who doesn't even know what a "remote procedure call" is can make a multiplayer game by just setting `maxHumanPlayers=5` and it "just works". The trick is the whole game simulation, including all the concurrent threads, can be executed deterministically and snapshotted for rollback netcode.

Normally when coding multiplayer you have to worry about following "the rules of multiplayer" like avoiding non-determinism, or not modifying entities your client has no authority over, but all that is just way too hard for someone who just wants to get straight into making games. So my idea was that if we put multiplayer into the fabric of the programming language, below all of your code, we can make the entire language multiplayer-safe. In Easel the entire world is hermetically sealed - there is nothing you can do to break multiplayer, which means it suits someone who just wants to make games and not learn all about networking. I've had people make multiplayer games on their first day of coding with Easel because you basically cannot go wrong.

There were so many other interesting things that went into this project. It's written in Rust and compiled to WebAssembly because I think that the zero-download nature of the web is a better way of getting many people together into multiplayer games. The networking is done by relaying peer-to-peer connections through Cloudflare Calls, which means Cloudflare collates the messages and reduces the bandwidth requirements for the clients so games can have more players.

I also took inspiration from my experience React when creating this language, here's how you would make a ship change color from green to red as it loses health:

`with Health { ImageSprite(@ship.svg, color=(Health / MaxHealth).BlendHue(#ff6600, #66ff00)) }`

There is a lot of hidden magic that makes the code snippet above work - it creates a async coroutine that loops each time Health sends a signal, and the ImageSprite has an implicit ID assigned by the compiler so it knows which one to update each time around the loop. All of this lets you work at a higher level of abstraction and, in my opinion, make code that is easier to understand.

Speaking of async coroutines, my belief is that they don't get used enough in other game engines because their lifetimes are not tied to anything - you have this danger where they can outlive their entities and crash your game. In Easel each async task lives and dies with its entity, which is why we call them behaviors. Clear lifetime semantics makes it safe to use async tasks everywhere in Easel, which is why Easel games often consist of thousands of concurrently-executing behaviors. In my opinion, this untangles your code and makes it easier to understand.

That's just the beginning, there is even more to talk about, it has been a long journey these past 3 years, but I will stop there for now! I hope that, even for those people who don't care about the multiplayer capabilities of Easel, they just find it an interesting proposal of how a next-generation game programming language could work.

The Editor runs in your web browser and is free to play around with, so I would love to see more people try out making some games! Click the "Try it out" button to open the Sample Project and see if you can change the code to achieve the suggested tasks listed in the README.

Show context
_--__--__ ◴[] No.44000281[source]
I'm curious how your engine decides to trigger rollbacks without explicit knowledge of which parts of the game state each input can actually affect. Naively rolling back on every missed input can and will be abused - in early versions of Street Fighter V players found out that while being comboed you could churn out inputs as fast as possible to cause rollbacks for your opponent, even though none of those inputs could actually do anything (this was made worse by other issues with that game's inability to keep opponent game clocks synced and constant one-sided rollbacks).
replies(3): >>44000374 #>>44000396 #>>44001365 #
dustbunny ◴[] No.44001365[source]
Theres some missing information here. A "rollback that doesn't do anything" shouldn't be noticeable to the user. It would only be noticeable if the game simulation can't keep up with the # of frames it's being asked to simulate. And in street fighter, the simulation is ridiculously simple. There should be no reason why street fighter wouldn't be able to "rollback and resimulate" dozens if not hundreds of times per frame. There's no way that game is CPU bound... Am I missing something?
replies(1): >>44001614 #
_--__--__ ◴[] No.44001614[source]
I think you are underestimating the per frame computation cost or massively overestimating the base model PS4. Remember that all fighting games have to lock 60fps, 16ms is not a ton of time for the handoff from input interpreter (SF has to parse backwards over the past several frames to see if a forward input is completing a quarter circle, double quarter circle, dash, etc) > game state update tick > graphical and sfx update pipeline. The issue with clock syncing meant that the worse your machine was, the more your opponent would roll back and the bigger the rollback windows would be because every single frame of your inputs would come late.

Here's an example of the constant one sided rollback: https://www.youtube.com/watch?v=aSB_JlJK_Ks

and an example of how players were aware that mashing caused frequent rollbacks: https://youtu.be/_jpg-ZiE70c?t=105

Eventually PC players learned that they could "fix" this by alt-tabbing out of the game, taking away dedicated GPU processing so they could be the peer that was falling behind.

replies(2): >>44001730 #>>44007850 #
1. BSTRhino ◴[] No.44001730[source]
I can't really speak for Street Fighter V, but I can say that Easel would assign command delay to the person who is introducing it. So if a person is somehow forcing their inputs to arrive later than they should, they would be assigned more input latency so that the other players would be unaffected. Easel also has a lag spike protection system where the server will just forcibly reschedule inputs if they arrive really late for some reason.
replies(1): >>44001806 #
2. _--__--__ ◴[] No.44001806[source]
Yeah, most of these are relatively solved problems, but they are nontrivial enough that one of the industry giants couldn't get it right on its first few tries.