libretro.h: add a link interface for machines joined by a cable - #19454
Draft
XenuIsWatching wants to merge 1 commit into
Draft
libretro.h: add a link interface for machines joined by a cable#19454XenuIsWatching wants to merge 1 commit into
XenuIsWatching wants to merge 1 commit into
Conversation
A frontend that runs several cores at once has no way to let two of them reach each other. Each instance is generally loaded from its own copy of the shared library, so each has its own copy of every global and no in-core coordinator can be shared between them. The frontend is the only thing they have in common, so the frontend has to host the bus. That is enough to emulate the cable a great many machines shipped with: a Game Boy serial cable, a Game Boy Advance link cable, a GameCube-to-GBA cable, a PlayStation link cable. Today a core that wants this either does without, or emulates both machines itself inside one instance, which only works when both ends are the same core. This is not what RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE does. That joins separate frontend instances over a network and carries packets in real time, and it says so by disabling pause, rewind, fast forward and save state loading while a session is up. This joins core instances inside one process and carries payloads stamped on emulated time, with the frontend deciding how far each core may advance so that a transfer lands on the cycle it is due. The timing is the feature rather than an implementation detail: a link protocol delivered a millisecond late is a link protocol that has failed, which is why it cannot be built on a packet interface with no clock. The frontend never interprets a payload. All knowledge of the wire protocol stays in the cores, which is what lets two DIFFERENT cores emulating two different machines share one cable, and what keeps unrelated cores apart: peers whose protocol_id strings differ are never connected to each other. The grant returned by advance() is required to be a pure function of the participants' published ticks, with no wall-clock input and no timeout. A frontend that violates that desyncs any peer replaying the same inputs and expecting the same result, which is what rollback and lockstep multiplayer both do. The port handle follows the two frontend-hosted resources this header already carries. attach() returns an opaque retro_link_port_t * and every other call takes it, the way retro_vfs_open_t returns a retro_vfs_file_handle * and retro_open_mic_t a retro_microphone_t *. It names a PORT rather than a core, which is why it is needed at all: a machine with more than one socket, and a GameCube has four, has to say which one it means on every call. A frontend that does not implement this returns false, and a core that receives false behaves exactly as a machine with nothing in its socket, which such a core must already do. Cores and frontends can therefore adopt this independently of one another. Definitions only; no behaviour changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A frontend that runs several cores at once has no way to let two of them reach each other. Each instance is generally loaded from its own copy of the shared library, so each has its own copy of every global and no in-core coordinator can be shared between them. The frontend is the only thing they have in common, so the frontend has to host the bus.
That is enough to emulate the cable a great many machines shipped with: a Game Boy serial cable, a Game Boy Advance link cable, a GameCube-to-GBA cable, a PlayStation link cable. Today a core that wants this either does without, or emulates both machines itself inside one instance, which only works when both ends are the same core.
This is not what RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE does. That joins separate frontend instances over a network and carries packets in real time, and it says so by disabling pause, rewind, fast forward and save state loading while a session is up. This joins core instances inside one process and carries payloads stamped on emulated time, with the frontend deciding how far each core may advance so that a transfer lands on the cycle it is due. The timing is the feature rather than an implementation detail: a link protocol delivered a millisecond late is a link protocol that has failed, which is why it cannot be built on a packet interface with no clock.
The frontend never interprets a payload. All knowledge of the wire protocol stays in the cores, which is what lets two DIFFERENT cores emulating two different machines share one cable, and what keeps unrelated cores apart: peers whose protocol_id strings differ are never connected to each other.
The grant returned by advance() is required to be a pure function of the participants' published ticks, with no wall-clock input and no timeout. A frontend that violates that desyncs any peer replaying the same inputs and expecting the same result, which is what rollback and lockstep multiplayer both do.
The port handle follows the two frontend-hosted resources this header already carries. attach() returns an opaque retro_link_port_t * and every other call takes it, the way retro_vfs_open_t returns a retro_vfs_file_handle * and retro_open_mic_t a retro_microphone_t *. It names a PORT rather than a core, which is why it is needed at all: a machine with more than one socket, and a GameCube has four, has to say which one it means on every call.
A frontend that does not implement this returns false, and a core that receives false behaves exactly as a machine with nothing in its socket, which such a core must already do. Cores and frontends can therefore adopt this independently of one another.