-
Notifications
You must be signed in to change notification settings - Fork 1
Home
This is a fork of RetroArch intended to fix and improve its Netplay.
- Removed the "here there be dragons" sign that prevented anyone else from looking at this code.
- Added a README that lightly documents how RetroArch's Netplay works.
- Netplay ring buffer hardened to prevent overruns, which previously caused insane desyncs.
- Ripped out UDP. Everything is done over TCP now. The previous implementation demanded reliability and in-order guarantees anyway, so UDP was just silly. Note that the protocol has also changed in more fundamental ways, so it's not really possible to make a "UDP or TCP" option except by having two incompatible implementations of Netplay.
- Removed all limits on the size of the replay buffer. You want to allow 10 seconds of drift? 10 seconds of drift it is!
- Made stalling work without blocking the UI thread.
- General bugfixes to make Netplay bulletproof.
- Remote pausing.
- Stalling without rerunning frames. Netplay is now one of the many reasons the frontend may pause.
- Support for loading state over Netplay. Similarly to remote pausing, because the frontend changes were iffy, they're in netplay-new-features-iffy.
- Frame CRCing as a last resort for sync. Configurable by netplay_check_frames or the --check-frames option.
- Deterministic player flipping.
- Fixed spectator mode. In addition, it's now far more similar to net (normal) mode than it was before.
- Infinite testing, of course!
- Compression of savestates when sent over the network.
- Support for launching Netplay on an existing session. This is half-done for the host side (if you start hosting the client can join any time), but not touched for the client side. Most of the changes are in the menu. If somebody who isn't me is willing to do the menu changes, I'll do the netplay changes, just contact me.
- Maybe: Rewindless mode. This is strictly-wrong Netplay, but with frame CRCing, can work fine. If one disabled both rewinds and frame CRCing, one would be left with ZSNES-style netplay (no attempt at sync)
- Maybe: Support for input grappling (two players controlling the same input device). Useful for e.g. handhelds.
- Probably not: UDP as redundancy for TCP: Still send input over TCP, but send it over UDP as well, and use the UDP data whenever possible. That gets the speed of UDP with the reliability of TCP. Please see the UDP note below.
- Super-future: >2 player netplay. Not as difficult as it seems, just need an array of buffers, frames and replay.
- Super-future: Netplay input support for devices other than the simple retropad.
I really, really don't want to reintroduce UDP. The problem is thus: Certain events, such as player flipping and savestate loading, cause synchronization points, and those synchronization points absolutely must be received in order.
Now, that doesn't make UDP impossible. One could send synchronization events over TCP and input events over UDP. However, that adds a complication:
Right now, Netplay is basically implemented as a ring buffer of frame states. That ring buffer has one read head, one write head and one read/write head: Other, read and self, respectively. 'Read' points to the next frame into which data will be read from the remote peer. Right now, since that data is always in order, we can know with certainty that all synchronization events before 'read' have been actioned, and so it's safe to overwrite 'read' in the ring buffer.
If synchronization events were not in order with respect to input events, this would need to change, and a fourth head, 'readahead', would need to be added. 'Read' would now be "read and no synchronization events may arrive for this point", while 'readahead' is allowed to, well, read ahead.
The relationship between other, self and read in the current implementation must be carefully maintained: Other ≤ self, other ≤ read, and both read and self may block if they would otherwise be required to overwrite 'other' due to the circular nature of the ring buffer.
The relationship becomes remarkably more complicated with four heads. Other ≤ self, other ≤ readahead, read ≤ readahead, and self, read and readahead must all make sure neither to overstep other nor to oversteap read, since read may now lag behind other.
That's all doable. There's nothing preventing it from being done. However, a big problem with the maintenance of the Netplay code up until this point is that nobody understood how it worked. I've simplified and documented the assumptions, but it's still nontrivial. Making it even more complicated is not worth the disputable performance benefit of UDP.
Find here some info on the cores I've tested. In every case I've used delay frames: My new Netplay does not break with any delay frames value unless the core's savestates are broken. For cores marked working that only have one input (e.g. handhelds), naturally the second player can't DO anything, but the ability to flip players makes this still a useful test. This should all be integrated into the RetroArch wiki when a RetroArch release with my new Netplay implementation has been made public.
- bnes
- bsnes_accuracy
- bsnes_balanced
- bsnes_mercury_accuracy
- bsnes_mercury_balanced
- bsnes_mercury_performance
- bsnes_performance
- fceumm
- mednafen_gba: Core barely functions as a GBA emulator, but does keep in sync.
- mednafen_ngp
- mednafen_pce_fast
- meteor
- nestopia
- snes9x
- snes9x2002
- snes9x2005
- snes9x2010
- vbam
- vba_next
- bsnes_cplusplus98: I can't get this core to load at all, let alone with netplay.
- quicknes: Well, semi-working. Savestating seems inconsistent and sometimes fails (!). I haven't managed to cause desync, but check_frames is a disaster and almost invariably one side or the other will switch to delay_frames=0 due to a savestate failure at some point.
- genesis_plus_gx: “Content SRAM sizes do not correspond.”, in spite of having the same content. Very odd.
- picodrive: Same as genesis_plus_gx. Perhaps SRAM size is only determined a few frames in or similar?
- gpsp: The second player just seems to have no interest in actually transmitting the server's input. Maybe it bypasses the frontend's input somehow?
- handy: Savestates don't work reliably. If they were disabled outright, ironically, it would work (in delay_frames=0 mode)
- mednafen_psx_hw: Savestates are very fussy. Starting in host mode causes the emulator to immediately quit (call exit) when it savestates at frame 1.
Nonworking because of non-retropad input:
- 81
- bluemsx
- desmume
- dosbox
- fmsx
- fuse
- hatari
These are cores that didn't work for me regardless of netplay.
- mednafen_lynx
- pcsx1
- pcsx_rearmed
- 4do
- fbalpha2012
- gw
- lutro
- mame
- mame2003
- mame2014
- mednafen_pcfx
- mednafen_supergrafx
- mednafen_vb
- mednafen_wswan
- mupen64plus
- nxengine
- o2em
- ppsspp
- prboom
- prosystem
- scummvm
- stella
- stonesoup
- tgbdual
- tyrquake
- vecx
- virtualjaguar
- yabause