diff -r 3e26f5eca137 src/CMakeLists.txt
--- a/src/CMakeLists.txt	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/CMakeLists.txt	Wed Oct 08 11:31:35 2014 +0200
@@ -1026,7 +1026,7 @@
 
 if( CMAKE_COMPILER_IS_GNUCXX )
 	# GCC misoptimizes this file
-	set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" )
+#	set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" )
 	
 	# Compile this one file with SSE2 support.
 	set_source_files_properties( nodebuild_classify_sse2.cpp PROPERTIES COMPILE_FLAGS "-msse2 -mfpmath=sse" )
diff -r 3e26f5eca137 src/astar.cpp
--- a/src/astar.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/astar.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -204,6 +204,7 @@
 {
 	ULONG	ulIdx;
 
+	Printf("Clearing bot nodes...\n");
 	delete[] g_aMasterNodeList;
 	g_aMasterNodeList = NULL;
 
diff -r 3e26f5eca137 src/bots.cpp
--- a/src/bots.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/bots.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -89,6 +89,7 @@
 #include "p_trace.h"
 #include "sbar.h"
 #include "doomerrors.h"
+#include "duel.h"
 
 //*****************************************************************************
 //	VARIABLES
@@ -683,6 +684,10 @@
 
 	// Refresh the HUD since the number of players in the game is potentially changing.
 	SCOREBOARD_RefreshHUD( );
+
+	// [K6] If there are no more bots left, clear the bot nodes.
+	if (BOTS_CountBots( ) == 0 && ASTAR_IsInitialized( ))
+		ASTAR_ClearNodes( );
 }
 
 //*****************************************************************************
@@ -998,6 +1003,29 @@
 }
 
 //*****************************************************************************
+//
+// [K6] Who created this function?!
+void BOTS_RemoveTempBots( bool bAdd )
+{
+	if ( sv_minplayers == 0 )
+		return;
+
+	if ( sv_minplayers > 2 && sv_removetempbots && ( bAdd ? SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) + 1 >= 2 : SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) >= 2 ))
+	{
+		C_DoCommand ( "removebots" );
+	}
+	else if ( bAdd ? sv_minplayers < static_cast<int>( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) + 1 : sv_minplayers < static_cast<int>( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )))
+	{
+		C_DoCommand ( "removebot" );
+	}
+	else
+		return;
+
+	// [K6] If a bot was just removed from duel to be replaced by a player, set up a new duel.
+	if ( duel )
+		DUEL_SetState( DS_WAITINGFORPLAYERS );
+}
+
 //*****************************************************************************
 //
 void bots_ParseBotInfoLump( FScanner &sc )
@@ -1609,6 +1637,9 @@
 {
 	ULONG	ulIdx;
 
+	if ( ASTAR_IsInitialized( ) == false )
+		ASTAR_BuildNodes( );
+
 	g_bBotIsInitialized[ulPlayerNum] = false;
 
 	// First, initialize all variables.
@@ -3852,16 +3883,9 @@
 		return;
 	}
 
-	if ( ASTAR_IsInitialized( ) == false )
+	if ( level.flags2 & LEVEL2_NOBOTNODES )
 	{
-		if ( sv_disallowbots )
-			Printf( "The bot pathing nodes have not been set up. Please set \"sv_disallowbots\" to \"false\" if you wish to use bots.\n" );
-		else if ( level.flags2 & LEVEL2_NOBOTNODES )
-			Printf( "The bot pathing nodes have not been set up. This level has disabled the ability to do so.\n" );
-		else
-			Printf( "The bot pathing nodes have not been set up. Please reload the level if you wish to use bots.\n" );
-
-		// Don't allow the bot to spawn.
+		Printf( "The bot pathing nodes have not been set up. This level has disabled the ability to do so.\n" );
 		return;
 	}
 
diff -r 3e26f5eca137 src/bots.h
--- a/src/bots.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/bots.h	Wed Oct 08 11:31:35 2014 +0200
@@ -644,6 +644,7 @@
 void		BOTS_PostWeaponFiredEvent( ULONG ulPlayer, BOTEVENT_e EventIfSelf, BOTEVENT_e EventIfEnemy, BOTEVENT_e EventIfPlayer );
 void		BOTS_RemoveGoal( AActor* pGoal );
 ULONG		BOTS_CountBots( void );
+void		BOTS_RemoveTempBots( bool bAdd = false );
 
 // Botinfo access functions.
 ULONG			BOTINFO_GetNumBotInfos( void );
@@ -704,5 +705,7 @@
 EXTERN_CVAR( Float, botdebug_maxgiveupnodes )
 EXTERN_CVAR( Float, botdebug_maxroamgiveupnodes )
 EXTERN_CVAR( Int, botdebug_shownodes )
+EXTERN_CVAR (Int, sv_minplayers)
+EXTERN_CVAR (Bool, sv_removetempbots)
 
 #endif	// __BOTS_H__
diff -r 3e26f5eca137 src/c_cmds.cpp
--- a/src/c_cmds.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/c_cmds.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -164,6 +164,10 @@
 	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);
 }
 
@@ -173,6 +177,10 @@
 	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);
 }
 
diff -r 3e26f5eca137 src/c_console.cpp
--- a/src/c_console.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/c_console.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -941,10 +941,8 @@
 	char *outlinecopy = new char[strlen(outline)+1];
 	strcpy (outlinecopy,outline);
 
-	if (Logfile)
-	{
-		// Strip out any color escape sequences before writing to the log file
-		char * copy = new char[strlen(outlinecopy)+1];
+	// Strip out any color escape sequences before writing to the log file
+	char * copy = new char[strlen(outlinecopy)+1];
 /* [BB] ST handles color codes a little differently (not all of them are converted to TEXTCOLOR_ESCAPE yet), so we strip them differently.
 		const char * srcp = outlinecopy;
 		char * dstp = copy;
@@ -963,10 +961,12 @@
 		}
 		*dstp=0;
 */
-		strcpy (copy,outlinecopy);
-		V_ColorizeString( copy );
-		V_RemoveColorCodes( copy );
+	strcpy (copy,outlinecopy);
+	V_ColorizeString( copy );
+	V_RemoveColorCodes( copy );
 
+	if (Logfile)
+	{
 		if( sv_logfiletimestamp )
 		{
 			// [BB] Generate time string "[YY:MM:DD;HH:MM:SS] " or "[HH:MM:SS] " and write it to the logfile.
@@ -983,7 +983,6 @@
 		}
 
 		fputs (copy, Logfile);
-		delete [] copy;
 //#ifdef _DEBUG
 		fflush (Logfile);
 //#endif
@@ -995,7 +994,12 @@
 		if ( printlevel != PRINT_LOW )
 		{			
 			// [RC] Send this to any connected RCON clients.
-			SERVER_RCON_Printf( outlinecopy );
+			// [K6] Include color codes only in case this is a message for luk.
+			if ( strstr(outlinecopy, "\bluk") )
+				SERVER_RCON_Printf( outlinecopy );
+			else
+				SERVER_RCON_Printf( copy );
+
 			if ( g_ulRCONPlayer != MAXPLAYERS )
 				SERVER_PrintfPlayer( printlevel, g_ulRCONPlayer, outlinecopy );
 
@@ -1004,6 +1008,7 @@
 		
 		const int length = static_cast<int>(strlen (outlinecopy));
 		delete [] outlinecopy;
+		delete [] copy;
 		return length;
 	}
 	
@@ -1034,6 +1039,7 @@
 	}
 	const int length = static_cast<int>(strlen (outlinecopy));
 	delete [] outlinecopy;
+	delete [] copy;
 	return length;
 }
 
diff -r 3e26f5eca137 src/callvote.cpp
--- a/src/callvote.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/callvote.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -666,6 +666,10 @@
 	if ( sv_limitnumvotes == false )
 		return true;
 
