*/
static void CL_ParseServerInfo (void)
{
- char *str;
+ const char *str;
int i;
protocolversion_t protocol;
int nummodels, numsounds;
if (protocol == PROTOCOL_QUAKEWORLD)
{
- char gamedir[1][MAX_QPATH];
-
cl.qw_servercount = MSG_ReadLong(&cl_message);
str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
Con_Printf("server gamedir is %s\n", str);
- dp_strlcpy(gamedir[0], str, sizeof(gamedir[0]));
// change gamedir if needed
- if (!FS_ChangeGameDirs(1, gamedir, true, false))
+ if (!FS_ChangeGameDirs(1, &str, false))
Host_Error("CL_ParseServerInfo: unable to switch to server specified gamedir");
cl.gametype = GAME_DEATHMATCH;
{
char name [MAX_OSPATH];
int i;
- int index = 0;
+ int index = GAME_NORMAL;
#ifdef FORCEGAME
COM_ToLowerString(FORCEGAME, name, sizeof (name));
COM_SetGameType(index);
}
-void COM_ChangeGameTypeForGameDirs(void)
+int COM_ChangeGameTypeForGameDirs(unsigned numgamedirs, const char *gamedirs[], qbool failmissing, qbool init)
{
unsigned i, gamemode_count = sizeof(gamemode_info) / sizeof(gamemode_info[0]);
- int index = -1;
+ int j, index = -1;
+ addgamedirs_t ret = GAMEDIRS_SUCCESS;
// this will not not change the gamegroup
// first check if a base game (single gamedir) matches
break;
}
}
- // now that we have a base game, see if there is a derivative game matching the startup one (two gamedirs)
- // bones_was_here: this prevents a Quake expansion (eg Rogue) getting switched to Quake,
- // and its gamedirname2 (eg "rogue") being lost from the search path, when adding a miscellaneous gamedir.
- for (i = 0; i < gamemode_count; i++)
+ if (index < 0)
+ Sys_Error("BUG: failed to find the base game!");
+
+ // See if there is a derivative game matching the startup one (two gamedirs),
+ // this prevents a Quake expansion (eg Rogue) getting switched to Quake during startup,
+ // not used when changing gamedirs later so that it's possible to change from the startup mod to the base one.
+ if (init)
{
- if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && gamemode_info[i].mode == com_startupgamemode)
+ for (i = 0; i < gamemode_count; i++)
{
- index = i;
- break;
+ if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && gamemode_info[i].mode == com_startupgamemode)
+ {
+ index = i;
+ break;
+ }
}
}
- // also see if the first gamedir (from -game parm or gamedir command) matches a derivative game (two/three gamedirs)
- if (fs_numgamedirs)
+
+ // See if the base game or mod can be identified by a gamedir,
+ // if more than one matches the last is used because the last gamedir is the primary.
+ for (j = numgamedirs - 1; j >= 0; --j)
{
for (i = 0; i < gamemode_count; i++)
{
- if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
+ if (gamemode_info[i].group == com_startupgamegroup)
+ if ((gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0] && !strcasecmp(gamedirs[j], gamemode_info[i].gamedirname2))
+ || (!gamemode_info[i].gamedirname2 && !strcasecmp(gamedirs[j], gamemode_info[i].gamedirname1)))
{
index = i;
- break;
+ goto double_break;
}
}
}
+double_break:
+
// we now have a good guess at which game this is meant to be...
- if (index >= 0 && gamemode != gamemode_info[index].mode)
- COM_SetGameType(index);
+ COM_SetGameType(index);
+
+ ret = FS_SetGameDirs(numgamedirs, gamedirs, failmissing, !init);
+ if (ret == GAMEDIRS_SUCCESS)
+ {
+ Con_Printf("Game is %s using %s", gamename, fs_numgamedirs > 1 ? "gamedirs" : "gamedir");
+ for (j = 0; j < fs_numgamedirs; ++j)
+ Con_Printf(" %s%s", (strcasecmp(fs_gamedirs[j], gamedirname1) && (!gamedirname2 || strcasecmp(fs_gamedirs[j], gamedirname2))) ? "^7" : "^9", fs_gamedirs[j]);
+ Con_Printf("\n");
+
+ Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
+ }
+ return ret;
}
static void COM_SetGameType(int index)
{
static char gamenetworkfilternamebuffer[64];
- int i, t;
+ int t;
+
if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
index = 0;
gamemode = gamemode_info[index].mode;
gameuserdirname = sys.argv[t+1];
}
- if (gamedirname2 && gamedirname2[0])
- Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
- else
- Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
- for (i = 0;i < fs_numgamedirs;i++)
- {
- if (i == 0)
- Con_Printf(", with mod gamedirs");
- Con_Printf(" %s", fs_gamedirs[i]);
- }
- Con_Printf("\n");
-
if (strchr(gamenetworkfiltername, ' '))
{
char *s;
*s = '_';
gamenetworkfiltername = gamenetworkfilternamebuffer;
}
-
- Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
}
extern char com_modname[MAX_OSPATH];
void COM_InitGameType (void);
-void COM_ChangeGameTypeForGameDirs(void);
+int COM_ChangeGameTypeForGameDirs(unsigned numgamedirs, const char *gamedirs[], qbool failmissing, qbool init);
#endif
char fs_basedir[MAX_OSPATH];
static pack_t *fs_selfpack = NULL;
-// list of active game directories (empty if not running a mod)
+// list of active game directories
int fs_numgamedirs = 0;
char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
void FS_Rescan (void)
{
int i;
- qbool fs_modified = false;
- qbool reset = false;
char gamedirbuf[MAX_INPUTLINE];
char vabuf[1024];
- if (fs_searchpaths)
- reset = true;
FS_ClearSearchPath();
- // automatically activate gamemode for the gamedirs specified
- if (reset)
- COM_ChangeGameTypeForGameDirs();
-
- // add the game-specific paths
- // gamedirname1 (typically id1)
- FS_AddGameHierarchy (gamedirname1);
// update the com_modname (used for server info)
if (gamedirname2 && gamedirname2[0])
dp_strlcpy(com_modname, gamedirname2, sizeof(com_modname));
else
dp_strlcpy(com_modname, gamedirname1, sizeof(com_modname));
- // add the game-specific path, if any
- // (only used for mission packs and the like, which should set fs_modified)
- if (gamedirname2 && gamedirname2[0])
- {
- fs_modified = true;
- FS_AddGameHierarchy (gamedirname2);
- }
-
// -game <gamedir>
// Adds basedir/gamedir as an override game
// LadyHavoc: now supports multiple -game directories
*gamedirbuf = 0;
for (i = 0;i < fs_numgamedirs;i++)
{
- fs_modified = true;
FS_AddGameHierarchy (fs_gamedirs[i]);
// update the com_modname (used server info)
dp_strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
case GAME_ROGUE:
if (!registered.integer)
{
- if (fs_modified)
+ if (fs_numgamedirs > 1)
Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
else
Con_Print("Playing shareware version.\n");
/*
================
-FS_ChangeGameDirs
+FS_AddGameDirs
================
*/
-extern qbool vid_opened;
-qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool complain, qbool failmissing)
+addgamedirs_t FS_SetGameDirs(int numgamedirs, const char *gamedirs[], qbool failmissing, qbool abortonfail)
{
- int i;
+ int i, j, k;
const char *p;
+ const char *gamedirs_ok[MAX_GAMEDIRS + 2];
+ int numgamedirs_ok;
- if (fs_numgamedirs == numgamedirs)
+ // prepend the game-specific gamedirs (the primary and search order can be overriden)
+ gamedirs_ok[0] = gamedirname1;
+ numgamedirs_ok = 1;
+ if (gamedirname2 && gamedirname2[0])
{
- for (i = 0;i < numgamedirs;i++)
- if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
- break;
- if (i == numgamedirs)
- return true; // already using this set of gamedirs, do nothing
+ gamedirs_ok[1] = gamedirname2;
+ ++numgamedirs_ok;
}
- if (numgamedirs > MAX_GAMEDIRS)
+ // check the game-specific gamedirs
+ for (i = 0; i < numgamedirs_ok; ++i)
{
- if (complain)
- Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
- return false; // too many gamedirs
+ p = FS_CheckGameDir(gamedirs_ok[i]);
+ if(!p)
+ Sys_Error("BUG: nasty gamedir name \"%s\" in gamemode_info", gamedirs_ok[i]);
+ if(p == fs_checkgamedir_missing && failmissing)
+ {
+ Con_Printf(abortonfail ? CON_ERROR : CON_WARN "Base gamedir \"%s\" empty or not found!\n", gamedirs_ok[i]);
+ if (abortonfail)
+ return GAMEDIRS_FAILURE; // missing gamedirs
+ }
}
- for (i = 0;i < numgamedirs;i++)
+ // copy and check the user-specified gamedirs
+ for (i = 0; i < numgamedirs && (size_t)numgamedirs_ok < sizeof(gamedirs_ok) / sizeof(gamedirs_ok[0]); ++i)
{
+ // remove any previously-added duplicate (last one wins)
+ for (j = 0; j < numgamedirs_ok; ++j)
+ if (!strcasecmp(gamedirs_ok[j], gamedirs[i]))
+ {
+ --numgamedirs_ok;
+ for (k = j; k < numgamedirs_ok; ++k)
+ gamedirs_ok[k] = gamedirs_ok[k + 1];
+ }
+
// if string is nasty, reject it
p = FS_CheckGameDir(gamedirs[i]);
if(!p)
{
- if (complain)
- Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
- return false; // nasty gamedirs
+ Con_Printf(abortonfail ? CON_ERROR : CON_WARN "Nasty gamedir name \"%s\" rejected\n", gamedirs[i]);
+ if (abortonfail)
+ return GAMEDIRS_FAILURE; // nasty gamedirs
+ else
+ continue;
}
if(p == fs_checkgamedir_missing && failmissing)
{
- if (complain)
- Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
- return false; // missing gamedirs
+ Con_Printf(abortonfail ? CON_ERROR : CON_WARN "Gamedir \"%s\" empty or not found!\n", gamedirs[i]);
+ if (abortonfail)
+ return GAMEDIRS_FAILURE; // missing gamedirs
+ else
+ continue;
}
+
+ gamedirs_ok[numgamedirs_ok++] = gamedirs[i];
}
- Host_SaveConfig(CONFIGFILENAME);
+ if (fs_numgamedirs == numgamedirs_ok)
+ {
+ for (i = 0;i < numgamedirs_ok;i++)
+ if (strcasecmp(fs_gamedirs[i], gamedirs_ok[i]))
+ break;
+ if (i == numgamedirs_ok)
+ return GAMEDIRS_ALLGOOD; // already using this set of gamedirs, do nothing
+ }
- fs_numgamedirs = numgamedirs;
- for (i = 0;i < fs_numgamedirs;i++)
- dp_strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
+ if (numgamedirs_ok > MAX_GAMEDIRS)
+ {
+ Con_Printf(abortonfail ? CON_ERROR : CON_WARN "That is too many gamedirs (%i > %i)\n", numgamedirs_ok, MAX_GAMEDIRS);
+ if (abortonfail)
+ return GAMEDIRS_FAILURE; // too many gamedirs
+ }
- // reinitialize filesystem to detect the new paks
- FS_Rescan();
+ for (i = 0, fs_numgamedirs = 0; i < numgamedirs_ok && fs_numgamedirs < MAX_GAMEDIRS; ++i)
+ dp_strlcpy(fs_gamedirs[fs_numgamedirs++], gamedirs_ok[i], sizeof(fs_gamedirs[0]));
+
+ return GAMEDIRS_SUCCESS;
+}
+
+qbool FS_ChangeGameDirs(int numgamedirs, const char *gamedirs[], qbool failmissing)
+{
+ addgamedirs_t addresult = COM_ChangeGameTypeForGameDirs(numgamedirs, gamedirs, failmissing, false);
+
+ if (addresult == GAMEDIRS_ALLGOOD)
+ return true; // already using this set of gamedirs, do nothing
+ else if (addresult == GAMEDIRS_FAILURE)
+ return false;
+
+ Host_SaveConfig(CONFIGFILENAME);
if (cls.demoplayback)
{
// unload all sounds so they will be reloaded from the new files as needed
S_UnloadAllSounds_f(cmd_local);
- // reset everything that can be and reload configs
+ // reinitialize filesystem to detect the new paks
+ FS_Rescan();
+
+ // reload assets after the config is executed
Cbuf_InsertText(cmd_local, "\nloadconfig\n");
return true;
{
int i;
int numgamedirs;
- char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
+ const char *gamedirs[MAX_GAMEDIRS];
if (Cmd_Argc(cmd) < 2)
{
Con_Printf("gamedirs active:");
for (i = 0;i < fs_numgamedirs;i++)
- Con_Printf(" %s", fs_gamedirs[i]);
+ Con_Printf(" %s%s", (strcasecmp(fs_gamedirs[i], gamedirname1) && (!gamedirname2 || strcasecmp(fs_gamedirs[i], gamedirname2))) ? "^7" : "^9", fs_gamedirs[i]);
Con_Printf("\n");
return;
}
numgamedirs = Cmd_Argc(cmd) - 1;
if (numgamedirs > MAX_GAMEDIRS)
{
- Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
+ Con_Printf(CON_ERROR "Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
return;
}
for (i = 0;i < numgamedirs;i++)
- dp_strlcpy(gamedirs[i], Cmd_Argv(cmd, i+1), sizeof(gamedirs[i]));
+ gamedirs[i] = Cmd_Argv(cmd, i+1);
if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
{
// actually, changing during game would work fine, but would be stupid
- Con_Printf("Can not change gamedir while client is connected or server is running!\n");
+ Con_Printf(CON_ERROR "Can not change gamedir while client is connected or server is running!\n");
return;
}
- // halt demo playback to close the file
- CL_Disconnect();
-
- FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
+ FS_ChangeGameDirs(numgamedirs, gamedirs, true);
}
static const char *FS_SysCheckGameDir(const char *gamedir, char *buf, size_t buflength)
static void FS_Init_Dir (void)
{
- const char *p;
int i;
+ int numgamedirs;
+ const char *cmdline_gamedirs[MAX_GAMEDIRS];
*fs_basedir = 0;
*fs_userdir = 0;
FS_ListGameDirs();
- p = FS_CheckGameDir(gamedirname1);
- if(!p || p == fs_checkgamedir_missing)
- Con_Printf(CON_WARN "WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
-
- if(gamedirname2)
- {
- p = FS_CheckGameDir(gamedirname2);
- if(!p || p == fs_checkgamedir_missing)
- Con_Printf(CON_WARN "WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
- }
-
// -game <gamedir>
// Adds basedir/gamedir as an override game
// LadyHavoc: now supports multiple -game directories
- for (i = 1;i < sys.argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
+ // the last one is the primary (where files are saved) and is used to identify mods
+ for (i = 1, numgamedirs = 0; i < sys.argc && numgamedirs < MAX_GAMEDIRS; i++)
{
if (!sys.argv[i])
continue;
if (!strcmp (sys.argv[i], "-game") && i < sys.argc-1)
{
i++;
- p = FS_CheckGameDir(sys.argv[i]);
- if(!p)
- Con_Printf("WARNING: Nasty -game name rejected: %s\n", sys.argv[i]);
- if(p == fs_checkgamedir_missing)
- Con_Printf(CON_WARN "WARNING: -game %s%s/ not found!\n", fs_basedir, sys.argv[i]);
- // add the gamedir to the list of active gamedirs
- dp_strlcpy (fs_gamedirs[fs_numgamedirs], sys.argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
- fs_numgamedirs++;
+ cmdline_gamedirs[numgamedirs++] = sys.argv[i];
}
}
+ COM_ChangeGameTypeForGameDirs(numgamedirs, cmdline_gamedirs, true, true);
// generate the searchpath
FS_Rescan();
extern char fs_userdir [MAX_OSPATH];
// list of active game directories (empty if not running a mod)
-#define MAX_GAMEDIRS 16
+#define MAX_GAMEDIRS 17 // 16 + gamedirname1
extern int fs_numgamedirs;
extern char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
extern gamedir_t *fs_all_gamedirs; // terminated by entry with empty name
extern int fs_all_gamedirs_count;
-qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool complain, qbool failmissing);
+typedef enum addgamedirs_e {
+ GAMEDIRS_ALLGOOD = -1,
+ GAMEDIRS_FAILURE = 0,
+ GAMEDIRS_SUCCESS = 1
+} addgamedirs_t;
+addgamedirs_t FS_SetGameDirs(int numgamedirs, const char *gamedirs[], qbool failmissing, qbool abortonfail);
+qbool FS_ChangeGameDirs(int numgamedirs, const char *gamedirs[], qbool failmissing);
qbool FS_IsRegisteredQuakePack(const char *name);
int FS_CRCFile(const char *filename, size_t *filesizepointer);
void FS_UnloadPacks_dlcache(void);
//=============================================================================
/* MODLIST MENU */
-// same limit of mod dirs as in fs.c
-#define MODLIST_MAXDIRS 16
-static int modlist_enabled [MODLIST_MAXDIRS]; //array of indexs to modlist
+// same limit of mod dirs as in fs.c (allowing that one is used by gamedirname1)
+#define MODLIST_MAXDIRS MAX_GAMEDIRS - 1
static int modlist_numenabled; //number of enabled (or in process to be..) mods
typedef struct modlist_entry_s
{
qbool loaded; // used to determine whether this entry is loaded and running
- int enabled; // index to array of modlist_enabled
// name of the modification, this is displayed on the menu entry
char name[128];
int i,j;
stringlist_t list;
const char *description;
+ int desc_len;
stringlistinit(&list);
listdirectory(&list, fs_basedir, "");
stringlistsort(&list, true);
modlist_count = 0;
- modlist_numenabled = fs_numgamedirs;
+ modlist_numenabled = 0;
for (i = 0;i < list.numstrings && modlist_count < MODLIST_TOTALSIZE;i++)
{
- int desc_len;
-
// reject any dirs that are part of the base game
if (gamedirname1 && !strcasecmp(gamedirname1, list.strings[i])) continue;
//if (gamedirname2 && !strcasecmp(gamedirname2, list.strings[i])) continue;
}
dp_strlcpy (modlist[modlist_count].dir, list.strings[i], sizeof(modlist[modlist_count].dir));
- //check currently loaded mods
+
+ // check if this mod is currently loaded
modlist[modlist_count].loaded = false;
- if (fs_numgamedirs)
- for (j = 0; j < fs_numgamedirs; j++)
- if (!strcasecmp(fs_gamedirs[j], modlist[modlist_count].dir))
- {
- modlist[modlist_count].loaded = true;
- modlist[modlist_count].enabled = j;
- modlist_enabled[j] = modlist_count;
- break;
- }
+ for (j = 0; j < fs_numgamedirs; j++)
+ if (!strcasecmp(fs_gamedirs[j], modlist[modlist_count].dir))
+ {
+ modlist[modlist_count].loaded = true;
+ modlist_numenabled++;
+ break;
+ }
+
modlist_count ++;
}
stringlistfreecontents(&list);
{
int i;
int numgamedirs;
- char gamedirs[MODLIST_MAXDIRS][MAX_QPATH];
-
- // copy our mod list into an array for FS_ChangeGameDirs
- numgamedirs = modlist_numenabled;
- for (i = 0; i < modlist_numenabled; i++)
- dp_strlcpy (gamedirs[i], modlist[modlist_enabled[i]].dir,sizeof (gamedirs[i]));
-
- // this code snippet is from FS_ChangeGameDirs
- if (fs_numgamedirs == numgamedirs)
- {
- for (i = 0;i < numgamedirs;i++)
- if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
- break;
- if (i == numgamedirs)
- return; // already using this set of gamedirs, do nothing
- }
+ const char *gamedirs[MODLIST_MAXDIRS];
// this part is basically the same as the FS_GameDir_f function
if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
return;
}
- FS_ChangeGameDirs (modlist_numenabled, gamedirs, true, true);
+ // copy our mod list into an array for FS_ChangeGameDirs
+ for (i = 0, numgamedirs = 0; i < modlist_count && numgamedirs < MODLIST_MAXDIRS; i++)
+ if (modlist[i].loaded)
+ gamedirs[numgamedirs++] = modlist[i].dir;
+ // allow disabling all active mods using the menu
+ if (numgamedirs == 0)
+ {
+ numgamedirs = 1;
+ gamedirs[0] = gamedirname1;
+ }
+
+ FS_ChangeGameDirs(numgamedirs, gamedirs, true);
}
void M_Menu_ModList_f(cmd_state_t *cmd)
static void M_Menu_ModList_AdjustSliders (int dir)
{
- int i;
S_LocalSound ("sound/misc/menu3.wav");
// stop adding mods, we reach the limit
if (!modlist[modlist_cursor].loaded && (modlist_numenabled == MODLIST_MAXDIRS)) return;
+
modlist[modlist_cursor].loaded = !modlist[modlist_cursor].loaded;
- if (modlist[modlist_cursor].loaded)
- {
- modlist[modlist_cursor].enabled = modlist_numenabled;
- //push the value on the enabled list
- modlist_enabled[modlist_numenabled++] = modlist_cursor;
- }
- else
- {
- //eliminate the value from the enabled list
- for (i = modlist[modlist_cursor].enabled; i < modlist_numenabled; i++)
- {
- modlist_enabled[i] = modlist_enabled[i+1];
- modlist[modlist_enabled[i]].enabled--;
- }
- modlist_numenabled--;
- }
+ modlist_numenabled += modlist[modlist_cursor].loaded ? 1 : -1;
}
static void M_ModList_Draw (void)
M_PrintRed(432, 32, s_enabled);
// Draw a list box with all enabled mods
DrawQ_Pic(menu_x + 432, menu_y + 48, NULL, 172, 8 * modlist_numenabled, 0, 0, 0, 0.5, 0);
- for (y = 0; y < modlist_numenabled; y++)
- M_PrintRed(432, 48 + y * 8, modlist[modlist_enabled[y]].dir);
+ for (n = 0, y = 48; n < modlist_count; n++)
+ if (modlist[n].loaded)
+ {
+ M_PrintRed(432, y, modlist[n].dir);
+ y += 8;
+ }
if (*cl_connect_status)
M_Print(16, menu_height - 8, cl_connect_status);