diff --git a/AMBuildScript b/AMBuildScript
index 6ada6eb9bf..863d7c2860 100644
--- a/AMBuildScript
+++ b/AMBuildScript
@@ -819,7 +819,7 @@ else:
'core/AMBuilder',
'core/logic/AMBuilder',
'extensions/bintools/AMBuilder',
- 'extensions/clientprefs/AMBuilder',
+ #'extensions/clientprefs/AMBuilder',
'extensions/curl/AMBuilder',
'extensions/cstrike/AMBuilder',
'extensions/dhooks/AMBuilder',
diff --git a/extensions/clientprefs/AMBuilder b/extensions/clientprefs/AMBuilder
deleted file mode 100644
index ba61ea4acf..0000000000
--- a/extensions/clientprefs/AMBuilder
+++ /dev/null
@@ -1,24 +0,0 @@
-# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
-import os
-
-for cxx in builder.targets:
- binary = SM.ExtLibrary(builder, cxx, 'clientprefs.ext')
- binary.compiler.cxxincludes += [
- os.path.join(SM.mms_root, 'core', 'sourcehook'),
- ]
- if binary.compiler.family == 'gcc' or binary.compiler.family == 'clang':
- binary.compiler.cxxflags += ['-fno-rtti']
- elif binary.compiler.family == 'msvc':
- binary.compiler.cxxflags += ['/GR-']
-
- binary.sources += [
- 'extension.cpp',
- 'cookie.cpp',
- 'menus.cpp',
- 'natives.cpp',
- 'query.cpp',
- '../../public/smsdk_ext.cpp'
- ]
-
- SM.extensions += [builder.Add(binary)]
-
diff --git a/extensions/clientprefs/cookie.cpp b/extensions/clientprefs/cookie.cpp
deleted file mode 100644
index 6c04fef52d..0000000000
--- a/extensions/clientprefs/cookie.cpp
+++ /dev/null
@@ -1,381 +0,0 @@
-/**
- * vim: set ts=4 sw=4 tw=99 noet:
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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 "cookie.h"
-#include "menus.h"
-#include "query.h"
-
-CookieManager g_CookieManager;
-
-CookieManager::CookieManager()
-{
- for (int i=0; i<=SM_MAXPLAYERS; i++)
- {
- connected[i] = false;
- statsLoaded[i] = false;
- statsPending[i] = false;
- }
-
- cookieDataLoadedForward = NULL;
- clientMenu = NULL;
-}
-CookieManager::~CookieManager(){}
-
-void CookieManager::Unload()
-{
- /* If clients are connected we should try save their data */
- for (int i = playerhelpers->GetMaxClients()+1; --i > 0;)
- {
- if (connected[i])
- OnClientDisconnecting(i);
- }
-
- /* Find all cookies and delete them */
- for (size_t iter = 0; iter < cookieList.size(); ++iter)
- delete cookieList[iter];
-
- cookieList.clear();
-}
-
-Cookie *CookieManager::FindCookie(const char *name)
-{
- Cookie *cookie;
- if (!cookieFinder.retrieve(name, &cookie))
- return NULL;
- return cookie;
-}
-
-Cookie *CookieManager::CreateCookie(const char *name, const char *description, CookieAccess access)
-{
- Cookie *pCookie = FindCookie(name);
-
- /* Check if cookie already exists */
- if (pCookie != NULL)
- {
- /* Update data fields to the provided values */
- UTIL_strncpy(pCookie->description, description, MAX_DESC_LENGTH);
- pCookie->access = access;
-
- return pCookie;
- }
-
- /* First time cookie - Create from scratch */
- pCookie = new Cookie(name, description, access);
-
- /* Attempt to insert cookie into the db and get its ID num */
- TQueryOp *op = new TQueryOp(Query_InsertCookie, pCookie);
- op->m_params.cookie = pCookie;
-
- cookieFinder.insert(name, pCookie);
- cookieList.push_back(pCookie);
-
- g_ClientPrefs.AddQueryToQueue(op);
-
- return pCookie;
-}
-
-bool CookieManager::GetCookieValue(Cookie *pCookie, int client, char **value)
-{
- CookieData *data = pCookie->data[client];
-
- /* Check if a value has been set before */
- if (data == NULL)
- {
- data = new CookieData("");
- data->parent = pCookie;
- clientData[client].push_back(data);
- pCookie->data[client] = data;
- data->changed = false;
- data->timestamp = 0;
- }
-
- *value = &data->value[0];
-
- return true;
-}
-
-bool CookieManager::SetCookieValue(Cookie *pCookie, int client, const char *value)
-{
- CookieData *data = pCookie->data[client];
-
- if (data == NULL)
- {
- data = new CookieData(value);
- data->parent = pCookie;
- clientData[client].push_back(data);
- pCookie->data[client] = data;
- }
- else
- {
- UTIL_strncpy(data->value, value, MAX_VALUE_LENGTH);
- }
-
- data->changed = true;
- data->timestamp = time(NULL);
-
- return true;
-}
-
-void CookieManager::OnClientAuthorized(int client, const char *authstring)
-{
- IGamePlayer *player = playerhelpers->GetGamePlayer(client);
-
- if (player == NULL)
- {
- return;
- }
-
- connected[client] = true;
- statsPending[client] = true;
-
- g_ClientPrefs.AttemptReconnection();
-
- TQueryOp *op = new TQueryOp(Query_SelectData, player->GetSerial());
- UTIL_strncpy(op->m_params.steamId, GetPlayerCompatAuthId(player), MAX_NAME_LENGTH);
-
- g_ClientPrefs.AddQueryToQueue(op);
-}
-
-void CookieManager::OnClientDisconnecting(int client)
-{
- connected[client] = false;
- statsLoaded[client] = false;
- statsPending[client] = false;
-
- CookieData *current = NULL;
-
- g_ClientPrefs.AttemptReconnection();
-
- /* Save this cookie to the database */
- IGamePlayer *player = playerhelpers->GetGamePlayer(client);
- const char *pAuth = NULL;
- int dbId;
-
- if (player)
- {
- pAuth = GetPlayerCompatAuthId(player);
- g_ClientPrefs.ClearQueryCache(player->GetSerial());
- }
-
- std::vector &clientvec = clientData[client];
- for (size_t iter = 0; iter < clientvec.size(); ++iter)
- {
- current = clientvec[iter];
- dbId = current->parent->dbid;
-
- if (player == NULL || pAuth == NULL || !current->changed || dbId == -1)
- {
- current->parent->data[client] = NULL;
- delete current;
- continue;
- }
-
- TQueryOp *op = new TQueryOp(Query_InsertData, client);
-
- UTIL_strncpy(op->m_params.steamId, pAuth, MAX_NAME_LENGTH);
- op->m_params.cookieId = dbId;
- op->m_params.data = current;
-
- g_ClientPrefs.AddQueryToQueue(op);
-
- current->parent->data[client] = NULL;
- }
-
- clientvec.clear();
-}
-
-void CookieManager::ClientConnectCallback(int serial, IQuery *data)
-{
- int client;
-
- /* Check validity of client */
- if ((client = playerhelpers->GetClientFromSerial(serial)) == 0)
- {
- return;
- }
- statsPending[client] = false;
-
- IResultSet *results;
- /* Check validity of results */
- if (data == NULL || (results = data->GetResultSet()) == NULL)
- {
- return;
- }
-
- CookieData *pData;
- IResultRow *row;
- unsigned int timestamp;
- CookieAccess access;
-
- while (results->MoreRows() && ((row = results->FetchRow()) != NULL))
- {
- const char *name = "";
- row->GetString(0, &name, NULL);
-
- const char *value = "";
- row->GetString(1, &value, NULL);
-
- pData = new CookieData(value);
- pData->changed = false;
-
- pData->timestamp = (row->GetInt(4, (int *)×tamp) == DBVal_Data) ? timestamp : 0;
-
- Cookie *parent = FindCookie(name);
-
- if (parent == NULL)
- {
- const char *desc = "";
- row->GetString(2, &desc, NULL);
-
- access = CookieAccess_Public;
- row->GetInt(3, (int *)&access);
-
- parent = CreateCookie(name, desc, access);
- }
-
- pData->parent = parent;
- parent->data[client] = pData;
- clientData[client].push_back(pData);
- }
-
- statsLoaded[client] = true;
-
- cookieDataLoadedForward->PushCell(client);
- cookieDataLoadedForward->Execute(NULL);
-}
-
-void CookieManager::InsertCookieCallback(Cookie *pCookie, int dbId)
-{
- if (dbId > 0)
- {
- pCookie->dbid = dbId;
- return;
- }
-
- TQueryOp *op = new TQueryOp(Query_SelectId, pCookie);
- /* Put the cookie name into the steamId field to save space - Make sure we remember that it's there */
- UTIL_strncpy(op->m_params.steamId, pCookie->name, MAX_NAME_LENGTH);
- g_ClientPrefs.AddQueryToQueue(op);
-}
-
-void CookieManager::SelectIdCallback(Cookie *pCookie, IQuery *data)
-{
- IResultSet *results;
-
- if (data == NULL || (results = data->GetResultSet()) == NULL)
- {
- return;
- }
-
- IResultRow *row = results->FetchRow();
-
- if (row == NULL)
- {
- return;
- }
-
- row->GetInt(0, &pCookie->dbid);
-}
-
-bool CookieManager::AreClientCookiesCached(int client)
-{
- return statsLoaded[client];
-}
-
-bool CookieManager::AreClientCookiesPending(int client)
-{
- return statsPending[client];
-}
-
-void CookieManager::OnPluginDestroyed(IPlugin *plugin)
-{
- std::vector *pList;
-
- if (plugin->GetProperty("SettingsMenuItems", (void **)&pList, true))
- {
- std::vector &menuitems = (*pList);
- char *name;
- ItemDrawInfo draw;
- const char *info;
- AutoMenuData * data;
- unsigned itemcount;
-
- for (size_t p_iter = 0; p_iter < menuitems.size(); ++p_iter)
- {
- name = menuitems[p_iter];
- itemcount = clientMenu->GetItemCount();
- //remove from this plugins list
- for (unsigned int i=0; i < itemcount; i++)
- {
- info = clientMenu->GetItemInfo(i, &draw);
-
- if (info == NULL)
- {
- continue;
- }
-
- if (strcmp(draw.display, name) == 0)
- {
- data = (AutoMenuData *)strtoul(info, NULL, 16);
-
- if (data->handler->forward != NULL)
- {
- forwards->ReleaseForward(data->handler->forward);
- }
- delete data->handler;
- delete data;
-
- clientMenu->RemoveItem(i);
- break;
- }
- }
-
- delete [] name;
- }
-
- menuitems.clear();
- }
-}
-
-bool CookieManager::GetCookieTime(Cookie *pCookie, int client, time_t *value)
-{
- CookieData *data = pCookie->data[client];
-
- /* Check if a value has been set before */
- if (data == NULL)
- {
- return false;
- }
-
- *value = data->timestamp;
-
- return true;
-}
diff --git a/extensions/clientprefs/cookie.h b/extensions/clientprefs/cookie.h
deleted file mode 100644
index 10c9df3ca6..0000000000
--- a/extensions/clientprefs/cookie.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/**
- * vim: set ts=4 sw=4 tw=99 noet :
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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_CLIENTPREFS_COOKIE_H_
-#define _INCLUDE_SOURCEMOD_CLIENTPREFS_COOKIE_H_
-
-#include "extension.h"
-#include "am-vector.h"
-#include
-
-#define MAX_NAME_LENGTH 30
-#define MAX_DESC_LENGTH 255
-#define MAX_VALUE_LENGTH 100
-
-enum CookieAccess
-{
- CookieAccess_Public, /**< Visible and Changeable by users */
- CookieAccess_Protected, /**< Read only to users */
- CookieAccess_Private, /**< Completely hidden cookie */
-};
-
-struct Cookie;
-
-struct CookieData
-{
- CookieData(const char *value)
- {
- UTIL_strncpy(this->value, value, MAX_VALUE_LENGTH);
- }
-
- char value[MAX_VALUE_LENGTH+1];
- bool changed;
- time_t timestamp;
- Cookie *parent;
-};
-
-struct Cookie
-{
- Cookie(const char *name, const char *description, CookieAccess access)
- {
- UTIL_strncpy(this->name, name, MAX_NAME_LENGTH);
- UTIL_strncpy(this->description, description, MAX_DESC_LENGTH);
-
- this->access = access;
-
- dbid = -1;
-
- for (int i=0; i<=SM_MAXPLAYERS; i++)
- data[i] = NULL;
- }
-
- ~Cookie()
- {
- for (int i=0; i<=SM_MAXPLAYERS; i++)
- {
- if (data[i] != NULL)
- delete data[i];
- }
- }
-
- char name[MAX_NAME_LENGTH+1];
- char description[MAX_DESC_LENGTH+1];
- int dbid;
- CookieData *data[SM_MAXPLAYERS+1];
- CookieAccess access;
-
- static inline bool matches(const char *name, const Cookie *cookie)
- {
- return strcmp(name, cookie->name) == 0;
- }
- static inline uint32_t hash(const detail::CharsAndLength &key)
- {
- return key.hash();
- }
-};
-
-class CookieManager : public IClientListener, public IPluginsListener
-{
-public:
- CookieManager();
- ~CookieManager();
-
- void OnClientAuthorized(int client, const char *authstring);
- void OnClientDisconnecting(int client);
-
- bool GetCookieValue(Cookie *pCookie, int client, char **value);
- bool SetCookieValue(Cookie *pCookie, int client, const char *value);
- bool GetCookieTime(Cookie *pCookie, int client, time_t *value);
-
- void Unload();
-
- void ClientConnectCallback(int serial, IQuery *data);
- void InsertCookieCallback(Cookie *pCookie, int dbId);
- void SelectIdCallback(Cookie *pCookie, IQuery *data);
-
- Cookie *FindCookie(const char *name);
- Cookie *CreateCookie(const char *name, const char *description, CookieAccess access);
-
- bool AreClientCookiesCached(int client);
-
- void OnPluginDestroyed(IPlugin *plugin);
-
- bool AreClientCookiesPending(int client);
-
-public:
- IForward *cookieDataLoadedForward;
- std::vector cookieList;
- IBaseMenu *clientMenu;
-
-private:
- NameHashSet cookieFinder;
- std::vector clientData[SM_MAXPLAYERS+1];
-
- bool connected[SM_MAXPLAYERS+1];
- bool statsLoaded[SM_MAXPLAYERS+1];
- bool statsPending[SM_MAXPLAYERS+1];
-};
-
-extern CookieManager g_CookieManager;
-
-#endif // _INCLUDE_SOURCEMOD_CLIENTPREFS_COOKIE_H_
diff --git a/extensions/clientprefs/extension.cpp b/extensions/clientprefs/extension.cpp
deleted file mode 100644
index 12c24ae7e3..0000000000
--- a/extensions/clientprefs/extension.cpp
+++ /dev/null
@@ -1,551 +0,0 @@
-/**
- * vim: set ts=4 sw=4 tw=99 noet:
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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
-#include "extension.h"
-
-using namespace ke;
-
-/**
- * @file extension.cpp
- * @brief Implement extension code here.
- */
-
-ClientPrefs g_ClientPrefs; /**< Global singleton for extension's main interface */
-
-SMEXT_LINK(&g_ClientPrefs);
-
-HandleType_t g_CookieType = 0;
-CookieTypeHandler g_CookieTypeHandler;
-
-HandleType_t g_CookieIterator = 0;
-CookieIteratorHandler g_CookieIteratorHandler;
-DbDriver g_DriverType;
-
-bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
-{
- DBInfo = dbi->FindDatabaseConf("clientprefs");
-
- if (DBInfo == NULL)
- {
- DBInfo = dbi->FindDatabaseConf("storage-local");
- if (DBInfo == NULL)
- {
- ke::SafeStrcpy(error, maxlength, "Could not find any suitable database configs");
- return false;
- }
- }
-
- if (DBInfo->driver && DBInfo->driver[0] != '\0')
- {
- Driver = dbi->FindOrLoadDriver(DBInfo->driver);
- }
- else
- {
- Driver = dbi->GetDefaultDriver();
- }
-
- if (Driver == NULL)
- {
- ke::SafeSprintf(error, maxlength, "Could not load DB Driver \"%s\"", DBInfo->driver);
- return false;
- }
-
- databaseLoading = true;
- TQueryOp *op = new TQueryOp(Query_Connect, 0);
- dbi->AddToThreadQueue(op, PrioQueue_High);
-
- dbi->AddDependency(myself, Driver);
-
- sharesys->AddNatives(myself, g_ClientPrefNatives);
- sharesys->RegisterLibrary(myself, "clientprefs");
- identity = sharesys->CreateIdentity(sharesys->CreateIdentType("ClientPrefs"), this);
- g_CookieManager.cookieDataLoadedForward = forwards->CreateForward("OnClientCookiesCached", ET_Ignore, 1, NULL, Param_Cell);
-
- g_CookieType = handlesys->CreateType("Cookie",
- &g_CookieTypeHandler,
- 0,
- NULL,
- NULL,
- myself->GetIdentity(),
- NULL);
-
- g_CookieIterator = handlesys->CreateType("CookieIterator",
- &g_CookieIteratorHandler,
- 0,
- NULL,
- NULL,
- myself->GetIdentity(),
- NULL);
-
- IMenuStyle *style = menus->GetDefaultStyle();
- g_CookieManager.clientMenu = style->CreateMenu(&g_Handler, identity);
- g_CookieManager.clientMenu->SetDefaultTitle("Client Settings:");
-
- plsys->AddPluginsListener(&g_CookieManager);
-
- phrases = translator->CreatePhraseCollection();
- phrases->AddPhraseFile("clientprefs.phrases");
- phrases->AddPhraseFile("common.phrases");
-
- if (late)
- {
- this->CatchLateLoadClients();
- }
-
- return true;
-}
-
-void ClientPrefs::SDK_OnAllLoaded()
-{
- playerhelpers->AddClientListener(&g_CookieManager);
-}
-
-bool ClientPrefs::QueryInterfaceDrop(SMInterface *pInterface)
-{
- if ((void *)pInterface == (void *)(Database->GetDriver()))
- {
- return false;
- }
-
- return true;
-}
-
-void ClientPrefs::NotifyInterfaceDrop(SMInterface *pInterface)
-{
- if (Database && (void *)pInterface == (void *)(Database->GetDriver()))
- Database = NULL;
-}
-
-void ClientPrefs::SDK_OnDependenciesDropped()
-{
- // At this point, we're guaranteed that DBI has flushed the worker thread
- // for us, so no cookies should have outstanding queries.
- g_CookieManager.Unload();
-
- handlesys->RemoveType(g_CookieType, myself->GetIdentity());
- handlesys->RemoveType(g_CookieIterator, myself->GetIdentity());
-
- Database = NULL;
-
- if (g_CookieManager.cookieDataLoadedForward != NULL)
- {
- forwards->ReleaseForward(g_CookieManager.cookieDataLoadedForward);
- g_CookieManager.cookieDataLoadedForward = NULL;
- }
-
- if (g_CookieManager.clientMenu != NULL)
- {
- Handle_t menuHandle = g_CookieManager.clientMenu->GetHandle();
-
- if (menuHandle != BAD_HANDLE)
- {
- HandleSecurity sec = HandleSecurity(identity, identity);
- HandleError err = handlesys->FreeHandle(menuHandle, &sec);
- if (HandleError_None != err)
- {
- g_pSM->LogError(myself, "Error %d when attempting to free client menu handle", err);
- }
- }
-
- g_CookieManager.clientMenu = NULL;
- }
-
- if (phrases != NULL)
- {
- phrases->Destroy();
- phrases = NULL;
- }
-
- plsys->RemovePluginsListener(&g_CookieManager);
- playerhelpers->RemoveClientListener(&g_CookieManager);
-}
-
-void ClientPrefs::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
-{
- this->AttemptReconnection();
-}
-
-void ClientPrefs::AttemptReconnection()
-{
- if (Database || databaseLoading)
- return; /* We're already loading, or have loaded. */
-
- g_pSM->LogMessage(myself, "Attempting to reconnect to database...");
- databaseLoading = true;
-
- TQueryOp *op = new TQueryOp(Query_Connect, 0);
- dbi->AddToThreadQueue(op, PrioQueue_High);
-
- this->CatchLateLoadClients(); /* DB reconnection, we should check if we missed anyone... */
-}
-
-void ClientPrefs::DatabaseConnect()
-{
- char error[256];
- int errCode = 0;
-
- Database = AdoptRef(Driver->Connect(DBInfo, true, error, sizeof(error)));
-
- if (!Database)
- {
- g_pSM->LogError(myself, error);
- databaseLoading = false;
- return;
- }
-
- const char *identifier = Driver->GetIdentifier();
-
- if (strcmp(identifier, "sqlite") == 0)
- {
- g_DriverType = Driver_SQLite;
-
- if (!Database->DoSimpleQuery(
- "CREATE TABLE IF NOT EXISTS sm_cookies \
- ( \
- id INTEGER PRIMARY KEY AUTOINCREMENT, \
- name varchar(30) NOT NULL UNIQUE, \
- description varchar(255), \
- access INTEGER \
- )"))
- {
- g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookies: %s", Database->GetError());
- goto fatal_fail;
- }
-
- if (!Database->DoSimpleQuery(
- "CREATE TABLE IF NOT EXISTS sm_cookie_cache \
- ( \
- player varchar(65) NOT NULL, \
- cookie_id int(10) NOT NULL, \
- value varchar(100), \
- timestamp int, \
- PRIMARY KEY (player, cookie_id) \
- )"))
- {
- g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookie_cache: %s", Database->GetError());
- goto fatal_fail;
- }
- }
- else if (strcmp(identifier, "mysql") == 0)
- {
- g_DriverType = Driver_MySQL;
-
- if (!Database->DoSimpleQuery(
- "CREATE TABLE IF NOT EXISTS sm_cookies \
- ( \
- id INTEGER unsigned NOT NULL auto_increment, \
- name varchar(30) NOT NULL UNIQUE, \
- description varchar(255), \
- access INTEGER, \
- PRIMARY KEY (id) \
- )"))
- {
- g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookies: %s", Database->GetError());
- goto fatal_fail;
- }
-
- if (!Database->DoSimpleQuery(
- "CREATE TABLE IF NOT EXISTS sm_cookie_cache \
- ( \
- player varchar(65) NOT NULL, \
- cookie_id int(10) NOT NULL, \
- value varchar(100), \
- timestamp int NOT NULL, \
- PRIMARY KEY (player, cookie_id) \
- )"))
- {
- g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookie_cache: %s", Database->GetError());
- goto fatal_fail;
- }
- }
- else if (strcmp(identifier, "pgsql") == 0)
- {
- g_DriverType = Driver_PgSQL;
- // PostgreSQL supports 'IF NOT EXISTS' as of 9.1
- if (!Database->DoSimpleQuery(
- "CREATE TABLE IF NOT EXISTS sm_cookies \
- ( \
- id serial, \
- name varchar(30) NOT NULL UNIQUE, \
- description varchar(255), \
- access INTEGER, \
- PRIMARY KEY (id) \
- )"))
- {
- g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookies: %s", Database->GetError());
- goto fatal_fail;
- }
-
- if (!Database->DoSimpleQuery(
- "CREATE TABLE IF NOT EXISTS sm_cookie_cache \
- ( \
- player varchar(65) NOT NULL, \
- cookie_id int NOT NULL, \
- value varchar(100), \
- timestamp int NOT NULL, \
- PRIMARY KEY (player, cookie_id) \
- )"))
- {
- g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookie_cache: %s", Database->GetError());
- goto fatal_fail;
- }
-
- if (!Database->DoSimpleQuery(
- "CREATE TABLE IF NOT EXISTS sm_cookie_cache \
- ( \
- player varchar(65) NOT NULL, \
- cookie_id int NOT NULL, \
- value varchar(100), \
- timestamp int NOT NULL, \
- PRIMARY KEY (player, cookie_id) \
- )"))
- {
- g_pSM->LogMessage(myself, "Failed to CreateTable sm_cookie_cache: %s", Database->GetError());
- goto fatal_fail;
- }
-
- if (!Database->DoSimpleQuery(
- "CREATE OR REPLACE FUNCTION add_or_update_cookie(in_player VARCHAR(65), in_cookie INT, in_value VARCHAR(100), in_time INT) RETURNS VOID AS \
- $$ \
- BEGIN \
- LOOP \
- UPDATE sm_cookie_cache SET value = in_value, timestamp = in_time WHERE player = in_player AND cookie_id = in_cookie; \
- IF found THEN \
- RETURN; \
- END IF; \
- BEGIN \
- INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) VALUES (in_player, in_cookie, in_value, in_time); \
- RETURN; \
- EXCEPTION WHEN unique_violation THEN \
- END; \
- END LOOP; \
- END; \
- $$ LANGUAGE plpgsql;"))
- {
- g_pSM->LogMessage(myself, "Failed to create function add_or_update_cookie: %s", Database->GetError());
- goto fatal_fail;
- }
- }
- else
- {
- g_pSM->LogError(myself, "Unsupported driver \"%s\"", identifier);
- goto fatal_fail;
- }
-
- databaseLoading = false;
-
- // Need a new scope because of the goto above.
- {
- std::lock_guard lock(queryLock);
- this->ProcessQueryCache();
- }
- return;
-
-fatal_fail:
- Database = NULL;
- databaseLoading = false;
-}
-
-bool ClientPrefs::AddQueryToQueue(TQueryOp *query)
-{
- {
- std::lock_guard lock(queryLock);
- if (!Database)
- {
- cachedQueries.push_back(query);
- return false;
- }
-
- if (!cachedQueries.empty())
- this->ProcessQueryCache();
- }
-
- query->SetDatabase(Database);
- dbi->AddToThreadQueue(query, PrioQueue_Normal);
- return true;
-}
-
-void ClientPrefs::ProcessQueryCache()
-{
- if (!Database)
- return;
-
- for (size_t iter = 0; iter < cachedQueries.size(); ++iter)
- {
- TQueryOp *op = cachedQueries[iter];
- op->SetDatabase(Database);
- dbi->AddToThreadQueue(op, PrioQueue_Normal);
- }
-
- cachedQueries.clear();
-}
-
-const char *GetPlayerCompatAuthId(IGamePlayer *pPlayer)
-{
- /* For legacy reasons, OnClientAuthorized gives the Steam2 id here if using Steam auth */
- const char *steamId = pPlayer->GetSteam2Id();
- return steamId ? steamId : pPlayer->GetAuthString();
-}
-
-void ClientPrefs::CatchLateLoadClients()
-{
- IGamePlayer *pPlayer;
- for (int i = playerhelpers->GetMaxClients()+1; --i > 0;)
- {
- if (g_CookieManager.AreClientCookiesPending(i) || g_CookieManager.AreClientCookiesCached(i))
- {
- continue;
- }
-
- pPlayer = playerhelpers->GetGamePlayer(i);
-
- if (!pPlayer || !pPlayer->IsAuthorized())
- {
- continue;
- }
-
- g_CookieManager.OnClientAuthorized(i, GetPlayerCompatAuthId(pPlayer));
- }
-}
-
-void ClientPrefs::ClearQueryCache(int serial)
-{
- std::lock_guard lock(queryLock);
-
- for (size_t iter = 0; iter < cachedQueries.size(); ++iter)
- {
- TQueryOp *op = cachedQueries[iter];
- if (op && op->PullQueryType() == Query_SelectData && op->PullQuerySerial() == serial)
- {
- op->Destroy();
- cachedQueries.erase(cachedQueries.begin() + iter);
- iter--;
- }
- }
-}
-
-bool Translate(char *buffer,
- size_t maxlength,
- const char *format,
- unsigned int numparams,
- size_t *pOutLength,
- ...)
-{
- va_list ap;
- unsigned int i;
- const char *fail_phrase;
- void *params[MAX_TRANSLATE_PARAMS];
-
- if (numparams > MAX_TRANSLATE_PARAMS)
- {
- assert(false);
- return false;
- }
-
- va_start(ap, pOutLength);
- for (i = 0; i < numparams; i++)
- {
- params[i] = va_arg(ap, void *);
- }
- va_end(ap);
-
- if (!g_ClientPrefs.phrases->FormatString(buffer,
- maxlength,
- format,
- params,
- numparams,
- pOutLength,
- &fail_phrase))
- {
- if (fail_phrase != NULL)
- {
- g_pSM->LogError(myself, "[SM] Could not find core phrase: %s", fail_phrase);
- }
- else
- {
- g_pSM->LogError(myself, "[SM] Unknown fatal error while translating a core phrase.");
- }
-
- return false;
- }
-
- return true;
-}
-
-char * UTIL_strncpy(char * destination, const char * source, size_t num)
-{
- if (source == NULL)
- {
- destination[0] = '\0';
- return destination;
- }
-
- size_t req = strlen(source);
- if (!req)
- {
- destination[0] = '\0';
- return destination;
- }
- else if (req >= num)
- {
- req = num-1;
- }
-
- strncpy(destination, source, req);
- destination[req] = '\0';
- return destination;
-}
-
-IdentityToken_t *ClientPrefs::GetIdentity() const
-{
- return identity;
-}
-
-const char *ClientPrefs::GetExtensionVerString()
-{
- return SOURCEMOD_VERSION;
-}
-
-const char *ClientPrefs::GetExtensionDateString()
-{
- return SOURCEMOD_BUILD_TIME;
-}
-
-ClientPrefs::ClientPrefs()
-{
- Driver = NULL;
- databaseLoading = false;
- phrases = NULL;
- DBInfo = NULL;
-
- identity = NULL;
-}
diff --git a/extensions/clientprefs/extension.h b/extensions/clientprefs/extension.h
deleted file mode 100644
index 333445c5b9..0000000000
--- a/extensions/clientprefs/extension.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/**
- * vim: set ts=4 sw=4 tw=99 noet:
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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_EXTENSION_PROPER_H_
-#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
-
-#include
-#include
-#include "smsdk_ext.h"
-#include "am-vector.h"
-
-#include
-#include
-
-char * UTIL_strncpy(char * destination, const char * source, size_t num);
-
-#include "cookie.h"
-#include "menus.h"
-#include "query.h"
-
-enum DbDriver
-{
- Driver_MySQL,
- Driver_SQLite,
- Driver_PgSQL
-};
-
-#define MAX_TRANSLATE_PARAMS 32
-
-class TQueryOp;
-
-/**
- * @brief Sample implementation of the SDK Extension.
- * Note: Uncomment one of the pre-defined virtual functions in order to use it.
- */
-class ClientPrefs : public SDKExtension
-{
-public:
- ClientPrefs();
-public:
- /**
- * @brief This is called after the initial loading sequence has been processed.
- *
- * @param error Error message buffer.
- * @param maxlength Size of error message buffer.
- * @param late Whether or not the module was loaded after map load.
- * @return True to succeed loading, false to fail.
- */
- virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
-
- virtual void SDK_OnDependenciesDropped();
-
- /**
- * @brief This is called once all known extensions have been loaded.
- * Note: It is is a good idea to add natives here, if any are provided.
- */
- virtual void SDK_OnAllLoaded();
-
- virtual bool QueryInterfaceDrop(SMInterface *pInterface);
-
- virtual void NotifyInterfaceDrop(SMInterface *pInterface);
-
- const char *GetExtensionVerString();
- const char *GetExtensionDateString();
-
- virtual void OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax);
-
- void DatabaseConnect();
-
- bool AddQueryToQueue(TQueryOp *query);
- void ProcessQueryCache();
-
- void AttemptReconnection();
- void CatchLateLoadClients();
- void ClearQueryCache(int serial);
-
- /**
- * @brief Called when the pause state is changed.
- */
- //virtual void SDK_OnPauseChange(bool paused);
-
- /**
- * @brief this is called when Core wants to know if your extension is working.
- *
- * @param error Error message buffer.
- * @param maxlength Size of error message buffer.
- * @return True if working, false otherwise.
- */
- //virtual bool QueryRunning(char *error, size_t maxlength);
-public:
-#if defined SMEXT_CONF_METAMOD
- /**
- * @brief Called when Metamod is attached, before the extension version is called.
- *
- * @param error Error buffer.
- * @param maxlength Maximum size of error buffer.
- * @param late Whether or not Metamod considers this a late load.
- * @return True to succeed, false to fail.
- */
- //virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late);
-
- /**
- * @brief Called when Metamod is detaching, after the extension version is called.
- * NOTE: By default this is blocked unless sent from SourceMod.
- *
- * @param error Error buffer.
- * @param maxlength Maximum size of error buffer.
- * @return True to succeed, false to fail.
- */
- //virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength);
-
- /**
- * @brief Called when Metamod's pause state is changing.
- * NOTE: By default this is blocked unless sent from SourceMod.
- *
- * @param paused Pause state being set.
- * @param error Error buffer.
- * @param maxlength Maximum size of error buffer.
- * @return True to succeed, false to fail.
- */
- //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
-#endif
-public:
- IdentityToken_t *GetIdentity() const;
-public:
- IDBDriver *Driver;
- ke::RefPtr Database;
- IPhraseCollection *phrases;
- const DatabaseInfo *DBInfo;
-
- bool databaseLoading;
-
-private:
- std::vector cachedQueries;
- std::mutex queryLock;
- IdentityToken_t *identity;
-};
-
-class CookieTypeHandler : public IHandleTypeDispatch
-{
-public:
- void OnHandleDestroy(HandleType_t type, void *object)
- {
- /* No delete needed since Cookies are persistent */
- }
-};
-
-class CookieIteratorHandler : public IHandleTypeDispatch
-{
-public:
- void OnHandleDestroy(HandleType_t type, void *object)
- {
- delete (size_t *)object;
- }
-};
-
-const char *GetPlayerCompatAuthId(IGamePlayer *pPlayer);
-
-extern sp_nativeinfo_t g_ClientPrefNatives[];
-
-extern ClientPrefs g_ClientPrefs;
-extern HandleType_t g_CookieType;
-extern CookieTypeHandler g_CookieTypeHandler;
-
-extern HandleType_t g_CookieIterator;
-extern CookieIteratorHandler g_CookieIteratorHandler;
-
-bool Translate(char *buffer,
- size_t maxlength,
- const char *format,
- unsigned int numparams,
- size_t *pOutLength,
- ...);
-
-extern DbDriver g_DriverType;
-
-#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
-
diff --git a/extensions/clientprefs/menus.cpp b/extensions/clientprefs/menus.cpp
deleted file mode 100644
index 571781892c..0000000000
--- a/extensions/clientprefs/menus.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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 "menus.h"
-
-ClientMenuHandler g_Handler;
-AutoMenuHandler g_AutoHandler;
-
-void ClientMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
-{
- ItemDrawInfo draw;
-
- const char *info = menu->GetItemInfo(item, &draw);
-
- AutoMenuData *data = (AutoMenuData *)strtoul(info, NULL, 16);
-
- if (data->handler->forward != NULL)
- {
- data->handler->forward->PushCell(client);
- data->handler->forward->PushCell(CookieMenuAction_SelectOption);
- data->handler->forward->PushCell(data->datavalue);
- data->handler->forward->PushString("");
- data->handler->forward->PushCell(0);
- data->handler->forward->Execute(NULL);
- }
-
- if (!data->handler->isAutoMenu)
- {
- return;
- }
-
- IBaseMenu *submenu = menus->GetDefaultStyle()->CreateMenu(&g_AutoHandler, g_ClientPrefs.GetIdentity());
-
- char message[256];
-
- Translate(message, sizeof(message), "%T:", 2, NULL, "Choose Option", &client);
- submenu->SetDefaultTitle(message);
-
- if (data->type == CookieMenu_YesNo || data->type == CookieMenu_YesNo_Int)
- {
- Translate(message, sizeof(message), "%T", 2, NULL, "Yes", &client);
- submenu->AppendItem(info, message);
-
- Translate(message, sizeof(message), "%T", 2, NULL, "No", &client);
- submenu->AppendItem(info, message);
- }
- else if (data->type == CookieMenu_OnOff || data->type == CookieMenu_OnOff_Int)
- {
- Translate(message, sizeof(message), "%T", 2, NULL, "On", &client);
- submenu->AppendItem(info, message);
-
- Translate(message, sizeof(message), "%T", 2, NULL, "Off", &client);
- submenu->AppendItem(info, message);
- }
-
- submenu->Display(client, 0, NULL);
-}
-
-unsigned int ClientMenuHandler::OnMenuDisplayItem(IBaseMenu *menu,
- int client,
- IMenuPanel *panel,
- unsigned int item,
- const ItemDrawInfo &dr)
-{
- ItemDrawInfo draw;
-
- const char *info = menu->GetItemInfo(item, &draw);
-
- AutoMenuData *data = (AutoMenuData *)strtoul(info, NULL, 16);
-
- if (data->handler->forward != NULL)
- {
- char buffer[100];
- g_pSM->Format(buffer, sizeof(buffer), "%s", dr.display);
-
- data->handler->forward->PushCell(client);
- data->handler->forward->PushCell(CookieMenuAction_DisplayOption);
- data->handler->forward->PushCell(data->datavalue);
- data->handler->forward->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
- data->handler->forward->PushCell(sizeof(buffer));
- data->handler->forward->Execute(NULL);
-
- ItemDrawInfo newdraw(buffer, draw.style);
-
- return panel->DrawItem(newdraw);
- }
-
- return 0;
-}
-
-void AutoMenuHandler::OnMenuSelect(SourceMod::IBaseMenu *menu, int client, unsigned int item)
-{
- static const char settings[CookieMenu_Elements][2][4] = { {"yes", "no"}, {"1", "0"}, {"on", "off"}, {"1", "0"} };
- ItemDrawInfo draw;
-
- const char *info = menu->GetItemInfo(item, &draw);
-
- AutoMenuData *data = (AutoMenuData *)strtoul(info, NULL, 16);
-
- g_CookieManager.SetCookieValue(data->pCookie, client, settings[data->type][item]);
-
- char message[255];
- char *value;
- g_CookieManager.GetCookieValue(data->pCookie, client, &value);
- Translate(message, sizeof(message), "[SM] %T", 4, NULL, "Cookie Changed Value", &client, &(data->pCookie->name), value);
-
- gamehelpers->TextMsg(client, 3, message);
-}
-
-void AutoMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
-{
- HandleSecurity sec = HandleSecurity(g_ClientPrefs.GetIdentity(), g_ClientPrefs.GetIdentity());
- HandleError err = handlesys->FreeHandle(menu->GetHandle(), &sec);
- if (HandleError_None != err)
- {
- g_pSM->LogError(myself, "Error %d when attempting to free automenu handle", err);
- }
-}
diff --git a/extensions/clientprefs/menus.h b/extensions/clientprefs/menus.h
deleted file mode 100644
index c25682d4f0..0000000000
--- a/extensions/clientprefs/menus.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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_CLIENTPREFS_MENUS_H_
-#define _INCLUDE_SOURCEMOD_CLIENTPREFS_MENUS_H_
-
-#include "extension.h"
-#include "cookie.h"
-
-enum CookieMenuAction
-{
- /**
- * An option is being drawn for a menu.
- *
- * INPUT : Client index and any data if available.
- * OUTPUT: Buffer for rendering, maxlength of buffer.
- */
- CookieMenuAction_DisplayOption = 0,
-
- /**
- * A menu option has been selected.
- *
- * INPUT : Client index and any data if available.
- */
- CookieMenuAction_SelectOption = 1,
-};
-
-enum CookieMenu
-{
- CookieMenu_YesNo, /**< Yes/No menu with "yes"/"no" results saved into the cookie */
- CookieMenu_YesNo_Int, /**< Yes/No menu with 1/0 saved into the cookie */
- CookieMenu_OnOff, /**< On/Off menu with "on"/"off" results saved into the cookie */
- CookieMenu_OnOff_Int, /**< On/Off menu with 1/0 saved into the cookie */
- CookieMenu_Elements
-};
-
-struct ItemHandler
-{
- IChangeableForward *forward;
- CookieMenu autoMenuType;
- bool isAutoMenu;
-};
-
-class ClientMenuHandler : public IMenuHandler
-{
- void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
- unsigned int OnMenuDisplayItem(IBaseMenu *menu,
- int client,
- IMenuPanel *panel,
- unsigned int item,
- const ItemDrawInfo &dr);
-};
-
-class AutoMenuHandler : public IMenuHandler
-{
- void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
- void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
-};
-
-extern ClientMenuHandler g_Handler;
-extern AutoMenuHandler g_AutoHandler;
-
-/* Something went wrong with the includes and made me do this */
-struct Cookie;
-enum CookieMenu;
-struct ItemHandler;
-
-struct AutoMenuData
-{
- ItemHandler *handler;
- Cookie *pCookie;
- cell_t datavalue;
- CookieMenu type;
-};
-
-#endif // _INCLUDE_SOURCEMOD_CLIENTPREFS_MENUS_H_
diff --git a/extensions/clientprefs/natives.cpp b/extensions/clientprefs/natives.cpp
deleted file mode 100644
index a7b9279940..0000000000
--- a/extensions/clientprefs/natives.cpp
+++ /dev/null
@@ -1,552 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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 "extension.h"
-#include "cookie.h"
-#include "menus.h"
-
-cell_t RegClientPrefCookie(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- char *name;
- pContext->LocalToString(params[1], &name);
-
- if (name[0] == '\0')
- {
- return pContext->ThrowNativeError("Cannot create preference cookie with no name");
- }
-
- char *desc;
- pContext->LocalToString(params[2], &desc);
-
- Cookie *pCookie = g_CookieManager.CreateCookie(name, desc, (CookieAccess)params[3]);
-
- if (!pCookie)
- {
- return BAD_HANDLE;
- }
-
- return handlesys->CreateHandle(g_CookieType,
- pCookie,
- pContext->GetIdentity(),
- myself->GetIdentity(),
- NULL);
-}
-
-cell_t FindClientPrefCookie(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- char *name;
- pContext->LocalToString(params[1], &name);
-
- Cookie *pCookie = g_CookieManager.FindCookie(name);
-
- if (!pCookie)
- {
- return BAD_HANDLE;
- }
-
- return handlesys->CreateHandle(g_CookieType,
- pCookie,
- pContext->GetIdentity(),
- myself->GetIdentity(),
- NULL);
-}
-
-size_t IsAuthIdConnected(char *authID)
-{
- IGamePlayer *player;
- const char *authString;
-
- for (int playerIndex = playerhelpers->GetMaxClients()+1; --playerIndex > 0;)
- {
- player = playerhelpers->GetGamePlayer(playerIndex);
- if (player == NULL || !player->IsAuthorized())
- {
- continue;
- }
-
- if (!strcmp(player->GetAuthString(), authID)
- || !strcmp(player->GetSteam2Id(), authID)
- || !strcmp(player->GetSteam3Id(), authID)
- )
- {
- return playerIndex;
- }
- }
-
- return 0;
-}
-
-cell_t SetAuthIdCookie(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- char *steamID;
- pContext->LocalToString(params[1], &steamID);
-
- // convert cookie handle to Cookie*
- Handle_t hndl = static_cast(params[2]);
- HandleError err;
- HandleSecurity sec;
-
- sec.pOwner = NULL;
- sec.pIdentity = myself->GetIdentity();
-
- Cookie *pCookie;
-
- if ((err = handlesys->ReadHandle(hndl, g_CookieType, &sec, (void **)&pCookie))
- != HandleError_None)
- {
- return pContext->ThrowNativeError("Invalid Cookie handle %x (error %d)", hndl, err);
- }
-
- int i_dbId = pCookie->dbid;
- char *value;
- pContext->LocalToString(params[3], &value);
-
- // make sure the steamID isn't currently connected
- if (int client = IsAuthIdConnected(steamID))
- {
- // use regular code for connected players
- return g_CookieManager.SetCookieValue(pCookie, client, value);
- }
-
- // constructor calls strncpy for us
- CookieData *payload = new CookieData(value);
-
- // set changed so players connecting later in during the same map will have the correct value
- payload->changed = true;
- payload->timestamp = time(NULL);
-
- // edit database table
- TQueryOp *op = new TQueryOp(Query_InsertData, pCookie);
- // limit player auth length which doubles for cookie name length
- UTIL_strncpy(op->m_params.steamId, steamID, MAX_NAME_LENGTH);
- op->m_params.cookieId = i_dbId;
- op->m_params.data = payload;
-
- g_ClientPrefs.AddQueryToQueue(op);
-
- return 1;
-}
-
-cell_t SetClientPrefCookie(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- int client = params[1];
-
- if ((client < 1) || (client > playerhelpers->GetMaxClients()))
- {
- return pContext->ThrowNativeError("Client index %d is invalid", client);
- }
-
- Handle_t hndl = static_cast(params[2]);
- HandleError err;
- HandleSecurity sec;
-
- sec.pOwner = NULL;
- sec.pIdentity = myself->GetIdentity();
-
- Cookie *pCookie;
-
- if ((err = handlesys->ReadHandle(hndl, g_CookieType, &sec, (void **)&pCookie))
- != HandleError_None)
- {
- return pContext->ThrowNativeError("Invalid Cookie handle %x (error %d)", hndl, err);
- }
-
- char *value;
- pContext->LocalToString(params[3], &value);
-
- return g_CookieManager.SetCookieValue(pCookie, client, value);
-}
-
-cell_t GetClientPrefCookie(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- int client = params[1];
-
- if ((client < 1) || (client > playerhelpers->GetMaxClients()))
- {
- return pContext->ThrowNativeError("Client index %d is invalid", client);
- }
-
- Handle_t hndl = static_cast(params[2]);
- HandleError err;
- HandleSecurity sec;
-
- sec.pOwner = NULL;
- sec.pIdentity = myself->GetIdentity();
-
- Cookie *pCookie;
-
- if ((err = handlesys->ReadHandle(hndl, g_CookieType, &sec, (void **)&pCookie))
- != HandleError_None)
- {
- return pContext->ThrowNativeError("Invalid Cookie handle %x (error %d)", hndl, err);
- }
-
- char *value = NULL;
-
- g_CookieManager.GetCookieValue(pCookie, client, &value);
-
- pContext->StringToLocal(params[3], params[4], value);
-
- return 1;
-}
-
-cell_t AreClientCookiesCached(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- int client = params[1];
-
- if ((client < 1) || (client > playerhelpers->GetMaxClients()))
- {
- return pContext->ThrowNativeError("Client index %d is invalid", client);
- }
-
- return g_CookieManager.AreClientCookiesCached(client);
-}
-
-cell_t GetCookieAccess(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- Handle_t hndl = static_cast(params[1]);
- HandleError err;
- HandleSecurity sec;
-
- sec.pOwner = NULL;
- sec.pIdentity = myself->GetIdentity();
-
- Cookie *pCookie;
-
- if ((err = handlesys->ReadHandle(hndl, g_CookieType, &sec, (void **)&pCookie))
- != HandleError_None)
- {
- return pContext->ThrowNativeError("Invalid Cookie handle %x (error %d)", hndl, err);
- }
-
- return pCookie->access;
-}
-
-static cell_t GetCookieIterator(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- size_t *iter = new size_t;
- *iter = 0;
-
- Handle_t hndl = handlesys->CreateHandle(g_CookieIterator, iter, pContext->GetIdentity(), myself->GetIdentity(), NULL);
- if (hndl == BAD_HANDLE)
- {
- delete iter;
- }
-
- return hndl;
-}
-
-static cell_t ReadCookieIterator(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- size_t *iter;
-
- Handle_t hndl = static_cast(params[1]);
- HandleError err;
- HandleSecurity sec;
-
- sec.pOwner = NULL;
- sec.pIdentity = myself->GetIdentity();
-
- if ((err = handlesys->ReadHandle(hndl, g_CookieIterator, &sec, (void **)&iter))
- != HandleError_None)
- {
- return pContext->ThrowNativeError("Invalid Cookie iterator handle %x (error %d)", hndl, err);
- }
-
- if (*iter >= g_CookieManager.cookieList.size())
- {
- return 0;
- }
-
- Cookie *pCookie = g_CookieManager.cookieList[(*iter)++];
-
- pContext->StringToLocalUTF8(params[2], params[3], pCookie->name, NULL);
- pContext->StringToLocalUTF8(params[5], params[6], pCookie->description, NULL);
-
- cell_t *addr;
- pContext->LocalToPhysAddr(params[4], &addr);
- *addr = pCookie->access;
-
- return 1;
-}
-
-cell_t ShowSettingsMenu(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- char message[256];
- Translate(message, sizeof(message), "%T:", 2, NULL, "Client Settings", ¶ms[1]);
-
- g_CookieManager.clientMenu->SetDefaultTitle(message);
- g_CookieManager.clientMenu->Display(params[1], 0, NULL);
-
- return 0;
-}
-
-cell_t AddSettingsMenuItem(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- char *display;
- pContext->LocalToString(params[3], &display);
-
- /* Register a callback */
- ItemHandler *pItem = new ItemHandler;
- pItem->isAutoMenu = false;
- pItem->forward = forwards->CreateForwardEx(NULL, ET_Ignore, 5, NULL, Param_Cell, Param_Cell, Param_Cell, Param_String, Param_Cell);
-
- pItem->forward->AddFunction(pContext, static_cast(params[1]));
-
- char info[20];
- AutoMenuData *data = new AutoMenuData;
- data->datavalue = params[2];
- data->handler = pItem;
- g_pSM->Format(info, sizeof(info), "%x", data);
-
- ItemDrawInfo draw(display, 0);
-
- g_CookieManager.clientMenu->AppendItem(info, draw);
-
- /* Track this in case the plugin unloads */
-
- IPlugin *pPlugin = plsys->FindPluginByContext(pContext->GetContext());
- std::vector *pList = NULL;
-
- if (!pPlugin->GetProperty("SettingsMenuItems", (void **)&pList, false) || !pList)
- {
- pList = new std::vector;
- pPlugin->SetProperty("SettingsMenuItems", pList);
- }
-
- char *copyarray = new char[strlen(display)+1];
- g_pSM->Format(copyarray, strlen(display)+1, "%s", display);
-
- pList->push_back(copyarray);
-
- return 0;
-}
-
-cell_t AddSettingsPrefabMenuItem(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- Handle_t hndl = static_cast(params[1]);
- HandleError err;
- HandleSecurity sec;
-
- sec.pOwner = NULL;
- sec.pIdentity = myself->GetIdentity();
-
- Cookie *pCookie;
-
- if ((err = handlesys->ReadHandle(hndl, g_CookieType, &sec, (void **)&pCookie))
- != HandleError_None)
- {
- return pContext->ThrowNativeError("Invalid Cookie handle %x (error %d)", hndl, err);
- }
-
- /* Register a callback */
- ItemHandler *pItem = new ItemHandler;
- pItem->isAutoMenu = true;
- pItem->autoMenuType = (CookieMenu)params[2];
-
-
- /* User passed a function id for a callback */
- if (params[4] != -1)
- {
- pItem->forward = forwards->CreateForwardEx(NULL, ET_Ignore, 5, NULL, Param_Cell, Param_Cell, Param_Cell, Param_String, Param_Cell);
- pItem->forward->AddFunction(pContext, static_cast(params[4]));
- }
- else
- {
- pItem->forward = NULL;
- }
-
- char *display;
- pContext->LocalToString(params[3], &display);
-
- ItemDrawInfo draw(display, 0);
-
- char info[20];
- AutoMenuData *data = new AutoMenuData;
- data->datavalue = params[5];
- data->pCookie = pCookie;
- data->type = (CookieMenu)params[2];
- data->handler = pItem;
- g_pSM->Format(info, sizeof(info), "%x", data);
-
- g_CookieManager.clientMenu->AppendItem(info, draw);
-
- /* Track this in case the plugin unloads */
-
- IPlugin *pPlugin = plsys->FindPluginByContext(pContext->GetContext());
- std::vector *pList = NULL;
-
- if (!pPlugin->GetProperty("SettingsMenuItems", (void **)&pList, false) || !pList)
- {
- pList = new std::vector;
- pPlugin->SetProperty("SettingsMenuItems", pList);
- }
-
- char *copyarray = new char[strlen(display)+1];
- g_pSM->Format(copyarray, strlen(display)+1, "%s", display);
-
- pList->push_back(copyarray);
-
- return 0;
-}
-
-cell_t GetClientCookieTime(IPluginContext *pContext, const cell_t *params)
-{
- g_ClientPrefs.AttemptReconnection();
-
- Handle_t hndl = static_cast(params[2]);
- HandleError err;
- HandleSecurity sec;
-
- sec.pOwner = NULL;
- sec.pIdentity = myself->GetIdentity();
-
- Cookie *pCookie;
-
- if ((err = handlesys->ReadHandle(hndl, g_CookieType, &sec, (void **)&pCookie))
- != HandleError_None)
- {
- return pContext->ThrowNativeError("Invalid Cookie handle %x (error %d)", hndl, err);
- }
-
- time_t value;
-
- if (!g_CookieManager.GetCookieTime(pCookie, params[1], &value))
- {
- return 0;
- }
-
- return value;
-}
-
-static cell_t Cookie_Set(IPluginContext *pContext, const cell_t *params)
-{
- // This version takes (handle, client, value). The original is (client, handle, value).
- const cell_t new_params[4] = {
- 3,
- params[2],
- params[1],
- params[3]
- };
-
- return SetClientPrefCookie(pContext, new_params);
-}
-
-static cell_t Cookie_Get(IPluginContext *pContext, const cell_t *params)
-{
- // This verson takes (handle, client, buffer, maxlen). The original is (client, handle, buffer, maxlen).
- const cell_t new_params[5] = {
- 4,
- params[2],
- params[1],
- params[3],
- params[4]
- };
-
- return GetClientPrefCookie(pContext, new_params);
-}
-
-static cell_t Cookie_SetByAuthId(IPluginContext *pContext, const cell_t *params)
-{
- // This version takes (handle, authid, value). The original is (authid, handle, value).
- const cell_t new_params[4] = {
- 3,
- params[2],
- params[1],
- params[3]
- };
-
- return SetAuthIdCookie(pContext, new_params);
-}
-
-static cell_t Cookie_GetClientTime(IPluginContext *pContext, const cell_t *params)
-{
- // This version takes (handle, client). The original is (client, handle)
- const cell_t new_params[3] = {
- 2,
- params[2],
- params[1],
- };
-
- return GetClientCookieTime(pContext, new_params);
-}
-
-sp_nativeinfo_t g_ClientPrefNatives[] =
-{
- {"RegClientCookie", RegClientPrefCookie},
- {"FindClientCookie", FindClientPrefCookie},
- {"SetClientCookie", SetClientPrefCookie},
- {"SetAuthIdCookie", SetAuthIdCookie},
- {"GetClientCookie", GetClientPrefCookie},
- {"AreClientCookiesCached", AreClientCookiesCached},
- {"GetCookieAccess", GetCookieAccess},
- {"ReadCookieIterator", ReadCookieIterator},
- {"GetCookieIterator", GetCookieIterator},
- {"ShowCookieMenu", ShowSettingsMenu},
- {"SetCookieMenuItem", AddSettingsMenuItem},
- {"SetCookiePrefabMenu", AddSettingsPrefabMenuItem},
- {"GetClientCookieTime", GetClientCookieTime},
-
- {"Cookie.Cookie", RegClientPrefCookie},
- {"Cookie.Find", FindClientPrefCookie},
- {"Cookie.Set", Cookie_Set},
- {"Cookie.Get", Cookie_Get},
- {"Cookie.SetByAuthId", Cookie_SetByAuthId},
- {"Cookie.SetPrefabMenu", AddSettingsPrefabMenuItem},
- {"Cookie.GetClientTime", Cookie_GetClientTime},
- {"Cookie.AccessLevel.get", GetCookieAccess},
-
- {NULL, NULL}
-};
diff --git a/extensions/clientprefs/query.cpp b/extensions/clientprefs/query.cpp
deleted file mode 100644
index a559b0abd9..0000000000
--- a/extensions/clientprefs/query.cpp
+++ /dev/null
@@ -1,346 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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 "query.h"
-
-
-void TQueryOp::RunThinkPart()
-{
- //handler for threaded sql queries
- switch (m_type)
- {
- case Query_InsertCookie:
- {
- g_CookieManager.InsertCookieCallback(m_pCookie, m_insertId);
- break;
- }
-
- case Query_SelectData:
- {
- g_CookieManager.ClientConnectCallback(m_serial, m_pResult);
- break;
- }
-
- case Query_SelectId:
- {
- g_CookieManager.SelectIdCallback(m_pCookie, m_pResult);
- break;
- }
-
- case Query_Connect:
- {
- return;
- }
-
- default:
- {
- break;
- }
- }
-}
-
-void TQueryOp::RunThreadPart()
-{
- if (m_type == Query_Connect)
- {
- g_ClientPrefs.DatabaseConnect();
- return;
- }
-
- assert(m_database != NULL);
- /* I don't think this is needed anymore... keeping for now. */
- m_database->LockForFullAtomicOperation();
- if (!BindParamsAndRun())
- {
- g_pSM->LogError(myself,
- "Failed SQL Query, Error: \"%s\" (Query id %i - serial %i)",
- m_database->GetError(),
- m_type,
- m_serial);
- }
-
- m_database->UnlockFromFullAtomicOperation();
-}
-
-IDBDriver *TQueryOp::GetDriver()
-{
- return m_driver;
-}
-
-IdentityToken_t *TQueryOp::GetOwner()
-{
- return myself->GetIdentity();
-}
-
-void TQueryOp::Destroy()
-{
- if (m_pResult != NULL)
- {
- m_pResult->Destroy();
- }
- delete this;
-}
-
-TQueryOp::TQueryOp(enum querytype type, int serial)
-{
- m_type = type;
- m_serial = serial;
- m_database = NULL;
- m_driver = NULL;
- m_insertId = -1;
- m_pResult = NULL;
-}
-
-TQueryOp::TQueryOp(enum querytype type, Cookie *cookie)
-{
- m_type = type;
- m_pCookie = cookie;
- m_database = NULL;
- m_driver = NULL;
- m_insertId = -1;
- m_pResult = NULL;
- m_serial = 0;
-}
-
-void TQueryOp::SetDatabase(IDatabase *db)
-{
- m_database = db;
- m_driver = m_database->GetDriver();
-}
-
-bool TQueryOp::BindParamsAndRun()
-{
- size_t ignore;
- char query[2048];
-
- switch (m_type)
- {
- case Query_InsertCookie:
- {
- char safe_name[MAX_NAME_LENGTH*2 + 1];
- char safe_desc[MAX_DESC_LENGTH*2 + 1];
-
- m_database->QuoteString(m_params.cookie->name,
- safe_name,
- sizeof(safe_name),
- &ignore);
- m_database->QuoteString(m_params.cookie->description,
- safe_desc,
- sizeof(safe_desc),
- &ignore);
-
- if (g_DriverType == Driver_MySQL)
- {
- g_pSM->Format(query,
- sizeof(query),
- "INSERT IGNORE INTO sm_cookies (name, description, access) \
- VALUES (\"%s\", \"%s\", %d)",
- safe_name,
- safe_desc,
- m_params.cookie->access);
- }
- else if (g_DriverType == Driver_SQLite)
- {
- g_pSM->Format(query,
- sizeof(query),
- "INSERT OR IGNORE INTO sm_cookies (name, description, access) \
- VALUES ('%s', '%s', %d)",
- safe_name,
- safe_desc,
- m_params.cookie->access);
- }
- else if (g_DriverType == Driver_PgSQL)
- {
- // just insert. Returns error on already exists, so ignore the error.
- g_pSM->Format(query,
- sizeof(query),
- "INSERT INTO sm_cookies (name, description, access) \
- VALUES ('%s', '%s', %d)",
- safe_name,
- safe_desc,
- m_params.cookie->access);
- }
-
- if (!m_database->DoSimpleQuery(query))
- {
- return false;
- }
-
- m_insertId = m_database->GetInsertID();
-
- return true;
- }
-
- case Query_SelectData:
- {
- char safe_str[128];
-
- m_database->QuoteString(m_params.steamId, safe_str, sizeof(safe_str), &ignore);
-
- g_pSM->Format(query,
- sizeof(query),
- "SELECT sm_cookies.name, sm_cookie_cache.value, sm_cookies.description, \
- sm_cookies.access, sm_cookie_cache.timestamp \
- FROM sm_cookies \
- JOIN sm_cookie_cache \
- ON sm_cookies.id = sm_cookie_cache.cookie_id \
- WHERE player = '%s'",
- safe_str);
-
- m_pResult = m_database->DoQuery(query);
-
- return (m_pResult != NULL);
- }
-
- case Query_InsertData:
- {
- char safe_id[128];
- char safe_val[MAX_VALUE_LENGTH*2 + 1];
-
- m_database->QuoteString(m_params.steamId,
- safe_id,
- sizeof(safe_id),
- &ignore);
- m_database->QuoteString(m_params.data->value,
- safe_val,
- sizeof(safe_val),
- &ignore);
-
- if (g_DriverType == Driver_MySQL)
- {
- g_pSM->Format(query,
- sizeof(query),
- "INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) \
- VALUES (\"%s\", %d, \"%s\", %d) \
- ON DUPLICATE KEY UPDATE \
- value = \"%s\", timestamp = %d",
- safe_id,
- m_params.cookieId,
- safe_val,
- (unsigned int)m_params.data->timestamp,
- safe_val,
- (unsigned int)m_params.data->timestamp);
- }
- else if (g_DriverType == Driver_SQLite)
- {
- g_pSM->Format(query,
- sizeof(query),
- "INSERT OR REPLACE INTO sm_cookie_cache \
- (player, cookie_id, value, timestamp) \
- VALUES ('%s', %d, '%s', %d)",
- safe_id,
- m_params.cookieId,
- safe_val,
- (unsigned int)m_params.data->timestamp);
- }
- else if (g_DriverType == Driver_PgSQL)
- {
- // Using a PL/Pgsql function, called add_or_update_cookie(),
- // since Postgres does not have an 'OR REPLACE' functionality.
- g_pSM->Format(query,
- sizeof(query),
- "SELECT add_or_update_cookie ('%s', %d, '%s', %d)",
- safe_id,
- m_params.cookieId,
- safe_val,
- (unsigned int)m_params.data->timestamp);
- }
-
- if (!m_database->DoSimpleQuery(query))
- {
- return false;
- }
-
- m_insertId = m_database->GetInsertID();
-
- return true;
- }
-
- case Query_SelectId:
- {
- char safe_name[MAX_NAME_LENGTH*2 + 1];
-
- /* the steamId var was actually used to store the name of the cookie - Save duplicating vars */
- m_database->QuoteString(m_params.steamId,
- safe_name,
- sizeof(safe_name),
- &ignore);
-
- g_pSM->Format(query,
- sizeof(query),
- "SELECT id FROM sm_cookies WHERE name = '%s'",
- safe_name);
-
- m_pResult = m_database->DoQuery(query);
-
- return (m_pResult != NULL);
- }
-
- default:
- {
- break;
- }
-
- }
-
- assert(false);
-
- return false;
-}
-
-querytype TQueryOp::PullQueryType()
-{
- return m_type;
-}
-
-int TQueryOp::PullQuerySerial()
-{
- return m_serial;
-}
-
-ParamData::~ParamData()
-{
- if (data)
- {
- /* Data is only ever passed in a client disconnect query and always needs to be deleted */
- delete data;
- data = NULL;
- }
-}
-
-ParamData::ParamData()
-{
- cookie = NULL;
- data = NULL;
- steamId[0] = '\0';
- cookieId = 0;
-}
-
diff --git a/extensions/clientprefs/query.h b/extensions/clientprefs/query.h
deleted file mode 100644
index 31cdf55d01..0000000000
--- a/extensions/clientprefs/query.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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_CLIENTPREFS_QUERY_H_
-#define _INCLUDE_SOURCEMOD_CLIENTPREFS_QUERY_H_
-
-#include "extension.h"
-#include "cookie.h"
-#include "sh_string.h"
-
-enum querytype
-{
- Query_InsertCookie = 0,
- Query_SelectData,
- Query_InsertData,
- Query_SelectId,
- Query_Connect,
-};
-
-struct Cookie;
-struct CookieData;
-#define MAX_NAME_LENGTH 30
-
-/* This stores all the info required for our param binding until the thread is executed */
-struct ParamData
-{
- ParamData();
-
- ~ParamData();
-
- /* Contains a name, description and access for InsertCookie queries */
- Cookie *cookie;
- /* A clients steamid - Used for most queries - Doubles as storage for the cookie name*/
- char steamId[MAX_NAME_LENGTH];
-
- int cookieId;
- CookieData *data;
-};
-
-class TQueryOp : public IDBThreadOperation
-{
-public:
- TQueryOp(enum querytype type, int serial);
- TQueryOp(enum querytype type, Cookie *cookie);
- ~TQueryOp() {}
-
- IDBDriver *GetDriver();
- IdentityToken_t *GetOwner();
-
- void SetDatabase(IDatabase *db);
-
- void Destroy();
-
- void RunThreadPart();
- /* Thread has been cancelled due to driver unloading. Nothing else to do? */
- void CancelThinkPart() {}
- void RunThinkPart();
-
- bool BindParamsAndRun();
-
- /* Params to be bound */
- ParamData m_params;
-
- inline IDatabase *GetDB()
- {
- return m_database;
- }
-
-public:
- querytype PullQueryType();
- int PullQuerySerial();
-
-private:
- IDatabase *m_database;
- IDBDriver *m_driver;
- IQuery *m_pResult;
-
- /* Query type */
- enum querytype m_type;
-
- /* Data to be passed to the callback */
- int m_serial;
- int m_insertId;
- Cookie *m_pCookie;
-};
-
-
-#endif // _INCLUDE_SOURCEMOD_CLIENTPREFS_QUERY_H_
diff --git a/extensions/clientprefs/smsdk_config.h b/extensions/clientprefs/smsdk_config.h
deleted file mode 100644
index 9a7f0c63f1..0000000000
--- a/extensions/clientprefs/smsdk_config.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Client Preferences Extension
- * 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_EXTENSION_CONFIG_H_
-#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
-
-/**
- * @file smsdk_config.h
- * @brief Contains macros for configuring basic extension information.
- */
-
-/* Basic information exposed publicly */
-#define SMEXT_CONF_NAME "Client Preferences"
-#define SMEXT_CONF_DESCRIPTION "Saves client preference settings"
-#define SMEXT_CONF_VERSION ""
-#define SMEXT_CONF_AUTHOR "AlliedModders"
-#define SMEXT_CONF_URL "http://www.sourcemod.net/"
-#define SMEXT_CONF_LOGTAG "CLIENTPREFS"
-#define SMEXT_CONF_LICENSE "GPL"
-#define SMEXT_CONF_DATESTRING ""
-
-/**
- * @brief Exposes plugin's main interface.
- */
-#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name;
-
-/**
- * @brief Sets whether or not this plugin required Metamod.
- * NOTE: Uncomment to enable, comment to disable.
- */
-//#define SMEXT_CONF_METAMOD
-
-/** Enable interfaces you want to use here by uncommenting lines */
-#define SMEXT_ENABLE_FORWARDSYS
-#define SMEXT_ENABLE_HANDLESYS
-#define SMEXT_ENABLE_PLAYERHELPERS
-#define SMEXT_ENABLE_DBMANAGER
-//#define SMEXT_ENABLE_GAMECONF
-//#define SMEXT_ENABLE_MEMUTILS
-#define SMEXT_ENABLE_GAMEHELPERS
-//#define SMEXT_ENABLE_TIMERSYS
-//#define SMEXT_ENABLE_THREADER
-//#define SMEXT_ENABLE_LIBSYS
-#define SMEXT_ENABLE_MENUS
-//#define SMEXT_ENABLE_ADTFACTORY
-#define SMEXT_ENABLE_PLUGINSYS
-//#define SMEXT_ENABLE_ADMINSYS
-//#define SMEXT_ENABLE_TEXTPARSERS
-//#define SMEXT_ENABLE_USERMSGS
-#define SMEXT_ENABLE_TRANSLATOR
-
-#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
diff --git a/extensions/clientprefs/version.rc b/extensions/clientprefs/version.rc
deleted file mode 100644
index 0b13c606b9..0000000000
--- a/extensions/clientprefs/version.rc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Microsoft Visual C++ generated resource script.
-//
-//#include "resource.h"
-
-#define APSTUDIO_READONLY_SYMBOLS
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 2 resource.
-//
-#include "winres.h"
-
-#include
-
-/////////////////////////////////////////////////////////////////////////////
-#undef APSTUDIO_READONLY_SYMBOLS
-
-/////////////////////////////////////////////////////////////////////////////
-// English (U.S.) resources
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-#ifdef _WIN32
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-#endif //_WIN32
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Version
-//
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION SM_VERSION_FILE
- PRODUCTVERSION SM_VERSION_FILE
- FILEFLAGSMASK 0x17L
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x4L
- FILETYPE 0x2L
- FILESUBTYPE 0x0L
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "000004b0"
- BEGIN
- VALUE "Comments", "Client Preferences Extension"
- VALUE "FileDescription", "SourceMod Client Preferences Extension"
- VALUE "FileVersion", SM_VERSION_STRING
- VALUE "InternalName", "SourceMod Client Preferences Extension"
- VALUE "LegalCopyright", "Copyright (c) 2004-2008, AlliedModders LLC"
- VALUE "OriginalFilename", BINARY_NAME
- VALUE "ProductName", "SourceMod Client Preferences Extension"
- VALUE "ProductVersion", SM_VERSION_STRING
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x0, 1200
- END
-END
-
-
-#ifdef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// TEXTINCLUDE
-//
-
-1 TEXTINCLUDE
-BEGIN
- "resource.h\0"
-END
-
-2 TEXTINCLUDE
-BEGIN
- "#include ""winres.h""\r\n"
- "\0"
-END
-
-3 TEXTINCLUDE
-BEGIN
- "\r\n"
- "\0"
-END
-
-#endif // APSTUDIO_INVOKED
-
-#endif // English (U.S.) resources
-/////////////////////////////////////////////////////////////////////////////
-
-
-
-#ifndef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 3 resource.
-//
-
-
-/////////////////////////////////////////////////////////////////////////////
-#endif // not APSTUDIO_INVOKED
-
diff --git a/plugins/clientprefs.sp b/plugins/clientprefs.sp
index d22fa3aa4b..12a6211c2b 100644
--- a/plugins/clientprefs.sp
+++ b/plugins/clientprefs.sp
@@ -1,174 +1,158 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Map Management Plugin
- * Provides all map related functionality, including map changing, map voting,
- * and nextmap.
- *
- * SourceMod (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$
- */
-
-
-#pragma semicolon 1
-#include
-#include
-
-#pragma newdecls required
-
-public Plugin myinfo =
-{
- name = "Client Preferences",
- author = "AlliedModders LLC",
- description = "Client preferences and settings menu",
- version = SOURCEMOD_VERSION,
- url = "http://www.sourcemod.net/"
-};
-
-public void OnPluginStart()
-{
- LoadTranslations("clientprefs.phrases");
-
- RegConsoleCmd("sm_cookies", Command_Cookie, "sm_cookies [value]");
- RegConsoleCmd("sm_settings", Command_Settings);
-}
-
-public Action Command_Cookie(int client, int args)
-{
- if (args == 0)
- {
- ReplyToCommand(client, "[SM] Usage: sm_cookies [value]");
- ReplyToCommand(client, "[SM] %t", "Printing Cookie List");
-
- /* Show list of cookies */
- Handle iter = GetCookieIterator();
-
- char name[30];
- char description[255];
-
- PrintToConsole(client, "%t:", "Cookie List");
-
- CookieAccess access;
-
- int count = 1;
-
- while (ReadCookieIterator(iter, name, sizeof(name), access, description, sizeof(description)) != false)
- {
- if (access < CookieAccess_Private)
- {
- PrintToConsole(client, "[%03d] %s - %s", count++, name, description);
- }
- }
-
- delete iter;
- return Plugin_Handled;
- }
-
- if (client == 0)
- {
- PrintToServer("%T", "No Console", LANG_SERVER);
- return Plugin_Handled;
- }
-
- char name[30];
-
- GetCmdArg(1, name, sizeof(name));
-
- Handle cookie = FindClientCookie(name);
-
- if (cookie == null)
- {
- ReplyToCommand(client, "[SM] %t", "Cookie not Found", name);
- return Plugin_Handled;
- }
-
- CookieAccess access = GetCookieAccess(cookie);
-
- if (access == CookieAccess_Private)
- {
- ReplyToCommand(client, "[SM] %t", "Cookie not Found", name);
- delete cookie;
- return Plugin_Handled;
- }
-
- char value[100];
-
- if (args == 1)
- {
- Handle iter = GetCookieIterator();
-
- GetClientCookie(client, cookie, value, sizeof(value));
- ReplyToCommand(client, "[SM] %t", "Cookie Value", name, value);
-
- char CookieName[30];
- char description[255];
-
- while (ReadCookieIterator(iter, CookieName, sizeof(CookieName), access, description, sizeof(description)) != false) // We're allowed to re-use access since we're about to return anyways.
- {
- if (StrEqual(CookieName, name, true))
- {
- TrimString(description);
- if (description[0] != EOS)
- ReplyToCommand(client, "- %s", description);
-
- break;
- }
- }
-
- delete iter;
- delete cookie;
- return Plugin_Handled;
- }
- if (access == CookieAccess_Protected)
- {
- ReplyToCommand(client, "[SM] %t", "Protected Cookie", name);
- delete cookie;
- return Plugin_Handled;
- }
-
- /* Set the new value of the cookie */
-
- GetCmdArg(2, value, sizeof(value));
-
- SetClientCookie(client, cookie, value);
- delete cookie;
- ReplyToCommand(client, "[SM] %t", "Cookie Changed Value", name, value);
-
- return Plugin_Handled;
-}
-
-public Action Command_Settings(int client, int args)
-{
- if (client == 0)
- {
- PrintToServer("%T", "No Console", LANG_SERVER);
- return Plugin_Handled;
- }
-
- ShowCookieMenu(client);
-
- return Plugin_Handled;
-}
+#include
+#include
+
+#pragma semicolon 1
+#pragma newdecls required
+
+Database g_Database = null;
+
+#include "clientprefs/utils.sp"
+#include "clientprefs/menus.sp"
+#include "clientprefs/cookies.sp"
+#include "clientprefs/commands.sp"
+#include "clientprefs/database.sp"
+#include "clientprefs/forwards.sp"
+#include "clientprefs/natives.sp"
+
+public Plugin myinfo =
+{
+ name = "Client Preferences",
+ author = "AlliedModders LLC",
+ description = "Saves client preference settings",
+ version = SOURCEMOD_VERSION,
+ url = "http://www.sourcemod.net/"
+};
+
+public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
+{
+ InitMenus();
+ InitCookies();
+ CreateNatives();
+ CreateForwards();
+
+ RegPluginLibrary("clientprefs");
+ return APLRes_Success;
+}
+
+public void OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+ LoadTranslations("clientprefs.phrases");
+
+ CreateCommands();
+
+ DB_TryConnect();
+}
+
+public void OnPluginEnd()
+{
+ // Try to save player data
+ for (int i = 1; i <= MaxClients; i++)
+ {
+ OnClientDisconnect(i);
+ }
+}
+
+public void OnMapStart()
+{
+ DB_TryConnect();
+}
+
+// Second param is unused
+public void OnClientAuthorized(int client)
+{
+ if (!DB_HasMigrated() || IsFakeClient(client))
+ {
+ return;
+ }
+
+ SetPlayerDataPending(client, true);
+
+ char steamId2[32];
+ GetClientAuthId(client, AuthId_Steam2, steamId2, sizeof(steamId2));
+
+ DB_SelectPlayerData(client, steamId2);
+}
+
+public void OnClientDisconnect(int client)
+{
+ if (!IsClientConnected(client) || IsFakeClient(client))
+ {
+ return;
+ }
+
+ char authId[32];
+
+ bool hasAuth = GetClientAuthId(client, AuthId_Steam2, authId, sizeof(authId));
+ if (!hasAuth)
+ {
+ // Unlucky
+ return;
+ }
+
+ // TODO: Could this use transactions per player?
+ StringMapSnapshot snap = GetCookieDataSnapshot();
+
+ for (int i = 0; i < snap.Length; i++)
+ {
+ char name[30];
+ snap.GetKey(i, name, sizeof(name));
+
+ CookieData cookieData;
+ GetCookieDataByName(name, cookieData);
+
+ if (cookieData.dbId > 0)
+ {
+ PlayerData playerData;
+ GetCookiePlayerData(client, name, playerData);
+
+ DB_InsertPlayerData(authId, cookieData.dbId, playerData.Value);
+ }
+ }
+
+ delete snap;
+
+ ClearPlayerData(client);
+ SetPlayerDataLoaded(client, false);
+ SetPlayerDataPending(client, false);
+}
+
+void LateLoadClients()
+{
+ for (int i = 1; i <= MaxClients; i++)
+ {
+ if (IsPlayerDataCached(i) || IsPlayerDataPending(i))
+ {
+ continue;
+ }
+
+ if (!IsClientAuthorized(i))
+ {
+ continue;
+ }
+
+ OnClientAuthorized(i);
+ }
+}
+
+void InsertPendingCookies()
+{
+ StringMapSnapshot snap = GetCookieDataSnapshot();
+
+ for (int i = 0; i < snap.Length; i++)
+ {
+ char name[30];
+ snap.GetKey(i, name, sizeof(name));
+
+ CookieData cookieData;
+ GetCookieDataByName(name, cookieData);
+
+ // If the database id is not set, this has not yet made it into the db
+ if (cookieData.dbId == 0)
+ {
+ DB_InsertCookie(cookieData.Name, cookieData.Description, cookieData.AccessLevel);
+ }
+ }
+
+ delete snap;
+}
diff --git a/plugins/clientprefs/commands.sp b/plugins/clientprefs/commands.sp
new file mode 100644
index 0000000000..c705700fbf
--- /dev/null
+++ b/plugins/clientprefs/commands.sp
@@ -0,0 +1,112 @@
+void CreateCommands()
+{
+ RegConsoleCmd("sm_cookies", Command_Cookies, "sm_cookies [value]");
+ RegConsoleCmd("sm_settings", Command_Settings);
+}
+
+public Action Command_Cookies(int client, int args)
+{
+ if (args == 0)
+ {
+ ReplyToCommand(client, "[SM] Usage: sm_cookies [value]");
+ ReplyToCommand(client, "[SM] %t", "Printing Cookie List");
+
+ PrintToConsole(client, "%t:", "Cookie List");
+
+ StringMapSnapshot snap = GetCookieDataSnapshot();
+
+ int count = 1;
+
+ for (int i = 0; i < snap.Length; i++)
+ {
+ char name[30];
+ snap.GetKey(i, name, sizeof(name));
+
+ CookieData cookieData;
+ GetCookieDataByName(name, cookieData);
+
+ if (cookieData.AccessLevel < CookieAccess_Private)
+ {
+ PrintToConsole(client, "[%03d] %s - %s", count++, name, cookieData.Description);
+ }
+ }
+
+ delete snap;
+ return Plugin_Handled;
+ }
+
+ if (client == 0)
+ {
+ PrintToServer("%T", "No Console", LANG_SERVER);
+ return Plugin_Handled;
+ }
+
+ char name[30];
+ GetCmdArg(1, name, sizeof(name));
+
+ CookieData cookieData;
+
+ bool hasCookieData = GetCookieDataByName(name, cookieData);
+ if (!hasCookieData)
+ {
+ ReplyToCommand(client, "[SM] %t", "Cookie not Found", name);
+ return Plugin_Handled;
+ }
+
+ if (cookieData.AccessLevel == CookieAccess_Private)
+ {
+ ReplyToCommand(client, "[SM] %t", "Cookie not Found", name);
+ return Plugin_Handled;
+ }
+
+ if (args == 1)
+ {
+ PlayerData playerData;
+
+ bool hasPlayerData = GetCookiePlayerData(client, name, playerData);
+ if (!hasPlayerData)
+ {
+ // Enum struct strings would default to "" so this is technically an useless branch
+ ReplyToCommand(client, "[SM] %t", "Cookie Value", name, "");
+ return Plugin_Handled;
+ }
+
+ ReplyToCommand(client, "[SM] %t", "Cookie Value", name, playerData.Value);
+
+ // Eww mutating...
+ TrimString(cookieData.Description);
+
+ if (cookieData.Description[0])
+ {
+ ReplyToCommand(client, "- %s", cookieData.Description);
+ }
+
+ return Plugin_Handled;
+ }
+
+ if (cookieData.AccessLevel == CookieAccess_Protected)
+ {
+ ReplyToCommand(client, "[SM] %t", "Protected Cookie", name);
+ return Plugin_Handled;
+ }
+
+ char value[100];
+ GetCmdArg(2, value, sizeof(value));
+
+ PlayerData playerData;
+ GetCookiePlayerData(client, name, playerData);
+
+ playerData.Value = value;
+ SetPlayerData(client, name, playerData);
+
+ // TODO: Decide on using internal state/funcs vs Plugin API
+
+ ReplyToCommand(client, "[SM] %t", "Cookie Changed Value", name, value);
+ return Plugin_Handled;
+}
+
+public Action Command_Settings(int client, int args)
+{
+ DisplaySettingsMenu(client);
+ return Plugin_Handled;
+}
diff --git a/plugins/clientprefs/cookies.sp b/plugins/clientprefs/cookies.sp
new file mode 100644
index 0000000000..e6f6dc9f2f
--- /dev/null
+++ b/plugins/clientprefs/cookies.sp
@@ -0,0 +1,194 @@
+enum struct CookieData
+{
+ int dbId;
+ char Name[COOKIE_MAX_NAME_LENGTH];
+ char Description[COOKIE_MAX_DESCRIPTION_LENGTH];
+ CookieAccess AccessLevel;
+}
+
+enum struct PlayerData
+{
+ int Timestamp;
+ char Value[COOKIE_MAX_VALUE_LENGTH];
+}
+
+static bool gB_DataLoaded[MAXPLAYERS + 1];
+static bool gB_DataPending[MAXPLAYERS + 1];
+
+static StringMap g_Cookies;
+static StringMap g_PlayerData[MAXPLAYERS + 1];
+
+void InitCookies()
+{
+ g_Cookies = new StringMap();
+
+ // MaxClients is not available here, creates potentially unnecessary handles
+ for (int i = 1; i < sizeof(g_PlayerData); i++)
+ {
+ g_PlayerData[i] = new StringMap();
+ }
+}
+
+void ClearPlayerData(int client)
+{
+ g_PlayerData[client].Clear();
+}
+
+bool IsPlayerDataCached(int client)
+{
+ return gB_DataLoaded[client];
+}
+
+bool IsPlayerDataPending(int client)
+{
+ return gB_DataPending[client];
+}
+
+void SetCookieData(const char[] name, CookieData data)
+{
+ g_Cookies.SetArray(name, data, sizeof(data));
+}
+
+void SetCookieKnownDatabaseId(const char[] name, int id)
+{
+ CookieData data;
+
+ bool exists = GetCookieDataByName(name, data);
+ if (!exists)
+ {
+ return;
+ }
+
+ data.dbId = id;
+ SetCookieData(name, data);
+}
+
+void SetPlayerData(int client, const char[] name, PlayerData playerData)
+{
+ g_PlayerData[client].SetArray(name, playerData, sizeof(playerData));
+}
+
+void SetPlayerDataLoaded(int client, bool loaded)
+{
+ gB_DataLoaded[client] = loaded;
+}
+
+void SetPlayerDataPending(int client, bool pending)
+{
+ gB_DataPending[client] = pending;
+}
+
+int GetCookieCount()
+{
+ StringMapSnapshot snap = GetCookieDataSnapshot();
+ int count = snap.Length;
+
+ delete snap;
+ return count;
+}
+
+StringMapSnapshot GetCookieDataSnapshot()
+{
+ return g_Cookies.Snapshot();
+}
+
+bool GetCookieDataByName(const char[] name, CookieData foundData)
+{
+ return g_Cookies.GetArray(name, foundData, sizeof(foundData));
+}
+
+bool GetCookieDataByIndex(int index, CookieData foundData)
+{
+ StringMapSnapshot snap = GetCookieDataSnapshot();
+
+ char name[30];
+ snap.GetKey(index, name, sizeof(name));
+
+ delete snap;
+ return GetCookieDataByName(name, foundData);
+}
+
+bool GetCookiePlayerData(int client, const char[] name, PlayerData foundData)
+{
+ return g_PlayerData[client].GetArray(name, foundData, sizeof(foundData));
+}
+
+int GetIteratorIndexFromConsumerHandle(ArrayList consumerHandle)
+{
+ return consumerHandle.Get(0);
+}
+
+bool UpdatePlayerCookieValue(int client, const char[] cookieName, const char value[100])
+{
+ CookieData cookieData;
+
+ bool hasCookieData = GetCookieDataByName(cookieName, cookieData);
+ if (!hasCookieData)
+ {
+ return false;
+ }
+
+ PlayerData playerData;
+ GetCookiePlayerData(client, cookieName, playerData);
+
+ playerData.Value = value;
+ playerData.Timestamp = GetTime();
+
+ SetPlayerData(client, cookieName, playerData);
+ return true;
+}
+
+bool IncrementIteratorIndexForConsumerHandle(ArrayList consumerHandle)
+{
+ int index = GetIteratorIndexFromConsumerHandle(consumerHandle);
+ if (index < 0)
+ {
+ return false;
+ }
+
+ consumerHandle.Set(0, index + 1);
+ return true;
+}
+
+bool GetCookieDataFromConsumerHandle(ArrayList consumerHandle, CookieData foundData)
+{
+ char name[30];
+ consumerHandle.GetString(0, name, sizeof(name));
+
+ return GetCookieDataByName(name, foundData);
+}
+
+bool GetCookiePlayerDataFromConsumerHandle(ArrayList consumerHandle, int client, PlayerData foundData)
+{
+ CookieData cookieData;
+
+ bool hasCookieData = GetCookieDataFromConsumerHandle(consumerHandle, cookieData);
+ if (!hasCookieData)
+ {
+ return false;
+ }
+
+ return GetCookiePlayerData(client, cookieData.Name, foundData);
+}
+
+ArrayList CreateConsumerHandleForIterator(Handle plugin)
+{
+ ArrayList originalHandle = new ArrayList(1, 1);
+ originalHandle.Set(0, 0);
+
+ Handle clonedHandle = CloneHandle(originalHandle, plugin);
+ delete originalHandle;
+
+ return view_as(clonedHandle);
+}
+
+ArrayList CreateConsumerHandleForCookieData(CookieData data, Handle plugin)
+{
+ ArrayList originalHandle = new ArrayList(ByteCountToCells(30), 1);
+ originalHandle.SetString(0, data.Name);
+
+ Handle clonedHandle = CloneHandle(originalHandle, plugin);
+ delete originalHandle;
+
+ return view_as(clonedHandle);
+}
diff --git a/plugins/clientprefs/database.sp b/plugins/clientprefs/database.sp
new file mode 100644
index 0000000000..bae84c714c
--- /dev/null
+++ b/plugins/clientprefs/database.sp
@@ -0,0 +1,470 @@
+static bool HasMigrated = false;
+static bool IsConnecting = false;
+
+bool DB_HasMigrated()
+{
+ return HasMigrated;
+}
+
+bool DB_IsConnected()
+{
+ return g_Database != null;
+}
+
+bool DB_IsConnecting()
+{
+ return IsConnecting;
+}
+
+void DB_SetHasMigrated(bool migrated)
+{
+ HasMigrated = migrated;
+}
+
+void DB_SetIsConnecting(bool connecting)
+{
+ IsConnecting = connecting;
+}
+
+bool DB_TryConnect()
+{
+ if (DB_IsConnected() || DB_IsConnecting())
+ {
+ return false;
+ }
+
+ DB_SetIsConnecting(true);
+
+ if (SQL_CheckConfig("clientprefs"))
+ {
+ Database.Connect(OnDatabaseConnect, "clientprefs");
+ return true;
+ }
+
+ if (SQL_CheckConfig("storage-local"))
+ {
+ Database.Connect(OnDatabaseConnect, "storage-local");
+ return true;
+ }
+
+ SetFailState("Could not find any suitable database configs");
+ return false;
+}
+
+void DB_CreateTables()
+{
+ if (!DB_IsConnected())
+ {
+ return;
+ }
+
+ char driverId[64];
+ g_Database.Driver.GetIdentifier(driverId, sizeof(driverId));
+
+ Transaction txn = new Transaction();
+
+ if (StrEqual(driverId, "mysql"))
+ {
+ txn.AddQuery(
+ "CREATE TABLE IF NOT EXISTS sm_cookies \
+ ( \
+ id INTEGER unsigned NOT NULL auto_increment, \
+ name varchar(30) NOT NULL UNIQUE, \
+ description varchar(255), \
+ access INTEGER, \
+ PRIMARY KEY (id) \
+ )"
+ );
+
+ txn.AddQuery(
+ "CREATE TABLE IF NOT EXISTS sm_cookie_cache \
+ ( \
+ player varchar(65) NOT NULL, \
+ cookie_id int(10) NOT NULL, \
+ value varchar(100), \
+ timestamp int NOT NULL, \
+ PRIMARY KEY (player, cookie_id) \
+ )"
+ );
+ }
+ else if (StrEqual(driverId, "sqlite"))
+ {
+ txn.AddQuery(
+ "CREATE TABLE IF NOT EXISTS sm_cookies \
+ ( \
+ id INTEGER PRIMARY KEY AUTOINCREMENT, \
+ name varchar(30) NOT NULL UNIQUE, \
+ description varchar(255), \
+ access INTEGER \
+ )"
+ );
+
+ txn.AddQuery(
+ "CREATE TABLE IF NOT EXISTS sm_cookie_cache \
+ ( \
+ player varchar(65) NOT NULL, \
+ cookie_id int(10) NOT NULL, \
+ value varchar(100), \
+ timestamp int, \
+ PRIMARY KEY (player, cookie_id) \
+ )"
+ );
+ }
+ else if (StrEqual(driverId, "pgsql"))
+ {
+ txn.AddQuery(
+ "CREATE TABLE IF NOT EXISTS sm_cookies \
+ ( \
+ id serial, \
+ name varchar(30) NOT NULL UNIQUE, \
+ description varchar(255), \
+ access INTEGER, \
+ PRIMARY KEY (id) \
+ )"
+ );
+
+ txn.AddQuery(
+ "CREATE TABLE IF NOT EXISTS sm_cookie_cache \
+ ( \
+ player varchar(65) NOT NULL, \
+ cookie_id int NOT NULL, \
+ value varchar(100), \
+ timestamp int NOT NULL, \
+ PRIMARY KEY (player, cookie_id) \
+ )"
+ );
+
+ txn.AddQuery(
+ "CREATE OR REPLACE FUNCTION add_or_update_cookie(in_player VARCHAR(65), in_cookie INT, in_value VARCHAR(100), in_time INT) RETURNS VOID AS \
+ $$ \
+ BEGIN \
+ LOOP \
+ UPDATE sm_cookie_cache SET value = in_value, timestamp = in_time WHERE player = in_player AND cookie_id = in_cookie; \
+ IF found THEN \
+ RETURN; \
+ END IF; \
+ BEGIN \
+ INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) VALUES (in_player, in_cookie, in_value, in_time); \
+ RETURN; \
+ EXCEPTION WHEN unique_violation THEN \
+ END; \
+ END LOOP; \
+ END; \
+ $$ LANGUAGE plpgsql"
+ );
+ }
+ else
+ {
+ ThrowError("Unsupported driver: %s", driverId);
+ delete txn;
+ return;
+ }
+
+ g_Database.Execute(txn, OnTablesSuccess, OnTablesFailure);
+}
+
+void DB_SelectCookieId(const char[] name)
+{
+ if (!DB_IsConnected())
+ {
+ return;
+ }
+
+ char query[2048];
+ g_Database.Format(query, sizeof(query),
+ "SELECT id, name FROM sm_cookies WHERE name = '%s'",
+ name
+ );
+
+ g_Database.Query(OnSelectCookieId, query);
+}
+
+void DB_SelectPlayerData(int client, const char[] authId)
+{
+ if (!DB_IsConnected())
+ {
+ return;
+ }
+
+ char query[2048];
+ g_Database.Format(query, sizeof(query),
+ "SELECT sm_cookies.id, sm_cookies.name, sm_cookie_cache.value, sm_cookies.description, \
+ sm_cookies.access, sm_cookie_cache.timestamp \
+ FROM sm_cookies \
+ JOIN sm_cookie_cache \
+ ON sm_cookies.id = sm_cookie_cache.cookie_id \
+ WHERE player = '%s'",
+ authId
+ );
+
+ g_Database.Query(OnSelectPlayerData, query, GetClientUserId(client));
+}
+
+void DB_InsertCookie(const char[] name, const char[] desc, CookieAccess accessLevel)
+{
+ if (!DB_IsConnected())
+ {
+ return;
+ }
+
+ char driverId[64];
+ g_Database.Driver.GetIdentifier(driverId, sizeof(driverId));
+
+ char query[2048];
+
+ if (StrEqual(driverId, "mysql"))
+ {
+ g_Database.Format(query, sizeof(query),
+ "INSERT IGNORE INTO sm_cookies (name, description, access) \
+ VALUES (\"%s\", \"%s\", %d)",
+ name,
+ desc,
+ accessLevel
+ );
+ }
+ else if (StrEqual(driverId, "sqlite"))
+ {
+ g_Database.Format(query, sizeof(query),
+ "INSERT OR IGNORE INTO sm_cookies (name, description, access) \
+ VALUES ('%s', '%s', %d)",
+ name,
+ desc,
+ accessLevel
+ );
+ }
+ else if (StrEqual(driverId, "pgsql"))
+ {
+ g_Database.Format(query, sizeof(query),
+ "INSERT INTO sm_cookies (name, description, access) \
+ VALUES ('%s', '%s', %d)",
+ name,
+ desc,
+ accessLevel
+ );
+ }
+ else
+ {
+ ThrowError("Unsupported driver: %s", driverId);
+ return;
+ }
+
+ DataPack dp = new DataPack();
+ dp.WriteString(name);
+
+ g_Database.Query(OnInsertCookie, query, dp);
+}
+
+void DB_InsertPlayerData(const char[] authId, int cookieId, const char[] value)
+{
+ if (!DB_IsConnected())
+ {
+ return;
+ }
+
+ char driverId[64];
+ g_Database.Driver.GetIdentifier(driverId, sizeof(driverId));
+
+ char query[2048];
+
+ int timestamp = GetTime();
+
+ if (StrEqual(driverId, "mysql"))
+ {
+ g_Database.Format(query, sizeof(query),
+ "INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) \
+ VALUES (\"%s\", %d, \"%s\", %d) \
+ ON DUPLICATE KEY UPDATE \
+ value = \"%s\", timestamp = %d",
+ authId,
+ cookieId,
+ value,
+ timestamp,
+ value,
+ timestamp
+ );
+ }
+ else if (StrEqual(driverId, "sqlite"))
+ {
+ g_Database.Format(query, sizeof(query),
+ "INSERT OR REPLACE INTO sm_cookie_cache \
+ (player, cookie_id, value, timestamp) \
+ VALUES ('%s', %d, '%s', %d)",
+ authId,
+ cookieId,
+ value,
+ timestamp
+ );
+ }
+ else if (StrEqual(driverId, "pgsql"))
+ {
+ /*
+ SourceMod comes with pgsql v9.6.9, UPSERT is in 9.5
+ INSERT INTO sm_cookie_cache \
+ (player, cookie_id, value, timestamp) \
+ VALUES ('%s', %d, '%s', %d)
+ ON CONFLICT (player, cookie_id) DO UPDATE
+ SET value = '%s', timestamp = %d
+ */
+
+ g_Database.Format(query, sizeof(query),
+ "SELECT add_or_update_cookie ('%s', %d, '%s', %d)",
+ authId,
+ cookieId,
+ value,
+ timestamp
+ );
+ }
+ else
+ {
+ ThrowError("Unsupported driver: %s", driverId);
+ return;
+ }
+
+ g_Database.Query(OnInsertPlayerData, query);
+}
+
+public void OnDatabaseConnect(Database db, const char[] error, any data)
+{
+ g_Database = db;
+
+ DB_SetIsConnecting(false);
+
+ if (!db || strlen(error) > 0)
+ {
+ LogError("Failed connecting to the database, error: %s", error);
+ return;
+ }
+
+ if (!DB_HasMigrated())
+ {
+ DB_CreateTables();
+ }
+ else
+ {
+ InsertPendingCookies();
+ }
+}
+
+public void OnTablesSuccess(Database db, any data, int numQueries, Handle[] results, any[] queryData)
+{
+ DB_SetHasMigrated(true);
+
+ LateLoadClients();
+ InsertPendingCookies();
+}
+
+public void OnTablesFailure(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData)
+{
+ PrintToServer("Failed creating tables, error: %s", error);
+}
+
+public void OnSelectCookieId(Database db, DBResultSet results, const char[] error, any data)
+{
+ if (!db || strlen(error) > 0)
+ {
+ LogError("Failed selecting cookie id, error: %s", error);
+ return;
+ }
+
+ if (!results.FetchRow())
+ {
+ return;
+ }
+
+ int id = results.FetchInt(0);
+
+ char name[30];
+ results.FetchString(1, name, sizeof(name));
+
+ SetCookieKnownDatabaseId(name, id);
+}
+
+public void OnSelectPlayerData(Database db, DBResultSet results, const char[] error, int userId)
+{
+ if (!db || strlen(error) > 0)
+ {
+ LogError("Failed selecting player data, error: %s", error);
+ return;
+ }
+
+ int client = GetClientOfUserId(userId);
+ if (client == 0)
+ {
+ return;
+ }
+
+ SetPlayerDataPending(client, false);
+
+ if (results == null)
+ {
+ return;
+ }
+
+ while (results.FetchRow())
+ {
+ char name[30];
+ results.FetchString(1, name, sizeof(name));
+
+ CookieData cookieData;
+ cookieData.Name = name;
+
+ bool hasData = GetCookieDataByName(name, cookieData);
+ if (!hasData)
+ {
+ cookieData.dbId = results.FetchInt(0);
+ cookieData.AccessLevel = view_as(results.FetchInt(4));
+
+ results.FetchString(3, cookieData.Description, sizeof(cookieData.Description));
+
+ SetCookieData(name, cookieData);
+ }
+
+ PlayerData playerData;
+ playerData.Timestamp = results.FetchInt(5);
+
+ results.FetchString(2, playerData.Value, sizeof(playerData.Value));
+
+ SetPlayerData(client, cookieData.Name, playerData);
+ }
+
+ SetPlayerDataLoaded(client, true);
+
+ Call_OnCookiesCached(client);
+ PrintToServer("Loaded data for %N", client);
+}
+
+public void OnInsertCookie(Database db, DBResultSet results, const char[] error, DataPack dp)
+{
+ if (!db || strlen(error) > 0)
+ {
+ LogError("Failed inserting cookie, error: %s", error);
+ delete dp;
+ return;
+ }
+
+ dp.Reset();
+
+ char name[30];
+ dp.ReadString(name, sizeof(name));
+
+ delete dp;
+
+ int dbId = results.InsertId;
+ if (dbId > 0)
+ {
+ SetCookieKnownDatabaseId(name, dbId);
+ return;
+ }
+
+ // Manually lookup id if this was not an insert
+ DB_SelectCookieId(name);
+}
+
+public void OnInsertPlayerData(Database db, DBResultSet results, const char[] error, any data)
+{
+ if (!db || strlen(error) > 0)
+ {
+ LogError("Failed inserting player data, error: %s", error);
+ return;
+ }
+}
diff --git a/plugins/clientprefs/forwards.sp b/plugins/clientprefs/forwards.sp
new file mode 100644
index 0000000000..0ace6910c4
--- /dev/null
+++ b/plugins/clientprefs/forwards.sp
@@ -0,0 +1,13 @@
+static GlobalForward OnCookiesCached;
+
+void CreateForwards()
+{
+ OnCookiesCached = new GlobalForward("OnClientCookiesCached", ET_Ignore, Param_Cell);
+}
+
+void Call_OnCookiesCached(int client)
+{
+ Call_StartForward(OnCookiesCached);
+ Call_PushCell(client);
+ Call_Finish();
+}
diff --git a/plugins/clientprefs/menus.sp b/plugins/clientprefs/menus.sp
new file mode 100644
index 0000000000..4f36b9bb07
--- /dev/null
+++ b/plugins/clientprefs/menus.sp
@@ -0,0 +1,202 @@
+enum struct CookieMenuData
+{
+ any UserData;
+ CookieMenu Type;
+ PrivateForward Handler;
+}
+
+static Menu g_settingsMenu;
+static StringMap g_menuData;
+
+void InitMenus()
+{
+ g_menuData = new StringMap();
+ g_settingsMenu = new Menu(MenuHandler_Settings);
+}
+
+bool GetMenuDataByName(const char[] name, CookieMenuData foundData)
+{
+ return g_menuData.GetArray(name, foundData, sizeof(foundData));
+}
+
+bool SetMenuDataByName(const char[] name, CookieMenuData data)
+{
+ return g_menuData.SetArray(name, data, sizeof(data));
+}
+
+void AddSettingsMenuItem(
+ Handle callingPlugin,
+ CookieData cookieData,
+ const char[] display,
+ CookieMenu type,
+ Function handler,
+ any userData
+) {
+ CookieMenuData data;
+
+ bool hasData = GetMenuDataByName(cookieData.Name, data);
+ if (!hasData)
+ {
+ data.Handler = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_String, Param_Cell);
+ g_settingsMenu.AddItem(cookieData.Name, display);
+ }
+
+ if (handler != INVALID_FUNCTION)
+ {
+ data.Handler.AddFunction(callingPlugin, handler);
+ }
+
+ data.Type = type;
+ data.UserData = userData;
+
+ SetMenuDataByName(cookieData.Name, data);
+}
+
+void DisplaySettingsMenu(int client)
+{
+ g_settingsMenu.SetTitle("%T:", "Client Settings", client);
+ g_settingsMenu.Display(client, MENU_TIME_FOREVER);
+}
+
+void DisplayOptionSelectionMenu(int client, CookieData cookieData)
+{
+ CookieMenuData menuData;
+
+ bool hasMenuData = GetMenuDataByName(cookieData.Name, menuData);
+ if (!hasMenuData)
+ {
+ return;
+ }
+
+ Menu menu = new Menu(MenuHandler_Option, MENU_ACTIONS_DEFAULT | MenuAction_DisplayItem);
+ menu.SetTitle("%T:", "Choose Option", client);
+
+ // Store cookie name in menu item 0 for context
+ menu.AddItem(cookieData.Name, "", ITEMDRAW_IGNORE);
+
+ switch (menuData.Type)
+ {
+ case CookieMenu_YesNo:
+ {
+ menu.AddItem("yes", "Yes");
+ menu.AddItem("no", "No");
+ }
+ case CookieMenu_YesNo_Int:
+ {
+ menu.AddItem("1", "Yes");
+ menu.AddItem("0", "No");
+ }
+ case CookieMenu_OnOff:
+ {
+ menu.AddItem("on", "On");
+ menu.AddItem("off", "Off");
+ }
+ case CookieMenu_OnOff_Int:
+ {
+ menu.AddItem("1", "On");
+ menu.AddItem("0", "Off");
+ }
+ }
+
+ menu.Display(client, MENU_TIME_FOREVER);
+}
+
+public int MenuHandler_Settings(Menu menu, MenuAction action, int param1, int param2)
+{
+ if (action == MenuAction_Select)
+ {
+ char name[30];
+ menu.GetItem(param2, name, sizeof(name));
+
+ CookieData cookieData;
+
+ bool hasCookieData = GetCookieDataByName(name, cookieData);
+ if (!hasCookieData)
+ {
+ return 0;
+ }
+
+ CookieMenuData menuData;
+
+ bool hasMenuData = GetMenuDataByName(name, menuData);
+ if (!hasMenuData)
+ {
+ return 0;
+ }
+
+ if (menuData.Handler != null)
+ {
+ Call_StartForward(menuData.Handler);
+ Call_PushCell(param1);
+ Call_PushCell(CookieMenuAction_SelectOption);
+ Call_PushCell(menuData.UserData);
+ Call_PushString("");
+ Call_PushCell(0);
+ Call_Finish();
+ }
+
+ DisplayOptionSelectionMenu(param1, cookieData);
+ }
+
+ return 0;
+}
+
+public int MenuHandler_Option(Menu menu, MenuAction action, int param1, int param2)
+{
+ if (action == MenuAction_Select)
+ {
+ char name[30];
+ menu.GetItem(0, name, sizeof(name));
+
+ char value[100];
+ menu.GetItem(param2, value, sizeof(value));
+
+ CookieData cookieData;
+
+ bool hasCookieData = GetCookieDataByName(name, cookieData);
+ if (!hasCookieData)
+ {
+ return 0;
+ }
+
+ bool updated = UpdatePlayerCookieValue(param1, name, value);
+ if (!updated)
+ {
+ return 0;
+ }
+
+ PrintToChat(param1, "[SM] %t", "Cookie Changed Value", name, value);
+ }
+ else if (action == MenuAction_DisplayItem)
+ {
+ char name[30];
+ menu.GetItem(0, name, sizeof(name));
+
+ CookieMenuData data;
+
+ bool hasData = GetMenuDataByName(name, data);
+ if (!hasData || data.Handler == null)
+ {
+ return 0;
+ }
+
+ char buffer[100];
+ menu.GetItem(param2, "", 0, _, buffer, sizeof(buffer));
+
+ Call_StartForward(data.Handler);
+ Call_PushCell(param1);
+ Call_PushCell(CookieMenuAction_DisplayOption);
+ Call_PushCell(data.UserData);
+ Call_PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
+ Call_PushCell(sizeof(buffer));
+ Call_Finish();
+
+ return RedrawMenuItem(buffer);
+ }
+ else if (action == MenuAction_End)
+ {
+ delete menu;
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/plugins/clientprefs/natives.sp b/plugins/clientprefs/natives.sp
new file mode 100644
index 0000000000..d7518b43e1
--- /dev/null
+++ b/plugins/clientprefs/natives.sp
@@ -0,0 +1,283 @@
+void CreateNatives()
+{
+ CreateNative("RegClientCookie", Native_RegCookie);
+ CreateNative("FindClientCookie", Native_FindCookie);
+ CreateNative("SetClientCookie", Native_SetCookieValue_Old);
+ CreateNative("GetClientCookie", Native_GetCookieValue_Old);
+ CreateNative("GetCookieAccess", Native_GetCookieAccess);
+ CreateNative("GetClientCookieTime", Native_GetClientCookieTime_Old);
+ CreateNative("SetAuthIdCookie", Native_SetCookieValueByAuthId_Old);
+ CreateNative("AreClientCookiesCached", Native_AreClientCookiesCached);
+ CreateNative("SetCookiePrefabMenu", Native_SetCookiePrefabMenu);
+ //CreateNative("SetCookieMenuItem", Native_SetCookieMenuItem);
+ CreateNative("ShowCookieMenu", Native_ShowCookieMenu);
+ CreateNative("GetCookieIterator", Native_GetCookieIterator);
+ CreateNative("ReadCookieIterator", Native_ReadCookieIterator);
+
+ CreateNative("Cookie.Cookie", Native_RegCookie);
+ CreateNative("Cookie.Find", Native_FindCookie);
+ CreateNative("Cookie.Set", Native_SetCookieValue);
+ CreateNative("Cookie.Get", Native_GetCookieValue);
+ CreateNative("Cookie.AccessLevel.get", Native_GetCookieAccess);
+ CreateNative("Cookie.GetClientTime", Native_GetClientCookieTime);
+ CreateNative("Cookie.SetPrefabMenu", Native_SetCookiePrefabMenu);
+ CreateNative("Cookie.SetByAuthId", Native_SetCookieValueByAuthId);
+}
+
+public any Native_RegCookie(Handle plugin, int numParams)
+{
+ char name[30];
+ GetNativeString(1, name, sizeof(name));
+
+ char description[255];
+ GetNativeString(2, description, sizeof(description));
+
+ CookieAccess accessLevel = GetNativeCell(3);
+
+ CookieData cookieData;
+
+ bool found = GetCookieDataByName(name, cookieData);
+ if (!found)
+ {
+ cookieData.Name = name;
+ cookieData.Description = description;
+ cookieData.AccessLevel = accessLevel;
+
+ SetCookieData(name, cookieData);
+ DB_InsertCookie(name, description, accessLevel);
+ }
+
+ return CreateConsumerHandleForCookieData(cookieData, plugin);
+}
+
+public any Native_FindCookie(Handle plugin, int numParams)
+{
+ char name[30];
+ GetNativeString(1, name, sizeof(name));
+
+ CookieData cookieData;
+
+ bool found = GetCookieDataByName(name, cookieData);
+ if (!found)
+ {
+ return INVALID_HANDLE;
+ }
+
+ return CreateConsumerHandleForCookieData(cookieData, plugin);
+}
+
+public any Native_SetCookieValue(Handle plugin, int numParams)
+{
+ any handle = GetNativeCell(1);
+ int client = GetNativeCell(2);
+
+ char value[100];
+ GetNativeString(3, value, sizeof(value));
+
+ return SetCookieValue_Impl(client, value, handle);
+}
+
+public any Native_SetCookieValue_Old(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+ any handle = GetNativeCell(2);
+
+ char value[100];
+ GetNativeString(3, value, sizeof(value));
+
+ return SetCookieValue_Impl(client, value, handle);
+}
+
+public any Native_GetCookieValue(Handle plugin, int numParams)
+{
+ any handle = GetNativeCell(1);
+ int client = GetNativeCell(2);
+
+ return GetCookieValue_Impl(client, handle);
+}
+
+public any Native_GetCookieValue_Old(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+ any handle = GetNativeCell(2);
+
+ return GetCookieValue_Impl(client, handle);
+}
+
+public any Native_GetCookieAccess(Handle plugin, int numParams)
+{
+ any handle = GetNativeCell(1);
+
+ CookieData data;
+
+ bool hasData = GetCookieDataFromConsumerHandle(handle, data);
+ if (!hasData)
+ {
+ // TODO: Throw?
+ return -1;
+ }
+
+ return data.AccessLevel;
+}
+
+public any Native_GetClientCookieTime(Handle plugin, int numParams)
+{
+ any handle = GetNativeCell(1);
+ int client = GetNativeCell(2);
+
+ return GetClientCookieTime_Impl(client, handle);
+}
+
+public any Native_GetClientCookieTime_Old(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+ any handle = GetNativeCell(2);
+
+ return GetClientCookieTime_Impl(client, handle);
+}
+
+public any Native_AreClientCookiesCached(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+ if (client < 1 || client > MaxClients)
+ {
+ ThrowNativeError(SP_ERROR_NATIVE, "Client index %d is invalid", client);
+ return false;
+ }
+
+ return IsPlayerDataCached(client);
+}
+
+public any Native_SetCookiePrefabMenu(Handle plugin, int numParams)
+{
+ any handle = GetNativeCell(1);
+
+ CookieData data;
+
+ bool hasData = GetCookieDataFromConsumerHandle(handle, data);
+ if (!hasData)
+ {
+ // TODO: Throw?
+ return false;
+ }
+
+ CookieMenu type = GetNativeCell(2);
+
+ char display[128];
+ GetNativeString(3, display, sizeof(display));
+
+ Function handler = GetNativeFunction(4);
+
+ any userData = GetNativeCell(5);
+
+ AddSettingsMenuItem(plugin, data, display, type, handler, userData);
+ return true;
+}
+
+public any Native_SetCookieValueByAuthId(Handle plugin, int numParams)
+{
+ any handle = GetNativeCell(1);
+
+ char authId[32];
+ GetNativeString(2, authId, sizeof(authId));
+
+ char value[100];
+ GetNativeString(3, value, sizeof(value));
+
+ return SetCookieValueByAuthId_Impl(authId, value, handle);
+}
+
+public any Native_SetCookieValueByAuthId_Old(Handle plugin, int numParams)
+{
+ char authId[32];
+ GetNativeString(1, authId, sizeof(authId));
+
+ any handle = GetNativeCell(2);
+
+ char value[100];
+ GetNativeString(3, value, sizeof(value));
+
+ return SetCookieValueByAuthId_Impl(authId, value, handle);
+}
+
+public any Native_ShowCookieMenu(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+ return DisplaySettingsMenu(client);
+}
+
+public any Native_GetCookieIterator(Handle plugin, int numParams)
+{
+ return CreateConsumerHandleForIterator(plugin);
+}
+
+public any Native_ReadCookieIterator(Handle plugin, int numParams)
+{
+ any handle = GetNativeCell(1);
+
+ int index = GetIteratorIndexFromConsumerHandle(handle);
+ if (index < 0 || index >= GetCookieCount())
+ {
+ return false;
+ }
+
+ CookieData data;
+ GetCookieDataByIndex(index, data);
+
+ SetNativeString(2, data.Name, GetNativeCell(3));
+
+ SetNativeCellRef(4, data.AccessLevel);
+
+ SetNativeString(5, data.Description, GetNativeCell(6));
+
+ IncrementIteratorIndexForConsumerHandle(handle);
+ return true;
+}
+
+any SetCookieValue_Impl(int client, const char value[100], any handle)
+{
+ CookieData cookieData;
+ GetCookieDataFromConsumerHandle(handle, cookieData);
+
+ return UpdatePlayerCookieValue(client, cookieData.Name, value);
+}
+
+any GetCookieValue_Impl(int client, any handle)
+{
+ PlayerData data;
+
+ bool hasData = GetCookiePlayerDataFromConsumerHandle(handle, client, data);
+ if (!hasData)
+ {
+ return false;
+ }
+
+ int maxlength = GetNativeCell(4);
+
+ SetNativeString(3, data.Value, maxlength);
+ return true;
+}
+
+any GetClientCookieTime_Impl(int client, any handle)
+{
+ PlayerData data;
+
+ bool hasData = GetCookiePlayerDataFromConsumerHandle(handle, client, data);
+ if (!hasData)
+ {
+ return -1;
+ }
+
+ return data.Timestamp;
+}
+
+any SetCookieValueByAuthId_Impl(const char[] authId, const char value[100], any handle)
+{
+ int foundClient = FindClientByAuthId(authId);
+ if (foundClient == -1)
+ {
+ return false;
+ }
+
+ return SetCookieValue_Impl(foundClient, value, handle);
+}
diff --git a/plugins/clientprefs/utils.sp b/plugins/clientprefs/utils.sp
new file mode 100644
index 0000000000..3079ea882e
--- /dev/null
+++ b/plugins/clientprefs/utils.sp
@@ -0,0 +1,23 @@
+int FindClientByAuthId(const char[] authId)
+{
+ for (int i = 1; i <= MaxClients; i++)
+ {
+ if (!IsFakeClient(i) && IsClientConnected(i))
+ {
+ char targetAuthId[32];
+
+ bool gotAuth = GetClientAuthId(i, AuthId_Steam2, targetAuthId, sizeof(targetAuthId));
+ if (!gotAuth)
+ {
+ continue;
+ }
+
+ if (StrEqual(targetAuthId, authId))
+ {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
diff --git a/plugins/include/clientprefs.inc b/plugins/include/clientprefs.inc
index 6ab4e203c4..84fb40802f 100644
--- a/plugins/include/clientprefs.inc
+++ b/plugins/include/clientprefs.inc
@@ -75,6 +75,7 @@ enum CookieMenuAction
};
#define COOKIE_MAX_NAME_LENGTH 30 /**< Maximum Cookie name length. */
+#define COOKIE_MAX_VALUE_LENGTH 100 /**< Maximum Cookie value length. */
#define COOKIE_MAX_DESCRIPTION_LENGTH 255 /**< Maximum Cookie description length. */
/**
@@ -388,24 +389,19 @@ native int GetClientCookieTime(int client, Handle cookie);
/**
* Do not edit below this line!
*/
-public Extension __ext_cprefs =
+public SharedPlugin __pl_cprefs =
{
name = "Client Preferences",
- file = "clientprefs.ext",
-#if defined AUTOLOAD_EXTENSIONS
- autoload = 1,
-#else
- autoload = 0,
-#endif
-#if defined REQUIRE_EXTENSIONS
+ file = "clientprefs.smx",
+#if defined REQUIRE_PLUGIN
required = 1,
#else
required = 0,
#endif
};
-#if !defined REQUIRE_EXTENSIONS
-public void __ext_cprefs_SetNTVOptional()
+#if !defined REQUIRE_PLUGIN
+public void __pl_cprefs_SetNTVOptional()
{
MarkNativeAsOptional("RegClientCookie");
MarkNativeAsOptional("FindClientCookie");