diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 2a40b1f259b96c461edd732d13f04fd291c169f1..cf1b387a601a58ac03d645cde810d1df1bba47b7 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1801,8 +1801,92 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
 	M_SortServerList();
 }
 
+#ifdef HAVE_THREADS
+struct Fetch_servers_ctx
+{
+	int room;
+	int id;
+};
+
+static void
+Fetch_servers_thread (struct Fetch_servers_ctx *ctx)
+{
+	msg_server_t *server_list;
+
+	server_list = GetShortServersList(ctx->room, ctx->id);
+
+	if (server_list)
+	{
+		I_lock_mutex(&ms_QueryId_mutex);
+		{
+			if (ctx->id != ms_QueryId)
+			{
+				free(server_list);
+				server_list = NULL;
+			}
+		}
+		I_unlock_mutex(ms_QueryId_mutex);
+
+		if (server_list)
+		{
+			I_lock_mutex(&m_menu_mutex);
+			{
+				if (m_waiting_mode == M_WAITING_SERVERS)
+					m_waiting_mode = M_NOT_WAITING;
+			}
+			I_unlock_mutex(m_menu_mutex);
+
+			I_lock_mutex(&ms_ServerList_mutex);
+			{
+				ms_ServerList = server_list;
+			}
+			I_unlock_mutex(ms_ServerList_mutex);
+		}
+	}
+
+	free(ctx);
+}
+#endif/*HAVE_THREADS*/
+
+void CL_QueryServerList (msg_server_t *server_list)
+{
+	INT32 i;
+
+	for (i = 0; server_list[i].header.buffer[0]; i++)
+	{
+		// Make sure MS version matches our own, to
+		// thwart nefarious servers who lie to the MS.
+
+		/* lol bruh, that version COMES from the servers */
+		//if (strcmp(version, server_list[i].version) == 0)
+		{
+			INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port);
+			if (node == -1)
+				break; // no more node free
+			SendAskInfo(node);
+			// Force close the connection so that servers can't eat
+			// up nodes forever if we never get a reply back from them
+			// (usually when they've not forwarded their ports).
+			//
+			// Don't worry, we'll get in contact with the working
+			// servers again when they send SERVERINFO to us later!
+			//
+			// (Note: as a side effect this probably means every
+			// server in the list will probably be using the same node (e.g. node 1),
+			// not that it matters which nodes they use when
+			// the connections are closed afterwards anyway)
+			// -- Monster Iestyn 12/11/18
+			Net_CloseConnection(node|FORCECLOSE);
+		}
+	}
+}
+
 void CL_UpdateServerList(boolean internetsearch, INT32 room)
 {
+#ifdef HAVE_THREADS
+	struct Fetch_servers_ctx *ctx;
+#endif
+
 	SL_ClearServerList(0);
 
 	if (!netgame && I_NetOpenSocket)
@@ -1820,53 +1904,32 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
 
 	if (internetsearch)
 	{
-		const msg_server_t *server_list;
-		INT32 i = -1;
-		server_list = GetShortServersList(room);
-		if (server_list)
-		{
-			char version[8] = "";
-#if VERSION > 0 || SUBVERSION > 0
-			snprintf(version, sizeof (version), "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION);
-#else
-			strcpy(version, GetRevisionString());
-#endif
-			version[sizeof (version) - 1] = '\0';
+#ifdef HAVE_THREADS
+		ctx = malloc(sizeof *ctx);
 
-			for (i = 0; server_list[i].header.buffer[0]; i++)
-			{
-				// Make sure MS version matches our own, to
-				// thwart nefarious servers who lie to the MS.
+		/* This called from M_Refresh so I don't use a mutex */
+		m_waiting_mode = M_WAITING_SERVERS;
 
-				/* lol bruh, that version COMES from the servers */
-				//if (strcmp(version, server_list[i].version) == 0)
-				{
-					INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port);
-					if (node == -1)
-						break; // no more node free
-					SendAskInfo(node);
-					// Force close the connection so that servers can't eat
-					// up nodes forever if we never get a reply back from them
-					// (usually when they've not forwarded their ports).
-					//
-					// Don't worry, we'll get in contact with the working
-					// servers again when they send SERVERINFO to us later!
-					//
-					// (Note: as a side effect this probably means every
-					// server in the list will probably be using the same node (e.g. node 1),
-					// not that it matters which nodes they use when
-					// the connections are closed afterwards anyway)
-					// -- Monster Iestyn 12/11/18
-					Net_CloseConnection(node|FORCECLOSE);
-				}
-			}
+		I_lock_mutex(&ms_QueryId_mutex);
+		{
+			ctx->id = ms_QueryId;
 		}
+		I_unlock_mutex(ms_QueryId_mutex);
+
+		ctx->room = room;
 
-		//no server list?(-1) or no servers?(0)
-		if (!i)
+		I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx);
+#else
+		msg_server_t *server_list;
+
+		server_list = GetShortServersList(room, 0);
+
+		if (server_list)
 		{
-			; /// TODO: display error or warning?
+			CL_QueryServerList(server_list);
+			free(server_list);
 		}
+#endif
 	}
 }
 
