Contrary to commonly held beliefs, bandwidth is almost irrelevant for online play in a 2-player fighting game, if the game is coded competently. (This is why I ignore anyone who posts a speedtest result when talking about their netplay experiences; that shit is usually pointless and often makes you look ignorant.)
All that's needed (oversmiplifying here to some extent, but bear with me) in order for a fighting game to faciliate netplay are the following two pieces of information:
- what each player inputs on their respective ends
- what exact point in time (frame) those inputs occur
That's a trivially small number of bytes that actually have to travel across the wire. The problems arise in the ways that the games try to reconcile the amount of time it takes to wait and see what the player on the other end is doing. Typical bad netcode does this by forcing you to wait until the other player's inputs are received on your end. This results in a lot of input delay that isn't even consistent; if your connection to the other player fluctuates between 50ms and 150ms throughout the match, then the input lag on your end is probably going to fluctuate between 50ms and 150ms at random as well. Or put you at the mercy of a lag switcher. And nobody likes that.
GGPO can't fix a bad connection, but it works because it allows you to function as if that lag isn't there (in good connections) or as if the lag is at least consistent (in bad connections).
GGPO allows the player to set a frame delay on their own terms. Let's say that you and I have a fairly stable 66ms ping to each other (4 frames). That means that it takes 4 frames for one of us to send a piece of information to the other and have it return to the original sender. Thusly, that also means that it takes about only 2 frames for one of us to send information to the other (ignoring the return trip). So if you set your GGPO delay to 2 frames, then that should mask any issues with the connection and give you a stable, consistent, playable delay of exactly 2 frames for the entire match.
If our connection gets rocky in the middle of the match and randomly spikes in latency, then you'll still only ever have 2 frames of input delay, but the game may rewind to a previous savestate every once in a while because my inputs made it to your side later than the game expected, and GGPO has to go "oh wait, he actually pressed a button a few moments ago that results in a different action than what's played out on your end, so I've got to go back and add that in so that things are correct now, sorry." But the beauty of this is that even though these rewinds may happen, at least some of them can be completely hidden because they don't affect the outcome of the on-screen action at all.
Let's say that I'm hitting you with a 2-minute Dante infinite. If I have my timing down and can account for that consistent 2-frame delay, then it doesn't matter if our connection randomly spikes or you hit a lag switch or whatever. Once the connection stabilizes again and my inputs are sent through to your side, then you'll be right back in that infinite as if nothing happened to the connection. I'll never know about the lag spike on my end in the first place because it doesn't matter what buttons you're pressing while you're being comboed. In traditional netcode, the delay would increase massively once the connection falters and it would throw off my timing.
You can adjust that GGPO delay accordingly per connection and to your own preferences. On better connections, you'd want to keep it low, and on worse (high latency) connections, you'd probably want to increase it depending on your tolerance to rewinds. If you really value low input delay and don't mind rewinds, then you can keep it low and put up with those, but if you just hate rewinds altogether, you can crank up the delay as much as you want to get rid of them and at least still get a consistent experience for the whole match. It also allows each of us to set our delay independently; I could use a really low delay setting because I hate input lag and you could use a high delay setting because you hate rewinds. So whether you have a godlike connection or a terrible one, there's always a benefit to this type of netcode.
The drawback to GGPO, though, is that it's computationally expensive. Rewinds are dependent on having enough memory to actually store a handful of savestates in the first place. From what I heard from Arturo, this is one big reason why devs haven't bothered to use it much in previous gen games, as the overhead for netcode isn't set aside early enough in development in order for there to be enough room for GGPO savestates. Not that I expect things to really improve that much in the current generation either, though...