Good Game, Peace Out
October 9, 2019 4:24 PM   Subscribe

GGPO, the gold standard for netplay code for fighting games — a notoriously difficult genre to implement online play for — is now open-source and freely available for commercial use.
posted by DoctorFedora (12 comments total) 19 users marked this as a favorite
For an idea of just how good GGPO is, I've played a lot of Fantasy Strike, which uses GGPO,* and even though I live in Japan, I've played games against people on the east coast of the US and Canada and had them feel like we were in the same room, despite 200–250 ms ping times. It's downright eerie.

*It's also (successfully) designed to be legitimately accessible for people who don't already play fighting games, so I highly recommend it for basically anyone who has ever been even fighting-game-curious.
posted by DoctorFedora at 4:26 PM on October 9, 2019 [1 favorite]

From src/lib/ggpo/input_queue.cpp:
       * The requested frame isn't in the queue.  Bummer.  This means we need
       * to return a prediction frame.  Predict that the user will do the
       * same thing they did last time.
      if (requested_frame == 0) {
         Log("basing new prediction frame from nothing, you're client wants frame 0.\n");
      } else if (_last_added_frame == GameInput::NullFrame) {
         Log("basing new prediction frame from nothing, since we have no frames yet.\n");
      } else {
         Log("basing new prediction frame from previously added frame (queue entry:%d, frame:%d).\n",
              PREVIOUS_FRAME(_head), _inputs[PREVIOUS_FRAME(_head)].frame);
         _prediction = _inputs[PREVIOUS_FRAME(_head)];
I was curious how smart the prediction logic was, and it looks like the answer is: not very. That's not a knock at all, since I assume using the simplest logic for this possible is a pragmatic choice that has benefits for both user and implementor, but I'm a little surprised that such a simple heuristic works so well.
posted by invitapriore at 4:58 PM on October 9, 2019 [6 favorites]

For a bit more clarification, at the risk of thread-sitting (sorry!), here is the basic gist of the two main approaches to fighting game netcode! Before we begin, please note that fighting games are played more or less exclusively peer-to-peer, with no central server except for matchmaking, because, due to their history of being in arcades, any additional latency incurred by going via a central server will not only feel worse, but also potentially make the game state a little weird.

First off, we have conventional delay-based netcode, which is still generally used by Japanese developers (presumably because, due to the smaller physical size of the country, the delay generally doesn't get to be tooooooo conspicuous, though also because it's way, way easier to implement). Basic gist of this is that you take the ping time (let's say 100 ms) and divide in half, and delay all game inputs by that much (i.e. 50 ms). This means that the game is waiting for 50 ms for your opponent's inputs to arrive, and then executes your own queued-up inputs, and kind of "weaves together" a game state that's consistent on both ends, but for any connection with more than a negligible ping time, it starts to feel really bad, because, as the name of the approach implies, you're playing on a delay. If you've ever tried using a computer that wasn't particularly responsive, you can probably guess why this isn't great.

Rollback netcode is a newer concept developed about ten years ago, and it takes a different approach. Rather than the whole game state being delayed and "woven together," the game state instead stays in real time but is occasionally "corrected." Suppose, for instance, that we're on that same 100 ms ping connection. In this case, if your opponent pushes a button,* there's still that 50 ms latency from their end to yours — you can't beat the speed of light, after all — so what will happen on your end is that your computer/console will receive game state information from the opponent that says that they pressed that button 50 ms ago, and it'll "fix" the current state on your end by effectively having them do what their button does, starting 50 ms into it. For really quick stuff, especially on poor connections with high ping times, it can get kind of jerky and teleport-y, but for the vast majority of things you do in fighting games, you're going to be committing to things that last long enough to play out inevitable results. Jumps, for instance, tend to last half a second or more, so beyond the first couple frames, they look perfectly natural. The result is something that plays much better and allows player to take advantage of muscle memory from playing offline.

*We're assuming no buffering here, for a worst-case scenario. A substantial buffer can further reduce unexpected inputs, to the point where, as mentioned above, Fantasy Strike feels almost like being in the same room, at least on the Steam version.

The failure states of the two styles are very different, as well — delay-based netcode still LOOKS fine on really bad connections, but feels positively awful to play, while rollback netcode will look horrible to watch but feel at least acceptable to players. Given the fact that is now available in both English and Japanese, we can only hope that that's meant as a pointed suggestion to Japanese developers that maybe they should get with the times, and use netcode that actually feels good and playable, even for cross-continental connections.
posted by DoctorFedora at 5:05 PM on October 9, 2019 [16 favorites]

Fighting games like the Street Fighter series typically run at 60 frames per second, but a given move may last a good 15 frames. Furthermore, the combos that require precise timing rely on your opponent being "hit stunned" so that you can chain one move to the next.

So basically, there will be a lot of moving around and posturing, but once someone lands a hit (or honestly, even commits to an attack), both players' games are updated quicker than an eye blink (100 ms), and the lag just doesn't factor in.

Yeah, it might be slightly jarring to think you landed the hit in one frame and then the game progresses as though the opponent landed their hit (because the game rolled back to the "correct" state), but that tiny stutter passes and you immediately start thinking about how you're going to recover.
posted by explosion at 5:09 PM on October 9, 2019 [1 favorite]

Right, yeah, in most fighting games, 3 frames of startup before an attack can actually hit is considered extremely fast, and that's 50 ms right there. For reference, anything below 20 frames (⅓ second) or so is effectively unreactable anyway, so in practice skipping the first little bit of an animation doesn't actually make that much of a difference most of the time.
posted by DoctorFedora at 5:12 PM on October 9, 2019

I get how this works for 20, 50ms latencies. That's faster than human processing. But how does this work with 200ms latency? Is the drawback that sometimes I see my opponent doing nothing, but in reality 150ms ago they started attacking? So I have 150ms less time to react than if I were playing with them in person?

(Not saying this is worse than the laggy solutions, just curious what the tradeoff is).

Heroes of the Storm never felt right because it was so mushy and laggy. Folks said that was because the Starcraft engine it was based on didn't do predictive netcode, so you had to wait for the lag. League of Legends has always fight tight and responsive, the rumor being they had something more like fighting game code. Perhaps that's GGPO? You can definitely see lag occasionally in LoL; projectiles can appear on screen seemingly from nowhere. But it's quite rare.
posted by Nelson at 5:52 PM on October 9, 2019

So, latency is going to be ping÷2 (because ping time is round trip). 200 ms ping means 100 ms delay on getting data from opponents (200 ms latency would be 400 ms ping, and god help you then under basically any circumstances — the best I've ever seen was Fantasy Strike making 300 ms ping between Europe and Japan playable). On a 200 ms ping connection, you'll be 100 ms behind what your opponent did (6 frames), which can be unpleasant and annoying, but surprisingly playable for at least some games. If the game has an input buffer, allowing you to press a button to do another thing right after the current thing you're doing, that input can conceivably be sent far enough ahead that the other end knows far in advance, hey, pressed this button, that it can show the animation from the very start once the current animation completes.

Most non-fighting games will have server-based netcode and will generally have some sort of "fudging" to account for the squishiness of online play. Shooters and Mario Kart are set up so that players merely have to hit where their opponents appear to be locally, rather than where they actually are on their own end, for instance.

And it's worth noting that the guys who developed GGPO do indeed now work for Riot, and are likely involved in the netcode for League of Legends. Projectiles appearing seemingly from nowhere is very much an artifact of rollback netcode on a high-ping connection, in fact!
posted by DoctorFedora at 6:14 PM on October 9, 2019 [2 favorites]

Also, here is a pretty interesting Twitter thread that goes down the specifics of how one actually implements GGPO! Sorry again for thread-sitting — this is fairly recent news and stuff is still coming out about it, and I think it's really cool overall.
posted by DoctorFedora at 6:17 PM on October 9, 2019

Rollback netcode is a newer concept developed about ten years ago, and it takes a different approach. Rather than the whole game state being delayed and "woven together," the game state instead stays in real time but is occasionally "corrected."

This is similar idea to the client-side prediction techniques used in shooters, right? In shooters it's a lot more than ten years old but the idea that networked fighting games are viable at all feels comparatively recent to me.
posted by atoxyl at 6:39 PM on October 9, 2019

A college prof spent some good DARPA funding on this back in the day...

Tho he had to simulate tank battles apparently....
posted by Heywood Mogroot III at 7:12 PM on October 9, 2019 [3 favorites]

Speaking of fighting games and lag compensation methods, For Honor tried a radical implementation of it back when it launched, an architecture that was ultimately considered a failure.

For Honor is a 4v4 three dimensional swordfighting game that mechanically plays out like a fighting game. All the usual tropes are there - instead of a high and low attack, you have 3 directions - high, left and right. The game has neutral guard but only if you're in the correct matching stance to receive the attack. Everything is else is similar - guard break (throw) goes through guard, and you also can counter a throw attempt. Moves have start up and recovery frames depending on hit, block, deflect or parry. Even the direction of momentum mattered - for some weapons a right direction attack can be quickly followed up by a left direction attack, but has a longer recovery if you attack twice from the same direction. Almost all moves could be cancelled / feinted to bait enemies. There are unblockable moves, or moves where their start up gives you block frames. Characters have a stamina meter like Dark Souls, with severe penalties for running out of stamina. Attacking or dodging costs stamina. Some moves directly drain enemy stamina. The combat also looks fricking amazing.

The developers claimed to have created some kind of P2P ring topology, they said server based network would introduce extra latency. In theory it was an 8 way cluster compute of the world simulation where each PC would negotiate what they agreed the real state of the world was with minimal latency. In practice it didn't quite work and they moved to a server architecture later.

The game was legit as deep as any 2D fighter. Every class had their own unique weapon and hitboxes and your stats could be customized - recovery time, damage, chip damage, stamina, etc. Switching stances took time. Some attacks could be started from a difference stance. Some characters even had a way of hiding their stance (nobushi hidden stance) so you had less warning which direction their next attack could come from. Executions were the best part, you had a variety to choose from, if you executed the enemy they couldn't be revived, and the longer the animation the more health you regenerated, so it was quite a strategic decision if you wanted to risk an execution and if so which one to use.

When the networking worked it was great but honestly at launch quite a few games were ruined by unstable connections.
posted by xdvesper at 8:36 PM on October 9, 2019 [1 favorite]

So it predicts the player's actions and if it is wrong it rewinds time to make itself right.
Isn't this a Ted Chiang story?
posted by thatwhichfalls at 6:56 AM on October 10, 2019 [3 favorites]

« Older studies do not prove that dog ownership has a...   |   Rebecca Wragg Sykes reviews evidence for... Newer »

This thread has been archived and is closed to new comments