@@ -5054,7 +5117,13 @@ void NetUpdate(void)
 	if (nowtime > resptime)
 	{
 		resptime = nowtime;
+#ifdef HAVE_THREADS
+		I_lock_mutex(&m_menu_mutex);
+#endif
 		M_Ticker();
+#ifdef HAVE_THREADS
+		I_unlock_mutex(m_menu_mutex);
+#endif
 		CON_Ticker();
 	}
 
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 463240a2adc87289f8e00f319c7f90df062bf5f2..5324d8f463040c337a2a3e8f5d6340544d62a9ae 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -18,6 +18,7 @@
 #include "d_net.h"
 #include "tables.h"
 #include "d_player.h"
+#include "mserv.h"
 
 /*
 The 'packet version' is used to distinguish packet formats.
@@ -541,6 +542,7 @@ void CL_AddSplitscreenPlayer(void);
 void CL_RemoveSplitscreenPlayer(void);
 void CL_Reset(void);
 void CL_ClearPlayer(INT32 playernum);
+void CL_QueryServerList(msg_server_t *list);
 void CL_UpdateServerList(boolean internetsearch, INT32 room);
 // Is there a game running
 boolean Playing(void);
diff --git a/src/d_main.c b/src/d_main.c
index 07a7ecf91038f802742077a283c3c1691d545529..cd4b68b78e7d50b0eff5143d8851e2740c01d5ee 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -46,6 +46,7 @@ int	snprintf(char *str, size_t n, const char *fmt, ...);
 #include "hu_stuff.h"
 #include "i_sound.h"
 #include "i_system.h"
+#include "i_threads.h"
 #include "i_video.h"
 #include "m_argv.h"
 #include "m_menu.h"
@@ -174,6 +175,8 @@ void D_ProcessEvents(void)
 {
 	event_t *ev;
 
+	boolean eaten;
+
 	for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
 	{
 		ev = &events[eventtail];
@@ -189,7 +192,17 @@ void D_ProcessEvents(void)
 		}
 
 		// Menu input
-		if (M_Responder(ev))
+#ifdef HAVE_THREADS
+		I_lock_mutex(&m_menu_mutex);
+#endif
+		{
+			eaten = M_Responder(ev);
+		}
+#ifdef HAVE_THREADS
+		I_unlock_mutex(m_menu_mutex);
+#endif
+
+		if (eaten)
 			continue; // menu ate the event
 
 		// console input
@@ -510,7 +523,13 @@ static void D_Display(void)
 	// vid size change is now finished if it was on...
 	vid.recalc = 0;
 
+#ifdef HAVE_THREADS
+	I_lock_mutex(&m_menu_mutex);
+#endif
 	M_Drawer(); // menu is drawn even on top of everything
+#ifdef HAVE_THREADS
+	I_unlock_mutex(m_menu_mutex);
+#endif
 	// focus lost moved to M_Drawer
 
 	CON_Drawer();
diff --git a/src/f_finale.c b/src/f_finale.c
index 825f646b04755cbdce6222c2527dc7aed96143df..b7b9a8c6df22ddc53a7cb4227074bd7838b31ef2 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -25,6 +25,7 @@
 #include "w_wad.h"
 #include "z_zone.h"
 #include "i_system.h"
+#include "i_threads.h"
 #include "m_menu.h"
 #include "dehacked.h"
 #include "g_input.h"
@@ -959,7 +960,13 @@ void F_IntroDrawer(void)
 
 					I_OsPolling();
 					I_UpdateNoBlit();
+#ifdef HAVE_THREADS
+					I_lock_mutex(&m_menu_mutex);
+#endif
 					M_Drawer(); // menu is drawn even on top of wipes
+#ifdef HAVE_THREADS
+					I_unlock_mutex(m_menu_mutex);
+#endif
 					I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001
 
 					if (moviemode) // make sure we save frames for the white hold too
diff --git a/src/f_wipe.c b/src/f_wipe.c
index 08d7ed9913cf0f02058ea74d4906076ad205bf4f..ef16ca5fc46cc131a8b14ae99d48e629b78d01b4 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -25,6 +25,7 @@
 #include "z_zone.h"
 
 #include "i_system.h"
+#include "i_threads.h"
 #include "m_menu.h"
 #include "console.h"
 #include "d_main.h"
@@ -589,7 +590,15 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
 		I_UpdateNoBlit();
 
 		if (drawMenu)
+		{
+#ifdef HAVE_THREADS
+			I_lock_mutex(&m_menu_mutex);
+#endif
 			M_Drawer(); // menu is drawn even on top of wipes
+#ifdef HAVE_THREADS
+			I_unlock_mutex(m_menu_mutex);
+#endif
+		}
 
 		I_FinishUpdate(); // page flip or blit buffer
 
diff --git a/src/hms123311.c b/src/hms123311.c
index a1a35f2b984f115e472be5feee9c1247ddb35900..29af48e4c46af0b6cd6b2f1f0f448ea84ad660d0 100644
--- a/src/hms123311.c
+++ b/src/hms123311.c
@@ -19,6 +19,7 @@ Documentation available here.
 #include "doomdef.h"
 #include "d_clisrv.h"
 #include "command.h"
+#include "m_menu.h"
 #include "mserv.h"
 #include "i_tcp.h"/* for current_port */
 #include "z_zone.h"
