Most of this code dates back to 1998, when I built a little 2 player Joust game to push the bounds of what you could do with Div (and at the time Layer) tags in the latest browsers such as IE4 and Netscape 3. Surprisingly, most of it still worked when I thawed it out recently, provided your browser knew about document.all.
Over the weekend, I brought it up to date a bit and introduced it to Socket.io. Thus far it hasn't seen more than a dozen players at a time, so I'm curious to see what happens when a bunch of folks jump in at once.
I'll stick around to answer questions (and likely post-mortem the bits that catch on fire).
It's probably obvious for people who already played the game, but a quick note mentioning 'the highest one win' would make things easier for newcomers.
I was a bit confused about how to fight, before checking the wikipedia page
I just joined for a quick session - just wanted to say that this is really fun and very well done (especially for someone who has played Joust before). Thanks for sharing!
If you can figure out a way to only send inputs for each player instead of their full state, it should significantly reduce network traffic and lag for each player-- right now it's sending ~1KB/frame!
It can also be used to mutate hard game state (I wouldn't try it with dynamic, interpolated values like object position, angle, acceleration, or velocity.)
There are many other methods, some are more precise and compressible than others:
Desync is possible with UDP protocols, where a dropped packet would mean lost inputs, but this uses websockets, which are reliable TCP connections, so just the list of inputs combined with a deterministic engine is sufficient.
They can arrive out of order so you still have to deal with rewinding an re'simulating' the whole sequence of moves since the skipped packet then dealing with resolving the client vs server when they disagree.
That doesn't change that TCP packets can arrive out of order does it? (It's been a while since my college networking class and it's not something I come into contact with in my job)
Yeah, 700b for 20 players, and normally at least somebody does something every tick, so that will go out 30 times per second. It still only works out to 24kbps, so you might not need to upgrade the modem you had back when the original was in the arcades.
I tried only sending updates every 3rd tick, but even there I could notice the difference.
You're right that it'd be worth experimenting with sending keystrokes instead, and backing off the full resync to once/second or so.
I remember making a CGI (that's an ancient, simple server backend for the young-ins) IM chat in 1996. It used the keep connection open trick present in HTTP 1.0 and a tiny bit of Netscape JS.
This is superb, addicting as hell, and now I have to force myself to stop because I have real work to do.
The game gets increasingly jittery as it gets busier. It might be worth writing some tests to simulate lots of players moving around so it's easier to tune the performance.
Glancing at the code, it looks like he's rendering everything via DOM elements. I think player sprites are positioned using the top/left properties; could potentially optimize by keeping those props constant and just updating `transform: translate(x,y)` instead...
That's a holdout from 1998. Can't go changing things up after all that time, can we?
I imagine today I would have built the whole thing in canvas. But it's cool how quick you can get something up and running if you let the browser do most of the work.
Awesome. I loved Joust as a kid and spent countless hours playing it. Unfortunately the physics don't work as well as in the original. When you hit another player, the bounce is missing (and the egg, but I guess that's not important).
It's a little confusing when the name doesn't get dimmed or disappears when the player gets knocked off the bird. It's almost as if it's the name of the birds only, and the players are entirely nameless ;-)
Came to say the same about the original iPhone SE. I thought it was because mine is a smaller model (most sites look weird on it these days). But now I guess that's not the reason.
Yeah, mobile safari is annoying in that it doesn't allow fullscreen mode for anything except video. And that it doesn't always leave you in a consistent scroll state or with knowledge of how much screen is visible. So the left/right buttons drop off the screen unless you scroll it a bit before hitting start.
> We encountered an error when trying to load your application and your page could not be served. Check the logs for your application in the App Platform dashboard.
We encountered an error when trying to load your application and your page could not be served. Check the logs for your application in the App Platform dashboard.
Yeah, shame I wasn't around when this got popular (I actually submitted it on Tuesday, but HN must have automatically resurrected it for a second try).
The whole thing is running on a $5 Digital Ocean Apps server (well, two actually, one in New York, one in Frankfurt so that I can get low latency here in France), so it's amazing that it survived as long as it did.
Had I known it was going to get this much love, I would have spun up a dozen more servers.
You know I've been playing this for a week now, and until reading your comment it had never occurred to me to put a volumeNode in the middle of the webaudio path.
I had a lot of fun, but there's a big incentive to hit idle players. I would suggest making players intangible until receiving their first input on respawn.
105 comments
[ 3.3 ms ] story [ 168 ms ] threadOver the weekend, I brought it up to date a bit and introduced it to Socket.io. Thus far it hasn't seen more than a dozen players at a time, so I'm curious to see what happens when a bunch of folks jump in at once.
I'll stick around to answer questions (and likely post-mortem the bits that catch on fire).
I was a bit confused about how to fight, before checking the wikipedia page
It would be nice if you could change the scaling to use nearest neighbour on the players so they don't look all smooged,
There are many other methods, some are more precise and compressible than others:
Trailing State Synchronization
https://www.cs.ubc.ca/~krasic/cpsc538a/summaries/38/
Basic ideas
https://www.gafferongames.com/post/state_synchronization/
https://engineering.monstar-lab.com/2021/02/09/Game-server-S...
https://www.gabrielgambetta.com/client-side-prediction-serve...
I tried only sending updates every 3rd tick, but even there I could notice the difference.
You're right that it'd be worth experimenting with sending keystrokes instead, and backing off the full resync to once/second or so.
I remember making a CGI (that's an ancient, simple server backend for the young-ins) IM chat in 1996. It used the keep connection open trick present in HTTP 1.0 and a tiny bit of Netscape JS.
The game gets increasingly jittery as it gets busier. It might be worth writing some tests to simulate lots of players moving around so it's easier to tune the performance.
I imagine today I would have built the whole thing in canvas. But it's cool how quick you can get something up and running if you let the browser do most of the work.
Found Tyrian a few years back.
You can also iterate between Space + Up + W keys to boost your jumps.
> We encountered an error when trying to load your application and your page could not be served. Check the logs for your application in the App Platform dashboard.
Error
--
We encountered an error when trying to load your application and your page could not be served. Check the logs for your application in the App Platform dashboard.
The whole thing is running on a $5 Digital Ocean Apps server (well, two actually, one in New York, one in Frankfurt so that I can get low latency here in France), so it's amazing that it survived as long as it did.
Had I known it was going to get this much love, I would have spun up a dozen more servers.
/. atleast earned the right to coin the term.
I had to change them to 6% to comfortably play this game.
I just thought I had my speakers too loud :)
I'll have to link this to my Dad so that we can have a quick deathmatch!