--- src/c_cmds.cpp
+++ src/c_cmds.cpp
@@ -163,6 +163,10 @@ CCMD (quit)
 	if ( ACS_IsCalledFromConsoleCommand( ))
 		return;
 
+	// [K6] Graceful shutdown, remove the map crash info file.
+	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
+		remove( GAMENAMELOWERCASE"-server.map" );
+
 	if (!insave) exit (0);
 }
 
@@ -172,6 +176,10 @@ CCMD (exit)
 	if ( ACS_IsCalledFromConsoleCommand( ))
 		return;
 
+	// [K6] Graceful shutdown, remove the map crash info file.
+	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
+		remove( GAMENAMELOWERCASE"-server.map" );
+
 	if (!insave) exit (0);
 }
 
--- src/d_main.cpp
+++ src/d_main.cpp
@@ -2559,18 +2559,47 @@ void D_DoomMain (void)
 			G_NewInit( );
 
 			// Check if we have map rotation setup. If we do, use the first map there.
+			// [K6] If a file with last played map exists, start from that map.
 			if (( sv_maprotation ) && ( MAPROTATION_GetNumEntries( ) > 0 ))
 			{
 				// [BB] G_InitNew seems to alter the contents of the first argument, which it shouldn't.
 				// This causes the "Frags" bug. The following is just a workaround, the behavior of
 				// G_InitNew should be fixed.
 				char levelname[10];
-				sprintf( levelname, "%s", MAPROTATION_GetMap( 0 )->mapname );
+				FILE * pFile;
+				pFile = fopen( GAMENAMELOWERCASE"-server.map","r" );
+				if ( pFile )
+				{
+					fgets( levelname, 10, pFile ); // let's just hijack the "levelname" variable here
+					int i;
+					i = atoi(levelname);
+					sprintf( levelname, "%s", MAPROTATION_GetMap( i )->mapname );
+					fclose( pFile );
+					MAPROTATION_SetPositionToMap( levelname );
+				}
+				else
+				{
+					sprintf( levelname, "%s", MAPROTATION_GetMap( 0 )->mapname );
+				}
 				G_InitNew( levelname, false );
 				//G_InitNew( MAPROTATION_GetMapName( 0 ), false );
 			}
 			else
-				G_InitNew( startmap, false );
+			{
+				FILE * pFile;
+				pFile = fopen( GAMENAMELOWERCASE"-server.map","r" );
+				if ( pFile )
+				{
+					char levelname[10];
+					fgets( levelname, 10, pFile );
+					fclose( pFile );
+					G_InitNew( levelname, false );
+				}
+				else
+				{
+					G_InitNew( startmap, false );
+				}
+			}
 		}
 		else
 		{
--- src/d_netinfo.cpp
+++ src/d_netinfo.cpp
@@ -1240,6 +1240,8 @@ CCMD (playerinfo)
 	if (argv.argc() < 2)
 	{
 		int i;
+		int position;
+		std::string strpass2;
 
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
@@ -1260,6 +1262,20 @@ CCMD (playerinfo)
 					// [K6/BB] Show the player's country, if the GeoIP db is available.
 					if ( NETWORK_IsGeoIPAvailable() )
 						infoString.AppendFormat ( "\\ce - FROM %s", NETWORK_GetCountryCodeFromAddress ( SERVER_GetClient( i )->Address ).GetChars() );
+
+					// [Dusk] Also, if we're using a password list, display
+					// the password the client connected with.
+					// [K6] If we use a colon delimiter in passwords (for user:pass), strip the pass part to RCON clients.
+					if (sv_passwordlist || sv_joinpasswordlist)
+					{
+						strpass2 = SERVER_GetClient(i)->szPassword;
+						position  = strpass2.find(":");
+						if (position != (-1))
+						{
+							strpass2.erase(position);
+							infoString.AppendFormat("\\cp - AUTH %s", strpass2.c_str());
+						}
+					}
 				}
 
 				Printf("%s\n", infoString.GetChars());
--- src/g_level.cpp
+++ src/g_level.cpp
@@ -129,6 +129,8 @@ EXTERN_CVAR (Int, disableautosave)
 #define RCLS_ID			MAKE_ID('r','c','L','s')
 #define PCLS_ID			MAKE_ID('p','c','L','s')
 
+std::vector<CVAROVERRIDEENTRY_t>	g_CvarOverrideEntries;
+
 static void SetEndSequence (char *nextmap, int type);
 void G_VerifySkill();
 
@@ -162,6 +164,8 @@ extern int mousex, mousey;
 extern bool sendpause, sendsave, sendturn180, SendLand;
 extern const AInventory *SendItemUse, *SendItemDrop;
 
+extern ULONG g_ulCurMapInList;
+
 void *statcopy;					// for statistics driver
 
 FLevelLocals level;			// info about current level
@@ -302,7 +306,7 @@ CCMD (map)
 	}
 	else
 	{
-		Printf ("Usage: map <map name>\n");
+		Printf( "%s: %s\n", level.mapname, level.LevelName.GetChars() );
 	}
 }
 
