Real time online gaming with the NES
Super Tilt Bro. is not a retro-game. It always tried to be a modern game on retro hardware, and modern games are playable online!
We "simply" put a WiFi chipset in the cartridge, and let's rock! New millennium, here we come!
A prototype of the WiFi cartridge by @BrokeStudio.
Challenges of online gaming
Writing a game to be played online is not an easy task. At any time, we have to ensure that both players see the same scene. When the game is fast-paced, some milliseconds of ping can make a big difference.
Let's assume Alice is playing against Bob. Bob unleashed his super-attack, and Alice dodged it on the last frame, EPIC! But, there is a ping of 20 milliseconds between Alice and Bob's houses (typical), and a frame lasts 16 milliseconds. Alice did actually dodge on time, so her game shows her character ready for a deadly counter-strike. Now, the information of Alice's epic dodge took 20 milliseconds to reach Bob's home, so Bob sees that Alice dodged too late, taking heavy damage. That's a typical case of desynchronization: the two players see a completely different outcome of their fight.
Alice and Bob's different timelines. Everybody wins!
The rollback netcode
Super Tilt Bro. is based on a rollback netcode. It does not wait to know opponent's inputs, it guesses it. When inputs finally come, the game discovers if its guess was right or wrong. A right guess is good. If the guess was wrong, the game rollbacks it's scene taking real inputs instead of guessed ones.
What does it mean for the epic fight between Alice and Bob? Alice still dodges on time, her game shows it without problem. Bob's game begins to guess that Alice did not dodge (that was an epic dodge like we rarely see), so it begins to show the attack hitting. Then, less than 20 milliseconds after, the information arrives. The game rollbacks, Alice was never hit. There will typically be one frame of flicker (Alice is hit one frame, has dodged the next), but finally both players see the same action, and can continue to fight.
The rollback engine, casually rewriting the past.
The guessing algorithm is super simple. It always guesses that nothing changed, no button was released nor pressed. For a game running at 60 FPS, even for a nervous player doing six inputs per second, this simple algorithm is right 90% of the time.
Adding some input lag
Another trick is to delay inputs while sending them immediately over the network. Let's say we artificially delay all inputs by four frames. If an input takes less than four frames to be transmitted from a player to another, rollback is not even necessary.
Getting back to the game between Alice and Bob, but with an input lag of four frames. When Alice inputs the dodge on the last possible frame, it has no immediate effect, so she is hit hard. One or two frames later, Bob receives the network packet with the dodge input, it has not yet had any effect. Both players see the same action: Alice took the hit. It is less epic, but at least everybody sees the same thing and there is no visual glitch.
Input-lag: glitch-less, but frustrating at times.
Of course, both approaches can be used together. That's what Super Tilt Bro. does. There is a little bit of input lag, which should be sufficient in most cases. In case of a latency spike the input lag may not be enough, the rollback code saves the day ensuring minimal glitch. In a nutshell, rollback makes your opponent teleport at the beginning of a move, input lag makes your character slow to react, a balance between both has to be found.
Some other implementations
All that is good, now you know how the Super Tilt Bro.'s netcode mitigates internet's latency. Most game developers just never openly discuss their netcode, while it is extremely interesting to see different approaches. Here are some known ones.
Super Smash Bros, the direct inspiration for Super Tilt Bro. does not do the same thing. There is no rollback at all in the original series, instead the game slows down or even freezes waiting for inputs. It can be easily seen by playing on an unstable connection, the game will regularly freeze. To limit the impact of these slow downs, the input lag seems dynamic. (Yes, sorry for that "seem", most available info is reverse engineered or outright guessed.) We know for sure that Super Smash Bros Brawl rates your connection and attributes it an input lag that can vary from 3 frames for the best to 15 frames for the worst. People trying to measure input lag in Super Smash Bros Ultimate failed with online mode, the input lag was too varying. Maybe Ultimate is adjusting dynamically input lag for each player during the game.
The approach of avoiding rollbacks and freezing the game has its benefits and drawbacks. First, it is really easy to implement, the only special case to handle is to wait for the needed information to be available. As a side effect, it requires very little overhead for the CPU and memory. The game engine does not have to be able to rollback from a previous point in time, the game can just run forward, forgetting anything that happened on previous frames. The effect on laggy connections is a freeze, while with a rollback netcode characters may teleport around on laggy connections. It is a matter of preference, a freeze is more "understandable", while a slight teleport is more smooth to play. The biggest problem is competitive play. Even at a moderate level, players train their combo, learning to execute moves with very precise timing (even frame-perfect sometimes). If the game slows down, freezes or changes the input lag in the middle of a frame-perfect combo, it messes it up, making it fail while player's execution is perfect. Finally, freezes must be avoided, it is not an option. So the player cannot be offered the choice to configure its input lag, it has to be conservative.
Another well-known solution is GGPO. It is a standalone rollback netcode made to be used by developers on their own game. It is especially popular in arcade emulation, and is the solution of choice of Skull Girls (which is known to have a good netcode.) GGPO is a rollback engine, their documentation does not mention input lag, but Skull Girls allows it to be configured to a fixed number of frames. This way the player can balance himself between more rollbacks or more input lag.
It is a lot like the Super Tilt Bro.'s netcode. The good thing is that input lag is fixed and constant for an entire game. You can still perform your frame-perfect combos. The biggest problem is when there is a big latency spike, characters will teleport around for a while until the game successfully re-synchronize itself with the other player. This kind of action is really confusing for the player. Also, the game engine has to take save-states and manage it to be able to rollback, putting pressure on the CPU and memory.
All these modern implementations have something in common: they are peer-to-peer. Avoiding packets to transit by a server noticeably reduces latency. That's a really cool solution. Super Tilt Bro., however, is server based, let's see why.
Super Tilt Bro.'s game server
We saw that peer-to-peer is the best model for online versus fighting. Super Tilt Bro. does not have this luxury. We also saw that rollback netcodes are costly for the CPU as the game has to be able to rollback, and the RAM because it has to store its old states. The NES runs with a 8-bit CPU at 1.5 Mhz and 2 KB of RAM. It is really far from modern systems. Implementing the rollback system entirely on the NES would be very limiting, most resources would be allocated to it at the expense of gameplay. The server is here to help.
Remember Alice and Bob? With the Super Tilt Bro.'s protocol, when Alice presses a button the game only sends the state of the gamepad and a timestamp to the server. The server knows the game, it is able to simulate frames, and compute the game's state at any point. Based on this knowledge and Alice's input, the server computes the state of the game at the frame of Alice's input, then sends all that to Bob. Bob receives the timestamp, Alice's input and a full game state. If input lag did its work, the game state can be ignored. If a rollback is needed, Bob's NES can use the state received from the server. Bob's NES does not have to manage a list of game states, the server generates it when needed, removing almost all pressure on the limited NES memory.
The server can also help with CPU budget. Let's say there is always a minimum of two frames of delay between Alice and Bob. When receiving Alice's input, instead of computing the state at the time of the input, the server can compute the state two frames after. The server is actually predicting the future, avoiding Bob's NES to do it itself. Of course a full rollback engine has to also be implemented in the server, but can help a lot with big latencies. Currently, Super Tilt Bro. is able to rollback only three frames of gameplay before running out of time. The server doing the rollback for one or two frames can help a lot.
UDP vs TCP... And web browsers
There are two big, omnipresent protocols on the internet: UDP and TCP. TCP is the most common one, it allows a computer to connect to another and send data. In TCP land, we don't lose data, and data is received in the same order as it was sent. Most of the internet is constructed on TCP, it is simple to use. UDP is a more lightweight protocol. In UDP's world, when we send a packet, the only sure thing is that we sent it. The packet may be randomly lost, or arrive before a packet sent earlier. So, TCP is a better protocol, right? No. Sadly, TCP's magic has a cost.
In Super Tilt Bro.'s protocol we don't care a lot about lost packets. A message from the server to a client contains a full game state. A recent message completely invalidates any older one. If we use TCP and lose a message, it will be re-emitted before the receiver processes any other message. If we send two messages in a row, only the most recent one is useful to Super Tilt Bro. If the useful one is physically arrived but the useless one is lost, the useful one will be delayed until the useless one is re-emitted. During development, we tested a simple ping implementation using TCP and voluntarily losing 10% of packets. With UDP, it resulted in 10% packet loss, without impact on the ping (around 20 ms in our test setup). With TCP, it resulted in 0% of messages lost (that's the TCP magic), but ping value sometimes reaching three full seconds.
Imagine having your input delayed by three seconds because a useless packet was lost? No way!
If you played the game, you probably noticed that it is available in an HTML5 NES emulator on the itch.io page. As innocent as it seems, that's actually a big deal. Web browsers don't let web pages send UDP packets. Super Tilt Bro. protocol is based on UDP and needs it. The solution was to use WebRTC, a modern protocol made for visio conferences. That's what your favorite Google Meet, Skype online, Facebook visio uses. Hijacking a video conference protocol to use it for gaming is a large topic. I may or may not write a devlog entry on the subject, for now I'll let you with the real-time twitter thread of my fight against internet's security: https://twitter.com/RogerBidon/status/1259171335135211523
Implementing modern network capabilities for a NES game was an epic journey... And it is far from over! This made it to the ALPHA stage. It is not complete, and I am in dire need for feedback.
You can test it yourself: https://sgadrat.itch.io/super-tilt-bro
Join the Discord to find a rival and/or send your feedback: https://discord.gg/qkxHkfx
Hope this little piece of internet knowledge can help somebody out there. Do not hesitate to reach me for more details, nothing is secret. Super Tilt Bro. is an open source software. It is made to make the internet a better place, not to keep its secrets!
Leave a comment
Log in with itch.io to leave a comment.