mirror of
https://github.com/libretro/RetroArch
synced 2025-03-29 22:20:21 +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
|
||||
/* const struct retro_netpacket_callback * --
|
||||
* When set, a core gets control over network packets sent and
|
||||
* received during a multiplayer session. This can be used to emulate
|
||||
* multiplayer games that were originally played on 2 or more separate
|
||||
* consoles or computers connected together.
|
||||
* When set, a core gains control over network packets sent and
|
||||
* received during a multiplayer session. This can be used to
|
||||
* emulate multiplayer games that were originally played on two
|
||||
* or more separate consoles or computers connected together.
|
||||
*
|
||||
* The frontend will take care of connecting players together.
|
||||
* The core only needs to send the actual data as needed for the
|
||||
* emulation while handshake and connection management happens in
|
||||
* the background.
|
||||
* The frontend will take care of connecting players together,
|
||||
* and the core only needs to send the actual data as needed for
|
||||
* the emulation, while handshake and connection management happen
|
||||
* in the background.
|
||||
*
|
||||
* When 2 or more players are connected and this interface has been
|
||||
* set, time manipulation features (pausing, slow motion, fast forward,
|
||||
* rewinding, save state loading, etc.) are disabled to not interrupt
|
||||
* communication.
|
||||
* When two or more players are connected and this interface has
|
||||
* been set, time manipulation features (such as pausing, slow motion,
|
||||
* fast forward, rewinding, save state loading, etc.) are disabled to
|
||||
* avoid interrupting communication.
|
||||
*
|
||||
* Should be set in either retro_init or retro_load_game, but not both.
|
||||
*
|
||||
* When not set, a frontend may use state serialization based
|
||||
* multiplayer where a deterministic core supporting multiple
|
||||
* input devices does not need to do anything on its own.
|
||||
* When not set, a frontend may use state serialization-based
|
||||
* multiplayer, where a deterministic core supporting multiple
|
||||
* input devices does not need to take any action on its own.
|
||||
*/
|
||||
|
||||
/* 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. */
|
||||
|
||||
/* 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.
|
||||
* 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
|
||||
* argument indicates a single client to send the packet to.
|
||||
*
|
||||
* A frontend must support sending of reliable packets (RETRO_NETPACKET_RELIABLE).
|
||||
* Unreliable packets might not be supported by the frontend but the flags can
|
||||
* still be specified, reliable transmission will be used instead.
|
||||
* A frontend must support sending reliable packets (RETRO_NETPACKET_RELIABLE).
|
||||
* Unreliable packets might not be supported by the frontend, but the flags can
|
||||
* 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
|
||||
* 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
|
||||
* at this point is already fully connected to the host.
|
||||
*
|
||||
* The core will have to store the retro_netpacket_send_t function pointer
|
||||
* passed here and use it whenever it wants to send a packet. That send
|
||||
* function pointer is valid until the frontend calls retro_netpacket_stop_t.
|
||||
* The core must store the retro_netpacket_send_t function pointer provided
|
||||
* here and use it whenever it wants to send a packet. This function pointer
|
||||
* 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);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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.
|
||||
* 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.
|
||||
* 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,
|
||||
const char *nick,
|
||||
enum rarch_netplay_connection_mode mode, uint32_t devices,
|
||||
int32_t ping)
|
||||
int32_t ping, uint32_t client_num)
|
||||
{
|
||||
char msg[512];
|
||||
const char *dmsg = NULL;
|
||||
@ -4478,16 +4478,23 @@ static void announce_play_spectate(netplay_t *netplay,
|
||||
uint32_t one_device = (uint32_t) -1;
|
||||
char *pdevice_str = NULL;
|
||||
|
||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||
if (netplay->modus == NETPLAY_MODUS_CORE_PACKET_INTERFACE)
|
||||
{
|
||||
if (!(devices & (1<<device)))
|
||||
continue;
|
||||
if (one_device == (uint32_t) -1)
|
||||
one_device = device;
|
||||
else
|
||||
one_device = client_num;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||
{
|
||||
one_device = (uint32_t) -1;
|
||||
break;
|
||||
if (!(devices & (1<<device)))
|
||||
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));
|
||||
|
||||
announce_play_spectate(netplay, connection->nick,
|
||||
NETPLAY_CONNECTION_SPECTATING, 0, -1);
|
||||
NETPLAY_CONNECTION_SPECTATING, 0, -1, client_num);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -4609,7 +4616,7 @@ static void handle_play_spectate(netplay_t *netplay,
|
||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
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);
|
||||
|
||||
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 */
|
||||
netplay_send_raw_cmd(netplay, connection,
|
||||
NETPLAY_CMD_MODE, &payload, sizeof(payload));
|
||||
|
||||
announce_play_spectate(netplay, connection->nick,
|
||||
connection->mode, devices, connection->ping);
|
||||
|
||||
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));
|
||||
connection->mode, devices, connection->ping, client_num);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -4828,7 +4850,7 @@ static void handle_play_spectate(netplay_t *netplay,
|
||||
netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
|
||||
|
||||
announce_play_spectate(netplay, NULL,
|
||||
netplay->self_mode, devices, -1);
|
||||
netplay->self_mode, devices, -1, client_num);
|
||||
}
|
||||
|
||||
payload.mode = htonl(mode);
|
||||
@ -5632,7 +5654,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
|
||||
/* Announce it */
|
||||
announce_play_spectate(netplay, NULL, netplay->self_mode, devices,
|
||||
connection->ping);
|
||||
connection->ping, client_num);
|
||||
|
||||
#ifdef DEBUG_NETPLAY_STEPS
|
||||
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;
|
||||
|
||||
/* 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
|
||||
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);
|
||||
|
||||
/* 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
|
||||
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:
|
||||
{
|
||||
{DBP_ASSERT_MODUS(NETPLAY_MODUS_INPUT_FRAME_SYNC)
|
||||
uint32_t i;
|
||||
uint32_t frame;
|
||||
size_t reset_ptr;
|
||||
@ -7418,7 +7440,8 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
|
||||
if (!netplay->is_server)
|
||||
{
|
||||
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;
|
||||
|
||||
@ -7430,7 +7453,7 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
|
||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
|
||||
announce_play_spectate(netplay, NULL,
|
||||
NETPLAY_CONNECTION_SPECTATING, 0, -1);
|
||||
NETPLAY_CONNECTION_SPECTATING, 0, -1, client_num);
|
||||
}
|
||||
|
||||
netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING);
|
||||
@ -9335,7 +9358,7 @@ bool netplay_decode_hostname(const char *hostname,
|
||||
* @buf : packet data pointer
|
||||
* @len : packet data size
|
||||
* @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.
|
||||
*/
|
||||
@ -9373,6 +9396,14 @@ static void RETRO_CALLCONV netplay_netpacket_send_cb(int flags,
|
||||
netplay_t *netplay = net_st->data;
|
||||
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)
|
||||
{
|
||||
/* client always sends packet to host, host will relay it if needed */
|
||||
|
Loading…
x
Reference in New Issue
Block a user