Merge branch 'mserv-servitude' into 'master'

MServ Servitude

See merge request KartKrew/Kart!1438
This commit is contained in:
Oni 2023-09-01 05:35:57 +00:00
commit aa5446cf40
16 changed files with 602 additions and 165 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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);

View file

@ -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);

View file

@ -11,7 +11,7 @@
/*
Documentation available here.
<https://ms.kartkrew.org/tools/api/2/>
<https://ms.kartkrew.org/tools/api/2.2/>
*/
#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)
{

View file

@ -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

View file

@ -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<<FRACBITS, 100<<FRACBITS, FRACUNIT, mpmenu.room ? (5<<V_ALPHASHIFT) : 0, butt1[(mpmenu.room) ? 1 : 0], NULL);
V_DrawFixedPatch(160<<FRACBITS, 90<<FRACBITS, FRACUNIT, mpmenu.room ? (5<<V_ALPHASHIFT) : 0, butt1[(mpmenu.room) ? 1 : 0], NULL);
if (!mpmenu.roomforced || mpmenu.room == 1)
V_DrawFixedPatch(160<<FRACBITS, 100<<FRACBITS, FRACUNIT, (!mpmenu.room) ? (5<<V_ALPHASHIFT) : 0, butt2[(!mpmenu.room) ? 1 : 0], NULL);
V_DrawFixedPatch(160<<FRACBITS, 90<<FRACBITS, FRACUNIT, (!mpmenu.room) ? (5<<V_ALPHASHIFT) : 0, butt2[(!mpmenu.room) ? 1 : 0], NULL);
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL);
V_DrawCenteredThinString(BASEVIDWIDTH/2, 12, 0, "Select today's type of play!");
M_DrawMasterServerReminder();
}
// SERVER BROWSER
static void M_DrawServerCountAndHorizontalBar(void)
{
const char *text;
INT32 y = currentMenu->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

View file

@ -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},

View file

@ -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)

View file

@ -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)

View file

@ -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);
}

View file

@ -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.
}

View file

@ -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.
}

View file

@ -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

View file

@ -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"

View file

@ -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.