@@ -193,11 +194,13 @@ HMS_end (struct HMS_buffer *buffer)
 }
 
 int
-HMS_fetch_rooms (int joining)
+HMS_fetch_rooms (int joining, int query_id)
 {
 	struct HMS_buffer *hms;
 	int ok;
 
+	int doing_shit;
+
 	char *id;
 	char *title;
 	char *room_motd;
@@ -209,6 +212,8 @@ HMS_fetch_rooms (int joining)
 
 	int i;
 
+	(void)query_id;
+
 	hms = HMS_connect("rooms");
 
 	if (! hms)
@@ -216,6 +221,8 @@ HMS_fetch_rooms (int joining)
 
 	if (HMS_do(hms))
 	{
+		doing_shit = 1;
+
 		p = hms->buffer;
 
 		for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") );)
@@ -236,6 +243,18 @@ HMS_fetch_rooms (int joining)
 				*/
 				if (joining || id_no != 0)
 				{
+#ifdef HAVE_THREADS
+					I_lock_mutex(&ms_QueryId_mutex);
+					{
+						if (query_id != ms_QueryId)
+							doing_shit = 0;
+					}
+					I_unlock_mutex(ms_QueryId_mutex);
+
+					if (! doing_shit)
+						break;
+#endif
+
 					room_list[i].header.buffer[0] = 1;
 
 					room_list[i].id = id_no;
@@ -251,9 +270,31 @@ HMS_fetch_rooms (int joining)
 				break;
 		}
 
-		room_list[i].header.buffer[0] = 0;
+		if (doing_shit)
+			room_list[i].header.buffer[0] = 0;
 
 		ok = 1;
+
+		if (doing_shit)
+		{
+#ifdef HAVE_THREADS
+			I_lock_mutex(&m_menu_mutex);
+#endif
+			{
+				for (i = 0; room_list[i].header.buffer[0]; i++)
+				{
+					if(*room_list[i].name != '\0')
+					{
+						MP_RoomMenu[i+1].text = room_list[i].name;
+						roomIds[i] = room_list[i].id;
+						MP_RoomMenu[i+1].status = IT_STRING|IT_CALL;
+					}
+				}
+			}
+#ifdef HAVE_THREADS
+			I_unlock_mutex(m_menu_mutex);
+#endif
+		}
 	}
 	else
 		ok = 0;
@@ -385,10 +426,12 @@ HMS_list_servers (void)
 }
 
 msg_server_t *