+	// [K6] No point in restricting how much a lone player can vote.
+	if ( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) == 1 && !PLAYER_IsTrueSpectator( &players[ulPlayer] ))
+		return true;
+
 	// Run through the vote cache (backwards, from recent to old) and search for grounds on which to reject the vote.
 	for( std::list<VOTE_s>::reverse_iterator i = g_PreviousVotes.rbegin(); i != g_PreviousVotes.rend(); ++i )
 	{
diff -r 3e26f5eca137 src/d_main.cpp
--- a/src/d_main.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/d_main.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -2578,18 +2578,50 @@
 			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
+				{
+					// [K6] Start with a random map if we are using sv_randommaprotation.
+					sprintf( levelname, "%s", MAPROTATION_GetMap( sv_randommaprotation ?
+						M_Random.Random( ) % MAPROTATION_GetNumEntries( ) : 0 )->mapname );
+					MAPROTATION_SetPositionToMap( levelname );
+				}
 				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
 		{
diff -r 3e26f5eca137 src/d_netinfo.cpp
--- a/src/d_netinfo.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/d_netinfo.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -1339,6 +1339,8 @@
 	if (argv.argc() < 2)
 	{
 		int i;
+		int position;
+		std::string strpass2;
 
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
@@ -1359,6 +1361,22 @@
 					// [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());
+						}
+					}
+
 				}
 
 				if ( PLAYER_IsTrueSpectator( &players[i] ))
diff -r 3e26f5eca137 src/d_player.h
--- a/src/d_player.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/d_player.h	Wed Oct 08 11:31:35 2014 +0200
@@ -602,7 +602,7 @@
 void	PLAYER_SetTeam( player_t *pPlayer, ULONG ulTeam, bool bNoBroadcast );
 void	PLAYER_SetSpectator( player_t *pPlayer, bool bBroadcast, bool bDeadSpectator );
 void	PLAYER_SetDefaultSpectatorValues( player_t *pPlayer );
-void	PLAYER_SpectatorJoinsGame ( player_t *pPlayer );
+void	PLAYER_SpectatorJoinsGame ( player_t *pPlayer, bool bRemoveTempBots = true );
 void	PLAYER_SetWins( player_t *pPlayer, ULONG ulWins );
 void	PLAYER_GetName( player_t *pPlayer, char *pszOutBuf );
 // [BB] PLAYER_GetHealth and PLAYER_GetLivesLeft are helper functions for PLAYER_GetPlayerWithSingleHighestValue.
diff -r 3e26f5eca137 src/deathmatch.cpp
--- a/src/deathmatch.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/deathmatch.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -162,6 +162,9 @@
 		teamlms.ForceSet( Val, CVAR_Bool );
 		possession.ForceSet( Val, CVAR_Bool );
 		teampossession.ForceSet( Val, CVAR_Bool );
+
+		Val.Int = 2;
+		sv_maxplayers.ForceSet( Val, CVAR_Int );
 	}
 
 	// Reset what the current game mode is.
diff -r 3e26f5eca137 src/duel.cpp
--- a/src/duel.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/duel.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -150,7 +150,7 @@
 
 //*****************************************************************************
 //
-ULONG DUEL_CountActiveDuelers( void )
+ULONG DUEL_CountActiveDuelers( bool bCountBots )
 {
 	ULONG	ulIdx;
 	ULONG	ulDuelers;
@@ -158,7 +158,7 @@
 	ulDuelers = 0;
 	for ( ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
 	{
-		if (( playeringame[ulIdx] ) && ( players[ulIdx].bSpectating == false ))
+		if (( playeringame[ulIdx] ) && ( players[ulIdx].bSpectating == false ) && ( !players[ulIdx].bIsBot || ( players[ulIdx].bIsBot && bCountBots )))
 			ulDuelers++;
 	}
 
diff -r 3e26f5eca137 src/duel.h
--- a/src/duel.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/duel.h	Wed Oct 08 11:31:35 2014 +0200
@@ -71,7 +71,7 @@
 void	DUEL_Construct( void );
 void	DUEL_Tick( void );
 
-ULONG	DUEL_CountActiveDuelers( void );
+ULONG	DUEL_CountActiveDuelers( bool bCountBots = true );
 void	DUEL_StartCountdown( ULONG ulTicks );
 void	DUEL_DoFight( void );
 void	DUEL_DoWinSequence( ULONG ulPlayer );
diff -r 3e26f5eca137 src/g_level.cpp
--- a/src/g_level.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/g_level.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -130,6 +130,8 @@
 #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();
 
@@ -163,6 +165,9 @@
 extern bool sendpause, sendsave, sendturn180, SendLand;
 extern const AInventory *SendItemUse, *SendItemDrop;
 
+extern std::vector<MAPROTATIONENTRY_t> g_MapRotationEntries;
+extern ULONG g_ulCurMapInList;
+
 void *statcopy;					// for statistics driver
 
 FLevelLocals level;			// info about current level
@@ -303,7 +308,7 @@
 	}
 	else
 	{
-		Printf ("Usage: map <map name>\n");
+		Printf( "%s: %s\n", level.mapname, level.LevelName.GetChars() );
 	}
 }
 
@@ -1082,6 +1087,62 @@
 	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;
+		const ULONG boobSize = g_CvarOverrideEntries.size( );
+
+		for ( ULONG ulIdx = 0; ulIdx < boobSize; 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 < boobSize; 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());
+				}
+			}
+
+		// [K6] Mark the map as being visited for the purpose of sv_randommaprotation.
+		if ( sv_randommaprotation && ( g_MapRotationEntries.size( ) > 1 ) )
+			g_MapRotationEntries[g_ulCurMapInList].bUsed = true;
+	}
+
 	// [BB] Make sure that dead spectators are respawned before moving to the next map.
 	if ( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_DEADSPECTATORS )
 		GAMEMODE_RespawnDeadSpectatorsAndPopQueue( );
@@ -1479,6 +1540,12 @@
 	// Make sure the game isn't frozen.
 	level.flags2 &= ~LEVEL2_FROZEN;
 
+	if ( NETWORK_GetState( ) == NETSTATE_SERVER && g_bGameFrozen )
+	{
+		C_DoCommand ( "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.
@@ -2676,3 +2743,22 @@
 		}
 	}
 }
+
+// [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" );
+}
diff -r 3e26f5eca137 src/g_level.h
--- a/src/g_level.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/g_level.h	Wed Oct 08 11:31:35 2014 +0200
@@ -57,6 +57,15 @@
 #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
diff -r 3e26f5eca137 src/gamemode.cpp
--- a/src/gamemode.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/gamemode.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -660,6 +660,13 @@
 	// 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 );
+	}
 }
 
 //*****************************************************************************
