Updating Netplay README to be more true

This commit is contained in:
Gregor Richards 2016-12-12 20:29:07 -05:00
parent f6f9905ae3
commit 3d7f1f6575

View File

@ -9,11 +9,7 @@ minor constraints:
Furthermore, if the core supports serialization (save states), Netplay allows Furthermore, if the core supports serialization (save states), Netplay allows
for latency and clock drift, providing both host and client with a smooth for latency and clock drift, providing both host and client with a smooth
experience. experience, as well as the option of more than two players.
Note that this documentation is all for (the poorly-named) "net" mode, which is
the normal mode, and not "spectator" mode, which has its own whole host of
problems.
Netplay in RetroArch works by expecting input to come delayed from the network, Netplay in RetroArch works by expecting input to come delayed from the network,
then rewinding and re-playing with the delayed input to get a consistent state. then rewinding and re-playing with the delayed input to get a consistent state.
@ -24,10 +20,10 @@ correct frame.
In terms of the implementation, Netplay is in effect a state buffer In terms of the implementation, Netplay is in effect a state buffer
(implemented as a ring of buffers) and some pre- and post-frame behaviors. (implemented as a ring of buffers) and some pre- and post-frame behaviors.
Within the state buffers, there are three locations: self, other and read. Each Within the state buffers, there are three locations: self, other and unread.
refers to a frame, and a state buffer corresponding to that frame. The state Each refers to a frame, and a state buffer corresponding to that frame. The
buffer contains the savestate for the frame, and the input from both the local state buffer contains the savestate for the frame, and the input from both the
and remote players. local and remote players.
Self is where the emulator believes itself to be, which may be ahead or behind Self is where the emulator believes itself to be, which may be ahead or behind
of what it's read from the peer. Generally speaking, self progresses at 1 frame of what it's read from the peer. Generally speaking, self progresses at 1 frame
@ -38,18 +34,31 @@ frame from which both local and remote input have been actioned. As such, other
is always less than or equal to both self and read. Since the state buffer is a is always less than or equal to both self and read. Since the state buffer is a
ring, other is the first frame that it's unsafe to overwrite. ring, other is the first frame that it's unsafe to overwrite.
Read is where it's read up to, which can be slightly ahead of other since it Unread is the first frame at which not all players' data has been read, which
can't always immediately act upon new data. can be slightly ahead of other since it can't always immediately act upon new
data.
In general, other ≤ read and other ≤ self. In all likelihood, read ≤ self, but In general, other ≤ unread and other ≤ self. In all likelihood, unread ≤ self,
it is both possible and supported for the remote host to get ahead of the local but it is both possible and supported for the remote host to get ahead of the
host. local host.
The server has a slightly more complicated job as it can handle multiple
clients, however it is not vastly more complicated: For each connection which
is playing (i.e., has a controller), it maintains a per-player unread frame,
and the global unread frame is the earliest of each player unread frame. The
server forwards input data: When input data is received from an earlier frame
than the server's current frame, it forwards it immediately. Otherwise, it
forwards it when the frame is reached. i.e., during frame n, the server may
send its own and any number of other players' data for frame n, but will never
send frame n+1. This is because the server's clock is the arbiter of all
synchronization-related events, such as flipping players, players joining and
parting, and saving/loading states.
Pre-frame, Netplay serializes the core's state, polls for local input, and Pre-frame, Netplay serializes the core's state, polls for local input, and
polls for input from the other side. If the input from the other side is too polls for input from the network. If the input from the network is too far
far behind, it stalls to allow the other side to catch up. To assure that this behind (i.e., unread is too far behind self), it stalls to allow the other side
stalling does not block the UI thread, it is implemented similarly to pausing, to catch up. To assure that this stalling does not block the UI thread, it is
rather than by blocking on the socket. implemented similarly to pausing, rather than by blocking on the socket.
If input has not been received for the other side up to the current frame (the If input has not been received for the other side up to the current frame (the
usual case), the remote input is simulated in a simplistic manner. Each usual case), the remote input is simulated in a simplistic manner. Each
@ -78,6 +87,17 @@ two relevant actions: Reading the data and emulating with the data. The frame
count is only incremented after the latter, so there is a period of time during count is only incremented after the latter, so there is a period of time during
which we've actually read self_frame_count+1 frames of local input. which we've actually read self_frame_count+1 frames of local input.
Clients may come and go, and may start or stop playing even as they're
connected. A client that is not playing is said to be “spectating”: It receives
all the same data but sends none. A client may switch from spectating to
playing by sending the appropriate request, at which point it is allotted a
player number (see the SPECTATE, PLAY and MODE commands below).
The server may also be in spectator mode, but as the server never sends data
early (i.e., it only forwards data on the frame it's reached), it must also
inform all clients of its own current frame even if it has no input. The
NOINPUT command is provided for that purpose.
* Guarantee not actually a guarantee. * Guarantee not actually a guarantee.
@ -122,6 +142,15 @@ Description:
synchronization point: No synchronization events from the given frame may synchronization point: No synchronization events from the given frame may
arrive after the server's input for the frame. arrive after the server's input for the frame.
Command: NOINPUT
Payload:
{
frame number: uint32
}
Description:
Sent by the server to indicate a frame has passed when the server is not
otherwise sending data.
Command: NICK Command: NICK
Payload: Payload:
{ {