From 5f723d1bda9da81f960008bf7c31431aa3cd43c8 Mon Sep 17 00:00:00 2001
From: Gregor Richards <hg-yff@gregor.im>
Date: Mon, 20 Feb 2017 19:08:31 -0500
Subject: [PATCH] Moving NAT traversal into a task to avoid blocking the UI.

---
 Makefile.common                    |  1 +
 griffin/griffin.c                  |  1 +
 network/netplay/netplay.h          |  3 +-
 network/netplay/netplay_frontend.c |  3 ++
 network/netplay/netplay_io.c       | 17 ++----
 tasks/task_netplay_nat_traversal.c | 87 ++++++++++++++++++++++++++++++
 tasks/tasks_internal.h             |  4 ++
 7 files changed, 101 insertions(+), 15 deletions(-)
 create mode 100644 tasks/task_netplay_nat_traversal.c

diff --git a/Makefile.common b/Makefile.common
index 91054f6a17..9ed9d260c1 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -1165,6 +1165,7 @@ ifeq ($(HAVE_NETWORKING), 1)
 			 network/net_http_special.o \
 			 tasks/task_http.o \
 			 tasks/task_netplay_lan_scan.o \
+			 tasks/task_netplay_nat_traversal.o \
 			 tasks/task_wifi.o \
 			 tasks/task_netplay_find_content.o
 
diff --git a/griffin/griffin.c b/griffin/griffin.c
index 7c8dc2e1e2..85ea85804b 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -911,6 +911,7 @@ NETPLAY
 #endif
 #include "../tasks/task_http.c"
 #include "../tasks/task_netplay_lan_scan.c"
+#include "../tasks/task_netplay_nat_traversal.c"
 #include "../tasks/task_wifi.c"
 #include "../tasks/task_netplay_find_content.c"
 #endif
diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h
index d873e62e99..8b72bfc50b 100644
--- a/network/netplay/netplay.h
+++ b/network/netplay/netplay.h
@@ -45,7 +45,8 @@ enum rarch_netplay_ctl_state
    RARCH_NETPLAY_CTL_UNPAUSE,
    RARCH_NETPLAY_CTL_LOAD_SAVESTATE,
    RARCH_NETPLAY_CTL_RESET,
-   RARCH_NETPLAY_CTL_DISCONNECT
+   RARCH_NETPLAY_CTL_DISCONNECT,
+   RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL
 };
 
 int16_t input_state_net(unsigned port, unsigned device,
diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c
index e02332fed0..83a9671b20 100644
--- a/network/netplay/netplay_frontend.c
+++ b/network/netplay/netplay_frontend.c
@@ -1197,6 +1197,9 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
       case RARCH_NETPLAY_CTL_DISCONNECT:
          ret = netplay_disconnect(netplay_data);
          goto done;
+      case RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL:
+         netplay_announce_nat_traversal(netplay_data);
+         goto done;
       default:
       case RARCH_NETPLAY_CTL_NONE:
          ret = false;
diff --git a/network/netplay/netplay_io.c b/network/netplay/netplay_io.c
index 4de8e02375..1e7c51f3be 100644
--- a/network/netplay/netplay_io.c
+++ b/network/netplay/netplay_io.c
@@ -25,6 +25,7 @@
 #include "netplay_private.h"
 
 #include "../../runloop.h"
+#include "../../tasks/tasks_internal.h"
 
 #if 0
 #define DEBUG_NETPLAY_STEPS 1
@@ -1430,18 +1431,6 @@ void netplay_announce_nat_traversal(netplay_t *netplay)
  */
 void netplay_init_nat_traversal(netplay_t *netplay)
 {
-   natt_init();
-
-   if (!natt_new(&netplay->nat_traversal_state))
-   {
-      netplay->nat_traversal = false;
-      return;
-   }
-
-   natt_open_port_any(&netplay->nat_traversal_state, netplay->tcp_port, SOCKET_PROTOCOL_TCP);
-
-#ifndef HAVE_SOCKET_LEGACY
-   if (!netplay->nat_traversal_state.request_outstanding)
-      netplay_announce_nat_traversal(netplay);
-#endif
+   memset(&netplay->nat_traversal_state, 0, sizeof(netplay->nat_traversal_state));
+   task_push_netplay_nat_traversal(&netplay->nat_traversal_state, netplay->tcp_port);
 }
diff --git a/tasks/task_netplay_nat_traversal.c b/tasks/task_netplay_nat_traversal.c
new file mode 100644
index 0000000000..d0dbc3ffae
--- /dev/null
+++ b/tasks/task_netplay_nat_traversal.c
@@ -0,0 +1,87 @@
+/*  RetroArch - A frontend for libretro.
+ *  Copyright (C) 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <lists/file_list.h>
+#include <string/stdstring.h>
+
+#include "tasks_internal.h"
+#include "net/net_natt.h"
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include "../network/netplay/netplay.h"
+#include "../verbosity.h"
+
+struct nat_traversal_state_data
+{
+   struct natt_status *nat_traversal_state;
+   uint16_t port;
+};
+
+static void netplay_nat_traversal_callback(void *task_data,
+                               void *user_data, const char *error)
+{
+   struct nat_traversal_state_data *ntsd =
+      (struct nat_traversal_state_data *) task_data;
+
+   free(ntsd);
+
+   netplay_driver_ctl(RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL, NULL);
+}
+
+static void task_netplay_nat_traversal_handler(retro_task_t *task)
+{
+   struct nat_traversal_state_data *ntsd =
+      (struct nat_traversal_state_data *) task->task_data;
+
+   natt_init();
+
+   if (natt_new(ntsd->nat_traversal_state))
+      natt_open_port_any(ntsd->nat_traversal_state, ntsd->port, SOCKET_PROTOCOL_TCP);
+
+   task_set_progress(task, 100);
+   task_set_finished(task, true);
+}
+
+bool task_push_netplay_nat_traversal(struct natt_status *nat_traversal_state, uint16_t port)
+{
+   struct nat_traversal_state_data *ntsd;
+   retro_task_t *task = (retro_task_t*)calloc(1, sizeof(*task));
+
+   if (!task)
+      return false;
+
+   ntsd = (struct nat_traversal_state_data *) calloc(1, sizeof(*ntsd));
+
+   if (!ntsd)
+   {
+      free(task);
+      return false;
+   }
+
+   ntsd->nat_traversal_state = nat_traversal_state;
+   ntsd->port = port;
+
+   task->type     = TASK_TYPE_BLOCKING;
+   task->handler  = task_netplay_nat_traversal_handler;
+   task->callback = netplay_nat_traversal_callback;
+   task->task_data = ntsd;
+
+   task_queue_ctl(TASK_QUEUE_CTL_PUSH, task);
+
+   return true;
+}
diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h
index 61bedf861a..9df0996709 100644
--- a/tasks/tasks_internal.h
+++ b/tasks/tasks_internal.h
@@ -25,6 +25,7 @@
 #include <queues/message_queue.h>
 #include <queues/task_queue.h>
 #include <formats/image.h>
+#include <net/net_natt.h>
 
 #include "../content.h"
 #include "../core_type.h"
@@ -101,6 +102,9 @@ bool task_push_netplay_lan_scan(void);
 bool task_push_netplay_crc_scan(uint32_t crc, char* name,
       const char *hostname, const char *corename);
 
+bool task_push_netplay_nat_traversal(struct natt_status *nat_traversal_state,
+      uint16_t port);
+
 #endif
 
 bool task_push_image_load(const char *fullpath,