diff -Nru tremulous/src/game/g_admin.h tremulous.temp/src/game/g_admin.h
--- tremulous/src/game/g_admin.h	2007-04-20 19:29:05.000000000 +0200
+++ tremulous.temp/src/game/g_admin.h	2007-04-20 19:29:35.000000000 +0200
@@ -70,6 +70,8 @@
 #define ADMF_IMMUTABLE '!'
 #define ADMF_INCOGNITO '@'
 
+#define ADMF_VIP '#'
+
 #define MAX_ADMIN_LISTITEMS 20
 #define MAX_ADMIN_SHOWBANS 10
 
diff -Nru tremulous/src/game/g_client.c tremulous.temp/src/game/g_client.c
--- tremulous/src/game/g_client.c	2007-04-20 19:29:05.000000000 +0200
+++ tremulous.temp/src/game/g_client.c	2007-04-20 19:29:35.000000000 +0200
@@ -1190,6 +1190,8 @@
   char      ip[ 16 ] = {""};
   char      reason[ MAX_STRING_CHARS ] = {""};
   int       i;
+  int       used_privateSlots;
+  int       privateClients;
 
   ent = &g_entities[ clientNum ];
 
@@ -1204,6 +1206,26 @@
     return va( "%s", reason );
   }
 
+  privateClients = trap_Cvar_VariableIntegerValue( "sv_privateClients" );
+  if( firstTime && clientNum >= privateClients && !G_admin_guid_permission( value, ADMF_VIP ) )
+  {
+    // not flag ADMF_VIP, not already connected player after mapchange, and not privateClient...
+    // may be refused, because too many players in game
+    used_privateSlots = 0;
+    for( i = 0 ; i < privateClients; i++ )
+    {
+      if( level.clients[ i ].pers.connected != CON_DISCONNECTED )
+      {
+        used_privateSlots++;
+      }
+    }
+     
+    if( ( level.numConnectedClients - used_privateSlots ) >= ( level.maxclients - privateClients - g_hiddenClients.integer ) )
+    {
+      // not flag ADMF_VIP and maxPlayers exceeded
+      return "Server is full";
+    }
+  }
 
   // IP filtering
   // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500
diff -Nru tremulous/src/game/g_local.h tremulous.temp/src/game/g_local.h
--- tremulous/src/game/g_local.h	2007-04-20 19:29:05.000000000 +0200
+++ tremulous.temp/src/game/g_local.h	2007-04-20 19:29:35.000000000 +0200
@@ -1180,6 +1180,8 @@
 
 extern  vmCvar_t  g_privateMessages;
 
+extern  vmCvar_t  g_hiddenClients;
+
 extern  vmCvar_t  g_dretchPunt;
 
 void      trap_Printf( const char *fmt );
diff -Nru tremulous/src/game/g_main.c tremulous.temp/src/game/g_main.c
--- tremulous/src/game/g_main.c	2007-04-20 19:29:05.000000000 +0200
+++ tremulous.temp/src/game/g_main.c	2007-04-20 19:29:35.000000000 +0200
@@ -140,6 +140,8 @@
 
 vmCvar_t  g_tag;
 
+vmCvar_t  g_hiddenClients;
+
 vmCvar_t  g_dretchPunt;
 
 vmCvar_t  g_allowShare;
@@ -268,6 +270,8 @@
   { &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse  },
   
   { &g_tag, "g_tag", "main", CVAR_INIT, 0, qfalse },
+
+  { &g_hiddenClients, "g_hiddenClients", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },
   
   { &g_dretchPunt, "g_dretchPunt", "1", CVAR_ARCHIVE, 0, qfalse  },
   
diff -Nru tremulous/src/qcommon/cvar.c tremulous.temp/src/qcommon/cvar.c
--- tremulous/src/qcommon/cvar.c	2007-04-20 19:29:00.000000000 +0200
+++ tremulous.temp/src/qcommon/cvar.c	2007-04-20 19:29:45.000000000 +0200
@@ -827,7 +827,13 @@
 
 	for (var = cvar_vars ; var ; var = var->next) {
 		if (var->flags & bit) {
-			Info_SetValueForKey (info, var->name, var->string);
+			if (!strcmp (var->name, "sv_maxclients")) {
+				cvar_t *g_hiddenClients = Cvar_Get ("g_hiddenClients", "0", 0);
+				Info_SetValueForKey (info, var->name,
+					va ("%i", var->integer - g_hiddenClients->integer));
+			} else {
+				Info_SetValueForKey (info, var->name, var->string);
+			}
 		}
 	}
 	return info;
diff -Nru tremulous/src/server/sv_main.c tremulous.temp/src/server/sv_main.c
--- tremulous/src/server/sv_main.c	2007-04-20 19:29:05.000000000 +0200
+++ tremulous.temp/src/server/sv_main.c	2007-04-20 19:29:35.000000000 +0200
@@ -398,6 +398,7 @@
 	int		i, count;
 	char	*gamedir;
 	char	infostring[MAX_INFO_STRING];
+	cvar_t	*g_hiddenClients;
 
 	/*
 	 * Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led
@@ -416,6 +417,12 @@
 		}
 	}
 
+   g_hiddenClients = Cvar_Get( "g_hiddenClients", "0", 0 );
+   if( g_hiddenClients->integer > sv_maxclients->integer - sv_privateClients->integer )
+   {
+      Cvar_Set( "g_hiddenClients", sv_maxclients->string - sv_privateClients->integer );
+   }
+
 	infostring[0] = 0;
 
 	// echo back the parameter to status. so servers can use it as a challenge
@@ -427,7 +434,7 @@
 	Info_SetValueForKey( infostring, "mapname", sv_mapname->string );
 	Info_SetValueForKey( infostring, "clients", va("%i", count) );
 	Info_SetValueForKey( infostring, "sv_maxclients", 
-		va("%i", sv_maxclients->integer - sv_privateClients->integer ) );
+		va("%i", sv_maxclients->integer - sv_privateClients->integer - g_hiddenClients->integer ) );
 	Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
 
 	if( sv_minPing->integer ) {
