diff --git a/Makefile.common b/Makefile.common index bd82ed4b89..43d9cf8c6f 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1830,6 +1830,7 @@ ifeq ($(HAVE_NETWORKING), 1) # Netplay DEFINES += -DHAVE_NETWORK_CMD OBJ += network/netplay/netplay_delta.o \ + network/netplay/netplay_frontend.o \ network/netplay/netplay_handshake.o \ network/netplay/netplay_init.o \ network/netplay/netplay_io.o \ diff --git a/griffin/griffin.c b/griffin/griffin.c index b002383690..5a49927fda 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1205,6 +1205,7 @@ NETPLAY #ifdef HAVE_NETWORKING #include "../network/netplay/netplay_delta.c" #include "../network/netplay/netplay_handshake.c" +#include "../network/netplay/netplay_frontend.c" #include "../network/netplay/netplay_init.c" #include "../network/netplay/netplay_io.c" #include "../network/netplay/netplay_keyboard.c" diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index 74b43a3259..b7da64ffe3 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -86,6 +86,32 @@ enum rarch_netplay_share_analog_preference RARCH_NETPLAY_SHARE_ANALOG_LAST }; +int16_t input_state_net(unsigned port, unsigned device, + unsigned idx, unsigned id); + +void video_frame_net(const void *data, unsigned width, + unsigned height, size_t pitch); + +void audio_sample_net(int16_t left, int16_t right); + +size_t audio_sample_batch_net(const int16_t *data, size_t frames); + +bool init_netplay_deferred(const char* server, unsigned port); + +/** + * init_netplay + * @direct_host : Host to connect to directly, if applicable (client only) + * @server : server address to connect to (client only) + * @port : TCP port to host on/connect to + * + * Initializes netplay. + * + * If netplay is already initialized, will return false (0). + * + * Returns: true (1) if successful, otherwise false (0). + **/ +bool init_netplay(void *direct_host, const char *server, unsigned port); + void deinit_netplay(void); bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data); diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c new file mode 100644 index 0000000000..9ece3fb050 --- /dev/null +++ b/network/netplay/netplay_frontend.c @@ -0,0 +1,1618 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - Gregor Richards + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include + +#include "../../version.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_DISCORD +#include +#endif + +#include + +#include "netplay_discovery.h" +#include "netplay_private.h" + +#include "../../configuration.h" +#include "../../frontend/frontend_driver.h" +#include "../../tasks/task_content.h" +#include "../../tasks/tasks_internal.h" +#include "../../file_path_special.h" +#include "../../paths.h" +#include "../../command.h" +#include "../../dynamic.h" +#include "../../retroarch.h" + +/* Only used before init_netplay */ +static bool netplay_enabled = false; +static bool netplay_is_client = false; + +/* Used while Netplay is running */ +static netplay_t *netplay_data = NULL; + +/* Used to avoid recursive netplay calls */ +static bool in_netplay = false; + +/* Used for deferred netplay initialization */ +static bool netplay_client_deferred = false; +static char server_address_deferred[512] = ""; +static unsigned server_port_deferred = 0; + +/* Used */ +static int reannounce = 0; +static bool is_mitm = false; + +static bool netplay_disconnect(netplay_t *netplay); + +#ifdef HAVE_DISCORD +/* TODO/FIXME - global */ +extern bool discord_is_inited; +#endif + +/** + * netplay_is_alive: + * @netplay : pointer to netplay object + * + * Checks if input port/index is controlled by netplay or not. + * + * Returns: true (1) if alive, otherwise false (0). + **/ +static bool netplay_is_alive(netplay_t *netplay) +{ + if (!netplay) + return false; + return (netplay->is_server) || + (!netplay->is_server && netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED); +} + +/** + * netplay_should_skip: + * @netplay : pointer to netplay object + * + * If we're fast-forward replaying to resync, check if we + * should actually show frame. + * + * Returns: bool (1) if we should skip this frame, otherwise + * false (0). + **/ +static bool netplay_should_skip(netplay_t *netplay) +{ + if (!netplay) + return false; + return netplay->is_replay && (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED); +} + +/** + * netplay_can_poll + * + * Just a frontend for netplay->can_poll that handles netplay==NULL + */ +static bool netplay_can_poll(netplay_t *netplay) +{ + if (!netplay) + return false; + return netplay->can_poll; +} + +/** + * get_self_input_state: + * @netplay : pointer to netplay object + * + * Grab our own input state and send this frame's input state (self and remote) + * over the network + * + * Returns: true (1) if successful, otherwise false (0). + */ +static bool get_self_input_state(netplay_t *netplay) +{ + unsigned i; + struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; + netplay_input_state_t istate = NULL; + uint32_t devices, used_devices = 0, devi, dev_type, local_device; + + if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) + return false; + + /* We've already read this frame! */ + if (ptr->have_local) + return true; + + devices = netplay->self_devices; + used_devices = 0; + + for (devi = 0; devi < MAX_INPUT_DEVICES; devi++) + { + if (!(devices & (1<config_devices[devi]&RETRO_DEVICE_MASK; + + for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++) + { + if (used_devices & (1<config_devices[local_device]&RETRO_DEVICE_MASK) == dev_type) + break; + } + + if (local_device == MAX_INPUT_DEVICES) + local_device = 0; + used_devices |= (1<real_input[devi], + /* If we're a slave, we write our own input to MAX_CLIENTS to keep it separate */ + (netplay->self_mode==NETPLAY_CONNECTION_SLAVE)?MAX_CLIENTS:netplay->self_client_num, + netplay_expected_input_size(netplay, 1 << devi), + true, false); + if (!istate) + continue; /* FIXME: More severe? */ + + /* First frame we always give zero input since relying on + * input from first frame screws up when we use -F 0. */ + if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) + { + uint32_t *state = istate->data; + retro_input_state_t cb = netplay->cbs.state_cb; + unsigned dtype = netplay->config_devices[devi]&RETRO_DEVICE_MASK; + + switch (dtype) + { + case RETRO_DEVICE_ANALOG: + for (i = 0; i < 2; i++) + { + int16_t tmp_x = cb(local_device, + RETRO_DEVICE_ANALOG, (unsigned)i, 0); + int16_t tmp_y = cb(local_device, + RETRO_DEVICE_ANALOG, (unsigned)i, 1); + state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + } + /* no break */ + + case RETRO_DEVICE_JOYPAD: + for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++) + { + int16_t tmp = cb(local_device, + RETRO_DEVICE_JOYPAD, 0, (unsigned)i); + state[0] |= tmp ? 1 << i : 0; + } + break; + + case RETRO_DEVICE_MOUSE: + case RETRO_DEVICE_LIGHTGUN: + { + int16_t tmp_x = cb(local_device, dtype, 0, 0); + int16_t tmp_y = cb(local_device, dtype, 0, 1); + state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + for (i = 2; + i <= (unsigned)((dtype == RETRO_DEVICE_MOUSE) ? + RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN : + RETRO_DEVICE_ID_LIGHTGUN_START); + i++) + { + int16_t tmp = cb(local_device, dtype, 0, + (unsigned) i); + state[0] |= tmp ? 1 << i : 0; + } + break; + } + + case RETRO_DEVICE_KEYBOARD: + { + unsigned key, word = 0, bit = 1; + for (key = 1; key < NETPLAY_KEY_LAST; key++) + { + state[word] |= + cb(local_device, RETRO_DEVICE_KEYBOARD, 0, netplay_key_ntoh(key)) ? + (1U << bit) : 0; + bit++; + if (bit >= 32) + { + bit = 0; + word++; + if (word >= istate->size) + break; + } + } + break; + } + } + } + } + + ptr->have_local = true; + if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) + { + ptr->have_real[netplay->self_client_num] = true; + netplay->read_ptr[netplay->self_client_num] = NEXT_PTR(netplay->self_ptr); + netplay->read_frame_count[netplay->self_client_num] = netplay->self_frame_count + 1; + } + + /* And send this input to our peers */ + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED) + netplay_send_cur_input(netplay, &netplay->connections[i]); + } + + /* Handle any delayed state changes */ + if (netplay->is_server) + netplay_delayed_state_change(netplay); + + return true; +} + +bool init_netplay_deferred(const char* server, unsigned port) +{ + if (!string_is_empty(server) && port != 0) + { + strlcpy(server_address_deferred, server, sizeof(server_address_deferred)); + server_port_deferred = port; + netplay_client_deferred = true; + } + else + netplay_client_deferred = false; + return netplay_client_deferred; +} + +/** + * netplay_poll: + * @netplay : pointer to netplay object + * + * Polls network to see if we have anything new. If our + * network buffer is full, we simply have to block + * for new input data. + * + * Returns: true (1) if successful, otherwise false (0). + **/ +static bool netplay_poll(netplay_t *netplay) +{ + int res; + uint32_t client; + size_t i; + + netplay->can_poll = false; + + if (!get_self_input_state(netplay)) + goto catastrophe; + + /* If we're not connected, we're done */ + if (netplay->self_mode == NETPLAY_CONNECTION_NONE) + return true; + + /* Read Netplay input, block if we're configured to stall for input every + * frame */ + netplay_update_unread_ptr(netplay); + if (netplay->stateless_mode && + (netplay->connected_players>1) && + netplay->unread_frame_count <= netplay->run_frame_count) + res = netplay_poll_net_input(netplay, true); + else + res = netplay_poll_net_input(netplay, false); + if (res == -1) + goto catastrophe; + + /* Resolve and/or simulate the input if we don't have real input */ + netplay_resolve_input(netplay, netplay->run_ptr, false); + + /* Handle any slaves */ + if (netplay->is_server && netplay->connected_slaves) + netplay_handle_slaves(netplay); + + netplay_update_unread_ptr(netplay); + + /* Figure out how many frames of input latency we should be using to hide + * network latency */ + if (netplay->frame_run_time_avg || netplay->stateless_mode) + { + /* FIXME: Using fixed 60fps for this calculation */ + unsigned frames_per_frame = netplay->frame_run_time_avg ? + (16666 / netplay->frame_run_time_avg) : + 0; + unsigned frames_ahead = (netplay->run_frame_count > netplay->unread_frame_count) ? + (netplay->run_frame_count - netplay->unread_frame_count) : + 0; + settings_t *settings = config_get_ptr(); + int input_latency_frames_min = settings->uints.netplay_input_latency_frames_min - + (settings->bools.run_ahead_enabled ? settings->uints.run_ahead_frames : 0); + int input_latency_frames_max = input_latency_frames_min + settings->uints.netplay_input_latency_frames_range; + + /* Assume we need a couple frames worth of time to actually run the + * current frame */ + if (frames_per_frame > 2) + frames_per_frame -= 2; + else + frames_per_frame = 0; + + /* Shall we adjust our latency? */ + if (netplay->stateless_mode) + { + /* In stateless mode, we adjust up if we're "close" and down if we + * have a lot of slack */ + if (netplay->input_latency_frames < input_latency_frames_min || + (netplay->unread_frame_count == netplay->run_frame_count + 1 && + netplay->input_latency_frames < input_latency_frames_max)) + netplay->input_latency_frames++; + else if (netplay->input_latency_frames > input_latency_frames_max || + (netplay->unread_frame_count > netplay->run_frame_count + 2 && + netplay->input_latency_frames > input_latency_frames_min)) + netplay->input_latency_frames--; + } + else if (netplay->input_latency_frames < input_latency_frames_min || + (frames_per_frame < frames_ahead && + netplay->input_latency_frames < input_latency_frames_max)) + { + /* We can't hide this much network latency with replay, so hide some + * with input latency */ + netplay->input_latency_frames++; + } + else if (netplay->input_latency_frames > input_latency_frames_max || + (frames_per_frame > frames_ahead + 2 && + netplay->input_latency_frames > input_latency_frames_min)) + { + /* We don't need this much latency (any more) */ + netplay->input_latency_frames--; + } + } + + /* If we're stalled, consider unstalling */ + switch (netplay->stall) + { + case NETPLAY_STALL_RUNNING_FAST: + { + if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2 + > netplay->self_frame_count) + { + netplay->stall = NETPLAY_STALL_NONE; + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (connection->active && connection->stall) + connection->stall = NETPLAY_STALL_NONE; + } + } + break; + } + + case NETPLAY_STALL_SPECTATOR_WAIT: + if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->unread_frame_count > netplay->self_frame_count) + netplay->stall = NETPLAY_STALL_NONE; + break; + + case NETPLAY_STALL_INPUT_LATENCY: + /* Just let it recalculate momentarily */ + netplay->stall = NETPLAY_STALL_NONE; + break; + + case NETPLAY_STALL_SERVER_REQUESTED: + /* See if the stall is done */ + if (netplay->connections[0].stall_frame == 0) + { + /* Stop stalling! */ + netplay->connections[0].stall = NETPLAY_STALL_NONE; + netplay->stall = NETPLAY_STALL_NONE; + } + else + netplay->connections[0].stall_frame--; + break; + case NETPLAY_STALL_NO_CONNECTION: + /* We certainly haven't fixed this */ + break; + default: /* not stalling */ + break; + } + + /* If we're not stalled, consider stalling */ + if (!netplay->stall) + { + /* Have we not read enough latency frames? */ + if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING && + netplay->connected_players && + netplay->run_frame_count + netplay->input_latency_frames > netplay->self_frame_count) + { + netplay->stall = NETPLAY_STALL_INPUT_LATENCY; + netplay->stall_time = 0; + } + + /* Are we too far ahead? */ + if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES + <= netplay->self_frame_count) + { + netplay->stall = NETPLAY_STALL_RUNNING_FAST; + netplay->stall_time = cpu_features_get_time_usec(); + + /* Figure out who to blame */ + if (netplay->is_server) + { + for (client = 1; client < MAX_CLIENTS; client++) + { + struct netplay_connection *connection; + if (!(netplay->connected_players & (1<read_frame_count[client] > netplay->unread_frame_count) + continue; + connection = &netplay->connections[client-1]; + if (connection->active && + connection->mode == NETPLAY_CONNECTION_PLAYING) + { + connection->stall = NETPLAY_STALL_RUNNING_FAST; + connection->stall_time = netplay->stall_time; + } + } + } + + } + + /* If we're a spectator, are we ahead at all? */ + if (!netplay->is_server && + (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING || + netplay->self_mode == NETPLAY_CONNECTION_SLAVE) && + netplay->unread_frame_count <= netplay->self_frame_count) + { + netplay->stall = NETPLAY_STALL_SPECTATOR_WAIT; + netplay->stall_time = cpu_features_get_time_usec(); + } + } + + /* If we're stalling, consider disconnection */ + if (netplay->stall && netplay->stall_time) + { + retro_time_t now = cpu_features_get_time_usec(); + + /* Don't stall out while they're paused */ + if (netplay->remote_paused) + netplay->stall_time = now; + else if (now - netplay->stall_time >= + (netplay->is_server ? MAX_SERVER_STALL_TIME_USEC : + MAX_CLIENT_STALL_TIME_USEC)) + { + /* Stalled out! */ + if (netplay->is_server) + { + bool fixed = false; + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (connection->active && + connection->mode == NETPLAY_CONNECTION_PLAYING && + connection->stall) + { + netplay_hangup(netplay, connection); + fixed = true; + } + } + + if (fixed) + { + /* Not stalled now :) */ + netplay->stall = NETPLAY_STALL_NONE; + return true; + } + } + else + goto catastrophe; + return false; + } + } + + return true; + +catastrophe: + for (i = 0; i < netplay->connections_size; i++) + netplay_hangup(netplay, &netplay->connections[i]); + return false; +} + +/** + * input_poll_net + * + * Poll the network if necessary. + */ +void input_poll_net(void) +{ + netplay_t *netplay = netplay_data; + if (!netplay_should_skip(netplay) && netplay_can_poll(netplay)) + netplay_poll(netplay); +} + +/* Netplay polling callbacks */ +void video_frame_net(const void *data, unsigned width, + unsigned height, size_t pitch) +{ + netplay_t *netplay = netplay_data; + if (!netplay_should_skip(netplay)) + netplay->cbs.frame_cb(data, width, height, pitch); +} + +void audio_sample_net(int16_t left, int16_t right) +{ + netplay_t *netplay = netplay_data; + if (!netplay_should_skip(netplay) && !netplay->stall) + netplay->cbs.sample_cb(left, right); +} + +size_t audio_sample_batch_net(const int16_t *data, size_t frames) +{ + netplay_t *netplay = netplay_data; + if (!netplay_should_skip(netplay) && !netplay->stall) + return netplay->cbs.sample_batch_cb(data, frames); + return frames; +} + +static int16_t netplay_input_state(netplay_t *netplay, + unsigned port, unsigned device, + unsigned idx, unsigned id) +{ + size_t ptr = netplay->is_replay ? + netplay->replay_ptr : netplay->run_ptr; + struct delta_frame *delta; + netplay_input_state_t istate; + + const uint32_t *curr_input_state = NULL; + + if (port >= MAX_INPUT_DEVICES) + return 0; + + /* If the port doesn't seem to correspond to the device, "correct" it. This + * is common with devices that typically only have one instance, such as + * keyboards, mice and lightguns. */ + if (device != RETRO_DEVICE_JOYPAD && + (netplay->config_devices[port]&RETRO_DEVICE_MASK) != device) + { + for (port = 0; port < MAX_INPUT_DEVICES; port++) + { + if ((netplay->config_devices[port]&RETRO_DEVICE_MASK) == device) + break; + } + if (port == MAX_INPUT_DEVICES) + return 0; + } + + delta = &netplay->buffer[ptr]; + istate = delta->resolved_input[port]; + if (!istate || !istate->used) + return 0; + + if (istate->size == 0) + return 0; + curr_input_state = istate->data; + + switch (device) + { + case RETRO_DEVICE_JOYPAD: + if (id == RETRO_DEVICE_ID_JOYPAD_MASK) + return curr_input_state[0]; + return ((1 << id) & curr_input_state[0]) ? 1 : 0; + + case RETRO_DEVICE_ANALOG: + { + uint32_t state; + if (istate->size != 3) + return 0; + state = curr_input_state[1 + idx]; + return (int16_t)(uint16_t)(state >> (id * 16)); + } + + case RETRO_DEVICE_MOUSE: + case RETRO_DEVICE_LIGHTGUN: + if (istate->size != 2) + return 0; + if (id <= RETRO_DEVICE_ID_MOUSE_Y) + return (int16_t)(uint16_t)(curr_input_state[1] >> (id * 16)); + return ((1 << id) & curr_input_state[0]) ? 1 : 0; + case RETRO_DEVICE_KEYBOARD: + { + unsigned word, bit; + unsigned key = netplay_key_hton(id); + if (key == NETPLAY_KEY_UNKNOWN) + return 0; + word = key / 32; + bit = key % 32; + if (word <= istate->size) + return ((1U<len == 0) + { + free(task_data); + return; + } + + buf = (char*)calloc(1, data->len + 1); + + memcpy(buf, data->data, data->len); + + lines = string_split(buf, "\n"); + + if (lines->size == 0) + { + string_list_free(lines); + free(buf); + free(task_data); + return; + } + + memset(host_room, 0, sizeof(*host_room)); + + for (i = 0; i < lines->size; i++) + { + const char *line = lines->elems[i].data; + + if (!string_is_empty(line)) + { + struct string_list *kv = string_split(line, "="); + const char *key = NULL; + const char *val = NULL; + + if (!kv) + continue; + + if (kv->size != 2) + { + string_list_free(kv); + continue; + } + + key = kv->elems[0].data; + val = kv->elems[1].data; + + if (string_is_equal(key, "id")) + sscanf(val, "%i", &host_room->id); + if (string_is_equal(key, "username")) + strlcpy(host_room->nickname, val, sizeof(host_room->nickname)); + if (string_is_equal(key, "ip")) + strlcpy(host_room->address, val, sizeof(host_room->address)); + if (string_is_equal(key, "mitm_ip")) + { + mitm_ip = strdup(val); + strlcpy(host_room->mitm_address, val, sizeof(host_room->mitm_address)); + } + if (string_is_equal(key, "port")) + sscanf(val, "%i", &host_room->port); + if (string_is_equal(key, "mitm_port")) + { + mitm_port = strdup(val); + sscanf(mitm_port, "%i", &host_room->mitm_port); + } + if (string_is_equal(key, "core_name")) + strlcpy(host_room->corename, val, sizeof(host_room->corename)); + if (string_is_equal(key, "frontend")) + strlcpy(host_room->frontend, val, sizeof(host_room->frontend)); + if (string_is_equal(key, "core_version")) + strlcpy(host_room->coreversion, val, sizeof(host_room->coreversion)); + if (string_is_equal(key, "game_name")) + strlcpy(host_room->gamename, val, sizeof(host_room->gamename)); + if (string_is_equal(key, "game_crc")) + sscanf(val, "%08d", &host_room->gamecrc); + if (string_is_equal(key, "host_method")) + sscanf(val, "%i", &host_room->host_method); + if (string_is_equal(key, "has_password")) + { + if (string_is_equal_noncase(val, "true") || string_is_equal(val, "1")) + host_room->has_password = true; + else + host_room->has_password = false; + } + if (string_is_equal(key, "has_spectate_password")) + { + if (string_is_equal_noncase(val, "true") || string_is_equal(val, "1")) + host_room->has_spectate_password = true; + else + host_room->has_spectate_password = false; + } + if (string_is_equal(key, "fixed")) + { + if (string_is_equal_noncase(val, "true") || string_is_equal(val, "1")) + host_room->fixed = true; + else + host_room->fixed = false; + } + if (string_is_equal(key, "retroarch_version")) + strlcpy(host_room->retroarch_version, val, sizeof(host_room->retroarch_version)); + if (string_is_equal(key, "country")) + strlcpy(host_room->country, val, sizeof(host_room->country)); + + string_list_free(kv); + } + } + + if (mitm_ip && mitm_port) + { + ip_len = (unsigned)strlen(mitm_ip); + port_len = (unsigned)strlen(mitm_port); + + /* Enable Netplay client mode */ + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) + { + command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); + is_mitm = true; + host_room->host_method = NETPLAY_HOST_METHOD_MITM; + } + + netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL); + + host_string = (char*)calloc(1, ip_len + port_len + 2); + + memcpy(host_string, mitm_ip, ip_len); + memcpy(host_string + ip_len, "|", 1); + memcpy(host_string + ip_len + 1, mitm_port, port_len); + + /* Enable Netplay */ + command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, (void*)host_string); + command_event(CMD_EVENT_NETPLAY_INIT, (void*)host_string); + + free(host_string); + } + +#ifdef HAVE_DISCORD + if (discord_is_inited) + { + discord_userdata_t userdata; + userdata.status = DISCORD_PRESENCE_NETPLAY_HOSTING; + command_event(CMD_EVENT_DISCORD_UPDATE, &userdata); + } +#endif + + string_list_free(lines); + free(buf); + free(task_data); + if (mitm_ip) + free(mitm_ip); + if (mitm_port) + free(mitm_port); + } + + return; +} + +static void netplay_announce(void) +{ + char buf[4600]; + char frontend_architecture[PATH_MAX_LENGTH]; + char frontend_architecture_tmp[PATH_MAX_LENGTH]; + const frontend_ctx_driver_t *frontend_drv = NULL; + char url[2048] = "http://lobby.libretro.com/add/"; + char *username = NULL; + char *corename = NULL; + char *gamename = NULL; + char *subsystemname = NULL; + char *coreversion = NULL; + char *frontend_ident = NULL; + settings_t *settings = config_get_ptr(); + struct retro_system_info *system = runloop_get_libretro_system_info(); + uint32_t content_crc = content_get_crc(); + struct string_list *subsystem = path_get_subsystem_list(); + + buf[0] = '\0'; + + if (subsystem) + { + unsigned i; + + for (i = 0; i < subsystem->size; i++) + { + strlcat(buf, path_basename(subsystem->elems[i].data), sizeof(buf)); + if (i < subsystem->size - 1) + strlcat(buf, "|", sizeof(buf)); + } + net_http_urlencode(&gamename, buf); + net_http_urlencode(&subsystemname, path_get(RARCH_PATH_SUBSYSTEM)); + content_crc = 0; + } + else + { + net_http_urlencode(&gamename, + !string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))) ? + path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A"); + net_http_urlencode(&subsystemname, "N/A"); + } + + frontend_drv = + (const frontend_ctx_driver_t*)frontend_driver_get_cpu_architecture_str( + frontend_architecture_tmp, sizeof(frontend_architecture_tmp)); + snprintf(frontend_architecture, sizeof(frontend_architecture), "%s %s", + frontend_drv->ident, frontend_architecture_tmp); + +#ifdef HAVE_DISCORD + if (discord_is_ready()) + net_http_urlencode(&username, discord_get_own_username()); + else +#endif + net_http_urlencode(&username, settings->paths.username); + net_http_urlencode(&corename, system->library_name); + net_http_urlencode(&coreversion, system->library_version); + net_http_urlencode(&frontend_ident, frontend_architecture); + + buf[0] = '\0'; + + snprintf(buf, sizeof(buf), "username=%s&core_name=%s&core_version=%s&" + "game_name=%s&game_crc=%08X&port=%d&mitm_server=%s" + "&has_password=%d&has_spectate_password=%d&force_mitm=%d" + "&retroarch_version=%s&frontend=%s&subsystem_name=%s", + username, corename, coreversion, gamename, content_crc, + settings->uints.netplay_port, + settings->arrays.netplay_mitm_server, + *settings->paths.netplay_password ? 1 : 0, + *settings->paths.netplay_spectate_password ? 1 : 0, + settings->bools.netplay_use_mitm_server, + PACKAGE_VERSION, frontend_architecture, subsystemname); + task_push_http_post_transfer(url, buf, true, NULL, netplay_announce_cb, NULL); + + if (username) + free(username); + if (corename) + free(corename); + if (gamename) + free(gamename); + if (coreversion) + free(coreversion); + if (frontend_ident) + free(frontend_ident); +} + +int16_t input_state_net(unsigned port, unsigned device, + unsigned idx, unsigned id) +{ + netplay_t *netplay = netplay_data; + if (netplay_is_alive(netplay)) + return netplay_input_state(netplay, port, device, idx, id); + return netplay->cbs.state_cb(port, device, idx, id); +} +/* ^^^ Netplay polling callbacks */ + +/** + * netplay_command: + * @netplay : pointer to netplay object + * @cmd : command to send + * @data : data to send as argument + * @sz : size of data + * @command_str : name of action + * @success_msg : message to display upon success + * + * Sends a single netplay command and waits for response. Only actually used + * for player flipping. FIXME: Should probably just be removed. + */ +bool netplay_command(netplay_t* netplay, struct netplay_connection *connection, + enum netplay_cmd cmd, void* data, size_t sz, const char* command_str, + const char* success_msg) +{ + retro_assert(netplay); + + if (!netplay_send_raw_cmd(netplay, connection, cmd, data, sz)) + return false; + + runloop_msg_queue_push(success_msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + + return true; +} + +/** +* netplay_frontend_paused + * @netplay : pointer to netplay object + * @paused : true if frontend is paused + * + * Inform Netplay of the frontend's pause state (paused or otherwise) + */ +static void netplay_frontend_paused(netplay_t *netplay, bool paused) +{ + size_t i; + uint32_t paused_ct; + + /* Nothing to do if we already knew this */ + if (netplay->local_paused == paused) + return; + + netplay->local_paused = paused; + + /* Communicating this is a bit odd: If exactly one other connection is + * paused, then we must tell them that we're unpaused, as from their + * perspective we are. If more than one other connection is paused, then our + * status as proxy means we are NOT unpaused to either of them. */ + paused_ct = 0; + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (connection->active && connection->paused) + paused_ct++; + } + if (paused_ct > 1) + return; + + /* Send our unpaused status. Must send manually because we must immediately + * flush the buffer: If we're paused, we won't be polled. */ + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED) + { + if (paused) + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PAUSE, + netplay->nick, NETPLAY_NICK_LEN); + else + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_RESUME, + NULL, 0); + + /* We're not going to be polled, so we need to flush this command now */ + netplay_send_flush(&connection->send_packet_buffer, connection->fd, true); + } + } +} + +/** + * netplay_pre_frame: + * @netplay : pointer to netplay object + * + * Pre-frame for Netplay. + * Call this before running retro_run(). + * + * Returns: true (1) if the frontend is cleared to emulate the frame, false (0) + * if we're stalled or paused + **/ +bool netplay_pre_frame(netplay_t *netplay) +{ + bool sync_stalled = false; + settings_t *settings = config_get_ptr(); + + retro_assert(netplay); + + if (settings->bools.netplay_public_announce) + { + reannounce++; + if ((netplay->is_server || is_mitm) && (reannounce % 600 == 0)) + netplay_announce(); + } + /* Make sure that if announcement is turned on mid-game, it gets announced */ + else + reannounce = -1; + + /* FIXME: This is an ugly way to learn we're not paused anymore */ + if (netplay->local_paused) + netplay_frontend_paused(netplay, false); + + /* Are we ready now? */ + if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) + netplay_try_init_serialization(netplay); + + if (netplay->is_server && !settings->bools.netplay_use_mitm_server) + { + /* Advertise our server */ + netplay_lan_ad_server(netplay); + + /* NAT traversal if applicable */ + if (netplay->nat_traversal && + !netplay->nat_traversal_task_oustanding && + netplay->nat_traversal_state.request_outstanding && + !netplay->nat_traversal_state.have_inet4) + { + struct timeval tmptv = {0}; + fd_set fds = netplay->nat_traversal_state.fds; + if (socket_select(netplay->nat_traversal_state.nfds, &fds, NULL, NULL, &tmptv) > 0) + natt_read(&netplay->nat_traversal_state); + +#ifndef HAVE_SOCKET_LEGACY + if (!netplay->nat_traversal_state.request_outstanding || + netplay->nat_traversal_state.have_inet4) + netplay_announce_nat_traversal(netplay); +#endif + } + } + + sync_stalled = !netplay_sync_pre_frame(netplay); + + /* If we're disconnected, deinitialize */ + if (!netplay->is_server && !netplay->connections[0].active) + { + netplay_disconnect(netplay); + return true; + } + + if (sync_stalled || + ((!netplay->is_server || (netplay->connected_players>1)) && + (netplay->stall || netplay->remote_paused))) + { + /* We may have received data even if we're stalled, so run post-frame + * sync */ + netplay_sync_post_frame(netplay, true); + return false; + } + return true; +} + +/** + * netplay_post_frame: + * @netplay : pointer to netplay object + * + * Post-frame for Netplay. + * We check if we have new input and replay from recorded input. + * Call this after running retro_run(). + **/ +void netplay_post_frame(netplay_t *netplay) +{ + size_t i; + retro_assert(netplay); + netplay_update_unread_ptr(netplay); + netplay_sync_post_frame(netplay, false); + + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (connection->active && + !netplay_send_flush(&connection->send_packet_buffer, connection->fd, + false)) + netplay_hangup(netplay, connection); + } + + /* If we're disconnected, deinitialize */ + if (!netplay->is_server && !netplay->connections[0].active) + netplay_disconnect(netplay); +} + +/** + * netplay_force_future + * @netplay : pointer to netplay object + * + * Force netplay to ignore all past input, typically because we've just loaded + * a state or reset. + */ +static void netplay_force_future(netplay_t *netplay) +{ + /* Wherever we're inputting, that's where we consider our state to be loaded */ + netplay->run_ptr = netplay->self_ptr; + netplay->run_frame_count = netplay->self_frame_count; + + /* We need to ignore any intervening data from the other side, + * and never rewind past this */ + netplay_update_unread_ptr(netplay); + + if (netplay->unread_frame_count < netplay->run_frame_count) + { + uint32_t client; + for (client = 0; client < MAX_CLIENTS; client++) + { + if (!(netplay->connected_players & (1<read_frame_count[client] < netplay->run_frame_count) + { + netplay->read_ptr[client] = netplay->run_ptr; + netplay->read_frame_count[client] = netplay->run_frame_count; + } + } + if (netplay->server_frame_count < netplay->run_frame_count) + { + netplay->server_ptr = netplay->run_ptr; + netplay->server_frame_count = netplay->run_frame_count; + } + netplay_update_unread_ptr(netplay); + } + if (netplay->other_frame_count < netplay->run_frame_count) + { + netplay->other_ptr = netplay->run_ptr; + netplay->other_frame_count = netplay->run_frame_count; + } +} + +/** + * netplay_send_savestate + * @netplay : pointer to netplay object + * @serial_info : the savestate being loaded + * @cx : compression type + * @z : compression backend to use + * + * Send a loaded savestate to those connected peers using the given compression + * scheme. + */ +void netplay_send_savestate(netplay_t *netplay, + retro_ctx_serialize_info_t *serial_info, uint32_t cx, + struct compression_transcoder *z) +{ + uint32_t header[4]; + uint32_t rd, wn; + size_t i; + + /* Compress it */ + z->compression_backend->set_in(z->compression_stream, + (const uint8_t*)serial_info->data_const, (uint32_t)serial_info->size); + z->compression_backend->set_out(z->compression_stream, + netplay->zbuffer, (uint32_t)netplay->zbuffer_size); + if (!z->compression_backend->trans(z->compression_stream, true, &rd, + &wn, NULL)) + { + /* Catastrophe! */ + for (i = 0; i < netplay->connections_size; i++) + netplay_hangup(netplay, &netplay->connections[i]); + return; + } + + /* Send it to relevant peers */ + header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE); + header[1] = htonl(wn + 2*sizeof(uint32_t)); + header[2] = htonl(netplay->run_frame_count); + header[3] = htonl(serial_info->size); + + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (!connection->active || + connection->mode < NETPLAY_CONNECTION_CONNECTED || + connection->compression_supported != cx) continue; + + if (!netplay_send(&connection->send_packet_buffer, connection->fd, header, + sizeof(header)) || + !netplay_send(&connection->send_packet_buffer, connection->fd, + netplay->zbuffer, wn)) + netplay_hangup(netplay, connection); + } +} + +/** + * netplay_load_savestate + * @netplay : pointer to netplay object + * @serial_info : the savestate being loaded, NULL means + * "load it yourself" + * @save : Whether to save the provided serial_info + * into the frame buffer + * + * Inform Netplay of a savestate load and send it to the other side + **/ +void netplay_load_savestate(netplay_t *netplay, + retro_ctx_serialize_info_t *serial_info, bool save) +{ + retro_ctx_serialize_info_t tmp_serial_info; + + netplay_force_future(netplay); + + /* Record it in our own buffer */ + if (save || !serial_info) + { + if (netplay_delta_frame_ready(netplay, + &netplay->buffer[netplay->run_ptr], netplay->run_frame_count)) + { + if (!serial_info) + { + tmp_serial_info.size = netplay->state_size; + tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state; + if (!core_serialize(&tmp_serial_info)) + return; + tmp_serial_info.data_const = tmp_serial_info.data; + serial_info = &tmp_serial_info; + } + else + { + if (serial_info->size <= netplay->state_size) + memcpy(netplay->buffer[netplay->run_ptr].state, + serial_info->data_const, serial_info->size); + } + } + /* FIXME: This is a critical failure! */ + else + return; + } + + /* Don't send it if we're expected to be desynced */ + if (netplay->desync) + return; + + /* If we can't send it to the peer, loading a state was a bad idea */ + if (netplay->quirks & ( + NETPLAY_QUIRK_NO_SAVESTATES + | NETPLAY_QUIRK_NO_TRANSMISSION)) + return; + + /* Send this to every peer */ + if (netplay->compress_nil.compression_backend) + netplay_send_savestate(netplay, serial_info, 0, &netplay->compress_nil); + if (netplay->compress_zlib.compression_backend) + netplay_send_savestate(netplay, serial_info, NETPLAY_COMPRESSION_ZLIB, + &netplay->compress_zlib); +} + +/** + * netplay_core_reset + * @netplay : pointer to netplay object + * + * Indicate that the core has been reset to netplay peers + **/ +static void netplay_core_reset(netplay_t *netplay) +{ + uint32_t cmd[3]; + size_t i; + + /* Ignore past input */ + netplay_force_future(netplay); + + /* Request that our peers reset */ + cmd[0] = htonl(NETPLAY_CMD_RESET); + cmd[1] = htonl(sizeof(uint32_t)); + cmd[2] = htonl(netplay->self_frame_count); + + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (!connection->active || + connection->mode < NETPLAY_CONNECTION_CONNECTED) continue; + + if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd, + sizeof(cmd))) + netplay_hangup(netplay, connection); + } +} + +/** + * netplay_settings_share_mode + * + * Get the preferred share mode + */ +uint8_t netplay_settings_share_mode(unsigned share_digital, unsigned share_analog) +{ + if (share_digital || share_analog) + { + uint8_t share_mode = 0; + + switch (share_digital) + { + case RARCH_NETPLAY_SHARE_DIGITAL_OR: + share_mode |= NETPLAY_SHARE_DIGITAL_OR; + break; + case RARCH_NETPLAY_SHARE_DIGITAL_XOR: + share_mode |= NETPLAY_SHARE_DIGITAL_XOR; + break; + case RARCH_NETPLAY_SHARE_DIGITAL_VOTE: + share_mode |= NETPLAY_SHARE_DIGITAL_VOTE; + break; + default: + share_mode |= NETPLAY_SHARE_NO_PREFERENCE; + } + + switch (share_analog) + { + case RARCH_NETPLAY_SHARE_ANALOG_MAX: + share_mode |= NETPLAY_SHARE_ANALOG_MAX; + break; + case RARCH_NETPLAY_SHARE_ANALOG_AVERAGE: + share_mode |= NETPLAY_SHARE_ANALOG_AVERAGE; + break; + default: + share_mode |= NETPLAY_SHARE_NO_PREFERENCE; + } + + return share_mode; + } + return 0; +} + +/** + * netplay_toggle_play_spectate + * + * Toggle between play mode and spectate mode + */ +static void netplay_toggle_play_spectate(netplay_t *netplay) +{ + switch (netplay->self_mode) + { + case NETPLAY_CONNECTION_PLAYING: + case NETPLAY_CONNECTION_SLAVE: + /* Switch to spectator mode immediately */ + netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; + netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING); + break; + case NETPLAY_CONNECTION_SPECTATING: + /* Switch only after getting permission */ + netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING); + break; + default: + break; + } +} + +/** + * netplay_disconnect + * @netplay : pointer to netplay object + * + * Disconnect netplay. + * + * Returns: true (1) if successful. At present, cannot fail. + **/ +static bool netplay_disconnect(netplay_t *netplay) +{ + size_t i; + + if (!netplay) + return true; + for (i = 0; i < netplay->connections_size; i++) + netplay_hangup(netplay, &netplay->connections[i]); + + deinit_netplay(); + +#ifdef HAVE_DISCORD + if (discord_is_inited) + { + discord_userdata_t userdata; + userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED; + command_event(CMD_EVENT_DISCORD_UPDATE, &userdata); + } +#endif + return true; +} + +void deinit_netplay(void) +{ + if (netplay_data) + { + netplay_free(netplay_data); + netplay_enabled = false; + netplay_is_client = false; + is_mitm = false; + } + netplay_data = NULL; + core_unset_netplay_callbacks(); +} + +/** + * init_netplay + * @direct_host : Host to connect to directly, if applicable (client only) + * @server : server address to connect to (client only) + * @port : TCP port to host on/connect to + * + * Initializes netplay. + * + * If netplay is already initialized, will return false (0). + * + * Returns: true (1) if successful, otherwise false (0). + **/ +bool init_netplay(void *direct_host, const char *server, unsigned port) +{ + struct retro_callbacks cbs = {0}; + settings_t *settings = config_get_ptr(); + uint64_t serialization_quirks = 0; + uint64_t quirks = 0; + bool _netplay_is_client = netplay_is_client; + bool _netplay_enabled = netplay_enabled; + + if (!_netplay_enabled) + return false; + + core_set_default_callbacks(&cbs); + if (!core_set_netplay_callbacks()) + return false; + + /* Map the core's quirks to our quirks */ + serialization_quirks = core_serialization_quirks(); + if (serialization_quirks & ~((uint64_t) NETPLAY_QUIRK_MAP_UNDERSTOOD)) + { + /* Quirks we don't support! Just disable everything. */ + quirks |= NETPLAY_QUIRK_NO_SAVESTATES; + } + if (serialization_quirks & NETPLAY_QUIRK_MAP_NO_SAVESTATES) + quirks |= NETPLAY_QUIRK_NO_SAVESTATES; + if (serialization_quirks & NETPLAY_QUIRK_MAP_NO_TRANSMISSION) + quirks |= NETPLAY_QUIRK_NO_TRANSMISSION; + if (serialization_quirks & NETPLAY_QUIRK_MAP_INITIALIZATION) + quirks |= NETPLAY_QUIRK_INITIALIZATION; + if (serialization_quirks & NETPLAY_QUIRK_MAP_ENDIAN_DEPENDENT) + quirks |= NETPLAY_QUIRK_ENDIAN_DEPENDENT; + if (serialization_quirks & NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT) + quirks |= NETPLAY_QUIRK_PLATFORM_DEPENDENT; + + if (_netplay_is_client) + { + RARCH_LOG("[netplay] %s\n", msg_hash_to_str(MSG_CONNECTING_TO_NETPLAY_HOST)); + } + else + { + RARCH_LOG("[netplay] %s\n", msg_hash_to_str(MSG_WAITING_FOR_CLIENT)); + runloop_msg_queue_push( + msg_hash_to_str(MSG_WAITING_FOR_CLIENT), + 0, 180, false, + NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + + if (settings->bools.netplay_public_announce) + netplay_announce(); + } + + netplay_data = (netplay_t*)netplay_new( + _netplay_is_client ? direct_host : NULL, + _netplay_is_client ? (!netplay_client_deferred ? server + : server_address_deferred) : NULL, + _netplay_is_client ? (!netplay_client_deferred ? port + : server_port_deferred ) : (port != 0 ? port : RARCH_DEFAULT_PORT), + settings->bools.netplay_stateless_mode, + settings->ints.netplay_check_frames, + &cbs, + settings->bools.netplay_nat_traversal && !settings->bools.netplay_use_mitm_server, +#ifdef HAVE_DISCORD + discord_get_own_username() ? discord_get_own_username() : +#endif + settings->paths.username, + quirks); + + if (netplay_data) + { + if (netplay_data->is_server && !settings->bools.netplay_start_as_spectator) + netplay_toggle_play_spectate(netplay_data); + return true; + } + + RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); + + runloop_msg_queue_push( + msg_hash_to_str(MSG_NETPLAY_FAILED), + 0, 180, false, + NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + return false; +} + +/** + * netplay_driver_ctl + * + * Frontend access to Netplay functionality + */ +bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) +{ + netplay_t *netplay = netplay_data; + bool ret = true; + + if (in_netplay) + return true; + in_netplay = true; + + if (!netplay) + { + switch (state) + { + case RARCH_NETPLAY_CTL_ENABLE_SERVER: + netplay_enabled = true; + netplay_is_client = false; + goto done; + + case RARCH_NETPLAY_CTL_ENABLE_CLIENT: + netplay_enabled = true; + netplay_is_client = true; + break; + + case RARCH_NETPLAY_CTL_DISABLE: + netplay_enabled = false; +#ifdef HAVE_DISCORD + if (discord_is_inited) + { + discord_userdata_t userdata; + userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED; + command_event(CMD_EVENT_DISCORD_UPDATE, &userdata); + } +#endif + goto done; + + case RARCH_NETPLAY_CTL_IS_ENABLED: + ret = netplay_enabled; + goto done; + + case RARCH_NETPLAY_CTL_IS_REPLAYING: + case RARCH_NETPLAY_CTL_IS_DATA_INITED: + ret = false; + goto done; + + case RARCH_NETPLAY_CTL_IS_SERVER: + ret = netplay_enabled && !netplay_is_client; + goto done; + + case RARCH_NETPLAY_CTL_IS_CONNECTED: + ret = false; + goto done; + + default: + goto done; + } + } + + switch (state) + { + case RARCH_NETPLAY_CTL_ENABLE_SERVER: + case RARCH_NETPLAY_CTL_ENABLE_CLIENT: + case RARCH_NETPLAY_CTL_IS_DATA_INITED: + goto done; + case RARCH_NETPLAY_CTL_DISABLE: + ret = false; + goto done; + case RARCH_NETPLAY_CTL_IS_ENABLED: + goto done; + case RARCH_NETPLAY_CTL_IS_REPLAYING: + ret = netplay->is_replay; + goto done; + case RARCH_NETPLAY_CTL_IS_SERVER: + ret = netplay_enabled && !netplay_is_client; + goto done; + case RARCH_NETPLAY_CTL_IS_CONNECTED: + ret = netplay->is_connected; + goto done; + case RARCH_NETPLAY_CTL_POST_FRAME: + netplay_post_frame(netplay); + break; + case RARCH_NETPLAY_CTL_PRE_FRAME: + ret = netplay_pre_frame(netplay); + goto done; + case RARCH_NETPLAY_CTL_GAME_WATCH: + netplay_toggle_play_spectate(netplay); + break; + case RARCH_NETPLAY_CTL_PAUSE: + netplay_frontend_paused(netplay, true); + break; + case RARCH_NETPLAY_CTL_UNPAUSE: + netplay_frontend_paused(netplay, false); + break; + case RARCH_NETPLAY_CTL_LOAD_SAVESTATE: + netplay_load_savestate(netplay, (retro_ctx_serialize_info_t*)data, true); + break; + case RARCH_NETPLAY_CTL_RESET: + netplay_core_reset(netplay); + break; + case RARCH_NETPLAY_CTL_DISCONNECT: + ret = netplay_disconnect(netplay); + goto done; + case RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL: + netplay->nat_traversal_task_oustanding = false; +#ifndef HAVE_SOCKET_LEGACY + netplay_announce_nat_traversal(netplay); +#endif + goto done; + case RARCH_NETPLAY_CTL_DESYNC_PUSH: + netplay->desync++; + break; + case RARCH_NETPLAY_CTL_DESYNC_POP: + if (netplay->desync) + { + netplay->desync--; + if (!netplay->desync) + netplay_load_savestate(netplay, NULL, true); + } + break; + default: + case RARCH_NETPLAY_CTL_NONE: + ret = false; + } + +done: + in_netplay = false; + return ret; +} diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index d77656aa4c..e6494ecad1 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -94,11 +94,15 @@ void netplay_log_connection(const struct sockaddr_storage *their_addr, } if (str) + { snprintf(s, len, msg_hash_to_str(MSG_GOT_CONNECTION_FROM_NAME), nick, str); + } else + { snprintf(s, len, msg_hash_to_str(MSG_GOT_CONNECTION_FROM), nick); + } } #else @@ -195,12 +199,11 @@ static uint32_t simple_rand_uint32(void) * Initialize our handshake and send the first part of the handshake protocol. */ bool netplay_handshake_init_send(netplay_t *netplay, - struct netplay_connection *connection, - const char *netplay_password, - const char *netplay_spectate_password) + struct netplay_connection *connection) { uint32_t header[6]; - unsigned conn_salt = 0; + unsigned conn_salt = 0; + settings_t *settings = config_get_ptr(); header[0] = htonl(netplay_magic); header[1] = htonl(netplay_platform_magic()); @@ -210,8 +213,8 @@ bool netplay_handshake_init_send(netplay_t *netplay, header[5] = htonl(netplay_impl_magic()); if (netplay->is_server && - (netplay_password[0] || - netplay_spectate_password[0])) + (settings->paths.netplay_password[0] || + settings->paths.netplay_spectate_password[0])) { /* Demand a password */ if (simple_rand_next == 1) @@ -493,7 +496,7 @@ static void netplay_handshake_ready(netplay_t *netplay, * * Send an INFO command. */ -static bool netplay_handshake_info(netplay_t *netplay, +bool netplay_handshake_info(netplay_t *netplay, struct netplay_connection *connection) { struct info_buf_s info_buf; @@ -542,7 +545,7 @@ static bool netplay_handshake_info(netplay_t *netplay, * * Send a SYNC command. */ -static bool netplay_handshake_sync(netplay_t *netplay, +bool netplay_handshake_sync(netplay_t *netplay, struct netplay_connection *connection) { /* If we're the server, now we send sync info */ @@ -687,11 +690,8 @@ static bool netplay_handshake_sync(netplay_t *netplay, * Data receiver for the second stage of handshake, receiving the other side's * nickname. */ -static bool netplay_handshake_pre_nick(netplay_t *netplay, - struct netplay_connection *connection, bool *had_input, - const char *netplay_password, - const char *netplay_spectate_password - ) +bool netplay_handshake_pre_nick(netplay_t *netplay, + struct netplay_connection *connection, bool *had_input) { struct nick_buf_s nick_buf; ssize_t recvd; @@ -724,9 +724,11 @@ static bool netplay_handshake_pre_nick(netplay_t *netplay, if (netplay->is_server) { + settings_t *settings = config_get_ptr(); + /* There's a password, so just put them in PRE_PASSWORD mode */ - if ( netplay_password[0] || - netplay_spectate_password[0]) + if ( settings->paths.netplay_password[0] || + settings->paths.netplay_spectate_password[0]) connection->mode = NETPLAY_CONNECTION_PRE_PASSWORD; else { @@ -751,10 +753,8 @@ static bool netplay_handshake_pre_nick(netplay_t *netplay, * Data receiver for the third, optional stage of server handshake, receiving * the password and sending core/content info. */ -static bool netplay_handshake_pre_password(netplay_t *netplay, - struct netplay_connection *connection, bool *had_input, - const char *netplay_password, - const char *netplay_spectate_password) +bool netplay_handshake_pre_password(netplay_t *netplay, + struct netplay_connection *connection, bool *had_input) { struct password_buf_s password_buf; char password[8+NETPLAY_PASS_LEN]; /* 8 for salt */ @@ -762,6 +762,7 @@ static bool netplay_handshake_pre_password(netplay_t *netplay, ssize_t recvd; char msg[512]; bool correct = false; + settings_t *settings = config_get_ptr(); msg[0] = '\0'; @@ -788,10 +789,10 @@ static bool netplay_handshake_pre_password(netplay_t *netplay, correct = false; snprintf(password, sizeof(password), "%08X", connection->salt); - if (netplay_password[0]) + if (settings->paths.netplay_password[0]) { strlcpy(password + 8, - netplay_password, sizeof(password)-8); + settings->paths.netplay_password, sizeof(password)-8); sha256_hash(hash, (uint8_t *) password, strlen(password)); @@ -801,10 +802,10 @@ static bool netplay_handshake_pre_password(netplay_t *netplay, connection->can_play = true; } } - if (netplay_spectate_password[0]) + if (settings->paths.netplay_spectate_password[0]) { strlcpy(password + 8, - netplay_spectate_password, sizeof(password)-8); + settings->paths.netplay_spectate_password, sizeof(password)-8); sha256_hash(hash, (uint8_t *) password, strlen(password)); @@ -832,7 +833,7 @@ static bool netplay_handshake_pre_password(netplay_t *netplay, * Data receiver for the third stage of server handshake, receiving * the password. */ -static bool netplay_handshake_pre_info(netplay_t *netplay, +bool netplay_handshake_pre_info(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) { struct info_buf_s info_buf; @@ -936,9 +937,8 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, * Data receiver for the client's third handshake stage, receiving the * synchronization information. */ -static bool netplay_handshake_pre_sync(netplay_t *netplay, - struct netplay_connection *connection, bool *had_input, - bool netplay_start_as_spectator) +bool netplay_handshake_pre_sync(netplay_t *netplay, + struct netplay_connection *connection, bool *had_input) { uint32_t cmd[2]; uint32_t new_frame_count, client_num; @@ -1069,8 +1069,7 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay, snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_NETPLAY_CHANGED_NICK), netplay->nick); RARCH_LOG("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false, NULL, - MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } /* Now check the SRAM */ @@ -1103,8 +1102,7 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay, uint32_t quickbuf; while (remote_sram_size > 0) { - RECV(&quickbuf, (remote_sram_size > sizeof(uint32_t)) - ? sizeof(uint32_t) : remote_sram_size) + RECV(&quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); @@ -1125,15 +1123,18 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay, #endif /* We're ready! */ - *had_input = true; + *had_input = true; netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; - connection->mode = NETPLAY_CONNECTION_PLAYING; + connection->mode = NETPLAY_CONNECTION_PLAYING; netplay_handshake_ready(netplay, connection); netplay_recv_flush(&connection->recv_packet_buffer); /* Ask to switch to playing mode if we should */ - if (!netplay_start_as_spectator) - return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING); + { + settings_t *settings = config_get_ptr(); + if (!settings->bools.netplay_start_as_spectator) + return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING); + } return true; } @@ -1146,7 +1147,7 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay, bool netplay_handshake(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) { - bool ret = false; + bool ret = false; switch (connection->mode) { @@ -1154,32 +1155,16 @@ bool netplay_handshake(netplay_t *netplay, ret = netplay_handshake_init(netplay, connection, had_input); break; case NETPLAY_CONNECTION_PRE_NICK: - { - settings_t *settings = config_get_ptr(); - ret = netplay_handshake_pre_nick(netplay, connection, had_input, - settings->paths.netplay_password, - settings->paths.netplay_spectate_password - ); - } + ret = netplay_handshake_pre_nick(netplay, connection, had_input); break; case NETPLAY_CONNECTION_PRE_PASSWORD: - { - settings_t *settings = config_get_ptr(); - ret = netplay_handshake_pre_password(netplay, connection, had_input, - settings->paths.netplay_password, - settings->paths.netplay_spectate_password - ); - } + ret = netplay_handshake_pre_password(netplay, connection, had_input); break; case NETPLAY_CONNECTION_PRE_INFO: ret = netplay_handshake_pre_info(netplay, connection, had_input); break; case NETPLAY_CONNECTION_PRE_SYNC: - { - settings_t *settings = config_get_ptr(); - ret = netplay_handshake_pre_sync(netplay, connection, had_input, - settings->bools.netplay_start_as_spectator); - } + ret = netplay_handshake_pre_sync(netplay, connection, had_input); break; case NETPLAY_CONNECTION_NONE: default: diff --git a/network/netplay/netplay_init.c b/network/netplay/netplay_init.c index 35f3e304ab..3b506097bd 100644 --- a/network/netplay/netplay_init.c +++ b/network/netplay/netplay_init.c @@ -418,8 +418,6 @@ static bool netplay_init_buffers(netplay_t *netplay) netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, bool stateless_mode, int check_frames, const struct retro_callbacks *cb, bool nat_traversal, const char *nick, - const char *netplay_password, - const char *netplay_spectate_password, uint64_t quirks) { netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay)); @@ -489,9 +487,7 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, else { /* Start our handshake */ - netplay_handshake_init_send(netplay, &netplay->connections[0], - netplay_password, - netplay_spectate_password); + netplay_handshake_init_send(netplay, &netplay->connections[0]); netplay->connections[0].mode = NETPLAY_CONNECTION_INIT; netplay->self_mode = NETPLAY_CONNECTION_INIT; diff --git a/network/netplay/netplay_io.c b/network/netplay/netplay_io.c index 6ff14f7f53..526d5cd4e6 100644 --- a/network/netplay/netplay_io.c +++ b/network/netplay/netplay_io.c @@ -730,20 +730,20 @@ static void handle_play_spectate(netplay_t *netplay, uint32_t client_num, case NETPLAY_CMD_PLAY: { uint32_t mode, devices = 0, device; - uint8_t share_mode = 0; - bool slave = false; + uint8_t share_mode; + bool slave = false; settings_t *settings = config_get_ptr(); if (cmd_size != sizeof(uint32_t) || !in_payload) return; - mode = ntohl(in_payload[0]); + mode = ntohl(in_payload[0]); /* Check the requested mode */ - slave = (mode & NETPLAY_CMD_PLAY_BIT_SLAVE)?true:false; - share_mode = (mode>>16) & 0xFF; + slave = (mode&NETPLAY_CMD_PLAY_BIT_SLAVE)?true:false; + share_mode = (mode>>16)&0xFF; /* And the requested devices */ - devices = mode & 0xFFFF; + devices = mode&0xFFFF; /* Check if their slave mode request corresponds with what we allow */ if (connection) @@ -964,8 +964,8 @@ static bool netplay_get_cmd(netplay_t *netplay, return false; RECV(&client_num, sizeof(client_num)) return false; - frame_num = ntohl(frame_num); - client_num = ntohl(client_num); + frame_num = ntohl(frame_num); + client_num = ntohl(client_num); client_num &= 0xFFFF; if (netplay->is_server) @@ -1246,9 +1246,8 @@ static bool netplay_get_cmd(netplay_t *netplay, if (frame < netplay->self_frame_count) netplay->force_rewind = true; - mode = ntohl(payload[1]); + mode = ntohl(payload[1]); client_num = mode & 0xFFFF; - if (client_num >= MAX_CLIENTS) { RARCH_ERR("Received NETPLAY_CMD_MODE for a higher player number than we support.\n"); diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index 0413bc0908..df67ef7d71 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -723,7 +723,7 @@ uint8_t netplay_settings_share_mode(unsigned share_digital, unsigned share_analo * * Poll the network if necessary. */ -void input_poll_net(netplay_t *netplay); +void input_poll_net(void); /*************************************************************** * NETPLAY-HANDSHAKE.C @@ -735,9 +735,7 @@ void input_poll_net(netplay_t *netplay); * Initialize our handshake and send the first part of the handshake protocol. */ bool netplay_handshake_init_send(netplay_t *netplay, - struct netplay_connection *connection, - const char *netplay_password, - const char *netplay_spectate_password); + struct netplay_connection *connection); /** * netplay_handshake @@ -790,8 +788,6 @@ bool netplay_wait_and_init_serialization(netplay_t *netplay); netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, bool stateless_mode, int check_frames, const struct retro_callbacks *cb, bool nat_traversal, const char *nick, - const char *netplay_password, - const char *netplay_spectate_password, uint64_t quirks); /** @@ -969,10 +965,7 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim); * * Pre-frame for Netplay synchronization. */ -bool netplay_sync_pre_frame(netplay_t *netplay, - const char *netplay_password, - const char *netplay_spectate_password - ); +bool netplay_sync_pre_frame(netplay_t *netplay); /** * netplay_sync_post_frame diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 227ca1da7e..7e3beb9f58 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -551,10 +551,7 @@ static void netplay_handle_frame_hash(netplay_t *netplay, * * Pre-frame for Netplay synchronization. */ -bool netplay_sync_pre_frame(netplay_t *netplay, - const char *netplay_password, - const char *netplay_spectate_password - ) +bool netplay_sync_pre_frame(netplay_t *netplay) { retro_ctx_serialize_info_t serial_info; @@ -729,15 +726,14 @@ bool netplay_sync_pre_frame(netplay_t *netplay, goto process; } - netplay_handshake_init_send(netplay, connection, - netplay_password, netplay_spectate_password); + netplay_handshake_init_send(netplay, connection); } } process: netplay->can_poll = true; - input_poll_net(netplay); + input_poll_net(); return (netplay->stall != NETPLAY_STALL_NO_CONNECTION); } @@ -752,7 +748,6 @@ process: void netplay_sync_post_frame(netplay_t *netplay, bool stalled) { uint32_t lo_frame_count, hi_frame_count; - retro_time_t current_time = cpu_features_get_time_usec(); /* Unless we're stalling, we've just finished running a frame */ if (!stalled) @@ -879,14 +874,15 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) while (netplay->replay_frame_count < netplay->run_frame_count) { - retro_time_t tm; + retro_time_t start, tm; struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr]; - retro_time_t start = current_time; serial_info.data = ptr->state; serial_info.size = netplay->state_size; serial_info.data_const = NULL; + start = cpu_features_get_time_usec(); + /* Remember the current state */ memset(serial_info.data, 0, serial_info.size); core_serialize(&serial_info); @@ -923,7 +919,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) #endif /* Get our time window */ - tm = current_time - start; + tm = cpu_features_get_time_usec() - start; netplay->frame_run_time_sum -= netplay->frame_run_time[netplay->frame_run_time_ptr]; netplay->frame_run_time[netplay->frame_run_time_ptr] = tm; netplay->frame_run_time_sum += tm; @@ -983,6 +979,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) { if (netplay->self_frame_count + 3 < lo_frame_count) { + retro_time_t cur_time = cpu_features_get_time_usec(); uint32_t cur_behind = lo_frame_count - netplay->self_frame_count; /* We're behind, but we'll only try to catch up if we're actually @@ -990,12 +987,11 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) if (netplay->catch_up_time == 0) { /* Record our current time to check for catch-up later */ - netplay->catch_up_time = current_time; + netplay->catch_up_time = cur_time; netplay->catch_up_behind = cur_behind; } - else if (current_time - netplay->catch_up_time - > CATCH_UP_CHECK_TIME_USEC) + else if (cur_time - netplay->catch_up_time > CATCH_UP_CHECK_TIME_USEC) { /* Time to check how far behind we are */ if (netplay->catch_up_behind <= cur_behind) @@ -1009,7 +1005,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) else { /* Check again in another period */ - netplay->catch_up_time = current_time; + netplay->catch_up_time = cur_time; netplay->catch_up_behind = cur_behind; } } diff --git a/retroarch.c b/retroarch.c index 99991e3454..f4fc16710c 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1276,9 +1276,6 @@ static struct bsv_state bsv_movie_state; /* Forward declarations */ -#ifdef HAVE_NETWORKING -static void netplay_disconnect(netplay_t *netplay); -#endif static void retroarch_core_options_intl_init(const struct retro_core_options_intl *core_options_intl); static void ui_companion_driver_toggle(bool force); @@ -3311,31 +3308,11 @@ static uint64_t runahead_last_frame_count = 0; #if defined(HAVE_NETWORKING) /* NETWORKING GLOBAL VARIABLES */ -/* Used to avoid recursive netplay calls */ -static bool in_netplay = false; - static bool has_set_netplay_mode = false; static bool has_set_netplay_ip_address = false; static bool has_set_netplay_ip_port = false; static bool has_set_netplay_stateless_mode = false; static bool has_set_netplay_check_frames = false; - -/* Only used before init_netplay */ -static bool netplay_enabled = false; -static bool netplay_is_client = false; - -/* Used */ -static bool netplay_is_mitm = false; - -/* Used while Netplay is running */ -static netplay_t *netplay_data = NULL; - -/* Used for deferred netplay initialization */ -static bool netplay_client_deferred = false; -static char server_address_deferred[512] = ""; -static unsigned server_port_deferred = 0; - -#define netplay_driver_ctl_internal(a, b) (in_netplay ? true : netplay_driver_ctl(a, b)) #endif /* INPUT REMOTE GLOBAL VARIABLES */ @@ -4026,1507 +4003,6 @@ static const struct cmd_map map[] = { }; #endif -#ifdef HAVE_NETWORKING -static bool netplay_is_alive(netplay_t *netplay) -{ - if (!netplay) - return false; - return (netplay->is_server) || - (!netplay->is_server && netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED); -} - -/** - * If we're fast-forward replaying to resync, check if we - * should actually show frame. - */ -static bool netplay_should_skip(netplay_t *netplay) -{ - return netplay->is_replay && (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED); -} - -static bool netplay_get_self_input_state(netplay_t *netplay) -{ - unsigned i; - struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; - netplay_input_state_t istate = NULL; - uint32_t devices, used_devices = 0, devi, dev_type, local_device; - - if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) - return false; - - /* We've already read this frame! */ - if (ptr->have_local) - return true; - - devices = netplay->self_devices; - used_devices = 0; - - for (devi = 0; devi < MAX_INPUT_DEVICES; devi++) - { - if (!(devices & (1<config_devices[devi]&RETRO_DEVICE_MASK; - - for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++) - { - if (used_devices & (1<config_devices[local_device] & RETRO_DEVICE_MASK) - == dev_type) - break; - } - - if (local_device == MAX_INPUT_DEVICES) - local_device = 0; - used_devices |= (1<real_input[devi], - /* If we're a slave, we write our own input to - * MAX_CLIENTS to keep it separate */ - (netplay->self_mode==NETPLAY_CONNECTION_SLAVE) - ? MAX_CLIENTS - : netplay->self_client_num, - netplay_expected_input_size(netplay, 1 << devi), - true, false); - if (!istate) - continue; /* FIXME: More severe? */ - - /* First frame we always give zero input since relying on - * input from first frame screws up when we use -F 0. */ - if ( !input_driver_is_libretro_input_blocked() - && netplay->self_frame_count > 0) - { - uint32_t *state = istate->data; - retro_input_state_t cb = netplay->cbs.state_cb; - unsigned dtype = netplay->config_devices[devi] & - RETRO_DEVICE_MASK; - - switch (dtype) - { - case RETRO_DEVICE_ANALOG: - for (i = 0; i < 2; i++) - { - int16_t tmp_x = cb(local_device, - RETRO_DEVICE_ANALOG, (unsigned)i, 0); - int16_t tmp_y = cb(local_device, - RETRO_DEVICE_ANALOG, (unsigned)i, 1); - state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); - } - /* no break */ - - case RETRO_DEVICE_JOYPAD: - for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++) - { - int16_t tmp = cb(local_device, - RETRO_DEVICE_JOYPAD, 0, (unsigned)i); - state[0] |= tmp ? 1 << i : 0; - } - break; - - case RETRO_DEVICE_MOUSE: - case RETRO_DEVICE_LIGHTGUN: - { - int16_t tmp_x = cb(local_device, dtype, 0, 0); - int16_t tmp_y = cb(local_device, dtype, 0, 1); - state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); - for (i = 2; - i <= (unsigned)((dtype == RETRO_DEVICE_MOUSE) ? - RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN : - RETRO_DEVICE_ID_LIGHTGUN_START); - i++) - { - int16_t tmp = cb(local_device, dtype, 0, - (unsigned) i); - state[0] |= tmp ? 1 << i : 0; - } - break; - } - - case RETRO_DEVICE_KEYBOARD: - { - unsigned key, word = 0, bit = 1; - for (key = 1; key < NETPLAY_KEY_LAST; key++) - { - state[word] |= - cb(local_device, RETRO_DEVICE_KEYBOARD, - 0, netplay_key_ntoh(key)) ? - (1U << bit) : 0; - bit++; - if (bit >= 32) - { - bit = 0; - word++; - if (word >= istate->size) - break; - } - } - break; - } - } - } - } - - ptr->have_local = true; - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) - { - ptr->have_real[netplay->self_client_num] = true; - netplay->read_ptr[netplay->self_client_num] = - NEXT_PTR(netplay->self_ptr); - netplay->read_frame_count[netplay->self_client_num] = - netplay->self_frame_count + 1; - } - - /* And send this input to our peers */ - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if ( connection->active && - connection->mode >= NETPLAY_CONNECTION_CONNECTED) - netplay_send_cur_input(netplay, &netplay->connections[i]); - } - - /* Handle any delayed state changes */ - if (netplay->is_server) - netplay_delayed_state_change(netplay); - - return true; -} - -static bool init_netplay_deferred(const char* server, unsigned port) -{ - if (!string_is_empty(server) && port != 0) - { - strlcpy(server_address_deferred, server, - sizeof(server_address_deferred)); - server_port_deferred = port; - netplay_client_deferred = true; - } - else - netplay_client_deferred = false; - return netplay_client_deferred; -} - -static void netplay_poll(netplay_t *netplay) -{ - size_t i; - uint32_t client; - bool blocking = false; - retro_time_t current_time = cpu_features_get_time_usec(); - - /* Grab our own input state and send this frame's input state - * (self and remote) over the network */ - if (!netplay_get_self_input_state(netplay)) - goto catastrophe; - - /* If we're not connected, we're done */ - if (netplay->self_mode == NETPLAY_CONNECTION_NONE) - return; - - /* Read Netplay input, block if we're configured - * to stall for input every frame */ - netplay_update_unread_ptr(netplay); - - blocking = netplay->stateless_mode && - (netplay->connected_players>1) && - netplay->unread_frame_count <= netplay->run_frame_count; - - if (netplay_poll_net_input(netplay, blocking) == -1) - goto catastrophe; - - /* Resolve and/or simulate the input if we don't have real input */ - netplay_resolve_input(netplay, netplay->run_ptr, false); - - /* Handle any slaves */ - if (netplay->is_server && netplay->connected_slaves) - netplay_handle_slaves(netplay); - - netplay_update_unread_ptr(netplay); - - /* Figure out how many frames of input latency we should be using to hide - * network latency */ - if (netplay->frame_run_time_avg || netplay->stateless_mode) - { - /* FIXME: Using fixed 60fps for this calculation */ - unsigned frames_per_frame = netplay->frame_run_time_avg ? - (16666 / netplay->frame_run_time_avg) : - 0; - unsigned frames_ahead = (netplay->run_frame_count > netplay->unread_frame_count) ? - (netplay->run_frame_count - netplay->unread_frame_count) : - 0; - settings_t *settings = configuration_settings; - unsigned netplay_frames_min = settings->uints.netplay_input_latency_frames_min; - unsigned netplay_frames_range = settings->uints.netplay_input_latency_frames_range; - bool runahead_enabled = settings->bools.run_ahead_enabled; - unsigned runahead_frames = settings->uints.run_ahead_frames; - int input_latency_frames_min = netplay_frames_min - - (runahead_enabled ? runahead_frames : 0); - int input_latency_frames_max = input_latency_frames_min + netplay_frames_range; - - /* Assume we need a couple frames worth of time to actually run the - * current frame */ - if (frames_per_frame > 2) - frames_per_frame -= 2; - else - frames_per_frame = 0; - - /* Shall we adjust our latency? */ - if (netplay->stateless_mode) - { - /* In stateless mode, we adjust up if we're "close" and down if we - * have a lot of slack */ - if (netplay->input_latency_frames < input_latency_frames_min || - (netplay->unread_frame_count == netplay->run_frame_count + 1 && - netplay->input_latency_frames < input_latency_frames_max)) - netplay->input_latency_frames++; - else if (netplay->input_latency_frames > input_latency_frames_max || - (netplay->unread_frame_count > netplay->run_frame_count + 2 && - netplay->input_latency_frames > input_latency_frames_min)) - netplay->input_latency_frames--; - } - else if (netplay->input_latency_frames < input_latency_frames_min || - (frames_per_frame < frames_ahead && - netplay->input_latency_frames < input_latency_frames_max)) - { - /* We can't hide this much network latency with replay, so hide some - * with input latency */ - netplay->input_latency_frames++; - } - else if (netplay->input_latency_frames > input_latency_frames_max || - (frames_per_frame > frames_ahead + 2 && - netplay->input_latency_frames > input_latency_frames_min)) - { - /* We don't need this much latency (any more) */ - netplay->input_latency_frames--; - } - } - - /* If we're stalled, consider unstalling */ - switch (netplay->stall) - { - case NETPLAY_STALL_RUNNING_FAST: - { - if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2 - > netplay->self_frame_count) - { - netplay->stall = NETPLAY_STALL_NONE; - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if (connection->active && connection->stall) - connection->stall = NETPLAY_STALL_NONE; - } - } - break; - } - - case NETPLAY_STALL_SPECTATOR_WAIT: - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->unread_frame_count > netplay->self_frame_count) - netplay->stall = NETPLAY_STALL_NONE; - break; - - case NETPLAY_STALL_INPUT_LATENCY: - /* Just let it recalculate momentarily */ - netplay->stall = NETPLAY_STALL_NONE; - break; - - case NETPLAY_STALL_SERVER_REQUESTED: - /* See if the stall is done */ - if (netplay->connections[0].stall_frame == 0) - { - /* Stop stalling! */ - netplay->connections[0].stall = NETPLAY_STALL_NONE; - netplay->stall = NETPLAY_STALL_NONE; - } - else - netplay->connections[0].stall_frame--; - break; - case NETPLAY_STALL_NO_CONNECTION: - default: /* not stalling */ - break; - } - - /* If we're not stalled, consider stalling */ - if (!netplay->stall) - { - /* Have we not read enough latency frames? */ - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING && - netplay->connected_players && - netplay->run_frame_count + netplay->input_latency_frames > netplay->self_frame_count) - { - netplay->stall = NETPLAY_STALL_INPUT_LATENCY; - netplay->stall_time = 0; - } - - /* Are we too far ahead? */ - if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - <= netplay->self_frame_count) - { - netplay->stall = NETPLAY_STALL_RUNNING_FAST; - netplay->stall_time = current_time; - - /* Figure out who to blame */ - if (netplay->is_server) - { - for (client = 1; client < MAX_CLIENTS; client++) - { - struct netplay_connection *connection; - if (!(netplay->connected_players & (1<read_frame_count[client] > - netplay->unread_frame_count) - continue; - connection = &netplay->connections[client-1]; - if (connection->active && - connection->mode == NETPLAY_CONNECTION_PLAYING) - { - connection->stall = NETPLAY_STALL_RUNNING_FAST; - connection->stall_time = netplay->stall_time; - } - } - } - - } - - /* If we're a spectator, are we ahead at all? */ - if (!netplay->is_server && - (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING || - netplay->self_mode == NETPLAY_CONNECTION_SLAVE) && - netplay->unread_frame_count <= netplay->self_frame_count) - { - netplay->stall = NETPLAY_STALL_SPECTATOR_WAIT; - netplay->stall_time = current_time; - } - } - - /* If we're stalling, consider disconnection */ - if (netplay->stall && netplay->stall_time) - { - retro_time_t now = current_time; - - /* Don't stall out while they're paused */ - if (netplay->remote_paused) - netplay->stall_time = now; - else if ( - now - netplay->stall_time >= - (netplay->is_server - ? MAX_SERVER_STALL_TIME_USEC - : MAX_CLIENT_STALL_TIME_USEC) - ) - { - /* Stalled out! */ - if (!netplay->is_server) - goto catastrophe; - - { - bool fixed = false; - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if (connection->active && - connection->mode == NETPLAY_CONNECTION_PLAYING && - connection->stall) - { - netplay_hangup(netplay, connection); - fixed = true; - } - } - - if (fixed) - { - /* Not stalled now */ - netplay->stall = NETPLAY_STALL_NONE; - return; - } - } - } - } - - return; - -catastrophe: - for (i = 0; i < netplay->connections_size; i++) - netplay_hangup(netplay, &netplay->connections[i]); -} - -/* Poll the network if necessary */ -void input_poll_net(netplay_t *netplay) -{ - if (!netplay_should_skip(netplay) && netplay->can_poll) - { - netplay->can_poll = false; - - /* Polls network to see if we have anything new. If our - * network buffer is full, we simply have to block - * for new input data. - */ - netplay_poll(netplay); - } -} - -/* Netplay polling callbacks */ -static void video_frame_net(const void *data, unsigned width, - unsigned height, size_t pitch) -{ - netplay_t *netplay = netplay_data; - if (!netplay_should_skip(netplay)) - netplay->cbs.frame_cb(data, width, height, pitch); -} - -static void audio_sample_net(int16_t left, int16_t right) -{ - netplay_t *netplay = netplay_data; - if (!netplay_should_skip(netplay) && !netplay->stall) - netplay->cbs.sample_cb(left, right); -} - -static size_t audio_sample_batch_net(const int16_t *data, size_t frames) -{ - netplay_t *netplay = netplay_data; - if (!netplay_should_skip(netplay) && !netplay->stall) - return netplay->cbs.sample_batch_cb(data, frames); - return frames; -} - -static int16_t netplay_input_state(netplay_t *netplay, - unsigned port, unsigned device, - unsigned idx, unsigned id) -{ - netplay_input_state_t istate; - struct delta_frame *delta = NULL; - const uint32_t *curr_input_state = NULL; - size_t ptr = netplay->is_replay - ? netplay->replay_ptr - : netplay->run_ptr; - - if (port >= MAX_INPUT_DEVICES) - return 0; - - /* If the port doesn't seem to correspond to the device, "correct" it. This - * is common with devices that typically only have one instance, such as - * keyboards, mice and lightguns. */ - if (device != RETRO_DEVICE_JOYPAD && - (netplay->config_devices[port]&RETRO_DEVICE_MASK) != device) - { - for (port = 0; port < MAX_INPUT_DEVICES; port++) - { - if ((netplay->config_devices[port]&RETRO_DEVICE_MASK) == device) - break; - } - if (port == MAX_INPUT_DEVICES) - return 0; - } - - delta = &netplay->buffer[ptr]; - istate = delta->resolved_input[port]; - if (!istate || !istate->used || istate->size == 0) - return 0; - - curr_input_state = istate->data; - - switch (device) - { - case RETRO_DEVICE_JOYPAD: - if (id == RETRO_DEVICE_ID_JOYPAD_MASK) - return curr_input_state[0]; - return ((1 << id) & curr_input_state[0]) ? 1 : 0; - case RETRO_DEVICE_ANALOG: - if (istate->size == 3) - { - uint32_t state = curr_input_state[1 + idx]; - return (int16_t)(uint16_t)(state >> (id * 16)); - } - break; - case RETRO_DEVICE_MOUSE: - case RETRO_DEVICE_LIGHTGUN: - if (istate->size == 2) - { - if (id <= RETRO_DEVICE_ID_MOUSE_Y) - return (int16_t)(uint16_t)(curr_input_state[1] >> (id * 16)); - return ((1 << id) & curr_input_state[0]) ? 1 : 0; - } - break; - case RETRO_DEVICE_KEYBOARD: - { - unsigned key = netplay_key_hton(id); - if (key != NETPLAY_KEY_UNKNOWN) - { - unsigned word = key / 32; - unsigned bit = key % 32; - if (word <= istate->size) - return ((1U<len == 0) - { - free(task_data); - return; - } - - buf = (char*)calloc(1, data->len + 1); - - memcpy(buf, data->data, data->len); - - lines = string_split(buf, "\n"); - - if (lines->size == 0) - { - string_list_free(lines); - free(buf); - free(task_data); - return; - } - - memset(host_room, 0, sizeof(*host_room)); - - for (i = 0; i < lines->size; i++) - { - const char *line = lines->elems[i].data; - - if (!string_is_empty(line)) - { - struct string_list - *kv = string_split(line, "="); - const char *key = NULL; - const char *val = NULL; - - if (!kv) - continue; - - if (kv->size != 2) - { - string_list_free(kv); - continue; - } - - key = kv->elems[0].data; - val = kv->elems[1].data; - - if (string_is_equal(key, "id")) - sscanf(val, "%i", &host_room->id); - if (string_is_equal(key, "username")) - strlcpy(host_room->nickname, val, - sizeof(host_room->nickname)); - if (string_is_equal(key, "ip")) - strlcpy(host_room->address, val, - sizeof(host_room->address)); - if (string_is_equal(key, "mitm_ip")) - { - mitm_ip = strdup(val); - strlcpy(host_room->mitm_address, val, - sizeof(host_room->mitm_address)); - } - if (string_is_equal(key, "port")) - sscanf(val, "%i", &host_room->port); - if (string_is_equal(key, "mitm_port")) - { - mitm_port = strdup(val); - sscanf(mitm_port, "%i", &host_room->mitm_port); - } - if (string_is_equal(key, "core_name")) - strlcpy(host_room->corename, val, - sizeof(host_room->corename)); - if (string_is_equal(key, "frontend")) - strlcpy(host_room->frontend, val, - sizeof(host_room->frontend)); - if (string_is_equal(key, "core_version")) - strlcpy(host_room->coreversion, val, - sizeof(host_room->coreversion)); - if (string_is_equal(key, "game_name")) - strlcpy(host_room->gamename, val, - sizeof(host_room->gamename)); - if (string_is_equal(key, "game_crc")) - sscanf(val, "%08d", &host_room->gamecrc); - if (string_is_equal(key, "host_method")) - sscanf(val, "%i", &host_room->host_method); - if (string_is_equal(key, "has_password")) - { - if ( string_is_equal_noncase(val, "true") - || string_is_equal(val, "1")) - host_room->has_password = true; - else - host_room->has_password = false; - } - if (string_is_equal(key, "has_spectate_password")) - { - if ( string_is_equal_noncase(val, "true") || - string_is_equal(val, "1")) - host_room->has_spectate_password = true; - else - host_room->has_spectate_password = false; - } - if (string_is_equal(key, "fixed")) - { - if ( string_is_equal_noncase(val, "true") - || string_is_equal(val, "1")) - host_room->fixed = true; - else - host_room->fixed = false; - } - if (string_is_equal(key, "retroarch_version")) - strlcpy(host_room->retroarch_version, - val, sizeof(host_room->retroarch_version)); - if (string_is_equal(key, "country")) - strlcpy(host_room->country, - val, sizeof(host_room->country)); - - string_list_free(kv); - } - } - - if (mitm_ip && mitm_port) - { - ip_len = (unsigned)strlen(mitm_ip); - port_len = (unsigned)strlen(mitm_port); - - /* Enable Netplay client mode */ - if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) - { - command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); - netplay_is_mitm = true; - host_room->host_method = NETPLAY_HOST_METHOD_MITM; - } - - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL); - - host_string = (char*)calloc(1, ip_len + port_len + 2); - - memcpy(host_string, mitm_ip, ip_len); - memcpy(host_string + ip_len, "|", 1); - memcpy(host_string + ip_len + 1, mitm_port, port_len); - - /* Enable Netplay */ - command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, - (void*)host_string); - command_event(CMD_EVENT_NETPLAY_INIT, (void*)host_string); - - free(host_string); - } - -#ifdef HAVE_DISCORD - if (discord_is_inited) - { - discord_userdata_t userdata; - userdata.status = DISCORD_PRESENCE_NETPLAY_HOSTING; - command_event(CMD_EVENT_DISCORD_UPDATE, &userdata); - } -#endif - - string_list_free(lines); - free(buf); - free(task_data); - if (mitm_ip) - free(mitm_ip); - if (mitm_port) - free(mitm_port); - } -} - -static void netplay_announce(void) -{ - char buf[4600]; - char frontend_architecture[PATH_MAX_LENGTH]; - char frontend_architecture_tmp[PATH_MAX_LENGTH]; - const frontend_ctx_driver_t *frontend_drv = NULL; - char url[2048] = "http://lobby.libretro.com/add/"; - char *username = NULL; - char *corename = NULL; - char *gamename = NULL; - char *subsystemname = NULL; - char *coreversion = NULL; - char *frontend_ident = NULL; - settings_t *settings = configuration_settings; - struct retro_system_info *system = &runloop_system.info; - uint32_t content_crc = content_get_crc(); - struct string_list *subsystem = path_get_subsystem_list(); - - buf[0] = '\0'; - - if (subsystem) - { - unsigned i; - - for (i = 0; i < subsystem->size; i++) - { - strlcat(buf, path_basename(subsystem->elems[i].data), sizeof(buf)); - if (i < subsystem->size - 1) - strlcat(buf, "|", sizeof(buf)); - } - net_http_urlencode(&gamename, buf); - net_http_urlencode(&subsystemname, path_get(RARCH_PATH_SUBSYSTEM)); - content_crc = 0; - } - else - { - net_http_urlencode(&gamename, - !string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))) ? - path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A"); - net_http_urlencode(&subsystemname, "N/A"); - } - - frontend_drv = - (const frontend_ctx_driver_t*)frontend_driver_get_cpu_architecture_str( - frontend_architecture_tmp, sizeof(frontend_architecture_tmp)); - snprintf(frontend_architecture, sizeof(frontend_architecture), "%s %s", - frontend_drv->ident, frontend_architecture_tmp); - -#ifdef HAVE_DISCORD - if (discord_is_ready()) - net_http_urlencode(&username, discord_get_own_username()); - else -#endif - net_http_urlencode(&username, settings->paths.username); - net_http_urlencode(&corename, system->library_name); - net_http_urlencode(&coreversion, system->library_version); - net_http_urlencode(&frontend_ident, frontend_architecture); - - buf[0] = '\0'; - - snprintf(buf, sizeof(buf), "username=%s&core_name=%s&core_version=%s&" - "game_name=%s&game_crc=%08X&port=%d&mitm_server=%s" - "&has_password=%d&has_spectate_password=%d&force_mitm=%d" - "&retroarch_version=%s&frontend=%s&subsystem_name=%s", - username, corename, coreversion, gamename, content_crc, - settings->uints.netplay_port, - settings->arrays.netplay_mitm_server, - *settings->paths.netplay_password ? 1 : 0, - *settings->paths.netplay_spectate_password ? 1 : 0, - settings->bools.netplay_use_mitm_server, - PACKAGE_VERSION, frontend_architecture, subsystemname); - task_push_http_post_transfer(url, buf, true, NULL, - netplay_announce_cb, NULL); - - if (username) - free(username); - if (corename) - free(corename); - if (gamename) - free(gamename); - if (coreversion) - free(coreversion); - if (frontend_ident) - free(frontend_ident); -} - -static int16_t input_state_net(unsigned port, unsigned device, - unsigned idx, unsigned id) -{ - netplay_t *netplay = netplay_data; - /* Check if input port/index is controlled by netplay or not. */ - if (netplay_is_alive(netplay)) - return netplay_input_state(netplay, port, device, idx, id); - return netplay->cbs.state_cb(port, device, idx, id); -} - -/* Inform Netplay of the frontend's pause state (paused or otherwise) */ -static void netplay_frontend_paused(netplay_t *netplay, bool paused) -{ - size_t i; - uint32_t paused_ct = 0; - - /* Nothing to do if we already knew this */ - if (netplay->local_paused == paused) - return; - - netplay->local_paused = paused; - - /* Communicating this is a bit odd: If exactly one other connection is - * paused, then we must tell them that we're unpaused, as from their - * perspective we are. If more than one other connection is paused, then our - * status as proxy means we are NOT unpaused to either of them. */ - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if (connection->active && connection->paused) - paused_ct++; - } - if (paused_ct > 1) - return; - - /* Send our unpaused status. Must send manually - * because we must immediately flush the buffer: - * If we're paused, we won't be polled. */ - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if ( connection->active && - connection->mode >= NETPLAY_CONNECTION_CONNECTED) - { - if (paused) - netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PAUSE, - netplay->nick, NETPLAY_NICK_LEN); - else - netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_RESUME, - NULL, 0); - - /* We're not going to be polled, so we need - * to flush this command now */ - netplay_send_flush(&connection->send_packet_buffer, - connection->fd, true); - } - } -} - -static bool netplay_pre_frame(netplay_t *netplay) -{ - static int reannounce = 0; - bool sync_stalled = false; - settings_t *settings = configuration_settings; - bool netplay_public_announce = settings->bools.netplay_public_announce; - bool netplay_use_mitm_server = settings->bools.netplay_use_mitm_server; - const char *netplay_password = settings->paths.netplay_password; - const char *netplay_spectate_pw = settings->paths.netplay_spectate_password; - - retro_assert(netplay); - - if (netplay_public_announce) - { - reannounce++; - if ((netplay->is_server || netplay_is_mitm) && (reannounce % 600 == 0)) - netplay_announce(); - } - /* Make sure that if announcement is turned on mid-game, it gets announced */ - else - reannounce = -1; - - /* FIXME: This is an ugly way to learn we're not paused anymore */ - if (netplay->local_paused) - netplay_frontend_paused(netplay, false); - - /* Are we ready now? */ - if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) - netplay_try_init_serialization(netplay); - - if (netplay->is_server && !netplay_use_mitm_server) - { - /* Advertise our server */ - netplay_lan_ad_server(netplay); - - /* NAT traversal if applicable */ - if (netplay->nat_traversal && - !netplay->nat_traversal_task_oustanding && - netplay->nat_traversal_state.request_outstanding && - !netplay->nat_traversal_state.have_inet4) - { - struct timeval tmptv = {0}; - fd_set fds = netplay->nat_traversal_state.fds; - if (socket_select(netplay->nat_traversal_state.nfds, - &fds, NULL, NULL, &tmptv) > 0) - natt_read(&netplay->nat_traversal_state); - -#ifndef HAVE_SOCKET_LEGACY - if (!netplay->nat_traversal_state.request_outstanding || - netplay->nat_traversal_state.have_inet4) - netplay_announce_nat_traversal(netplay); -#endif - } - } - - sync_stalled = !netplay_sync_pre_frame(netplay, - netplay_password, - netplay_spectate_pw - ); - - /* If we're disconnected, deinitialize */ - if (!netplay->is_server && !netplay->connections[0].active) - { - netplay_disconnect(netplay); - return true; - } - - if (sync_stalled || - ((!netplay->is_server || (netplay->connected_players>1)) && - (netplay->stall || netplay->remote_paused))) - { - /* We may have received data even if we're stalled, - * so run post-frame sync */ - netplay_sync_post_frame(netplay, true); - return false; - } - return true; -} - -static void netplay_post_frame(netplay_t *netplay) -{ - size_t i; - retro_assert(netplay); - netplay_update_unread_ptr(netplay); - netplay_sync_post_frame(netplay, false); - - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if (connection->active && - !netplay_send_flush(&connection->send_packet_buffer, - connection->fd, false)) - netplay_hangup(netplay, connection); - } - - /* If we're disconnected, deinitialize */ - if (!netplay->is_server && !netplay->connections[0].active) - netplay_disconnect(netplay); -} - -/** - * netplay_force_future - * @netplay : pointer to netplay object - * - * Force netplay to ignore all past input, typically because - * we've just loaded a state or reset. - */ -static void netplay_force_future(netplay_t *netplay) -{ - /* Wherever we're inputting, that's where we consider our - * state to be loaded */ - netplay->run_ptr = netplay->self_ptr; - netplay->run_frame_count = netplay->self_frame_count; - - /* We need to ignore any intervening data from the other side, - * and never rewind past this */ - netplay_update_unread_ptr(netplay); - - if (netplay->unread_frame_count < netplay->run_frame_count) - { - uint32_t client; - for (client = 0; client < MAX_CLIENTS; client++) - { - if (!(netplay->connected_players & (1<read_frame_count[client] < netplay->run_frame_count) - { - netplay->read_ptr[client] = netplay->run_ptr; - netplay->read_frame_count[client] = netplay->run_frame_count; - } - } - if (netplay->server_frame_count < netplay->run_frame_count) - { - netplay->server_ptr = netplay->run_ptr; - netplay->server_frame_count = netplay->run_frame_count; - } - netplay_update_unread_ptr(netplay); - } - if (netplay->other_frame_count < netplay->run_frame_count) - { - netplay->other_ptr = netplay->run_ptr; - netplay->other_frame_count = netplay->run_frame_count; - } -} - -static void netplay_send_savestate(netplay_t *netplay, - retro_ctx_serialize_info_t *serial_info, uint32_t cx, - struct compression_transcoder *z) -{ - uint32_t header[4]; - uint32_t rd, wn; - size_t i; - - /* Compress it */ - z->compression_backend->set_in(z->compression_stream, - (const uint8_t*)serial_info->data_const, (uint32_t)serial_info->size); - z->compression_backend->set_out(z->compression_stream, - netplay->zbuffer, (uint32_t)netplay->zbuffer_size); - if (!z->compression_backend->trans(z->compression_stream, true, &rd, - &wn, NULL)) - { - /* Catastrophe! */ - for (i = 0; i < netplay->connections_size; i++) - netplay_hangup(netplay, &netplay->connections[i]); - return; - } - - /* Send it to relevant peers */ - header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE); - header[1] = htonl(wn + 2*sizeof(uint32_t)); - header[2] = htonl(netplay->run_frame_count); - header[3] = htonl(serial_info->size); - - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if (!connection->active || - connection->mode < NETPLAY_CONNECTION_CONNECTED || - connection->compression_supported != cx) - continue; - - if (!netplay_send(&connection->send_packet_buffer, connection->fd, header, - sizeof(header)) || - !netplay_send(&connection->send_packet_buffer, connection->fd, - netplay->zbuffer, wn)) - netplay_hangup(netplay, connection); - } -} - -/** - * netplay_load_savestate - * @netplay : pointer to netplay object - * @serial_info : the savestate being loaded, NULL means - * "load it yourself" - * @save : Whether to save the provided serial_info - * into the frame buffer - * - * Inform Netplay of a savestate load and send it to the other side - **/ -void netplay_load_savestate(netplay_t *netplay, - retro_ctx_serialize_info_t *serial_info, bool save) -{ - retro_ctx_serialize_info_t tmp_serial_info; - - netplay_force_future(netplay); - - /* Record it in our own buffer */ - if (save || !serial_info) - { - /* FIXME: This is a critical failure! */ - if (!netplay_delta_frame_ready(netplay, - &netplay->buffer[netplay->run_ptr], netplay->run_frame_count)) - return; - - if (!serial_info) - { - tmp_serial_info.size = netplay->state_size; - tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state; - if (!core_serialize(&tmp_serial_info)) - return; - tmp_serial_info.data_const = tmp_serial_info.data; - serial_info = &tmp_serial_info; - } - else - { - if (serial_info->size <= netplay->state_size) - memcpy(netplay->buffer[netplay->run_ptr].state, - serial_info->data_const, serial_info->size); - } - } - - /* Don't send it if we're expected to be desynced */ - if (netplay->desync) - return; - - /* If we can't send it to the peer, loading a state was a bad idea */ - if (netplay->quirks & ( - NETPLAY_QUIRK_NO_SAVESTATES - | NETPLAY_QUIRK_NO_TRANSMISSION)) - return; - - /* Send this to every peer - * - * Send a loaded savestate to those connected peers using the - * given compression scheme. - */ - if (netplay->compress_nil.compression_backend) - netplay_send_savestate(netplay, serial_info, 0, &netplay->compress_nil); - if (netplay->compress_zlib.compression_backend) - netplay_send_savestate(netplay, serial_info, NETPLAY_COMPRESSION_ZLIB, - &netplay->compress_zlib); -} - -static void netplay_core_reset(netplay_t *netplay) -{ - uint32_t cmd[3]; - size_t i; - - /* Ignore past input */ - netplay_force_future(netplay); - - /* Request that our peers reset */ - cmd[0] = htonl(NETPLAY_CMD_RESET); - cmd[1] = htonl(sizeof(uint32_t)); - cmd[2] = htonl(netplay->self_frame_count); - - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if (!connection->active || - connection->mode < NETPLAY_CONNECTION_CONNECTED) - continue; - - if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd, - sizeof(cmd))) - netplay_hangup(netplay, connection); - } -} - -/** - * netplay_settings_share_mode - * - * Get the preferred share mode - */ -uint8_t netplay_settings_share_mode(unsigned share_digital, - unsigned share_analog) -{ - if (share_digital || share_analog) - { - uint8_t share_mode = 0; - - switch (share_digital) - { - case RARCH_NETPLAY_SHARE_DIGITAL_OR: - share_mode |= NETPLAY_SHARE_DIGITAL_OR; - break; - case RARCH_NETPLAY_SHARE_DIGITAL_XOR: - share_mode |= NETPLAY_SHARE_DIGITAL_XOR; - break; - case RARCH_NETPLAY_SHARE_DIGITAL_VOTE: - share_mode |= NETPLAY_SHARE_DIGITAL_VOTE; - break; - default: - share_mode |= NETPLAY_SHARE_NO_PREFERENCE; - } - - switch (share_analog) - { - case RARCH_NETPLAY_SHARE_ANALOG_MAX: - share_mode |= NETPLAY_SHARE_ANALOG_MAX; - break; - case RARCH_NETPLAY_SHARE_ANALOG_AVERAGE: - share_mode |= NETPLAY_SHARE_ANALOG_AVERAGE; - break; - default: - share_mode |= NETPLAY_SHARE_NO_PREFERENCE; - } - - return share_mode; - } - return 0; -} - -/* Toggle between play mode and spectate mode */ -static void netplay_toggle_play_spectate(netplay_t *netplay) -{ - switch (netplay->self_mode) - { - case NETPLAY_CONNECTION_PLAYING: - case NETPLAY_CONNECTION_SLAVE: - /* Switch to spectator mode immediately */ - netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; - netplay_cmd_mode(netplay, NETPLAY_CONNECTION_SPECTATING); - break; - case NETPLAY_CONNECTION_SPECTATING: - /* Switch only after getting permission */ - netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING); - break; - default: - break; - } -} - -static void netplay_disconnect(netplay_t *netplay) -{ - size_t i; - - for (i = 0; i < netplay->connections_size; i++) - netplay_hangup(netplay, &netplay->connections[i]); - - deinit_netplay(); - -#ifdef HAVE_DISCORD - if (discord_is_inited) - { - discord_userdata_t userdata; - userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED; - command_event(CMD_EVENT_DISCORD_UPDATE, &userdata); - } -#endif -} - -void deinit_netplay(void) -{ - if (netplay_data) - { - netplay_free(netplay_data); - netplay_enabled = false; - netplay_is_client = false; - netplay_is_mitm = false; - } - netplay_data = NULL; - core_unset_netplay_callbacks(); -} - -/** - * init_netplay - * @direct_host : Host to connect to directly, if applicable (client only) - * @server : server address to connect to (client only) - * @port : TCP port to host on/connect to - * - * Initializes netplay. - * - * If netplay is already initialized, will return false (0). - * - * Returns: true (1) if successful, otherwise false (0). - **/ -static bool init_netplay(void *direct_host, const char *server, unsigned port) -{ - struct retro_callbacks cbs = {0}; - settings_t *settings = configuration_settings; - uint64_t serialization_quirks = 0; - uint64_t quirks = 0; - bool _netplay_is_client = netplay_is_client; - bool _netplay_enabled = netplay_enabled; - const char *username = settings->paths.username; - bool netplay_public_announce = settings->bools.netplay_public_announce; - bool netplay_start_as_spectator = settings->bools.netplay_start_as_spectator; - bool netplay_stateless_mode = settings->bools.netplay_stateless_mode; - bool netplay_nat_traversal = settings->bools.netplay_nat_traversal; - bool netplay_use_mitm_server = settings->bools.netplay_use_mitm_server; - const char *netplay_password = settings->paths.netplay_password; - const char *netplay_spectate_pw = settings->paths.netplay_spectate_password; - int netplay_check_frames = settings->ints.netplay_check_frames; -#ifdef HAVE_DISCORD - const char *discord_username = discord_get_own_username(); - const char *path_username = discord_username - ? discord_username - : username; -#else - const char *path_username = username; -#endif - - if (!_netplay_enabled) - return false; - - core_set_default_callbacks(&cbs); - if (!core_set_netplay_callbacks()) - return false; - - /* Map the core's quirks to our quirks */ - serialization_quirks = core_serialization_quirks(); - if (serialization_quirks & ~((uint64_t) NETPLAY_QUIRK_MAP_UNDERSTOOD)) - { - /* Quirks we don't support! Just disable everything. */ - quirks |= NETPLAY_QUIRK_NO_SAVESTATES; - } - if (serialization_quirks & NETPLAY_QUIRK_MAP_NO_SAVESTATES) - quirks |= NETPLAY_QUIRK_NO_SAVESTATES; - if (serialization_quirks & NETPLAY_QUIRK_MAP_NO_TRANSMISSION) - quirks |= NETPLAY_QUIRK_NO_TRANSMISSION; - if (serialization_quirks & NETPLAY_QUIRK_MAP_INITIALIZATION) - quirks |= NETPLAY_QUIRK_INITIALIZATION; - if (serialization_quirks & NETPLAY_QUIRK_MAP_ENDIAN_DEPENDENT) - quirks |= NETPLAY_QUIRK_ENDIAN_DEPENDENT; - if (serialization_quirks & NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT) - quirks |= NETPLAY_QUIRK_PLATFORM_DEPENDENT; - - if (_netplay_is_client) - { - RARCH_LOG("[netplay] %s\n", - msg_hash_to_str(MSG_CONNECTING_TO_NETPLAY_HOST)); - } - else - { - RARCH_LOG("[netplay] %s\n", - msg_hash_to_str(MSG_WAITING_FOR_CLIENT)); - runloop_msg_queue_push( - msg_hash_to_str(MSG_WAITING_FOR_CLIENT), - 0, 180, false, - NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - - if (netplay_public_announce) - netplay_announce(); - } - - netplay_data = (netplay_t*)netplay_new( - _netplay_is_client ? direct_host : NULL, - _netplay_is_client ? (!netplay_client_deferred ? server - : server_address_deferred) : NULL, - _netplay_is_client ? (!netplay_client_deferred ? port - : server_port_deferred ) - : (port != 0 ? port : RARCH_DEFAULT_PORT), - netplay_stateless_mode, - netplay_check_frames, - &cbs, - netplay_nat_traversal - && !netplay_use_mitm_server, - path_username, - netplay_password, - netplay_spectate_pw, - quirks); - - if (netplay_data) - { - if ( netplay_data->is_server && - !netplay_start_as_spectator) - netplay_toggle_play_spectate(netplay_data); - return true; - } - - RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); - - runloop_msg_queue_push( - msg_hash_to_str(MSG_NETPLAY_FAILED), - 0, 180, false, - NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - return false; -} - -/* Frontend access to Netplay functionality */ -bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) -{ - bool ret = true; - netplay_t *netplay = netplay_data; - - in_netplay = true; - - switch (state) - { - case RARCH_NETPLAY_CTL_ENABLE_SERVER: - if (!netplay) - { - netplay_enabled = true; - netplay_is_client = false; - } - break; - case RARCH_NETPLAY_CTL_ENABLE_CLIENT: - if (!netplay) - { - netplay_enabled = true; - netplay_is_client = true; - } - break; - case RARCH_NETPLAY_CTL_IS_DATA_INITED: - if (!netplay) - ret = false; - break; - case RARCH_NETPLAY_CTL_IS_ENABLED: - if (!netplay) - ret = netplay_enabled; - break; - case RARCH_NETPLAY_CTL_IS_REPLAYING: - if (netplay) - ret = netplay->is_replay; - else - ret = false; - break; - case RARCH_NETPLAY_CTL_DISABLE: - if (netplay) - ret = false; - else - { - netplay_enabled = false; -#ifdef HAVE_DISCORD - if (discord_is_inited) - { - discord_userdata_t userdata; - userdata.status = DISCORD_PRESENCE_NETPLAY_NETPLAY_STOPPED; - command_event(CMD_EVENT_DISCORD_UPDATE, &userdata); - } -#endif - } - break; - case RARCH_NETPLAY_CTL_IS_CONNECTED: - if (netplay) - ret = netplay->is_connected; - else - ret = false; - break; - case RARCH_NETPLAY_CTL_IS_SERVER: - return netplay_enabled && !netplay_is_client; - case RARCH_NETPLAY_CTL_POST_FRAME: - if (netplay) - { - /* We check if we have new input and replay from recorded input. - * Call this after running retro_run(). - */ - netplay_post_frame(netplay); - } - break; - case RARCH_NETPLAY_CTL_PRE_FRAME: - if (netplay) - { - /* - * Call netplay_pre_frame before running retro_run(). - * - * Returns true if the frontend is cleared to - * render the frame, false if we're stalled or paused - */ - ret = netplay_pre_frame(netplay); - } - break; - case RARCH_NETPLAY_CTL_GAME_WATCH: - if (netplay) - netplay_toggle_play_spectate(netplay); - break; - case RARCH_NETPLAY_CTL_PAUSE: - if (netplay) - netplay_frontend_paused(netplay, true); - break; - case RARCH_NETPLAY_CTL_UNPAUSE: - if (netplay) - netplay_frontend_paused(netplay, false); - break; - case RARCH_NETPLAY_CTL_LOAD_SAVESTATE: - if (netplay) - netplay_load_savestate(netplay, - (retro_ctx_serialize_info_t*)data, true); - break; - case RARCH_NETPLAY_CTL_RESET: - /* Indicate that the core has been reset to netplay peers */ - if (netplay) - netplay_core_reset(netplay); - break; - case RARCH_NETPLAY_CTL_DISCONNECT: - if (netplay) - netplay_disconnect(netplay); - break; - case RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL: - if (netplay) - { - netplay->nat_traversal_task_oustanding = false; -#ifndef HAVE_SOCKET_LEGACY - netplay_announce_nat_traversal(netplay); -#endif - } - break; - case RARCH_NETPLAY_CTL_DESYNC_PUSH: - if (netplay) - netplay->desync++; - break; - case RARCH_NETPLAY_CTL_DESYNC_POP: - if (netplay && netplay->desync) - { - netplay->desync--; - if (!netplay->desync) - netplay_load_savestate(netplay, NULL, true); - } - break; - case RARCH_NETPLAY_CTL_NONE: - if (netplay) - ret = false; - break; - default: - if (netplay) - ret = false; - break; - } - - in_netplay = false; - return ret; -} -#endif - bool retroarch_apply_shader(enum rarch_shader_type type, const char *preset_path, bool message) { #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) @@ -7484,7 +5960,7 @@ static void command_event_init_cheats(void) bool apply_cheats_after_load = settings->bools.apply_cheats_after_load; const char *path_cheat_db = settings->paths.path_cheat_database; #ifdef HAVE_NETWORKING - allow_cheats &= !netplay_driver_ctl_internal( + allow_cheats &= !netplay_driver_ctl( RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL); #endif allow_cheats &= !(bsv_movie_state_handle != NULL); @@ -7515,7 +5991,7 @@ static void command_event_load_auto_state(void) return; #endif #ifdef HAVE_NETWORKING - if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) return; #endif @@ -8154,8 +6630,7 @@ static void command_event_undo_load_state(char *s, size_t len) } #ifdef HAVE_NETWORKING - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); #endif strlcpy(s, @@ -8217,8 +6692,7 @@ static bool command_event_main_state(unsigned cmd) #endif ret = true; #ifdef HAVE_NETWORKING - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); #endif { settings_t *settings = configuration_settings; @@ -8655,8 +7129,7 @@ bool command_event(enum event_command cmd, void *data) rcheevos_reset_game(); #endif #if HAVE_NETWORKING - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_RESET, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_RESET, NULL); #endif return false; case CMD_EVENT_SAVE_STATE: @@ -8811,7 +7284,7 @@ bool command_event(enum event_command cmd, void *data) #ifdef HAVE_NETWORKING /* Only enable state manager if netplay is not underway TODO/FIXME: Add a setting for these tweaks */ - if (!netplay_driver_ctl_internal( + if (!netplay_driver_ctl( RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) #endif { @@ -8842,7 +7315,7 @@ bool command_event(enum event_command cmd, void *data) /* Only enable state manager if netplay is not underway TODO/FIXME: Add a setting for these tweaks */ if ( (autosave_interval != 0) - && !netplay_driver_ctl_internal( + && !netplay_driver_ctl( RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) #endif runloop_autosave = autosave_init(); @@ -9347,8 +7820,7 @@ bool command_event(enum event_command cmd, void *data) break; #ifdef HAVE_NETWORKING case CMD_EVENT_NETPLAY_GAME_WATCH: - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_GAME_WATCH, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_GAME_WATCH, NULL); break; case CMD_EVENT_NETPLAY_DEINIT: deinit_netplay(); @@ -9465,10 +7937,9 @@ bool command_event(enum event_command cmd, void *data) content_get_status(&contentless, &is_inited); - if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_SERVER, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_SERVER, NULL); /* If we haven't yet started, this will load on its own */ if (!is_inited) @@ -9488,11 +7959,8 @@ bool command_event(enum event_command cmd, void *data) } case CMD_EVENT_NETPLAY_DISCONNECT: { - if (!in_netplay) - { - netplay_driver_ctl(RARCH_NETPLAY_CTL_DISCONNECT, NULL); - netplay_driver_ctl(RARCH_NETPLAY_CTL_DISABLE, NULL); - } + netplay_driver_ctl(RARCH_NETPLAY_CTL_DISCONNECT, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_DISABLE, NULL); { settings_t *settings = configuration_settings; @@ -9510,12 +7978,12 @@ bool command_event(enum event_command cmd, void *data) break; } case CMD_EVENT_NETPLAY_HOST_TOGGLE: - if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) && - netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_SERVER, NULL)) + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) && + netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL)) command_event(CMD_EVENT_NETPLAY_DISCONNECT, NULL); - else if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) && - !netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_SERVER, NULL) && - netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_CONNECTED, NULL)) + else 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_CONNECTED, NULL)) command_event(CMD_EVENT_NETPLAY_DISCONNECT, NULL); else command_event(CMD_EVENT_NETPLAY_ENABLE_HOST, NULL); @@ -11950,7 +10418,7 @@ static bool rarch_environment_cb(unsigned cmd, void *data) const struct retro_audio_callback *cb = (const struct retro_audio_callback*)data; RARCH_LOG("[Environ]: SET_AUDIO_CALLBACK.\n"); #ifdef HAVE_NETWORKING - if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) return false; #endif if (recording_data) /* A/V sync is a must. */ @@ -11971,7 +10439,7 @@ static bool rarch_environment_cb(unsigned cmd, void *data) #ifdef HAVE_NETWORKING /* retro_run() will be called in very strange and * mysterious ways, have to disable it. */ - if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) return false; #endif runloop_frame_time = *info; @@ -12473,9 +10941,9 @@ static bool rarch_environment_cb(unsigned cmd, void *data) result |= 8; #endif #ifdef HAVE_NETWORKING - if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_REPLAYING, NULL)) + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_REPLAYING, NULL)) result &= ~(1|2); - if (netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) result |= 4; #endif if (data) @@ -27464,8 +25932,7 @@ static void retroarch_parse_input_and_config(int argc, char *argv[]) case 'H': retroarch_override_setting_set( RARCH_OVERRIDE_SETTING_NETPLAY_MODE, NULL); - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_SERVER, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_SERVER, NULL); break; case 'C': @@ -27475,9 +25942,7 @@ static void retroarch_parse_input_and_config(int argc, char *argv[]) RARCH_OVERRIDE_SETTING_NETPLAY_MODE, NULL); retroarch_override_setting_set( RARCH_OVERRIDE_SETTING_NETPLAY_IP_ADDRESS, NULL); - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL); - + netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL); configuration_set_string(settings, settings->paths.netplay_server, optarg); } @@ -30413,8 +28878,7 @@ int runloop_iterate(void) case RUNLOOP_STATE_POLLED_AND_SLEEP: #ifdef HAVE_NETWORKING /* FIXME: This is an ugly way to tell Netplay this... */ - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL); #endif #if defined(HAVE_COCOATOUCH) if (!main_ui_companion_is_on_foreground) @@ -30425,19 +28889,15 @@ int runloop_iterate(void) #ifdef HAVE_NETWORKING /* FIXME: This is an ugly way to tell Netplay this... */ if (settings->bools.menu_pause_libretro && - netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) + netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) ) - { - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL); - } + netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL); #endif goto end; case RUNLOOP_STATE_MENU_ITERATE: #ifdef HAVE_NETWORKING /* FIXME: This is an ugly way to tell Netplay this... */ - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL); #endif return 0; case RUNLOOP_STATE_ITERATE: @@ -30483,7 +28943,7 @@ int runloop_iterate(void) /* Run Ahead Feature replaces the call to core_run in this loop */ bool want_runahead = settings->bools.run_ahead_enabled && run_ahead_num_frames > 0; #ifdef HAVE_NETWORKING - want_runahead = want_runahead && !netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_ENABLED, NULL); + want_runahead = want_runahead && !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL); #endif if (want_runahead) @@ -30814,7 +29274,7 @@ static bool core_init_libretro_cbs(struct retro_callbacks *cbs) core_set_default_callbacks(cbs); #ifdef HAVE_NETWORKING - if (!netplay_driver_ctl_internal(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) + if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) return true; core_set_netplay_callbacks(); @@ -30987,8 +29447,7 @@ bool core_unserialize(retro_ctx_serialize_info_t *info) return false; #if HAVE_NETWORKING - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info); + netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info); #endif return true; @@ -31048,7 +29507,7 @@ bool core_run(void) bool early_polling = new_poll_type == POLL_TYPE_EARLY; bool late_polling = new_poll_type == POLL_TYPE_LATE; #ifdef HAVE_NETWORKING - bool netplay_preframe = netplay_driver_ctl_internal( + bool netplay_preframe = netplay_driver_ctl( RARCH_NETPLAY_CTL_PRE_FRAME, NULL); if (!netplay_preframe) @@ -31072,8 +29531,7 @@ bool core_run(void) input_driver_poll(); #ifdef HAVE_NETWORKING - if (!in_netplay) - netplay_driver_ctl(RARCH_NETPLAY_CTL_POST_FRAME, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_POST_FRAME, NULL); #endif return true;