diff --git a/core/Makefile b/core/Makefile
index 0862d4cc..d68423c5 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -19,7 +19,7 @@ OBJECTS = AdminCache.cpp CDataPack.cpp ConCmdManager.cpp ConVarManager.cpp CoreC
sourcemm_api.cpp sourcemod.cpp MenuStyle_Base.cpp MenuStyle_Valve.cpp MenuManager.cpp \
MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp MenuVoting.cpp sm_crc32.cpp \
frame_hooks.cpp concmd_cleaner.cpp Profiler.cpp PhraseCollection.cpp NextMap.cpp \
- NativeOwner.cpp
+ NativeOwner.cpp TagsSystem.cpp
OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \
smn_datapacks.cpp smn_entities.cpp smn_events.cpp smn_fakenatives.cpp \
smn_filesystem.cpp smn_float.cpp smn_functions.cpp smn_gameconfigs.cpp smn_halflife.cpp \
diff --git a/core/TagsSystem.cpp b/core/TagsSystem.cpp
new file mode 100644
index 00000000..93615368
--- /dev/null
+++ b/core/TagsSystem.cpp
@@ -0,0 +1,206 @@
+/**
+* vim: set ts=4 :
+* =============================================================================
+* SourceMod
+* Copyright (C) 2004-2008 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$
+*/
+
+#include "TagsSystem.h"
+#include "sourcemod.h"
+#include "sourcemm_api.h"
+#include "sm_stringutil.h"
+#include "PluginSys.h"
+#include "compat_wrappers.h"
+
+TagHandler g_Tags;
+
+TagHandler::TagHandler()
+{
+ m_SvTags = NULL;
+}
+
+TagHandler::~TagHandler()
+{
+ SourceHook::List::iterator iter = m_TagList.begin();
+
+ while (iter != m_TagList.end())
+ {
+ g_pMemAlloc->Free(*iter);
+ iter = m_TagList.erase(iter);
+ }
+}
+
+void TagHandler::OnSourceModAllInitialized_Post()
+{
+ g_PluginSys.AddPluginsListener(this);
+
+ m_SvTags = icvar->FindVar("sv_tags");
+ AddTag("sourcemod");
+}
+
+void TagHandler::OnSourceModLevelActivated()
+{
+ SourceHook::List::iterator iter = m_TagList.begin();
+
+ while (iter != m_TagList.end())
+ {
+ ApplyTag(*iter);
+ iter++;
+ }
+}
+
+void TagHandler::OnSourceModShutdown()
+{
+ RemoveTag("sourcemod");
+}
+
+void TagHandler::OnPluginDestroyed(IPlugin *plugin)
+{
+ SourceHook::List *pList = NULL;
+
+ if (!plugin->GetProperty("ServerTags", (void **)&pList, false) || !pList)
+ {
+ return;
+ }
+
+ SourceHook::List::iterator iter = pList->begin();
+
+ while (iter != pList->end())
+ {
+ RemoveTag(*iter);
+ g_pMemAlloc->Free(*iter);
+ iter = pList->erase(iter);
+ }
+
+ delete pList;
+}
+
+bool TagHandler::AddTag(const char *addTag)
+{
+ SourceHook::List::iterator iter = m_TagList.begin();
+
+ while (iter != m_TagList.end())
+ {
+ if (strcmp(*iter, addTag) == 0)
+ {
+ return false;
+ }
+ }
+
+ m_TagList.push_back(strdup(addTag));
+ ApplyTag(addTag);
+
+ return true;
+}
+
+bool TagHandler::RemoveTag(const char *removeTag)
+{
+ SourceHook::List::iterator iter = m_TagList.begin();
+
+ while (iter != m_TagList.end())
+ {
+ if (strcmp(*iter, removeTag) == 0)
+ {
+ g_pMemAlloc->Free(*iter);
+ m_TagList.erase(iter);
+ StripTag(removeTag);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void TagHandler::ApplyTag(const char *addTag)
+{
+ if (m_SvTags == NULL)
+ {
+ return;
+ }
+
+ const char *curTags = m_SvTags->GetString();
+
+ if (strstr(curTags, addTag) != NULL)
+ {
+ /* Already tagged */
+ return;
+ }
+
+ if (curTags[0] == '\0')
+ {
+ m_SvTags->SetValue(addTag);
+ return;
+ }
+
+ /* New tags buffer (+2 for , and null char) */
+ size_t newLen = strlen(curTags) + strlen(addTag) + 2;
+ char *newTags = (char *)alloca(newLen);
+
+ g_SourceMod.Format(newTags, newLen, "%s,%s", curTags, addTag);
+
+ m_SvTags->SetValue(newTags);
+}
+
+void TagHandler::StripTag(const char *removeTag)
+{
+ if (m_SvTags == NULL)
+ {
+ return;
+ }
+
+ const char *curTags = m_SvTags->GetString();
+
+ if (strcmp(curTags, removeTag) == 0)
+ {
+ m_SvTags->SetValue("");
+ return;
+ }
+
+ char *newTags = strdup(curTags);
+ size_t searchLen = strlen(removeTag) + 2;
+ char *search = (char *)alloca(searchLen);
+
+ if (strncmp(curTags, removeTag, strlen(removeTag)) == 0)
+ {
+ strcpy(search, removeTag);
+ search[searchLen-2] = ',';
+ search[searchLen-1] = '\0';
+ }
+ else
+ {
+ strcpy(search+1, removeTag);
+ search[0] = ',';
+ search[searchLen-1] = '\0';
+ }
+
+ UTIL_ReplaceAll(newTags, strlen(newTags) + 1, search, "" , true);
+
+ m_SvTags->SetValue(newTags);
+
+ g_pMemAlloc->Free(newTags);
+}
diff --git a/core/TagsSystem.h b/core/TagsSystem.h
new file mode 100644
index 00000000..2f8337a8
--- /dev/null
+++ b/core/TagsSystem.h
@@ -0,0 +1,71 @@
+/**
+* vim: set ts=4 :
+* =============================================================================
+* SourceMod
+* Copyright (C) 2004-2008 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$
+*/
+
+#ifndef _INCLUDE_SOURCEMOD_TAGSSYSTEM_H_
+#define _INCLUDE_SOURCEMOD_TAGSSYSTEM_H_
+
+#include "sm_globals.h"
+#include "sh_list.h"
+#include "convar.h"
+#include "IPluginSys.h"
+
+class TagHandler :
+ public SMGlobalClass,
+ public IPluginsListener
+{
+public:
+ TagHandler();
+ ~TagHandler();
+
+public: // SMGlobalClass
+ void OnSourceModAllInitialized_Post();
+ void OnSourceModLevelActivated();
+ void OnSourceModShutdown();
+
+public: // IPluginsListener
+ void OnPluginDestroyed(IPlugin *plugin);
+
+public:
+ bool AddTag(const char *addTag);
+ bool RemoveTag(const char *removeTag);
+
+private:
+ void ApplyTag(const char *addTag);
+ void StripTag(const char *removeTag);
+
+private:
+ SourceHook::Listm_TagList;
+ ConVar *m_SvTags;
+};
+
+extern TagHandler g_Tags;
+
+#endif // _INCLUDE_SOURCEMOD_TAGSSYSTEM_H_
diff --git a/core/msvc9/sourcemod_mm.vcproj b/core/msvc9/sourcemod_mm.vcproj
index 2f939677..2da543f3 100644
--- a/core/msvc9/sourcemod_mm.vcproj
+++ b/core/msvc9/sourcemod_mm.vcproj
@@ -1512,6 +1512,10 @@
RelativePath="..\sourcemod.cpp"
>
+
+
@@ -1750,6 +1754,10 @@
RelativePath="..\sourcemod.h"
>
+
+
diff --git a/core/smn_console.cpp b/core/smn_console.cpp
index a210fdd5..19d9d5ef 100644
--- a/core/smn_console.cpp
+++ b/core/smn_console.cpp
@@ -43,6 +43,7 @@
#include
#include
#include
+#include "TagsSystem.h"
#if SOURCE_ENGINE == SE_LEFT4DEAD
#define NET_SETCONVAR 6
@@ -1304,6 +1305,60 @@ static cell_t SendConVarValue(IPluginContext *pContext, const cell_t *params)
return 1;
}
+static cell_t AddServerTag(IPluginContext *pContext, const cell_t *params)
+{
+ char *value;
+ pContext->LocalToString(params[1], &value);
+
+ if (!g_Tags.AddTag(value))
+ {
+ return 0;
+ }
+
+ IPlugin *pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
+ SourceHook::List *pList = NULL;
+
+ if (!pPlugin->GetProperty("ServerTags", (void **)&pList, false) || !pList)
+ {
+ pList = new SourceHook::List;
+ pPlugin->SetProperty("ServerTags", pList);
+ }
+
+ pList->push_back(strdup(value));
+
+ return 1;
+}
+
+static cell_t RemoveServerTag(IPluginContext *pContext, const cell_t *params)
+{
+ char *value;
+ pContext->LocalToString(params[1], &value);
+
+ IPlugin *pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
+ SourceHook::List *pList = NULL;
+
+ if (!pPlugin->GetProperty("ServerTags", (void **)&pList, false) || !pList)
+ {
+ pList = new SourceHook::List;
+ pPlugin->SetProperty("ServerTags", pList);
+ }
+
+ SourceHook::List::iterator iter = pList->begin();
+
+ while (iter != pList->end())
+ {
+ if (strcmp(*iter, value) == 0)
+ {
+ g_pMemAlloc->Free(*iter);
+ pList->erase(iter);
+
+ return g_Tags.RemoveTag(value);
+ }
+ }
+
+ return 0;
+}
+
REGISTER_NATIVES(consoleNatives)
{
{"CreateConVar", sm_CreateConVar},
@@ -1351,5 +1406,7 @@ REGISTER_NATIVES(consoleNatives)
{"FindFirstConCommand", FindFirstConCommand},
{"FindNextConCommand", FindNextConCommand},
{"SendConVarValue", SendConVarValue},
+ {"AddServerTag", AddServerTag},
+ {"RemoveServerTag", RemoveServerTag},
{NULL, NULL}
};
diff --git a/plugins/include/console.inc b/plugins/include/console.inc
index ffe5dcda..2130c30e 100644
--- a/plugins/include/console.inc
+++ b/plugins/include/console.inc
@@ -815,3 +815,23 @@ native bool:FindNextConCommand(Handle:search, String:buffer[], max_size, &bool:i
* @error Invalid client index, client not in game, or client is fake
*/
native bool:SendConVarValue(client, Handle:convar, const String:value[]);
+
+/**
+ * Appends a string to Valve's sv_tags convar and makes sure it remains after mapchanges.
+ *
+ * Note: Tags are automatically removed on plugin unload
+ *
+ * @param tag Tag string to append.
+ * @noreturn
+ */
+native AddServerTag(const String:tag[]);
+
+/**
+ * Removes a string from valve's sv_tags convar.
+ *
+ * Note: You can only remove tags created by you.
+ *
+ * @param tag Tag string to remove.
+ * @noreturn
+ */
+native RemoveServerTag(const String:tag[]);