@@ -741,7 +748,8 @@
 bool GAMEMODE_PreventPlayersFromJoining( ULONG ulExcludePlayer )
 {
 	// [BB] No free player slots.
-	if ( ( NETWORK_GetState( ) == NETSTATE_SERVER ) && ( SERVER_CalcNumNonSpectatingPlayers( ulExcludePlayer ) >= static_cast<unsigned> (sv_maxplayers) ) )
+	// [K6] Temporary bots don't count.
+	if ( ( NETWORK_GetState( ) == NETSTATE_SERVER ) && ( SERVER_CalcNumNonSpectatingPlayers( ulExcludePlayer, (sv_minplayers > 0 ? false : true) ) >= static_cast<unsigned> (sv_maxplayers) ) )
 		return true;
 
 	// [BB] Don't let players join during intermission. They will be put in line and the queue is popped once intermission ends.
@@ -749,7 +757,8 @@
 		return true;
 
 	// [BB] Duel in progress.
-	if ( duel && ( DUEL_CountActiveDuelers( ) >= 2 ) )
+	// [K6] Temporary bots don't count.
+	if ( duel && ( DUEL_CountActiveDuelers( sv_minplayers > 0 ? false : true ) >= 2 ) )
 		return true;
 
 	// [BB] If lives are limited, players are not allowed to join most of the time.
diff -r 3e26f5eca137 src/joinqueue.cpp
--- a/src/joinqueue.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/joinqueue.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -68,6 +68,8 @@
 #include "sv_commands.h"
 #include "team.h"
 
+#include "v_font.h"
+
 //*****************************************************************************
 //	VARIABLES
 
@@ -274,7 +276,15 @@
 		{
 			// [K6] Reset their AFK timer now - they may have been waiting in the queue silently and we don't want to kick them.
 			SERVER_GetClient( g_lJoinQueue[ulIdx].ulPlayer )->lLastActionTic = gametic;
-			PLAYER_SpectatorJoinsGame ( &players[g_lJoinQueue[ulIdx].ulPlayer] );
+
+			// [K6] We should somehow let the (possibly even minimized) client loudly know that he's in the game now.
+			// This might not be the ideal solution but it works for me...
+			SERVERCOMMANDS_AnnouncerSound( "fight", (ULONG)g_lJoinQueue[ulIdx].ulPlayer, SVCF_ONLYTHISCLIENT );
+			SERVERCOMMANDS_PrintHUDMessageFadeOut( "FIGHT!", 160.4f, 75.0f, 320, 200, CR_RED, 3.0f, 2.0f, "BigFont", false, MAKE_ID('C','N','T','R'),
+			(ULONG)g_lJoinQueue[ulIdx].ulPlayer, SVCF_ONLYTHISCLIENT );
+
+			// [K6] We can't remove temporary bots just now, or we get a crash, so postpone it.
+			PLAYER_SpectatorJoinsGame ( &players[g_lJoinQueue[ulIdx].ulPlayer], false );
 
 			// [BB/Spleen] The "lag interval" is only half of the "spectate info send" interval. Account for this here.
 			if (( gametic - SERVER_GetClient( g_lJoinQueue[ulIdx].ulPlayer )->ulLastCommandTic ) <= 2*TICRATE )
@@ -318,6 +328,9 @@
 
 			JOINQUEUE_RemovePlayerAtPosition ( ulIdx );
 
+			// [K6] Remove temporary bots as needed.
+			BOTS_RemoveTempBots( );
+
 			if ( lNumSlots > 0 )
 				lNumSlots--;
 		}
diff -r 3e26f5eca137 src/maprotation.cpp
--- a/src/maprotation.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/maprotation.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -61,7 +61,7 @@
 
 std::vector<MAPROTATIONENTRY_t>	g_MapRotationEntries;
 
-static	ULONG					g_ulCurMapInList;
+/*static*/	ULONG					g_ulCurMapInList;
 static	ULONG					g_ulNextMapInList;
 
 //*****************************************************************************
@@ -91,7 +91,7 @@
 	if ( sv_randommaprotation && ( g_MapRotationEntries.size( ) > 1 ) )
 	{
 		// Mark the current map in the list as being used.
-		g_MapRotationEntries[g_ulCurMapInList].bUsed = true;
+//		g_MapRotationEntries[g_ulCurMapInList].bUsed = true;
 
 		// If all the maps have been played, make them all available again. 
 		{
@@ -291,7 +291,8 @@
 	{
 		Printf( "Map rotation list: \n" );
 		for ( ULONG ulIdx = 0; ulIdx < g_MapRotationEntries.size( ); ulIdx++ )
-			Printf( "%lu. %s - %s\n", ulIdx + 1, g_MapRotationEntries[ulIdx].pMap->mapname, g_MapRotationEntries[ulIdx].pMap->LookupLevelName( ).GetChars( ));
+			Printf( "%lu. %s - %s%s\n", ulIdx + 1, g_MapRotationEntries[ulIdx].pMap->mapname, g_MapRotationEntries[ulIdx].pMap->LookupLevelName( ).GetChars( ),
+				g_MapRotationEntries[ulIdx].bUsed ? "\\cd - used" : ""); // [K6] Added debug output.
 	}
 }
 
diff -r 3e26f5eca137 src/network.cpp
--- a/src/network.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/network.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -912,7 +912,7 @@
 
 //*****************************************************************************
 // [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 ) ||
@@ -923,7 +923,8 @@
 	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;
 }
 
diff -r 3e26f5eca137 src/network.h
--- a/src/network.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/network.h	Wed Oct 08 11:31:35 2014 +0200
@@ -347,7 +347,7 @@
 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<std::pair<FString, FString> >	*NETWORK_GetPWADList( void ); // [RC]
diff -r 3e26f5eca137 src/networkshared.cpp
--- a/src/networkshared.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/networkshared.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -678,8 +678,8 @@
 	}
 
 	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;
 }
 
diff -r 3e26f5eca137 src/p_acs.cpp
--- a/src/p_acs.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/p_acs.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -557,6 +557,12 @@
 		// [BB] The armor display amount has to be updated separately.
 		if( item->GetClass()->IsDescendantOf (RUNTIME_CLASS(AArmor)))
 			SERVERCOMMANDS_SetPlayerArmor( actor->player - players );
+
+		if (strcmp(item->GetClass( )->TypeName.GetChars( ), "BombCrateFXGiverBlue") == 0
+		|| strcmp(item->GetClass( )->TypeName.GetChars( ), "BombCrateFXGiverRed") == 0)
+		{
+			SERVER_GetClient( ULONG(actor->player - players) )->lBombCrateTic = gametic;
+		}
 	}
 
 	// [BB]
diff -r 3e26f5eca137 src/p_interaction.cpp
--- a/src/p_interaction.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/p_interaction.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -2549,7 +2549,7 @@
 
 //*****************************************************************************
 //
-void PLAYER_SpectatorJoinsGame( player_t *pPlayer )
+void PLAYER_SpectatorJoinsGame( player_t *pPlayer, bool bRemoveTempBots )
 {
 	if ( pPlayer == NULL )
 		return;
@@ -2581,6 +2581,10 @@
 	// [BB] If he's a bot, tell him that he successfully joined.
 	if ( pPlayer->pSkullBot )
 		pPlayer->pSkullBot->PostEvent( BOTEVENT_JOINEDGAME );
+
+	// [K6] Remove temporary bots as needed.
+	if ( bRemoveTempBots )
+		BOTS_RemoveTempBots( );
 }
 
 //*****************************************************************************
@@ -3032,6 +3036,11 @@
 	// [BB] Clear the players medals and the medal related counters. The former is something also clients need to do.
 	memset( players[ulPlayer].ulMedalCount, 0, sizeof( ULONG ) * NUM_MEDALS );
 	PLAYER_ResetSpecialCounters ( &players[ulPlayer] );
+
+	// [K6] Remove temporary bots as needed.
+	// For some reason we get a crash if this is in BOTS_RemoveTempBots() AND that function is called from elsewhere.
+	if ( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) == 0 )
+		C_DoCommand ( "removebots" );
 }
 
 //*****************************************************************************
diff -r 3e26f5eca137 src/p_setup.cpp
--- a/src/p_setup.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/p_setup.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -2899,13 +2899,17 @@
 	blockmap = blockmaplump+4;
 
 	// [BC] Also, build the node list for the bot pathing module.
+	// [K6] For the server, this is handled in CSkullBot(), unless we already have bots in game (from the previous map).
 	if (( NETWORK_GetState( ) != NETSTATE_CLIENT ) &&
 		( CLIENTDEMO_IsPlaying( ) == false ) &&
 		(( level.flags2 & LEVEL2_NOBOTNODES ) == false ) &&
-		(( NETWORK_GetState( ) != NETSTATE_SERVER ) || ( sv_disallowbots == false )))
+		(( NETWORK_GetState( ) != NETSTATE_SERVER ) || ( BOTS_CountBots( ) > 0 )))
 	{
 		ASTAR_BuildNodes( );
 	}
+
+	if ( level.flags2 & LEVEL2_NOBOTNODES || level.flags2 & LEVEL2_ISLOBBY )
+		BOTS_RemoveAllBots( false );
 }
 
 
diff -r 3e26f5eca137 src/p_spec.cpp
--- a/src/p_spec.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/p_spec.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -87,6 +87,9 @@
 
 static FRandom pr_playerinspecialsector ("PlayerInSpecialSector");
 
+// [Dusk]
+static int g_lastLevelExit = -1;
+
 // [GrafZahl] Make this message changable by the user! ;)
 CVAR(String, secretmessage, "A Secret is revealed!", CVAR_ARCHIVE)
 
