From c4eb12a58366af8bcf4d22112634262e135b70dd Mon Sep 17 00:00:00 2001
From: Gregor Richards <hg-yff@gregor.im>
Date: Sat, 24 Sep 2016 23:47:57 -0400
Subject: [PATCH] Adding backend functionality to reconnect a Netplay client.

---
 network/netplay/netplay.c        | 40 +++++++++++++++++++++++++++++++-
 network/netplay/netplay.h        | 15 +++++++++++-
 network/netplay/netplay_common.c | 21 +++++++++++++++++
 3 files changed, 74 insertions(+), 2 deletions(-)

diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c
index 142d08e280..078f0192b6 100644
--- a/network/netplay/netplay.c
+++ b/network/netplay/netplay.c
@@ -997,7 +997,7 @@ netplay_t *netplay_new(const char *server, uint16_t port,
       return NULL;
 
    netplay->fd                = -1;
-   netplay->tcp_port          = server ? 0 : port;
+   netplay->tcp_port          = port;
    netplay->cbs               = *cb;
    netplay->port              = server ? 0 : 1;
    netplay->spectate.enabled  = spectate;
@@ -1277,6 +1277,42 @@ void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *seri
    }
 }
 
+/**
+ * netplay_reconnect
+ * @netplay              : pointer to netplay object
+ *
+ * Reconnect netplay. Only does anything as a client, and only if netplay isn't
+ * currently connected.
+ *
+ * Returns: true (1) if successful, false (0) if unsuccessful, false (0) if not
+ * client or already connected.
+ **/
+bool netplay_reconnect(netplay_t *netplay)
+{
+   /* FIXME: This function has some things remembered in netplay, some things
+    * brought back from global */
+   global_t *global     = global_get_ptr();
+
+   if (!netplay || netplay->has_connection || netplay->is_server)
+   {
+      return false;
+   }
+
+   if (!init_socket(netplay, global->netplay.server, netplay->tcp_port))
+   {
+      RARCH_WARN("Failed to reconnect Netplay.\n");
+      runloop_msg_queue_push("Failed to reconnect Netplay.", 0, 480, false);
+   }
+
+   /* FIXME: Connection info is weirdly conflated into info_cb */
+   if (!netplay_info_cb(netplay, netplay->stall_frames))
+   {
+      return false;
+   }
+
+   return true;
+}
+
 void deinit_netplay(void)
 {
    netplay_t *netplay = (netplay_t*)netplay_data;
@@ -1386,6 +1422,8 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
       case RARCH_NETPLAY_CTL_LOAD_SAVESTATE:
          netplay_load_savestate((netplay_t*)netplay_data, (retro_ctx_serialize_info_t*)data, true);
          break;
+      case RARCH_NETPLAY_CTL_RECONNECT:
+         return netplay_reconnect((netplay_t*)netplay_data);
       default:
       case RARCH_NETPLAY_CTL_NONE:
          break;
diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h
index a2bb44efba..19b0f05342 100644
--- a/network/netplay/netplay.h
+++ b/network/netplay/netplay.h
@@ -38,7 +38,8 @@ enum rarch_netplay_ctl_state
    RARCH_NETPLAY_CTL_IS_DATA_INITED,
    RARCH_NETPLAY_CTL_PAUSE,
    RARCH_NETPLAY_CTL_UNPAUSE,
-   RARCH_NETPLAY_CTL_LOAD_SAVESTATE
+   RARCH_NETPLAY_CTL_LOAD_SAVESTATE,
+   RARCH_NETPLAY_CTL_RECONNECT
 };
 
 enum netplay_cmd
@@ -194,6 +195,18 @@ void netplay_frontend_paused(netplay_t *netplay, bool paused);
  **/
 void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save);
 
+/**
+ * netplay_reconnect
+ * @netplay              : pointer to netplay object
+ *
+ * Reconnect netplay. Only does anything as a client, and only if netplay isn't
+ * currently connected.
+ *
+ * Returns: true (1) if successful, false (0) if unsuccessful, false (0) if not
+ * client or already connected.
+ **/
+bool netplay_reconnect(netplay_t *netplay);
+
 /**
  * init_netplay:
  *
diff --git a/network/netplay/netplay_common.c b/network/netplay/netplay_common.c
index 287bf88912..c55174a398 100644
--- a/network/netplay/netplay_common.c
+++ b/network/netplay/netplay_common.c
@@ -133,6 +133,7 @@ bool netplay_send_info(netplay_t *netplay)
    uint32_t *content_crc_ptr = NULL;
    void *sram                = NULL;
    uint32_t header[3]        = {0};
+   size_t i;
 
    mem_info.id = RETRO_MEMORY_SAVE_RAM;
 
@@ -197,6 +198,22 @@ bool netplay_send_info(netplay_t *netplay)
       return false;
    }
 
+   /* Reset our frame count so it's consistent with the server */
+   netplay->self_frame_count = netplay->read_frame_count = netplay->other_frame_count = 0;
+   for (i = 0; i < netplay->buffer_size; i++)
+   {
+      netplay->buffer[i].used = false;
+      if (i == netplay->self_ptr)
+      {
+         netplay_delta_frame_ready(netplay, &netplay->buffer[i], 0);
+         netplay->read_ptr = netplay->other_ptr = i;
+      }
+      else
+      {
+         netplay->buffer[i].used = false;
+      }
+   }
+
    snprintf(msg, sizeof(msg), "%s: \"%s\"",
          msg_hash_to_str(MSG_CONNECTED_TO),
          netplay->other_nick);
@@ -215,6 +232,9 @@ bool netplay_get_info(netplay_t *netplay)
    const void *sram          = NULL;
    size_t i;
 
+   /* FIXME: There's a huge amount of duplication between send_info and
+    * get_info */
+
    mem_info.id = RETRO_MEMORY_SAVE_RAM;
 
    core_get_memory(&mem_info);
@@ -286,6 +306,7 @@ bool netplay_get_info(netplay_t *netplay)
       if (i == netplay->self_ptr)
       {
          netplay_delta_frame_ready(netplay, &netplay->buffer[i], 0);
+         netplay->read_ptr = netplay->other_ptr = i;
       }
       else
       {