diff --git a/README.md b/README.md index c155884..5441869 100644 --- a/README.md +++ b/README.md @@ -1 +1,24 @@ -# Keys \ No newline at end of file +# [Keys] Core (+ Modules) + +Плагин-Ядро для системы, которая позволяет создавать ключи для последующей их продажи/раздачи/розыгрыша, которые позволяют игрокам с помощью их активации получать различные бонусы и привилегии (VIP-статус, кредиты, опыт, админка). + +### Особенности/возможности: + + * Генерация ключей заданной длины или по заданному шаблону (например: XXXXX-XXXXX-XXXXX-XXXXX) + * Добавление ключей с желаемым названием + * Возможность создания ключей с заданным сроком жизни, по истечении которого ключи становятся неактивными и удаляются + * Возможность создания ключей с заданным количеством использований одного ключа (присутствует защита от повторного использования ключа одним игроком) + * При попытке подбора ключа игрок заносится в черный список (с сохранением) на заданный срок + * Работа с SQLite/MySQL + * При работе с MySQL поддерживается разделение по серверам + * Полное ведение логов (создание/удаление/использование ключа) + * API достаточное для полноценной работы с другими плагинами + +#### Подробнее - [Тема на HlMod.ru](http://hlmod.ru/resources/keys-core.438/) + + +### Модули: + + +* [[Keys] Shop](http://hlmod.ru/resources/keys-shop.439/) - Получение кредитов/предметов +* [[Keys] VIP](http://hlmod.ru/resources/keys-vip.440/) - Получение/продление VIP-статуса, смена VIP-группы \ No newline at end of file diff --git a/addons/sourcemod/scripting/Keys_Core.sp b/addons/sourcemod/scripting/Keys_Core.sp new file mode 100644 index 0000000..ad48bbb --- /dev/null +++ b/addons/sourcemod/scripting/Keys_Core.sp @@ -0,0 +1,429 @@ +#pragma semicolon 1 + +#include +#include + +public Plugin:myinfo = +{ + name = "[Keys] Core", + author = "R1KO", + version = "1.1", + url = "hlmod.ru" +}; + +#include "keys/vars.sp" +#include "keys/utils.sp" +#include "keys/api.sp" +#include "keys/cmds.sp" + +public OnPluginStart() +{ + LoadTranslations("keys_core.phrases"); + + g_bIsStarted = false; + + BuildPath(Path_SM, SZF(g_sLogFile), "logs/Keys.log"); + + g_hKeysTrie = CreateTrie(); + g_hKeysArray = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); + + new Handle:hCvar = CreateConVar("key_length", "32", "Длина генерируемого ключа (8-64)", _, true, 8.0, true, 64.0); + HookConVarChange(hCvar, OnKeyLengthChange); + g_CVAR_iKeyLength = GetConVarInt(hCvar); + + hCvar = CreateConVar("key_template", "", "Шаблон для генерируемого пароля:\n\ + A - Буква в любом регистре\n\ + B - Цифра 0-9\n\ + X - Цифра 0-9 либо буква в любом регистре\n\ + Пример: XXXX-XXXX-XXXX-XXXX", _, false, 0.0, true, 64.0); + + HookConVarChange(hCvar, OnKeyTemplateChange); + GetConVarString(hCvar, SZF(g_CVAR_sKeyTemplate)); + + hCvar = CreateConVar("key_server_id", "0", "ID сервера", _, true, -1.0); + HookConVarChange(hCvar, OnServerIDChange); + g_CVAR_iServerID = GetConVarInt(hCvar); + + hCvar = CreateConVar("key_attempts", "3", "Количество попыток ввода ключа до получения блокировки (0 - Отключено)", _, true, 0.0); + HookConVarChange(hCvar, OnAttemptsChange); + g_CVAR_iAttempts = GetConVarInt(hCvar); + + hCvar = CreateConVar("key_block_time", "3600", "На сколько минут будет заблокирован игрок при вводе неверных ключей", _, true, 1.0); + HookConVarChange(hCvar, OnBlockTimeChange); + g_CVAR_iBlockTime = GetConVarInt(hCvar); + + AutoExecConfig(true, "Keys_Core"); + + RegAdminCmds(); + + Connect_DB(); +} + +public OnKeyLengthChange(Handle:hCvar, const String:oldValue[], const String:newValue[]) g_CVAR_iKeyLength = GetConVarInt(hCvar); +public OnKeyTemplateChange(Handle:hCvar, const String:oldValue[], const String:newValue[]) GetConVarString(hCvar, SZF(g_CVAR_sKeyTemplate)); +public OnServerIDChange(Handle:hCvar, const String:oldValue[], const String:newValue[]) +{ + g_CVAR_iServerID = GetConVarInt(hCvar); + if(g_bIsStarted && g_bDBMySQL) + { + GetServerID(false); + } +} +public OnAttemptsChange(Handle:hCvar, const String:oldValue[], const String:newValue[]) g_CVAR_iAttempts = GetConVarInt(hCvar); +public OnBlockTimeChange(Handle:hCvar, const String:oldValue[], const String:newValue[]) g_CVAR_iBlockTime = GetConVarInt(hCvar); + +Connect_DB() +{ + if (SQL_CheckConfig("keys_core")) + { + SQL_TConnect(DB_OnConnect, "keys_core", 1); + } + else + { + decl String:sError[256]; + sError[0] = '\0'; + g_hDatabase = SQLite_UseDatabase("keys_core", SZF(sError)); + DB_OnConnect(g_hDatabase, g_hDatabase, sError, 0); + } +} + +public DB_OnConnect(Handle:owner, Handle:hndl, const String:sError[], any:data) +{ + g_hDatabase = hndl; + + if (g_hDatabase == INVALID_HANDLE || sError[0]) + { + SetFailState("Failed DB Connect %s", sError); + return; + } + + decl String:sDriver[16]; + if(data) + { + SQL_GetDriverIdent(owner, SZF(sDriver)); + } + else + { + SQL_ReadDriver(owner, SZF(sDriver)); + } + + g_bDBMySQL = (strcmp(sDriver, "mysql", false) == 0); + + if (g_bDBMySQL) + { + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "SET NAMES 'utf8'"); + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "SET CHARSET 'utf8'"); + } + + CreateTables(); +} + +CreateTables() +{ + SQL_LockDatabase(g_hDatabase); + if (g_bDBMySQL) + { + g_iServerID = -1; + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "CREATE TABLE IF NOT EXISTS `table_keys` (\ + `key_name` VARCHAR(64) NOT NULL, \ + `type` VARCHAR(64) NOT NULL, \ + `expires` INTEGER UNSIGNED NOT NULL default 0, \ + `uses` INTEGER UNSIGNED NOT NULL default 1, \ + `sid` INTEGER NOT NULL, \ + `param1` VARCHAR(64) NULL default NULL, \ + `param2` VARCHAR(64) NULL default NULL, \ + `param3` VARCHAR(64) NULL default NULL, \ + `param4` VARCHAR(64) NULL default NULL, \ + `param5` VARCHAR(64) NULL default NULL, \ + PRIMARY KEY(`key_name`)) DEFAULT CHARSET=utf8;"); + + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "CREATE TABLE IF NOT EXISTS `keys_blocked_players` (\ + `auth` VARCHAR(24) NOT NULL, \ + `block_end` INTEGER UNSIGNED NOT NULL, \ + `sid` INTEGER NOT NULL, \ + PRIMARY KEY(`auth`)) DEFAULT CHARSET=utf8;"); + + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "CREATE TABLE IF NOT EXISTS `keys_players_used` (\ + `auth` VARCHAR(24) NOT NULL, \ + `key_name` VARCHAR(64) NOT NULL, \ + `sid` INTEGER NOT NULL) DEFAULT CHARSET=utf8;"); + + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "CREATE TABLE IF NOT EXISTS `keys_servers` (\ + `sid` INTEGER NOT NULL AUTO_INCREMENT,\ + `address` VARCHAR(24) NOT NULL, \ + PRIMARY KEY(`sid`), \ + UNIQUE KEY `address` (`address`)) DEFAULT CHARSET=utf8;"); + } + else + { + g_iServerID = 0; + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "CREATE TABLE IF NOT EXISTS `table_keys` (\ + `key_name` VARCHAR(64) NOT NULL PRIMARY KEY, \ + `type` VARCHAR(64) NOT NULL, \ + `expires` INTEGER UNSIGNED NOT NULL default 0, \ + `uses` INTEGER UNSIGNED NOT NULL default 1, \ + `param1` VARCHAR(64) NULL default NULL, \ + `param2` VARCHAR(64) NULL default NULL, \ + `param3` VARCHAR(64) NULL default NULL, \ + `param4` VARCHAR(64) NULL default NULL, \ + `param5` VARCHAR(64) NULL default NULL);"); + + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "CREATE TABLE IF NOT EXISTS `keys_blocked_players` (\ + `auth` VARCHAR(24) NOT NULL PRIMARY KEY, \ + `block_end` INTEGER UNSIGNED NOT NULL);"); + + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, "CREATE TABLE IF NOT EXISTS `keys_players_used` (\ + `auth` VARCHAR(24) NOT NULL, \ + `key_name` VARCHAR(64) NOT NULL);"); + } + + SQL_UnlockDatabase(g_hDatabase); + + if(g_bDBMySQL && g_iServerID == -1) + { + GetServerID(true); + return; + } + + Notify_Started(); +} + +Notify_Started() +{ + g_bIsStarted = true; + + CreateForward_OnCoreStarted(); +} + +public OnConfigsExecuted() +{ + if(g_bIsStarted) + { + GetServerID(false); + } +} + +DeleteExpiredKeys() +{ + decl String:sQuery[256]; + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `expires` > 0 AND `expires` < %d;", GetTime()); + } + else + { + FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `expires` > 0 AND `expires` < %d AND `sid` = %d;", GetTime(), g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, sQuery); +} + +public SQL_Callback_ErrorCheck(Handle:hOwner, Handle:hResult, const String:sError[], any:data) +{ + if (sError[0]) + { + LogError("SQL_Callback_ErrorCheck: %s", sError); + } +} + +#define GetServerIp(%1,%2) GetServerIpFunc(_:(%1), %1, %2) + +GetServerIpFunc(array[], String:sBuffer[], iMaxLength) +{ + array[0] = GetConVarInt(FindConVar("hostip")); + FormatEx(sBuffer, iMaxLength, "%d.%d.%d.%d:%d", sBuffer[3] + 0, sBuffer[2] + 0, sBuffer[1] + 0, sBuffer[0] + 0, GetConVarInt(FindConVar("hostport"))); +} + +GetServerID(bool:bNotifyStarted) +{ + if(g_CVAR_iServerID == -1) + { + decl String:sAddress[24], String:sQuery[256]; + GetServerIp(sAddress, sizeof(sAddress)); + FormatEx(SZF(sQuery), "SELECT `sid` FROM `keys_servers` WHERE `address` = '%s';", sAddress); + SQL_TQuery(g_hDatabase, SQL_Callback_SelectServerID, sQuery, bNotifyStarted); + return; + } + + g_iServerID = g_CVAR_iServerID; + + if(bNotifyStarted) + { + DeleteExpiredKeys(); + + Notify_Started(); + } +} + +public SQL_Callback_SelectServerID(Handle:hOwner, Handle:hResult, const String:sError[], any:bNotifyStarted) +{ + if (hResult == INVALID_HANDLE || sError[0]) + { + LogError("SQL_Callback_SelectServerID: %s", sError); + return; + } + + if(SQL_FetchRow(hResult)) + { + g_iServerID = SQL_FetchInt(hResult, 0); + + if(bNotifyStarted) + { + DeleteExpiredKeys(); + + Notify_Started(); + } + return; + } + + decl String:sAddress[24], String:sQuery[256]; + GetServerIp(sAddress, sizeof(sAddress)); + FormatEx(SZF(sQuery), "INSERT INTO `keys_servers` (`address`) VALUES ('%s');", sAddress); + SQL_TQuery(g_hDatabase, SQL_Callback_CreateServerID, sQuery, bNotifyStarted); +} + +public SQL_Callback_CreateServerID(Handle:hOwner, Handle:hResult, const String:sError[], any:bNotifyStarted) +{ + if (hResult == INVALID_HANDLE || sError[0]) + { + LogError("SQL_Callback_CreateServerID: %s", sError); + return; + } + + if(SQL_GetAffectedRows(hResult)) + { + g_iServerID = SQL_GetInsertId(g_hDatabase); + + if(bNotifyStarted) + { + DeleteExpiredKeys(); + + Notify_Started(); + } + } +} + +public Action:OnClientSayCommand(iClient, const String:sCommand[], const String:sArgs[]) +{ + if(StrContains(sArgs, "key") != -1) + { + return Plugin_Handled; + } + + return Plugin_Continue; +} + +public OnClientDisconnect(iClient) +{ + g_iAttempts[iClient] = 0; + g_bIsBlocked[iClient] = false; +} + +public OnClientPostAdminCheck(iClient) +{ + if(!IsFakeClient(iClient)) + { + decl String:sQuery[256], String:sAuth[32]; + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "SELECT `block_end` FROM `keys_blocked_players` WHERE `auth` = '%s';", sAuth); + } + else + { + FormatEx(SZF(sQuery), "SELECT `block_end` FROM `keys_blocked_players` WHERE `auth` = '%s' AND `sid` = %d;", sAuth, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_SearchPlayer, sQuery, UID(iClient)); + } +} + +public SQL_Callback_SearchPlayer(Handle:hOwner, Handle:hResult, const String:sError[], any:UserID) +{ + if (hResult == INVALID_HANDLE || sError[0]) + { + LogError("SQL_Callback_SearchPlayer: %s", sError); + return; + } + + new iClient = CID(UserID); + if (iClient) + { + if(SQL_FetchRow(hResult)) + { + g_iAttempts[iClient] = SQL_FetchInt(hResult, 0); + if(g_iAttempts[iClient] < GetTime()) + { + UnBlockClient(iClient); + return; + } + + g_bIsBlocked[iClient] = true; + } + } +} + +BlockClient(iClient) +{ + g_bIsBlocked[iClient] = true; + g_iAttempts[iClient] = GetTime()+(g_CVAR_iBlockTime*60); + decl String:sQuery[256], String:sName[MAX_NAME_LENGTH], String:sAuth[32]; + GetClientName(iClient, SZF(sName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + + LogToFile(g_sLogFile, "%T", "LOG_BLOCKED", LANG_SERVER, sName, sAuth); + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "INSERT INTO `keys_blocked_players` (`auth`, `block_end`) VALUES ('%s', %d);", sAuth, g_iAttempts[iClient]); + } + else + { + FormatEx(SZF(sQuery), "INSERT INTO `keys_blocked_players` (`auth`, `block_end`, `sid`) VALUES ('%s', %d, %d);", sAuth, g_iAttempts[iClient], g_iServerID); + } + + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, sQuery); +} + +UnBlockClient(iClient) +{ + g_bIsBlocked[iClient] = false; + g_iAttempts[iClient] = 0; + decl String:sQuery[256], String:sAuth[32]; + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "DELETE FROM `keys_blocked_players` WHERE `auth` = '%s';", sAuth); + } + else + { + FormatEx(SZF(sQuery), "DELETE FROM `keys_blocked_players` WHERE `auth` = '%s' AND `sid` = %d;", sAuth, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, sQuery); +} + +DeleteKey(const String:sKey[], iClient = -1, ReplySource:CmdReplySource = SM_REPLY_TO_CONSOLE) +{ + decl String:sQuery[256], Handle:hDP; + + hDP = CreateDataPack(); + WritePackString(hDP, sKey); + if(iClient == -1) + { + WritePackCell(hDP, false); + } + else + { + WritePackCell(hDP, true); + WritePackCell(hDP, GET_UID(iClient)); + WritePackCell(hDP, CmdReplySource); + } + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `key_name` = '%s';", sKey); + } + else + { + FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d;", sKey, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_RemoveKey, sQuery, hDP); +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/keys_core.inc b/addons/sourcemod/scripting/include/keys_core.inc new file mode 100644 index 0000000..7d43cd5 --- /dev/null +++ b/addons/sourcemod/scripting/include/keys_core.inc @@ -0,0 +1,107 @@ +#if defined _keys_core_included + #endinput +#endif +#define _keys_core_included + +#define KEYS_MAX_LENGTH 64 + +// Прототип вызова при валидации параметров ключа +functag public bool:KeyParamsValidateCallback(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen); + +// Прототип вызова при акцивации ключа +functag public bool:KeyUseCallback(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen); + +// Прототип вызова при выводе параметров ключа +functag public KeyPrintCallback(iClient, const String:sKeyType[], Handle:hParamsArr, String:sBuffer[], iBufLen); + +// Вызывается когда ядро было загружено +forward Keys_OnCoreStarted(); + +// Загружено ли ядро +native bool:Keys_IsCoreStarted(); + +// Получает Handle базы данных +native Handle:Keys_GetCoreDatabase(); + +// Регистрирует тип ключей +native bool:Keys_RegKey(const String:sKeyType[], + KeyParamsValidateCallback:OnKeyParamsValidate, + KeyUseCallback:OnKeyUse, + KeyPrintCallback:OnKeyPrint); + +// Разрегистрирует тип ключей +native Keys_UnregKey(const String:sKeyType[]); + +// Для использования не забыть: +// LoadTranslations("keys_core.phrases"); +stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = LANG_SERVER) +{ + if (iTimeStamp > 31536000) + { + new iYears = iTimeStamp / 31536000; + new i = iTimeStamp - (iYears*31536000); + if(i > 2592000) + { + FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iYears, "YEARS", iClient, i / 2592000, "MONTHS", iClient); + } + else + { + FormatEx(sBuffer, iMaxLength, "%d %T", iYears, "YEARS", iClient); + } + return; + } + + if (iTimeStamp > 2592000) + { + new iMonths = iTimeStamp / 2592000; + new i = iTimeStamp - (iMonths*2592000); + if (i > 86400) + { + FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iMonths, "MONTHS", iClient, i / 86400, "DAYS", iClient); + } + else + { + FormatEx(sBuffer, iMaxLength, "%d %T", iMonths, "MONTHS", iClient); + } + return; + } + + if (iTimeStamp > 86400) + { + new iDays = iTimeStamp / 86400 % 365; + new iHours = (iTimeStamp / 3600) % 24; + if (iHours > 0) + { + FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iDays, "DAYS", iClient, iHours, "HOURS", iClient); + } + else + { + FormatEx(sBuffer, iMaxLength, "%d %T", iDays, "DAYS", iClient); + } + return; + } + + new iHours = (iTimeStamp / 3600); + new iMins = (iTimeStamp / 60) % 60; + new iSecs = iTimeStamp % 60; + + if (iHours > 0) + { + FormatEx(sBuffer, iMaxLength, "%02d:%02d:%02d", iHours, iMins, iSecs); + } + else + { + FormatEx(sBuffer, iMaxLength, "%02d:%02d", iMins, iSecs); + } +} + +public SharedPlugin:__pl_keys_core= +{ + name = "keys_core", + file = "Keys_Core.smx", +#if defined REQUIRE_PLUGIN + required = 1 +#else + required = 0 +#endif +}; \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/store.inc b/addons/sourcemod/scripting/include/store.inc new file mode 100644 index 0000000..057fcf9 --- /dev/null +++ b/addons/sourcemod/scripting/include/store.inc @@ -0,0 +1,100 @@ +#if defined _store_included + #endinput +#endif +#define _store_included + +new g_cvarChatTag = -1; +#define CHAT_TAG g_eCvars[g_cvarChatTag][sCache] + +#define ITEM_NAME_LENGTH 64 +#define STORE_MAX_ITEMS 2048 +#define STORE_MAX_HANDLERS 64 +#define STORE_MAX_PLANS 8 +#define STORE_MAX_SLOTS 4 + +enum Item_Plan +{ + String:szName[ITEM_NAME_LENGTH], + iPrice, + iTime +} + +enum Store_Item +{ + String:szName[ITEM_NAME_LENGTH], + String:szUniqueId[PLATFORM_MAX_PATH], + String:szShortcut[64], + iId, + iPrice, + iParent, + iHandler, + iFlagBits, + iData, + iPlans, + bool:bBuyable, + bool:bIgnoreVIP, + Handle:hAttributes +} + +enum Type_Handler +{ + String:szType[64], + String:szUniqueKey[32], + bool:bEquipable, + bool:bRaw, + Handle:hPlugin, + Function:fnMapStart, + Function:fnReset, + Function:fnConfig, + Function:fnUse, + Function:fnRemove +} + +enum Client_Item +{ + iId, + iUniqueId, + bool:bSynced, + bool:bDeleted, + iDateOfPurchase, + iDateOfExpiration, + iPriceOfPurchase, +} + +native Store_RegisterHandler(String:type[], String:uniquekey[], Function:mapstart, Function:reset, Function:config, Function:use, Function:remove, bool:equipable = true, bool:raw = false); +native Store_RegisterMenuHandler(String:identifier[], Function:menu, Function:handler); +native Store_SetDataIndex(itemid, index); +native Store_GetDataIndex(itemid); +native Store_GetEquippedItem(client, String:type[], slot=0); +native Store_IsClientLoaded(client); +native Store_DisplayPreviousMenu(client); +native Store_SetClientMenu(client, num); +native Store_GetClientCredits(client); +native Store_SetClientCredits(client, credits); +native Store_IsClientVIP(client); +native Store_IsItemInBoughtPackage(client, itemid, uid=-1); +native Store_ShouldConfirm(); +native Store_DisplayConfirmMenu(client, String:title[], Function:callback, data); +native Store_GetItem(itemid, output[Store_Item]); +native Store_GetHandler(index, output[Type_Handler]); +native Store_GiveItem(client, itemid, purchase=0, expiration=0, price=0); +native Store_RemoveItem(client, itemid); +native Store_GetClientItem(client, itemid, output[Client_Item]); +native Store_GetClientTarget(client); +native Store_GiveClientItem(client, recipient, itemid); +native Store_HasClientItem(client, itemid); +native Store_IterateEquippedItems(client, &start, bool:attributes=false); + +forward Store_OnClientModelChanged(client, String:model[]); + +public Extension:__ext_store_sm = +{ + name = "Store - The Resurrection", + file = "store_sm.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif + required = 0, +}; \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/api.sp b/addons/sourcemod/scripting/keys/api.sp new file mode 100644 index 0000000..21bce4d --- /dev/null +++ b/addons/sourcemod/scripting/keys/api.sp @@ -0,0 +1,82 @@ + +static Handle:g_hGlobalForward_OnCoreStarted; + +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + MarkNativeAsOptional("GetClientAuthId"); + MarkNativeAsOptional("GetClientAuthString"); + + g_hGlobalForward_OnCoreStarted = CreateGlobalForward("Keys_OnCoreStarted", ET_Ignore); + + CreateNative("Keys_IsCoreStarted", Native_IsCoreStarted); + CreateNative("Keys_GetCoreDatabase", Native_GetCoreDatabase); + CreateNative("Keys_RegKey", Native_RegKey); + CreateNative("Keys_UnregKey", Native_UnregKey); + + /* + CreateNative("Keys_GenerateKey", Native_GenerateKey); + CreateNative("Keys_AddKey", Keys_AddKey); + CreateNative("Keys_RemoveKey", Keys_RemoveKey); + CreateNative("Keys_IsValidKey", Native_IsValidKey); + */ + + RegPluginLibrary("keys_core"); + + return APLRes_Success; +} + +CreateForward_OnCoreStarted() +{ + Call_StartForward(g_hGlobalForward_OnCoreStarted); + Call_Finish(); +} + +public Native_IsCoreStarted(Handle:hPlugin, iNumParams) +{ + return g_bIsStarted; +} + +public Native_GetCoreDatabase(Handle:hPlugin, iNumParams) +{ + return _:CloneHandle(g_hDatabase, hPlugin); +} + +public Native_RegKey(Handle:hPlugin, iNumParams) +{ + decl String:sKeyType[KEYS_MAX_LENGTH]; + GetNativeString(1, sKeyType, sizeof(sKeyType)); + + if(FindStringInArray(g_hKeysArray, sKeyType) != -1) + { + ThrowNativeError(SP_ERROR_NATIVE, "Тип ключа \"%s\" уже зарегистрирован!", sKeyType); + return false; + } + + new Handle:hDataPack = CreateDataPack(); + WritePackCell(hDataPack, hPlugin); + WritePackCell(hDataPack, GetNativeCell(2)); + WritePackCell(hDataPack, GetNativeCell(3)); + WritePackCell(hDataPack, GetNativeCell(4)); + + SetTrieValue(g_hKeysTrie, sKeyType, hDataPack); + PushArrayString(g_hKeysArray, sKeyType); + + return true; +} + +public Native_UnregKey(Handle:hPlugin, iNumParams) +{ + decl String:sKeyType[KEYS_MAX_LENGTH], index; + GetNativeString(1, sKeyType, sizeof(sKeyType)); + + if((index = FindStringInArray(g_hKeysArray, sKeyType)) != -1) + { + RemoveFromArray(g_hKeysArray, index); + decl Handle:hDataPack; + if(GetTrieValue(g_hKeysTrie, sKeyType, hDataPack)) + { + CloseHandle(hDataPack); + } + RemoveFromTrie(g_hKeysArray, sKeyType); + } +} diff --git a/addons/sourcemod/scripting/keys/cmds.sp b/addons/sourcemod/scripting/keys/cmds.sp new file mode 100644 index 0000000..2b8147f --- /dev/null +++ b/addons/sourcemod/scripting/keys/cmds.sp @@ -0,0 +1,994 @@ +#define GET_UID(%0) (%0 == 0 ? 0:UID(%0)) + +GET_CID(iClient) +{ + if(iClient) + { + iClient = CID(iClient); + if(!iClient) + { + return -1; + } + + return iClient; + } + + return 0; +} + +RegAdminCmds() +{ + // CMD`s for use keys + RegConsoleCmd("key", UseKey_CMD); + RegConsoleCmd("usekey", UseKey_CMD); + + // CMD`s for create keys + RegAdminCmd("key_add", AddKey_CMD, ADMFLAG_ROOT); + RegAdminCmd("key_create", AddKey_CMD, ADMFLAG_ROOT); + RegAdminCmd("keys_gen", AddKey_CMD, ADMFLAG_ROOT); + + // CMD`s for remove keys + RegAdminCmd("key_del", DelKey_CMD, ADMFLAG_ROOT); + RegAdminCmd("key_rem", DelKey_CMD, ADMFLAG_ROOT); + RegAdminCmd("keys_clear", ClearKeys_CMD, ADMFLAG_ROOT); + + // CMD`s for keys output + RegAdminCmd("keys_list", KeysListDump_CMD, ADMFLAG_ROOT); + RegAdminCmd("keys_dump", KeysListDump_CMD, ADMFLAG_ROOT); +} + +public Action:UseKey_CMD(iClient, iArgs) +{ + if (iClient) + { + new ReplySource:CmdReplySource = GetCmdReplySource(); + + if(g_CVAR_iAttempts && g_bIsBlocked[iClient]) + { + if(g_iAttempts[iClient] > GetTime()) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_BLOCKED"); + return Plugin_Handled; + + } + else + { + UnBlockClient(iClient); + g_bIsBlocked[iClient] = false; + g_iAttempts[iClient] = 0; + } + } + + if(iArgs != 1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "USAGE_ERROR_USE_KEY"); + return Plugin_Handled; + } + + decl String:sKey[KEYS_MAX_LENGTH], String:sQuery[512]; + GetCmdArg(1, SZF(sKey)); + + if(!UTIL_ValidateKey(sKey, strlen(sKey), SZF(sQuery))) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", sQuery); + + if(g_CVAR_iAttempts) + { + if(g_iAttempts[iClient]++ >= g_CVAR_iAttempts) + { + BlockClient(iClient); + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_BLOCKED"); + return Plugin_Handled; + } + + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_KEY_LEFT", g_CVAR_iAttempts-g_iAttempts[iClient]); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_KEY"); + } + + return Plugin_Handled; + } + + decl Handle:hDP, String:sAuth[32]; + hDP = CreateDataPack(); + WritePackCell(hDP, UID(iClient)); + WritePackCell(hDP, CmdReplySource); + + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + if (g_bDBMySQL) + { + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "SELECT `key_name`, `type`, `expires`, `uses`, IF((SELECT `key_name` FROM `keys_players_used` WHERE `auth` = '%s' AND `key_name` = '%s') IS NULL, 0, 1) as `used`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` WHERE `key_name` = '%s' LIMIT 1;", sAuth, sKey, sKey); + } + else + { + FormatEx(SZF(sQuery), "SELECT `key_name`, `type`, `expires`, `uses`, IF((SELECT `key_name` FROM `keys_players_used` WHERE `auth` = '%s' AND `key_name` = '%s') IS NULL, 0, 1) as `used`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d LIMIT 1;", sAuth, sKey, sKey, g_iServerID); + } + } + else + { + FormatEx(SZF(sQuery), "SELECT `key_name`, `type`, `expires`, `uses`, CASE WHEN (SELECT `key_name` FROM `keys_players_used` WHERE `auth` = '%s' AND `key_name` = '%s') IS NULL THEN 0 ELSE 1 END AS `used`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` WHERE `key_name` = '%s' LIMIT 1;", sAuth, sKey, sKey); + } + + SQL_TQuery(g_hDatabase, SQL_Callback_UseKey, sQuery, hDP); + } + + return Plugin_Handled; +} + +public SQL_Callback_UseKey(Handle:hOwner, Handle:hResult, const String:sDBError[], any:hDP) +{ + if (hResult == INVALID_HANDLE || sDBError[0]) + { + CloseHandle(hDP); + LogError("SQL_Callback_UseKey: %s", sDBError); + return; + } + + ResetPack(hDP); + + new iClient = CID(ReadPackCell(hDP)); + new ReplySource:CmdReplySource = ReplySource:ReadPackCell(hDP); + CloseHandle(hDP); + + if (iClient) + { + if(SQL_FetchRow(hResult)) + { + decl Handle:hDataPack, String:sKeyType[KEYS_MAX_LENGTH]; + SQL_FetchString(hResult, 1, SZF(sKeyType)); + if(GetTrieValue(g_hKeysTrie, sKeyType, hDataPack)) + { + decl Handle:hPlugin, Function:fUseCallback, Handle:hParamsArr, String:sKey[KEYS_MAX_LENGTH], String:sParam[KEYS_MAX_LENGTH], String:sError[256], iExpires, iUses, i, bool:bResult; + SQL_FetchString(hResult, 0, SZF(sKey)); + + iExpires = SQL_FetchInt(hResult, 2); + if(iExpires) + { + if(iExpires < GetTime()) + { + DeleteKey(sKey); + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_KEY_NOT_EXIST"); + return; + } + } + + iUses = SQL_FetchInt(hResult, 3); + if(!iUses) + { + DeleteKey(sKey); + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_KEY_NOT_EXIST"); + return; + } + + if(SQL_FetchInt(hResult, 4)) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_KEY_ALREADY_USED"); + return; + } + + hParamsArr = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); + for(i = 5; i < 10; ++i) + { + if(SQL_IsFieldNull(hResult, i)) + { + break; + } + + SQL_FetchString(hResult, i, SZF(sParam)); + PushArrayString(hParamsArr, sParam); + } + + SetPackPosition(hDataPack, DP_Plugin); + hPlugin = Handle:ReadPackCell(hDataPack); + + SetPackPosition(hDataPack, DP_OnUseCallback); + fUseCallback = Function:ReadPackCell(hDataPack); + + bResult = false; + Call_StartFunction(hPlugin, fUseCallback); + Call_PushCell(iClient); + Call_PushString(sKeyType); + Call_PushCell(hParamsArr); + Call_PushStringEx(SZF(sError), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushCell(sizeof(sError)); + Call_Finish(bResult); + + CloseHandle(hParamsArr); + + if(!bResult) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%s", sError); + return; + } + + decl String:sName[MAX_NAME_LENGTH], String:sAuth[32], String:sQuery[256]; + GetClientName(iClient, SZF(sName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + + if(--iUses) + { + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "INSERT INTO `keys_players_used` (`auth`, `key_name`) VALUES ('%s', '%s');", sAuth, sKey); + } + else + { + FormatEx(SZF(sQuery), "INSERT INTO `keys_players_used` (`auth`, `key_name`, `sid`) VALUES ('%s', '%s', %d);", sAuth, sKey, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, sQuery); + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "UPDATE `table_keys` SET `uses` = %d WHERE `key_name` = '%s';", iUses, sKey); + } + else + { + FormatEx(SZF(sQuery), "UPDATE `table_keys` SET `uses` = %d WHERE `key_name` = '%s' AND `sid` = %d;", iUses, sKey, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, sQuery); + } + else + { + DeleteKey(sKey); + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "DELETE FROM `keys_players_used` WHERE `key_name` = '%s';", sKey); + } + else + { + FormatEx(SZF(sQuery), "DELETE FROM `keys_players_used` WHERE `key_name` = '%s' AND `sid` = %d;", sKey, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, sQuery); + } + + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_USE_KEY", sKey); + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_USE_KEY", LANG_SERVER, sName, sAuth, sKey); + return; + } + + return; + } + + if(g_CVAR_iAttempts) + { + if(g_iAttempts[iClient]++ >= g_CVAR_iAttempts) + { + BlockClient(iClient); + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_BLOCKED"); + return; + } + + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_KEY_LEFT", g_CVAR_iAttempts-g_iAttempts[iClient]); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_KEY"); + } + } +} + +public Action:AddKey_CMD(iClient, iArgs) +{ + new ReplySource:CmdReplySource = GetCmdReplySource(); + + if(iArgs < 5) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_NUM_ARGS"); + return Plugin_Handled; + } + + decl String:sKeyType[KEYS_MAX_LENGTH], Handle:hDataPack; + GetCmdArg(4, SZF(sKeyType)); + + if(!GetTrieValue(g_hKeysTrie, sKeyType, hDataPack)) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_TYPE"); + return Plugin_Handled; + } + + decl Handle:hParamsArr, Handle:hPlugin, Function:FuncOnValidateParams, String:sKey[KEYS_MAX_LENGTH], String:sParam[KEYS_MAX_LENGTH], String:sError[256], iLifeTime, iUses, iCount, i, bool:bResult, bool:bGen; + + GetCmdArg(0, SZF(sKey)); + + bGen = bool:(sKey[3] == 's'); + + if(bGen) + { + GetCmdArg(1, SZF(sParam)); + iCount = StringToInt(sParam); + if(iCount < 1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_AMOUNT"); + return Plugin_Handled; + } + } + else + { + GetCmdArg(1, SZF(sKey)); + if(!UTIL_ValidateKey(sKey, strlen(sKey), SZF(sError))) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", sError); + return Plugin_Handled; + } + } + + GetCmdArg(2, SZF(sParam)); + iLifeTime = StringToInt(sParam); + if(iLifeTime < 0) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_LIFETIME"); + return Plugin_Handled; + } + + GetCmdArg(3, SZF(sParam)); + iUses = StringToInt(sParam); + if(iUses < 1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_USES"); + return Plugin_Handled; + } + + hParamsArr = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); + + for(i = 5; i <= iArgs; ++i) + { + GetCmdArg(i, SZF(sParam)); + PushArrayString(hParamsArr, sParam); + } + + SetPackPosition(hDataPack, DP_Plugin); + hPlugin = Handle:ReadPackCell(hDataPack); + + SetPackPosition(hDataPack, DP_OnValidateCallback); + FuncOnValidateParams = Function:ReadPackCell(hDataPack); + + bResult = false; + Call_StartFunction(hPlugin, FuncOnValidateParams); + Call_PushCell(iClient); + Call_PushString(sKeyType); + Call_PushCell(hParamsArr); + Call_PushStringEx(SZF(sError), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushCell(sizeof(sError)); + Call_Finish(bResult); + + if(!bResult) + { + CloseHandle(hParamsArr); + UTIL_ReplyToCommand(iClient, CmdReplySource, "%s", sError); + return Plugin_Handled; + } + + decl Handle:hDP, String:sQuery[256], iExpires; + + iClient = GET_UID(iClient); + iExpires = iLifeTime ? (iLifeTime + GetTime()):iLifeTime; + + if(bGen) + { + while(iCount > 0) + { + --iCount; + + UTIL_GenerateKey(sKey); + + hDP = CreateDataPack(); + WritePackCell(hDP, CloneArray(hParamsArr)); + WritePackString(hDP, sKey); + WritePackCell(hDP, false); + WritePackCell(hDP, iClient); + WritePackCell(hDP, CmdReplySource); + WritePackString(hDP, sKeyType); + WritePackCell(hDP, iUses); + WritePackCell(hDP, iExpires); + WritePackCell(hDP, iLifeTime); + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s';", sKey); + } + else + { + FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d;", sKey, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_SearchKey, sQuery, hDP); + } + } + else + { + hDP = CreateDataPack(); + WritePackCell(hDP, CloneArray(hParamsArr)); + WritePackString(hDP, sKey); + WritePackCell(hDP, true); + WritePackCell(hDP, iClient); + WritePackCell(hDP, CmdReplySource); + WritePackString(hDP, sKeyType); + WritePackCell(hDP, iUses); + WritePackCell(hDP, iExpires); + WritePackCell(hDP, iLifeTime); + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s';", sKey); + } + else + { + FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d;", sKey, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_SearchKey, sQuery, hDP); + } + + CloseHandle(hParamsArr); + + return Plugin_Handled; +} + +public SQL_Callback_SearchKey(Handle:hOwner, Handle:hResult, const String:sError[], any:hDP) +{ + ResetPack(hDP); + + new Handle:hParamsArr = Handle:ReadPackCell(hDP); + + if (hResult == INVALID_HANDLE || sError[0]) + { + LogError("SQL_Callback_SearchKey: %s", sError); + CloseHandle(hParamsArr); + CloseHandle(hDP); + return; + } + + decl String:sQuery[1024], String:sKey[KEYS_MAX_LENGTH], i; + ReadPackString(hDP, SZF(sKey)); + + if(SQL_FetchRow(hResult)) + { + if(ReadPackCell(hDP)) + { + i = GET_CID(ReadPackCell(hDP)); + if(i != -1) + { + UTIL_ReplyToCommand(i, ReplySource:ReadPackCell(hDP), "%t", "ERROR_KEY_ALREADY_EXISTS", sKey); + } + + CloseHandle(hParamsArr); + return; + } + else + { + decl Handle:hDP2; + hDP2 = CreateDataPack(); + WritePackCell(hDP2, hParamsArr); + UTIL_GenerateKey(sKey); + WritePackString(hDP2, sKey); // New Key + WritePackCell(hDP2, false); + i = ReadPackCell(hDP); // Client + WritePackCell(hDP2, i); + i = ReadPackCell(hDP); // CmdReplySource + WritePackCell(hDP2, i); + ReadPackString(hDP, SZF(sKey)); + WritePackString(hDP2, sKey); + i = ReadPackCell(hDP); // Uses + WritePackCell(hDP2, i); + i = ReadPackCell(hDP); // Expires + WritePackCell(hDP2, i); + i = ReadPackCell(hDP); // LifeTime + WritePackCell(hDP2, i); + CloseHandle(hDP); + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s';", sKey); + } + else + { + FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d;", sKey, g_iServerID); + } + SQL_TQuery(g_hDatabase, SQL_Callback_SearchKey, sQuery, hDP2); + } + + return; + } + + decl String:sBufferColumns[256], String:sBufferValues[256], String:sKeyType[KEYS_MAX_LENGTH], String:sParam[KEYS_MAX_LENGTH], iExpires, iUses; + + ReadPackCell(hDP); // ... + ReadPackCell(hDP); // Client + ReadPackCell(hDP); // CmdReplySource + + ReadPackString(hDP, SZF(sKeyType)); + + iUses = ReadPackCell(hDP); + iExpires = ReadPackCell(hDP); + + strcopy(SZF(sBufferColumns), "`param1`"); + GetArrayString(hParamsArr, 0, SZF(sParam)); + FormatEx(SZF(sBufferValues), "'%s'", sParam); + + for(i = 1; i < GetArraySize(hParamsArr); ++i) + { + Format(SZF(sBufferColumns), "%s, `param%d`", sBufferColumns, i+1); + GetArrayString(hParamsArr, i, SZF(sParam)); + Format(SZF(sBufferValues), "%s, '%s'", sBufferValues, sParam); + } + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "INSERT INTO `table_keys` (`key_name`, `type`, `expires`, `uses`, %s) VALUES ('%s', '%s', %d, %d, %s);", sBufferColumns, sKey, sKeyType, iExpires, iUses, sBufferValues); + + } + else + { + FormatEx(SZF(sQuery), "INSERT INTO `table_keys` (`key_name`, `type`, `expires`, `uses`, `sid`, %s) VALUES ('%s', '%s', %d, %d, %d, %s);", sBufferColumns, sKey, sKeyType, iExpires, iUses, g_iServerID, sBufferValues); + + } + SQL_TQuery(g_hDatabase, SQL_Callback_AddKey, sQuery, hDP); +} + +public SQL_Callback_AddKey(Handle:hOwner, Handle:hResult, const String:sError[], any:hDP) +{ + ResetPack(hDP); + + new Handle:hParamsArr = Handle:ReadPackCell(hDP); + + if (hResult == INVALID_HANDLE || sError[0]) + { + LogError("SQL_Callback_AddKey: %s", sError); + CloseHandle(hParamsArr); + CloseHandle(hDP); + return; + } + + decl Handle:hDataPack, Handle:hPlugin, Function:fPrintCallback, String:sKey[KEYS_MAX_LENGTH], String:sKeyType[KEYS_MAX_LENGTH], String:sParams[512], String:sName[MAX_NAME_LENGTH], String:sAuth[32], String:sExpires[64], iLifeTime, iUses, iClient, ReplySource:CmdReplySource; + ReadPackString(hDP, SZF(sKey)); + ReadPackCell(hDP); + iClient = GET_CID(ReadPackCell(hDP)); + CmdReplySource = ReplySource:ReadPackCell(hDP); + + ReadPackString(hDP, SZF(sKeyType)); + + iUses = ReadPackCell(hDP); + ReadPackCell(hDP); + iLifeTime = ReadPackCell(hDP); + + if(iClient == -1) + { + iClient = 0; + } + + if(!iClient) + { + strcopy(SZF(sName), "CONSOLE"); + strcopy(SZF(sAuth), "STEAM_ID_SERVER"); + } + else + { + GetClientName(iClient, SZF(sName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + } + + if(iLifeTime) + { + Keys_GetTimeFromStamp(SZF(sExpires), iLifeTime, iClient); + } + else + { + FormatEx(SZF(sExpires), "%T", "FOREVER", iClient); + } + + sParams[0] = 0; + + GetTrieValue(g_hKeysTrie, sKeyType, hDataPack); + SetPackPosition(hDataPack, DP_Plugin); + hPlugin = Handle:ReadPackCell(hDataPack); + SetPackPosition(hDataPack, DP_OnPrintCallback); + fPrintCallback = Function:ReadPackCell(hDataPack); + Call_StartFunction(hPlugin, fPrintCallback); + Call_PushCell(LANG_SERVER); + Call_PushString(sKeyType); + Call_PushCell(hParamsArr); + Call_PushStringEx(SZF(sParams), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushCell(sizeof(sParams)); + Call_Finish(); + + if(SQL_GetAffectedRows(hOwner)) + { + if(iClient != -1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_CREATE_KEY", sKey); + } + + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_CREATE_KEY", LANG_SERVER, sName, sAuth, sKey, sExpires, iUses, sKeyType, sParams); + } + else + { + if(iClient != -1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_CREATE_KEY", sKey); + } + + LogToFile(g_sLogFile, "%T", "LOG_ERROR_CREATE_KEY", LANG_SERVER, sKey, sName, sAuth, sExpires, iUses, sKeyType, sParams); + } + + CloseHandle(hParamsArr); + CloseHandle(hDP); +} + +public Action:DelKey_CMD(iClient, iArgs) +{ + new ReplySource:CmdReplySource = GetCmdReplySource(); + + if(iArgs != 1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_NUM_ARGS"); + return Plugin_Handled; + } + + decl String:sKey[KEYS_MAX_LENGTH], iLength; + GetCmdArg(1, SZF(sKey)); + + iLength = strlen(sKey); + if(iLength > KEYS_MAX_LENGTH || iLength < 8) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_KEY"); + return Plugin_Handled; + } + + DeleteKey(sKey, iClient, CmdReplySource); + + return Plugin_Handled; +} + +public SQL_Callback_RemoveKey(Handle:hOwner, Handle:hResult, const String:sError[], any:hDP) +{ + if (hResult == INVALID_HANDLE || sError[0]) + { + CloseHandle(hDP); + LogError("SQL_Callback_RemoveKey: %s", sError); + return; + } + + ResetPack(hDP); + + decl String:sKey[KEYS_MAX_LENGTH]; + ReadPackString(hDP, SZF(sKey)); + + if(ReadPackCell(hDP)) + { + decl iClient, String:sName[MAX_NAME_LENGTH], String:sAuth[32], ReplySource:CmdReplySource; + iClient = GET_CID(ReadPackCell(hDP)); + CmdReplySource = ReplySource:ReadPackCell(hDP); + + if(iClient == -1) + { + iClient = 0; + } + + if(!iClient) + { + strcopy(SZF(sName), "CONSOLE"); + strcopy(SZF(sAuth), "STEAM_ID_SERVER"); + } + else + { + GetClientName(iClient, SZF(sName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + } + + if(SQL_GetAffectedRows(hOwner)) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_REMOVE_KEY", sKey); + + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_REMOVE_KEY", LANG_SERVER, sKey, sName, sAuth); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_REMOVE_KEY", sKey); + + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_REMOVE_KEY", LANG_SERVER, sKey, sName, sAuth); + } + } + else + { + if(SQL_GetAffectedRows(hOwner)) + { + LogToFile(g_sLogFile, "%T", "SUCCESS_REMOVE_KEY", LANG_SERVER, sKey); + } + else + { + LogToFile(g_sLogFile, "%T", "ERROR_REMOVE_KEY", LANG_SERVER, sKey); + } + } + + CloseHandle(hDP); +} + +public Action:ClearKeys_CMD(iClient, iArgs) +{ + new ReplySource:CmdReplySource = GetCmdReplySource(); + + decl String:sKeyType[64]; + if(iArgs == 1) + { + GetCmdArg(1, SZF(sKeyType)); + if(FindStringInArray(g_hKeysArray, sKeyType) == -1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_INCORRECT_TYPE"); + return Plugin_Handled; + } + } + else + { + sKeyType[0] = 0; + } + + decl Handle:hDP, String:sQuery[256]; + hDP = CreateDataPack(); + WritePackCell(hDP, GET_UID(iClient)); + WritePackCell(hDP, CmdReplySource); + if(sKeyType[0]) + { + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `type` = '%s';", sKeyType); + } + else + { + FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `type` = '%s' AND `sid` = %d;", sKeyType, g_iServerID); + } + WritePackCell(hDP, true); + WritePackString(hDP, sKeyType); + } + else + { + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "DELETE FROM `table_keys`;"); + } + else + { + FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `sid` = %d;", g_iServerID); + } + } + + SQL_TQuery(g_hDatabase, SQL_Callback_RemoveKeys, sQuery, hDP); + + return Plugin_Handled; +} + +public SQL_Callback_RemoveKeys(Handle:hOwner, Handle:hResult, const String:sError[], any:hDP) +{ + if (hResult == INVALID_HANDLE || sError[0]) + { + LogError("SQL_Callback_RemoveKeys: %s", sError); + CloseHandle(hDP); + return; + } + + ResetPack(hDP); + + decl iClient, String:sKeyType[64], String:sName[MAX_NAME_LENGTH], String:sAuth[32], ReplySource:CmdReplySource; + iClient = GET_CID(ReadPackCell(hDP)); + CmdReplySource = ReplySource:ReadPackCell(hDP); + + if(iClient == -1) + { + iClient = 0; + } + + if(!iClient) + { + strcopy(SZF(sName), "CONSOLE"); + strcopy(SZF(sAuth), "STEAM_ID_SERVER"); + } + else + { + GetClientName(iClient, SZF(sName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + } + + if(IsPackReadable(hDP, 4)) + { + ReadPackCell(hDP); + ReadPackString(hDP, SZF(sKeyType)); + } + else + { + sKeyType[0] = 0; + } + + CloseHandle(hDP); + + if(SQL_GetAffectedRows(hOwner)) + { + if(sKeyType[0]) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_REMOVE_KEYS_TYPE", sKeyType); + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_REMOVE_KEYS_TYPE", LANG_SERVER, sKeyType, sName, sAuth); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_REMOVE_KEYS"); + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_REMOVE_KEYS", LANG_SERVER, sKeyType, sName, sAuth); + } + } + else + { + if(sKeyType[0]) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_REMOVE_KEYS_TYPE", sKeyType); + LogToFile(g_sLogFile, "%T", "LOG_ERROR_REMOVE_KEYS_TYPE", LANG_SERVER, sKeyType, sName, sAuth); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_REMOVE_KEYS"); + LogToFile(g_sLogFile, "%T", "LOG_ERROR_REMOVE_KEYS", LANG_SERVER, sKeyType, sName, sAuth); + } + } +} + +public Action:KeysListDump_CMD(iClient, iArgs) +{ + decl ReplySource:CmdReplySource, Handle:hDP, String:sQuery[512], iOffset, bool:bToFile; + CmdReplySource = GetCmdReplySource(); + + if(iArgs) + { + GetCmdArg(2, sQuery, 16); + iOffset = StringToInt(sQuery); + if(iOffset < 0) + { + iOffset = 0; + } + } + else + { + iOffset = 0; + } + + hDP = CreateDataPack(); + WritePackCell(hDP, GET_UID(iClient)); + WritePackCell(hDP, CmdReplySource); + GetCmdArg(0, sQuery, 32); + bToFile = sQuery[5] == 'd'; + WritePackCell(hDP, bToFile); + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "SELECT `key_name`, `type`, `expires`, `uses`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` ORDER BY `type`, `param1`, `param2`, `param3`, `param4`, `param5`, `expires`, `uses`;"); + } + else + { + FormatEx(SZF(sQuery), "SELECT `key_name`, `type`, `expires`, `uses`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` WHERE `sid` = %d ORDER BY `type`, `param1`, `param2`, `param3`, `param4`, `param5`, `expires`, `uses`;", g_iServerID); + } + + if(!bToFile) + { + sQuery[strlen(sQuery)-1] = 0; + Format(SZF(sQuery), "%s LIMIT %d, %d;", sQuery, iOffset, iClient ? 20:100); + } + + SQL_TQuery(g_hDatabase, SQL_Callback_SelectKeysList, sQuery, hDP); + + return Plugin_Handled; +} + +public SQL_Callback_SelectKeysList(Handle:hOwner, Handle:hResult, const String:sError[], any:hDP) +{ + if (hResult == INVALID_HANDLE || sError[0]) + { + CloseHandle(hDP); + LogError("SQL_Callback_SelectKeysList: %s", sError); + return; + } + + ResetPack(hDP); + decl iClient, ReplySource:CmdReplySource, bool:bToFile; + iClient = GET_CID(ReadPackCell(hDP)); + CmdReplySource = ReplySource:ReadPackCell(hDP); + bToFile = bool:ReadPackCell(hDP); + CloseHandle(hDP); + + if(!bToFile && iClient == -1) + { + return; + } + + if(SQL_GetRowCount(hResult) > 0) + { + decl String:sKey[64], String:sKeyType[64], String:sExpires[64], iUses, iCount, iTime, iExpires, i; + decl Handle:hFile, Handle:hDataPack, Handle:hPlugin, Function:fPrintCallback, Handle:hParamsArr, String:sParam[KEYS_MAX_LENGTH], String:sParams[512]; + + if(bToFile) + { + BuildPath(Path_SM, SZF(sParams), "data/keys_dump.txt"); + hFile = OpenFile(sParams, "w+"); + } + + iCount = 0; + iTime = GetTime(); + + while(SQL_FetchRow(hResult)) + { + SQL_FetchString(hResult, 1, SZF(sKeyType)); + + if(GetTrieValue(g_hKeysTrie, sKeyType, hDataPack)) + { + SQL_FetchString(hResult, 0, SZF(sKey)); + + iExpires = SQL_FetchInt(hResult, 2); + + if(iExpires) + { + if(iExpires < iTime) + { + DeleteKey(sKey); + continue; + } + + Keys_GetTimeFromStamp(SZF(sExpires), iExpires-iTime, iClient); + } + else + { + FormatEx(SZF(sExpires), "%T", "FOREVER", iClient); + } + + iUses = SQL_FetchInt(hResult, 3); + + if(!iUses) + { + DeleteKey(sKey); + continue; + } + + hParamsArr = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); + + for(i = 4; i < 9; ++i) + { + if(SQL_IsFieldNull(hResult, i)) + { + break; + } + + SQL_FetchString(hResult, i, SZF(sParam)); + PushArrayString(hParamsArr, sParam); + } + + sParams[0] = 0; + + SetPackPosition(hDataPack, DP_Plugin); + hPlugin = Handle:ReadPackCell(hDataPack); + SetPackPosition(hDataPack, DP_OnPrintCallback); + fPrintCallback = Function:ReadPackCell(hDataPack); + Call_StartFunction(hPlugin, fPrintCallback); + Call_PushCell(iClient); + Call_PushString(sKeyType); + Call_PushCell(hParamsArr); + Call_PushStringEx(sParams, 256, SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushCell(256); + Call_Finish(); + + CloseHandle(hParamsArr); + + if(bToFile) + { + WriteFileLine(hFile, "%d. %s\t\t%T: %12s\t\t%T: %4i\t\t%T: %s\t\t%s", ++iCount, sKey, "EXPIRES", iClient, sExpires, "USAGE_LEFT", iClient, iUses, "TYPE", iClient, sKeyType, sParams); + continue; + } + + UTIL_ReplyToCommand(iClient, CmdReplySource, "%d. %s\t\t%T: %12s\t\t%T: %4i\t\t%T: %s\t\t%s", ++iCount, sKey, "EXPIRES", iClient, sExpires, "USAGE_LEFT", iClient, iUses, "TYPE", iClient, sKeyType, sParams); + } + } + + if(bToFile) + { + CloseHandle(hFile); + } + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "ERROR_LIST_NO_KEYS"); + } +} diff --git a/addons/sourcemod/scripting/keys/utils.sp b/addons/sourcemod/scripting/keys/utils.sp new file mode 100644 index 0000000..404c60c --- /dev/null +++ b/addons/sourcemod/scripting/keys/utils.sp @@ -0,0 +1,126 @@ + + +UTIL_ReplyToCommand(iClient, ReplySource:CmdReplySource, const String:sFormat[], any:...) +{ + static String:sBuffer[2048]; + SetGlobalTransTarget(iClient); + VFormat(sBuffer, sizeof(sBuffer), sFormat, 4); + + if(iClient) + { + switch(CmdReplySource) + { + case SM_REPLY_TO_CONSOLE: PrintToConsole(iClient, "[KEYS] %s", sBuffer); + case SM_REPLY_TO_CHAT: PrintToChat(iClient, GetEngineVersion() == Engine_CSGO ? " \x04[KEYS] \x01%s":"\x04[KEYS] \x01%s", sBuffer); + } + } + else + { + PrintToServer("[KEYS] %s", sBuffer); + } +} + +UTIL_ValidateKey(String:sKey[], iLength, String:sError[], iErrLen) +{ + if(!sKey[0]) + { + strcopy(sError, iErrLen, "ERROR_KEY_EMPTY"); + return false; + } + + if(iLength < 8) + { + strcopy(sError, iErrLen, "ERROR_KEY_SHORT"); + return false; + } + + if(iLength > 64) + { + strcopy(sError, iErrLen, "ERROR_KEY_LONG"); + return false; + } + + new i = 0; + + while (i < iLength) + { + if((sKey[i] > 0x2F && sKey[i] < 0x3A) || + (sKey[i] > 0x40 && sKey[i] < 0x5B) || + (sKey[i] > 0x60 && sKey[i] < 0x7B) || + sKey[i] == 0x2D) + { + ++i; + continue; + } + + strcopy(sError, iErrLen, "ERROR_KEY_INVALID_CHARACTERS"); + return false; + } + + return true; +} + +UTIL_GenerateKey(String:sKey[]) +{ + sKey[0] = '\0'; + + new i = 0; + + if(g_CVAR_sKeyTemplate[0]) + { + new iLength = strlen(g_CVAR_sKeyTemplate); + while (i < iLength) + { + sKey[i] = UTIL_GetCharTemplate(g_CVAR_sKeyTemplate[i]); + ++i; + } + } + else + { + while (i < g_CVAR_iKeyLength) + { + sKey[i] = UTIL_GetCharTemplate(0x58); + ++i; + } + } + + sKey[i] = '\0'; +} +/* +A - Буква в любом регистре\n\ +B - Цифра 0-9\n\ +X - Цифра 0-9 либо буква в любом регистре\n\ +*/ + +static const g_iNumbers[] = {0x30, 0x39}; +static const g_iLettersUpper[] = {0x41, 0x5A}; +static const g_iLettersLower[] = {0x61, 0x7A}; + +UTIL_GetCharTemplate(iChar) +{ + switch(iChar) + { + // A - буква в любом регистре + case 0x41: return GetRandomInt(1, 20) > 10 ? UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1]); + // B - число 0-9 + case 0x42: return UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]); + // X - число 0-9 либо буква в любом регистре + case 0x58: return GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):(GetRandomInt(1, 20) > 10 ? UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1])); + // Другой символ + default: return iChar; + } + + return iChar; +} + +UTIL_GetRandomInt(iMin, iMax) +{ + new iRandom = GetURandomInt(); + + if (iRandom == 0) + { + ++iRandom; + } + + return RoundToCeil(float(iRandom) / (float(2147483647) / float(iMax - iMin + 1))) + iMin - 1; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/vars.sp b/addons/sourcemod/scripting/keys/vars.sp new file mode 100644 index 0000000..3c839a2 --- /dev/null +++ b/addons/sourcemod/scripting/keys/vars.sp @@ -0,0 +1,30 @@ + +#define UID(%0) GetClientUserId(%0) +#define CID(%0) GetClientOfUserId(%0) +#define SZF(%0) %0, sizeof(%0) + +#define DP_Plugin 0 +#define DP_OnValidateCallback 9 +#define DP_OnUseCallback 18 +#define DP_OnPrintCallback 27 + +new String:g_sLogFile[256]; + +new bool:g_bIsStarted; +new Handle:g_hDatabase, + bool:g_bDBMySQL; + +new bool:g_bIsBlocked[MAXPLAYERS+1], + g_iAttempts[MAXPLAYERS+1]; + +new Handle:g_hKeysTrie; +new Handle:g_hKeysArray; + +new g_iServerID = -1; + +new g_CVAR_iServerID; +new g_CVAR_iKeyLength; +new String:g_CVAR_sKeyTemplate[64]; +new g_CVAR_iAttempts; +new g_CVAR_iBlockTime; + diff --git a/addons/sourcemod/translations/keys_core.phrases.txt b/addons/sourcemod/translations/keys_core.phrases.txt new file mode 100644 index 0000000..66b2ed4 --- /dev/null +++ b/addons/sourcemod/translations/keys_core.phrases.txt @@ -0,0 +1,290 @@ +"Phrases" +{ + "USAGE_ERROR_USE_KEY" + { + "ru" "Используйте: key/usekey <ключ>" + "en" "Usage: key/usekey " + } + "USAGE_ERROR_ADD_KEY" + { + "ru" "Используйте: key_create/key_add <ключ> <время жизни> <количество использований> <тип> <параметры>" + "en" "Usage: key_create/key_add " + } + "USAGE_ERROR_GEN_KEYS" + { + "ru" "Используйте: keys_gen <количество> <время жизни> <количество использований> <тип> <параметры>" + "en" "Usage: keys_gen " + } + + "SUCCESS_USE_KEY" + { + "#format" "{1:s}" + "ru" "Вы успешно использовали ключ: {1}!" + "en" "You have successfully used the key: {1}!" + } + + "LOG_SUCCESS_USE_KEY" + { + "#format" "{1:s},{2:s},{3:s}" + "ru" "Игрок {1} ({2}) использовал ключ {3}" + "en" "Player {1} ({2}) used the key {3}" + } + + "ERROR_KEY_NOT_EXIST" + { + "ru" "ОШИБКА: Ключ не существует!" + "en" "ERROR: The key does not exist!" + "fi" "Avain ei ole olemassa!" + } + + "ERROR_KEY_ALREADY_USED" + { + "ru" "Вы уже использовали этот ключ!" + "en" "You have already used this key!" + } + + "ERROR_NUM_ARGS" + { + "ru" "ОШИБКА: Неверное количество аргументов!" + "en" "ERROR: Wrong number of arguments!" + } + + "ERROR_INCORRECT_TYPE" + { + "ru" "ОШИБКА: Неверный тип ключа!" + "en" "ERROR: Invalid key type!" + } + + "ERROR_KEY_ALREADY_EXISTS" + { + "#format" "{1:s}" + "ru" "ОШИБКА: Ключ {1} уже существует!" + "en" "ERROR: Key {1} already exists!" + } + + "ERROR_INCORRECT_KEY" + { + "ru" "ОШИБКА: Некорректный ключ!" + "en" "ERROR: Incorrect key!" + } + + "ERROR_INCORRECT_TIME" + { + "ru" "ОШИБКА: Неверное время!" + "en" "ERROR: Incorrect time!" + "fi" "VIRHE: Väärä aika!" + } + + "ERROR_INCORRECT_AMOUNT" + { + "ru" "ОШИБКА: Неверное количество!" + "en" "ERROR: Incorrect amount!" + "fi" "VIRHE: Väärä määrä!" + } + + "ERROR_KEY_EMPTY" + { + "ru" "ОШИБКА: Ключ пустой!" + "en" "ERROR: The key is empty!" + } + + "ERROR_KEY_SHORT" + { + "ru" "ОШИБКА: Ключ слишком короткий!" + "en" "ERROR: The key is too short!" + } + + "ERROR_KEY_LONG" + { + "ru" "ОШИБКА: Ключ слишком длинный!" + "en" "ERROR: The key is too long!" + } + + "ERROR_KEY_INVALID_CHARACTERS" + { + "ru" "ОШИБКА: Ключ содержит запрещенные символы!" + "en" "ERROR: The key contains illegal characters!" + } + + "SUCCESS_CREATE_KEY" + { + "#format" "{1:s}" + "ru" "Ключ {1} успешно добавлен!" + "en" "The key {1} successfully added!" + "fi" "Avain {1} onnistuneesti lisätty!" + } + + "ERROR_CREATE_KEY" + { + "#format" "{1:s}" + "ru" "Ошибка добавления ключа {1}" + "en" "Error adding key {1}" + } + + "LOG_SUCCESS_CREATE_KEY" + { + "#format" "{1:s},{2:s},{3:s},{4:s},{5:i},{6:s},{7:s}" + "ru" "Игрок {1} ({2}) добавил ключ {3} (Время жизни: {4}, Количество использований: {5}, Тип: {6}, {7})" + "en" "Player {1} ({2}) added key {3} (Lifetime: {4}, Number of uses: {5}, Type: {6}, {7})" + } + + "LOG_ERROR_CREATE_KEY" + { + "#format" "{1:s},{2:s},{3:s},{4:s},{5:i},{6:s},{7:s}" + "ru" "Ошибка добавления ключа {1} (Игрок {2} ({3}), Время жизни: {4}, Количество использований: {5}, Тип: {6}, {7})" + "en" "Error adding key {1} (Player {2} ({3}), Life time: {4}, Number of uses: {5}, Type: {6}, {7})" + } + + "SUCCESS_REMOVE_KEY" + { + "#format" "{1:s}" + "ru" "Ключ {1} успешно удален!" + "en" "Key {1} deleted successfully!" + "fi" "Avain {1} poistettu onnistuneesti!" + } + + "ERROR_REMOVE_KEY" + { + "#format" "{1:s}" + "ru" "Ошибка удаления ключа {1}" + "en" "Error removing key {1}" + } + + "LOG_SUCCESS_REMOVE_KEY" + { + "#format" "{1:s},{2:s},{3:s}" + "ru" "Ключ {1} успешно удален игроком {2} ({3})!" + "en" "The key {1} was successfully deleted by the player {2} ({3})!" + } + + "LOG_ERROR_REMOVE_KEY" + { + "#format" "{1:s},{2:s},{3:s}" + "ru" "Ошибка удаления ключа {1} игроком {2} ({3})!" + "en" "Error removing key {1} by player {2} ({3})!" + } + + "SUCCESS_REMOVE_KEYS" + { + "ru" "Ключи успешно удалены!" + "en" "Keys deleted successfully!" + } + + "LOG_SUCCESS_REMOVE_KEYS" + { + "#format" "{1:s},{2:s}" + "ru" "Ключи успешно удалены игроком {1} ({2})!" + "en" "Keys were successfully deleted by player {1} ({2})!" + } + + "ERROR_REMOVE_KEYS" + { + "ru" "Ошибка удаления ключей" + "en" "Error removing keys" + } + + "LOG_ERROR_REMOVE_KEYS" + { + "#format" "{1:s},{2:s}" + "ru" "Ошибка удаления ключей игроком {1} ({2})!" + "en" "Key removal error by player {1} ({2})!" + } + + "SUCCESS_REMOVE_KEYS_TYPE" + { + "#format" "{1:s}" + "ru" "Ключи (тип: {1}) успешно удален!" + "en" "Keys (type: {1}) deleted successfully!" + } + + "LOG_SUCCESS_REMOVE_KEYS_TYPE" + { + "#format" "{1:s},{2:s},{3:s}" + "ru" "Ключи (тип: {1}) успешно удалены игроком {1} ({2})!" + "en" "Keys (type: {1}) were successfully deleted by player {1} ({2})!" + } + + "ERROR_REMOVE_KEYS_TYPE" + { + "#format" "{1:s}" + "ru" "Произошла ошибка при удалении ключей (тип: {1})" + "en" "An error occurred while deleting the keys (type: {1})" + } + + "LOG_ERROR_REMOVE_KEYS_TYPE" + { + "#format" "{1:s},{2:s},{3:s}" + "ru" "Произошла ошибка при удалении ключей (тип: {1}) игроком {1} ({2})!" + "en" "An error occurred while deleting keys (type: {1}) by player {1} ({2})!" + } + + "ERROR_LIST_NO_KEYS" + { + "ru" "Нет ни одного ключа!" + "en" "No Keys!" + "fi" "Ei ole yhtään avainta!" + } + + "ERROR_BLOCKED" + { + "ru" "Вы были заблокированы и не можете использовать ключи!" + "en" "You have been blocked and can not use keys!" + "fi" "Sinut on estetty etkä voi käyttää avaimia!" + } + + "LOG_BLOCKED" + { + "#format" "{1:s},{2:s}" + "ru" "Игрок {1} ({2}) был заблокирован за попытку подбора ключа" + "en" "Player {1} ({2}) was blocked for attempting to select the key" + } + + "ERROR_INCORRECT_KEY_LEFT" + { + "#format" "{1:i}" + "ru" "Вы ввели неверный ключ! Осталось попыток: {1}" + "en" "You have entered incorrect key! Attempts remaining: {1}" + "fi" "Olet antanut väärän avaimen! Yrityksiä jäljellä: {1}" + } + + "FOREVER" + { + "ru" "Навсегда" + "en" "Forever" + } + "EXPIRES" + { + "ru" "Истекает" + "en" "Expires" + } + "USAGE_LEFT" + { + "ru" "Использований осталось" + "en" "Usage left" + } + "TYPE" + { + "ru" "Тип" + "en" "Type" + } + "YEARS" + { + "en" "y." + "ru" "г." + } + "MONTHS" + { + "en" "mo." + "ru" "мес." + } + "DAYS" + { + "en" "d." + "ru" "д." + } + "HOURS" + { + "en" "h." + "ru" "ч." + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/keys_shop_module.phrases.txt b/addons/sourcemod/translations/keys_shop_module.phrases.txt new file mode 100644 index 0000000..75a7970 --- /dev/null +++ b/addons/sourcemod/translations/keys_shop_module.phrases.txt @@ -0,0 +1,71 @@ +"Phrases" +{ + "ERROR_INVALID_CREDITS" + { + "ru" "ОШИБКА: Неверное количество кредитов!" + "en" "ERROR: Wrong number of credits!" + } + + "ERROR_INVALID_CATEGORY" + { + "ru" "ОШИБКА: Неверная категория!" + "en" "ERROR: Invalid category!" + } + + "ERROR_INVALID_ITEM" + { + "ru" "ОШИБКА: Неверный предмет!" + "en" "ERROR: Wrong subject!" + } + + "CREDITS" + { + "ru" "Кредиты" + "en" "Credits" + } + + "CATEGORY" + { + "ru" "Категория" + "en" "Category" + } + + "ITEM" + { + "ru" "Предмет" + "en" "Subject" + } + + "ALL" + { + "ru" "Все" + "en" "All" + } + + "CHAT_PREFIX" + { + "ru" "[KEYS][Shop] " + "en" "[KEYS][Shop] " + } + + "YOU_RECEIVED_CREDITS" + { + "#format" "{1:d}" + "ru" "Вы получили {1} кредитов!" + "en" "You received {1} credits!" + } + + "YOU_RECEIVED_ITEM_FROM_CATEGORY" + { + "#format" "{1:s},{2:s}" + "ru" "Вы получили {1} из категории {2}!" + "en" "You received {1} from the category {2}!" + } + + "YOU_RECEIVED_ALL_ITEMS_FROM_CATEGORY" + { + "#format" "{1:s}" + "ru" "Вы получили Все предметы из категории {1}!" + "en" "You received All items from the category {1}!" + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/keys_store_module.phrases.txt b/addons/sourcemod/translations/keys_store_module.phrases.txt new file mode 100644 index 0000000..b687ae0 --- /dev/null +++ b/addons/sourcemod/translations/keys_store_module.phrases.txt @@ -0,0 +1,21 @@ +"Phrases" +{ + "ERROR_INVALID_CREDITS" + { + "ru" "ОШИБКА: Неверное количество кредитов!" + "en" "ERROR: Wrong number of credits!" + } + + "CREDITS" + { + "ru" "Кредиты" + "en" "Credits" + } + + "YOU_RECEIVED_CREDITS" + { + "#format" "{1:d}" + "ru" "[KEYS][Store] Вы получили {1} кредитов!" + "en" "[KEYS][Store] You received {1} credits!" + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/keys_vip_module.phrases.txt b/addons/sourcemod/translations/keys_vip_module.phrases.txt new file mode 100644 index 0000000..5bd0bcb --- /dev/null +++ b/addons/sourcemod/translations/keys_vip_module.phrases.txt @@ -0,0 +1,84 @@ +"Phrases" +{ + "ERROR_INVALID_CREDITS" + { + "ru" "ОШИБКА: Неверное количество кредитов!" + "en" "ERROR: Wrong number of credits!" + } + + "ERROR_INVALID_GROUP" + { + "ru" "ОШИБКА: Неверная VIP-группа!" + "en" "ERROR: Invalid VIP-group!" + } + + "ERROR_INVALID_TIME" + { + "ru" "ОШИБКА: Неверное время!" + "en" "ERROR: Invalid time!" + } + + "ERROR_VIP_ALREADY" + { + "ru" "Вы уже являетесь VIP-игроком!" + "en" "You are VIP already!" + "fi" "Olet jo VIP-Pelaaja!" + } + + "ERROR_CAN_NOT_USE" + { + "ru" "Вы не можете использовать этот ключ!" + "en" "You can not use this key!" + } + + "ERROR_ALREADY_VIP_GROUP" + { + "ru" "У вас уже установлена эта VIP-группа!" + "en" "You already have this VIP-group!" + } + + "CHAT_PREFIX" + { + "ru" "[KEYS][VIP] " + "en" "[KEYS][VIP] " + } + + "USE_KEY_GOT" + { + "#format" "{1:s},{2:s}" + "ru" "Вы получили VIP-статус (Группа: {1}, Срок: {2})" + "en" "You have received VIP-status (Group: {1}, Term: {2})" + } + + "USE_KEY_EXT" + { + "#format" "{1:s}" + "ru" "Ваш VIP-статус продлен на {1}!" + "en" "Your VIP-status has been extended to {1}!" + } + + "USE_KEY_GRP_CNG" + { + "#format" "{1:s}" + "ru" "Ваша VIP-группа изменена на {{1}!" + "en" "Your VIP-group has been changed to {1}!" + } + + "VIP_GROUP" + { + "ru" "VIP-группа" + "en" "VIP-group" + } + + "TERM" + { + "ru" "Срок" + "en" "Term" + } + + "FOREVER" + { + "ru" "Навсегда" + "en" "Forever" + } +} \ No newline at end of file