-HMS_fetch_servers (msg_server_t *list, int room_number)
+HMS_fetch_servers (msg_server_t *list, int room_number, int query_id)
 {
 	struct HMS_buffer *hms;
 
+	int doing_shit;
+
 	char local_version[9];
 
 	char *room;
@@ -404,6 +447,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number)
 
 	int i;
 
+	(void)query_id;
+
 	if (room_number > 0)
 	{
 		hms = HMS_connect("rooms/%d/servers", room_number);
@@ -416,6 +461,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number)
 
 	if (HMS_do(hms))
 	{
+		doing_shit = 1;
+
 		snprintf(local_version, sizeof local_version,
 				"%d.%d.%d",
 				VERSION/100,
@@ -448,6 +495,18 @@ HMS_fetch_servers (msg_server_t *list, int room_number)
 
 				if (address && port && title && version)
 				{
+#ifdef HAVE_THREADS
+					I_lock_mutex(&ms_QueryId_mutex);
+					{
+						if (query_id != ms_QueryId)
+							doing_shit = 0;
+					}
+					I_unlock_mutex(ms_QueryId_mutex);
+
+					if (! doing_shit)
+						break;
+#endif
+
 					if (strcmp(version, local_version) == 0)
 					{
 						strlcpy(list[i].ip,      address, sizeof list[i].ip);
@@ -474,11 +533,15 @@ HMS_fetch_servers (msg_server_t *list, int room_number)
 				}
 			}
 
+			if (! doing_shit)
+				break;
+
 			p = ( section_end + 2 );
 		}
 		while (section_end) ;
 
-		list[i].header.buffer[0] = 0;
+		if (doing_shit)
+			list[i].header.buffer[0] = 0;
 	}
 	else
 		list = NULL;
diff --git a/src/m_menu.c b/src/m_menu.c
index c8d36300ff19db936f14160861ccdbb63a0d85a0..aa36d3d6dce6e4edccacbe0674911000b24a42d7 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -32,6 +32,7 @@
 #include "sounds.h"
 #include "s_sound.h"
 #include "i_system.h"
+#include "i_threads.h"
 
 // Addfile
 #include "filesrch.h"
@@ -121,6 +122,12 @@ typedef enum
 	NUM_QUITMESSAGES
 } text_enum;
 
+#ifdef HAVE_THREADS
+I_mutex m_menu_mutex;
+#endif
+
+M_waiting_mode_t m_waiting_mode = M_NOT_WAITING;
+
 const char *quitmsg[NUM_QUITMESSAGES];
 
 // Stuff for customizing the player select screen Tails 09-22-2003
@@ -996,7 +1003,7 @@ enum
 	FIRSTSERVERLINE
 };
 
