Enable clients to send netpackets to other clients

This commit is contained in:
Bernhard Schelling 2023-06-25 02:37:44 +09:00 committed by LibretroAdmin
parent eceb712ca9
commit 6ccef38fea
3 changed files with 122 additions and 53 deletions

View File

@ -1826,6 +1826,8 @@ enum retro_mod
* rewinding, save state loading, etc.) are disabled to not interrupt
* 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.
@ -3052,26 +3054,32 @@ struct retro_disk_control_ext_callback
retro_get_image_label_t get_image_label; /* Optional - may be NULL */
};
/* Callbacks for RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE.
/* Definitions for RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE.
* A core can set it if sending and receiving custom network packets
* during a multiplayer session is desired.
*/
/* Netpacket flags for retro_netpacket_send_t */
#define RETRO_NETPACKET_UNRELIABLE 0 /* Packet to be sent unreliable, depending on network quality it might not arrive. */
#define RETRO_NETPACKET_RELIABLE (1 << 0) /* Reliable packets are guaranteed to arrive at the target in the order they were send. */
#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.
*
* If the ready callback has indicated the local player to be the host:
* - 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 packet to
* - Otherwise, the client_id argument indicates a single client to send the packet to
* If the local player is a client connected to a host:
* - The broadcast flag is ignored
* - The client_id argument must be set to 0
* 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
* 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.
*
* 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.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_send_t)(const void* buf, size_t len, uint16_t client_id, bool broadcast);
typedef void (RETRO_CALLCONV *retro_netpacket_send_t)(int flags, const void* buf, size_t len, uint16_t client_id, bool broadcast);
/* Called by the frontend to signify that a multiplayer session has started.
* If client_id is 0 the local player is the host of the session and at this
@ -3087,12 +3095,8 @@ typedef void (RETRO_CALLCONV *retro_netpacket_send_t)(const void* buf, size_t le
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
* a connected client or the host with retro_netpacket_send_t.
* The client_id argument indicates who has sent the packet. On the host side
* this will always be > 0 (coming from a connected client).
* On a client connected to the host it is always 0 (coming from the host).
* Packets sent with this interface arrive at this callback in a reliable
* manner, meaning in the same order they were sent and without packet loss.
* another peer 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);

View File

@ -675,8 +675,10 @@ static uint32_t simple_rand_uint32(unsigned long *simple_rand_next)
return ((part0 << 30) + (part1 << 15) + part2);
}
static void RETRO_CALLCONV netplay_netpacket_send(const void* buf, size_t len,
uint16_t client_id, bool broadcast);
static void netplay_send_cmd_netpacket(netplay_t *netplay, size_t conn_i,
const void* buf, size_t len, uint16_t client_id, bool broadcast);
static void RETRO_CALLCONV netplay_netpacket_send_cb(int flags,
const void* buf, size_t len, uint16_t client_id, bool broadcast);
/*
* netplay_init_socket_buffer
@ -1883,7 +1885,7 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay,
if (networking_driver_st.core_netpacket_interface &&
networking_driver_st.core_netpacket_interface->start)
networking_driver_st.core_netpacket_interface->start
((uint16_t)netplay->self_client_num, netplay_netpacket_send);
((uint16_t)netplay->self_client_num, netplay_netpacket_send_cb);
/* Ask to switch to playing mode if we should */
if (!settings->bools.netplay_start_as_spectator)
@ -6160,7 +6162,10 @@ static bool netplay_get_cmd(netplay_t *netplay,
}
case NETPLAY_CMD_NETPACKET:
case NETPLAY_CMD_NETPACKET_BROADCAST:
{
uint32_t pkt_client_id;
void* buf = netplay->zbuffer;
if (!networking_driver_st.core_netpacket_interface)
{
RARCH_ERR("[Netplay] NETPLAY_CMD_NETPACKET while core netpacket interface is not set.\n");
@ -6172,15 +6177,46 @@ static bool netplay_get_cmd(netplay_t *netplay,
return netplay_cmd_nak(netplay, connection);
}
RECV(netplay->zbuffer, cmd_size)
RECV(&pkt_client_id, sizeof(pkt_client_id))
return false;
pkt_client_id = ntohl(pkt_client_id);
RECV(buf, cmd_size)
return false;
if (networking_driver_st.core_netpacket_interface->receive)
if (!netplay->is_server)
{
uint16_t client_id = (!netplay->is_server ? (uint16_t)0 :
(uint16_t)(connection - netplay->connections + 1));
networking_driver_st.core_netpacket_interface->receive
(netplay->zbuffer, cmd_size, client_id);
/* packets arriving at a client are always meant for us */
if (networking_driver_st.core_netpacket_interface->receive)
networking_driver_st.core_netpacket_interface->receive
(buf, cmd_size, (uint16_t)pkt_client_id);
}
else
{
bool broadcast = (cmd == NETPLAY_CMD_NETPACKET_BROADCAST);
uint16_t incoming_client_id =
(uint16_t)(connection - netplay->connections + 1);
/* check if this is a packet for the host */
if ((broadcast ? pkt_client_id : !pkt_client_id)
&& networking_driver_st.core_netpacket_interface->receive)
networking_driver_st.core_netpacket_interface->receive
(buf, cmd_size, incoming_client_id);
if (broadcast)
{
/* relay to all but designated client and incoming client */
size_t i, skip1 = pkt_client_id, skip2 = incoming_client_id;
for (i = 0; i < netplay->connections_size; i++)
if (i+1 != skip1 && i+1 != skip2)
netplay_send_cmd_netpacket(netplay, i,
buf, cmd_size, incoming_client_id, false);
}
else if (pkt_client_id && pkt_client_id != incoming_client_id)
{
/* relay unless target is the host or the incoming client */
netplay_send_cmd_netpacket(netplay, pkt_client_id-1,
buf, cmd_size, incoming_client_id, false);
}
}
break;
}
@ -8705,7 +8741,7 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session)
/* Tell a core that uses the netpacket interface that the host is ready */
if (netplay->is_server && net_st->core_netpacket_interface &&
net_st->core_netpacket_interface->start)
net_st->core_netpacket_interface->start(0, netplay_netpacket_send);
net_st->core_netpacket_interface->start(0, netplay_netpacket_send_cb);
return true;
@ -9292,42 +9328,68 @@ bool netplay_decode_hostname(const char *hostname,
return true;
}
static void RETRO_CALLCONV netplay_netpacket_send(const void* buf, size_t len,
uint16_t client_id, bool broadcast)
/**
* netplay_send_cmd_netpacket
* @netplay : pointer to netplay object
* @conn_idx : connection index to send cmd to
* @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
*
* Send a netpacket command to a connected peer.
*/
static void netplay_send_cmd_netpacket(netplay_t *netplay, size_t conn_idx,
const void* buf, size_t len, uint16_t pkt_client_id, bool broadcast)
{
struct netplay_connection *connection;
struct socket_buffer *sbuf;
uint32_t cmdbuf[3];
bool need_flush;
if (conn_idx >= netplay->connections_size) return;
connection = &netplay->connections[conn_idx];
if (!(connection->flags & NETPLAY_CONN_FLAG_ACTIVE)) return;
if (connection->mode != NETPLAY_CONNECTION_PLAYING) return;
cmdbuf[0] = htonl(broadcast
? NETPLAY_CMD_NETPACKET_BROADCAST : NETPLAY_CMD_NETPACKET);
cmdbuf[1] = htonl(len);
cmdbuf[2] = htonl(pkt_client_id);
sbuf = &connection->send_packet_buffer;
need_flush = (buf_remaining(sbuf) < sizeof(cmdbuf)+len);
if ( (need_flush && !netplay_send_flush(sbuf, connection->fd, true))
|| (!netplay_send(sbuf, connection->fd, cmdbuf, sizeof(cmdbuf)))
|| (len && !netplay_send(sbuf, connection->fd, buf, len)))
netplay_hangup(netplay, connection);
}
static void RETRO_CALLCONV netplay_netpacket_send_cb(int flags,
const void* buf, size_t len, uint16_t client_id, bool broadcast)
{
net_driver_state_t *net_st = &networking_driver_st;
netplay_t *netplay = net_st->data;
if (!netplay) return;
if (broadcast && netplay->is_server)
if (!netplay->is_server)
{
size_t i, skip = client_id;
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (i+1 != skip && (connection->flags & NETPLAY_CONN_FLAG_ACTIVE)
&& (connection->mode == NETPLAY_CONNECTION_PLAYING)
&& !netplay_send_raw_cmd(netplay, connection,
NETPLAY_CMD_NETPACKET, buf, len))
netplay_hangup(netplay, connection);
}
/* client always sends packet to host, host will relay it if needed */
netplay_send_cmd_netpacket(netplay, 0, buf, len, client_id, broadcast);
}
else
else if (broadcast)
{
struct netplay_connection *connection;
size_t i = client_id - (netplay->is_server ? 1 : 0);
if (i >= netplay->connections_size)
{
RARCH_ERR("[Netplay] Unable to send netpacket to client id %d.\n",
client_id);
return;
}
connection = &netplay->connections[i];
if ( (connection->flags & NETPLAY_CONN_FLAG_ACTIVE)
&& (connection->mode == NETPLAY_CONNECTION_PLAYING)
&& !netplay_send_raw_cmd(netplay, connection,
NETPLAY_CMD_NETPACKET, buf, len))
netplay_hangup(netplay, connection);
/* send packet to all clients (client_id can be set as exception) */
size_t i;
for (i = 0; i < netplay->connections_size; i++)
if (i+1 != client_id)
netplay_send_cmd_netpacket(netplay, i, buf, len, 0, false);
}
else if (client_id)
{
/* send packet to specific client */
netplay_send_cmd_netpacket(netplay, client_id-1, buf, len, 0, false);
}
}

View File

@ -165,6 +165,9 @@ enum netplay_cmd
/* Send a network packet from the raw packet core interface */
NETPLAY_CMD_NETPACKET = 0x0048,
/* Used by clients to have the host also forward it to other clients */
NETPLAY_CMD_NETPACKET_BROADCAST = 0x0049,
/* Misc. commands */
/* Sends multiple config requests over,