mirror of
https://github.com/libretro/RetroArch
synced 2025-04-01 04:20:27 +00:00
Enhance netpacket interface
- Enable core host to refuse connecting new players to limit the number of connected players - Enable a core to flush outgoing packets and read incoming packets without waiting for the next frame (can be used for lower latency or blocking reads)
This commit is contained in:
parent
2546ac6bbc
commit
e8283363fb
@ -1811,26 +1811,26 @@ enum retro_mod
|
|||||||
|
|
||||||
#define RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE 76
|
#define RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE 76
|
||||||
/* const struct retro_netpacket_callback * --
|
/* const struct retro_netpacket_callback * --
|
||||||
* When set, a core gets control over network packets sent and
|
* When set, a core gains control over network packets sent and
|
||||||
* received during a multiplayer session. This can be used to emulate
|
* received during a multiplayer session. This can be used to
|
||||||
* multiplayer games that were originally played on 2 or more separate
|
* emulate multiplayer games that were originally played on two
|
||||||
* consoles or computers connected together.
|
* or more separate consoles or computers connected together.
|
||||||
*
|
*
|
||||||
* The frontend will take care of connecting players together.
|
* The frontend will take care of connecting players together,
|
||||||
* The core only needs to send the actual data as needed for the
|
* and the core only needs to send the actual data as needed for
|
||||||
* emulation while handshake and connection management happens in
|
* the emulation, while handshake and connection management happen
|
||||||
* the background.
|
* in the background.
|
||||||
*
|
*
|
||||||
* When 2 or more players are connected and this interface has been
|
* When two or more players are connected and this interface has
|
||||||
* set, time manipulation features (pausing, slow motion, fast forward,
|
* been set, time manipulation features (such as pausing, slow motion,
|
||||||
* rewinding, save state loading, etc.) are disabled to not interrupt
|
* fast forward, rewinding, save state loading, etc.) are disabled to
|
||||||
* communication.
|
* avoid interrupting communication.
|
||||||
*
|
*
|
||||||
* Should be set in either retro_init or retro_load_game, but not both.
|
* Should be set in either retro_init or retro_load_game, but not both.
|
||||||
*
|
*
|
||||||
* When not set, a frontend may use state serialization based
|
* When not set, a frontend may use state serialization-based
|
||||||
* multiplayer where a deterministic core supporting multiple
|
* multiplayer, where a deterministic core supporting multiple
|
||||||
* input devices does not need to do anything on its own.
|
* input devices does not need to take any action on its own.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* VFS functionality */
|
/* VFS functionality */
|
||||||
@ -3065,16 +3065,23 @@ struct retro_disk_control_ext_callback
|
|||||||
#define RETRO_NETPACKET_UNSEQUENCED (1 << 1) /* Packet will not be sequenced with other packets and may arrive out of order. Cannot be set on reliable packets. */
|
#define RETRO_NETPACKET_UNSEQUENCED (1 << 1) /* Packet will not be sequenced with other packets and may arrive out of order. Cannot be set on reliable packets. */
|
||||||
|
|
||||||
/* Used by the core to send a packet to one or more connected players.
|
/* Used by the core to send a packet to one or more connected players.
|
||||||
* A single packet sent via this interface can contain up to 64kb of data.
|
* A single packet sent via this interface can contain up to 64 KB of data.
|
||||||
*
|
*
|
||||||
* The broadcast flag can be set to true to send to multiple connected clients.
|
* The broadcast flag can be set to true to send to multiple connected clients.
|
||||||
* On a broadcast, the client_id argument indicates 1 client NOT to send the
|
* In a broadcast, the client_id argument indicates 1 client NOT to send the
|
||||||
* packet to (pass 0xFFFF to send to everyone). Otherwise, the client_id
|
* packet to (pass 0xFFFF to send to everyone). Otherwise, the client_id
|
||||||
* argument indicates a single client to send the packet to.
|
* argument indicates a single client to send the packet to.
|
||||||
*
|
*
|
||||||
* A frontend must support sending of reliable packets (RETRO_NETPACKET_RELIABLE).
|
* A frontend must support sending reliable packets (RETRO_NETPACKET_RELIABLE).
|
||||||
* Unreliable packets might not be supported by the frontend but the flags can
|
* Unreliable packets might not be supported by the frontend, but the flags can
|
||||||
* still be specified, reliable transmission will be used instead.
|
* still be specified. Reliable transmission will be used instead.
|
||||||
|
*
|
||||||
|
* If this function is called passing NULL for buf, it will instead flush all
|
||||||
|
* previously buffered outgoing packets and instantly read any incoming packets.
|
||||||
|
* During such a call, retro_netpacket_receive_t and retro_netpacket_stop_t can
|
||||||
|
* be called. The core can perform this in a loop to do a blocking read, i.e.,
|
||||||
|
* wait for incoming data, but needs to handle stop getting called and also
|
||||||
|
* give up after a short while to avoid freezing on a connection problem.
|
||||||
*
|
*
|
||||||
* This function is not guaranteed to be thread-safe and must be called during
|
* This function is not guaranteed to be thread-safe and must be called during
|
||||||
* retro_run or any of the netpacket callbacks passed with this interface.
|
* retro_run or any of the netpacket callbacks passed with this interface.
|
||||||
@ -3088,14 +3095,14 @@ typedef void (RETRO_CALLCONV *retro_netpacket_send_t)(int flags, const void* buf
|
|||||||
* If client_id is > 0 the local player is a client connected to a host and
|
* If client_id is > 0 the local player is a client connected to a host and
|
||||||
* at this point is already fully connected to the host.
|
* at this point is already fully connected to the host.
|
||||||
*
|
*
|
||||||
* The core will have to store the retro_netpacket_send_t function pointer
|
* The core must store the retro_netpacket_send_t function pointer provided
|
||||||
* passed here and use it whenever it wants to send a packet. That send
|
* here and use it whenever it wants to send a packet. This function pointer
|
||||||
* function pointer is valid until the frontend calls retro_netpacket_stop_t.
|
* remains valid until the frontend calls retro_netpacket_stop_t.
|
||||||
*/
|
*/
|
||||||
typedef void (RETRO_CALLCONV *retro_netpacket_start_t)(uint16_t client_id, retro_netpacket_send_t send_fn);
|
typedef void (RETRO_CALLCONV *retro_netpacket_start_t)(uint16_t client_id, retro_netpacket_send_t send_fn);
|
||||||
|
|
||||||
/* Called by the frontend when a new packet arrives which has been sent from
|
/* Called by the frontend when a new packet arrives which has been sent from
|
||||||
* another peer with retro_netpacket_send_t. The client_id argument indicates
|
* another player with retro_netpacket_send_t. The client_id argument indicates
|
||||||
* who has sent the packet.
|
* who has sent the packet.
|
||||||
*/
|
*/
|
||||||
typedef void (RETRO_CALLCONV *retro_netpacket_receive_t)(const void* buf, size_t len, uint16_t client_id);
|
typedef void (RETRO_CALLCONV *retro_netpacket_receive_t)(const void* buf, size_t len, uint16_t client_id);
|
||||||
@ -3114,8 +3121,10 @@ typedef void (RETRO_CALLCONV *retro_netpacket_poll_t)(void);
|
|||||||
|
|
||||||
/* Called by the frontend when a new player connects to the hosted session.
|
/* Called by the frontend when a new player connects to the hosted session.
|
||||||
* This is only called on the host side, not for clients connected to the host.
|
* This is only called on the host side, not for clients connected to the host.
|
||||||
|
* If this function returns false, the newly connected player gets dropped.
|
||||||
|
* This can be used for example to limit the number of players.
|
||||||
*/
|
*/
|
||||||
typedef void (RETRO_CALLCONV *retro_netpacket_connected_t)(uint16_t client_id);
|
typedef bool (RETRO_CALLCONV *retro_netpacket_connected_t)(uint16_t client_id);
|
||||||
|
|
||||||
/* Called by the frontend when a player leaves or disconnects from the hosted session.
|
/* Called by the frontend when a player leaves or disconnects from the hosted session.
|
||||||
* This is only called on the host side, not for clients connected to the host.
|
* This is only called on the host side, not for clients connected to the host.
|
||||||
|
@ -4451,7 +4451,7 @@ static uint8_t netplay_settings_share_mode(
|
|||||||
static void announce_play_spectate(netplay_t *netplay,
|
static void announce_play_spectate(netplay_t *netplay,
|
||||||
const char *nick,
|
const char *nick,
|
||||||
enum rarch_netplay_connection_mode mode, uint32_t devices,
|
enum rarch_netplay_connection_mode mode, uint32_t devices,
|
||||||
int32_t ping)
|
int32_t ping, uint32_t client_num)
|
||||||
{
|
{
|
||||||
char msg[512];
|
char msg[512];
|
||||||
const char *dmsg = NULL;
|
const char *dmsg = NULL;
|
||||||
@ -4478,16 +4478,23 @@ static void announce_play_spectate(netplay_t *netplay,
|
|||||||
uint32_t one_device = (uint32_t) -1;
|
uint32_t one_device = (uint32_t) -1;
|
||||||
char *pdevice_str = NULL;
|
char *pdevice_str = NULL;
|
||||||
|
|
||||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
if (netplay->modus == NETPLAY_MODUS_CORE_PACKET_INTERFACE)
|
||||||
{
|
{
|
||||||
if (!(devices & (1<<device)))
|
one_device = client_num;
|
||||||
continue;
|
}
|
||||||
if (one_device == (uint32_t) -1)
|
else
|
||||||
one_device = device;
|
{
|
||||||
else
|
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||||
{
|
{
|
||||||
one_device = (uint32_t) -1;
|
if (!(devices & (1<<device)))
|
||||||
break;
|
continue;
|
||||||
|
if (one_device == (uint32_t) -1)
|
||||||
|
one_device = device;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
one_device = (uint32_t) -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4598,7 +4605,7 @@ static void handle_play_spectate(netplay_t *netplay,
|
|||||||
NETPLAY_CMD_MODE, &payload, sizeof(payload));
|
NETPLAY_CMD_MODE, &payload, sizeof(payload));
|
||||||
|
|
||||||
announce_play_spectate(netplay, connection->nick,
|
announce_play_spectate(netplay, connection->nick,
|
||||||
NETPLAY_CONNECTION_SPECTATING, 0, -1);
|
NETPLAY_CONNECTION_SPECTATING, 0, -1, client_num);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -4609,7 +4616,7 @@ static void handle_play_spectate(netplay_t *netplay,
|
|||||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||||
|
|
||||||
announce_play_spectate(netplay, NULL,
|
announce_play_spectate(netplay, NULL,
|
||||||
NETPLAY_CONNECTION_SPECTATING, 0, -1);
|
NETPLAY_CONNECTION_SPECTATING, 0, -1, client_num);
|
||||||
|
|
||||||
/* It was the server, so tell everyone else */
|
/* It was the server, so tell everyone else */
|
||||||
netplay_send_raw_cmd_all(netplay, NULL,
|
netplay_send_raw_cmd_all(netplay, NULL,
|
||||||
@ -4643,7 +4650,12 @@ static void handle_play_spectate(netplay_t *netplay,
|
|||||||
share_mode &= ~NETPLAY_SHARE_NO_PREFERENCE;
|
share_mode &= ~NETPLAY_SHARE_NO_PREFERENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (devices)
|
if (netplay->modus == NETPLAY_MODUS_CORE_PACKET_INTERFACE)
|
||||||
|
{
|
||||||
|
/* no device needs to be assigned with netpacket interface */
|
||||||
|
devices = 0;
|
||||||
|
}
|
||||||
|
else if (devices)
|
||||||
{
|
{
|
||||||
/* Make sure the devices are available and/or shareable */
|
/* Make sure the devices are available and/or shareable */
|
||||||
for (i = 0; i < MAX_INPUT_DEVICES; i++)
|
for (i = 0; i < MAX_INPUT_DEVICES; i++)
|
||||||
@ -4800,17 +4812,27 @@ static void handle_play_spectate(netplay_t *netplay,
|
|||||||
|
|
||||||
payload.mode = htonl(mode | NETPLAY_CMD_MODE_BIT_YOU);
|
payload.mode = htonl(mode | NETPLAY_CMD_MODE_BIT_YOU);
|
||||||
|
|
||||||
|
if (networking_driver_st.core_netpacket_interface
|
||||||
|
&& networking_driver_st.core_netpacket_interface->connected
|
||||||
|
&& !networking_driver_st.core_netpacket_interface->connected(
|
||||||
|
(uint16_t)(connection - netplay->connections + 1)))
|
||||||
|
{
|
||||||
|
/* core wants us to drop this new client */
|
||||||
|
connection->mode = NETPLAY_CONNECTION_CONNECTED;
|
||||||
|
uint32_t reason = htonl(
|
||||||
|
NETPLAY_CMD_MODE_REFUSED_REASON_OTHER);
|
||||||
|
netplay_send_raw_cmd(netplay, connection,
|
||||||
|
NETPLAY_CMD_MODE_REFUSED, &reason, sizeof(reason));
|
||||||
|
netplay_hangup(netplay, connection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tell the player */
|
/* Tell the player */
|
||||||
netplay_send_raw_cmd(netplay, connection,
|
netplay_send_raw_cmd(netplay, connection,
|
||||||
NETPLAY_CMD_MODE, &payload, sizeof(payload));
|
NETPLAY_CMD_MODE, &payload, sizeof(payload));
|
||||||
|
|
||||||
announce_play_spectate(netplay, connection->nick,
|
announce_play_spectate(netplay, connection->nick,
|
||||||
connection->mode, devices, connection->ping);
|
connection->mode, devices, connection->ping, client_num);
|
||||||
|
|
||||||
if (networking_driver_st.core_netpacket_interface
|
|
||||||
&& networking_driver_st.core_netpacket_interface->connected)
|
|
||||||
networking_driver_st.core_netpacket_interface->connected
|
|
||||||
((uint16_t)(connection - netplay->connections + 1));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -4828,7 +4850,7 @@ static void handle_play_spectate(netplay_t *netplay,
|
|||||||
netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
|
netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
|
||||||
|
|
||||||
announce_play_spectate(netplay, NULL,
|
announce_play_spectate(netplay, NULL,
|
||||||
netplay->self_mode, devices, -1);
|
netplay->self_mode, devices, -1, client_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.mode = htonl(mode);
|
payload.mode = htonl(mode);
|
||||||
@ -5632,7 +5654,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||||||
|
|
||||||
/* Announce it */
|
/* Announce it */
|
||||||
announce_play_spectate(netplay, NULL, netplay->self_mode, devices,
|
announce_play_spectate(netplay, NULL, netplay->self_mode, devices,
|
||||||
connection->ping);
|
connection->ping, client_num);
|
||||||
|
|
||||||
#ifdef DEBUG_NETPLAY_STEPS
|
#ifdef DEBUG_NETPLAY_STEPS
|
||||||
RARCH_LOG("[Netplay] Received mode change self->%X\n", devices);
|
RARCH_LOG("[Netplay] Received mode change self->%X\n", devices);
|
||||||
@ -5677,7 +5699,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||||||
netplay->read_frame_count[client_num] = netplay->server_frame_count;
|
netplay->read_frame_count[client_num] = netplay->server_frame_count;
|
||||||
|
|
||||||
/* Announce it */
|
/* Announce it */
|
||||||
announce_play_spectate(netplay, nick, NETPLAY_CONNECTION_PLAYING, devices, -1);
|
announce_play_spectate(netplay, nick, NETPLAY_CONNECTION_PLAYING, devices, -1, client_num);
|
||||||
|
|
||||||
#ifdef DEBUG_NETPLAY_STEPS
|
#ifdef DEBUG_NETPLAY_STEPS
|
||||||
RARCH_LOG("[Netplay] Received mode change %u->%u\n", client_num, devices);
|
RARCH_LOG("[Netplay] Received mode change %u->%u\n", client_num, devices);
|
||||||
@ -5692,7 +5714,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||||||
netplay->device_clients[device] &= ~(1<<client_num);
|
netplay->device_clients[device] &= ~(1<<client_num);
|
||||||
|
|
||||||
/* Announce it */
|
/* Announce it */
|
||||||
announce_play_spectate(netplay, nick, NETPLAY_CONNECTION_SPECTATING, 0, -1);
|
announce_play_spectate(netplay, nick, NETPLAY_CONNECTION_SPECTATING, 0, -1, client_num);
|
||||||
|
|
||||||
#ifdef DEBUG_NETPLAY_STEPS
|
#ifdef DEBUG_NETPLAY_STEPS
|
||||||
RARCH_LOG("[Netplay] Received mode change %u->spectator\n", client_num);
|
RARCH_LOG("[Netplay] Received mode change %u->spectator\n", client_num);
|
||||||
@ -5970,7 +5992,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case NETPLAY_CMD_RESET:
|
case NETPLAY_CMD_RESET:
|
||||||
{
|
{DBP_ASSERT_MODUS(NETPLAY_MODUS_INPUT_FRAME_SYNC)
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint32_t frame;
|
uint32_t frame;
|
||||||
size_t reset_ptr;
|
size_t reset_ptr;
|
||||||
@ -7418,7 +7440,8 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
|
|||||||
if (!netplay->is_server)
|
if (!netplay->is_server)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
uint32_t client_mask = ~(1 << netplay->self_client_num);
|
uint32_t client_num = netplay->self_client_num;
|
||||||
|
uint32_t client_mask = ~(1 << client_num);
|
||||||
|
|
||||||
netplay->connected_players &= client_mask;
|
netplay->connected_players &= client_mask;
|
||||||
|
|
||||||
@ -7430,7 +7453,7 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
|
|||||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||||
|
|
||||||
announce_play_spectate(netplay, NULL,
|
announce_play_spectate(netplay, NULL,
|
||||||
NETPLAY_CONNECTION_SPECTATING, 0, -1);
|
NETPLAY_CONNECTION_SPECTATING, 0, -1, client_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING);
|
netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING);
|
||||||
@ -9335,7 +9358,7 @@ bool netplay_decode_hostname(const char *hostname,
|
|||||||
* @buf : packet data pointer
|
* @buf : packet data pointer
|
||||||
* @len : packet data size
|
* @len : packet data size
|
||||||
* @pkt_client_id : source id if host sending to client, otherwise recipient id or excepted id if broadcast
|
* @pkt_client_id : source id if host sending to client, otherwise recipient id or excepted id if broadcast
|
||||||
* @broadcast : pass as true from client if host should relay this to everyone else
|
* @broadcast : pass true on client if host should relay this to everyone else
|
||||||
*
|
*
|
||||||
* Send a netpacket command to a connected peer.
|
* Send a netpacket command to a connected peer.
|
||||||
*/
|
*/
|
||||||
@ -9373,6 +9396,14 @@ static void RETRO_CALLCONV netplay_netpacket_send_cb(int flags,
|
|||||||
netplay_t *netplay = net_st->data;
|
netplay_t *netplay = net_st->data;
|
||||||
if (!netplay) return;
|
if (!netplay) return;
|
||||||
|
|
||||||
|
if (buf == NULL)
|
||||||
|
{
|
||||||
|
/* With NULL this function instead flushes packets and checks incoming */
|
||||||
|
netplay_send_flush_all(netplay, NULL);
|
||||||
|
input_poll_net(netplay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!netplay->is_server)
|
if (!netplay->is_server)
|
||||||
{
|
{
|
||||||
/* client always sends packet to host, host will relay it if needed */
|
/* client always sends packet to host, host will relay it if needed */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user