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.