RingRacers/src/menus/play-online-server-browser.c
toaster 6c6306889a M_StartMessage revamp
Immense 25-file diff, so spun off into its own branch.
- Improved appearance
    - Not just a big block of text on a blue background
    - Well, OK, the main part is, but some stuff has been spun out into its own fields
        - Title text
        - Text and button prompt for Yes/No or OK
    - Slides with pow on and off the screen
- Disabled MM_EVENTHANDLER, which has always been dog but got considerably worse after newmenus to the point nothing's using it anymore
    - Required in order to reduce the reliance on FUNCPTRCAST, which prevents Eidolon from compiling some stuff because it's not valid C++
2023-06-09 19:23:36 +01:00

496 lines
11 KiB
C

/// \file menus/play-online-server-browser.c
/// \brief MULTIPLAYER ROOM FETCH / REFRESH THREADS
#include "../k_menu.h"
#include "../v_video.h"
#include "../i_system.h" // I_OsPolling
#include "../i_video.h" // I_UpdateNoBlit
#ifdef SERVERLISTDEBUG
#include "../m_random.h"
#endif
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_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0},
};
menu_t PLAY_MP_ServerBrowserDef = {
sizeof (PLAY_MP_ServerBrowser) / sizeof (menuitem_t),
&PLAY_MP_RoomSelectDef,
0,
PLAY_MP_ServerBrowser,
32, 36,
0, 0,
0,
"NETMD2",
0, 0,
M_DrawMPServerBrowser,
M_MPServerBrowserTick,
NULL,
NULL,
M_ServerBrowserInputs
};
static CV_PossibleValue_t serversort_cons_t[] = {
{0,"Ping"},
{1,"AVG. Power Level"},
{2,"Most Players"},
{3,"Least Players"},
{4,"Max Player Slots"},
{5,"Gametype"},
{0,NULL}
};
consvar_t cv_serversort = CVAR_INIT ("serversort", "Ping", CV_CALL, serversort_cons_t, M_SortServerList);
// 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)
{
#ifdef HAVE_THREADS
I_lock_mutex(&k_menu_mutex);
#endif
{
m_waiting_mode = mode;
}
#ifdef HAVE_THREADS
I_unlock_mutex(k_menu_mutex);
#endif
}
int
M_GetWaitingMode (void)
{
int mode;
#ifdef HAVE_THREADS
I_lock_mutex(&k_menu_mutex);
#endif
{
mode = m_waiting_mode;
}
#ifdef HAVE_THREADS
I_unlock_mutex(k_menu_mutex);
#endif
return mode;
}
#ifdef MASTERSERVER
#ifdef HAVE_THREADS
void
Spawn_masterserver_thread (const char *name, void (*thread)(int*))
{
int *id = malloc(sizeof *id);
I_lock_mutex(&ms_QueryId_mutex);
{
*id = ms_QueryId;
}
I_unlock_mutex(ms_QueryId_mutex);
I_spawn_thread(name, (I_thread_fn)thread, id);
}
int
Same_instance (int id)
{
int okay;
I_lock_mutex(&ms_QueryId_mutex);
{
okay = ( id == ms_QueryId );
}
I_unlock_mutex(ms_QueryId_mutex);
return okay;
}
#endif/*HAVE_THREADS*/
void
Fetch_servers_thread (int *id)
{
msg_server_t * server_list;
(void)id;
M_SetWaitingMode(M_WAITING_SERVERS);
#ifdef HAVE_THREADS
server_list = GetShortServersList(*id);
#else
server_list = GetShortServersList(0);
#endif
if (server_list)
{
#ifdef HAVE_THREADS
if (Same_instance(*id))
#endif
{
M_SetWaitingMode(M_NOT_WAITING);
#ifdef HAVE_THREADS
I_lock_mutex(&ms_ServerList_mutex);
{
ms_ServerList = server_list;
}
I_unlock_mutex(ms_ServerList_mutex);
#else
CL_QueryServerList(server_list);
free(server_list);
#endif
}
#ifdef HAVE_THREADS
else
{
free(server_list);
}
#endif
}
#ifdef HAVE_THREADS
free(id);
#endif
}
#endif/*MASTERSERVER*/
// updates serverlist
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
#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();
}
#ifdef UPDATE_ALERT
static void M_CheckMODVersion(int id)
{
char updatestring[500];
const char *updatecheck = GetMODVersion(id);
if(updatecheck)
{
sprintf(updatestring, UPDATE_ALERT_STRING, VERSIONSTRING, updatecheck);
#ifdef HAVE_THREADS
I_lock_mutex(&k_menu_mutex);
#endif
M_StartMessage("Game Update", updatestring, NULL, MM_NOTHING, NULL, NULL);
#ifdef HAVE_THREADS
I_unlock_mutex(k_menu_mutex);
#endif
}
}
#endif/*UPDATE_ALERT*/
#if defined (UPDATE_ALERT) && defined (HAVE_THREADS)
static void
Check_new_version_thread (int *id)
{
M_SetWaitingMode(M_WAITING_VERSION);
M_CheckMODVersion(*id);
if (Same_instance(*id))
{
Fetch_servers_thread(id);
}
else
{
free(id);
}
}
#endif/*defined (UPDATE_ALERT) && defined (HAVE_THREADS)*/
// Initializes serverlist when entering the menu...
void M_ServersMenu(INT32 choice)
{
(void)choice;
// modified game check: no longer handled
// we don't request a restart unless the filelist differs
mpmenu.servernum = 0;
mpmenu.scrolln = 0;
mpmenu.slide = 0;
M_SetupNextMenu(&PLAY_MP_ServerBrowserDef, false);
itemOn = 0;
#if defined (MASTERSERVER) && defined (HAVE_THREADS)
I_lock_mutex(&ms_QueryId_mutex);
{
ms_QueryId++;
}
I_unlock_mutex(ms_QueryId_mutex);
I_lock_mutex(&ms_ServerList_mutex);
{
if (ms_ServerList)
{
free(ms_ServerList);
ms_ServerList = NULL;
}
}
I_unlock_mutex(ms_ServerList_mutex);
#ifdef UPDATE_ALERT
Spawn_masterserver_thread("check-new-version", Check_new_version_thread);
#else/*UPDATE_ALERT*/
Spawn_masterserver_thread("fetch-servers", Fetch_servers_thread);
#endif/*UPDATE_ALERT*/
#else/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/
#ifdef UPDATE_ALERT
M_CheckMODVersion(0);
#endif/*UPDATE_ALERT*/
M_RefreshServers(0);
#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/
#ifdef SERVERLISTDEBUG
M_ServerListFillDebug();
#endif
M_CleanServerList();
M_SortServerList();
}
#ifdef SERVERLISTDEBUG
// Fill serverlist with a bunch of garbage to make our life easier in debugging
void M_ServerListFillDebug(void)
{
UINT8 i = 0;
serverlistcount = 10;
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.avgpwrlv = P_RandomRange(PR_UNDEFINED, 500, 1500);
serverlist[i].info.time = P_RandomRange(PR_UNDEFINED, 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.modifiedgame = P_RandomRange(PR_UNDEFINED, 0, 1);
CONS_Printf("Serv %d | %d...\n", i, serverlist[i].info.modifiedgame);
}
}
#endif // SERVERLISTDEBUG
// Ascending order, not descending.
// The casts are safe as long as the caller doesn't do anything stupid.
#define SERVER_LIST_ENTRY_COMPARATOR(key) \
static int ServerListEntryComparator_##key(const void *entry1, const void *entry2) \
{ \
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); \
}
// This does descending instead of ascending.
#define SERVER_LIST_ENTRY_COMPARATOR_REVERSE(key) \
static int ServerListEntryComparator_##key##_reverse(const void *entry1, const void *entry2) \
{ \
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); \
}
SERVER_LIST_ENTRY_COMPARATOR(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_gametypename(const void *entry1, const void *entry2)
{
const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2;
int c;
if (( c = strcasecmp(sa->info.gametypename, sb->info.gametypename) ))
return c;
return strcmp(sa->info.servername, sb->info.servername); \
}
void M_SortServerList(void)
{
switch(cv_serversort.value)
{
case 0: // Ping.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time);
break;
case 1: // AVG. Power Level
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_avgpwrlv);
break;
case 2: // Most players.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse);
break;
case 3: // Least players.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer);
break;
case 4: // Max players.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse);
break;
case 5: // Gametype.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename);
break;
}
}
// Server browser inputs & ticker
void M_MPServerBrowserTick(void)
{
mpmenu.slide /= 2;
}
// Input handler for server browser.
boolean M_ServerBrowserInputs(INT32 ch)
{
UINT8 pid = 0;
UINT8 maxscroll = serverlistcount-(SERVERSPERPAGE/2);
(void) ch;
if (!itemOn && menucmd[pid].dpad_ud < 0)
{
M_PrevOpt(); // go to itemOn 2
if (serverlistcount)
{
UINT8 prevscroll = mpmenu.scrolln;
mpmenu.servernum = serverlistcount;
mpmenu.scrolln = maxscroll;
mpmenu.slide = SERVERSPACE * (prevscroll - mpmenu.scrolln);
}
else
{
itemOn = 1; // Sike! If there are no servers, go to refresh instead.
}
return true; // overwrite behaviour.
}
else if (itemOn == 2) // server browser itself...
{
// we have to manually do that here.
if (M_MenuBackPressed(pid))
{
M_GoBack(0);
M_SetMenuDelay(pid);
}
else 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
{
mpmenu.servernum++;
if (mpmenu.scrolln < maxscroll && mpmenu.servernum > SERVERSPERPAGE/2)
{
mpmenu.scrolln++;
mpmenu.slide += SERVERSPACE;
}
}
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
else if (menucmd[pid].dpad_ud < 0)
{
if (!mpmenu.servernum)
{
M_PrevOpt();
}
else
{
if (mpmenu.servernum <= serverlistcount-(SERVERSPERPAGE/2) && mpmenu.scrolln)
{
mpmenu.scrolln--;
mpmenu.slide -= SERVERSPACE;
}
mpmenu.servernum--;
}
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
return true; // Overwrite behaviour.
}
return false; // use normal behaviour.
}