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;