@@ -164,6 +167,12 @@
 {
 	cluster_info_t *cluster;
 
+	// [Dusk] Never exit twice.
+	if ( g_lastLevelExit == level.time )
+		return false;
+
+	g_lastLevelExit = level.time;
+
 	// The world can always exit itself.
 	if (self == NULL)
 		return true;
diff -r 3e26f5eca137 src/sdl/i_main.cpp
--- a/src/sdl/i_main.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/sdl/i_main.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -73,6 +73,10 @@
 
 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
 
+#ifdef unix
+void handle_sighup(int signum);
+#endif
+
 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
 
 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
@@ -204,6 +208,9 @@
 
 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);
diff -r 3e26f5eca137 src/sv_ban.cpp
--- a/src/sv_ban.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/sv_ban.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -47,6 +47,9 @@
 //
 //-----------------------------------------------------------------------------
 
+#ifdef unix
+#include <signal.h>
+#endif
 #include <stdio.h>
 #include <errno.h>
 
@@ -429,6 +432,11 @@
 
 	// Kick any players using a banned address.
 	serverban_KickBannedPlayers( );
+
+	// [Dusk] Take the opportunity to load the password list too.
+	PASSLIST_Construct ();
+	PASSLIST_EnforcePasswords ();
+
 }
 
 //*****************************************************************************
@@ -713,3 +721,14 @@
 {
 	serverban_LoadBansAndBanExemptions( );
 }
+
+#ifdef unix
+//*****************************************************************************
+//
+void handle_sighup(int signum)
+{
+	// in case we registered this handler for multiple signals
+	if (signum == SIGHUP)
+		serverban_LoadBansAndBanExemptions( );
+}
+#endif
diff -r 3e26f5eca137 src/sv_commands.cpp
--- a/src/sv_commands.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/sv_commands.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -886,7 +886,13 @@
 			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 );
@@ -2459,6 +2465,13 @@
 		NETWORK_WriteByte( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, ulProperty );
 		NETWORK_WriteLong( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, ulPropertyValue );
 	}
+	if (strstr(sv_hostname, "All Out War") && ulProperty == APROP_Speed)
+	{
+		if (pActor->tid && pActor->tid == 410)
+			BlueAOWTickets = ulPropertyValue;
+		if (pActor->tid && pActor->tid == 411)
+			RedAOWTickets = ulPropertyValue;
+	}
 }
 
 //*****************************************************************************
diff -r 3e26f5eca137 src/sv_commands.h
--- a/src/sv_commands.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/sv_commands.h	Wed Oct 08 11:31:35 2014 +0200
@@ -58,6 +58,11 @@
 #include "vectors.h"
 #include "a_weaponpiece.h"
 
+
+extern int BlueAOWTickets;
+extern int RedAOWTickets;
+
+
 //*****************************************************************************
 //	DEFINES
 
diff -r 3e26f5eca137 src/sv_main.cpp
--- a/src/sv_main.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/sv_main.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -116,6 +116,13 @@
 #include "network/cl_auth.h"
 #include "network/sv_auth.h"
 
+#include "maprotation.h"
+// [Dusk] we're reading files here
+#include <errno.h>
+
+int BlueAOWTickets = 0;
+int RedAOWTickets = 0;
+
 //*****************************************************************************
 //	MISC CRAP THAT SHOULDN'T BE HERE BUT HAS TO BE BECAUSE OF SLOPPY CODING
 
@@ -126,6 +133,8 @@
 void SERVERCONSOLE_ReListPlayers( void );
 
 EXTERN_CVAR( Bool, sv_cheats );
+EXTERN_CVAR (Float, sv_gravity);
+EXTERN_CVAR( Float, sv_aircontrol );
 
 //*****************************************************************************
 //	PROTOTYPES
@@ -160,6 +169,8 @@
 static	bool	server_MorphCheat( BYTESTREAM_s *pByteStream );
 static	bool	server_CheckForClientMinorCommandFlood( ULONG ulClient );
 static	bool	server_ProcessMoveCommand( CLIENT_MOVE_COMMAND_s &ClientMoveCmd, const ULONG ulClient );
+static	ULONG	CalcNumZombies( void );
+static	void	SendMaplistToPlayer( ULONG ulPlayer );
 static	bool	server_CheckJoinPassword( const FString& clientPassword );
 static	bool	server_Linetarget( BYTESTREAM_s* pByteStream );
 static	bool	server_CheckLogin( const ULONG ulClient );
@@ -232,6 +243,13 @@
 // [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;
+static char *g_pszStartupTime;
+
 // [RC] File to log packets to.
 #ifdef CREATE_PACKET_LOG
 static	FILE		*PacketLogFile = NULL;
@@ -259,7 +277,6 @@
 CVAR( Bool, sv_timestamp, false, CVAR_ARCHIVE )
 CVAR( Int, sv_timestampformat, 0, CVAR_ARCHIVE )
 CVAR( Int, sv_colorstripmethod, 0, CVAR_ARCHIVE )
-CVAR( Bool, sv_disallowbots, false, CVAR_ARCHIVE )
 CVAR( Bool, sv_minimizetosystray, true, CVAR_ARCHIVE )
 CVAR( Int, sv_queryignoretime, 10, CVAR_ARCHIVE )
 CVAR( Bool, sv_markchatlines, false, CVAR_ARCHIVE )
@@ -268,6 +285,21 @@
 CVAR( Bool, sv_pure, true, CVAR_SERVERINFO | CVAR_LATCH )
 CVAR( Int, sv_maxclientsperip, 2, CVAR_ARCHIVE )
 CVAR( Int, sv_afk2spec, 0, CVAR_ARCHIVE ) // [K6]
+CUSTOM_CVAR( Int, sv_minplayers, 0, CVAR_ARCHIVE ) // [K6]
+{
+	if (self < 0)
+		self == 0;
+
+	if (self > sv_maxplayers)
+	{
+		Printf( "sv_minplayers must be less than or equal to sv_maxplayers.\n" );
+		self = sv_maxplayers;
+	}
+}
+CVAR( Bool, sv_removetempbots, 0, CVAR_ARCHIVE ) // [K6]
+
+CVAR( Bool, sv_passwordlist, false, CVAR_ARCHIVE ) // [Dusk]
+CVAR( Bool, sv_joinpasswordlist, false, CVAR_ARCHIVE ) // [Dusk]
 CVAR( Bool, sv_forcelogintojoin, false, CVAR_ARCHIVE|CVAR_NOSETBYACS )
 
 CUSTOM_CVAR( String, sv_adminlistfile, "adminlist.txt", CVAR_ARCHIVE|CVAR_NOSETBYACS )
@@ -297,6 +329,9 @@
 // [BB] To stay compatible with old mods, the default value is at most 32.
 CUSTOM_CVAR( Int, sv_maxplayers, MIN ( MAXPLAYERS, 32 ), CVAR_ARCHIVE )
 {
+	if ( duel && self != 2 )
+		self = 2;
+
 	if ( self < 0 )
 		self = 0;
 
@@ -358,6 +393,19 @@
 }
 
 //*****************************************************************************
+// [Dusk]
+CUSTOM_CVAR (String, sv_passwordlistfile, "passwordlist.txt", CVAR_ARCHIVE)
+{
+	if (NETWORK_GetState () != NETSTATE_SERVER)
+		return;
+
+	PASSLIST_Construct ();
+
+}
+
+
+//*****************************************************************************
+
 //	FUNCTIONS
 
 void SERVER_Construct( void )
@@ -452,6 +500,9 @@
 	sprintf( g_szCurrentFont, "SmallFont" );
 	sprintf( g_szScriptActiveFont, "SmallFont" );
 
+	time_t rawtime = time(NULL);
+	g_pszStartupTime = ctime(&rawtime);
+
 #ifdef CREATE_PACKET_LOG
 
 	// [RC] Create the packet log.
@@ -942,7 +993,7 @@
 
 //*****************************************************************************
 //
