diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp index 1a0ccdc3..fac8d004 100644 --- a/core/PlayerManager.cpp +++ b/core/PlayerManager.cpp @@ -45,6 +45,8 @@ PlayerManager g_Players; bool g_OnMapStarted = false; +IForward *PreAdminCheck = NULL; +IForward *PostAdminCheck = NULL; SH_DECL_HOOK5(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, edict_t *, const char *, const char *, char *, int); SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, const char *); @@ -112,6 +114,9 @@ void PlayerManager::OnSourceModAllInitialized() m_clauth = g_Forwards.CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String); m_onActivate = g_Forwards.CreateForward("OnServerLoad", ET_Ignore, 0, NULL); m_onActivate2 = g_Forwards.CreateForward("OnMapStart", ET_Ignore, 0, NULL); + + PreAdminCheck = g_Forwards.CreateForward("OnClientPreAdminCheck", ET_Event, 1, p1); + PostAdminCheck = g_Forwards.CreateForward("OnClientPostAdminCheck", ET_Ignore, 1, p1); } void PlayerManager::OnSourceModShutdown() @@ -135,6 +140,9 @@ void PlayerManager::OnSourceModShutdown() g_Forwards.ReleaseForward(m_onActivate); g_Forwards.ReleaseForward(m_onActivate2); + g_Forwards.ReleaseForward(PreAdminCheck); + g_Forwards.ReleaseForward(PostAdminCheck); + delete [] m_Players; } @@ -751,6 +759,7 @@ CPlayer::CPlayer() m_Admin = INVALID_ADMIN_ID; m_TempAdmin = false; m_Info = NULL; + m_bAdminCheckSignalled = false; m_LastPassword.clear(); } @@ -760,6 +769,7 @@ void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity) m_Name.assign(name); m_Ip.assign(ip); m_pEdict = pEntity; + m_iIndex = engine->IndexOfEdict(pEntity); char ip2[24], *ptr; strncopy(ip2, ip, sizeof(ip2)); @@ -791,7 +801,7 @@ void CPlayer::Connect() if (m_IsAuthorized) { - DoBasicAdminChecks(); + DoPostConnectAuthorization(); } } @@ -817,6 +827,7 @@ void CPlayer::Disconnect() m_AuthID.clear(); m_pEdict = NULL; m_Info = NULL; + m_bAdminCheckSignalled = false; } void CPlayer::SetName(const char *name) @@ -902,8 +913,7 @@ void CPlayer::DumpAdmin(bool deleting) void CPlayer::Kick(const char *str) { - int client = engine->IndexOfEdict(m_pEdict); - INetChannel *pNetChan = static_cast(engine->GetPlayerNetInfo(client)); + INetChannel *pNetChan = static_cast(engine->GetPlayerNetInfo(m_iIndex)); IClient *pClient = static_cast(pNetChan->GetMsgHandler()); pClient->Disconnect("%s", str); } @@ -912,10 +922,49 @@ void CPlayer::Authorize_Post() { if (m_IsInGame) { - DoBasicAdminChecks(); + DoPostConnectAuthorization(); } } +void CPlayer::DoPostConnectAuthorization() +{ + cell_t result; + PreAdminCheck->PushCell(m_iIndex); + PreAdminCheck->Execute(&result); + + /* Defer, for better or worse */ + if ((ResultType)result >= Pl_Handled) + { + return; + } + + /* Sanity check */ + if (!IsConnected()) + { + return; + } + + /* Otherwise, go ahead and do admin checks */ + DoBasicAdminChecks(); + + /* Send the notification out */ + NotifyPostAdminChecks(); +} + +void CPlayer::NotifyPostAdminChecks() +{ + if (m_bAdminCheckSignalled) + { + return; + } + + /* Block beforehand so they can't double-call */ + m_bAdminCheckSignalled = true; + + PostAdminCheck->PushCell(m_iIndex); + PostAdminCheck->Execute(NULL); +} + void CPlayer::DoBasicAdminChecks() { if (GetAdminId() != INVALID_ADMIN_ID) diff --git a/core/PlayerManager.h b/core/PlayerManager.h index fea682f1..457b1d18 100644 --- a/core/PlayerManager.h +++ b/core/PlayerManager.h @@ -62,8 +62,10 @@ public: void SetAdminId(AdminId id, bool temporary); AdminId GetAdminId(); void Kick(const char *str); -public: IPlayerInfo *GetPlayerInfo(); +public: + void NotifyPostAdminChecks(); + void DoBasicAdminChecks(); private: void Initialize(const char *name, const char *ip, edict_t *pEntity); void Connect(); @@ -72,7 +74,7 @@ private: void DumpAdmin(bool deleting); void Authorize(const char *auth); void Authorize_Post(); - void DoBasicAdminChecks(); + void DoPostConnectAuthorization(); private: bool m_IsConnected; bool m_IsInGame; @@ -86,6 +88,8 @@ private: edict_t *m_pEdict; IPlayerInfo *m_Info; String m_LastPassword; + bool m_bAdminCheckSignalled; + int m_iIndex; }; class PlayerManager : diff --git a/core/smn_console.cpp b/core/smn_console.cpp index e68bbcd8..ee33f357 100644 --- a/core/smn_console.cpp +++ b/core/smn_console.cpp @@ -702,10 +702,15 @@ static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params) static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params) { + g_SourceMod.SetGlobalTarget(LANG_SERVER); + char buffer[1024]; size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1); - g_SourceMod.SetGlobalTarget(LANG_SERVER); + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } /* One byte for null terminator, one for newline */ buffer[len++] = '\n'; @@ -718,10 +723,15 @@ static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params) static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params) { + g_SourceMod.SetGlobalTarget(LANG_SERVER); + char buffer[1024]; size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1); - g_SourceMod.SetGlobalTarget(LANG_SERVER); + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } /* One byte for null terminator, one for newline */ buffer[len++] = '\n'; @@ -758,6 +768,11 @@ static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params) char buffer[256]; g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2); + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer); return 1; @@ -781,6 +796,11 @@ static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params) char buffer[256]; g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2); + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + unsigned int old = g_ChatTriggers.SetReplyTo(SM_REPLY_CONSOLE); serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer); g_ChatTriggers.SetReplyTo(old); @@ -796,6 +816,11 @@ static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params) char buffer[1024]; size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2); + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + /* If we're printing to the server, shortcut out */ if (params[1] == 0) { diff --git a/core/smn_player.cpp b/core/smn_player.cpp index 531a130e..1af0cca0 100644 --- a/core/smn_player.cpp +++ b/core/smn_player.cpp @@ -1020,6 +1020,11 @@ static cell_t KickClient(IPluginContext *pContext, const cell_t *params) char buffer[256]; g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2); + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + pClient->Disconnect("%s", buffer); return 1; @@ -1048,6 +1053,45 @@ static cell_t ChangeClientTeam(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t RunAdminCacheChecks(IPluginContext *pContext, const cell_t *params) +{ + int client = params[1]; + + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", client); + } else if (!pPlayer->IsInGame()) { + return pContext->ThrowNativeError("Client %d is not in game", client); + } else if (!pPlayer->IsAuthorized()) { + return pContext->ThrowNativeError("Client %d is not authorized", client); + } + + AdminId id = pPlayer->GetAdminId(); + pPlayer->DoBasicAdminChecks(); + + return (id != pPlayer->GetAdminId()) ? 1 : 0; +} + +static cell_t NotifyPostAdminCheck(IPluginContext *pContext, const cell_t *params) +{ + int client = params[1]; + + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", client); + } else if (!pPlayer->IsInGame()) { + return pContext->ThrowNativeError("Client %d is not in game", client); + } else if (!pPlayer->IsAuthorized()) { + return pContext->ThrowNativeError("Client %d is not authorized", client); + } + + pPlayer->NotifyPostAdminChecks(); + + return 1; +} + REGISTER_NATIVES(playernatives) { {"AddUserFlags", AddUserFlags}, @@ -1095,5 +1139,7 @@ REGISTER_NATIVES(playernatives) {"ShowActivity", ShowActivity}, {"ShowActivityEx", ShowActivityEx}, {"KickClient", KickClient}, + {"RunAdminCacheChecks", RunAdminCacheChecks}, + {"NotifyPostAdminCheck", NotifyPostAdminCheck}, {NULL, NULL} }; diff --git a/plugins/include/clients.inc b/plugins/include/clients.inc index 7fc0caf1..070c22c5 100644 --- a/plugins/include/clients.inc +++ b/plugins/include/clients.inc @@ -30,7 +30,7 @@ enum NetFlow /** * Called on client connection. * - * @param client Player index. + * @param client Client index. * @param rejectmsg Buffer to store the rejection message when the connection is refused. * @param maxlen Maximum number of characters for rejection buffer. * @return True to validate client's connection, false to refuse it. @@ -40,7 +40,7 @@ forward bool:OnClientConnect(client, String:rejectmsg[], maxlen); /** * Called when a client is entering to the game. * - * @param client Player index. + * @param client Client index. * @noreturn */ forward OnClientPutInServer(client); @@ -48,7 +48,7 @@ forward OnClientPutInServer(client); /** * Called when a client is disconnecting from the server. * - * @param client Player index. + * @param client Client index. * @noreturn */ forward OnClientDisconnect(client); @@ -56,7 +56,7 @@ forward OnClientDisconnect(client); /** * Called when a client is disconnected from the server. * - * @param client Player index. + * @param client Client index. * @noreturn */ forward OnClientDisconnect_Post(client); @@ -64,7 +64,7 @@ forward OnClientDisconnect_Post(client); /** * Called when a client is sending a command. * - * @param client Player index. + * @param client Client index. * @param args Number of arguments. * @noreturn */ @@ -73,20 +73,45 @@ forward Action:OnClientCommand(client, args); /** * Called whenever the client's settings are changed. * - * @param client Player index. + * @param client Client index. * @noreturn */ forward OnClientSettingsChanged(client); /** * Called when a client receives a Steam ID. - * @note This is called by bots, but the ID will be "BOT" + + * Admin properties are not assigned here, but on OnClientPostAdminCheck(). + * This is called by bots, but the ID will be "BOT". * - * @param client Player index. - * @param auth Player auth string. + * @param client Client index. + * @param auth Client auth string. + * @noreturn */ forward OnClientAuthorized(client, const String:auth[]); +/** + * Called once a client is authorized and fully in-game, but + * before admin checks are done. This can be used to override + * the default admin checks for a client. + * + * Note: If handled/blocked, PostAdminCheck must be signalled + * manually via NotifyPostAdminCheck(). + * + * @param client Client index. + * @return Plugin_Handled to block admin checks. + */ +forward Action:OnClientPreAdminCheck(client); + +/** + * Called once a client is authorized and fully in-game, and + * after all post-connection authorizations have been performed. + * + * @param client Client index. + * @noreturn + */ +forward OnClientPostAdminCheck(client); + /** * Returns the maximum number of clients allowed on the server. * @@ -290,6 +315,30 @@ native GetUserFlagBits(client); */ native bool:CanUserTarget(client, target); +/** + * Runs through the Core-defined admin authorization checks on a player. + * Has no effect if the player is already an admin. + * + * Note: This function is based on the internal cache only. + * + * @param client Client index. + * @return True if access was changed, false if it did not. + * @error Invalid client index or client not in-game AND authorized. + */ +native bool:RunAdminCacheChecks(client); + +/** + * Signals that a player has completed post-connection admin checks. + * Has no effect if the player has already had this event signalled. + * + * Note: This must be sent even if no admin id was assigned. + * + * @param client Client index. + * @noreturn + * @error Invalid client index or client not in-game AND authorized. + */ +native NotifyPostAdminCheck(client); + /** * Creates a fake client. * diff --git a/plugins/reservedslots.sp b/plugins/reservedslots.sp index 956817d6..1087e0e0 100644 --- a/plugins/reservedslots.sp +++ b/plugins/reservedslots.sp @@ -63,7 +63,7 @@ public OnConfigsExecuted() } } -public OnClientAuthorized(client, const String:auth[]) +public OnClientPostAdminCheck(client) { new reserved = GetConVarInt(sm_reserved_slots);