plugins/reservedslots.sp
author Nicholas Hastings <skamonkey@gmail.com>
Thu Jul 04 11:28:32 2013 -0400 (2013-07-04)
changeset 3931 576fa42454dc
parent 3697 735dc2e19807
child 3961 0dc477c29792
permissions -rw-r--r--
Fixed build for real.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod Reserved Slots Plugin
     5  * Provides basic reserved slots.
     6  *
     7  * SourceMod (C)2004-2008 AlliedModders LLC.  All rights reserved.
     8  * =============================================================================
     9  *
    10  * This program is free software; you can redistribute it and/or modify it under
    11  * the terms of the GNU General Public License, version 3.0, as published by the
    12  * Free Software Foundation.
    13  * 
    14  * This program is distributed in the hope that it will be useful, but WITHOUT
    15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    17  * details.
    18  *
    19  * You should have received a copy of the GNU General Public License along with
    20  * this program.  If not, see <http://www.gnu.org/licenses/>.
    21  *
    22  * As a special exception, AlliedModders LLC gives you permission to link the
    23  * code of this program (as well as its derivative works) to "Half-Life 2," the
    24  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    25  * by the Valve Corporation.  You must obey the GNU General Public License in
    26  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    27  * this exception to all derivative works.  AlliedModders LLC defines further
    28  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    29  * or <http://www.sourcemod.net/license.php>.
    30  *
    31  * Version: $Id$
    32  */
    33 
    34 #pragma semicolon 1
    35 
    36 #include <sourcemod>
    37 
    38 public Plugin:myinfo = 
    39 {
    40 	name = "Reserved Slots",
    41 	author = "AlliedModders LLC",
    42 	description = "Provides basic reserved slots",
    43 	version = SOURCEMOD_VERSION,
    44 	url = "http://www.sourcemod.net/"
    45 };
    46 
    47 new g_adminCount = 0;
    48 new bool:g_isAdmin[MAXPLAYERS+1];
    49 
    50 /* Handles to convars used by plugin */
    51 new Handle:sm_reserved_slots;
    52 new Handle:sm_hide_slots;
    53 new Handle:sv_visiblemaxplayers;
    54 new Handle:sm_reserve_type;
    55 new Handle:sm_reserve_maxadmins;
    56 new Handle:sm_reserve_kicktype;
    57 
    58 new g_SDKVersion;
    59 new g_SourceTV = -1;
    60 new g_Replay = -1;
    61 
    62 enum KickType
    63 {
    64 	Kick_HighestPing,
    65 	Kick_HighestTime,
    66 	Kick_Random,	
    67 };
    68 
    69 public OnPluginStart()
    70 {
    71 	LoadTranslations("reservedslots.phrases");
    72 	
    73 	sm_reserved_slots = CreateConVar("sm_reserved_slots", "0", "Number of reserved player slots", 0, true, 0.0);
    74 	sm_hide_slots = CreateConVar("sm_hide_slots", "0", "If set to 1, reserved slots will hidden (subtracted from the max slot count)", 0, true, 0.0, true, 1.0);
    75 	sv_visiblemaxplayers = FindConVar("sv_visiblemaxplayers");
    76 	sm_reserve_type = CreateConVar("sm_reserve_type", "0", "Method of reserving slots", 0, true, 0.0, true, 2.0);
    77 	sm_reserve_maxadmins = CreateConVar("sm_reserve_maxadmins", "1", "Maximum amount of admins to let in the server with reserve type 2", 0, true, 0.0);
    78 	sm_reserve_kicktype = CreateConVar("sm_reserve_kicktype", "0", "How to select a client to kick (if appropriate)", 0, true, 0.0, true, 2.0);
    79 	
    80 	HookConVarChange(sm_reserved_slots, SlotCountChanged);
    81 	HookConVarChange(sm_hide_slots, SlotHideChanged);
    82 	
    83 	g_SDKVersion = GuessSDKVersion();
    84 	
    85 	if (g_SDKVersion == SOURCE_SDK_EPISODE2VALVE)
    86 	{
    87 		for (new i = 1; i <= MaxClients; i++)
    88 		{
    89 			if (!IsClientConnected(i))
    90 				continue;
    91 			
    92 			if (IsClientSourceTV(i))
    93 			{
    94 				g_SourceTV = i;
    95 			}
    96 			else if (IsClientReplay(i))
    97 			{
    98 				g_Replay = i;
    99 			}
   100 		}
   101 	}
   102 }
   103 
   104 public OnPluginEnd()
   105 {
   106 	/* 	If the plugin has been unloaded, reset visiblemaxplayers. In the case of the server shutting down this effect will not be visible */
   107 	ResetVisibleMax();
   108 }
   109 
   110 public OnMapStart()
   111 {
   112 	if (GetConVarBool(sm_hide_slots))
   113 	{		
   114 		SetVisibleMaxSlots(GetClientCount(false), GetMaxHumanPlayers() - GetConVarInt(sm_reserved_slots));
   115 	}
   116 }
   117 
   118 public OnConfigsExecuted()
   119 {
   120 	if (GetConVarBool(sm_hide_slots))
   121 	{
   122 		SetVisibleMaxSlots(GetClientCount(false), GetMaxHumanPlayers() - GetConVarInt(sm_reserved_slots));
   123 	}	
   124 }
   125 
   126 public Action:OnTimedKick(Handle:timer, any:client)
   127 {	
   128 	if (!client || !IsClientInGame(client))
   129 	{
   130 		return Plugin_Handled;
   131 	}
   132 	
   133 	KickClient(client, "%T", "Slot reserved", client);
   134 	
   135 	if (GetConVarBool(sm_hide_slots))
   136 	{				
   137 		SetVisibleMaxSlots(GetClientCount(false), GetMaxHumanPlayers() - GetConVarInt(sm_reserved_slots));
   138 	}
   139 	
   140 	return Plugin_Handled;
   141 }
   142 
   143 public OnClientPostAdminCheck(client)
   144 {
   145 	if (g_SDKVersion == SOURCE_SDK_EPISODE2VALVE)
   146 	{
   147 		if (IsClientSourceTV(client))
   148 		{
   149 			g_SourceTV = client;
   150 		}
   151 		else if (IsClientReplay(client))
   152 		{
   153 			g_Replay = client;
   154 		}
   155 	}
   156 	
   157 	new reserved = GetConVarInt(sm_reserved_slots);
   158 
   159 	if (reserved > 0)
   160 	{
   161 		new clients = GetClientCount(false);
   162 		new limit = GetMaxHumanPlayers() - reserved;
   163 		new flags = GetUserFlagBits(client);
   164 		
   165 		new type = GetConVarInt(sm_reserve_type);
   166 		
   167 		if (type == 0)
   168 		{
   169 			if (clients <= limit || IsFakeClient(client) || flags & ADMFLAG_ROOT || flags & ADMFLAG_RESERVATION)
   170 			{
   171 				if (GetConVarBool(sm_hide_slots))
   172 				{
   173 					SetVisibleMaxSlots(clients, limit);
   174 				}
   175 				
   176 				return;
   177 			}
   178 			
   179 			/* Kick player because there are no public slots left */
   180 			CreateTimer(0.1, OnTimedKick, client);
   181 		}
   182 		else if (type == 1)
   183 		{	
   184 			if (clients > limit)
   185 			{
   186 				if (flags & ADMFLAG_ROOT || flags & ADMFLAG_RESERVATION)
   187 				{
   188 					new target = SelectKickClient();
   189 						
   190 					if (target)
   191 					{
   192 						/* Kick public player to free the reserved slot again */
   193 						CreateTimer(0.1, OnTimedKick, target);
   194 					}
   195 				}
   196 				else
   197 				{				
   198 					/* Kick player because there are no public slots left */
   199 					CreateTimer(0.1, OnTimedKick, client);
   200 				}
   201 			}
   202 		}
   203 		else if (type == 2)
   204 		{
   205 			if (flags & ADMFLAG_ROOT || flags & ADMFLAG_RESERVATION)
   206 			{
   207 				g_adminCount++;
   208 				g_isAdmin[client] = true;
   209 			}
   210 			
   211 			if (clients > limit && g_adminCount < GetConVarInt(sm_reserve_maxadmins))
   212 			{
   213 				/* Server is full, reserved slots aren't and client doesn't have reserved slots access */
   214 				
   215 				if (g_isAdmin[client])
   216 				{
   217 					new target = SelectKickClient();
   218 						
   219 					if (target)
   220 					{
   221 						/* Kick public player to free the reserved slot again */
   222 						CreateTimer(0.1, OnTimedKick, target);
   223 					}
   224 				}
   225 				else
   226 				{				
   227 					/* Kick player because there are no public slots left */
   228 					CreateTimer(0.1, OnTimedKick, client);
   229 				}		
   230 			}
   231 		}
   232 	}
   233 }
   234 
   235 public OnClientDisconnect_Post(client)
   236 {
   237 	if (g_SDKVersion == SOURCE_SDK_EPISODE2VALVE)
   238 	{
   239 		if (client == g_SourceTV)
   240 		{
   241 			g_SourceTV = -1;
   242 		}
   243 		else if (client == g_Replay)
   244 		{
   245 			g_Replay = -1;
   246 		}
   247 	}
   248 	
   249 	if (GetConVarBool(sm_hide_slots))
   250 	{		
   251 		SetVisibleMaxSlots(GetClientCount(false), GetMaxHumanPlayers() - GetConVarInt(sm_reserved_slots));
   252 	}
   253 	
   254 	if (g_isAdmin[client])
   255 	{
   256 		g_adminCount--;
   257 		g_isAdmin[client] = false;	
   258 	}
   259 }
   260 
   261 public SlotCountChanged(Handle:convar, const String:oldValue[], const String:newValue[])
   262 {
   263 	/* Reserved slots or hidden slots have been disabled - reset sv_visiblemaxplayers */
   264 	new slotcount = GetConVarInt(convar);
   265 	if (slotcount == 0)
   266 	{
   267 		ResetVisibleMax();
   268 	}
   269 	else if (GetConVarBool(sm_hide_slots))
   270 	{
   271 		SetVisibleMaxSlots(GetClientCount(false), GetMaxHumanPlayers() - slotcount);
   272 	}
   273 }
   274 
   275 public SlotHideChanged(Handle:convar, const String:oldValue[], const String:newValue[])
   276 {
   277 	/* Reserved slots or hidden slots have been disabled - reset sv_visiblemaxplayers */
   278 	if (!GetConVarBool(convar))
   279 	{
   280 		ResetVisibleMax();
   281 	}
   282 	else
   283 	{
   284 		SetVisibleMaxSlots(GetClientCount(false), GetMaxHumanPlayers() - GetConVarInt(sm_reserved_slots));
   285 	}
   286 }
   287 
   288 SetVisibleMaxSlots(clients, limit)
   289 {
   290 	new num = clients;
   291 	
   292 	if (clients == GetMaxHumanPlayers())
   293 	{
   294 		num = GetMaxHumanPlayers();
   295 	} else if (clients < limit) {
   296 		num = limit;
   297 	}
   298 	
   299 	if (g_SDKVersion == SOURCE_SDK_EPISODE2VALVE)
   300 	{
   301 		if (g_SourceTV > -1)
   302 		{
   303 			--num;
   304 		}
   305 		
   306 		if (g_Replay > -1)
   307 		{
   308 			--num;
   309 		}
   310 	}
   311 	
   312 	SetConVarInt(sv_visiblemaxplayers, num);
   313 }
   314 
   315 ResetVisibleMax()
   316 {
   317 	SetConVarInt(sv_visiblemaxplayers, -1);
   318 }
   319 
   320 SelectKickClient()
   321 {
   322 	new KickType:type = KickType:GetConVarInt(sm_reserve_kicktype);
   323 	
   324 	new Float:highestValue;
   325 	new highestValueId;
   326 	
   327 	new Float:highestSpecValue;
   328 	new highestSpecValueId;
   329 	
   330 	new bool:specFound;
   331 	
   332 	new Float:value;
   333 	
   334 	for (new i=1; i<=MaxClients; i++)
   335 	{	
   336 		if (!IsClientConnected(i))
   337 		{
   338 			continue;
   339 		}
   340 	
   341 		new flags = GetUserFlagBits(i);
   342 		
   343 		if (IsFakeClient(i) || flags & ADMFLAG_ROOT || flags & ADMFLAG_RESERVATION || CheckCommandAccess(i, "sm_reskick_immunity", ADMFLAG_RESERVATION, false))
   344 		{
   345 			continue;
   346 		}
   347 		
   348 		value = 0.0;
   349 			
   350 		if (IsClientInGame(i))
   351 		{
   352 			if (type == Kick_HighestPing)
   353 			{
   354 				value = GetClientAvgLatency(i, NetFlow_Outgoing);
   355 			}
   356 			else if (type == Kick_HighestTime)
   357 			{
   358 				value = GetClientTime(i);
   359 			}
   360 			else
   361 			{
   362 				value = GetRandomFloat(0.0, 100.0);
   363 			}
   364 
   365 			if (IsClientObserver(i))
   366 			{			
   367 				specFound = true;
   368 				
   369 				if (value > highestSpecValue)
   370 				{
   371 					highestSpecValue = value;
   372 					highestSpecValueId = i;
   373 				}
   374 			}
   375 		}
   376 		
   377 		if (value >= highestValue)
   378 		{
   379 			highestValue = value;
   380 			highestValueId = i;
   381 		}
   382 	}
   383 	
   384 	if (specFound)
   385 	{
   386 		return highestSpecValueId;
   387 	}
   388 	
   389 	return highestValueId;
   390 }