-ULONG SERVER_CalcNumNonSpectatingPlayers( ULONG ulExcludePlayer )
+ULONG SERVER_CalcNumNonSpectatingPlayers( ULONG ulExcludePlayer, bool bCountBots )
 {
 	ULONG	ulIdx;
 	ULONG	ulNumPlayers = 0;
@@ -951,7 +1002,8 @@
 	{
 		if (( playeringame[ulIdx] == false ) ||
 			( PLAYER_IsTrueSpectator( &players[ulIdx] )) ||
-			( ulIdx == ulExcludePlayer ))
+			( ulIdx == ulExcludePlayer ) ||
+			( players[ulIdx].bIsBot && bCountBots == false ))
 		{
 			continue;
 		}
@@ -1115,12 +1167,18 @@
 
 	SERVERCOMMANDS_PlayerSay( ulPlayer, pszString, ulMode, bFordidChatToPlayers );
 
+	V_EscapeBacklashes( cleanedChatString );
+	pszString = cleanedChatString.GetChars();
+
 	// [BB] This is to make the lines readily identifiable, necessary
 	// for MiX-MaN's IRC server control tool for example.
 	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.
@@ -1138,6 +1196,33 @@
 			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 ) && ( stricmp( "hold", pszString ) == 0 ) && (playeringame[ulPlayer]) && ( !PLAYER_IsTrueSpectator( &players[ulPlayer] )) && (!g_bGameFrozen))
+		{
+			C_DoCommand ( "freezeall" );
+			SERVER_Printf( PRINT_HIGH, "Game paused. Say \"go\" to resume.\n" );
+		}
+		if (( duel ) && ( stricmp( "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 (strstr(sv_hostname, "All Out War") && (strnicmp( "tickets", pszString, 7 ) == 0 || strnicmp( "lives", pszString, 5 ) == 0)
+		&& gamestate == GS_LEVEL && BlueAOWTickets && RedAOWTickets)
+			SERVER_Printf(PRINT_HIGH, "Lives: \\cH%d \\c-- \\cG%d\\c-\n", BlueAOWTickets, RedAOWTickets);
+
+		if (stricmp( "!music", pszString ) == 0 && gamestate == GS_LEVEL)
+			SERVER_Printf(PRINT_HIGH, "%s\n", SERVER_GetMapMusic());
+
+		if (stricmp( "!maplist", pszString ) == 0)
+                        SendMaplistToPlayer(ulPlayer);
+
+
+
 	}
 	if( sv_markchatlines && sv_logfiletimestampOldValue )
 		sv_logfiletimestamp = true;
@@ -1309,7 +1394,7 @@
 	// Don't restart the map! There's people here!
 	g_lMapRestartTimer = 0;
 
-	g_aClients[g_lCurrentClient].ulLastGameTic = gametic;
+	g_aClients[g_lCurrentClient].ulLastGameTime = I_MSTime();
 	g_aClients[g_lCurrentClient].ulLastCommandTic = gametic;
 
 	// Clear out the client's netbuffer.
@@ -1319,7 +1404,58 @@
 	SERVERCOMMANDS_BeginSnapshot( g_lCurrentClient );
 
 	// Send welcome message.
-	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Version %s Server\n", DOTVERSIONSTR );
+	// [K6] Include the server startup time.
+	SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Version %s Server, started on %s\n", DOTVERSIONSTR, g_pszStartupTime );
+
+	// [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 )
+	{
+		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 );
@@ -1562,14 +1698,37 @@
 	{
 		// [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() );
-
-		if ( players[g_lCurrentClient].bSpectating )
-			SERVER_Printf( PRINT_HIGH, "%s \\c-has connected.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() );
+		std::string strpass;
+		int position;
+
+		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(), strpass.c_str() );
+
+			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() );
+		}
+		SERVERCOMMANDS_Sound( CHAN_VOICE, "zandronum/connect", 1.f, ATTN_NONE, (ULONG)g_lCurrentClient, SVCF_SKIPTHISCLIENT );
 	}
 
 	BOTCMD_SetLastJoinedPlayer( players[g_lCurrentClient].userinfo.netname );
@@ -1598,6 +1757,10 @@
 
 	// [RC] Clients may wish to ignore this new player.
 	SERVERCOMMANDS_PotentiallyIgnorePlayer( g_lCurrentClient );
+
+	// [K6] Remove temporary bots as needed.
+	if ( players[g_lCurrentClient].bSpectating == false )
+		BOTS_RemoveTempBots( );
 }
 
 //*****************************************************************************
@@ -1751,7 +1914,7 @@
 				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( ) );
@@ -1773,8 +1936,24 @@
 	}
 
 	// Don't handle connection attempts from clients if we're in intermission.
+	// [K6] But inform the client so he doesn't just stare at "connecting connecting".
 	if ( gamestate != GS_LEVEL )
+	{
+		if ( gamestate == GS_INTERMISSION ) // is this really necessary?
+		{
+			NETBUFFER_s TempBuffer;
+
+			NETWORK_InitBuffer( &TempBuffer, MAX_UDP_PACKET, BUFFERTYPE_WRITE );
+			NETWORK_ClearBuffer( &TempBuffer );
+			NETWORK_WriteByte( &TempBuffer.ByteStream, SVC_UNRELIABLEPACKET );
+			NETWORK_WriteHeader( &TempBuffer.ByteStream, SVC_PRINT );
+			NETWORK_WriteByte( &TempBuffer.ByteStream, PRINT_HIGH );
+			NETWORK_WriteString( &TempBuffer.ByteStream, "Server is in intermission. Please wait a moment.\n" );
+			NETWORK_LaunchPacket( &TempBuffer, NETWORK_GetFromAddress( ) );
+			NETWORK_FreeBuffer( &TempBuffer );
+		}
 		return;
+	}
 
 	// Setup a new player (setup CLIENT_t and player_t)
 	SERVER_SetupNewConnection( pByteStream, true );
@@ -1787,6 +1966,7 @@
 	LONG			lClient;
 	FString			clientVersion;
 	FString			clientPassword;
+	FString			tempPassword;
 	char			szServerPassword[MAX_NETWORK_STRING];
 	LONG			lClientNetworkGameVersion;
 	char			szAddress[4][4];
@@ -1902,7 +2082,7 @@
 		// 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
@@ -1910,6 +2090,7 @@
 		}
 	}
 
+/*
 	// Make sure the network game version matches.
 	if ( NETGAMEVERSION != lClientNetworkGameVersion )
 	{
@@ -1920,12 +2101,29 @@
 
 		return;
 	}
+*/
 
 	// Client is now in the loop, prevent from a early timeout
 	// [BB] Only do this if this is actually a new player connecting. Otherwise it
 	// could be abused to keep non-finished connections alive.
 	if ( bNewPlayer )
-		g_aClients[lClient].ulLastCommandTic = g_aClients[lClient].ulLastGameTic = gametic;
+	{
+		g_aClients[lClient].ulLastCommandTic = gametic;
+		g_aClients[lClient].ulLastGameTime = I_MSTime();
+	}
+
+	// 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 );
@@ -1941,6 +2139,16 @@
 			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.
@@ -1975,7 +2183,11 @@
 	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();
@@ -1996,6 +2208,8 @@
 	g_aClients[lClient].ulNumConsistencyWarnings = 0;
 	g_aClients[lClient].szSkin[0] = 0;
 	g_aClients[lClient].IgnoredAddresses.clear();
+	g_aClients[lClient].lLastActionTic = 0;
+	g_aClients[lClient].lBombCrateTic = -5*TICRATE;
 
 	SERVER_InitClientSRPData ( lClient );
 
@@ -2021,6 +2235,13 @@
 
 	pPlayer = &players[g_lCurrentClient];
 
+	// Read in which userinfo entries the client is sending us.
+	ulFlags = NETWORK_ReadShort( pByteStream );
+
+	// [K6] Not all user info properties trigger the kick.
+	if ( ulFlags == USERINFO_AIMDISTANCE )
+		bAllowKick = false;
+
 	if ( bAllowKick )
 	{
 		ulUserInfoInstance = ( ++g_aClients[g_lCurrentClient].ulLastUserInfoInstance % MAX_USERINFOINSTANCE_STORAGE );
@@ -2066,9 +2287,6 @@
 		}
 	}
 
