mirror of
https://github.com/libretro/RetroArch
synced 2025-04-10 06:44:27 +00:00
Updating Netplay README to be more true
This commit is contained in:
parent
f6f9905ae3
commit
3d7f1f6575
@ -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:
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user