diff --git a/src/cvars.cpp b/src/cvars.cpp
index 6f7e3e812..e1dbbc8af 100644
--- a/src/cvars.cpp
+++ b/src/cvars.cpp
@@ -502,6 +502,7 @@ consvar_t cv_kicktime = Server("kicktime", "20").values(CV_Unsigned);
void MasterServer_OnChange(void);
consvar_t cv_masterserver = Server("masterserver", "https://ms.kartkrew.org/ms/api").onchange(MasterServer_OnChange);
+consvar_t cv_masterserver_nagattempts = Server("masterserver_nagattempts", "5").values(CV_Unsigned);
void MasterServer_Debug_OnChange (void);
consvar_t cv_masterserver_debug = Server("masterserver_debug", "Off").on_off().onchange(MasterServer_Debug_OnChange);
@@ -540,13 +541,14 @@ consvar_t cv_server_contact = Server("server_contact", "").onchange_noinit(Updat
consvar_t cv_servername = Server("servername", "Ring Racers server").onchange_noinit(Update_parameters);
void M_SortServerList(void);
-consvar_t cv_serversort = Server("serversort", "Ping").dont_save().onchange(M_SortServerList).values({
- {0,"Ping"},
- {1,"AVG. Power Level"},
- {2,"Most Players"},
- {3,"Least Players"},
- {4,"Max Player Slots"},
- {5,"Gametype"},
+consvar_t cv_serversort = Server("serversort", "Recommended").dont_save().onchange(M_SortServerList).values({
+ {-1, "Recommended"},
+ { 0, "Ping"},
+ { 1, "AVG. Power Level"},
+ { 2, "Most Players"},
+ { 3, "Least Players"},
+ { 4, "Max Player Slots"},
+ { 5, "Gametype"},
});
// show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping)
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 92d67f224..3fc62490e 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1550,6 +1550,10 @@ static void SendAskInfo(INT32 node)
serverelem_t serverlist[MAXSERVERLIST];
UINT32 serverlistcount = 0;
+UINT32 serverlistultimatecount = 0;
+
+static boolean resendserverlistnode[MAXNETNODES];
+static tic_t serverlistepoch;
static void SL_ClearServerList(INT32 connectedserver)
{
@@ -1562,6 +1566,8 @@ static void SL_ClearServerList(INT32 connectedserver)
serverlist[i].node = 0;
}
serverlistcount = 0;
+
+ memset(resendserverlistnode, 0, sizeof resendserverlistnode);
}
static UINT32 SL_SearchServer(INT32 node)
@@ -1574,32 +1580,37 @@ static UINT32 SL_SearchServer(INT32 node)
return UINT32_MAX;
}
-static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
+static boolean SL_InsertServer(serverinfo_pak* info, SINT8 node)
{
UINT32 i;
+ resendserverlistnode[node] = false;
+
// search if not already on it
i = SL_SearchServer(node);
if (i == UINT32_MAX)
{
// not found add it
if (serverlistcount >= MAXSERVERLIST)
- return; // list full
+ return false; // list full
if (info->_255 != 255)
- return;/* old packet format */
+ return false;/* old packet format */
if (info->packetversion != PACKETVERSION)
- return;/* old new packet format */
+ return false;/* old new packet format */
if (info->version != VERSION)
- return; // Not same version.
+ return false; // Not same version.
if (info->subversion != SUBVERSION)
- return; // Close, but no cigar.
+ return false; // Close, but no cigar.
if (strcmp(info->application, SRB2APPLICATION))
- return;/* that's a different mod */
+ return false;/* that's a different mod */
+
+ if (info->modifiedgame != (mpmenu.room == 1))
+ return false;/* CORE vs MODDED! */
i = serverlistcount++;
}
@@ -1609,6 +1620,8 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
// resort server list
M_SortServerList();
+
+ return true;
}
void CL_QueryServerList (msg_server_t *server_list)
@@ -1617,6 +1630,8 @@ void CL_QueryServerList (msg_server_t *server_list)
CL_UpdateServerList();
+ serverlistepoch = I_GetTime();
+
for (i = 0; server_list[i].header.buffer[0]; i++)
{
// Make sure MS version matches our own, to
@@ -1629,19 +1644,43 @@ void CL_QueryServerList (msg_server_t *server_list)
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);
+
+ resendserverlistnode[node] = true;
+ // Leave this node open. It'll be closed if the
+ // request times out (CL_TimeoutServerList).
+ }
+ }
+
+ serverlistultimatecount = i;
+}
+
+#define SERVERLISTRESENDRATE NEWTICRATE
+
+void CL_TimeoutServerList(void)
+{
+ if (netgame && serverlistultimatecount > serverlistcount)
+ {
+ const tic_t timediff = I_GetTime() - serverlistepoch;
+ const tic_t timetoresend = timediff % SERVERLISTRESENDRATE;
+ const boolean timedout = timediff > connectiontimeout;
+
+ if (timedout || (timediff > 0 && timetoresend == 0))
+ {
+ INT32 node;
+
+ for (node = 1; node < MAXNETNODES; ++node)
+ {
+ if (resendserverlistnode[node])
+ {
+ if (timedout)
+ Net_CloseConnection(node|FORCECLOSE);
+ else
+ SendAskInfo(node);
+ }
+ }
+
+ if (timedout)
+ serverlistultimatecount = serverlistcount;
}
}
}
@@ -4187,6 +4226,11 @@ boolean SV_SpawnServer(void)
I_NetOpenSocket();
}
+ if (cv_advertise.value)
+ {
+ RegisterServer();
+ }
+
ourIP = 0;
STUN_bind(GotOurIP);
}
@@ -4609,7 +4653,9 @@ static void HandleServerInfo(SINT8 node)
memcpy(servername, netbuffer->u.serverinfo.servername, MAXSERVERNAME);
CopyCaretColors(netbuffer->u.serverinfo.servername, servername, MAXSERVERNAME);
- SL_InsertServer(&netbuffer->u.serverinfo, node);
+ // If we have cause to reject it, it's not worth observing.
+ if (SL_InsertServer(&netbuffer->u.serverinfo, node) == false)
+ serverlistultimatecount--;
}
static void PT_WillResendGamestate(void)
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index fdcf4d0fe..04221aeb9 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -460,7 +460,7 @@ struct serverelem_t
};
extern serverelem_t serverlist[MAXSERVERLIST];
-extern UINT32 serverlistcount;
+extern UINT32 serverlistcount, serverlistultimatecount;
extern INT32 mapchangepending;
// Points inside doomcom
@@ -595,6 +595,7 @@ void CL_ClearPlayer(INT32 playernum);
void CL_RemovePlayer(INT32 playernum, kickreason_t reason);
void CL_QueryServerList(msg_server_t *list);
void CL_UpdateServerList(void);
+void CL_TimeoutServerList(void);
// Is there a game running
boolean Playing(void);
diff --git a/src/f_wipe.c b/src/f_wipe.c
index 5a1df0e96..48dfc0f60 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -454,7 +454,7 @@ void F_RunWipe(UINT8 wipemode, UINT8 wipetype, boolean drawMenu, const char *col
I_OsPolling();
I_UpdateNoBlit();
- if (drawMenu)
+ if (drawMenu && rendermode != render_none)
{
#ifdef HAVE_THREADS
I_lock_mutex(&k_menu_mutex);
diff --git a/src/http-mserv.c b/src/http-mserv.c
index d47122cbf..7794a8f7c 100644
--- a/src/http-mserv.c
+++ b/src/http-mserv.c
@@ -11,7 +11,7 @@
/*
Documentation available here.
-
+
*/
#ifdef HAVE_CURL
@@ -441,7 +441,12 @@ HMS_fetch_servers (msg_server_t *list, int query_id)
break;
#endif
+//#define MSERVTESTALONE
+#ifdef MSERVTESTALONE
+ strcpy(list[i].ip, "127.0.0.1"); // MS test without needing a second person to host
+#else
strlcpy(list[i].ip, address, sizeof list[i].ip);
+#endif
strlcpy(list[i].port, port, sizeof list[i].port);
if (contact)
@@ -511,6 +516,40 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size)
return ok;
}
+const char *
+HMS_fetch_rules (char *buffer, size_t buffer_size)
+{
+ struct HMS_buffer *hms;
+
+ hms = HMS_connect("rules");
+
+ if (! hms)
+ return NULL;
+
+ boolean ok = HMS_do(hms);
+
+ if (ok)
+ {
+ char *p = strstr(hms->buffer, "\n\n");
+
+ if (p)
+ {
+ p[1] = '\0';
+
+ strlcpy(buffer, hms->buffer, buffer_size);
+ }
+ else
+ buffer = NULL;
+ }
+
+ HMS_end(hms);
+
+ if (!ok)
+ return NULL;
+
+ return buffer;
+}
+
static char *
Strip_trailing_slashes (char *api)
{
diff --git a/src/k_menu.h b/src/k_menu.h
index 875da6535..ea894fd3e 100644
--- a/src/k_menu.h
+++ b/src/k_menu.h
@@ -29,8 +29,6 @@
extern "C" {
#endif
-#define SERVERLISTDEBUG
-
// flags for items in the menu
// menu handle (what we do when key is pressed
#define IT_TYPE 14 // (2+4+8)
@@ -831,7 +829,6 @@ extern struct mpmenu_s {
// See M_OptSelectTick, it'll make more sense there. Sorry if this is a bit of a mess!
UINT8 room;
- boolean roomforced;
tic_t ticker;
UINT8 servernum;
@@ -842,6 +839,9 @@ extern struct mpmenu_s {
} mpmenu;
+void M_PleaseWait(void);
+void M_PopupMasterServerRules(void);
+
// Time Attack
void M_PrepareTimeAttack(INT32 choice);
void M_StartTimeAttack(INT32 choice);
@@ -895,11 +895,6 @@ void Fetch_servers_thread (int *id);
void M_RefreshServers(INT32 choice);
void M_ServersMenu(INT32 choice);
-// for debugging purposes...
-#ifdef SERVERLISTDEBUG
-void M_ServerListFillDebug(void);
-#endif
-
// Options menu:
// mode descriptions for video mode menu
diff --git a/src/k_menudraw.c b/src/k_menudraw.c
index 800ff3c6c..6e6e5864a 100644
--- a/src/k_menudraw.c
+++ b/src/k_menudraw.c
@@ -3086,6 +3086,23 @@ void M_DrawTimeAttack(void)
// NOTE: This is pretty rigid and only intended for use with the multiplayer options menu which has *3* choices.
+static void M_DrawMasterServerReminder(void)
+{
+ // Did you change the Server Browser address? Have a little reminder.
+
+ INT32 mservflags = 0;
+ if (CV_IsSetToDefault(&cv_masterserver))
+ mservflags = highlightflags;
+ else
+ mservflags = warningflags;
+
+ INT32 y = BASEVIDHEIGHT - 24;
+
+ V_DrawFadeFill(0, y-1, BASEVIDWIDTH, 10+1, 0, 31, 5);
+ V_DrawCenteredThinString(BASEVIDWIDTH/2, y,
+ mservflags, va("List via \"%s\"", cv_masterserver.string));
+}
+
static void M_MPOptDrawer(menu_t *m, INT16 extend[3][3])
{
// This is a copypaste of the generic gamemode menu code with a few changes.
@@ -3149,6 +3166,7 @@ void M_DrawMPOptSelect(void)
M_DrawEggaChannel();
M_DrawMenuTooltips();
M_MPOptDrawer(&PLAY_MP_OptSelectDef, mpmenu.modewinextend);
+ M_DrawMasterServerReminder();
}
// Multiplayer mode option select: HOST GAME!
@@ -3400,14 +3418,79 @@ void M_DrawMPRoomSelect(void)
// Draw buttons:
- if (!mpmenu.roomforced || mpmenu.room == 0)
- V_DrawFixedPatch(160<y+STRINGHEIGHT;
+
+ const char throbber[4] = {'-', '\\', '|', '/'};
+ UINT8 throbindex = (mpmenu.ticker/4) % 4;
+
+ switch (M_GetWaitingMode())
+ {
+ case M_WAITING_VERSION:
+ text = "Checking for updates...";
+ break;
+
+ case M_WAITING_SERVERS:
+ text = "Loading server list...";
+ break;
+
+ default:
+ if (serverlistultimatecount > serverlistcount)
+ {
+ text = va("%d/%d server%s found...",
+ serverlistcount,
+ serverlistultimatecount,
+ serverlistultimatecount == 1 ? "" : "s"
+ );
+ }
+ else
+ {
+ throbindex = UINT8_MAX; // No throbber!
+ text = va("%d server%s found",
+ serverlistcount,
+ serverlistcount == 1 ? "" : "s"
+ );
+ }
+ }
+
+ if (throbindex == UINT8_MAX)
+ {
+ V_DrawRightAlignedString(
+ BASEVIDWIDTH - currentMenu->x,
+ y,
+ highlightflags,
+ text
+ );
+ }
+ else
+ {
+ V_DrawRightAlignedString(
+ BASEVIDWIDTH - currentMenu->x - 12, y,
+ highlightflags,
+ text
+ );
+
+ V_DrawCenteredString( // Only clean way to center the throbber without exposing character width
+ BASEVIDWIDTH - currentMenu->x - 4, y,
+ highlightflags,
+ va("%c", throbber[throbindex])
+ );
+ }
+}
+
void M_DrawMPServerBrowser(void)
{
patch_t *text1 = W_CachePatchName("MENUBGT1", PU_CACHE);
@@ -3503,11 +3586,19 @@ void M_DrawMPServerBrowser(void)
V_DrawFill(0, 53, 320, 1, 31);
V_DrawFill(0, 55, 320, 1, 31);
- V_DrawCenteredGamemodeString(160, 2, 0, 0, "Server Browser");
+ const char *headertext;
+ if (M_SecretUnlocked(SECRET_ADDONS, true))
+ headertext = va("%s Servers", mpmenu.room ? "Modded" : "Core");
+ else
+ headertext = "Server Browser";
+ V_DrawCenteredGamemodeString(160, 2, 0, 0, headertext);
// normal menu options
M_DrawGenericMenu();
+ // And finally, the overlay bar!
+ M_DrawServerCountAndHorizontalBar();
+ M_DrawMasterServerReminder();
}
// OPTIONS MENU
diff --git a/src/menus/play-online-1.c b/src/menus/play-online-1.c
index 7f9a9c294..e58490187 100644
--- a/src/menus/play-online-1.c
+++ b/src/menus/play-online-1.c
@@ -4,6 +4,7 @@
#include "../k_menu.h"
#include "../m_cond.h"
#include "../s_sound.h"
+#include "../mserv.h" // cv_masterserver
#if defined (TESTERS)
#define IT_STRING_CALL_NOTESTERS IT_DISABLED
@@ -11,13 +12,74 @@
#define IT_STRING_CALL_NOTESTERS (IT_STRING | IT_CALL)
#endif // TESTERS
+static boolean firstDismissedNagThisBoot = true;
+
+static void M_HandleMasterServerResetChoice(INT32 ch)
+{
+ if (ch == MA_YES)
+ {
+ CV_Set(&cv_masterserver, cv_masterserver.defaultvalue);
+ CV_Set(&cv_masterserver_nagattempts, cv_masterserver_nagattempts.defaultvalue);
+ S_StartSound(NULL, sfx_s221);
+ }
+ else
+ {
+ if (firstDismissedNagThisBoot)
+ {
+ if (cv_masterserver_nagattempts.value > 0)
+ {
+ CV_SetValue(&cv_masterserver_nagattempts, cv_masterserver_nagattempts.value - 1);
+ }
+ firstDismissedNagThisBoot = false;
+ }
+ }
+}
+
+static void M_PreMPHostInitChoice(INT32 ch)
+{
+ M_HandleMasterServerResetChoice(ch);
+ M_MPHostInit(0);
+}
+
+static void M_PreMPHostInit(INT32 choice)
+{
+ (void)choice;
+
+ if (!CV_IsSetToDefault(&cv_masterserver) && cv_masterserver_nagattempts.value > 0)
+ {
+ M_StartMessage("Server Browser Alert", M_GetText("Hey! You've changed the game's\naddress for the Server Browser.\n\nYou won't be able to host games on\nthe official Server Browser.\n\nUnless you're from the future, this\nprobably isn't what you want.\n"), &M_PreMPHostInitChoice, MM_YESNO, "Fix and continue", "I changed the URL intentionally");
+ return;
+ }
+
+ M_MPHostInit(0);
+}
+
+static void M_PreMPRoomSelectInitChoice(INT32 ch)
+{
+ M_HandleMasterServerResetChoice(ch);
+ M_MPRoomSelectInit(0);
+}
+
+static void M_PreMPRoomSelectInit(INT32 choice)
+{
+ (void)choice;
+
+ if (!CV_IsSetToDefault(&cv_masterserver) && cv_masterserver_nagattempts.value > 0)
+ {
+ M_StartMessage("Server Browser Alert", M_GetText("Hey! You've changed the game's\naddress for the Server Browser.\n\nYou won't be able to see games from\nthe official Server Browser.\n\nUnless you're from the future, this\nprobably isn't what you want.\n"), &M_PreMPRoomSelectInitChoice, MM_YESNO, "Fix and continue", "I changed the URL intentionally");
+ return;
+ }
+
+ M_MPRoomSelectInit(0);
+}
+
menuitem_t PLAY_MP_OptSelect[] =
{
{IT_STRING_CALL_NOTESTERS, "Host Game", "Start your own online game!",
- NULL, {.routine = M_MPHostInit}, 0, 0},
+ NULL, {.routine = M_PreMPHostInit}, 0, 0},
{IT_STRING_CALL_NOTESTERS, "Server Browser", "Search for game servers to play in.",
- NULL, {.routine = M_MPRoomSelectInit}, 0, 0},
+ NULL, {.routine = M_PreMPRoomSelectInit}, 0, 0},
{IT_STRING | IT_CALL, "Join by IP", "Join an online game by its IP address.",
NULL, {.routine = M_MPJoinIPInit}, 0, 0},
diff --git a/src/menus/play-online-host.c b/src/menus/play-online-host.c
index a7061a32a..0b34f36b1 100644
--- a/src/menus/play-online-host.c
+++ b/src/menus/play-online-host.c
@@ -3,6 +3,8 @@
#include "../k_menu.h"
#include "../s_sound.h"
+#include "../z_zone.h"
+#include "../mserv.h"
// MULTIPLAYER HOST SCREEN -- see mhost_e
menuitem_t PLAY_MP_Host[] =
@@ -43,13 +45,41 @@ menu_t PLAY_MP_HostDef = {
NULL
};
+void M_PopupMasterServerRules(void)
+{
+#ifdef MASTERSERVER
+ if (cv_advertise.value && (serverrunning || currentMenu == &PLAY_MP_HostDef))
+ {
+ char *rules = GetMasterServerRules();
+
+ if (rules != NULL)
+ {
+ M_StartMessage("Server List Rules", rules, NULL, MM_NOTHING, NULL, NULL);
+ Z_Free(rules);
+ }
+ }
+#endif
+}
+
void M_MPHostInit(INT32 choice)
{
-
(void)choice;
mpmenu.modewinextend[0][0] = 1;
M_SetupNextMenu(&PLAY_MP_HostDef, true);
itemOn = mhost_go;
+
+ Get_rules();
+ // There's one downside to doing it this way:
+ // if you turn advertise on via the console,
+ // then access this menu for the first time,
+ // no rules will pop up because they haven't
+ // arrived yet.
+ M_PopupMasterServerRules();
+ // HOWEVER, this menu popup isn't for people
+ // who know how to use the Developer Console.
+ // People who CAN do that should already know
+ // what kind of service they're connecting to.
+ // (it'll still appear in the logs later, too!)
}
void M_HandleHostMenuGametype(INT32 choice)
diff --git a/src/menus/play-online-join-ip.c b/src/menus/play-online-join-ip.c
index a176d4e72..b3b0b3e8e 100644
--- a/src/menus/play-online-join-ip.c
+++ b/src/menus/play-online-join-ip.c
@@ -6,6 +6,7 @@
#include "../i_system.h" // I_OsPolling
#include "../i_video.h" // I_UpdateNoBlit
#include "../m_misc.h" // NUMLOGIP
+#include "../f_finale.h" // g_wipeskiprender
menuitem_t PLAY_MP_JoinIP[] =
{
@@ -56,6 +57,21 @@ void M_MPJoinIPInit(INT32 choice)
M_SetupNextMenu(&PLAY_MP_JoinIPDef, true);
}
+void M_PleaseWait(void)
+{
+ if (rendermode == render_none)
+ return;
+
+ g_wipeskiprender = true;
+
+ M_DrawTextBox(56, BASEVIDHEIGHT/2-12, 24, 2);
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "PLEASE WAIT...");
+ I_OsPolling();
+ I_UpdateNoBlit();
+ if (rendermode == render_soft)
+ I_FinishUpdate(); // page flip or blit buffer
+}
+
// Attempts to join a given IP from the menu.
void M_JoinIP(const char *ipa)
{
@@ -67,13 +83,7 @@ void M_JoinIP(const char *ipa)
COM_BufAddText(va("connect \"%s\"\n", ipa));
- // A little "please wait" message.
- M_DrawTextBox(56, BASEVIDHEIGHT/2-12, 24, 2);
- V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "Connecting to server...");
- I_OsPolling();
- I_UpdateNoBlit();
- if (rendermode == render_soft)
- I_FinishUpdate(); // page flip or blit buffer
+ M_PleaseWait();
}
boolean M_JoinIPInputs(INT32 ch)
diff --git a/src/menus/play-online-room-select.c b/src/menus/play-online-room-select.c
index 666b7c987..d0d6b9a58 100644
--- a/src/menus/play-online-room-select.c
+++ b/src/menus/play-online-room-select.c
@@ -42,8 +42,7 @@ void M_MPRoomSelect(INT32 choice)
M_ServersMenu(0);
M_SetMenuDelay(pid);
}
- else if (mpmenu.roomforced == false
- && menucmd[pid].dpad_lr != 0)
+ else if (menucmd[pid].dpad_lr != 0)
{
mpmenu.room ^= 1;
S_StartSound(NULL, sfx_s3k5b);
@@ -59,12 +58,28 @@ void M_MPRoomSelectTick(void)
void M_MPRoomSelectInit(INT32 choice)
{
(void)choice;
+
+ if (modifiedgame)
+ {
+ M_StartMessage("Server Browser & Add-Ons", M_GetText("You have add-ons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\n\n\"Dr. Robotnik's Ring Racers\" will\nautomatically add everything\nyou need when you join.\n"), NULL, MM_NOTHING, NULL, NULL);
+ return;
+ }
+
+ // The following behaviour is affected by modifiedgame despite the above restriction.
+ // It's a sanity check were that to be removed, wheither by us or by a modified client.
+ // "wheither"? That typo rules, I'm keeping that ~toast 280823
+
mpmenu.room = (modifiedgame == true) ? 1 : 0;
- mpmenu.roomforced = ((modifiedgame == true) || (!M_SecretUnlocked(SECRET_ADDONS, true)));
mpmenu.ticker = 0;
mpmenu.servernum = 0;
mpmenu.scrolln = 0;
mpmenu.slide = 0;
+ if ((modifiedgame == true) || (M_SecretUnlocked(SECRET_ADDONS, true) == false))
+ {
+ M_ServersMenu(0);
+ return;
+ }
+
M_SetupNextMenu(&PLAY_MP_RoomSelectDef, false);
}
diff --git a/src/menus/play-online-server-browser.c b/src/menus/play-online-server-browser.c
index 730a34956..29c2e4379 100644
--- a/src/menus/play-online-server-browser.c
+++ b/src/menus/play-online-server-browser.c
@@ -3,11 +3,14 @@
#include "../k_menu.h"
#include "../v_video.h"
-#include "../i_system.h" // I_OsPolling
-#include "../i_video.h" // I_UpdateNoBlit
+#include "../s_sound.h"
+
+//#define SERVERLISTDEBUG
#ifdef SERVERLISTDEBUG
#include "../m_random.h"
+
+void M_ServerListFillDebug(void);
#endif
menuitem_t PLAY_MP_ServerBrowser[] =
@@ -16,8 +19,8 @@ menuitem_t PLAY_MP_ServerBrowser[] =
{IT_STRING | IT_CVAR, "SORT BY", NULL, // tooltip MUST be null.
NULL, {.cvar = &cv_serversort}, 0, 0},
- {IT_STRING, "REFRESH", NULL,
- NULL, {NULL}, 0, 0},
+ {IT_STRING | IT_CALL, "REFRESH", NULL,
+ NULL, {.routine = &M_RefreshServers}, 0, 0},
{IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0},
};
@@ -42,28 +45,6 @@ menu_t PLAY_MP_ServerBrowserDef = {
// for server fetch threads...
M_waiting_mode_t m_waiting_mode = M_NOT_WAITING;
-// depending on mpmenu.room, either allows only unmodded servers or modded ones. Remove others from the list.
-// we do this by iterating backwards.
-static void M_CleanServerList(void)
-{
- UINT8 i = serverlistcount;
-
- while (i)
- {
-
- if (serverlist[i].info.modifiedgame != mpmenu.room)
- {
- // move everything after this index 1 slot down...
- if (i != serverlistcount)
- memcpy(&serverlist[i], &serverlist[i+1], sizeof(serverelem_t)*(serverlistcount-i));
-
- serverlistcount--;
- }
-
- i--;
- }
-}
-
void
M_SetWaitingMode (int mode)
{
@@ -180,31 +161,19 @@ void M_RefreshServers(INT32 choice)
{
(void)choice;
- // 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.");
- I_OsPolling();
- I_UpdateNoBlit();
- if (rendermode == render_soft)
- I_FinishUpdate(); // page flip or blit buffer
+ CL_UpdateServerList();
+#ifdef SERVERLISTDEBUG
+ M_ServerListFillDebug();
+#else /*SERVERLISTDEBUG*/
#ifdef MASTERSERVER
#ifdef HAVE_THREADS
Spawn_masterserver_thread("fetch-servers", Fetch_servers_thread);
#else/*HAVE_THREADS*/
Fetch_servers_thread(NULL);
#endif/*HAVE_THREADS*/
-#else/*MASTERSERVER*/
- CL_UpdateServerList();
#endif/*MASTERSERVER*/
-
-#ifdef SERVERLISTDEBUG
- M_ServerListFillDebug();
-#endif
- M_CleanServerList();
- M_SortServerList();
-
+#endif /*SERVERLISTDEBUG*/
}
#ifdef UPDATE_ALERT
@@ -253,13 +222,21 @@ void M_ServersMenu(INT32 choice)
// modified game check: no longer handled
// we don't request a restart unless the filelist differs
+ CL_UpdateServerList();
+
+ mpmenu.ticker = 0;
mpmenu.servernum = 0;
mpmenu.scrolln = 0;
mpmenu.slide = 0;
+ PLAY_MP_ServerBrowserDef.prevMenu = currentMenu;
M_SetupNextMenu(&PLAY_MP_ServerBrowserDef, false);
itemOn = 0;
+#ifdef SERVERLISTDEBUG
+ M_ServerListFillDebug();
+#else /*SERVERLISTDEBUG*/
+
#if defined (MASTERSERVER) && defined (HAVE_THREADS)
I_lock_mutex(&ms_QueryId_mutex);
{
@@ -289,12 +266,7 @@ void M_ServersMenu(INT32 choice)
M_RefreshServers(0);
#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/
-#ifdef SERVERLISTDEBUG
- M_ServerListFillDebug();
-#endif
-
- M_CleanServerList();
- M_SortServerList();
+#endif /*SERVERLISTDEBUG*/
}
#ifdef SERVERLISTDEBUG
@@ -304,29 +276,31 @@ void M_ServerListFillDebug(void)
{
UINT8 i = 0;
- serverlistcount = 10;
+ serverlistcount = 40;
memset(serverlist, 0, sizeof(serverlist)); // zero out the array for convenience...
for (i = 0; i < serverlistcount; i++)
{
// We don't really care about the server node for this, let's just fill in the info so that we have a visual...
- serverlist[i].info.numberofplayer = min(i, 8);
- serverlist[i].info.maxplayer = 8;
+ serverlist[i].info.maxplayer = M_RandomRange(8, 16);
+ UINT8 val = i % 16;
+ serverlist[i].info.numberofplayer = min(val, serverlist[i].info.maxplayer);
- serverlist[i].info.avgpwrlv = P_RandomRange(PR_UNDEFINED, 500, 1500);
- serverlist[i].info.time = P_RandomRange(PR_UNDEFINED, 1, 8); // ping
+ serverlist[i].info.avgpwrlv = M_RandomRange(500, 1500);
+ serverlist[i].info.time = M_RandomRange(1, 8); // ping
strcpy(serverlist[i].info.servername, va("Serv %d", i+1));
strcpy(serverlist[i].info.gametypename, i & 1 ? "Race" : "Battle");
- P_RandomRange(PR_UNDEFINED, 0, 5); // change results...
- serverlist[i].info.kartvars = P_RandomRange(PR_UNDEFINED, 0, 3) & SV_SPEEDMASK;
+ serverlist[i].info.kartvars = M_RandomRange(0, 3) & SV_SPEEDMASK;
- serverlist[i].info.modifiedgame = P_RandomRange(PR_UNDEFINED, 0, 1);
+ serverlist[i].info.modifiedgame = M_RandomRange(0, 1);
CONS_Printf("Serv %d | %d...\n", i, serverlist[i].info.modifiedgame);
}
+
+ M_SortServerList();
}
#endif // SERVERLISTDEBUG
@@ -339,7 +313,7 @@ static int ServerListEntryComparator_##key(const void *entry1, const void *entry
const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; \
if (sa->info.key != sb->info.key) \
return sa->info.key - sb->info.key; \
- return strcmp(sa->info.servername, sb->info.servername); \
+ return sa->info.time - sb->info.time; \
}
// This does descending instead of ascending.
@@ -349,15 +323,22 @@ static int ServerListEntryComparator_##key##_reverse(const void *entry1, const v
const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; \
if (sb->info.key != sa->info.key) \
return sb->info.key - sa->info.key; \
- return strcmp(sb->info.servername, sa->info.servername); \
+ return sa->info.time - sb->info.time; \
}
-SERVER_LIST_ENTRY_COMPARATOR(time)
+//SERVER_LIST_ENTRY_COMPARATOR(time) -- done seperately due to the usual tiebreaker being time
SERVER_LIST_ENTRY_COMPARATOR(numberofplayer)
SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer)
SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer)
SERVER_LIST_ENTRY_COMPARATOR(avgpwrlv)
+static int ServerListEntryComparator_time(const void *entry1, const void *entry2)
+{
+ const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2;
+ if (sa->info.time != sb->info.time)
+ return sa->info.time - sb->info.time;
+ return strcmp(sa->info.servername, sb->info.servername);
+}
static int ServerListEntryComparator_gametypename(const void *entry1, const void *entry2)
{
@@ -365,13 +346,51 @@ static int ServerListEntryComparator_gametypename(const void *entry1, const void
int c;
if (( c = strcasecmp(sa->info.gametypename, sb->info.gametypename) ))
return c;
- return strcmp(sa->info.servername, sb->info.servername); \
+ return sa->info.time - sb->info.time;
+}
+
+static int ServerListEntryComparator_recommended(const void *entry1, const void *entry2)
+{
+ const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2;
+
+ INT32 saseedval = sa->info.numberofplayer;
+ INT32 sbseedval = sb->info.numberofplayer;
+
+ // Tyron wrote the following on 25072022:
+ // "sort should be two parts
+ // top part of the list is "all non-empty servers sorted by reverse playercount", with servers above a certain reported ping marked as bad connection or whatever
+ // bottom part of the list is all empty servers sorted by ping"
+ // toast is implementing on 27082023, over a year later, because
+ // "fixing server join flow" is saner to do near the end
+
+ {
+ const UINT8 SERVER_EMPTY = 1;
+
+ // The intent with this nudge is to show you
+ // good games you could get a memorable Duel in,
+ // with the possibility to really katamari into
+ // something more substantial.
+ // By comparison, empty games are not nearly as
+ // fun to get going, so let's lower their SEO.
+ if (!saseedval)
+ saseedval = MAXPLAYERS + SERVER_EMPTY;
+ if (!sbseedval)
+ sbseedval = MAXPLAYERS + SERVER_EMPTY;
+ }
+
+ if (saseedval != sbseedval)
+ return saseedval - sbseedval;
+
+ return sa->info.time - sb->info.time;
}
void M_SortServerList(void)
{
switch(cv_serversort.value)
{
+ case -1:
+ qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_recommended);
+ break;
case 0: // Ping.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time);
break;
@@ -397,89 +416,115 @@ void M_SortServerList(void)
// Server browser inputs & ticker
void M_MPServerBrowserTick(void)
{
+ mpmenu.ticker++;
mpmenu.slide /= 2;
+
+#if defined (MASTERSERVER) && defined (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
+
+ CL_TimeoutServerList();
}
// Input handler for server browser.
boolean M_ServerBrowserInputs(INT32 ch)
{
UINT8 pid = 0;
- UINT8 maxscroll = serverlistcount-(SERVERSPERPAGE/2);
+ INT16 maxscroll = serverlistcount - (SERVERSPERPAGE/2) - 2; // Why? Because
+ if (maxscroll < 0)
+ maxscroll = 0;
+
+ const INT16 serverbrowserOn = (currentMenu->numitems - 1);
+
(void) ch;
if (!itemOn && menucmd[pid].dpad_ud < 0)
{
- M_PrevOpt(); // go to itemOn 2
if (serverlistcount)
{
- UINT8 prevscroll = mpmenu.scrolln;
+ // Return the MS listing to the bottom.
+ INT32 prevscroll = mpmenu.scrolln;
- mpmenu.servernum = serverlistcount;
+ mpmenu.servernum = serverlistcount-1;
mpmenu.scrolln = maxscroll;
- mpmenu.slide = SERVERSPACE * (prevscroll - mpmenu.scrolln);
+ mpmenu.slide = SERVERSPACE * (prevscroll - (INT32)mpmenu.scrolln);
}
else
{
- itemOn = 1; // Sike! If there are no servers, go to refresh instead.
+ M_PrevOpt(); // Double apply
}
-
- return true; // overwrite behaviour.
}
- else if (itemOn == 2) // server browser itself...
+ else if (itemOn == (serverbrowserOn - 1) && menucmd[pid].dpad_ud > 0 && !serverlistcount)
{
- // we have to manually do that here.
- if (M_MenuBackPressed(pid))
+ M_NextOpt(); // Double apply
+ }
+ else if (itemOn == serverbrowserOn) // server browser itself...
+ {
+#ifndef SERVERLISTDEBUG
+ if (M_MenuConfirmPressed(pid))
{
- M_GoBack(0);
M_SetMenuDelay(pid);
+
+ COM_BufAddText(va("connect node %d\n", serverlist[mpmenu.servernum].node));
+
+ M_PleaseWait();
+
+ return true;
}
+#endif
- else if (menucmd[pid].dpad_ud > 0) // down
+ if (menucmd[pid].dpad_ud > 0) // down
{
- if (mpmenu.servernum >= serverlistcount-1)
- {
- UINT8 prevscroll = mpmenu.scrolln;
- mpmenu.servernum = 0;
- mpmenu.scrolln = 0;
- mpmenu.slide = SERVERSPACE * (prevscroll - mpmenu.scrolln);
-
- M_NextOpt(); // Go back to the top of the real menu.
- }
- else
+ if ((UINT32)(mpmenu.servernum+1) < serverlistcount)
{
+ // Listing scroll down
mpmenu.servernum++;
if (mpmenu.scrolln < maxscroll && mpmenu.servernum > SERVERSPERPAGE/2)
{
mpmenu.scrolln++;
mpmenu.slide += SERVERSPACE;
}
- }
- S_StartSound(NULL, sfx_s3k5b);
- M_SetMenuDelay(pid);
+ S_StartSound(NULL, sfx_s3k5b);
+ M_SetMenuDelay(pid);
+
+ return true;
+ }
+
+ // Return the MS listing to the top.
+ INT32 prevscroll = mpmenu.scrolln;
+
+ mpmenu.servernum = 0;
+ mpmenu.scrolln = 0;
+ mpmenu.slide = SERVERSPACE * (prevscroll - (INT32)mpmenu.scrolln);
}
else if (menucmd[pid].dpad_ud < 0)
{
-
- if (!mpmenu.servernum)
+ if (mpmenu.servernum)
{
- M_PrevOpt();
- }
- else
- {
- if (mpmenu.servernum <= serverlistcount-(SERVERSPERPAGE/2) && mpmenu.scrolln)
+ // Listing scroll up
+ if (mpmenu.servernum <= (INT16)maxscroll && mpmenu.scrolln)
{
mpmenu.scrolln--;
mpmenu.slide -= SERVERSPACE;
}
-
mpmenu.servernum--;
- }
- S_StartSound(NULL, sfx_s3k5b);
- M_SetMenuDelay(pid);
+ S_StartSound(NULL, sfx_s3k5b);
+ M_SetMenuDelay(pid);
+
+ return true;
+ }
}
- return true; // Overwrite behaviour.
}
return false; // use normal behaviour.
}
diff --git a/src/menus/transient/message-box.c b/src/menus/transient/message-box.c
index f69b3ebfe..72bc50731 100644
--- a/src/menus/transient/message-box.c
+++ b/src/menus/transient/message-box.c
@@ -31,19 +31,20 @@ static inline size_t M_StringHeight(const char *string)
void M_StartMessage(const char *header, const char *string, void (*routine)(INT32), menumessagetype_t itemtype, const char *confirmstr, const char *defaultstr)
{
const UINT8 pid = 0;
- static char *message = NULL;
- Z_Free(message);
DEBFILE(string);
- message = V_ScaledWordWrap(
- BASEVIDWIDTH << FRACBITS,
+ char *message = V_ScaledWordWrap(
+ (BASEVIDWIDTH - 8) << FRACBITS,
FRACUNIT, FRACUNIT, FRACUNIT,
0,
HU_FONT,
string
);
- strncpy(menumessage.message, string, MAXMENUMESSAGE);
+ strncpy(menumessage.message, message, MAXMENUMESSAGE);
+
+ Z_Free(message);
+
menumessage.header = header;
menumessage.flags = itemtype;
menumessage.routine = routine;
@@ -84,7 +85,7 @@ void M_StartMessage(const char *header, const char *string, void (*routine)(INT3
// oogh my god this was replaced in 2023
menumessage.x = (8 * MAXSTRINGLENGTH) - 1;
- menumessage.y = M_StringHeight(message);
+ menumessage.y = M_StringHeight(menumessage.message);
M_SetMenuDelay(pid); // Set menu delay to avoid setting off any of the handlers.
}
diff --git a/src/mserv.c b/src/mserv.c
index e1f2b9820..2f851d3e8 100644
--- a/src/mserv.c
+++ b/src/mserv.c
@@ -17,6 +17,7 @@
#include "doomstat.h"
#include "doomdef.h"
+#include "console.h" // con_startup
#include "command.h"
#include "i_threads.h"
#include "mserv.h"
@@ -40,6 +41,8 @@ static boolean MSUpdateAgain;
static time_t MSLastPing;
+static char *MSRules;
+
#ifdef HAVE_THREADS
static I_mutex MSMutex;
static I_cond MSCond;
@@ -157,6 +160,43 @@ static void Command_Listserv_f(void)
}
}
+static boolean firstmsrules = false;
+
+static void
+Get_masterserver_rules (boolean checkfirst)
+{
+ char rules[256];
+
+ if (checkfirst)
+ {
+ boolean MSRulesExist;
+
+ Lock_state();
+ MSRulesExist = (MSRules != NULL);
+ Unlock_state();
+
+ if (MSRulesExist)
+ return;
+ }
+
+ if (HMS_fetch_rules(rules, sizeof rules))
+ {
+ Lock_state();
+ Z_Free(MSRules);
+ MSRules = Z_StrDup(rules);
+
+ if (MSRegistered == true)
+ {
+ CONS_Printf("\n");
+ CONS_Alert(CONS_NOTICE, "%s\n", rules);
+ }
+
+ firstmsrules = true;
+
+ Unlock_state();
+ }
+}
+
static void
Finish_registration (void)
{
@@ -175,6 +215,17 @@ Finish_registration (void)
}
Unlock_state();
+ char *rules = GetMasterServerRules();
+ if (rules == NULL)
+ {
+ Get_masterserver_rules(true);
+ }
+ else
+ {
+ CONS_Printf("\n");
+ CONS_Alert(CONS_NOTICE, "%s\n", rules);
+ }
+
if (registered)
CONS_Printf("Master server registration successful.\n");
}
@@ -257,6 +308,15 @@ Finish_unlist (void)
}
}
+static void
+Finish_masterserver_change (char *api)
+{
+ HMS_set_api(api);
+
+ if (!con_startup)
+ Get_masterserver_rules(false);
+}
+
#ifdef HAVE_THREADS
static int *
Server_id (void)
@@ -350,7 +410,14 @@ Change_masterserver_thread (char *api)
}
Unlock_state();
- HMS_set_api(api);
+ Finish_masterserver_change(api);
+}
+
+static void
+Get_masterserver_rules_thread (void)
+{
+ // THIS FUNC has its own lock check in it
+ Get_masterserver_rules(true);
}
#endif/*HAVE_THREADS*/
@@ -397,6 +464,17 @@ void UnregisterServer(void)
#endif/*MASTERSERVER*/
}
+char *GetMasterServerRules(void)
+{
+ char *rules;
+
+ Lock_state();
+ rules = MSRules ? Z_StrDup(MSRules) : NULL;
+ Unlock_state();
+
+ return rules;
+}
+
static boolean
Online (void)
{
@@ -447,7 +525,21 @@ Set_api (const char *api)
strdup(api)
);
#else
- HMS_set_api(strdup(api));
+ Finish_masterserver_change(strdup(api));
+#endif
+}
+
+void
+Get_rules (void)
+{
+#ifdef HAVE_THREADS
+ I_spawn_thread(
+ "get-masterserver-rules",
+ (I_thread_fn)Get_masterserver_rules_thread,
+ NULL
+ );
+#else
+ Get_masterserver_rules(true);
#endif
}
@@ -521,6 +613,8 @@ void Advertise_OnChange(void)
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
#endif
+
+ M_PopupMasterServerRules();
}
#ifdef DEVELOP
diff --git a/src/mserv.h b/src/mserv.h
index 7417585d6..6fe9a77c4 100644
--- a/src/mserv.h
+++ b/src/mserv.h
@@ -57,6 +57,7 @@ struct msg_ban_t
// ================================ GLOBALS ===============================
extern consvar_t cv_masterserver, cv_servername;
+extern consvar_t cv_masterserver_nagattempts;
extern consvar_t cv_server_contact;
extern consvar_t cv_masterserver_update_rate;
extern consvar_t cv_masterserver_timeout;
@@ -77,6 +78,8 @@ extern I_mutex ms_ServerList_mutex;
void RegisterServer(void);
void UnregisterServer(void);
+void Get_rules(void);
+
void MasterClient_Ticker(void);
msg_server_t *GetShortServersList(int id);
@@ -84,6 +87,8 @@ msg_server_t *GetShortServersList(int id);
char *GetMODVersion(int id);
#endif
+char *GetMasterServerRules(void);
+
void AddMServCommands(void);
/* HTTP */
@@ -94,6 +99,7 @@ int HMS_update (void);
void HMS_list_servers (void);
msg_server_t * HMS_fetch_servers (msg_server_t *list, int id);
int HMS_compare_mod_version (char *buffer, size_t size_of_buffer);
+const char * HMS_fetch_rules (char *buffer, size_t size_of_buffer);
#ifdef __cplusplus
} // extern "C"
diff --git a/src/version.h b/src/version.h
index e350082fc..c8b673542 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,4 +1,4 @@
-#define SRB2VERSION "2.0"/* this must be the first line, for cmake !! */
+#define SRB2VERSION "1.0"/* this must be the first line, for cmake !! */
// The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/showgroups.php ).
// DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server.