-	// Read in which userinfo entries the client is sending us.
-	ulFlags = NETWORK_ReadShort( pByteStream );
-
 	// Read in the player's name.
 	if ( ulFlags & USERINFO_NAME )
 	{
@@ -2086,7 +2304,7 @@
 		if ( nameString.Compare( pPlayer->userinfo.netname ) != 0 )
 		{
 			bKickPlayer = true;
-			kickReason = "User name contains illegal characters." ;
+			kickReason = "Attempted to use illegal user name." ;
 		}
 
 		// [BB] It's extremely critical that we NEVER use the uncleaned player name!
@@ -2094,22 +2312,37 @@
 		// a tampered name.
 		strcpy( pPlayer->userinfo.netname, nameString.GetChars() );
 
+		char    szPlayerNameNoColor[MAXPLAYERNAME+1];
+		char    szOldPlayerNameNoColor[MAXPLAYERNAME+1];
+
+		sprintf( szPlayerNameNoColor, "%s", pPlayer->userinfo.netname );
+		sprintf( szOldPlayerNameNoColor, "%s", szOldPlayerName );
+
+		V_ColorizeString( szPlayerNameNoColor );
+		V_ColorizeString( szOldPlayerNameNoColor );
+		V_RemoveColorCodes( szPlayerNameNoColor );
+		V_RemoveColorCodes( szOldPlayerNameNoColor );
+
+		// [K6] No duplicate names on the server. In case of name collision, we find a free alternate name for the player.
+		unsigned int counter = 0;
+		while ( SERVER_GetPlayerIndexFromName( szPlayerNameNoColor, true, false, g_lCurrentClient ) != MAXPLAYERS )
+		{
+			counter++;
+			mysnprintf( pPlayer->userinfo.netname, MAXPLAYERNAME+1, "(%u) %s", counter, nameString.GetChars() );
+			V_CleanPlayerName( pPlayer->userinfo.netname ); // truncating the name might have exposed trailing crap and god knows what else
+			sprintf( szPlayerNameNoColor, "%s", pPlayer->userinfo.netname );
+			V_ColorizeString( szPlayerNameNoColor );
+			V_RemoveColorCodes( szPlayerNameNoColor );
+		}
+
 		if ( g_aClients[g_lCurrentClient].State == CLS_SPAWNED )
 		{
-			char	szPlayerNameNoColor[MAXPLAYERNAME+1];
-			char	szOldPlayerNameNoColor[MAXPLAYERNAME+1];
-
-			sprintf( szPlayerNameNoColor, "%s", pPlayer->userinfo.netname );
-			sprintf( szOldPlayerNameNoColor, "%s", szOldPlayerName );
-
-			V_ColorizeString( szPlayerNameNoColor );
-			V_ColorizeString( szOldPlayerNameNoColor );
-			V_RemoveColorCodes( szPlayerNameNoColor );
-			V_RemoveColorCodes( szOldPlayerNameNoColor );
-
 			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 );
@@ -2193,12 +2426,42 @@
 
 	if ( bKickPlayer )
 	{
+
+		// [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-ragefloods 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 rageflooding as the first zombie." );
+				C_DoCommand ( "freezeall" );
+				for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+				{
+					if ( playeringame[ulIdx] == false )
+						continue;
+					players[ulIdx].health = 100;
+				}
+				char commandString[50];
+				sprintf ( commandString, "wait %d;freezeall;changemap %s", 3 * TICRATE, level.mapname );
+				AddCommandString ( commandString );
+				return (false);
+			}
+		}
+
+
 		SERVER_KickPlayer( g_lCurrentClient, kickReason.GetChars() );
 		return ( false );
 	}
 
 	// Now send out the player's userinfo out to other players.
-	SERVERCOMMANDS_SetPlayerUserInfo( g_lCurrentClient, ulFlags, g_lCurrentClient, SVCF_SKIPTHISCLIENT );
+	// [K6] We always send the player name to reduce desyncs with consoleplayer in case the player was renamed.
+	// [K6] There is still a desync though when the client silently changes his name to the one it thinks he has :(
+	if ( !(ulFlags & USERINFO_NAME) )
+		ulFlags |= USERINFO_NAME;
+	SERVERCOMMANDS_SetPlayerUserInfo( g_lCurrentClient, ulFlags );
 
 	// Also, update the scoreboard.
 	SERVERCONSOLE_UpdatePlayerInfo( g_lCurrentClient, UDF_NAME );
@@ -2233,7 +2496,7 @@
 
 //*****************************************************************************
 //
-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 );
@@ -2250,11 +2513,19 @@
 		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 ));
@@ -2774,11 +3045,29 @@
 				}
 			}
 		}
+
+		// [K6] Add temporary bots one by one as needed.
+		if (( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) > 0 && sv_minplayers > 0 &&
+		 sv_minplayers > static_cast<int>( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) &&
+		 static_cast<int>( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) < sv_maxplayers && GAMEMODE_PreventPlayersFromJoining( ) == false &&
+		 !( level.flags2 & LEVEL2_ISLOBBY ) && !( level.flags2 & LEVEL2_NOBOTNODES )) &&
+		 ( sv_removetempbots == false || ( sv_removetempbots == true && SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) == 1 )))
+		{
+			C_DoCommand( "addbot" );
+		}
 	}
 
 	// Once every minute, update the level time.
 	if (( gametic % ( 60 * TICRATE )) == 0 )
-		SERVERCOMMANDS_SetMapTime( );
+	{
+ 		SERVERCOMMANDS_SetMapTime( );
+
+		if (SERVER_STATISTIC_GetTotalSecondsElapsed() > 24 * DAY && SERVER_CountPlayers(false) == 0)
+		{
+			Printf("----------Scheduled emergency shutdown after 24 days----------\n");
+			exit(-1);
+		}
+	}
 }
 
 //*****************************************************************************
@@ -2814,10 +3103,19 @@
 		// (which we do when the player is spawned in SERVER_ConnectNewPlayer)
 		if ( players[ulClient].userinfo.netname && ( SERVER_GetClient( ulClient )->State >= CLS_SPAWNED_BUT_NEEDS_AUTHENTICATION ) )
 		{
+			// [K6] Include the player's IP in server console.
 			if ( ( gametic - g_aClients[ulClient].ulLastCommandTic ) == ( CLIENT_TIMEOUT * 35 ) )
-				SERVER_Printf( PRINT_HIGH, "%s \\c-timed out.\n", players[ulClient].userinfo.netname );
+			{
+				for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+					SERVER_PrintfPlayer( PRINT_HIGH, ulIdx, "%s \\c-timed out.\n", players[ulClient].userinfo.netname );
+				Printf( "%s \\c-(%s) timed out.\n", players[ulClient].userinfo.netname, NETWORK_AddressToString( g_aClients[ulClient].Address ));
+			}
 			else
-				SERVER_Printf( PRINT_HIGH, "client %s \\c-disconnected.\n", players[ulClient].userinfo.netname );
+			{
+				for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+					SERVER_PrintfPlayer( PRINT_HIGH, ulIdx, "%s \\c-disconnected.\n", players[ulClient].userinfo.netname );
+				Printf( "%s \\c-(%s) disconnected.\n", players[ulClient].userinfo.netname, NETWORK_AddressToString( g_aClients[ulClient].Address ));
+			}
 		}
 		else
 			Printf( "%s \\c-disconnected.\n", NETWORK_AddressToString( g_aClients[ulClient].Address ));
@@ -2889,9 +3187,12 @@
 
 	memset( &g_aClients[ulClient].Address, 0, sizeof( g_aClients[ulClient].Address ));
 	g_aClients[ulClient].State = CLS_FREE;
-	g_aClients[ulClient].ulLastGameTic = 0;
+	g_aClients[ulClient].ulLastGameTime = 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 ))
@@ -2972,7 +3273,7 @@
 	// Save the time we pinged them at. The client's ping can be calculated
 	// subtracting the client's pong from this.
 	for ( ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
-		g_aClients[ulIdx].ulLastGameTic = I_MSTime( );
+		g_aClients[ulIdx].ulLastGameTime = I_MSTime( );
 }
 
 //*****************************************************************************
