mirror of
https://github.com/libretro/RetroArch.git
synced 2025-01-18 23:04:25 +00:00
Add netpacket interface
Adds a new libretro interface for a core to send and receive custom network packets for implementing a communication based multiplayer system instead of using the default state serialization based multiplayer. Connection management is still done by the frontend while a core gains the ability to easily support tunneling of multi-console data communication traffic.
This commit is contained in:
parent
3c1c58cfc3
commit
c60878a10d
@ -1809,6 +1809,28 @@ enum retro_mod
|
||||
* even before the microphone driver is ready.
|
||||
*/
|
||||
|
||||
#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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 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.
|
||||
*/
|
||||
|
||||
/* VFS functionality */
|
||||
|
||||
/* File paths:
|
||||
@ -3030,6 +3052,89 @@ 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.
|
||||
* A core can set it if sending and receiving custom network packets
|
||||
* during a multiplayer session is desired.
|
||||
*/
|
||||
|
||||
/* 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
|
||||
*
|
||||
* 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);
|
||||
|
||||
/* 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
|
||||
* point no other player has connected yet.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
typedef void (RETRO_CALLCONV *retro_netpacket_receive_t)(const void* buf, size_t len, uint16_t client_id);
|
||||
|
||||
/* Called by the frontend when the multiplayer session has ended.
|
||||
* Once this gets called the retro_netpacket_send_t function pointer passed
|
||||
* to retro_netpacket_start_t will not be valid anymore.
|
||||
*/
|
||||
typedef void (RETRO_CALLCONV *retro_netpacket_stop_t)(void);
|
||||
|
||||
/* Called by the frontend every frame (between calls to retro_run while
|
||||
* updating the state of the multiplayer session.
|
||||
* This is a good place for the core to call retro_netpacket_send_t from.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
typedef void (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.
|
||||
*/
|
||||
typedef void (RETRO_CALLCONV *retro_netpacket_disconnected_t)(uint16_t client_id);
|
||||
|
||||
/**
|
||||
* A callback interface for giving a core the ability to send and receive custom
|
||||
* network packets during a multiplayer session between two or more instances
|
||||
* of a libretro frontend.
|
||||
*
|
||||
* @see RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE
|
||||
*/
|
||||
struct retro_netpacket_callback
|
||||
{
|
||||
retro_netpacket_start_t start;
|
||||
retro_netpacket_receive_t receive;
|
||||
retro_netpacket_stop_t stop; /* Optional - may be NULL */
|
||||
retro_netpacket_poll_t poll; /* Optional - may be NULL */
|
||||
retro_netpacket_connected_t connected; /* Optional - may be NULL */
|
||||
retro_netpacket_disconnected_t disconnected; /* Optional - may be NULL */
|
||||
};
|
||||
|
||||
enum retro_pixel_format
|
||||
{
|
||||
/* 0RGB1555, native endian.
|
||||
|
@ -147,6 +147,7 @@ typedef struct
|
||||
struct netplay_room host_room;
|
||||
struct netplay_room *room_list;
|
||||
struct netplay_rooms *rooms_data;
|
||||
struct retro_netpacket_callback *core_netpacket_interface;
|
||||
/* Used while Netplay is running */
|
||||
netplay_t *data;
|
||||
netplay_client_info_t *client_info;
|
||||
|
@ -74,7 +74,10 @@ enum rarch_netplay_ctl_state
|
||||
RARCH_NETPLAY_CTL_DESYNC_PUSH,
|
||||
RARCH_NETPLAY_CTL_DESYNC_POP,
|
||||
RARCH_NETPLAY_CTL_KICK_CLIENT,
|
||||
RARCH_NETPLAY_CTL_BAN_CLIENT
|
||||
RARCH_NETPLAY_CTL_BAN_CLIENT,
|
||||
RARCH_NETPLAY_CTL_SET_CORE_PACKET_INTERFACE,
|
||||
RARCH_NETPLAY_CTL_SKIP_NETPLAY_CALLBACKS,
|
||||
RARCH_NETPLAY_CTL_ALLOW_TIMESKIP
|
||||
};
|
||||
|
||||
/* The current status of a connection */
|
||||
|
@ -675,6 +675,9 @@ 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);
|
||||
|
||||
/*
|
||||
* netplay_init_socket_buffer
|
||||
*
|
||||
@ -1876,6 +1879,12 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay,
|
||||
|
||||
netplay->next_ping = cpu_features_get_time_usec() + NETPLAY_PING_AFTER;
|
||||
|
||||
/* Tell a core that uses the netpacket interface that the client is ready */
|
||||
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);
|
||||
|
||||
/* Ask to switch to playing mode if we should */
|
||||
if (!settings->bools.netplay_start_as_spectator)
|
||||
return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
|
||||
@ -3541,7 +3550,8 @@ static bool netplay_sync_pre_frame(netplay_t *netplay)
|
||||
bool ret = true;
|
||||
|
||||
if (netplay->run_frame_count > 0 && netplay_delta_frame_ready(netplay,
|
||||
&netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
|
||||
&netplay->buffer[netplay->run_ptr], netplay->run_frame_count)
|
||||
&& !networking_driver_st.core_netpacket_interface)
|
||||
{
|
||||
/* Don't serialize until it's safe. */
|
||||
if (!(netplay->quirks & NETPLAY_QUIRK_INITIALIZATION))
|
||||
@ -3664,6 +3674,10 @@ static void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
{
|
||||
uint32_t lo_frame_count, hi_frame_count;
|
||||
|
||||
/* When a core uses the netpacket interface frames are not synced */
|
||||
if (networking_driver_st.core_netpacket_interface)
|
||||
return;
|
||||
|
||||
/* Unless we're stalling, we've just finished running a frame */
|
||||
if (!stalled)
|
||||
{
|
||||
@ -3748,8 +3762,9 @@ static void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
#endif
|
||||
|
||||
/* Now replay the real input if we've gotten ahead of it */
|
||||
if (netplay->force_rewind ||
|
||||
if ((netplay->force_rewind ||
|
||||
netplay->replay_frame_count < netplay->run_frame_count)
|
||||
&& !networking_driver_st.core_netpacket_interface)
|
||||
{
|
||||
retro_ctx_serialize_info_t serial_info;
|
||||
|
||||
@ -4067,6 +4082,12 @@ static void netplay_hangup(netplay_t *netplay,
|
||||
#endif
|
||||
}
|
||||
|
||||
if (networking_driver_st.core_netpacket_interface
|
||||
&& was_playing && netplay->is_server
|
||||
&& networking_driver_st.core_netpacket_interface->disconnected)
|
||||
networking_driver_st.core_netpacket_interface->disconnected
|
||||
((uint16_t)(connection - netplay->connections + 1));
|
||||
|
||||
RARCH_LOG("[Netplay] %s\n", dmsg);
|
||||
/* This notification is really only important to the server if the client was playing.
|
||||
* Let it be optional if server and the client wasn't playing. */
|
||||
@ -4180,6 +4201,10 @@ static bool send_input_frame(netplay_t *netplay, struct delta_frame *dframe,
|
||||
buffer[2] = htonl(dframe->frame);
|
||||
buffer[3] = htonl(client_num);
|
||||
|
||||
/* When a core uses the netpacket interface input is not shared */
|
||||
if (networking_driver_st.core_netpacket_interface)
|
||||
return true;
|
||||
|
||||
/* Add the device data */
|
||||
devices = netplay->client_devices[client_num];
|
||||
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
||||
@ -4749,7 +4774,10 @@ static void handle_play_spectate(netplay_t *netplay,
|
||||
|
||||
if (settings->bools.netplay_allow_slaves)
|
||||
{
|
||||
if (settings->bools.netplay_require_slaves)
|
||||
/* Slave mode unused when core uses netpacket interface */
|
||||
if (networking_driver_st.core_netpacket_interface)
|
||||
slave = false;
|
||||
else if (settings->bools.netplay_require_slaves)
|
||||
slave = true;
|
||||
else
|
||||
slave = (mode & NETPLAY_CMD_PLAY_BIT_SLAVE) ?
|
||||
@ -4783,6 +4811,11 @@ static void handle_play_spectate(netplay_t *netplay,
|
||||
|
||||
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));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -5511,7 +5544,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
/* A change to me! */
|
||||
if (mode & NETPLAY_CMD_MODE_BIT_PLAYING)
|
||||
{
|
||||
if (frame != netplay->server_frame_count)
|
||||
/* When a core uses the netpacket interface this is valid */
|
||||
if (frame != netplay->server_frame_count
|
||||
&& !networking_driver_st.core_netpacket_interface)
|
||||
{
|
||||
RARCH_ERR("[Netplay] Received mode change out of order.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
@ -5626,7 +5661,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
/* Somebody else is joining or parting */
|
||||
if (mode & NETPLAY_CMD_MODE_BIT_PLAYING)
|
||||
{
|
||||
if (frame != netplay->server_frame_count)
|
||||
/* When a core uses the netpacket interface this is valid */
|
||||
if (frame != netplay->server_frame_count
|
||||
&& !networking_driver_st.core_netpacket_interface)
|
||||
{
|
||||
RARCH_ERR("[Netplay] Received mode change out of order.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
@ -6126,6 +6163,32 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_CMD_NETPACKET:
|
||||
{
|
||||
if (!networking_driver_st.core_netpacket_interface)
|
||||
{
|
||||
RARCH_ERR("[Netplay] NETPLAY_CMD_NETPACKET while core netpacket interface is not set.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
if (cmd_size > netplay->zbuffer_size)
|
||||
{
|
||||
RARCH_ERR("[Netplay] Received netpacket of unexpected size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
RECV(netplay->zbuffer, cmd_size)
|
||||
return false;
|
||||
|
||||
if (networking_driver_st.core_netpacket_interface->receive)
|
||||
{
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_CMD_PLAYER_CHAT:
|
||||
{
|
||||
char nickname[NETPLAY_NICK_LEN];
|
||||
@ -6742,15 +6805,23 @@ static bool netplay_init_socket_buffers(netplay_t *netplay)
|
||||
|
||||
static bool netplay_init_serialization(netplay_t *netplay)
|
||||
{
|
||||
size_t i, info_size;
|
||||
size_t i;
|
||||
|
||||
if (netplay->state_size)
|
||||
return true;
|
||||
|
||||
info_size = core_serialize_size_special();
|
||||
if (!info_size)
|
||||
return false;
|
||||
netplay->state_size = info_size;
|
||||
if (networking_driver_st.core_netpacket_interface)
|
||||
{
|
||||
/* max core netpacket size is 64 kb, hold 2 of them */
|
||||
netplay->state_size = 64*1024*2;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t info_size = core_serialize_size_special();
|
||||
if (!info_size)
|
||||
return false;
|
||||
netplay->state_size = info_size;
|
||||
}
|
||||
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
{
|
||||
@ -7127,6 +7198,12 @@ static void netplay_frontend_paused(netplay_t *netplay, bool paused)
|
||||
size_t i;
|
||||
uint32_t paused_ct = 0;
|
||||
|
||||
/* When a core uses the netpacket interface netplay doesn't control pause.
|
||||
* We need this because even if RARCH_NETPLAY_CTL_ALLOW_PAUSE returns false
|
||||
* on some platforms the frontend may try to force netplay to pause. */
|
||||
if (networking_driver_st.core_netpacket_interface)
|
||||
return;
|
||||
|
||||
netplay->local_paused = paused;
|
||||
|
||||
/* Communicating this is a bit odd: If exactly one other connection is
|
||||
@ -7250,6 +7327,10 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
{
|
||||
retro_ctx_serialize_info_t tmp_serial_info = {0};
|
||||
|
||||
/* When a core uses the netpacket interface save states are not shared */
|
||||
if (networking_driver_st.core_netpacket_interface)
|
||||
return;
|
||||
|
||||
if (!serial_info)
|
||||
save = true;
|
||||
|
||||
@ -7581,6 +7662,18 @@ static bool netplay_poll(netplay_t *netplay, bool block_libretro_input)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* Use simplified polling loop when a core uses the netpacket interface. */
|
||||
if (networking_driver_st.core_netpacket_interface)
|
||||
{
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_NONE)
|
||||
return true;
|
||||
netplay_poll_net_input(netplay);
|
||||
if (networking_driver_st.core_netpacket_interface->poll
|
||||
&& netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
|
||||
networking_driver_st.core_netpacket_interface->poll();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!get_self_input_state(block_libretro_input, netplay))
|
||||
goto catastrophe;
|
||||
|
||||
@ -8442,6 +8535,10 @@ void deinit_netplay(void)
|
||||
/* Reinitialize preemptive frames if enabled */
|
||||
preempt_init(runloop_state_get_ptr());
|
||||
#endif
|
||||
|
||||
if (net_st->core_netpacket_interface
|
||||
&& net_st->core_netpacket_interface->stop)
|
||||
net_st->core_netpacket_interface->stop();
|
||||
}
|
||||
|
||||
free(net_st->client_info);
|
||||
@ -8471,9 +8568,10 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session)
|
||||
|
||||
serialization_quirks = core_serialization_quirks();
|
||||
|
||||
if (!core_info_current_supports_netplay() ||
|
||||
if ((!core_info_current_supports_netplay() ||
|
||||
serialization_quirks & (RETRO_SERIALIZATION_QUIRK_INCOMPLETE |
|
||||
RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION))
|
||||
&& !net_st->core_netpacket_interface)
|
||||
{
|
||||
RARCH_ERR("[Netplay] %s\n", msg_hash_to_str(MSG_NETPLAY_UNSUPPORTED));
|
||||
runloop_msg_queue_push(
|
||||
@ -8593,6 +8691,11 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session)
|
||||
netplay->self_mode = NETPLAY_CONNECTION_INIT;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
return true;
|
||||
|
||||
failure:
|
||||
@ -8748,6 +8851,18 @@ static bool kick_client_by_id_and_name(netplay_t *netplay,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool netplay_have_any_active_connection(netplay_t *netplay)
|
||||
{
|
||||
size_t i;
|
||||
if (!netplay || !netplay->is_server)
|
||||
return (netplay && netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED);
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
if ( (netplay->connections[i].flags & NETPLAY_CONN_FLAG_ACTIVE)
|
||||
&& (netplay->connections[i].mode >= NETPLAY_CONNECTION_CONNECTED))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_driver_ctl
|
||||
*
|
||||
@ -8915,7 +9030,7 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_GAME_WATCH:
|
||||
if (netplay)
|
||||
if (netplay && net_st->core_netpacket_interface == NULL)
|
||||
netplay_toggle_play_spectate(netplay);
|
||||
else
|
||||
ret = false;
|
||||
@ -8929,7 +9044,9 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_ALLOW_PAUSE:
|
||||
ret = (!netplay || netplay->allow_pausing);
|
||||
ret = (!netplay || netplay->allow_pausing) &&
|
||||
((net_st->core_netpacket_interface == NULL)
|
||||
|| !netplay_have_any_active_connection(netplay));
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_PAUSE:
|
||||
@ -8944,13 +9061,13 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_LOAD_SAVESTATE:
|
||||
if (netplay)
|
||||
if (netplay && !net_st->core_netpacket_interface)
|
||||
netplay_load_savestate(netplay,
|
||||
(retro_ctx_serialize_info_t*)data, true);
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_RESET:
|
||||
if (netplay)
|
||||
if (netplay && !net_st->core_netpacket_interface)
|
||||
netplay_core_reset(netplay);
|
||||
break;
|
||||
|
||||
@ -8967,7 +9084,10 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_DESYNC_PUSH:
|
||||
if (netplay)
|
||||
if (net_st->core_netpacket_interface
|
||||
&& netplay_have_any_active_connection(netplay))
|
||||
ret = false;
|
||||
else if (netplay)
|
||||
netplay->desync++;
|
||||
break;
|
||||
|
||||
@ -9027,6 +9147,35 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
|
||||
ret = false;
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_SET_CORE_PACKET_INTERFACE:
|
||||
if (net_st->core_netpacket_interface)
|
||||
{
|
||||
free(net_st->core_netpacket_interface);
|
||||
net_st->core_netpacket_interface = NULL;
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
/* copy passed interface to local state */
|
||||
net_st->core_netpacket_interface = (struct retro_netpacket_callback*)
|
||||
malloc(sizeof(*net_st->core_netpacket_interface));
|
||||
*net_st->core_netpacket_interface = *(struct retro_netpacket_callback*)data;
|
||||
/* reset savefile dir as core_netpacket_interface affects it */
|
||||
runloop_path_set_redirect(config_get_ptr(),
|
||||
dir_get_ptr(RARCH_DIR_SAVEFILE),
|
||||
dir_get_ptr(RARCH_DIR_CURRENT_SAVESTATE));
|
||||
}
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_SKIP_NETPLAY_CALLBACKS:
|
||||
ret = (net_st->core_netpacket_interface != NULL);
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_ALLOW_TIMESKIP:
|
||||
ret = ((net_st->core_netpacket_interface == NULL)
|
||||
|| !netplay_have_any_active_connection(netplay));
|
||||
break;
|
||||
|
||||
case RARCH_NETPLAY_CTL_NONE:
|
||||
default:
|
||||
ret = false;
|
||||
@ -9122,6 +9271,44 @@ 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)
|
||||
{
|
||||
net_driver_state_t *net_st = &networking_driver_st;
|
||||
netplay_t *netplay = net_st->data;
|
||||
if (!netplay) return;
|
||||
|
||||
if (broadcast && 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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
struct netplay_connection *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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Netplay Widgets */
|
||||
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
|
@ -162,6 +162,9 @@ enum netplay_cmd
|
||||
/* Sends over cheats enabled on client (unsupported) */
|
||||
NETPLAY_CMD_CHEATS = 0x0047,
|
||||
|
||||
/* Send a network packet from the raw packet core interface */
|
||||
NETPLAY_CMD_NETPACKET = 0x0048,
|
||||
|
||||
/* Misc. commands */
|
||||
|
||||
/* Sends multiple config requests over,
|
||||
|
@ -2528,6 +2528,10 @@ bool command_event(enum event_command cmd, void *data)
|
||||
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEEVOS_LOAD_STATE_PREVENTED_BY_HARDCORE_MODE), 0, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_WARNING);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_NETWORKING
|
||||
if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_TIMESKIP, NULL))
|
||||
return false;
|
||||
#endif
|
||||
if (!command_event_main_state(cmd))
|
||||
return false;
|
||||
|
30
runloop.c
30
runloop.c
@ -3484,6 +3484,13 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
}
|
||||
break;
|
||||
|
||||
case RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE:
|
||||
#ifdef HAVE_NETWORKING
|
||||
RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE.\n");
|
||||
netplay_driver_ctl(RARCH_NETPLAY_CTL_SET_CORE_PACKET_INTERFACE, data);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
RARCH_LOG("[Environ]: UNSUPPORTED (#%u).\n", cmd);
|
||||
return false;
|
||||
@ -4032,6 +4039,9 @@ void runloop_event_deinit_core(void)
|
||||
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
||||
runloop_st->runtime_shader_preset_path[0] = '\0';
|
||||
#endif
|
||||
#ifdef HAVE_NETWORKING
|
||||
netplay_driver_ctl(RARCH_NETPLAY_CTL_SET_CORE_PACKET_INTERFACE, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool runloop_path_init_subsystem(runloop_state_t *runloop_st)
|
||||
@ -6310,6 +6320,12 @@ static enum runloop_state_enum runloop_check_state(
|
||||
input_st->flags |= INP_FLAG_NONBLOCKING;
|
||||
}
|
||||
|
||||
#ifdef HAVE_NETWORKING
|
||||
if (check2
|
||||
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_TIMESKIP, NULL))
|
||||
check2 = false;
|
||||
#endif
|
||||
|
||||
if (check2)
|
||||
{
|
||||
if (input_st->flags & INP_FLAG_NONBLOCKING)
|
||||
@ -6408,6 +6424,14 @@ static enum runloop_state_enum runloop_check_state(
|
||||
runloop_st->flags &= ~RUNLOOP_FLAG_SLOWMOTION;
|
||||
}
|
||||
|
||||
#ifdef HAVE_NETWORKING
|
||||
if ((runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
|
||||
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_TIMESKIP, NULL))
|
||||
{
|
||||
runloop_st->flags &= ~RUNLOOP_FLAG_SLOWMOTION;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
|
||||
{
|
||||
if (settings->uints.video_black_frame_insertion)
|
||||
@ -7404,6 +7428,9 @@ bool core_set_netplay_callbacks(void)
|
||||
{
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
|
||||
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_SKIP_NETPLAY_CALLBACKS, NULL))
|
||||
return true;
|
||||
|
||||
/* Force normal poll type for netplay. */
|
||||
runloop_st->current_core.poll_type = POLL_TYPE_NORMAL;
|
||||
|
||||
@ -7983,7 +8010,8 @@ void runloop_path_set_redirect(settings_t *settings,
|
||||
#ifdef HAVE_NETWORKING
|
||||
/* Special save directory for netplay clients. */
|
||||
if ( netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)
|
||||
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL))
|
||||
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL)
|
||||
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_SKIP_NETPLAY_CALLBACKS, NULL))
|
||||
{
|
||||
fill_pathname_join(new_savefile_dir, new_savefile_dir, ".netplay",
|
||||
sizeof(new_savefile_dir));
|
||||
|
@ -748,8 +748,9 @@ bool state_manager_check_rewind(
|
||||
{
|
||||
#ifdef HAVE_NETWORKING
|
||||
/* Make sure netplay isn't confused */
|
||||
if (!was_reversed)
|
||||
netplay_driver_ctl(RARCH_NETPLAY_CTL_DESYNC_PUSH, NULL);
|
||||
if (!was_reversed
|
||||
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_DESYNC_PUSH, NULL))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
rewind_st->flags |= STATE_MGR_REWIND_ST_FLAG_FRAME_IS_REVERSED;
|
||||
|
@ -894,9 +894,7 @@ bool task_push_netplay_content_reload(const char *hostname)
|
||||
strlcpy(data->hostname, hostname, sizeof(data->hostname));
|
||||
|
||||
flags = content_get_flags();
|
||||
if (flags & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT)
|
||||
scan_state.state |= STATE_LOAD_CONTENTLESS;
|
||||
else if (flags & CONTENT_ST_FLAG_IS_INITED)
|
||||
if (flags & CONTENT_ST_FLAG_IS_INITED)
|
||||
{
|
||||
const char *psubsystem = path_get(RARCH_PATH_SUBSYSTEM);
|
||||
|
||||
@ -929,6 +927,10 @@ bool task_push_netplay_content_reload(const char *hostname)
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT) &&
|
||||
!(scan_state.state & (STATE_LOAD|STATE_LOAD_SUBSYSTEM)))
|
||||
scan_state.state |= STATE_LOAD_CONTENTLESS;
|
||||
|
||||
data->current.core_loaded = true;
|
||||
|
||||
scan_state.running = true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user