diff --git a/plugins/adminmenu.sp b/plugins/adminmenu.sp
new file mode 100644
index 00000000..99760f5f
--- /dev/null
+++ b/plugins/adminmenu.sp
@@ -0,0 +1,176 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Anti-Flood Plugin
+ * Protects against chat flooding.
+ *
+ * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#pragma semicolon 1
+
+#include
+#include
+
+public Plugin:myinfo =
+{
+ name = "Admin Menu",
+ author = "AlliedModders LLC",
+ description = "Administration Menu",
+ version = SOURCEMOD_VERSION,
+ url = "http://www.sourcemod.net/"
+};
+
+/* Forwards */
+new Handle:hOnAdminMenuReady = INVALID_HANDLE;
+new Handle:hOnAdminMenuCreated = INVALID_HANDLE;
+
+/* Menus */
+new Handle:hAdminMenu = INVALID_HANDLE;
+
+/* Top menu objects */
+new TopMenuObject:obj_playercmds = INVALID_TOPMENUOBJECT;
+
+public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
+{
+ CreateNative("GetAdminTopMenu", __GetAdminTopMenu);
+ CreateNative("AddTargetsToMenu", __AddTargetsToMenu);
+ RegPluginLibrary("adminmenu");
+ return true;
+}
+
+public OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+
+ hOnAdminMenuCreated = CreateGlobalForward("OnAdminMenuCreated", ET_Ignore, Param_Cell);
+ hOnAdminMenuReady = CreateGlobalForward("OnAdminMenuReady", ET_Ignore, Param_Cell);
+
+ RegAdminCmd("sm_admin", Command_DisplayMenu, ADMFLAG_GENERIC, "sm_admin");
+}
+
+public OnAllPluginsLoaded()
+{
+ hAdminMenu = CreateTopMenu(CategoryHandler);
+
+ obj_playercmds = AddToTopMenu(hAdminMenu,
+ "PlayerCommands",
+ TopMenuObject_Category,
+ CategoryHandler,
+ INVALID_TOPMENUOBJECT);
+
+ Call_StartForward(hOnAdminMenuCreated);
+ Call_PushCell(hAdminMenu);
+ Call_Finish();
+
+ Call_StartForward(hOnAdminMenuReady);
+ Call_PushCell(hAdminMenu);
+ Call_Finish();
+}
+
+public CategoryHandler(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DrawTitle)
+ {
+ if (object_id == INVALID_TOPMENUOBJECT)
+ {
+ Format(buffer, maxlength, "%T:", "Admin Menu", param);
+ }
+ else if (object_id == obj_playercmds)
+ {
+ Format(buffer, maxlength, "%T:", "Player Commands", param);
+ }
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ if (object_id == obj_playercmds)
+ {
+ Format(buffer, maxlength, "%T", "Player Commands", param);
+ }
+ return ITEMDRAW_DEFAULT;
+ }
+
+ return 0;
+}
+
+public __GetAdminTopMenu(Handle:plugin, numParams)
+{
+ return _:hAdminMenu;
+}
+
+public __AddTargetsToMenu(Handle:plugin, numParams)
+{
+ return UTIL_AddTargetsToMenu(GetNativeCell(1), GetNativeCell(2), GetNativeCell(3));
+}
+
+public Action:Command_DisplayMenu(client, args)
+{
+ DisplayTopMenu(hAdminMenu, client, TopMenuPosition_Start);
+
+ return Plugin_Handled;
+}
+
+stock UTIL_AddTargetsToMenu(Handle:menu, source_client, bool:in_game_only)
+{
+ new max_clients = GetMaxClients();
+ decl String:user_id[12];
+ decl String:name[MAX_NAME_LENGTH];
+ decl String:display[MAX_NAME_LENGTH+12];
+
+ new num_clients;
+
+ for (new i = 1; i <= max_clients; i++)
+ {
+ if (!IsClientConnected(i) || IsClientInKickQueue(i))
+ {
+ continue;
+ }
+
+ if (in_game_only && !IsClientInGame(i))
+ {
+ continue;
+ }
+
+ if (source_client && !CanUserTarget(source_client, i))
+ {
+ continue;
+ }
+
+ IntToString(GetClientUserId(i), user_id, sizeof(user_id));
+ GetClientName(i, name, sizeof(name));
+ Format(display, sizeof(display), "%s (%s)", name, user_id);
+ AddMenuItem(menu, user_id, display);
+ num_clients++;
+ }
+
+ return num_clients;
+}
diff --git a/plugins/basecommands.sp b/plugins/basecommands.sp
index 3c7ce935..2cc119bc 100644
--- a/plugins/basecommands.sp
+++ b/plugins/basecommands.sp
@@ -34,6 +34,8 @@
#pragma semicolon 1
#include
+#undef REQUIRE_PLUGIN
+#include
public Plugin:myinfo =
{
@@ -44,6 +46,10 @@ public Plugin:myinfo =
url = "http://www.sourcemod.net/"
};
+new Handle:hTopMenu = INVALID_HANDLE;
+
+#include "basecommands/kick.sp"
+
public OnPluginStart()
{
LoadTranslations("common.phrases");
@@ -56,6 +62,47 @@ public OnPluginStart()
RegAdminCmd("sm_who", Command_Who, ADMFLAG_GENERIC, "sm_who [#userid|name]");
RegAdminCmd("sm_reloadadmins", Command_ReloadAdmins, ADMFLAG_BAN, "sm_reloadadmins");
RegAdminCmd("sm_cancelvote", Command_CancelVote, ADMFLAG_VOTE, "sm_cancelvote");
+
+ /* Account for late loading */
+ new Handle:topmenu;
+ if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
+ {
+ OnAdminMenuReady(topmenu);
+ }
+}
+
+public OnAdminMenuReady(Handle:topmenu)
+{
+ /* Block us from being called twice */
+ if (topmenu == hTopMenu)
+ {
+ return;
+ }
+
+ /* Save the Handle */
+ hTopMenu = topmenu;
+
+ /* Build the "Player Commands" category */
+ new TopMenuObject:player_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_PLAYERCOMMANDS);
+
+ if (player_commands != INVALID_TOPMENUOBJECT)
+ {
+ AddToTopMenu(hTopMenu,
+ "Kick",
+ TopMenuObject_Item,
+ AdminMenu_Kick,
+ player_commands,
+ "sm_kick",
+ ADMFLAG_KICK);
+ }
+}
+
+public OnLibraryRemoved(const String:name[])
+{
+ if (strcmp(name, "adminmenu") == 0)
+ {
+ hTopMenu = INVALID_HANDLE;
+ }
}
public Action:Command_ReloadAdmins(client, args)
@@ -365,51 +412,6 @@ public Action:Timer_ChangeMap(Handle:timer, Handle:dp)
return Plugin_Stop;
}
-public Action:Command_Kick(client, args)
-{
- if (args < 1)
- {
- ReplyToCommand(client, "[SM] Usage: sm_kick <#userid|name> [reason]");
- return Plugin_Handled;
- }
-
-
- decl String:Arguments[256];
- GetCmdArgString(Arguments, sizeof(Arguments));
-
- decl String:arg[65];
- new len = BreakString(Arguments, arg, sizeof(arg));
-
- new target = FindTarget(client, arg);
- if (target == -1)
- {
- return Plugin_Handled;
- }
-
- GetClientName(target, arg, sizeof(arg));
-
- if (len == -1)
- {
- /* Safely null terminate */
- len = 0;
- Arguments[0] = '\0';
- }
-
- ShowActivity(client, "%t", "Kicked player", arg);
- LogAction(client, target, "\"%L\" kicked \"%L\" (reason \"%s\")", client, target, Arguments[len]);
-
- if (Arguments[0] == '\0')
- {
- KickClient(target, "%t", "Kicked by admin");
- }
- else
- {
- KickClient(target, "%s", Arguments[len]);
- }
-
- return Plugin_Handled;
-}
-
public Action:Command_CancelVote(client, args)
{
if (!IsVoteInProgress())
@@ -424,3 +426,4 @@ public Action:Command_CancelVote(client, args)
return Plugin_Handled;
}
+
diff --git a/plugins/basecommands/kick.sp b/plugins/basecommands/kick.sp
new file mode 100644
index 00000000..b4b59743
--- /dev/null
+++ b/plugins/basecommands/kick.sp
@@ -0,0 +1,124 @@
+
+PerformKick(client, target, const String:reason[])
+{
+ decl String:name[MAX_NAME_LENGTH];
+
+ GetClientName(target, name, sizeof(name));
+
+ ShowActivity(client, "%t", "Kicked player", name);
+ LogAction(client, target, "\"%L\" kicked \"%L\" (reason \"%s\")", client, target, reason);
+
+ if (reason[0] == '\0')
+ {
+ KickClient(target, "%t", "Kicked by admin");
+ }
+ else
+ {
+ KickClient(target, "%s", reason);
+ }
+}
+
+DisplayKickMenu(client)
+{
+ new Handle:menu = CreateMenu(MenuHandler_Kick);
+
+ decl String:title[100];
+ Format(title, sizeof(title), "%T:", "Kick player", client);
+ SetMenuTitle(menu, title);
+ SetMenuExitBackButton(menu, true);
+
+ AddTargetsToMenu(menu, client, false);
+
+ DisplayMenu(menu, client, MENU_TIME_FOREVER);
+}
+
+public AdminMenu_Kick(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DrawOption)
+ {
+ Format(buffer, maxlength, "%T", "Kick player", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ DisplayKickMenu(param);
+ }
+}
+
+public MenuHandler_Kick(Handle:menu, MenuAction:action, param1, param2)
+{
+ if (action == MenuAction_End)
+ {
+ CloseHandle(menu);
+ }
+ else if (action == MenuAction_Cancel)
+ {
+ if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ else if (action == MenuAction_Select)
+ {
+ decl String:info[32];
+ new userid, target;
+
+ GetMenuItem(menu, param2, info, sizeof(info));
+ userid = StringToInt(info);
+
+ if ((target = GetClientOfUserId(userid)) == 0)
+ {
+ PrintToChat(param1, "[SM] %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ PrintToChat(param1, "[SM] %t", "Unable to target");
+ }
+ else
+ {
+ PerformKick(param1, target, "");
+ }
+
+ /* Re-draw the menu if they're still valid */
+ if (IsClientInGame(param1) && !IsClientInKickQueue(param1))
+ {
+ DisplayKickMenu(param1);
+ }
+ }
+}
+
+public Action:Command_Kick(client, args)
+{
+ if (args < 1)
+ {
+ ReplyToCommand(client, "[SM] Usage: sm_kick <#userid|name> [reason]");
+ return Plugin_Handled;
+ }
+
+ decl String:Arguments[256];
+ GetCmdArgString(Arguments, sizeof(Arguments));
+
+ decl String:arg[65];
+ new len = BreakString(Arguments, arg, sizeof(arg));
+
+ new target = FindTarget(client, arg);
+ if (target == -1)
+ {
+ return Plugin_Handled;
+ }
+
+ if (len == -1)
+ {
+ /* Safely null terminate */
+ len = 0;
+ Arguments[0] = '\0';
+ }
+
+ PerformKick(client, target, Arguments[len]);
+
+ return Plugin_Handled;
+}
diff --git a/plugins/include/adminmenu.inc b/plugins/include/adminmenu.inc
new file mode 100644
index 00000000..094d2146
--- /dev/null
+++ b/plugins/include/adminmenu.inc
@@ -0,0 +1,110 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This file is part of the SourceMod/SourcePawn SDK.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#if defined _adminmenu_included
+ #endinput
+#endif
+#define _adminmenu_included
+
+/* Decide whether topmenus should be required */
+#if !defined REQUIRE_PLUGIN
+ #if defined REQUIRE_EXTENSIONS
+ #define TEMP_REQUIRE_EXTENSIONS
+ #undef REQUIRE_EXTENSIONS
+#endif
+
+#include
+
+/* Restore old REQUIRE_EXTENSIONS value if necessary */
+#if defined TEMP_REQUIRE_EXTENSIONS
+ #define REQUIRE_EXTENSIONS
+ #undef TEMP_REQUIRE_EXTENSIONS
+#endif
+
+/** Category for player commands. */
+#define ADMINMENU_PLAYERCOMMANDS "PlayerCommands"
+
+/**
+ * Called when the admin menu is created and 3rd party plugins can grab
+ * the Handle or add categories.
+ *
+ * @param topmenu Handle to the admin menu's TopMenu.
+ * @noreturn
+ */
+forward OnAdminMenuCreated(Handle:topmenu);
+
+/**
+ * Called when the admin menu is ready to have items added.
+ *
+ * @param topmenu Handle to the admin menu's TopMenu.
+ * @noreturn
+ */
+forward OnAdminMenuReady(Handle:topmenu);
+
+/**
+ * Retrieves the Handle to the admin top menu.
+ *
+ * @return Handle to the admin menu's TopMenu,
+ * or INVALID_HANDLE if not created yet.
+ */
+native Handle:GetAdminTopMenu();
+
+/**
+ * Adds targets to an admin menu.
+ *
+ * Each client is displayed as: name (userid)
+ * Each item contains the userid as a string for its info.
+ *
+ * @param menu Menu Handle.
+ * @param source_client Source client, or 0 to ignore immunity.
+ * @param in_game_only True to only select in-game players.
+ * @return Number of clients added.
+ */
+native AddTargetsToMenu(Handle:menu, source_client, bool:in_game_only=true);
+
+/* DO NOT EDIT BELOW THIS LINE */
+
+public SharedPlugin:__pl_adminmenu =
+{
+ name = "adminmenu",
+ file = "adminmenu.smx",
+#if defined REQUIRE_PLUGIN
+ required = 1,
+#else
+ required = 0,
+#endif
+};
+
+public __pl_adminmenu_SetNTVOptional()
+{
+ MarkNativeAsOptional("GetAdminTopMenu");
+}
diff --git a/tools/builder/PkgCore.cs b/tools/builder/PkgCore.cs
index 9e59b490..8c98617e 100644
--- a/tools/builder/PkgCore.cs
+++ b/tools/builder/PkgCore.cs
@@ -29,7 +29,7 @@ namespace builder
*/
public override string [] GetFolders()
{
- string [] folders = new string[16];
+ string [] folders = new string[17];
folders[0] = "addons/sourcemod/bin";
folders[1] = "addons/sourcemod/plugins/disabled";
@@ -47,6 +47,7 @@ namespace builder
folders[13] = "addons/sourcemod/configs/sql-init-scripts/mysql";
folders[14] = "addons/sourcemod/configs/sql-init-scripts/sqlite";
folders[15] = "addons/sourcemod/extensions/games";
+ folders[16] = "addons/sourcemod/scripting/basecommands";
return folders;
}
@@ -93,6 +94,7 @@ namespace builder
builder.CopyFolder(this, "public/licenses", "addons/sourcemod", null);
builder.CopyFolder(this, "plugins/admin-flatfile", "addons/sourcemod/scripting/admin-flatfile", null);
builder.CopyFolder(this, "plugins/testsuite", "addons/sourcemod/scripting/testsuite", null);
+ builder.CopyFolder(this, "plugins/basecommands", "addons/sourcemod/scripting/basecommands", null);
}
/**
@@ -100,7 +102,7 @@ namespace builder
*/
public override Library [] GetLibraries()
{
- Library [] libs = new Library[9];
+ Library [] libs = new Library[10];
for (int i=0; i