-static menuitem_t MP_RoomMenu[] =
+menuitem_t MP_RoomMenu[] =
 {
 	{IT_STRING | IT_CALL, NULL, "<Unlisted Mode>", M_ChooseRoom,   9},
 	{IT_DISABLED,         NULL, "",               M_ChooseRoom,  18},
@@ -3769,6 +3776,30 @@ void M_SetupNextMenu(menu_t *menudef)
 {
 	INT16 i;
 
+#ifdef HAVE_THREADS
+	if (currentMenu == &MP_RoomDef || currentMenu == &MP_ConnectDef)
+	{
+		I_lock_mutex(&ms_QueryId_mutex);
+		{
+			ms_QueryId++;
+		}
+		I_unlock_mutex(ms_QueryId_mutex);
+	}
+
+	if (currentMenu == &MP_ConnectDef)
+	{
+		I_lock_mutex(&ms_ServerList_mutex);
+		{
+			if (ms_ServerList)
+			{
+				free(ms_ServerList);
+				ms_ServerList = NULL;
+			}
+		}
+		I_unlock_mutex(ms_ServerList_mutex);
+	}
+#endif/*HAVE_THREADS*/
+
 	if (currentMenu->quitroutine)
 	{
 		// If you're going from a menu to itself, why are you running the quitroutine? You're not quitting it! -SH
@@ -3832,6 +3863,19 @@ void M_Ticker(void)
 
 	if (currentMenu == &OP_ScreenshotOptionsDef)
 		M_SetupScreenshotMenu();
+
+#ifdef HAVE_THREADS
+	I_lock_mutex(&ms_ServerList_mutex);
+	{
+		if (ms_ServerList)
+		{
+			CL_QueryServerList(ms_ServerList);
+			free(ms_ServerList);
+			ms_ServerList = NULL;
+		}
+	}
+	I_unlock_mutex(ms_ServerList_mutex);
+#endif
 }
 
 //
@@ -10407,22 +10451,65 @@ static INT32 menuRoomIndex = 0;
 
 static void M_DrawRoomMenu(void)
 {
+	static int frame = -12;
+	int dot_frame;
+	char text[4];
+
 	const char *rmotd;
+	const char *waiting_message;
+
+	int dots;
+
+	if (m_waiting_mode)
+	{
+		dot_frame = frame / 4;
+		dots = dot_frame + 3;
+
+		strcpy(text, "   ");
+
+		if (dots > 0)
+		{
+			if (dot_frame < 0)
+				dot_frame = 0;
+
+			strncpy(&text[dot_frame], "...", min(dots, 3 - dot_frame));
+		}
+
+		if (++frame == 12)
+			frame = -12;
+
+		currentMenu->menuitems[0].text = text;
+	}
 
 	// use generic drawer for cursor, items and title
 	M_DrawGenericMenu();
 
 	V_DrawString(currentMenu->x - 16, currentMenu->y, V_YELLOWMAP, M_GetText("Select a room"));
 
-	M_DrawTextBox(144, 24, 20, 20);
+	if (m_waiting_mode == M_NOT_WAITING)
+	{
+		M_DrawTextBox(144, 24, 20, 20);
 
-	if (itemOn == 0)
-		rmotd = M_GetText("Don't connect to the Master Server.");
-	else
-		rmotd = room_list[itemOn-1].motd;
+		if (itemOn == 0)
+			rmotd = M_GetText("Don't connect to the Master Server.");
+		else
+			rmotd = room_list[itemOn-1].motd;
+
+		rmotd = V_WordWrap(0, 20*8, 0, rmotd);
+		V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd);
+	}
 
-	rmotd = V_WordWrap(0, 20*8, 0, rmotd);
-	V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd);
+	if (m_waiting_mode)
+	{
+		// Display a little "please wait" message.
+		M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3);
+		if (m_waiting_mode == M_WAITING_VERSION)
+			waiting_message = "Checking for updates...";
+		else
+			waiting_message = "Fetching room info...";
+		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, waiting_message);
+		V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait.");
+	}
 }
 
 static void M_DrawConnectMenu(void)
@@ -10490,6 +10577,14 @@ static void M_DrawConnectMenu(void)
 	localservercount = serverlistcount;
 
 	M_DrawGenericMenu();
+
+	if (m_waiting_mode)
+	{
+		// Display a little "please wait" message.
+		M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3);
+		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "Searching for servers...");
+		V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait.");
+	}
 }
 
 static boolean M_CancelConnect(void)
@@ -10579,10 +10674,10 @@ void M_SortServerList(void)
 
 #ifndef NONET
 #ifdef UPDATE_ALERT