@@ -3493,6 +3794,12 @@
 
 		SERVER_PrintfPlayer( PRINT_HIGH, ulIdx, szKickString );
 	}
+
+	if ( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS ) < 2 && g_bGameFrozen )
+	{
+		C_DoCommand ( "freezeall" );
+		g_bGameFrozen = 0;
+	}
 }
 
 //*****************************************************************************
@@ -4196,6 +4503,53 @@
 		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." );
+				C_DoCommand ( "freezeall" );
+				for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+				{
+					if ( playeringame[ulIdx] == false )
+						continue;
+					players[ulIdx].health = 100;
+				}
+				char commandString[50];
+				sprintf ( commandString, "wait %d;freezeall;changemap %s", 3 * TICRATE, level.mapname );
+				AddCommandString ( commandString );
+				return (true);
+			}
+		}
+
+		// [K6] Handle WDI ragequitting. (is this still needed?)
+		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");
+			}
+		}
+
+		// [K6] Handle AOW spectate exploit.
+		else if (strstr(sv_hostname, "All Out War"))
+		{
+			if (players[g_lCurrentClient].mo == NULL)
+				Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname);
+			else if (g_aClients[g_lCurrentClient].lBombCrateTic + 5*TICRATE > gametic) // did the player just recently got bomb crate?
+			{
+				return ( false );
+			}
+		}
+
 		// Client has left the game.
 		SERVER_DisconnectClient( g_lCurrentClient, true, true );
 		break;
@@ -4280,6 +4634,54 @@
 		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." );
+				C_DoCommand ( "freezeall" );
+				for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ )
+				{
+					if ( playeringame[ulIdx] == false )
+						continue;
+					players[ulIdx].health = 100;
+				}
+				char commandString[50];
+				sprintf ( commandString, "wait %d;freezeall;changemap %s", 3 * TICRATE, level.mapname );
+				AddCommandString ( commandString );
+				return ( true );
+			}
+		}
+
+		// [K6] Handle WDI ragequitting. (is this still needed?)
+		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");
+			}
+		}
+
+		// [K6] Handle AOW spectate exploit.
+		else if (strstr(sv_hostname, "All Out War"))
+		{
+			if (players[g_lCurrentClient].mo == NULL)
+				Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname);
+			else if (g_aClients[g_lCurrentClient].lBombCrateTic + 5*TICRATE > gametic) // did the player just recently got bomb crate?
+			{
+				SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "You cannot spectate at this moment (averting exploit)\n" );
+				return ( false );
+			}
+		}
+
 		// Client now wishes to spectate.
 		return ( server_Spectate( pByteStream ));
 	case CLC_REQUESTJOIN:
@@ -4406,7 +4808,8 @@
 //*****************************************************************************
 //
 // [RC] Finds the first player (or, optionally, bot) with the given name; returns MAXPLAYERS if none were found.
-ULONG SERVER_GetPlayerIndexFromName( const char *pszName, bool bIgnoreColors, bool bReturnBots )
+// [K6] Added ulIgnoreIdx.
+ULONG SERVER_GetPlayerIndexFromName( const char *pszName, bool bIgnoreColors, bool bReturnBots, ULONG ulIgnoreIdx )
 {
 	char	szPlayerName[64];
 
@@ -4425,7 +4828,10 @@
 		if ( stricmp( bIgnoreColors ? szPlayerName : players[ulIdx].userinfo.netname, pszName ) == 0 )
 		{
 			if ( !players[ulIdx].bIsBot || bReturnBots )
-				return ulIdx;
+			{
+				if ( ulIdx != ulIgnoreIdx )
+					return ulIdx;
+			}
 		}
 	}
 
@@ -5038,6 +5444,7 @@
 //
 static bool server_UpdateClientPing( BYTESTREAM_s *pByteStream )
 {
+
 	ULONG	ulPing;
 
 	ulPing = NETWORK_ReadLong( pByteStream );
@@ -5047,7 +5454,7 @@
 	if ( ulPing > nowTime )
 		return false;
 
-	ULONG currentPing = (nowTime - ulPing);
+	ULONG currentPing = (nowTime - g_aClients[g_lCurrentClient].ulLastGameTime);
 	const ULONG ticLength = 1000 / TICRATE;
 	player_t *p = &players[g_lCurrentClient];
 	// [BB] Lag spike, reset the averaging.
@@ -5161,6 +5568,9 @@
 {
 	ULONG	ulIdx;
 
+	if ( server_CheckForClientCommandFlood ( g_lCurrentClient ) == true )
+		return ( true );
+
 	// Already a spectator!
 	if ( PLAYER_IsTrueSpectator( &players[g_lCurrentClient] ))
 	{
@@ -5207,6 +5617,9 @@
 	// [BB/Spleen] Read in the client's gametic.
 	ulGametic = NETWORK_ReadLong( pByteStream );
 
+	if ( server_CheckForClientCommandFlood ( g_lCurrentClient ) == true )
+		return ( true );
+
 	// Player can't rejoin game if he's not spectating!
 	if (( playeringame[g_lCurrentClient] == false ) || ( players[g_lCurrentClient].bSpectating == false ))
 		return ( false );
@@ -5215,8 +5628,27 @@
 	if (( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_DEADSPECTATORS ) && ( players[g_lCurrentClient].bDeadSpectator ))
 		return ( false );
 
+
+	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 we're forcing a join password, prevent him from joining if it doesn't match.
-	if ( server_CheckJoinPassword( clientJoinPassword ) == false )
+	else if ( server_CheckJoinPassword( clientJoinPassword ) == false )
 		return ( false );
 
 	// [BB] Possibly the player needs to login before joining.
@@ -5278,6 +5710,13 @@
 	// 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 )
@@ -5473,6 +5912,8 @@
 	else
 	{
 		SERVER_Printf( PRINT_HIGH, "%s \\c-joined the \\c%c%s \\c-team.\n", players[g_lCurrentClient].userinfo.netname, V_GetColorChar( TEAM_GetTextColor( players[g_lCurrentClient].ulTeam )), TEAM_GetName( players[g_lCurrentClient].ulTeam ));
+		// [K6] Here we need to add 1 to count the newly joined player. Shrug.
+		BOTS_RemoveTempBots( true );
 	}
 
 	if ( players[g_lCurrentClient].mo )
@@ -6250,6 +6691,36 @@
 	return false;
 }
 
+
+//*****************************************************************************
+// [K6]
+extern std::vector<MAPROTATIONENTRY_t>	g_MapRotationEntries;
+static void SendMaplistToPlayer( ULONG ulPlayer )
+{
+	if ( g_MapRotationEntries.size( ) == 0 )
+		SERVER_PrintfPlayer( PRINT_HIGH, ulPlayer, "The map rotation list is empty.\n" );
+	else
+	{
+		SERVER_PrintfPlayer( PRINT_HIGH, ulPlayer, "Map rotation list: \n" );
+		for ( ULONG ulIdx = 0; ulIdx < g_MapRotationEntries.size( ); ulIdx++ )
+			SERVER_PrintfPlayer( PRINT_HIGH, ulPlayer, "%lu. %s - %s\n", ulIdx + 1, g_MapRotationEntries[ulIdx].pMap->mapname, g_MapRotationEntries[ulIdx].pMap->LookupLevelName( ).GetChars( ));
+	}
+}
+//*****************************************************************************
+// [K6]
+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
 
@@ -6617,3 +7088,218 @@
 	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
+}
diff -r 3e26f5eca137 src/sv_main.h
--- a/src/sv_main.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/sv_main.h	Wed Oct 08 11:31:35 2014 +0200
@@ -218,7 +218,7 @@
 	ULONG			ulLastCommandTic;
 
 	// Used for calculating pings.
-    ULONG			ulLastGameTic;
+    ULONG			ulLastGameTime;
 
 	// Can client remotely control server?
 	bool			bRCONAccess;
@@ -297,9 +297,15 @@
 	// [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;
 
+	// [K6] When did the player receive a bomb crate on AOW?
+	LONG			lBombCrateTic;
+
 	// [BB] Buffer storing all movement commands received from the client we haven't executed yet.
 	TArray<CLIENT_MOVE_COMMAND_s>	MoveCMDs;
 
@@ -372,7 +378,7 @@
 ULONG		SERVER_CalcNumConnectedClients( void );
 ULONG		SERVER_CalcNumPlayers( void );
 ULONG		SERVER_CountPlayers( bool bCountBots );
-ULONG		SERVER_CalcNumNonSpectatingPlayers( ULONG ulExcludePlayer );
+ULONG		SERVER_CalcNumNonSpectatingPlayers( ULONG ulExcludePlayer, bool bCountBots = true );
 void		SERVER_CheckTimeouts( void );
 void		SERVER_GetPackets( void );
 void		SERVER_SendChatMessage( ULONG ulPlayer, ULONG ulMode, const char *pszString );
@@ -384,7 +390,7 @@
 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 );
