Merge branch 'join-passwords' into 'next'

join passwords

See merge request KartKrew/Kart-Public!104
This commit is contained in:
colette 2019-03-10 19:12:59 -04:00
commit f4318aed58
15 changed files with 537 additions and 85 deletions

View file

@ -1254,7 +1254,8 @@ found:
var->string = var->zstring = Z_StrDup(valstr); var->string = var->zstring = Z_StrDup(valstr);
if (override) if (var->flags & CV_PASSWORD); // Don't change value for password field
else if (override)
var->value = overrideval; var->value = overrideval;
else if (var->flags & CV_FLOAT) else if (var->flags & CV_FLOAT)
{ {

View file

@ -95,7 +95,8 @@ typedef enum
CV_HIDEN = 1024, // variable is not part of the cvar list so cannot be accessed by the console CV_HIDEN = 1024, // variable is not part of the cvar list so cannot be accessed by the console
// can only be set when we have the pointer to it // can only be set when we have the pointer to it
// used on menus // used on menus
CV_CHEAT = 2048 // Don't let this be used in multiplayer unless cheats are on. CV_CHEAT = 2048, // Don't let this be used in multiplayer unless cheats are on.
CV_PASSWORD = 4096 // Password field
} cvflags_t; } cvflags_t;
typedef struct CV_PossibleValue_s typedef struct CV_PossibleValue_s

View file

