mirror of
https://github.com/libretro/RetroArch
synced 2025-03-29 22:20:21 +00:00
Enable clients to send netpackets to other clients
This commit is contained in:
parent
eceb712ca9
commit
6ccef38fea
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user