@@ -1076,6 +1080,57 @@ void G_DoLoadLevel (int position, bool a
 	CAMPAIGNINFO_s		*pInfo;
 	UCVarValue			Val;
 
+
+	// [K6] Save our position in the map rotation to an external file so we can revive it in case of a crash.
+	// If we don't have a rotation, just use the map name.
+	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
+	{
+		FILE * pFile;
+		pFile = fopen( GAMENAMELOWERCASE"-server.map","w" );
+		if (pFile)
+		{
+			if ( sv_maprotation )
+			{
+				fprintf( pFile, "%lu", g_ulCurMapInList );
+			}
+			else
+			{
+				fprintf( pFile, "%s", level.mapname );
+			}
+			fclose( pFile );
+		}
+
+
+		// [K6] Apply/revert map specific CVAR overrides here.
+		FBaseCVar* var;
+		UCVarValue val;
+		bool bLogRevert = false;
+
+		for ( ULONG ulIdx = 0; ulIdx < g_CvarOverrideEntries.size( ); ulIdx++ )
+			if (g_CvarOverrideEntries[ulIdx].originalValue.IsNotEmpty())
+			{
+				cvar_forceset (g_CvarOverrideEntries[ulIdx].cvarToOverride, g_CvarOverrideEntries[ulIdx].originalValue);
+				g_CvarOverrideEntries[ulIdx].originalValue = NULL;
+				bLogRevert = true;
+			}
+
+		if ( bLogRevert )
+			Printf("Reverting CVAR overrides\n");
+
+		for ( ULONG ulIdx = 0; ulIdx < g_CvarOverrideEntries.size( ); ulIdx++ )
+			if (stricmp(g_CvarOverrideEntries[ulIdx].mapname, level.mapname) == 0)
+			{
+				if ((var = FindCVar (g_CvarOverrideEntries[ulIdx].cvarToOverride, NULL)))
+				{
+					val = var->GetGenericRep (CVAR_String);
+					g_CvarOverrideEntries[ulIdx].originalValue = val.String; // save the current value so we can restore it when the map ends
+					cvar_forceset (g_CvarOverrideEntries[ulIdx].cvarToOverride, g_CvarOverrideEntries[ulIdx].overrideValue);
+					Printf("Applying CVAR override for next map: %s %s\n", g_CvarOverrideEntries[ulIdx].cvarToOverride.GetChars(),
+						g_CvarOverrideEntries[ulIdx].overrideValue.GetChars());
+				}
+			}
+	}
+
 	// [BB] Make sure that dead spectators are respawned before moving to the next map.
 	if ( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_DEADSPECTATORS )
 		GAMEMODE_RespawnDeadSpectatorsAndPopQueue( );
@@ -1472,6 +1527,12 @@ void G_DoLoadLevel (int position, bool a
 	// Make sure the game isn't frozen.
 	level.flags2 &= ~LEVEL2_FROZEN;
 
+	if ( NETWORK_GetState( ) == NETSTATE_SERVER && g_bGameFrozen )
+	{
+		AddCommandString ( "freezeall" );
+		g_bGameFrozen = 0;
+	}
+
 	// [BB] Initialize/clear the free network ID list, but only if there are no level snapshots.
 	// The actors stored in these snapshots still have their NetIDs, so as long as snapshots exist,
 	// we may not clear the NetID list.
@@ -2635,4 +2696,21 @@ CCMD(listmaps)
 
 
 
+// [K6] Map specific CVAR overrides.
+CCMD(add_cvaroverride)
+{
+	if ( argv.argc( ) > 3 )
+	{
+		CVAROVERRIDEENTRY_t newEntry;
 
+		newEntry.mapname = argv[1];
+		newEntry.cvarToOverride = argv[2];
+		for(int i = 3; i < argv.argc(); i++)
+			newEntry.overrideValue.AppendFormat("%s ", argv[i]);
+		V_RemoveTrailingCrapFromFString ( newEntry.overrideValue );
+		g_CvarOverrideEntries.push_back( newEntry );
+		Printf("CVAR override for %s: %s %s\n", newEntry.mapname.GetChars(), newEntry.cvarToOverride.GetChars(), newEntry.overrideValue.GetChars());
+	}
+	else
+		Printf( "Usage: add_cvaroverride <mapname> <cvar> <new value>\n" );
+}
--- src/g_level.h
+++ src/g_level.h
@@ -57,6 +57,15 @@ class FScanner;
 #endif
 
 
+typedef struct
+{
+	FString	mapname;
+	FString	cvarToOverride;
+	FString	overrideValue;
+	FString	originalValue;
+
+} CVAROVERRIDEENTRY_t; // [K6] Yeah it's probably lame to use strings but it was easier and it works...
+
 struct FMapInfoParser
 {
 	enum EFormatType
--- src/g_shared/a_flags.cpp
+++ src/g_shared/a_flags.cpp
@@ -71,6 +71,8 @@
 #define	ALLOW_PICKUP		1
 #define	RETURN_FLAG			2
 
+static bool g_ReturnOnScore = 1;
+
 // Base team item -----------------------------------------------------------
 
 IMPLEMENT_CLASS( ATeamItem )
@@ -429,6 +431,7 @@ bool AFlag::HandlePickup( AInventory *pI
 			// Give his team a point.
 			TEAM_SetScore( Owner->player->ulTeam, TEAM_GetScore( Owner->player->ulTeam ) + 1, true );
 			Owner->player->lPointCount++;
+			g_ReturnOnScore = 0;
 
 			// Award the scorer with a "Capture!" medal.
 			MEDAL_GiveMedal( ULONG( Owner->player - players ), MEDAL_CAPTURE );
@@ -439,6 +442,7 @@ bool AFlag::HandlePickup( AInventory *pI
 
 			// [RC] Clear the 'returned automatically' message. A bit hackish, but leaves the flag structure unchanged.
 			this->ReturnFlag( NULL );
+			g_ReturnOnScore = 1;
 			if ( NETWORK_GetState( ) != NETSTATE_SERVER )
 			{
 				pMsg = new DHUDMessageFadeOut( SmallFont, "", 1.5f, TEAM_MESSAGE_Y_AXIS_SUB, 0, 0, CR_UNTRANSLATED, 3.0f, 0.5f );
@@ -505,7 +509,10 @@ bool AFlag::HandlePickup( AInventory *pI
 			}
 			// If necessary, send it to clients.
 			else
+			{
 				SERVERCOMMANDS_PrintHUDMessageFadeOut( szString, 1.5f, TEAM_MESSAGE_Y_AXIS_SUB, 0, 0, CR_UNTRANSLATED, 3.0f, 0.5f, "SmallFont", false, MAKE_ID( 'S','U','B','S' ));
+				SERVER_Printf( PRINT_HIGH, "%s \\c-scored for the \\c%c%s \\c-team!\n", Owner->player->userinfo.netname, V_GetColorChar( TEAM_GetTextColor( Owner->player->ulTeam )), TEAM_GetName( Owner->player->ulTeam ));
+			}
 
 			
 			// If someone just recently returned the flag, award him with an "Assist!" medal.
@@ -650,7 +657,10 @@ void AFlag::DisplayFlagTaken( AActor *pT
 	}
 	// If necessary, send it to clients.
 	else
+	{
 		SERVERCOMMANDS_PrintHUDMessageFadeOut( szString, 1.5f, TEAM_MESSAGE_Y_AXIS_SUB, 0, 0, CR_UNTRANSLATED, 3.0f, 0.25f, "SmallFont", false, MAKE_ID('S','U','B','S'), ULONG( pToucher->player - players ), SVCF_SKIPTHISCLIENT  );
+		SERVER_Printf( PRINT_MEDIUM, "%s \\c-has taken the \\c%c%s \\c-flag.\n", players[playerIndex].userinfo.netname, V_GetColorChar( TEAM_GetTextColor( TEAM_GetTeamFromItem( this ))), TEAM_GetName( TEAM_GetTeamFromItem( this )));
+	}
 }
 
 //===========================================================================
@@ -738,6 +748,14 @@ void AFlag::ReturnFlag( AActor *pReturne
 
 	V_ColorizeString( szString );
 	GAMEMODE_DisplaySUBSMessage( szString, true );
+
+	if (( NETWORK_GetState( ) == NETSTATE_SERVER ) && (g_ReturnOnScore))
+	{
+		if ( pReturner && pReturner->player )
+			SERVER_Printf( PRINT_MEDIUM, "%s \\c-returned the \\c%c%s \\c-flag.\n", pReturner->player->userinfo.netname, V_GetColorChar( TEAM_GetTextColor( TEAM_GetTeamFromItem( this ))), TEAM_GetName( TEAM_GetTeamFromItem( this )));
+		else
+			SERVER_Printf( PRINT_MEDIUM, "\\c%c%s \\c-flag returned.\n", V_GetColorChar( TEAM_GetTextColor( TEAM_GetTeamFromItem( this ))), TEAM_GetName( TEAM_GetTeamFromItem( this )));
+	}
 }
 
 //===========================================================================
@@ -1242,7 +1260,10 @@ void ASkull::DisplayFlagTaken( AActor *p
 	}
 	// If necessary, send it to clients.
 	else
+	{
 		SERVERCOMMANDS_PrintHUDMessageFadeOut( szString, 1.5f, TEAM_MESSAGE_Y_AXIS_SUB, 0, 0, CR_UNTRANSLATED, 3.0f, 0.25f, "SmallFont", false, MAKE_ID('S','U','B','S'), ULONG( pToucher->player - players ), SVCF_SKIPTHISCLIENT  );
+		SERVER_Printf( PRINT_MEDIUM, "%s \\c-has taken the \\c%c%s \\c-skull.\n", players[playerIndex].userinfo.netname, V_GetColorChar( TEAM_GetTextColor( TEAM_GetTeamFromItem( this ))), TEAM_GetName( TEAM_GetTeamFromItem( this )));
+	}
 }
 
 //===========================================================================
@@ -1330,6 +1351,13 @@ void ASkull::ReturnFlag( AActor *pReturn
 
 	V_ColorizeString( szString );
 	GAMEMODE_DisplaySUBSMessage( szString, true );
+	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
+	{
+		if ( pReturner && pReturner->player )
+			SERVER_Printf( PRINT_MEDIUM, "%s \\c-returned the \\c%c%s \\c-skull.\n", pReturner->player->userinfo.netname, V_GetColorChar( TEAM_GetTextColor( TEAM_GetTeamFromItem( this ))), TEAM_GetName( TEAM_GetTeamFromItem( this )));
+		else
+			SERVER_Printf( PRINT_MEDIUM, "\\c%c%s \\c-skull returned.\n", V_GetColorChar( TEAM_GetTextColor( TEAM_GetTeamFromItem( this ))), TEAM_GetName( TEAM_GetTeamFromItem( this )));
+	}
 }
 
 //===========================================================================
--- src/gamemode.cpp
+++ src/gamemode.cpp
@@ -526,6 +526,13 @@ void GAMEMODE_SpawnPlayer( const ULONG u
 	// Otherwise, just spawn at their normal player start.
 	else
 		G_CooperativeSpawnPlayer( ulPlayer, bClientUpdate );
+
+	// [K6] If the game is paused, freeze the newly respawned player.
+	if ( g_bGameFrozen )
+	{
+		players[ulPlayer].cheats |= CF_TOTALLYFROZEN | CF_GODMODE;
+		SERVERCOMMANDS_SetPlayerCheats( ulPlayer, ulPlayer, SVCF_ONLYTHISCLIENT );
+	}
 }
 
 //*****************************************************************************
--- src/maprotation.cpp
+++ src/maprotation.cpp
@@ -61,7 +61,7 @@
 
 std::vector<MAPROTATIONENTRY_t>	g_MapRotationEntries;
 
-static	ULONG					g_ulCurMapInList;
+/*static*/	ULONG					g_ulCurMapInList;
 static	ULONG					g_ulNextMapInList;
 
 //*****************************************************************************
--- src/network.cpp
+++ src/network.cpp
@@ -874,7 +874,7 @@ bool NETWORK_IsGeoIPAvailable ( void )
 
 //*****************************************************************************
 // [BB] 
-FString NETWORK_GetCountryCodeFromAddress( NETADDRESS_s Address )
+FString NETWORK_GetCountryCodeFromAddress( NETADDRESS_s Address, bool FullName )
 {
 	const char * addressString = NETWORK_AddressToStringIgnorePort( Address );
 	if ( ( strnicmp( "10.", addressString, 3 ) == 0 ) ||
@@ -885,7 +885,8 @@ FString NETWORK_GetCountryCodeFromAddres
 	if ( g_GeoIPDB == NULL )
 		return "";
 
-	FString country = GeoIP_country_code_by_addr ( g_GeoIPDB, NETWORK_AddressToStringIgnorePort( Address ) );
+	FString country = FullName? GeoIP_country_name_by_addr ( g_GeoIPDB, NETWORK_AddressToStringIgnorePort( Address ) ) :
+	 GeoIP_country_code_by_addr ( g_GeoIPDB, NETWORK_AddressToStringIgnorePort( Address ) );
 	return country.IsEmpty() ? "N/A" : country;
 }
 
--- src/network.h
+++ src/network.h
@@ -343,7 +343,7 @@ NETADDRESS_s	NETWORK_GetCachedLocalAddre
 NETBUFFER_s		*NETWORK_GetNetworkMessageBuffer( void );
 ULONG			NETWORK_ntohs( ULONG ul );
 bool			NETWORK_IsGeoIPAvailable( void );
-FString			NETWORK_GetCountryCodeFromAddress( NETADDRESS_s Address );
+FString			NETWORK_GetCountryCodeFromAddress( NETADDRESS_s Address, bool FullName=false );
 USHORT			NETWORK_GetLocalPort( void );
 
 std::list<FString>	*NETWORK_GetPWADList( void ); // [RC]
--- src/networkshared.cpp
+++ src/networkshared.cpp
@@ -49,12 +49,14 @@
 
 #include "networkheaders.h"
 #include "networkshared.h"
+#include "c_cvars.h"
 #include <sstream>
 #include <errno.h>
 #include <iostream>
 #include <algorithm>
 #include "i_system.h"
 
+
 //*****************************************************************************
 //	VARIABLES
 
@@ -669,8 +671,8 @@ bool IPFileParser::parseIPList( const ch
 	}
 
 	fclose( pFile );
-	if ( _numberOfEntries > 0 )
-		Printf( "%s: %d entr%s loaded.\n", FileName, static_cast<unsigned int>(_numberOfEntries), ( _numberOfEntries == 1 ) ? "y" : "ies" );
+//	if ( _numberOfEntries > 0 )
+//		Printf( "%s: %d entr%s loaded.\n", FileName, static_cast<unsigned int>(_numberOfEntries), ( _numberOfEntries == 1 ) ? "y" : "ies" );
 	return true;
 }
 
--- src/sdl/i_main.cpp
+++ src/sdl/i_main.cpp
@@ -73,6 +73,10 @@ extern "C" int cc_install_handlers(int, 
 
 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
 
+#ifdef unix
+void handle_sighup(int signum);
+#endif
+
 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
 
 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
@@ -204,6 +208,9 @@ static int DoomSpecificInfo (char *buffe
 
 int main (int argc, char **argv)
 {
+#ifdef unix
+signal(SIGHUP, handle_sighup);
+#endif
 	{
 		int s[4] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS };
 		cc_install_handlers(argc, argv, 4, s, GAMENAMELOWERCASE"-crash.log", DoomSpecificInfo);
--- src/sv_ban.cpp
+++ src/sv_ban.cpp
@@ -47,6 +47,9 @@
 //
 //-----------------------------------------------------------------------------
 
+#ifdef unix
+#include <signal.h>
+#endif
 #include <stdio.h>
 #include <errno.h>
 
@@ -428,6 +431,11 @@ static void serverban_LoadBansAndBanExem
 
 	// Kick any players using a banned address.
 	serverban_KickBannedPlayers( );
+
+	// [Dusk] Take the opportunity to load the password list too.
+	PASSLIST_Construct ();
+	PASSLIST_EnforcePasswords ();
+
 }
 
 //*****************************************************************************
@@ -712,3 +720,14 @@ CCMD( reloadbans )
 {
 	serverban_LoadBansAndBanExemptions( );
 }
+
+#ifdef unix
+//*****************************************************************************
+//
+void handle_sighup(int signum)
+{
+	// in case we registered this handler for multiple signals
+	if (signum == SIGHUP)
+		serverban_LoadBansAndBanExemptions( );
+}
+#endif
--- src/sv_commands.cpp
+++ src/sv_commands.cpp
@@ -891,7 +891,13 @@ void SERVERCOMMANDS_SetPlayerUserInfo( U
 			NETWORK_WriteByte( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, players[ulPlayer].userinfo.lRailgunTrailColor );
 
 		if ( ulUserInfoFlags & USERINFO_SKIN )
-			NETWORK_WriteString( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, SERVER_GetClient( ulPlayer )->szSkin );
+		{
+			//[K6] Don't bother with skins on ZH - fixes a skin exploit.
+			if (strstr(sv_hostname, "Zombie HORDE"))
+				NETWORK_WriteString( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, "0" );
+			else
+				NETWORK_WriteString( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, SERVER_GetClient( ulPlayer )->szSkin );
+		}
 
 		if ( ulUserInfoFlags & USERINFO_HANDICAP )
 			NETWORK_WriteByte( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, players[ulPlayer].userinfo.lHandicap );
--- src/sv_main.cpp
+++ src/sv_main.cpp
@@ -114,6 +114,9 @@
 #include "a_movingcamera.h"
 #include "cl_main.h"
 
+// [Dusk] we're reading files here
+#include <errno.h>
+
 //*****************************************************************************
 //	MISC CRAP THAT SHOULDN'T BE HERE BUT HAS TO BE BECAUSE OF SLOPPY CODING
 
@@ -124,6 +127,8 @@ void SERVERCONSOLE_UpdatePlayerInfo( LON
 void SERVERCONSOLE_ReListPlayers( void );
 
 EXTERN_CVAR( Bool, sv_cheats );
+EXTERN_CVAR (Float, sv_gravity);
+EXTERN_CVAR( Float, sv_aircontrol );
 
 //*****************************************************************************
 //	PROTOTYPES
@@ -158,6 +163,8 @@ static	bool	server_Puke( BYTESTREAM_s *p
 static	bool	server_MorphCheat( BYTESTREAM_s *pByteStream );
 static	bool	server_CheckForClientMinorCommandFlood( ULONG ulClient );
 
+static	ULONG	CalcNumZombies( void );
+
 // [RC]
 #ifdef CREATE_PACKET_LOG
 static  void	server_LogPacket( BYTESTREAM_s *pByteStream, NETADDRESS_s Address, const char *pszReason );
@@ -226,6 +233,12 @@ static	TArray<EDITEDTRANSLATION_s>		g_Ed
 // [BB] List of all sector links created by calls to Sector_SetLink.
 static	TArray<SECTORLINK_s>		g_SectorLinkList;
 
+// [Dusk] List of passwords
+std::vector<FString>	g_PasswordList;
+
+// [K6]
+bool g_bGameFrozen = 0;
+
 // [RC] File to log packets to.
 #ifdef CREATE_PACKET_LOG
 static	FILE		*PacketLogFile = NULL;
@@ -263,6 +276,9 @@ CVAR( Bool, sv_pure, true, CVAR_SERVERIN
 CVAR( Int, sv_maxclientsperip, 2, CVAR_ARCHIVE )
 CVAR( Int, sv_afk2spec, 0, CVAR_ARCHIVE ) // [K6]
 
+CVAR( Bool, sv_passwordlist, false, CVAR_ARCHIVE ) // [Dusk]
+CVAR( Bool, sv_joinpasswordlist, false, CVAR_ARCHIVE ) // [Dusk]
+
 CUSTOM_CVAR( String, sv_adminlistfile, "adminlist.txt", CVAR_ARCHIVE|CVAR_NOSETBYACS )
 {
 	if ( NETWORK_GetState( ) != NETSTATE_SERVER )
@@ -351,6 +367,19 @@ CUSTOM_CVAR( Int, sv_maxpacketsize, 1024
 }
 
 //*****************************************************************************
+// [Dusk]
+CUSTOM_CVAR (String, sv_passwordlistfile, "passwordlist.txt", CVAR_ARCHIVE)
+{
+	if (NETWORK_GetState () != NETSTATE_SERVER)
+		return;
+
+	PASSLIST_Construct ();
+
+}
+
+
+//*****************************************************************************
+
 //	FUNCTIONS
 
 void SERVER_Construct( void )
@@ -1077,7 +1106,10 @@ void SERVER_SendChatMessage( ULONG ulPla
 	bool sv_logfiletimestampOldValue = sv_logfiletimestamp;
 	if( sv_markchatlines )
 	{
-		Printf( "CHAT " );
+		if ( ulMode == CHATMODE_TEAM )
+			Printf( "TEAMCHAT " );
+		else
+			Printf( "CHAT " );
 		sv_logfiletimestamp = false;
 	}
 	// Print this message in the server's local window.
@@ -1095,6 +1127,23 @@ void SERVER_SendChatMessage( ULONG ulPla
 			Printf( "<server>: %s\n", pszString );
 		else
 			Printf( "%s: %s\n", players[ulPlayer].userinfo.netname, pszString );
+
+		// [K6] On duel, either of players may pause the game by saying "hold" and resume by saying "go"
+		if (( duel ) && ( strcmp( "hold", pszString ) == 0 ) && (playeringame[ulPlayer]) && ( !PLAYER_IsTrueSpectator( &players[ulPlayer] )) && (!g_bGameFrozen))
+		{
+			AddCommandString ( "freezeall" );
+			SERVER_Printf( PRINT_HIGH, "Game paused. Say \"go\" to resume.\n" );
+		}
+		if (( duel ) && ( strcmp( "go", pszString ) == 0 ) && (playeringame[ulPlayer]) && ( !PLAYER_IsTrueSpectator( &players[ulPlayer] )) && (g_bGameFrozen))
+		{
+			char commandString[32];
+			sprintf ( commandString, "wait %d;freezeall", 5 * TICRATE );
+			AddCommandString ( commandString );
+			SERVER_Printf( PRINT_HIGH, "Resuming in 5..4..3..\n" );
+		}
+
+
+
 	}
 	if( sv_markchatlines && sv_logfiletimestampOldValue )
 		sv_logfiletimestamp = true;
@@ -1270,6 +1319,56 @@ void SERVER_ConnectNewPlayer( BYTESTREAM
 	// Send welcome message.
 	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Version %s Server\n", DOTVERSIONSTR );
 
+	// [K6] Tell the player some info about our server
+	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "\\cnServer running kpatch build %s\n", KPATCH_BUILD );
+	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Consoleplayer is: %d\n", g_lCurrentClient );
+	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "SERVER: dmflags=%lu dmflags2=%lu dmflags3=%lu compatflags=%lu compatflags2=%lu\n", (LONG)dmflags, (LONG)dmflags2, (LONG)dmflags3, (LONG)compatflags, (LONG)compatflags2 );
+	if ( instagib )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Instagib mode\n" );
+	if ( buckshot )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Buckshot mode\n" );
+	if ( ( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_PLAYERSONTEAMS ) || cooperative )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Team damage factor: %lu%%\n", ((LONG)teamdamage.GetGenericRep( CVAR_Float ).Float * 100) );
+	if ( fraglimit )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Frag limit: %d\n", fraglimit.GetGenericRep( CVAR_Int ) );
+	if ( pointlimit )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Point limit: %d\n", pointlimit.GetGenericRep( CVAR_Int ) );
+	if ( winlimit )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Win limit: %d\n", winlimit.GetGenericRep( CVAR_Int ) );
+	if ( duellimit )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Duel limit: %d\n", duellimit.GetGenericRep( CVAR_Int ) );
+	if ( timelimit )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Time limit: %d\n", timelimit.GetGenericRep( CVAR_Int ) );
+	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Gravity: %lu%%\n", ((LONG)sv_gravity.GetGenericRep( CVAR_Float ).Float / 8) );
+	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Air control: %s\n", sv_aircontrol.GetGenericRep( CVAR_String ) );
+	if ( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_PLAYERSONTEAMS )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "%d teams\n", sv_maxteams.GetGenericRep( CVAR_Int ) );
+	FString FSVoting;
+	if ( sv_nocallvote.GetGenericRep( CVAR_Int ).Int == 1 )
+		FSVoting.AppendFormat ( "" );
+	else
+	{
+		if ( !sv_nomapvote )
+			FSVoting.AppendFormat ( " map" );
+		if ( !sv_nochangemapvote )
+			FSVoting.AppendFormat ( " changemap" );
+		if ( !sv_nokickvote )
+			FSVoting.AppendFormat ( " kick" );
+		if ( !sv_noduellimitvote )
+			FSVoting.AppendFormat ( " duellimit" );
+		if ( !sv_nofraglimitvote )
+			FSVoting.AppendFormat ( " fraglimit" );
+		if ( !sv_nopointlimitvote )
+			FSVoting.AppendFormat ( " pointlimit" );
+		if ( !sv_notimelimitvote )
+			FSVoting.AppendFormat ( " timelimit" );
+		if ( !sv_nowinlimitvote )
+			FSVoting.AppendFormat ( " winlimit" );
+	}
+	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Voting:%s\n", FSVoting.IsEmpty() ? " off" : FSVoting.GetChars() );
+	if ( sv_coop_damagefactor != 1.0f )
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Coop damage factor: %.2f\n", static_cast<float> (sv_coop_damagefactor) );
+
 	// Send consoleplayer number.
 	SERVERCOMMANDS_SetConsolePlayer( g_lCurrentClient );
 
@@ -1511,14 +1610,36 @@ void SERVER_ConnectNewPlayer( BYTESTREAM
 	{
 		// [K6/BB] Show the player's country on connect, if the GeoIP db is available.
 		FString countryInfo;
-		// [BB] All players see the connect message, so only show the country code if the player doesn't want it to be hidden.
-		if ( NETWORK_IsGeoIPAvailable() && ( SERVER_GetClient( g_lCurrentClient )->bWantHideCountry == false ) )
-			countryInfo.AppendFormat ( " (from: %s)", NETWORK_GetCountryCodeFromAddress ( SERVER_GetClient( g_lCurrentClient )->Address ).GetChars() );
+		std::string strpass;
+               int position;
 
-		if ( players[g_lCurrentClient].bSpectating )
-			SERVER_Printf( PRINT_HIGH, "%s \\c-has connected.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() );
+               strpass = g_aClients[g_lCurrentClient].szPassword;
+               position  = strpass.find(":");
+               if (sv_passwordlist && position != (-1))
+               {
+                       strpass.erase(position);
+
+                       // [BB] All players see the connect message, so only show the country code if the player doesn't want it to be hidden.
+                       //if ( NETWORK_IsGeoIPAvailable() && ( SERVER_GetClient( g_lCurrentClient )->bWantHideCountry == false ) )
+                               countryInfo.AppendFormat ( " (from: %s, auth: %s)", NETWORK_GetCountryCodeFromAddress ( SERVER_GetClient( g_lCurrentClient )->Address, true ).GetChars() );
+
+                       if ( players[g_lCurrentClient].bSpectating )
+                               SERVER_Printf( PRINT_HIGH, "%s \\c-has connected.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() );
+                       else
+                               SERVER_Printf( PRINT_HIGH, "%s \\c-entered the game.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() );
+               }
 		else
-			SERVER_Printf( PRINT_HIGH, "%s \\c-entered the game.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() );
+		{
+                       // [BB] All players see the connect message, so only show the country code if the player doesn't want it to be hidden.
+                       //if ( NETWORK_IsGeoIPAvailable() && ( SERVER_GetClient( g_lCurrentClient )->bWantHideCountry == false ) )
+                               countryInfo.AppendFormat ( " (from: %s)", NETWORK_GetCountryCodeFromAddress ( SERVER_GetClient( g_lCurrentClient )->Address, true ).GetChars() );
+
+                       if ( players[g_lCurrentClient].bSpectating )
+                               SERVER_Printf( PRINT_HIGH, "%s \\c-has connected.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() );
+                       else
+                               SERVER_Printf( PRINT_HIGH, "%s \\c-entered the game.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() );
+               }
+
 	}
 
 	BOTCMD_SetLastJoinedPlayer( players[g_lCurrentClient].userinfo.netname );
@@ -1571,6 +1692,13 @@ void SERVER_DetermineConnectionType( BYT
 	if ( lCommand == -1 )
 		return;
 
+	if ( lCommand == MSC_IPISBANNED && NETWORK_CompareAddress( NETWORK_GetFromAddress( ), SERVER_MASTER_GetMasterAddress( ), false ) &&
+		SERVER_STATISTIC_GetTotalSecondsElapsed( ) < 20 )
+	{
+		Printf( "\n*** ERROR: You are banned from hosting on the master server!\n" );
+		return;
+	}
+
 	// If it's not a launcher querying the server, it must be a client.
 	if ( lCommand != CLCC_ATTEMPTCONNECTION )
 	{
@@ -1693,7 +1821,7 @@ void SERVER_DetermineConnectionType( BYT
 				NETWORK_WriteByte( &TempBuffer.ByteStream, 254 /* = NETWORK_ERROR in 97d-beta4.3 */ );
 				NETWORK_WriteByte( &TempBuffer.ByteStream, 1 /* = NETWORK_ERRORCODE_WRONGVERSION in 97d-beta4.3 */ );
 				FString versionStringMessage;
-				versionStringMessage.Format ( "%s\nYou are way out of the loop :)", DOTVERSIONSTR );
+				versionStringMessage.Format ( "Zandronum %s\n*** Skulltag has been updated to Zandronum! You are way out of the loop :)\n*** Visit http://www.zandronum.com/ for updates.", DOTVERSIONSTR );
 				NETWORK_WriteString( &TempBuffer.ByteStream, versionStringMessage.GetChars() );
 
 				NETWORK_LaunchPacket( &TempBuffer, NETWORK_GetFromAddress( ) );
@@ -1729,6 +1857,7 @@ void SERVER_SetupNewConnection( BYTESTRE
 	LONG			lClient;
 	FString			clientVersion;
 	FString			clientPassword;
+	FString			tempPassword;
 	char			szServerPassword[MAX_NETWORK_STRING];
 	LONG			lClientNetworkGameVersion;
 	char			szAddress[4][4];
@@ -1844,7 +1973,7 @@ void SERVER_SetupNewConnection( BYTESTRE
 		// Make sure the version matches.
 		if ( stricmp( clientVersion.GetChars(), DOTVERSIONSTR ) != 0 )
 		{
-			SERVER_ClientError( lClient, NETWORK_ERRORCODE_WRONGVERSION );
+			SERVER_ClientError( lClient, NETWORK_ERRORCODE_WRONGVERSION, clientVersion );
 #ifdef CREATE_PACKET_LOG
 			server_LogPacket(pByteStream,  NETWORK_GetFromAddress( ), "Wrong version.");
 #endif
@@ -1869,6 +1998,19 @@ void SERVER_SetupNewConnection( BYTESTRE
 	if ( bNewPlayer )
 		g_aClients[lClient].ulLastCommandTic = g_aClients[lClient].ulLastGameTic = gametic;
 
+	// Store the password.
+	tempPassword = clientPassword;
+	if (sv_passwordlist) {
+		tempPassword.ToLower();
+		// [Dusk] The string must be truncated first to prevent
+		// buffer overflows.
+		// [K6] A bit more
+		if (tempPassword.Len () > 48)
+			tempPassword.Truncate (48);
+
+		strcpy (g_aClients[lClient].szPassword, tempPassword.GetChars());
+	}
+
 	// Check if we require a password to join this server.
 	Val = sv_password.GetGenericRep( CVAR_String );
 	if (( sv_forcepassword ) && ( strlen( Val.String )))
@@ -1883,6 +2025,16 @@ void SERVER_SetupNewConnection( BYTESTRE
 			SERVER_ClientError( lClient, NETWORK_ERRORCODE_WRONGPASSWORD );
 			return;
 		}
+	} else if (sv_passwordlist) {
+		// [Dusk] We use a password list, so check if the password is listed.
+		if (!PASSLIST_CheckPassword (clientPassword)) {
+			// not listed!
+			SERVER_ClientError (lClient, NETWORK_ERRORCODE_WRONGPASSWORD);
+			return;
+		}
+
+
+		Printf ("%s uses password %s\n", NETWORK_AddressToString( NETWORK_GetFromAddress( )), tempPassword.GetChars());
 	}
 
 	// Check if this IP has been banned.
@@ -1917,7 +2069,11 @@ void SERVER_SetupNewConnection( BYTESTRE
 	players[lClient].ulTeam = teams.Size( );
 	players[lClient].bOnTeam = false;
 
-	g_aClients[lClient].bRCONAccess = false;
+	if ( SERVER_GetAdminList()->isIPInList( SERVER_GetClient( lClient )->Address ) )
+		g_aClients[lClient].bRCONAccess = true;
+	else
+		g_aClients[lClient].bRCONAccess = false;
+
 	g_aClients[lClient].ulDisplayPlayer = lClient;
 	g_aClients[lClient].bFullUpdateIncomplete = false;
 	g_aClients[lClient].commandInstances.clear();
@@ -2049,7 +2205,10 @@ bool SERVER_GetUserInfo( BYTESTREAM_s *p
 
 			if ( stricmp( szPlayerNameNoColor, szOldPlayerNameNoColor ) != 0 )
 			{
-				SERVER_Printf( PRINT_HIGH, "%s \\c-is now known as %s\n", szOldPlayerName, pPlayer->userinfo.netname );
+				// [K6] Include the player's IP in server console.
+				for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+					SERVER_PrintfPlayer( PRINT_HIGH, ulIdx, "%s \\c-is now known as %s\n", szOldPlayerName, pPlayer->userinfo.netname );
+				Printf( "%s \\c-(%s) is now known as %s\n", szOldPlayerName, NETWORK_AddressToString( SERVER_GetClient( g_lCurrentClient )->Address ), pPlayer->userinfo.netname );
 
 				// [RC] Update clients using the RCON utility.
 				SERVER_RCON_UpdateInfo( SVRCU_PLAYERDATA );
@@ -2173,7 +2332,7 @@ void SERVER_ConnectionError( NETADDRESS_
 
 //*****************************************************************************
 //
-void SERVER_ClientError( ULONG ulClient, ULONG ulErrorCode )
+void SERVER_ClientError( ULONG ulClient, ULONG ulErrorCode, FString clientVersion )
 {
 	NETWORK_WriteByte( &g_aClients[ulClient].PacketBuffer.ByteStream, SVCC_ERROR );
 	NETWORK_WriteByte( &g_aClients[ulClient].PacketBuffer.ByteStream, ulErrorCode );
@@ -2190,11 +2349,19 @@ void SERVER_ClientError( ULONG ulClient,
 		Printf( "Incorrect version.\n" );
 
 		// Tell the client what version this server using.
-		NETWORK_WriteString( &g_aClients[ulClient].PacketBuffer.ByteStream, DOTVERSIONSTR );
+		// [K6] Add link to zandronum.com explicitly here. It will be shown to 98d or older clients (though only if the server doesn't load ST compatibility wads)
+		// Normally this is handled on the client but old ST clients instruct the user to go to st.com... Better this than nothing.
+		char verString[256];
+		if (strncmp(clientVersion, "0", 1) == 0)
+			// We have an old Skulltag client connecting!
+			sprintf(verString, "\\cnZandronum %s\\n\\c-Your version is: \\cnSkulltag %s\\n\\cg*** Skulltag has been updated to Zandronum! You are way out of the loop :)\\n\\cg*** Visit http://www.zandronum.com/ for updates.\\c-", DOTVERSIONSTR, clientVersion.GetChars());
+		else
+			sprintf(verString, "\\cn%s\\n\\c-Your version is: \\cn%s\\c-", DOTVERSIONSTR, clientVersion.GetChars());
+		NETWORK_WriteString( &g_aClients[ulClient].PacketBuffer.ByteStream, verString );
 		break;
 	case NETWORK_ERRORCODE_BANNED:
 
-		Printf( "Client banned.\n" );
+		Printf( "Client banned (%s)\n", SERVERBAN_GetBanList( )->getEntryComment( g_aClients[ulClient].Address ) );
 
 		// Tell the client why he was banned, and when his ban expires.
 		NETWORK_WriteString( &g_aClients[ulClient].PacketBuffer.ByteStream, SERVERBAN_GetBanList( )->getEntryComment( g_aClients[ulClient].Address ));
@@ -2689,7 +2856,7 @@ void SERVER_WriteCommands( void )
 			for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ++ulIdx )
 			{
 				// [BB] Don't kick dead spectators for inactivity.
-				if ( ( SERVER_IsValidClient( ulIdx ) == false ) || ( players[ulIdx].bSpectating ) )
+				if ( ( SERVER_IsValidClient( ulIdx ) == false ) || PLAYER_IsTrueSpectator( &players[ulIdx] ) )
 					continue;
 
 				const int afkKickTick = ( g_aClients[ulIdx].lLastActionTic + ( sv_afk2spec * MINUTE * TICRATE ));
@@ -2712,7 +2879,15 @@ void SERVER_WriteCommands( void )
 
 	// Once every minute, update the level time.
 	if (( gametic % ( 60 * TICRATE )) == 0 )
+	{
 		SERVERCOMMANDS_SetMapTime( );
+
+		if (SERVER_STATISTIC_GetTotalSecondsElapsed() > 2073600 && SERVER_CountPlayers(false) == 0)
+		{
+			Printf("----------Scheduled emergency shutdown after 24 days----------\n");
+			exit(-1);
+		}
+	}
 }
 
 //*****************************************************************************
@@ -2821,6 +2996,9 @@ void SERVER_DisconnectClient( ULONG ulCl
 	g_aClients[ulClient].ulLastGameTic = 0;
 	playeringame[ulClient] = false;
 
+	// [K6] Clear the player's password.
+	g_aClients[ulClient].szPassword[0] = '\0';
+
 	// Run the disconnect scripts now that the player is leaving.
 	if (( players[ulClient].bSpectating == false ) ||
 		( players[ulClient].bDeadSpectator ))
@@ -2876,6 +3054,12 @@ void SERVER_DisconnectClient( ULONG ulCl
 		if ( domination )
 			DOMINATION_Reset();
 	}
+
+	if ( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS ) < 2 && g_bGameFrozen )
+	{
+		AddCommandString ( "freezeall" );
+		g_bGameFrozen = 0;
+	}
 }
 
 //*****************************************************************************
@@ -4048,6 +4232,41 @@ bool SERVER_ProcessCommand( LONG lComman
 		break;
 	case CLC_QUIT:
 
+		// [K6] Handle ZH ragequitting.
+		if (strstr(sv_hostname, "Zombie HORDE"))
+		{
+			if (players[g_lCurrentClient].mo == NULL)
+				Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname);
+			else if ((static_cast<signed> (SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) > 2)
+			&& (players[g_lCurrentClient].mo->MaxHealth > 2000) && (CalcNumZombies() == 1) && (level.time < (3 * MINUTE * TICRATE)))
+			{
+				SERVER_Printf( PRINT_HIGH, "%s \\c-ragequits as the first zombie! Temporarily banning %s and restarting the map.\n", players[g_lCurrentClient].userinfo.netname, players[g_lCurrentClient].userinfo.gender == GENDER_MALE ? "him" : players[g_lCurrentClient].userinfo.gender == GENDER_FEMALE ? "her" : "it" );
+				SERVERBAN_BanPlayer( g_lCurrentClient, "10min", "10 minutes ban for ragequitting as the first zombie." );
+				for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+				{
+					if ( playeringame[ulIdx] == false )
+						continue;
+					players[ulIdx].health = 100;
+				}
+				char commandString[32];
+				sprintf ( commandString, "wait %d;changemap %s", 3 * TICRATE, level.mapname );
+				AddCommandString ( commandString );
+				return (true);
+			}
+		}
+
+		// [K6] Handle WDI ragequitting.
+		else if (strstr(sv_hostname, "Who Dun It"))
+		{
+			if (players[g_lCurrentClient].mo == NULL)
+				Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname);
+			else if (players[g_lCurrentClient].mo->FindInventory( PClass::FindClass( "MurderMarker" )))
+			{
+				GAMEMODE_RespawnDeadSpectatorsAndPopQueue( );
+				Printf("Respawning players\n");
+			}
+		}
+
 		// Client has left the game.
 		SERVER_DisconnectClient( g_lCurrentClient, true, true );
 		break;
@@ -4132,6 +4351,41 @@ bool SERVER_ProcessCommand( LONG lComman
 		return ( server_Taunt( pByteStream ));
 	case CLC_SPECTATE:
 
+		// [K6] Handle ZH ragequitting.
+		if (strstr(sv_hostname, "Zombie HORDE"))
+		{
+			if (players[g_lCurrentClient].mo == NULL)
+				Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname);
+			else if ((static_cast<signed> (SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) > 2)
+			&& (players[g_lCurrentClient].mo->MaxHealth > 2000) && (CalcNumZombies() == 1) && (level.time < (3 * MINUTE * TICRATE)))
+			{
+				SERVER_Printf( PRINT_HIGH, "%s \\c-ragespects as the first zombie! Temporarily banning %s and restarting the map.\n", players[g_lCurrentClient].userinfo.netname, players[g_lCurrentClient].userinfo.gender == GENDER_MALE ? "him" : players[g_lCurrentClient].userinfo.gender == GENDER_FEMALE ? "her" : "it" );
+				SERVERBAN_BanPlayer( g_lCurrentClient, "10min", "10 minutes ban for ragespecting as the first zombie." );
+				for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+				{
+					if ( playeringame[ulIdx] == false )
+						continue;
+					players[ulIdx].health = 100;
+				}
+				char commandString[32];
+				sprintf ( commandString, "wait %d;changemap %s", 3 * TICRATE, level.mapname );
+				AddCommandString ( commandString );
+				return ( true );
+			}
+		}
+
+		// [K6] Handle WDI ragequitting.
+		else if (strstr(sv_hostname, "Who Dun It"))
+		{
+			if (players[g_lCurrentClient].mo == NULL)
+				Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname);
+			else if (players[g_lCurrentClient].mo->FindInventory( PClass::FindClass( "MurderMarker" )))
+			{
+				GAMEMODE_RespawnDeadSpectatorsAndPopQueue( );
+				Printf("Respawning players\n");
+			}
+		}
+
 		// Client now wishes to spectate.
 		return ( server_Spectate( pByteStream ));
 	case CLC_REQUESTJOIN:
@@ -5013,6 +5267,23 @@ static bool server_RequestJoin( BYTESTRE
 			SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Incorrect join password.\n" );
 			return ( false );
 		}
+
+	} else if (sv_joinpasswordlist) {
+		// [Dusk] We're using a password list for joining.
+		if (!PASSLIST_CheckPassword (clientJoinPassword)) {
+			// not listed!
+			SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Bad join password, not listed.\n" );
+			SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "This server is using a password list (account) system. Contact administrators to request one.\n" );
+			return (false);
+		}
+
+		Printf ("%s \\c-uses password %s to join the game.\n",
+			players[g_lCurrentClient].userinfo.netname, clientJoinPassword.GetChars());
+
+		// Store the password.
+		FString tempPassword = clientJoinPassword;
+		tempPassword.ToLower();
+		strcpy (g_aClients[g_lCurrentClient].szPassword, tempPassword.GetChars());
 	}
 
 	// If there aren't currently any slots available, just put the person in line.
@@ -5070,6 +5341,13 @@ static bool server_RequestRCON( BYTESTRE
 	// If the user password matches our PW, and we have a PW set, give him RCON access.
 	pszUserPassword = NETWORK_ReadString( pByteStream );
 
+	// kgsws - already admin check
+	if(g_aClients[g_lCurrentClient].bRCONAccess)
+	{
+		SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "You already have RCON access.\n" );
+		return ( false );
+	}
+
 	// [BB] If the client is flooding the server with commands, the client is
 	// kicked and we don't need to handle the command.
 	if ( server_CheckForClientCommandFlood ( g_lCurrentClient ) == true )
@@ -5976,6 +6254,22 @@ static bool server_MorphCheat( BYTESTREA
 	return ( false );
 }
 
+
+//*****************************************************************************
+//
+static ULONG CalcNumZombies( void )
+{
+	ULONG	ulNumZombies = 0;
+
+	for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+	{
+		if ( playeringame[ulIdx] && (!PLAYER_IsTrueSpectator( &players[ulIdx] )) && players[ulIdx].mo->FindInventory( PClass::FindClass( "zombiemaker" )))
+			ulNumZombies++;
+	}
+
+	return ulNumZombies;
+}
+
 //*****************************************************************************
 //	CONSOLE COMMANDS
 
@@ -6343,3 +6637,218 @@ static void	server_LogPacket( BYTESTREAM
 	fflush (PacketLogFile);
 }
 #endif
+
+//*****************************************************************************
+// [Dusk] Constructs the password list.
+void PASSLIST_Construct () {
+	const char *szFileName = sv_passwordlistfile.GetGenericRep (CVAR_String).String;
+	FILE *fp = fopen (szFileName, "r");
+
+	// crap.
+	if (fp == NULL) {
+		Printf ("%s\n", GenerateCouldNotOpenFileErrorString ("PASSLIST_Construct", szFileName, errno).c_str());
+		return;
+	}
+
+	g_PasswordList.clear ();
+
+	FString password;
+	int len;
+	int lineNumber = 1;
+	int passwordCount = 0;
+	bool bSlashed = false;
+	bool bCommenting = false;
+	char c;
+
+	while (1) {
+		c = fgetc (fp);
+		if (feof (fp)) break;
+
+		if (c == '/') {
+			if (bSlashed) {
+				// Double slash. Ignore text until newline
+				bCommenting = true;
+				bSlashed = false;
+			} else {
+				bSlashed = true;
+			}
+			continue;
+		}
+
+		if (c == '\n') {
+			// Newline!
+			if (bCommenting)
+				bCommenting = false;
+			if (bSlashed)
+				bSlashed = false;
+
+			len = strlen(password.GetChars());
+			if (len > 0) {
+				password.ToUpper(); // Passwords are stored in uppercase
+				g_PasswordList.push_back (password);
+				passwordCount++;
+			}
+
+			password = "";
+			lineNumber++;
+			continue;
+		}
+
+		if (c == '\r' || c == -1 || c == ' ' || bCommenting) {
+			continue;
+		}
+
+		if (bSlashed) {
+			// We have one slash recorded and there is no second one.
+			// Add the ignored slash first now that it's known to be
+			// legit.
+
+			password += "/";
+			bSlashed = false;
+		}
+
+		password += c;
+	}
+	fclose( fp );
+}
+
+bool PASSLIST_CheckPassword (FString pw) {
+	// We're assuming passwords are in uppercase. They are *now*, but...
+	pw.ToUpper();
+
+	std::vector<FString>::iterator i;
+	FString entry;
+	for (i = g_PasswordList.begin (); i < g_PasswordList.end (); i++) {
+		entry = *i;
+		if (!strcmp (pw.GetChars(), entry.GetChars())) return true;
+	}
+
+	return false;
+}
+
+// Goes through all the clients. Anyone who doesn't have a
+// proper password that is in the list is booted out.
+void PASSLIST_EnforcePasswords () {
+	// If we do not use the list, do not enforce it either.
+	if (!sv_passwordlist && !sv_joinpasswordlist) return;
+
+	for (int i = 0; i < MAXPLAYERS; i++) {
+		if (!playeringame[i]) continue;
+		if (!PASSLIST_CheckPassword (FString (g_aClients[i].szPassword))) {
+			if (sv_passwordlist) {
+				SERVER_KickPlayer (i, "Password not in list");
+			} else {
+				SERVER_KickPlayerFromGame (i, "Password not in list");
+				strcpy (g_aClients[i].szPassword, "");
+			}
+		}
+	}
+}
+
+// List all passwords in the list
+// [K6] If we use a colon delimiter in passwords (for user:pass), strip the pass part to RCON clients.
+CCMD (viewpasswordlist) {
+	std::vector<FString>::iterator i;
+	FString entry;
+	std::string strpass;
+	int idx = 0;
+	int position = 0;
+	for (i = g_PasswordList.begin (); i < g_PasswordList.end (); i++) {
+		strpass = *i;
+		position = strpass.find(":");
+		if (position != (-1))
+		{
+			strpass.erase(position);
+			Printf ("%d. %s\n", idx, strpass.c_str());
+		}
+
+	idx++;
+	}
+	Printf ("End of password auth list.\n");
+}
+
+// [K6]
+CCMD( kickall )
+{
+        // Only the server can boot players!
+        if ( NETWORK_GetState( ) != NETSTATE_SERVER )
+                return;
+
+        if ( argv.argc( ) > 1 )
+        SERVER_KickAllPlayers( argv[1] );
+
+        else
+        SERVER_KickAllPlayers( "" );
+}
+
+// kgsws
+CCMD (freeze_idx) {
+	int i;
+	if ( NETWORK_GetState( ) != NETSTATE_SERVER )
+	{
+		Printf("This is only for the server.\n");
+		return;
+	}
+	if(argv.argc() <= 1) {
+		Printf("usage: freeze_idx <player index>\n");
+		return;
+	}
+	i = atoi(argv[1]);
+	if(PLAYER_IsValidPlayer( i ) == false) return;
+	players[i].cheats ^= CF_TOTALLYFROZEN;
+	if(players[i].cheats & CF_TOTALLYFROZEN)
+		Printf("%s is frozen\n", players[i].userinfo.netname);
+	else
+		Printf("%s can move\n", players[i].userinfo.netname);
+	SERVERCOMMANDS_SetPlayerCheats( i, i, SVCF_ONLYTHISCLIENT );
+}
+
+// [K6] To be used for pausing the game
+CCMD (freezeall) {
+	if ( NETWORK_GetState( ) != NETSTATE_SERVER )
+	{
+		Printf("This is only for the server.\n");
+		return;
+	}
+	if ( !g_bGameFrozen )
+	{
+		for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+		{
+			if (playeringame[ulIdx] == false)
+				continue;
+			players[ulIdx].cheats |= CF_TOTALLYFROZEN | CF_GODMODE;
+			SERVERCOMMANDS_SetPlayerCheats( ulIdx, ulIdx, SVCF_ONLYTHISCLIENT );
+		}
+		g_bGameFrozen = 1;
+	}
+	else
+	{
+		for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+		{
+			if (playeringame[ulIdx] == false)
+				continue;
+			players[ulIdx].cheats &= ~CF_TOTALLYFROZEN & ~CF_GODMODE;
+			SERVERCOMMANDS_SetPlayerCheats( ulIdx, ulIdx, SVCF_ONLYTHISCLIENT );
+		}
+		g_bGameFrozen = 0;
+	}
+}
+
+// kgsws
+CCMD (say_bot) {
+	FString FullMessage;
+	int i;
+	if ( NETWORK_GetState( ) != NETSTATE_SERVER )
+	{
+		Printf("This is only for the server.\n");
+		return;
+	}
+	if(argv.argc() <= 1) {
+		Printf("usage: say_bot <text>\n");
+		return;
+	}
+	for(i = 1; i < argv.argc(); i++)
+		FullMessage.AppendFormat("%s ", argv[i]);
+	SERVER_Printf(PRINT_CHAT, "\\ci%s\n", FullMessage.GetChars()); // send message
+	SERVERCOMMANDS_Sound( CHAN_AUTO, "misc/chat2", 1.f, ATTN_NONE ); // play sound
+}
--- src/sv_main.h
+++ src/sv_main.h
@@ -287,6 +287,9 @@ typedef struct
 	// [RC] List of IP addresses that this client is ignoring.
 	std::list<STORED_QUERY_IP_s> IgnoredAddresses;
 
+	// [Dusk] The password the client connected with, if we're using a password list.
+	char			szPassword[64];
+
 	// [K6] Last tic we got some action from the client. Used to determine his presence.
 	LONG			lLastActionTic;
 
@@ -361,7 +364,7 @@ bool		SERVER_PerformAuthenticationChecks
 void		SERVER_ConnectNewPlayer( BYTESTREAM_s *pByteStream );
 bool		SERVER_GetUserInfo( BYTESTREAM_s *pByteStream, bool bAllowKick );
 void		SERVER_ConnectionError( NETADDRESS_s Address, const char *pszMessage, ULONG ulErrorCode );
-void		SERVER_ClientError( ULONG ulClient, ULONG ulErrorCode );
+void		SERVER_ClientError( ULONG ulClient, ULONG ulErrorCode, FString clientVersion="" );
 void		SERVER_SendFullUpdate( ULONG ulClient );
 void		SERVER_WriteCommands( void );
 bool		SERVER_IsValidClient( ULONG ulClient );
@@ -444,6 +447,14 @@ LONG		SERVER_STATISTIC_GetPeakInboundDat
 void		SERVER_STATISTIC_AddToInboundDataTransfer( ULONG ulNumBytes );
 LONG		SERVER_STATISTIC_GetCurrentInboundDataTransfer( void );
 
+// [Dusk] Password list
+void			PASSLIST_Construct ();
+bool			PASSLIST_CheckPassword (FString pw);
+void			PASSLIST_EnforcePasswords ();
+
+// [K6]
+extern bool g_bGameFrozen;
+
 //*****************************************************************************
 //	EXTERNAL CONSOLE VARIABLES
 
@@ -465,6 +476,10 @@ EXTERN_CVAR( Bool, sv_disallowbots );
 EXTERN_CVAR( Bool, sv_minimizetosystray )
 EXTERN_CVAR( Int, sv_queryignoretime )
 
+EXTERN_CVAR( Bool, sv_passwordlist );
+EXTERN_CVAR( Bool, sv_joinpasswordlist );
+EXTERN_CVAR( String, sv_passwordlistfile );
+
 // From sv_master.cpp
 EXTERN_CVAR( Bool, sv_updatemaster );
 EXTERN_CVAR( Bool, sv_broadcast );
--- src/sv_master.cpp
+++ src/sv_master.cpp
@@ -48,6 +48,9 @@
 //
 //-----------------------------------------------------------------------------
 
+#ifndef _WIN32
+#include <sys/utsname.h>
+#endif
 #include "networkheaders.h"
 #include "c_dispatch.h"
 #include "cooperative.h"
@@ -88,6 +91,8 @@ static	LONG				g_lStoredQueryIPTail;
 
 extern	NETADDRESS_s		g_LocalAddress;
 
+char versionWithOS[256];
+
 //*****************************************************************************
 //	CONSOLE VARIABLES
 
@@ -123,6 +128,15 @@ void SERVER_MASTER_Construct( void )
 	g_lStoredQueryIPHead = 0;
 	g_lStoredQueryIPTail = 0;
 
+#ifndef _WIN32
+	struct utsname u_name;
+	uname(&u_name);
+	if (uname(&u_name) < 0)
+		sprintf(versionWithOS, "%s", DOTVERSIONSTR_REV); //error, no data
+	else
+		sprintf(versionWithOS, "%s on %s %s", DOTVERSIONSTR_REV, u_name.sysname, u_name.release); // "Linux 2.6.32.5-amd64" or "FreeBSD 9.0-RELEASE" etc
+#endif
+
 	// Call SERVER_MASTER_Destruct() when Skulltag closes.
 	atterm( SERVER_MASTER_Destruct );
 }
@@ -289,24 +303,25 @@ void SERVER_MASTER_SendServerInfo( NETAD
 		}
 	
 		// Now, check to see if this IP has been banend from this server.
+		// [K6] Let's not.
 		NETWORK_AddressToIPStringArray( Address, szAddress );
-		if ( SERVERBAN_IsIPBanned( szAddress ))
-		{
+//		if ( SERVERBAN_IsIPBanned( szAddress ))
+//		{
 			// Write our header.
-			NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, SERVER_LAUNCHER_BANNED );
+//			NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, SERVER_LAUNCHER_BANNED );
 
 			// Send the time the launcher sent to us.
-			NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, ulTime );
+//			NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, ulTime );
 
 			// Send the packet.
-			NETWORK_LaunchPacket( &g_MasterServerBuffer, Address );
+//			NETWORK_LaunchPacket( &g_MasterServerBuffer, Address );
 
-			if ( sv_showlauncherqueries )
-				Printf( "Denied BANNED IP launcher challenge.\n" );
+//			if ( sv_showlauncherqueries )
+//				Printf( "Denied BANNED IP launcher challenge.\n" );
 
 			// Nothing more to do here.
-			return;
-		}
+//			return;
+//		}
 
 		// This IP didn't exist in the list. and it wasn't banned. 
 		// So, add it, and keep it there for 10 seconds.
@@ -325,8 +340,8 @@ void SERVER_MASTER_SendServerInfo( NETAD
 	// Send the time the launcher sent to us.
 	NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, ulTime );
 
-	// Send our version.
-	NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, DOTVERSIONSTR_REV );
+	// Send our version. [K6] ...with OS
+	NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, versionWithOS );
 
 	// Send the information about the data that will be sent.
 	ulBits = ulFlags;
@@ -421,10 +436,10 @@ void SERVER_MASTER_SendServerInfo( NETAD
 		NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, NETWORK_GetIWAD( ));
 
 	if ( ulBits & SQF_FORCEPASSWORD )
-		NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, sv_forcepassword );
+		NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, sv_forcepassword || sv_passwordlist );
 
 	if ( ulBits & SQF_FORCEJOINPASSWORD )
-		NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, sv_forcejoinpassword );
+		NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, sv_forcejoinpassword || sv_joinpasswordlist );
 
 	if ( ulBits & SQF_GAMESKILL )
 		NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, gameskill );
--- src/sv_rcon.cpp
+++ src/sv_rcon.cpp
@@ -156,6 +156,10 @@ void SERVER_RCON_ParseMessage( NETADDRES
 {
 	int iIndex = -1;
 
+// [K6] When a client fails rcon utility authorization, ignore him for a while.
+if ( g_BadRequestFloodQueue.isFull( ) || g_BadRequestFloodQueue.addressInQueue( NETWORK_GetFromAddress( )))
+	return;
+
 	switch ( lMessage )
 	{
 	case CLRC_BEGINCONNECTION:
--- src/svnrevision.h
+++ src/svnrevision.h
@@ -0,0 +1,8 @@
+// 130718-2050M
+//
+// This file was automatically generated by the
+// updaterevision tool. Do not edit by hand.
+
+#define SVN_REVISION_STRING "130718-2050M"
+#define SVN_REVISION_NUMBER 1374180653
+#define HG_REVISION_HASH_STRING "661815f0a49d"
--- src/team.cpp
+++ src/team.cpp
@@ -493,9 +493,9 @@ void TEAM_ScoreSkulltagPoint( player_t *
 
 	// Create the console message.
 	if( ( bAssisted ) && ( ! bSelfAssisted ) )
-		sprintf(szString, "%s \\c-and %s\\c- scored for the %s team!\n", pPlayer->userinfo.netname, players[TEAM_GetAssistPlayer( pPlayer->ulTeam )].userinfo.netname, TEAM_GetName( pPlayer->ulTeam ));
+		sprintf(szString, "%s \\c-and %s\\c- scored for the \\c%c%s \\c-team!\n", pPlayer->userinfo.netname, players[TEAM_GetAssistPlayer( pPlayer->ulTeam )].userinfo.netname, V_GetColorChar( TEAM_GetTextColor( pPlayer->ulTeam )), TEAM_GetName( pPlayer->ulTeam ));
 	else
-		sprintf(szString, "%s \\c-scored for the %s team!\n", pPlayer->userinfo.netname, TEAM_GetName( pPlayer->ulTeam ) );
+		sprintf(szString, "%s \\c-scored for the \\c%c%s \\c-team!\n", pPlayer->userinfo.netname, V_GetColorChar( TEAM_GetTextColor( pPlayer->ulTeam )), TEAM_GetName( pPlayer->ulTeam ));
 
 	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
 		SERVERCOMMANDS_Print( szString, PRINT_HIGH );
@@ -719,6 +719,7 @@ void TEAM_FlagDropped( player_t *pPlayer
 	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
 	{
 		SERVERCOMMANDS_TeamFlagDropped( ULONG( pPlayer - players ), ulTeamIdx );
+		SERVER_Printf( PRINT_MEDIUM, "%s \\c-lost the \\c%c%s \\c-%s.\n", pPlayer->userinfo.netname, V_GetColorChar( TEAM_GetTextColor( ulTeamIdx)), TEAM_GetName( ulTeamIdx), ( skulltag ) ? "skull" : "flag" );
 		return;
 	}
 
@@ -1053,7 +1054,7 @@ void TEAM_SetScore( ULONG ulTeamIdx, LON
 	if ( TEAM_GetScore( ulTeamIdx ) >= (LONG)pointlimit )
 	{
 		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
-			SERVER_Printf( PRINT_HIGH, "%s has won the game!\n", TEAM_GetName( ulTeamIdx ));
+			SERVER_Printf( PRINT_HIGH, "\\c%c%s \\c-has won the game!\n", V_GetColorChar( TEAM_GetTextColor( ulTeamIdx )), TEAM_GetName( ulTeamIdx ));
 		else
 			Printf( "%s has won the game!\n", TEAM_GetName( ulTeamIdx ));
 
--- src/version.h
+++ src/version.h
@@ -36,7 +36,12 @@
 
 // The svnrevision.h is automatically updated to grab the revision of
 // of the current source tree so that it can be included with version numbers.
-#include "svnrevision.h"
+//#include "svnrevision.h"
+#define SVN_REVISION_STRING "130718-2050M"
+#define SVN_REVISION_NUMBER 1374180653
+#define HG_REVISION_HASH_STRING "661815f0a49d"
+#define KPATCH_BUILD "38a"
+
 
 /** Lots of different version numbers **/
 
@@ -49,7 +54,8 @@
 #define ZD_SVN_REVISION_NUMBER 1551
 
 // [BB] The version string that includes revision / compatibility data.
-#define DOTVERSIONSTR_REV DOTVERSIONSTR "-r" SVN_REVISION_STRING
+// [K6] Include our kpatch version to mark the server as such.
+#define DOTVERSIONSTR_REV DOTVERSIONSTR "-r" SVN_REVISION_STRING "-kpatch" KPATCH_BUILD
 
 // [BC] What version of ZDoom is this based off of?
 #define	ZDOOMVERSIONSTR		ZDVER_STRING"-"ZD_SVN_REVISION_STRING
--- src/w_wad.cpp
+++ src/w_wad.cpp
@@ -499,6 +499,7 @@ int STACK_ARGS FWadCollection::lumpcmp(c
 // [BC] Edited a little.
 void FWadCollection::AddFile (const char *filename, const char *data, int length, bool bLoadedAutomatically)
 {
+	FString infoString;
 	WadFileRecord	*wadinfo;
 	MergedHeader	header;
 	LumpRecord*		lump_p;
@@ -528,7 +529,7 @@ void FWadCollection::AddFile (const char
 		// This is an in-memory WAD created from a WAD inside a .zip
 		wadinfo = new WadFileRecord(data, length);
 	}
-	Printf (" adding %s", filename);
+	infoString.AppendFormat (" adding %s", filename);
 	startlump = NumLumps;
 
 
@@ -555,7 +556,7 @@ void FWadCollection::AddFile (const char
 		wadinfo->Seek (header.wad.InfoTableOfs, SEEK_SET);
 		wadinfo->Read (fileinfo, header.wad.NumLumps * sizeof(wadlump_t));
 		NumLumps += header.wad.NumLumps;
-		Printf (" (%u lumps)", header.wad.NumLumps);
+		infoString.AppendFormat (" (%u lumps)", header.wad.NumLumps);
 	}
 	else if (header.magic[0] == RFF_ID)
 	{ // This is a Blood RFF file
@@ -607,7 +608,7 @@ void FWadCollection::AddFile (const char
 			lump_p->fullname = copystring ((char *)rff_p->IDontKnow);
 			lump_p++;
 		}
-		Printf (" (%u files)", header.rff.NumLumps);
+		infoString.AppendFormat (" (%u files)", header.rff.NumLumps);
 		delete[] lumps;
 	}
 	else if (header.magic[0] == GRP_ID_0 && header.magic[1] == GRP_ID_1 && header.magic[2] == GRP_ID_2)
@@ -639,7 +640,7 @@ void FWadCollection::AddFile (const char
 			lump_p->namespc = ns_global;
 			lump_p++;
 		}
-		Printf (" (%u files)", header.grp.NumLumps);
+		infoString.AppendFormat (" (%u files)", header.grp.NumLumps);
 		delete[] lumps;
 	}
 	else if (header.magic[0] == ZIP_ID)
@@ -751,7 +752,7 @@ void FWadCollection::AddFile (const char
 		// Resize the lump record array to its actual size
 		NumLumps -= skipped;
 		LumpInfo.Resize(NumLumps);
-		Printf (" (%d files)", LittleShort(info.NumEntries) - skipped);
+		infoString.AppendFormat (" (%d files)", LittleShort(info.NumEntries) - skipped);
 		
 		// Entries in Zips are sorted alphabetically.
 		qsort(&LumpInfo[startlump], NumLumps - startlump, sizeof(LumpRecord), lumpcmp);
@@ -767,7 +768,7 @@ void FWadCollection::AddFile (const char
 		{
 			delete arc;
 			delete wadinfo;
-			Printf("\n%s: ", filename);
+			Printf("\n%s:\n", filename);
 			if (res == SZ_ERROR_UNSUPPORTED)
 			{
 				Printf("Decoder does not support this archive\n");
@@ -831,7 +832,7 @@ void FWadCollection::AddFile (const char
 		// Resize the lump record array to its actual size
 		NumLumps -= skipped;
 		LumpInfo.Resize(NumLumps);
-		Printf (" (%u files)", arc->DB.db.NumFiles - skipped);
+		infoString.AppendFormat (" (%u files)", arc->DB.db.NumFiles - skipped);
 
 		// Entries in archives are sorted alphabetically
 		qsort(&LumpInfo[startlump], NumLumps - startlump, sizeof(LumpRecord), lumpcmp);
@@ -846,7 +847,7 @@ void FWadCollection::AddFile (const char
 		uppercopy(singleinfo.Name, name);
 		NumLumps++;
 	}
-	Printf ("\n");
+	Printf ("%s\n", infoString.GetChars());
 
 	// Fill in lumpinfo
 	if (header.magic[0] != RFF_ID &&
--- src/win32/i_system.cpp
+++ src/win32/i_system.cpp
@@ -107,6 +107,8 @@ void CalculateCPUSpeed ();
 
 const IWADInfo *DoomStartupInfo;
 
+extern char versionWithOS[256];
+
 int (*I_GetTime) (bool saveMS);
 int (*I_WaitForTic) (int);
 void (*I_FreezeTime) (bool frozen);
@@ -294,11 +296,17 @@ void I_WaitVBL (int count)
 // [RH] Detect the OS the game is running under
 void I_DetectOS (void)
 {
-	OSVERSIONINFO info;
+// from ZDoom ->
+	OSVERSIONINFOEX info;
 	const char *osname;
 
-	info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-	GetVersionEx (&info);
+	info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+	if (!GetVersionEx((OSVERSIONINFO *)&info))
+	{
+		// Retry with the older OSVERSIONINFO structure.
+		info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+		GetVersionEx((OSVERSIONINFO *)&info);
+	}
 
 	switch (info.dwPlatformId)
 	{
@@ -336,9 +344,42 @@ void I_DetectOS (void)
 				osname = "Server 2003";
 			}
 		}
-		else if (info.dwMajorVersion == 6 && info.dwMinorVersion == 0)
+		else if (info.dwMajorVersion == 6)
 		{
-			osname = "Vista";
+			if (info.dwMinorVersion == 0)
+			{
+				if (info.wProductType == VER_NT_WORKSTATION)
+				{
+					osname = "Vista";
+				}
+				else
+				{
+					osname = "Server 2008";
+				}
+			}
+			if (info.dwMinorVersion == 1)
+			{
+				if (info.wProductType == VER_NT_WORKSTATION)
+				{
+					osname = "7";
+				}
+				else
+				{
+					osname = "Server 2008 R2";
+				}
+			}
+// from ZDoom <-
+			else if (info.dwMinorVersion == 2)
+			{
+				if (info.wProductType == VER_NT_WORKSTATION)
+				{
+					osname = "8";
+				}
+				else
+				{
+					osname = "Server 2012";
+				}
+			}
 		}
 		break;
 
@@ -354,13 +395,19 @@ void I_DetectOS (void)
 				osname,
 				info.dwMajorVersion, info.dwMinorVersion,
 				info.dwBuildNumber & 0xffff, info.szCSDVersion);
+		sprintf(versionWithOS, "%s on Windows %s (%lu.%lu.%lu)", DOTVERSIONSTR_REV, osname,
+				info.dwMajorVersion, info.dwMinorVersion,
+				info.dwBuildNumber & 0xffff);
 	}
 	else
 	{
-		Printf ("OS: Windows %s %lu.%lu (Build %lu)\n    %s\n",
+		Printf ("OS: Windows %s (NT %lu.%lu) Build %lu\n    %s\n", // from ZDoom
 				osname,
 				info.dwMajorVersion, info.dwMinorVersion,
 				info.dwBuildNumber, info.szCSDVersion);
+		sprintf(versionWithOS, "%s on Windows %s (%lu.%lu.%lu)", DOTVERSIONSTR_REV, osname,
+				info.dwMajorVersion, info.dwMinorVersion,
+				info.dwBuildNumber);
 	}
 
 	if (OSPlatform == os_unknown)
--- src/x86.cpp
+++ src/x86.cpp
@@ -212,18 +212,19 @@ void DumpCPUInfo(const CPUInfo *cpu)
 			Printf("  Family %d, Model %d, Stepping %d\n",
 				cpu->Family, cpu->Model, cpu->Stepping);
 		}
-		Printf("  Features:");
-		if (cpu->bMMX)			Printf(" MMX");
-		if (cpu->bMMXPlus)		Printf(" MMX+");
-		if (cpu->bSSE)			Printf(" SSE");
-		if (cpu->bSSE2)			Printf(" SSE2");
-		if (cpu->bSSE3)			Printf(" SSE3");
-		if (cpu->bSSSE3)		Printf(" SSSE3");
-		if (cpu->bSSE41)		Printf(" SSE4.1");
-		if (cpu->bSSE42)		Printf(" SSE4.2");
-		if (cpu->b3DNow)		Printf(" 3DNow!");
-		if (cpu->b3DNowPlus)	Printf(" 3DNow!+");
-		Printf ("\n");
+		FString infoString;
+		infoString.AppendFormat("  Features:");
+		if (cpu->bMMX)			infoString.AppendFormat(" MMX");
+		if (cpu->bMMXPlus)		infoString.AppendFormat(" MMX+");
+		if (cpu->bSSE)			infoString.AppendFormat(" SSE");
+		if (cpu->bSSE2)			infoString.AppendFormat(" SSE2");
+		if (cpu->bSSE3)			infoString.AppendFormat(" SSE3");
+		if (cpu->bSSSE3)		infoString.AppendFormat(" SSSE3");
+		if (cpu->bSSE41)		infoString.AppendFormat(" SSE4.1");
+		if (cpu->bSSE42)		infoString.AppendFormat(" SSE4.2");
+		if (cpu->b3DNow)		infoString.AppendFormat(" 3DNow!");
+		if (cpu->b3DNowPlus)	infoString.AppendFormat(" 3DNow!+");
+		Printf ("%s\n", infoString.GetChars());
 	}
 }
 