-static boolean M_CheckMODVersion(void)
+static boolean M_CheckMODVersion(int id)
 {
 	char updatestring[500];
-	const char *updatecheck = GetMODVersion();
+	const char *updatecheck = GetMODVersion(id);
 	if(updatecheck)
 	{
 		sprintf(updatestring, UPDATE_ALERT_STRING, VERSIONSTRING, updatecheck);
@@ -10591,7 +10686,62 @@ static boolean M_CheckMODVersion(void)
 	} else
 		return true;
 }
-#endif
+
+#ifdef HAVE_THREADS
+static void
+Check_new_version_thread (int *id)
+{
+	int hosting;
+	int ok;
+
+	ok = 0;
+
+	if (M_CheckMODVersion(*id))
+	{
+		I_lock_mutex(&ms_QueryId_mutex);
+		{
+			ok = ( *id == ms_QueryId );
+		}
+		I_unlock_mutex(ms_QueryId_mutex);
+
+		if (ok)
+		{
+			I_lock_mutex(&m_menu_mutex);
+			{
+				m_waiting_mode = M_WAITING_ROOMS;
+				hosting = ( currentMenu->prevMenu == &MP_ServerDef );
+			}
+			I_unlock_mutex(m_menu_mutex);
+
+			GetRoomsList(hosting, *id);
+		}
+	}
+	else
+	{
+		I_lock_mutex(&ms_QueryId_mutex);
+		{
+			ok = ( *id == ms_QueryId );
+		}
+		I_unlock_mutex(ms_QueryId_mutex);
+	}
+
+	if (ok)
+	{
+		I_lock_mutex(&m_menu_mutex);
+		{
+			if (m_waiting_mode)
+			{
+				m_waiting_mode = M_NOT_WAITING;
+				MP_RoomMenu[0].text = "<Offline Mode>";
+			}
+		}
+		I_unlock_mutex(m_menu_mutex);
+	}
+
+	free(id);
+}
+#endif/*HAVE_THREADS*/
+#endif/*UPDATE_ALERT*/
 
 static void M_ConnectMenu(INT32 choice)
 {
@@ -10627,11 +10777,14 @@ static void M_ConnectMenuModChecks(INT32 choice)
 	M_ConnectMenu(-1);
 }
 
-static UINT32 roomIds[NUM_LIST_ROOMS];
+UINT32 roomIds[NUM_LIST_ROOMS];
 
 static void M_RoomMenu(INT32 choice)
 {
 	INT32 i;
+#ifdef HAVE_THREADS
+	int *id;
+#endif
 
 	(void)choice;
 
@@ -10644,34 +10797,47 @@ static void M_RoomMenu(INT32 choice)
 	if (rendermode == render_soft)
 		I_FinishUpdate(); // page flip or blit buffer
 
-	if (GetRoomsList(currentMenu == &MP_ServerDef) < 0)
-		return;
-
-#ifdef UPDATE_ALERT
-	if (!M_CheckMODVersion())
-		return;
-#endif
-
 	for (i = 1; i < NUM_LIST_ROOMS+1; ++i)
 		MP_RoomMenu[i].status = IT_DISABLED;
 	memset(roomIds, 0, sizeof(roomIds));
 
-	for (i = 0; room_list[i].header.buffer[0]; i++)
+	MP_RoomDef.prevMenu = currentMenu;
+	M_SetupNextMenu(&MP_RoomDef);
+
+#ifdef UPDATE_ALERT
+#ifdef HAVE_THREADS
+	m_waiting_mode = M_WAITING_VERSION;
+	MP_RoomMenu[0].text = "";
+
+	id = malloc(sizeof *id);
+
+	I_lock_mutex(&ms_QueryId_mutex);
 	{
-		if(*room_list[i].name != '\0')
-		{
-			MP_RoomMenu[i+1].text = room_list[i].name;
-			roomIds[i] = room_list[i].id;
-			MP_RoomMenu[i+1].status = IT_STRING|IT_CALL;
-		}
+		*id = ms_QueryId;
 	}
+	I_unlock_mutex(ms_QueryId_mutex);
 
-	MP_RoomDef.prevMenu = currentMenu;
-	M_SetupNextMenu(&MP_RoomDef);
+	I_spawn_thread("check-new-version",
+			(I_thread_fn)Check_new_version_thread, id);
+#else/*HAVE_THREADS*/
+	if (M_CheckMODVersion(0))
+	{
+		GetRoomsList(currentMenu->prevMenu == &MP_ServerDef, 0);
+	}
+#endif/*HAVE_THREADS*/
+#endif/*UPDATE_ALERT*/
 }
 
 static void M_ChooseRoom(INT32 choice)
 {
+#ifdef HAVE_THREADS
+	I_lock_mutex(&ms_QueryId_mutex);
+	{
+		ms_QueryId++;
+	}
+	I_unlock_mutex(ms_QueryId_mutex);
+#endif
+
 	if (choice == 0)
 		ms_RoomId = -1;
 	else
diff --git a/src/m_menu.h b/src/m_menu.h
index 565b98945718e9612a47da82e1d306fa64a54845..b01910d8b53aa9d7f94652120301cb4ac5ae7ea2 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -18,8 +18,10 @@
 #include "doomstat.h" // for NUMGAMETYPES
 #include "d_event.h"
 #include "command.h"
-#include "r_skins.h" // for SKINNAMESIZE
 #include "f_finale.h" // for ttmode_enum
+#include "i_threads.h"
+#include "mserv.h"
+#include "r_things.h" // for SKINNAMESIZE
 
 //
 // MENUS
@@ -223,6 +225,18 @@ typedef enum
 } menumessagetype_t;
 void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype);
 