@@ -431,7 +437,7 @@
 void		SERVER_ErrorCleanup( void );
 void		SERVER_ParsePacket( BYTESTREAM_s *pByteStream );
 bool		SERVER_ProcessCommand( LONG lCommand, BYTESTREAM_s *pByteStream );
-ULONG		SERVER_GetPlayerIndexFromName( const char *pszName, bool bIgnoreColors, bool bReturnBots );
+ULONG		SERVER_GetPlayerIndexFromName( const char *pszName, bool bIgnoreColors, bool bReturnBots, ULONG ulIgnoreIdx = MAXPLAYERS );
 LONG		SERVER_GetCurrentClient( void );
 void		SERVER_GiveInventoryToPlayer( const player_t *player, AInventory *pInventory );
 void		SERVER_HandleWeaponStateJump( ULONG ulPlayer, FState *pState, LONG lPosition );
@@ -468,6 +474,14 @@
 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
 
@@ -485,10 +499,12 @@
 EXTERN_CVAR( Bool, sv_timestamp );
 EXTERN_CVAR( Int, sv_timestampformat );
 EXTERN_CVAR( Int, sv_colorstripmethod );
-EXTERN_CVAR( Bool, sv_disallowbots );
 EXTERN_CVAR( Bool, sv_minimizetosystray )
 EXTERN_CVAR( Int, sv_queryignoretime )
 EXTERN_CVAR( Bool, sv_forcelogintojoin )
+EXTERN_CVAR( Bool, sv_passwordlist );
+EXTERN_CVAR( Bool, sv_joinpasswordlist );
+EXTERN_CVAR( String, sv_passwordlistfile );
 
 // From sv_master.cpp
 EXTERN_CVAR( Bool, sv_updatemaster );
diff -r 3e26f5eca137 src/sv_master.cpp
--- a/src/sv_master.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/sv_master.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -48,6 +48,9 @@
 //
 //-----------------------------------------------------------------------------
 
+#ifndef _WIN32
+#include <sys/utsname.h>
+#endif
 #include "networkheaders.h"
 #include "c_dispatch.h"
 #include "cooperative.h"
@@ -67,6 +70,8 @@
 #include "sv_ban.h"
 #include "version.h"
 
+#include "v_text.h"
+
 //--------------------------------------------------------------------------------------------------------------------------------------------------
 //-- VARIABLES -------------------------------------------------------------------------------------------------------------------------------------
 //--------------------------------------------------------------------------------------------------------------------------------------------------
@@ -88,6 +93,8 @@
 
 extern	NETADDRESS_s		g_LocalAddress;
 
+char versionWithOS[256];
+
 //*****************************************************************************
 //	CONSOLE VARIABLES
 
@@ -123,6 +130,15 @@
 	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 +305,25 @@
 		}
 	
 		// 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 +342,8 @@
 	// 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 +438,10 @@
 		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 );
@@ -494,7 +511,12 @@
 			if ( playeringame[ulIdx] == false )
 				continue;
 
-			NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, players[ulIdx].userinfo.netname );
+			// [K6/Dusk] IDE can't cope with color codes in "\c[bla]" format... so we'll have to get rid of them here.
+			FString cleanName (players[ulIdx].userinfo.netname);
+			if (cleanName.IndexOf (g_NewColors) != -1)
+				V_RemoveColorCodes(cleanName);
+
+			NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, cleanName.GetChars() );
 			if ( GAMEMODE_GetFlags(GAMEMODE_GetCurrentMode()) & GMF_PLAYERSEARNPOINTS )
 				NETWORK_WriteShort( &g_MasterServerBuffer.ByteStream, players[ulIdx].lPointCount );
 			else if ( GAMEMODE_GetFlags(GAMEMODE_GetCurrentMode()) & GMF_PLAYERSEARNWINS )
diff -r 3e26f5eca137 src/sv_rcon.cpp
--- a/src/sv_rcon.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/sv_rcon.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -156,6 +156,10 @@
 {
 	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:
diff -r 3e26f5eca137 src/timidity/timidity.h
--- a/src/timidity/timidity.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/timidity/timidity.h	Wed Oct 08 11:31:35 2014 +0200
@@ -117,7 +117,7 @@
   #define PI 3.14159265358979323846
 #endif
 
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#if defined(__GNUC__) && !defined(__clang__) && (defined(__i386__) || defined(__x86_64__))
 // [RH] MinGW's pow() function is terribly slow compared to VC8's
 // (I suppose because it's using an old version from MSVCRT.DLL).
 // On an Opteron running x86-64 Linux, this also ended up being about
diff -r 3e26f5eca137 src/v_text.cpp
--- a/src/v_text.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/v_text.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -713,7 +713,8 @@
 	}
 		
 	// Check the length again, as characters were removed.
-	if ( ulNonWhitespace < 3 )
+	// [K6] The player's name really shouldn't be "<server>"...
+	if ( ulNonWhitespace < 3 || stricmp( "<server>", szColorlessName ) == 0 )
 		strcpy( pszStart, "Player" );
 }
 
diff -r 3e26f5eca137 src/v_text.h
--- a/src/v_text.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/v_text.h	Wed Oct 08 11:31:35 2014 +0200
@@ -74,6 +74,9 @@
 #define TEXTCOLOR_CHAT			"\034*"
 #define TEXTCOLOR_TEAMCHAT		"\034!"
 
+// [K6] New color codes start sequence.
+const char g_NewColors[] = { TEXTCOLOR_ESCAPE, '[', '\0' };
+
 // [BC] New text functions.
 void	V_ApplyCharArrayFunctionToFString ( FString &String, void (*CharArrayFunction) ( char *pszString ) );
 void	V_ColorizeString( char *pszString );
diff -r 3e26f5eca137 src/version.h
--- a/src/version.h	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/version.h	Wed Oct 08 11:31:35 2014 +0200
@@ -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 "141005-1947M"
+#define SVN_REVISION_NUMBER 1412538424
+#define HG_REVISION_HASH_STRING "-"
+#define KPATCH_BUILD "46"
+
 
 /** Lots of different version numbers **/
 
@@ -51,7 +56,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
diff -r 3e26f5eca137 src/w_wad.cpp
--- a/src/w_wad.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/w_wad.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -499,6 +499,7 @@
 // [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 @@
 		// 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 @@
 		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 @@
 			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 @@
 			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 @@
 		// 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 @@
 		{
 			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 @@
 		// 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 @@
 		uppercopy(singleinfo.Name, name);
 		NumLumps++;
 	}
-	Printf ("\n");
+	Printf ("%s\n", infoString.GetChars());
 
 	// Fill in lumpinfo
 	if (header.magic[0] != RFF_ID &&
diff -r 3e26f5eca137 src/win32/i_system.cpp
--- a/src/win32/i_system.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/win32/i_system.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -107,6 +107,8 @@
 
 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 @@
 // [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 @@
 				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 @@
 				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)
diff -r 3e26f5eca137 src/x86.cpp
--- a/src/x86.cpp	Sun Oct 05 21:47:04 2014 +0200
+++ b/src/x86.cpp	Wed Oct 08 11:31:35 2014 +0200
@@ -212,18 +212,19 @@
 			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());
 	}
 }
 