@ -544,6 +544,22 @@ static void CON_MoveConsole(void)
} }
} }
INT32 CON_ShiftChar(INT32 ch)
{
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
{
if (shiftdown ^ capslock)
ch = shiftxform[ch];
}
else // if we're holding shift we should still shift non letter symbols
{
if (shiftdown)
ch = shiftxform[ch];
}
return ch;
}
// Clear time of console heads up messages // Clear time of console heads up messages
// //
void CON_ClearHUD(void) void CON_ClearHUD(void)
@ -1084,16 +1100,6 @@ boolean CON_Responder(event_t *ev)
else if (key == KEY_KPADSLASH) else if (key == KEY_KPADSLASH)
key = '/'; key = '/';
// capslock
if (key == KEY_CAPSLOCK) // it's a toggle.
{
if (capslock)
capslock = false;
else
capslock = true;
return true;
}
// same capslock code as hu_stuff.c's HU_responder. Check there for details. // same capslock code as hu_stuff.c's HU_responder. Check there for details.
if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z')) if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z'))
{ {

View file

@ -44,6 +44,8 @@ extern UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *or
// Console bg color (auto updated to match) // Console bg color (auto updated to match)
extern UINT8 *consolebgmap; extern UINT8 *consolebgmap;
INT32 CON_ShiftChar(INT32 ch);
void CON_SetupBackColormap(void); void CON_SetupBackColormap(void);
void CON_ClearHUD(void); // clear heads up messages void CON_ClearHUD(void); // clear heads up messages

View file

@ -22,6 +22,7 @@
#include "i_video.h" #include "i_video.h"
#include "d_net.h" #include "d_net.h"
#include "d_main.h" #include "d_main.h"
#include "d_event.h"
#include "g_game.h" #include "g_game.h"
#include "hu_stuff.h" #include "hu_stuff.h"
#include "keys.h" #include "keys.h"
@ -1126,12 +1127,22 @@ typedef enum
CL_DOWNLOADSAVEGAME, CL_DOWNLOADSAVEGAME,
#endif #endif
CL_CONNECTED, CL_CONNECTED,
CL_ABORTED CL_ABORTED,
CL_ASKDOWNLOADFILES,
CL_WAITDOWNLOADFILESRESPONSE,
CL_CHALLENGE
} cl_mode_t; } cl_mode_t;
static void GetPackets(void); static void GetPackets(void);
static cl_mode_t cl_mode = CL_SEARCHING; static cl_mode_t cl_mode = CL_SEARCHING;
static boolean cl_needsdownload = false;
static UINT8 cl_challengenum = 0;
static UINT8 cl_challengequestion[MD5_LEN+1];
static char cl_challengepassword[65];
static UINT8 cl_challengeanswer[MD5_LEN+1];
static UINT8 cl_challengeattempted = 0;
// Player name send/load // Player name send/load
@ -1168,6 +1179,8 @@ static void CV_LoadPlayerNames(UINT8 **p)
} }
#ifdef CLIENT_LOADINGSCREEN #ifdef CLIENT_LOADINGSCREEN
static UINT32 SL_SearchServer(INT32 node);
// //
// CL_DrawConnectionStatus // CL_DrawConnectionStatus
// //
@ -1191,11 +1204,42 @@ static inline void CL_DrawConnectionStatus(void)
// 15 pal entries total. // 15 pal entries total.
const char *cltext; const char *cltext;
if (cl_mode != CL_CHALLENGE)
for (i = 0; i < 16; ++i) for (i = 0; i < 16; ++i)
V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15)); V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15));
switch (cl_mode) switch (cl_mode)
{ {
case CL_CHALLENGE:
{
char asterisks[33];
size_t sl = min(32, strlen(cl_challengepassword));
UINT32 i;
memset(asterisks, '*', sl);
memset(asterisks+sl, 0, 33-sl);
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_MONOSPACE|V_ALLOWLOWERCASE, asterisks);
V_DrawFixedPatch((BASEVIDWIDTH/2) << FRACBITS, (BASEVIDHEIGHT/2) << FRACBITS, FRACUNIT, 0, W_CachePatchName("BSRVLOCK", PU_CACHE), NULL);
i = SL_SearchServer(servernode);
if (i == -1)
{
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT/2-8, 32, 1);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, V_REDMAP, M_GetText("This server is password protected."));
}
else
{
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT/2-8, 32, 3);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, V_REDMAP, M_GetText("This server,"));
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2+8, V_ALLOWLOWERCASE, serverlist[i].info.servername);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2+16, V_REDMAP, M_GetText("is password protected."));
}
cltext = M_GetText(cl_challengeattempted ? "Incorrect password. Please try again." : "Please enter the server password.");
}
break;
#ifdef JOININGAME #ifdef JOININGAME
case CL_DOWNLOADSAVEGAME: case CL_DOWNLOADSAVEGAME:
if (lastfilenum != -1) if (lastfilenum != -1)
@ -1215,6 +1259,9 @@ static inline void CL_DrawConnectionStatus(void)
case CL_WAITJOINRESPONSE: case CL_WAITJOINRESPONSE:
cltext = M_GetText("Requesting to join..."); cltext = M_GetText("Requesting to join...");
break; break;
case CL_ASKDOWNLOADFILES:
case CL_WAITDOWNLOADFILESRESPONSE:
cltext = M_GetText("Waiting to download files...");
default: default:
cltext = M_GetText("Connecting to server..."); cltext = M_GetText("Connecting to server...");
break; break;
@ -1292,6 +1339,9 @@ static boolean CL_SendJoin(void)
netbuffer->u.clientcfg.localplayers = localplayers; netbuffer->u.clientcfg.localplayers = localplayers;
netbuffer->u.clientcfg.version = VERSION; netbuffer->u.clientcfg.version = VERSION;
netbuffer->u.clientcfg.subversion = SUBVERSION; netbuffer->u.clientcfg.subversion = SUBVERSION;
netbuffer->u.clientcfg.needsdownload = cl_needsdownload;
netbuffer->u.clientcfg.challengenum = cl_challengenum;
memcpy(netbuffer->u.clientcfg.challengeanswer, cl_challengeanswer, MD5_LEN);
return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak)); return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
} }
@ -1312,7 +1362,13 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
netbuffer->u.serverinfo.gametype = (UINT8)(G_BattleGametype() ? VANILLA_GT_MATCH : VANILLA_GT_RACE); // SRB2Kart: Vanilla's gametype constants for MS support netbuffer->u.serverinfo.gametype = (UINT8)(G_BattleGametype() ? VANILLA_GT_MATCH : VANILLA_GT_RACE); // SRB2Kart: Vanilla's gametype constants for MS support
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated;
netbuffer->u.serverinfo.kartvars = (UINT8) (
(cv_kartspeed.value & SV_SPEEDMASK) |
(dedicated ? SV_DEDICATED : 0) |
(D_IsJoinPasswordOn() ? SV_PASSWORD : 0)
);
strncpy(netbuffer->u.serverinfo.servername, cv_servername.string, strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
MAXSERVERNAME); MAXSERVERNAME);
strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
@ -1872,7 +1928,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
/** Called by CL_ServerConnectionTicker /** Called by CL_ServerConnectionTicker
* *
* \param viams ??? * \param viams ???
* \param asksent ??? * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
* \return False if the connection was aborted * \return False if the connection was aborted
* \sa CL_ServerConnectionTicker * \sa CL_ServerConnectionTicker
* \sa CL_ConnectToServer * \sa CL_ConnectToServer
@ -1965,9 +2021,12 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent)
), NULL, MM_NOTHING); ), NULL, MM_NOTHING);
return false; return false;
} }
cl_mode = CL_ASKDOWNLOADFILES;
// no problem if can't send packet, we will retry later // no problem if can't send packet, we will retry later
if (CL_SendRequestFile()) //if (CL_SendRequestFile())
cl_mode = CL_DOWNLOADFILES; // cl_mode = CL_DOWNLOADFILES;
} }
} }
else else
@ -1997,7 +2056,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent)
* \param viams ??? * \param viams ???
* \param tmpsave The name of the gamestate file??? * \param tmpsave The name of the gamestate file???
* \param oldtic Used for knowing when to poll events and redraw * \param oldtic Used for knowing when to poll events and redraw
* \param asksent ??? * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
* \return False if the connection was aborted * \return False if the connection was aborted
* \sa CL_ServerConnectionSearchTicker * \sa CL_ServerConnectionSearchTicker
* \sa CL_ConnectToServer * \sa CL_ConnectToServer
@ -2035,6 +2094,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
/* FALLTHRU */ /* FALLTHRU */
case CL_ASKJOIN: case CL_ASKJOIN:
cl_needsdownload = false;
CL_LoadServerFiles(); CL_LoadServerFiles();
#ifdef JOININGAME #ifdef JOININGAME
// prepare structures to save the file // prepare structures to save the file
@ -2043,9 +2103,23 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
CL_PrepareDownloadSaveGame(tmpsave); CL_PrepareDownloadSaveGame(tmpsave);
#endif #endif
if (CL_SendJoin()) if (CL_SendJoin())
{
*asksent = I_GetTime();
cl_mode = CL_WAITJOINRESPONSE; cl_mode = CL_WAITJOINRESPONSE;
}
break; break;
case CL_ASKDOWNLOADFILES:
cl_needsdownload = true;
if (CL_SendJoin())
{
*asksent = I_GetTime();
cl_mode = CL_WAITDOWNLOADFILESRESPONSE;
}
break;
#ifdef JOININGAME #ifdef JOININGAME
case CL_DOWNLOADSAVEGAME: case CL_DOWNLOADSAVEGAME:
// At this state, the first (and only) needed file is the gamestate // At this state, the first (and only) needed file is the gamestate
@ -2059,7 +2133,19 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
break; break;
#endif #endif
case CL_CHALLENGE:
(*asksent) = I_GetTime() - NEWTICRATE; // Send password immediately upon entering
break;
case CL_WAITJOINRESPONSE: case CL_WAITJOINRESPONSE:
case CL_WAITDOWNLOADFILESRESPONSE:
if (*asksent + NEWTICRATE < I_GetTime() && CL_SendJoin())
{
*asksent = I_GetTime();
}
break;
case CL_CONNECTED: case CL_CONNECTED:
default: default:
break; break;
@ -2077,20 +2163,10 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
// Call it only once by tic // Call it only once by tic
if (*oldtic != I_GetTime()) if (*oldtic != I_GetTime())
{ {
INT32 key;
I_OsPolling(); I_OsPolling();
key = I_GetKey(); D_ProcessEvents();
// Only ESC and non-keyboard keys abort connection if (gamestate != GS_WAITINGPLAYERS)
if (key == KEY_ESCAPE || key >= KEY_MOUSE1)
{
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
D_QuitNetGame();
CL_Reset();
D_StartTitle();
return false; return false;
}
// why are these here? this is for servers, we're a client // why are these here? this is for servers, we're a client
//if (key == 's' && server) //if (key == 's' && server)
@ -2119,6 +2195,71 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
return true; return true;
} }
boolean CL_Responder(event_t *ev)
{
size_t len;
INT32 ch;
if (!(client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED))
return false; // Don't do anything outside of the connection screen
if (ev->type != ev_keydown)
return false;
ch = (INT32)ev->data1;
// Only ESC and non-keyboard keys abort connection
if (ch == KEY_ESCAPE || ch >= KEY_MOUSE1)
{
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
//M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
D_QuitNetGame();
CL_Reset();
D_StartTitle();
return true;
}
if (cl_mode != CL_CHALLENGE)
return false;
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART])
|| ch == ' ') // Allow spaces, of course
{
len = strlen(cl_challengepassword);
if (len < 64)
{
cl_challengepassword[len+1] = 0;
cl_challengepassword[len] = CON_ShiftChar(ch);
}
cl_challengeattempted = 0;
}
else if (ch == KEY_BACKSPACE)
{
len = strlen(cl_challengepassword);
if (len > 0)
cl_challengepassword[len-1] = 0;
cl_challengeattempted = 0;
}
else if (ch == KEY_ENTER)
{
netgame = true;
multiplayer = true;
#ifndef NONET
SL_ClearServerList(servernode);
#endif
cl_mode = CL_SEARCHING;
D_ComputeChallengeAnswer(cl_challengequestion, cl_challengepassword, cl_challengeanswer);
cl_challengeattempted = 1;
}
return true;
}
/** Use adaptive send using net_bandwidth and stat.sendbytes /** Use adaptive send using net_bandwidth and stat.sendbytes
* *
* \param viams ??? * \param viams ???
@ -2139,6 +2280,7 @@ static void CL_ConnectToServer(boolean viams)
#endif #endif
cl_mode = CL_SEARCHING; cl_mode = CL_SEARCHING;
cl_challengenum = 0;
#ifdef CLIENT_LOADINGSCREEN #ifdef CLIENT_LOADINGSCREEN
lastfilenum = -1; lastfilenum = -1;
@ -2196,6 +2338,8 @@ static void CL_ConnectToServer(boolean viams)
SL_ClearServerList(servernode); SL_ClearServerList(servernode);
#endif #endif
cl_challengeattempted = 0;
do do
{ {
// If the connection was aborted for some reason, leave // If the connection was aborted for some reason, leave
@ -3142,6 +3286,9 @@ void D_ClientServerInit(void)
gametic = 0; gametic = 0;
localgametic = 0; localgametic = 0;
memset(cl_challengequestion, 0x00, MD5_LEN+1);
memset(cl_challengeanswer, 0x00, MD5_LEN+1);
// do not send anything before the real begin // do not send anything before the real begin
SV_StopServer(); SV_StopServer();
SV_ResetServer(); SV_ResetServer();
@ -3631,6 +3778,33 @@ static void HandleConnect(SINT8 node)
boolean newnode = false; boolean newnode = false;
#endif #endif
if (node != servernode && !nodeingame[node] && D_IsJoinPasswordOn())
{
// Ensure node sent the correct password challenge
boolean passed = false;
if (netbuffer->u.clientcfg.challengenum && D_VerifyJoinPasswordChallenge(netbuffer->u.clientcfg.challengenum, netbuffer->u.clientcfg.challengeanswer))
passed = true;
if (!passed)
{
D_MakeJoinPasswordChallenge(&netbuffer->u.joinchallenge.challengenum, netbuffer->u.joinchallenge.question);
netbuffer->packettype = PT_JOINCHALLENGE;
HSendPacket(node, true, 0, sizeof(joinchallenge_pak));
Net_CloseConnection(node);
return;
}
}
if (netbuffer->u.clientcfg.needsdownload)
{
netbuffer->packettype = PT_DOWNLOADFILESOKAY;
HSendPacket(node, true, 0, 0);
return;
}
// client authorised to join // client authorised to join
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]); nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
if (!nodeingame[node]) if (!nodeingame[node])
@ -3639,6 +3813,7 @@ static void HandleConnect(SINT8 node)
#ifndef NONET #ifndef NONET
newnode = true; newnode = true;
#endif #endif
SV_AddNode(node); SV_AddNode(node);
/// \note Wait what??? /// \note Wait what???
@ -3794,6 +3969,43 @@ static void HandlePacketFromAwayNode(SINT8 node)
Net_CloseConnection(node); Net_CloseConnection(node);
break; break;
case PT_JOINCHALLENGE:
if (server && serverrunning)
{ // But wait I thought I'm the server?
Net_CloseConnection(node);
break;
}
SERVERONLY
if (cl_mode == CL_WAITJOINRESPONSE || cl_mode == CL_WAITDOWNLOADFILESRESPONSE)
{
cl_challengenum = netbuffer->u.joinchallenge.challengenum;
memcpy(cl_challengequestion, netbuffer->u.joinchallenge.question, 16);
Net_CloseConnection(node|FORCECLOSE); // Don't need to stay connected while challenging
cl_mode = CL_CHALLENGE;
switch (cl_challengeattempted)
{
case 2:
// We already sent a correct password, so throw it back up again.
D_ComputeChallengeAnswer(cl_challengequestion, cl_challengepassword, cl_challengeanswer);
cl_mode = CL_ASKJOIN;
break;
case 1:
// We entered the wrong password!
S_StartSound(NULL, sfx_s26d);
break;
default:
// First entry to the password screen.
S_StartSound(NULL, sfx_s224);
break;
}
}
break;
case PT_SERVERREFUSE: // Negative response of client join request case PT_SERVERREFUSE: // Negative response of client join request
if (server && serverrunning) if (server && serverrunning)
{ // But wait I thought I'm the server? { // But wait I thought I'm the server?
@ -3822,6 +4034,41 @@ static void HandlePacketFromAwayNode(SINT8 node)
} }
break; break;
case PT_DOWNLOADFILESOKAY:
if (server && serverrunning)
{ // But wait I thought I'm the server?
Net_CloseConnection(node);
break;
}
SERVERONLY
// This should've already been checked, but just to be safe...
if (!CL_CheckDownloadable())
{
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText(
"You cannot connect to this server\n"
"because you cannot download the files\n"
"that you are missing from the server.\n\n"
"See the console or log file for\n"
"more details.\n\n"
"Press ESC\n"
), NULL, MM_NOTHING);
break;
}
if (cl_challengeattempted == 1) // Successful password noise.
S_StartSound(NULL, sfx_s221);
cl_challengeattempted = 2;
CONS_Printf("trying to download\n");
if (CL_SendRequestFile())
cl_mode = CL_DOWNLOADFILES;
break;
case PT_SERVERCFG: // Positive response of client join request case PT_SERVERCFG: // Positive response of client join request
{ {
INT32 j; INT32 j;
@ -3837,6 +4084,9 @@ static void HandlePacketFromAwayNode(SINT8 node)
if (cl_mode != CL_WAITJOINRESPONSE) if (cl_mode != CL_WAITJOINRESPONSE)
break; break;
if (cl_challengeattempted == 1) // Successful password noise.
S_StartSound(NULL, sfx_s221);
if (client) if (client)
{ {
maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);

View file

@ -13,11 +13,14 @@
#ifndef __D_CLISRV__ #ifndef __D_CLISRV__
#define __D_CLISRV__ #define __D_CLISRV__
#include "d_event.h"
#include "d_ticcmd.h" #include "d_ticcmd.h"
#include "d_netcmd.h" #include "d_netcmd.h"
#include "tables.h" #include "tables.h"
#include "d_player.h" #include "d_player.h"
#include "md5.h"
// Network play related stuff. // Network play related stuff.
// There is a data struct that stores network // There is a data struct that stores network
// communication related stuff, and another // communication related stuff, and another
@ -73,6 +76,9 @@ typedef enum
PT_CLIENT4MIS, PT_CLIENT4MIS,
PT_BASICKEEPALIVE,// Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called PT_BASICKEEPALIVE,// Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called
PT_JOINCHALLENGE, // You must give a password to joinnnnn
PT_DOWNLOADFILESOKAY, // You can download files from the server....
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
// allows HSendPacket(*, true, *, *) to return false. // allows HSendPacket(*, true, *, *) to return false.
// In addition, this packet can't occupy all the available slots. // In addition, this packet can't occupy all the available slots.
@ -353,9 +359,21 @@ typedef struct
UINT8 version; // Different versions don't work UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version UINT8 subversion; // Contains build version
UINT8 localplayers; UINT8 localplayers;
UINT8 mode; UINT8 needsdownload;
UINT8 challengenum; // Non-zero if trying to join with a password attempt
UINT8 challengeanswer[MD5_LEN]; // Join challenge
} ATTRPACK clientconfig_pak; } ATTRPACK clientconfig_pak;
typedef struct
{
UINT8 challengenum; // Number to send back in join attempt
UINT8 question[MD5_LEN]; // Challenge data to be manipulated and answered with
} ATTRPACK joinchallenge_pak;
#define SV_SPEEDMASK 0x03
#define SV_DEDICATED 0x40
#define SV_PASSWORD 0x80
#define MAXSERVERNAME 32 #define MAXSERVERNAME 32
#define MAXFILENEEDED 915 #define MAXFILENEEDED 915
// This packet is too large // This packet is too large
@ -368,7 +386,7 @@ typedef struct
UINT8 gametype; UINT8 gametype;
UINT8 modifiedgame; UINT8 modifiedgame;
UINT8 cheatsenabled; UINT8 cheatsenabled;
UINT8 isdedicated; UINT8 kartvars; // Previously isdedicated, now appropriated for our own nefarious purposes
UINT8 fileneedednum; UINT8 fileneedednum;
SINT8 adminplayer; SINT8 adminplayer;
tic_t time; tic_t time;
@ -447,7 +465,8 @@ typedef struct
UINT8 resynchgot; // UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
filetx_pak filetxpak; // 139 bytes filetx_pak filetxpak; // 139 bytes
clientconfig_pak clientcfg; // 136 bytes clientconfig_pak clientcfg; // 153 bytes
joinchallenge_pak joinchallenge; // 17 bytes
serverinfo_pak serverinfo; // 1024 bytes serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
askinfo_pak askinfo; // 61 bytes askinfo_pak askinfo; // 61 bytes
@ -555,6 +574,7 @@ void CL_RemoveSplitscreenPlayer(UINT8 p);
void CL_Reset(void); void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum); void CL_ClearPlayer(INT32 playernum);
void CL_UpdateServerList(boolean internetsearch, INT32 room); void CL_UpdateServerList(boolean internetsearch, INT32 room);
boolean CL_Responder(event_t *ev);
// Is there a game running // Is there a game running
boolean Playing(void); boolean Playing(void);

View file

@ -203,6 +203,8 @@ static inline void D_ModifierKeyResponder(event_t *ev)
case KEY_RCTRL: ctrldown |= 0x2; return; case KEY_RCTRL: ctrldown |= 0x2; return;
case KEY_LALT: altdown |= 0x1; return; case KEY_LALT: altdown |= 0x1; return;
case KEY_RALT: altdown |= 0x2; return; case KEY_RALT: altdown |= 0x2; return;
case KEY_CAPSLOCK: capslock = !capslock; return;
default: return; default: return;
} }
else if (ev->type == ev_keyup) switch (ev->data1) else if (ev->type == ev_keyup) switch (ev->data1)
@ -236,6 +238,9 @@ void D_ProcessEvents(void)
if (M_ScreenshotResponder(ev)) if (M_ScreenshotResponder(ev))
continue; // ate the event continue; // ate the event
if (CL_Responder(ev))
continue;
if (gameaction == ga_nothing && gamestate == GS_TITLESCREEN) if (gameaction == ga_nothing && gamestate == GS_TITLESCREEN)
{ {
if (cht_Responder(ev)) if (cht_Responder(ev))

View file

@ -821,10 +821,6 @@ static const char *packettypename[NUMPACKETTYPE] =
"CLIENTMIS", "CLIENTMIS",
"CLIENT2CMD", "CLIENT2CMD",
"CLIENT2MIS", "CLIENT2MIS",
"CLIENT3CMD",
"CLIENT3MIS",
"CLIENT4CMD",
"CLIENT4MIS",
"NODEKEEPALIVE", "NODEKEEPALIVE",
"NODEKEEPALIVEMIS", "NODEKEEPALIVEMIS",
"SERVERTICS", "SERVERTICS",
@ -841,6 +837,15 @@ static const char *packettypename[NUMPACKETTYPE] =
"RESYNCHEND", "RESYNCHEND",
"RESYNCHGET", "RESYNCHGET",
"CLIENT3CMD",
"CLIENT3MIS",
"CLIENT4CMD",
"CLIENT4MIS",
"BASICKEEPALIVE",
"JOINCHALLENGE",
"DOWNLOADFILESOKAY",
"FILEFRAGMENT", "FILEFRAGMENT",
"TEXTCMD", "TEXTCMD",
"TEXTCMD2", "TEXTCMD2",
@ -868,7 +873,7 @@ static void DebugPrintpacket(const char *header)
break; break;
case PT_CLIENTJOIN: case PT_CLIENTJOIN:
fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers, fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
netbuffer->u.clientcfg.mode); netbuffer->u.clientcfg.needsdownload);
break; break;
case PT_SERVERTICS: case PT_SERVERTICS:
{ {

View file

@ -169,6 +169,7 @@ static void Got_Verification(UINT8 **cp, INT32 playernum);
static void Got_Removal(UINT8 **cp, INT32 playernum); static void Got_Removal(UINT8 **cp, INT32 playernum);
static void Command_Verify_f(void); static void Command_Verify_f(void);
static void Command_RemoveAdmin_f(void); static void Command_RemoveAdmin_f(void);
static void Command_ChangeJoinPassword_f(void);
static void Command_MotD_f(void); static void Command_MotD_f(void);
static void Got_MotD_f(UINT8 **cp, INT32 playernum); static void Got_MotD_f(UINT8 **cp, INT32 playernum);
@ -534,6 +535,8 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_PICKVOTE, Got_PickVotecmd); RegisterNetXCmd(XD_PICKVOTE, Got_PickVotecmd);
// Remote Administration // Remote Administration
CV_RegisterVar(&cv_dummyjoinpassword);
COM_AddCommand("joinpassword", Command_ChangeJoinPassword_f);
COM_AddCommand("password", Command_Changepassword_f); COM_AddCommand("password", Command_Changepassword_f);
RegisterNetXCmd(XD_LOGIN, Got_Login); RegisterNetXCmd(XD_LOGIN, Got_Login);
COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
@ -3427,6 +3430,7 @@ static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt,
if (len > 256-sl) if (len > 256-sl)
len = 256-sl; len = 256-sl;
memcpy(tmpbuf, buffer, len); memcpy(tmpbuf, buffer, len);
memmove(&tmpbuf[len], salt, sl); memmove(&tmpbuf[len], salt, sl);
//strcpy(&tmpbuf[len], salt); //strcpy(&tmpbuf[len], salt);
@ -3440,7 +3444,7 @@ static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt,
} }
#define BASESALT "basepasswordstorage" #define BASESALT "basepasswordstorage"
static UINT8 adminpassmd5[16]; static UINT8 adminpassmd5[MD5_LEN];
static boolean adminpasswordset = false; static boolean adminpasswordset = false;
void D_SetPassword(const char *pw) void D_SetPassword(const char *pw)
@ -3479,7 +3483,7 @@ static void Command_Login_f(void)
// If we have no MD5 support then completely disable XD_LOGIN responses for security. // If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n"); CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else #else
XBOXSTATIC UINT8 finalmd5[16]; XBOXSTATIC UINT8 finalmd5[MD5_LEN];
const char *pw; const char *pw;
if (!netgame) if (!netgame)
@ -3502,11 +3506,11 @@ static void Command_Login_f(void)
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &finalmd5); D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &finalmd5);
// Do the final pass to get the comparison the server will come up with // Do the final pass to get the comparison the server will come up with
D_MD5PasswordPass(finalmd5, 16, va("PNUM%02d", consoleplayer), &finalmd5); D_MD5PasswordPass(finalmd5, MD5_LEN, va("PNUM%02d", consoleplayer), &finalmd5);
CONS_Printf(M_GetText("Sending login... (Notice only given if password is correct.)\n")); CONS_Printf(M_GetText("Sending login... (Notice only given if password is correct.)\n"));
SendNetXCmd(XD_LOGIN, finalmd5, 16); SendNetXCmd(XD_LOGIN, finalmd5, MD5_LEN);
#endif #endif
} }
@ -3517,9 +3521,9 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
(void)cp; (void)cp;
(void)playernum; (void)playernum;
#else #else
UINT8 sentmd5[16], finalmd5[16]; UINT8 sentmd5[MD5_LEN], finalmd5[MD5_LEN];
READMEM(*cp, sentmd5, 16); READMEM(*cp, sentmd5, MD5_LEN);
if (client) if (client)
return; return;
@ -3531,9 +3535,9 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
} }
// Do the final pass to compare with the sent md5 // Do the final pass to compare with the sent md5
D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", playernum), &finalmd5); D_MD5PasswordPass(adminpassmd5, MD5_LEN, va("PNUM%02d", playernum), &finalmd5);
if (!memcmp(sentmd5, finalmd5, 16)) if (!memcmp(sentmd5, finalmd5, MD5_LEN))
{ {
CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[playernum]); CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[playernum]);
COM_BufInsertText(va("promote %d\n", playernum)); // do this immediately COM_BufInsertText(va("promote %d\n", playernum)); // do this immediately
@ -3702,6 +3706,131 @@ static void Got_Removal(UINT8 **cp, INT32 playernum)
CONS_Printf(M_GetText("You are no longer a server administrator.\n")); CONS_Printf(M_GetText("You are no longer a server administrator.\n"));
} }
// Join password stuff
consvar_t cv_dummyjoinpassword = {"dummyjoinpassword", "", CV_HIDEN|CV_NOSHOWHELP|CV_PASSWORD, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
#define NUMJOINCHALLENGES 32
static UINT8 joinpassmd5[MD5_LEN+1];
boolean joinpasswordset = false;
static UINT8 joinpasschallenges[NUMJOINCHALLENGES][MD5_LEN];
static tic_t joinpasschallengeson[NUMJOINCHALLENGES];
boolean D_IsJoinPasswordOn(void)
{
return joinpasswordset;
}
static inline void GetChallengeAnswer(UINT8 *question, UINT8 *passwordmd5, UINT8 *answer)
{
D_MD5PasswordPass(question, MD5_LEN, (char *) passwordmd5, answer);
}
void D_ComputeChallengeAnswer(UINT8 *question, const char *pw, UINT8 *answer)
{
static UINT8 passwordmd5[MD5_LEN+1];
memset(passwordmd5, 0x00, MD5_LEN+1);
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &passwordmd5);
GetChallengeAnswer(question, passwordmd5, answer);
}
void D_SetJoinPassword(const char *pw)
{
memset(joinpassmd5, 0x00, MD5_LEN+1);
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &joinpassmd5);
joinpasswordset = true;
}
boolean D_VerifyJoinPasswordChallenge(UINT8 num, UINT8 *answer)
{
boolean passed = false;
num %= NUMJOINCHALLENGES;
//@TODO use a constant-time memcmp....
if (joinpasschallengeson[num] > 0 && memcmp(answer, joinpasschallenges[num], MD5_LEN) == 0)
passed = true;
// Wipe and reset the challenge so that it can't be tried against again, as a small measure against brute-force attacks.
memset(joinpasschallenges[num], 0x00, MD5_LEN);
joinpasschallengeson[num] = 0;
return passed;
}
void D_MakeJoinPasswordChallenge(UINT8 *num, UINT8 *question)
{
size_t i;
for (i = 0; i < NUMJOINCHALLENGES; i++)
{
(*num) = M_RandomKey(NUMJOINCHALLENGES);
if (joinpasschallengeson[(*num)] == 0)
break;
}
if (joinpasschallengeson[(*num)] > 0)
{
// Ugh, all challenges are (probably) taken. Let's find the oldest one and overwrite it.
tic_t oldesttic = INT32_MAX;
for (i = 0; i < NUMJOINCHALLENGES; i++)
{
if (joinpasschallengeson[i] < oldesttic)
{
(*num) = i;
oldesttic = joinpasschallengeson[i];
}
}
}
joinpasschallengeson[(*num)] = I_GetTime();
memset(question, 0x00, MD5_LEN);
for (i = 0; i < MD5_LEN; i++)
question[i] = M_RandomByte();
// Store the answer in memory. What was the question again?
GetChallengeAnswer(question, joinpassmd5, joinpasschallenges[(*num)]);
// This ensures that num is always non-zero and will be valid when used for the answer
if ((*num) == 0)
(*num) = NUMJOINCHALLENGES;
}
// Remote Administration
static void Command_ChangeJoinPassword_f(void)
{
#ifdef NOMD5
// If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else
if (client) // cannot change remotely
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
}
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("joinpassword <password>: set a password to join the server\nUse -remove to disable the password.\n"));
return;
}
if (strcmp(COM_Argv(1), "-remove") == 0)
{
joinpasswordset = false;
CONS_Printf(M_GetText("Join password removed.\n"));
}
else
{
D_SetJoinPassword(COM_Argv(1));
CONS_Printf(M_GetText("Join password set.\n"));
}
#endif
}
static void Command_MotD_f(void) static void Command_MotD_f(void)
{ {
size_t i, j; size_t i, j;

View file

@ -248,6 +248,14 @@ void RemoveAdminPlayer(INT32 playernum);
void ItemFinder_OnChange(void); void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw); void D_SetPassword(const char *pw);
extern consvar_t cv_dummyjoinpassword;
extern boolean joinpasswordset;
boolean D_IsJoinPasswordOn(void);
void D_ComputeChallengeAnswer(UINT8 *question, const char *pw, UINT8 *answer);
void D_SetJoinPassword(const char *pw);
boolean D_VerifyJoinPasswordChallenge(UINT8 num, UINT8 *answer);
void D_MakeJoinPasswordChallenge(UINT8 *num, UINT8 *question);
// used for the player setup menu // used for the player setup menu
UINT8 CanChangeSkin(INT32 playernum); UINT8 CanChangeSkin(INT32 playernum);

View file

@ -1225,8 +1225,6 @@ static INT16 typelines = 1; // number of drawfill lines we need when drawing the
// //
boolean HU_Responder(event_t *ev) boolean HU_Responder(event_t *ev)
{ {
INT32 c=0;
if (ev->type != ev_keydown) if (ev->type != ev_keydown)
return false; return false;
@ -1252,18 +1250,6 @@ boolean HU_Responder(event_t *ev)
return false; return false;
} }
c = (INT32)ev->data1;
// capslock (now handled outside of chat on so that it works everytime......)
if (c && c == KEY_CAPSLOCK) // it's a toggle.
{
if (capslock)
capslock = false;
else
capslock = true;
return true;
}
#ifndef NONET #ifndef NONET
if (!chat_on) if (!chat_on)
{ {
@ -1291,6 +1277,7 @@ boolean HU_Responder(event_t *ev)
} }
else // if chat_on else // if chat_on
{ {
INT32 c = (INT32)ev->data1;
// Ignore modifier keys // Ignore modifier keys
// Note that we do this here so users can still set // Note that we do this here so users can still set
@ -1306,20 +1293,7 @@ boolean HU_Responder(event_t *ev)
&& ev->data1 != gamecontrol[gc_talkkey][1])) && ev->data1 != gamecontrol[gc_talkkey][1]))
return false; return false;
c = (INT32)ev->data1; c = CON_ShiftChar(c);
// I know this looks very messy but this works. If it ain't broke, don't fix it!
// shift LETTERS to uppercase if we have capslock or are holding shift
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
{
if (shiftdown ^ capslock)
c = shiftxform[c];
}
else // if we're holding shift we should still shift non letter symbols
{
if (shiftdown)
c = shiftxform[c];
}
// pasting. pasting is cool. chat is a bit limited, though :( // pasting. pasting is cool. chat is a bit limited, though :(
if (((c == 'v' || c == 'V') && ctrldown) && !CHAT_MUTE) if (((c == 'v' || c == 'V') && ctrldown) && !CHAT_MUTE)

View file

@ -948,9 +948,10 @@ static menuitem_t MP_MainMenu[] =
static menuitem_t MP_ServerMenu[] = static menuitem_t MP_ServerMenu[] =
{ {
{IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 10}, {IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 0},
{IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 20}, {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 10},
{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30}, {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 20},
{IT_STRING|IT_CVAR|IT_CV_PASSWORD, NULL, "Password", &cv_dummyjoinpassword, 44},
{IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68}, {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68},
{IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78}, {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78},
@ -2383,6 +2384,9 @@ static void M_NextOpt(void)
{ {
INT16 oldItemOn = itemOn; // prevent infinite loop INT16 oldItemOn = itemOn; // prevent infinite loop
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value = 0;
do do
{ {
if (itemOn + 1 > currentMenu->numitems - 1) if (itemOn + 1 > currentMenu->numitems - 1)
@ -2396,6 +2400,9 @@ static void M_PrevOpt(void)
{ {
INT16 oldItemOn = itemOn; // prevent infinite loop INT16 oldItemOn = itemOn; // prevent infinite loop
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value = 0;
do do
{ {
if (!itemOn) if (!itemOn)
@ -2682,8 +2689,11 @@ boolean M_Responder(event_t *ev)
// BP: one of the more big hack i have never made // BP: one of the more big hack i have never made
if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR) if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)
{ {
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING) if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING || (currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
{ {
if (ch == KEY_TAB && (currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value ^= 1;
if (shiftdown && ch >= 32 && ch <= 127) if (shiftdown && ch >= 32 && ch <= 127)
ch = shiftxform[ch]; ch = shiftxform[ch];
if (M_ChangeStringCvar(ch)) if (M_ChangeStringCvar(ch))
@ -3557,6 +3567,8 @@ static void M_DrawGenericMenu(void)
case IT_CVAR: case IT_CVAR:
{ {
consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction;
char asterisks[MAXSTRINGLENGTH+1];
size_t sl;
switch (currentMenu->menuitems[i].status & IT_CVARTYPE) switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
{ {
case IT_CV_SLIDER: case IT_CV_SLIDER:
@ -3564,6 +3576,27 @@ static void M_DrawGenericMenu(void)
case IT_CV_NOPRINT: // color use this case IT_CV_NOPRINT: // color use this
case IT_CV_INVISSLIDER: // monitor toggles use this case IT_CV_INVISSLIDER: // monitor toggles use this
break; break;
case IT_CV_PASSWORD:
if (i == itemOn)
{
V_DrawRightAlignedThinString(x + MAXSTRINGLENGTH*8 + 10, y, V_ALLOWLOWERCASE, va(M_GetText("Tab: %s password"), cv->value ? "hide" : "show"));
}
if (!cv->value || i != itemOn)
{
sl = strlen(cv->string);
memset(asterisks, '*', sl);
memset(asterisks + sl, 0, MAXSTRINGLENGTH+1-sl);
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, asterisks);
if (skullAnimCounter < 4 && i == itemOn)
V_DrawCharacter(x + 8 + V_StringWidth(asterisks, 0), y + 12,
'_' | 0x80, false);
y += 16;
break;
}
/* fallthru */
case IT_CV_STRING: case IT_CV_STRING:
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1); M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string); V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
@ -7241,6 +7274,7 @@ static void M_DrawConnectMenu(void)
{ {
UINT16 i, j; UINT16 i, j;
const char *gt = "Unknown"; const char *gt = "Unknown";
const char *spd = "";
INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE; INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++) for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
@ -7294,7 +7328,17 @@ static void M_DrawConnectMenu(void)
V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags, V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags,
va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer)); va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, va("Gametype: %s", gt)); V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, gt);
if (serverlist[slindex].info.gametype == GT_RACE)
{
spd = kartspeed_cons_t[serverlist[slindex].info.kartvars & SV_SPEEDMASK].strvalue;
V_DrawSmallString(currentMenu->x+132, S_LINEY(i)+8, globalflags, va("(%s Speed)", spd));
}
if (serverlist[slindex].info.kartvars & SV_PASSWORD)
V_DrawFixedPatch((currentMenu->x - 9) << FRACBITS, (S_LINEY(i)) << FRACBITS, FRACUNIT, globalflags & (~V_ALLOWLOWERCASE), W_CachePatchName("SERVLOCK", PU_CACHE), NULL);
MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL; MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
} }
@ -7529,6 +7573,11 @@ static void M_StartServer(INT32 choice)
// Still need to reset devmode // Still need to reset devmode
cv_debug = 0; cv_debug = 0;
if (strlen(cv_dummyjoinpassword.string) > 0)
D_SetJoinPassword(cv_dummyjoinpassword.string);
else
joinpasswordset = false;
if (demoplayback) if (demoplayback)
G_StopDemo(); G_StopDemo();
if (metalrecording) if (metalrecording)

View file

@ -104,6 +104,7 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
#define IT_CV_NOPRINT 1536 #define IT_CV_NOPRINT 1536
#define IT_CV_NOMOD 2048 #define IT_CV_NOMOD 2048
#define IT_CV_INVISSLIDER 2560 #define IT_CV_INVISSLIDER 2560
#define IT_CV_PASSWORD 3072
//call/submenu specific //call/submenu specific
// There used to be a lot more here but ... // There used to be a lot more here but ...

View file

@ -22,6 +22,8 @@
# include <limits.h> # include <limits.h>
#endif #endif
#define MD5_LEN 16
/* The following contortions are an attempt to use the C preprocessor /* The following contortions are an attempt to use the C preprocessor
to determine an unsigned integral type that is 32 bits wide. An to determine an unsigned integral type that is 32 bits wide. An
alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but

View file

@ -1556,7 +1556,6 @@ void *W_CachePatchName(const char *name, INT32 tag)
return W_CachePatchNum(num, tag); return W_CachePatchNum(num, tag);
} }
#ifndef NOMD5 #ifndef NOMD5
#define MD5_LEN 16
/** /**
* Prints an MD5 string into a human-readable textual format. * Prints an MD5 string into a human-readable textual format.