+typedef enum
+{
+	M_NOT_WAITING,
+
+	M_WAITING_VERSION,
+	M_WAITING_ROOMS,
+	M_WAITING_SERVERS,
+}
+M_waiting_mode_t;
+
+extern M_waiting_mode_t m_waiting_mode;
+
 // Called by linux_x/i_video_xshm.c
 void M_QuitResponse(INT32 ch);
 
@@ -313,6 +327,9 @@ typedef struct menuitem_s
 	UINT8 alphaKey;
 } menuitem_t;
 
+extern menuitem_t MP_RoomMenu[];
+extern UINT32     roomIds[NUM_LIST_ROOMS];
+
 typedef struct menu_s
 {
 	UINT32         menuid;             // ID to encode menu type and hierarchy
@@ -332,6 +349,10 @@ void M_ClearMenus(boolean callexitmenufunc);
 // Maybe this goes here????? Who knows.
 boolean M_MouseNeeded(void);
 
+#ifdef HAVE_THREADS
+extern I_mutex m_menu_mutex;
+#endif
+
 extern menu_t *currentMenu;
 
 extern menu_t MainDef;
diff --git a/src/mserv.c b/src/mserv.c
index 92a6399f36b51bf47f3d3f2148b9f34b614147ef..4404d9ffaa9237a046302d1fe42a19d1dc745edd 100644
--- a/src/mserv.c
+++ b/src/mserv.c
@@ -18,6 +18,7 @@
 #include "doomstat.h"
 #include "doomdef.h"
 #include "command.h"
+#include "i_threads.h"
 #include "mserv.h"
 #include "m_menu.h"
 #include "z_zone.h"
@@ -46,6 +47,14 @@ consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SA
 char *ms_API;
 INT16 ms_RoomId = -1;
 
+#ifdef HAVE_THREADS
+int           ms_QueryId;
+I_mutex       ms_QueryId_mutex;
+
+msg_server_t *ms_ServerList;
+I_mutex       ms_ServerList_mutex;
+#endif
+
 static enum { MSCS_NONE, MSCS_WAITING, MSCS_REGISTERED, MSCS_FAILED } con_state = MSCS_NONE;
 
 UINT16 current_port = 0;
@@ -72,26 +81,36 @@ void AddMServCommands(void)
 
 static void WarnGUI (void)
 {
+#ifdef HAVE_THREADS
+	I_lock_mutex(&m_menu_mutex);
+#endif
 	M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n\nCheck the console for details.\n"), NULL, MM_NOTHING);
+#ifdef HAVE_THREADS
+	I_unlock_mutex(m_menu_mutex);
+#endif
 }
 
 #define NUM_LIST_SERVER MAXSERVERLIST
-const msg_server_t *GetShortServersList(INT32 room)
+msg_server_t *GetShortServersList(INT32 room, int id)
 {
-	static msg_server_t server_list[NUM_LIST_SERVER+1]; // +1 for easy test
+	msg_server_t *server_list;
+
+	// +1 for easy test
+	server_list = malloc(( NUM_LIST_SERVER + 1 ) * sizeof *server_list);
 
-	if (HMS_fetch_servers(server_list, room))
+	if (HMS_fetch_servers(server_list, room, id))
 		return server_list;
 	else
 	{
+		free(server_list);
 		WarnGUI();
 		return NULL;
 	}
 }
 
-INT32 GetRoomsList(boolean hosting)
+INT32 GetRoomsList(boolean hosting, int id)
 {
-	if (HMS_fetch_rooms( ! hosting ))
+	if (HMS_fetch_rooms( ! hosting, id))
 		return 1;
 	else
 	{
@@ -101,17 +120,32 @@ INT32 GetRoomsList(boolean hosting)
 }
 
 #ifdef UPDATE_ALERT
-const char *GetMODVersion(void)
+char *GetMODVersion(int id)
 {
-	static char buffer[16];
+	char *buffer;
 	int c;
 
-	c = HMS_compare_mod_version(buffer, sizeof buffer);
+	(void)id;
+
+	buffer = malloc(16);
+
+	c = HMS_compare_mod_version(buffer, 16);
+
+#ifdef HAVE_THREADS
+	I_lock_mutex(&ms_QueryId_mutex);
+	{
+		if (id != ms_QueryId)
+			c = -1;
+	}
+	I_unlock_mutex(ms_QueryId_mutex);
+#endif
 
 	if (c > 0)
 		return buffer;
 	else
 	{
+		free(buffer);
+
 		if (! c)
 			WarnGUI();
 
diff --git a/src/mserv.h b/src/mserv.h
index b59b7b2b6a99081456896ca83aee7ab2ea2a6198..bce6f04ef5340c7171899ad856e0286444f62182 100644
--- a/src/mserv.h
+++ b/src/mserv.h
@@ -14,6 +14,8 @@
 #ifndef _MSERV_H_
 #define _MSERV_H_
 
+#include "i_threads.h"
+
 #define HMS123311 // don't mess with nights, man
 
 // lowered from 32 due to menu changes
@@ -75,15 +77,23 @@ extern char *ms_API;
 // anything else is whatever room the MS assigns to that number (online mode)
 extern INT16 ms_RoomId;
 
+#ifdef HAVE_THREADS
+extern int           ms_QueryId;
+extern I_mutex       ms_QueryId_mutex;
+
+extern msg_server_t *ms_ServerList;
+extern I_mutex       ms_ServerList_mutex;
+#endif
+
 void RegisterServer(void);
 void UnregisterServer(void);
 
 void MasterClient_Ticker(void);
 
-const msg_server_t *GetShortServersList(INT32 room);
-INT32 GetRoomsList(boolean hosting);
+msg_server_t *GetShortServersList(INT32 room, int id);
+INT32 GetRoomsList(boolean hosting, int id);
 #ifdef UPDATE_ALERT
-const char *GetMODVersion(void);
+char *GetMODVersion(int id);
 void GetMODVersion_Console(void);
 #endif
 extern msg_rooms_t room_list[NUM_LIST_ROOMS+1];
@@ -92,12 +102,12 @@ void AddMServCommands(void);
 
 /* HTTP */
 int  HMS_in_use (void);
-int  HMS_fetch_rooms (int joining);
+int  HMS_fetch_rooms (int joining, int id);
 int  HMS_register (void);
 void HMS_unlist (void);
 int  HMS_update (void);
 void HMS_list_servers (void);
-msg_server_t * HMS_fetch_servers (msg_server_t *list, int room);
+msg_server_t * HMS_fetch_servers (msg_server_t *list, int room, int id);
 int  HMS_compare_mod_version (char *buffer, size_t size_of_buffer);
 
 #endif