Since the quirks protocol was that a core could report variable
savestate size, but the host then tells it "no", we should actually
accept the variable size quirk in netplay, since RetroArch refuses to
allow cores to actually produce variable-size states.
In the previous catch-up system, we would only try to catch up if we
were falling behind the farthest-behind peer. However, as they would
also only try to catch up to us, everyone basically agreed to the
worst-case latency. It makes more sense to try to be in parity with your
direct peer than with indirect connections.
Previously, we could be stalled by one player but still reading data
from another, which would wedge the client because we would never act
upon the newly-read data. Now we act upon data even if we're stalled.
Fixes bugs in initial connection with high latency.
Making the netplay handshake protocol send the core and content as an
explicit command, so that the other side can (notionally) choose to load
it. That isn't implemented, of course.
The idea:
* Use a fixed number of delay_frames (eventually to be fixed at 120,
currently still uses the config variable, 0 will still be an option)
* Determine how long it takes to simulate a frame.
* Stall only if resimulating the intervening frames would be
sufficiently annoying (currently fixed at three frames worth of
time)
Because clients always try to catch up, the actual frame delay works out
automatically to be minimally zero and maximally the latency. If one
client is underpowered but the other is fine, the powerful one will
automatically take up the slack. Seems like the most reasonable system.
Adding a key to toggle between playing and spectating. This key takes
the place of the previous flip key, although player flipping does
continue to work (and must be rebound if you still want it)
In resimulation mode, we only copy the buttons. The reason for this
is nonobvious:
If we resimulated nothing, then the /duration/ with which any input
was pressed would be approximately correct, since the original
simulation came in as the input came in, but the /number of times/
the input was pressed would be wrong, as there would be an
advancing wavefront of real data overtaking the simulated data
(which is really just real data offset by some frames).
That's acceptable for arrows in most situations, since the amount
you move is tied to the duration, but unacceptable for buttons,
which will seem to jerkily be pressed numerous times with those
wavefronts.
In particular, we need to disentangle the interceding netplay callbacks.
In previous versions, if you disconnected netplay but were using a core
that made netplay stall for connections (i.e., one that has no
savestates), netplay would continue to stall because it still
interceded, even though it was supposed to be off. This fixes that.
In the unlikely situation that serial_info wasn't provided and the delta
frame wasn't ready (possibly an impossible situation) it previously
would have segfaulted. This fixes that.
I had previously assumed that if AF_INET6 is defined, IPv6 support was
present. Some psychopathic console SDKs which shall not be named
actually define AF_INET6 but none of the IPv6 structures. As there is
therefore no way to determine whether IPv6 support is present at
preprocessor time, I'm ust assuming that HAVE_SOCKET_LEGACY implies no
IPv6. This means in effect that no consoles get IPv6 support.
Silence some Coverity warnings (including a real memory leak) and be
more careful about checking IPv6 mode (for backwards compatibility with
systems that don't support IPv6 but may run code compiled to expect IPv6)
For the time being, if NAT traversal is successful it simply announces
it as an OSD message. In the future it will be used to inform a
matchmaking server of the public port.
This patch also included minor fixes to the NAT traversal implementation
to make the select it demands actually doable.
As well as the implementation magic, we now send a platform magic in our
connection header. If the core reports platform dependence and the
platform magic differs relevantly, the connection will be refused.
Since netplay_send_info (client handshake) and netplay_get_info (server
handshake) were practically identical, they've also been merged into a
single netplay_handshake.
This effectively disables check_frames if frame 1's CRC differs between
host and client. This is necessary because some important cores have
nondeterminism in core_run, thus mandating check_frames, while some
important cores have nondeterminism in core_serialize, thus mandating no
check_frames.
This (mostly) prevents other paths from accidentally side-stepping
Netplay. Netplay itself now sets an in_netplay variable to avoid
self-recursion in its own core_run calls.
This commit:
* Reorders the Netplay settings menu to put more useful options at the
top.
* Renames the swap_input setting from "Swap Netplay Input", which is
meaningless and confusing, to "Netplay P2 Uses C1", which is oddly
truncated but at least true.
* Removes the is_client setting altogether, as that's no longer how
client vs. server mode is determined (each are separate options when
enabling Netplay)
Moved settings values into settings->netplay, and global->netplay.enable
is moved into netplay itself, and is no longer a configuration value
whatsoever, as that conflicts with the behavior of the netplay menu.
Without early saves we couldn't properly detect savestateless cores. We
still can't, really, but we can at least EVENTUALLY detect this
condition. netplay_net has been fixed to do so.
A couple fixes are still necessary for the client, in particular to
recover a "lost" hostname, but now it is possible to start server or
client mid-stream, and to disconnect intentionally.
Some cores can only determine the true size of sram after fully loading
the content, and so will report different values when the sram size is
queried. As a result, netplay cannot count on sram size as a reliable
verification technique. sram is still sent for the savestateless core
case.
This fixes a bug whereby reconnections would be out of sync due to the
flip state of the server and client being different. The server now
resets its flip state on disconnect.
This changes netplay host mode's behavior in net (normal) mode from
immediately blocking to waiting for a connection while allowing the game
to run, like spectator mode.
Spectate mode is now far more similar to net (normal) mode, and, more
importantly, it works. In addition, spectate mode will not fast-forward
to catch up with the server if it lags too far behind.
The receiving side of a player-flip request now does a forced rewind to
assure that any already-computed frames are computed with the players on
the right side.
Every frame (soon to be configurable), the server does a CRC-32 hash and
sends it to the client. If the client finds that its own hash is
different from the server's, it requests a fresh savestate.
This is a last-ditch effort to sync if all else fails, and it's a
best-effort situation. The size of the buffer should assure that we
always still have the frame around to CRC, but I imagine there are edge
cases where we don't. If you're in an edge case, the CRC is ignored.
Assuming the core supports saving/loading states, and (crucially)
assuming the states are portable across the architectures on both sides
of the connection, Netplay now supports the transmission of savestates.
Right now the frontend doesn't actually send any such requests, as it's
not clear exactly where the code for that should be.
This works in either direction, although I'll admit I have no idea what
happens if they both load at the same time.
Support for remote pausing, and with it, support for Netplay pausing the
frontend correctly. With this patch alone this doesn't work, since
there's no clean way for the frontend to tell Netplay that it's paused.
Rather than counting on the complexicon of used_real calculations, set
used_real when... real is used. (Problem: If the core doesn't read input
at all, used_real won't be set; todo: test with handhelds.) Minor
resilience fixes.
working":
(1) Fixups to the stall logic to make sure it always receives frames
while stalling :)
(2) Disused the used_real field. It was misconfigured and would
frequently claim to be using real data when real data hadn't been
used... this means more replays for now, but used_real will be readded.
(TODO)
(3) Stall duration is now related to sync frames, and thus configurable.
(4) Delta frames were having the good ol' initialization problem, as
frame==0 was indistinguishable from unused. Quickfixed by adding a
"used" field, but maybe there's a better way.
(5) If serialization fails, switch immediately to blocking mode
(stall_frames = 0). Blocking mode barely works, but if serialization
fails, no mode will work!
(6) I'm not sure which bug my replaying-from-previous-frame was trying
to fix, but the correct behavior is to replay from the last frame we had
vital information, not the frame prior. Notionally this should just be
an efficiency thing, but unsigned arithmetic at 0 made this a "just
ignore all input from now on" thing.
for this, and in particular, it now sometimes stalls in a way that makes
it very difficult to actually input anything (whoops :) ). Simply
setting the sync frames higher avoids that. With supported cores, this
is incredibly risilient, but when it fails, it mostly fails to freezing,
which is less than ideal.
TODO: Stall frames should be configurable. All the UDP code is still
there but commented out, should be gutted. The original fast-forward
code is now commented out, but really both fast-forward and stalling
should be options; the only complication is that it needs to send
simulated self-input for fast-forward.
they're mostly related:
(1) Renamed frame_count to self_frame_count to be consistent with all
other names.
(2) Previously, it was possible to overwrite data in the ring buffer
that hadn't yet been used. Now that's not possible, but that just
changes one breakage for another: It's now possible to miss the NEW
data. The final resolution for this will probably be requesting stalls.
This is accomplished simply by storing frame numbers in the ring buffer
and checking them against the 'other' head.
(3) In TCP packets, separated cmd_size from cmd. It was beyond pointless
for these to be combined, and restricted cmd_size to 16 bits, which
will probably fail when/if state loading is supported.
(4) Readahead is now allowed. In the past, if the peer got ahead of us,
we would simply ignore their data. Thus, if they got too far ahead of
us, we'd stop reading their data altogether. Fabulous. Now, we're happy
to read future input.
(5) If the peer gets too far ahead of us (currently an unconfigurable 10
frames), fast forward to catch up. This should prevent desync due to
clock drift or stutter.
(6) Used frame_count in a few places where ptr was used. Doing a
comparison of pointers on a ring buffer is a far more dangerous way to
assure we're done with a task than simply using the count, since the
ring buffer is... well, a ring.
(7) Renamed tmp_{ptr,frame_count} to replay_{ptr,frame_count} for
clarity.
(8) Slightly changed the protocol version hash, just to assure that
other clients wouldn't think they were compatible with this one.
(9) There was an off-by-one error which, under some circumstances, could
allow the replay engine to run a complete round through the ring buffer,
replaying stale data. Fixed.