From 90567affc042f8c93606ab19023c6e878b162aff Mon Sep 17 00:00:00 2001 From: R1KO Date: Thu, 3 Aug 2017 23:10:03 +0300 Subject: [PATCH 1/8] Fix "Native "VIP_RemoveClientVIP" reported: Client 6 is not VIP" --- addons/sourcemod/scripting/Keys_VIP.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/Keys_VIP.sp b/addons/sourcemod/scripting/Keys_VIP.sp index 5af9077..cdfa29e 100644 --- a/addons/sourcemod/scripting/Keys_VIP.sp +++ b/addons/sourcemod/scripting/Keys_VIP.sp @@ -213,7 +213,7 @@ public bool:OnKeyUse(iClient, const String:sKeyType[], Handle:hParamsArr, String return false; } - if(iClientID == -1) + if(bVip && iClientID == -1) { VIP_RemoveClientVIP(iClient, false, false); } From 36b57761943acc4413de0bbc4b4179dd0755ba3b Mon Sep 17 00:00:00 2001 From: R1KO Date: Sun, 24 Sep 2017 11:11:37 +0300 Subject: [PATCH 2/8] Init v2.0 --- addons/sourcemod/plugins/Keys_Core.smx | Bin 0 -> 23614 bytes addons/sourcemod/scripting/Keys_Core.sp | 7 +- .../sourcemod/scripting/include/keys_core.inc | 56 +- .../sourcemod/scripting/include/lvl_ranks.inc | 256 +++++ addons/sourcemod/scripting/include/rank.inc | 2 + addons/sourcemod/scripting/keys/BLOCK.sp | 90 ++ addons/sourcemod/scripting/keys/CMD.sp | 491 ++++++++ addons/sourcemod/scripting/keys/EVENTS.sp | 36 + addons/sourcemod/scripting/keys/KEYS.sp | 1006 +++++++++++++++++ addons/sourcemod/scripting/keys/STATS.sp | 322 ++++++ addons/sourcemod/scripting/keys/UTIL.sp | 70 ++ addons/sourcemod/scripting/keys/api.sp | 341 +++++- addons/sourcemod/scripting/keys/cmds.sp | 116 +- addons/sourcemod/scripting/keys/utils.sp | 122 +- addons/sourcemod/scripting/keys/vars.sp | 80 +- keys_core_logo_.png | Bin 0 -> 63562 bytes keys_module_logo.png | Bin 0 -> 61679 bytes 17 files changed, 2874 insertions(+), 121 deletions(-) create mode 100644 addons/sourcemod/plugins/Keys_Core.smx create mode 100644 addons/sourcemod/scripting/include/lvl_ranks.inc create mode 100644 addons/sourcemod/scripting/include/rank.inc create mode 100644 addons/sourcemod/scripting/keys/BLOCK.sp create mode 100644 addons/sourcemod/scripting/keys/CMD.sp create mode 100644 addons/sourcemod/scripting/keys/EVENTS.sp create mode 100644 addons/sourcemod/scripting/keys/KEYS.sp create mode 100644 addons/sourcemod/scripting/keys/STATS.sp create mode 100644 addons/sourcemod/scripting/keys/UTIL.sp create mode 100644 keys_core_logo_.png create mode 100644 keys_module_logo.png diff --git a/addons/sourcemod/plugins/Keys_Core.smx b/addons/sourcemod/plugins/Keys_Core.smx new file mode 100644 index 0000000000000000000000000000000000000000..d15f3f0bf86e1423cb0729e3c817ca5e1ba7a191 GIT binary patch literal 23614 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

e}{V5bFOu}$})4(794yMTacI8XPK(ws4 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 literal 0 HcmV?d00001 diff --git a/addons/sourcemod/scripting/Keys_Core.sp b/addons/sourcemod/scripting/Keys_Core.sp index 16c9696..c31fa45 100644 --- a/addons/sourcemod/scripting/Keys_Core.sp +++ b/addons/sourcemod/scripting/Keys_Core.sp @@ -32,10 +32,7 @@ public OnPluginStart() HookConVarChange(hCvar, OnKeyLengthChange); g_CVAR_iKeyLength = GetConVarInt(hCvar); - hCvar = CreateConVar("key_template", "", "Шаблон для генерируемого пароля:\n\ - A - Буква в любом регистре\n\ - B - Цифра 0-9\n\ - X - Цифра 0-9 либо буква в любом регистре\n\ + hCvar = CreateConVar("key_template", "", "Шаблон для генерируемого ключа (Подробнее http://hlmod.ru/resources/keys-core.438/) Пример: XXXX-XXXX-XXXX-XXXX", _, false, 0.0, true, 64.0); HookConVarChange(hCvar, OnKeyTemplateChange); @@ -130,7 +127,7 @@ CreateTables() `type` VARCHAR(64) NOT NULL, \ `expires` INTEGER UNSIGNED NOT NULL default 0, \ `uses` INTEGER UNSIGNED NOT NULL default 1, \ - `sid` INTEGER NOT NULL, \ + `sid` INTEGER NOT NULL default 0, \ `param1` VARCHAR(64) NULL default NULL, \ `param2` VARCHAR(64) NULL default NULL, \ `param3` VARCHAR(64) NULL default NULL, \ diff --git a/addons/sourcemod/scripting/include/keys_core.inc b/addons/sourcemod/scripting/include/keys_core.inc index b8dafbe..da5024f 100644 --- a/addons/sourcemod/scripting/include/keys_core.inc +++ b/addons/sourcemod/scripting/include/keys_core.inc @@ -14,6 +14,21 @@ functag public bool:KeyUseCallback(iClient, const String:sKeyType[], Handle:hPar // Прототип вызова при выводе параметров ключа functag public KeyPrintCallback(iClient, const String:sKeyType[], Handle:hParamsArr, String:sBuffer[], iBufLen); +// Прототип вызова при проверке существования ключа +functag public KeyCheckValidKeyCallback(const String:sKey[], bool:bKeyExists); + +// Прототип вызова при получении данных ключа +functag public KeyGetDataCallback(const String:sKey[], bool:bKeyExists, const String:sKeyType[], iUses, iExpires, Handle:hParamsArr); + +// Прототип вызова при добавлении ключа +functag public KeyAddCallback(iClient, const String:sKey[], bool:bSuccess, const String:sError[]); + +// Прототип вызова при использоании ключа игроком +functag public KeyUseCallback(iClient, const String:sKey[], bool:bSuccess, String:sError[]); + +// Прототип вызова при удалении ключа +functag public KeyRemoveCallback(const String:sKey[], bool:bSuccess); + // Вызывается когда ядро было загружено forward Keys_OnCoreStarted(); @@ -23,6 +38,9 @@ native bool:Keys_IsCoreStarted(); // Получает Handle базы данных native Handle:Keys_GetCoreDatabase(); +// Получает тип базы данных (false - SQLite, true - MySQL) +native bool:Keys_GetDatabaseType(); + // Регистрирует тип ключей native bool:Keys_RegKey(const String:sKeyType[], KeyParamsValidateCallback:OnKeyParamsValidate, @@ -32,6 +50,30 @@ native bool:Keys_RegKey(const String:sKeyType[], // Разрегистрирует тип ключей native Keys_UnregKey(const String:sKeyType[]); +// Проверяет существование типа ключей +native bool:Keys_IsValidKeyType(const String:sKeyType[]); + +// Получает adt_array со всеми типами ключей (нужно закрывать Handle) +native Handle:Keys_FillArrayByKeyTypes(); + +// Проверяет существование ключа +native Keys_IsValidKey(const String:sKey[], KeyCheckValidKeyCallback:IsValidKeyCallback); + +// Получает данные ключа +native Keys_GetKeyData(const String:sKey[], KeyGetDataCallback:GetKeyDataCallback); + +// Генерирует ключ +native Keys_GenerateKey(String:sBuffer[], iBufLen, const String:sTemplate[] = ""); + +// Добавляет ключ +native Keys_AddKey(iClient = 0, const String:sKey[] = "", const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyAddCallback:AddKeyCallback); + +// Удаляет ключ +native Keys_RemoveKey(const String:sKey[], KeyRemoveCallback:RemoveKeyCallback); + +// Использует ключ игроком +native Keys_UseKey(iClient, const String:sKey[], bool:bNotify, KeyUseCallback:UseKeyCallback); + // Для использования не забыть: // LoadTranslations("keys_core.phrases"); stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = LANG_SERVER) @@ -107,15 +149,21 @@ public SharedPlugin:__pl_keys_core= }; #if !defined REQUIRE_PLUGIN -<<<<<<< HEAD -public void __pl_keys_core_SetNTVOptional() -======= + public __pl_keys_core_SetNTVOptional() ->>>>>>> 4ee67050d3fb25df6e30b26e03301ee38d46b425 { MarkNativeAsOptional("Keys_IsCoreStarted"); MarkNativeAsOptional("Keys_GetCoreDatabase"); + MarkNativeAsOptional("Keys_GetDatabaseType"); MarkNativeAsOptional("Keys_RegKey"); MarkNativeAsOptional("Keys_UnregKey"); + MarkNativeAsOptional("Keys_IsValidKeyType"); + MarkNativeAsOptional("Keys_FillArrayByKeyTypes"); + MarkNativeAsOptional("Keys_IsValidKey"); + MarkNativeAsOptional("Keys_GetKeyData"); + MarkNativeAsOptional("Keys_GenerateKey"); + MarkNativeAsOptional("Keys_AddKey"); + MarkNativeAsOptional("Keys_RemoveKey"); + MarkNativeAsOptional("Keys_UseKey"); } #endif diff --git a/addons/sourcemod/scripting/include/lvl_ranks.inc b/addons/sourcemod/scripting/include/lvl_ranks.inc new file mode 100644 index 0000000..fbdfe82 --- /dev/null +++ b/addons/sourcemod/scripting/include/lvl_ranks.inc @@ -0,0 +1,256 @@ +#if defined _levelsranks_included_ + #endinput +#endif +#define _levelsranks_included_ + +#define PLUGIN_VERSION "v2.3.0" + +#define ST_VALUE 0 +#define ST_RANK 1 +#define ST_KILLS 2 +#define ST_DEATHS 3 +#define ST_SHOOTS 4 +#define ST_HITS 5 +#define ST_HEADSHOTS 6 +#define ST_ASSISTS 7 + +int g_iColorsOther[] = {0xFFFFFF, 0xFF0000, 0x00AD00, 0x00FF00, 0x99FF99, 0xFF4040, 0xCCCCCC, 0xFFBD6B, 0xFA8B00, 0x99CCFF, 0x3D46FF, 0xFA00FA}; +char g_sColors[][] = {"{WHITE}", "{RED}", "{GREEN}", "{LIME}", "{LIGHTGREEN}", "{LIGHTRED}", "{GRAY}", "{LIGHTOLIVE}", "{OLIVE}", "{LIGHTBLUE}", "{BLUE}", "{PURPLE}"}; +char g_sColorsCSGO[][] = {"\x01", "\x02", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x10", "\x0B", "\x0C", "\x0E"}; + +/** + * Checks if enough players are in this round to activate statistics + * + * @return bool - if true - yes, otherwise - no + */ +native bool LR_CheckCountPlayers(); + +/** + * Returns ID of statistics type + * + * @return int - ID of stastics type + */ +native int LR_GetTypeStatistics(); + +/** + * Gets the client's place in the TOP + * + * @param iClient - Client index + * @return int - Client position in TOP + */ +native int LR_GetClientPos(int iClient); + +/** + * Получает подробную статистику об игроке + * + * @param iClient - Client index + * @param iStats ID получаемых данных (Example: iStats = ST_VALUE). + * @return int Значение выбранных данных + */ +native int LR_GetClientInfo(int iClient, int iStats); + +/** + * Изменяет кол-во расчетных единиц у игрока + * + * @param iClient Индекс клиента. + * @param iAmount Кол-во расчетных единиц (очки опыта/счетчик времени) + * @return int Новое значение кол-ва расчетных единиц. + */ +native int LR_ChangeClientValue(int iClient, int iAmount); + +/** + * Принудительно выставляет очки опыта клиенту (only for lr_type_statistics 2) + * + * @param iClient Индекс клиента. + * @param iAmount Кол-во очков опыта + * @return bool Если true - выдача прошла успешно, иначе false. + */ +native bool LR_SetClientValue(int iClient, int iAmount); + +/** + * Вызывает меню инвентаря (используется модулями для возможности откатиться назад по Менюшке) + * + * @param iClient Индекс клиента. + * @noreturn + */ +native void LR_MenuInventory(int iClient); + +/** + * Проверяет на валидность VIP-группу + * + * @param sGroup Проверяемое название VIP-группы + * @return bool Если true - VIP-группа валидна, иначе false. + */ +native bool LR_IsValidGroupVIP(const char[] sGroup); + +/** + * Проверяет VIP-статус клиента + * + * @param iClient Индекс клиента. + * @return bool Если true - клиент имеет статус VIP, иначе false. + */ +native bool LR_IsClientVIP(int iClient); + +/** + * Выдает VIP-статус клиенту + * + * @param iClient Индекс клиента. + * @param iTime Время в Unix TimeStamp, до которого у клиента будет VIP-статус. + * @param sGroup Наименование присуждаемой группы + * @return bool Если true - VIP выдан, иначе false. + */ +native bool LR_SetClientVIP(int iClient, int iTime, const char[] sGroup); + +/** + * Возвращает информацию о VIP-статусе клиента + * + * @param iClient Индекс клиента. + * @return DataPack Если Валидный (1 - время окончания VIP-статуса, 2 - наименование VIP-группы) + */ +native DataPack LR_GetClientInfoVIP(int iClient); + +/** + * Изменяет параметры VIP-статуса клиента + * + * @param iClient Индекс клиента. + * @param sGroup Наименование группы , на которую вы хотите сменить (если пустое название - группа не меняется) + * @param iTime Время в UnixTime до которого будет VIP-статус (-1 - не изменять, 0 - навсегда) + * @return bool Если true - VIP изменен, иначе false. + */ +native bool LR_ChangeClientVIP(int iClient, const char[] sGroup = "", int iTime = -1); + +/** + * Удаляет VIP-статус клиента + * + * @param iClient Индекс клиента. + * @return bool Если true - VIP удален, иначе false. + */ +native bool LR_DeleteClientVIP(int iClient); + +/** + * Called when a list opens Inventory + * + * @noreturn + */ +forward void LR_OnMenuCreated(int iClient, int iRank, Menu& hMenu); + +/** + * Called when a list opens Inventory + * + * @noreturn + */ +forward void LR_OnMenuItemSelected(int iClient, int iRank, const char[] sInfo); + +/** + * Вызывается при изменении уровня игроком + * + * @param iClient ID игрока + * @param iNewLevel Новый уровень + * @param bUp Если true - то уровень поднялся, false - упал. + */ +forward void LR_OnLevelChanged(int iClient, int iNewLevel, bool bUp); + +stock bool IsValidClient(int iClient) +{ + return (1 <= iClient <= MaxClients && IsClientInGame(iClient)) ? true : false; +} + +stock void LR_PrintToChat(int iClient, char[] szMessage, any ...) +{ + if(IsValidClient(iClient) && !IsFakeClient(iClient)) + { + SetGlobalTransTarget(iClient); + char szBuffer[PLATFORM_MAX_PATH], szNewMessage[PLATFORM_MAX_PATH]; + + switch(GetEngineVersion()) + { + case Engine_CSGO: + { + Format(szBuffer, sizeof(szBuffer), " \x01%s", szMessage); + VFormat(szNewMessage, sizeof(szNewMessage), szBuffer, 3); + + for(int i = 0; i < 12; i++) + { + ReplaceString(szNewMessage, sizeof(szNewMessage), g_sColors[i], g_sColorsCSGO[i]); + } + ReplaceString(szNewMessage, sizeof(szNewMessage), "{TEAM}", "\x03"); + } + + case Engine_CSS, Engine_TF2: + { + char sBuff[64]; + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szNewMessage, sizeof(szNewMessage), szBuffer, 3); + + switch(GetClientTeam(iClient)) + { + case 1: Format(sBuff, sizeof(sBuff), "\x07%06X", g_iColorsOther[6]); + case 2: Format(sBuff, sizeof(sBuff), "\x07%06X", g_iColorsOther[5]); + case 3: Format(sBuff, sizeof(sBuff), "\x07%06X", g_iColorsOther[9]); + } + ReplaceString(szNewMessage, sizeof(szNewMessage), "{TEAM}", sBuff); + + for(int i = 0; i < 12; i++) + { + Format(sBuff, sizeof(sBuff), "\x07%06X", g_iColorsOther[i]); + ReplaceString(szNewMessage, sizeof(szNewMessage), g_sColors[i], sBuff); + } + } + } + + Handle hBf = StartMessageOne("SayText2", iClient, USERMSG_RELIABLE | USERMSG_BLOCKHOOKS); + if(hBf != null) + { + if(GetUserMessageType() == UM_Protobuf) + { + Protobuf hProtoBuffer = UserMessageToProtobuf(hBf); + hProtoBuffer.SetInt("ent_idx", iClient); + hProtoBuffer.SetBool("chat", true); + hProtoBuffer.SetString("msg_name", szNewMessage); + hProtoBuffer.AddString("params", ""); + hProtoBuffer.AddString("params", ""); + hProtoBuffer.AddString("params", ""); + hProtoBuffer.AddString("params", ""); + } + else + { + BfWrite hBfBuffer = UserMessageToBfWrite(hBf); + hBfBuffer.WriteByte(iClient); + hBfBuffer.WriteByte(true); + hBfBuffer.WriteString(szNewMessage); + } + } + EndMessage(); + } +} + +public SharedPlugin __pl_levelsranks = +{ + name = "levelsranks", + file = "levelsranks.smx", + + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_levelsranks_SetNTVOptional() +{ + MarkNativeAsOptional("LR_CheckCountPlayers"); + MarkNativeAsOptional("LR_GetTypeStatistics"); + MarkNativeAsOptional("LR_GetClientPos"); + MarkNativeAsOptional("LR_GetClientInfo"); + MarkNativeAsOptional("LR_ChangeClientValue"); + MarkNativeAsOptional("LR_SetClientValue"); + MarkNativeAsOptional("LR_MenuInventory"); + MarkNativeAsOptional("LR_IsValidGroupVIP"); + MarkNativeAsOptional("LR_IsClientVIP"); + MarkNativeAsOptional("LR_SetClientVIP"); + MarkNativeAsOptional("LR_GetClientInfoVIP"); + MarkNativeAsOptional("LR_ChangeClientVIP"); + MarkNativeAsOptional("LR_DeleteClientVIP"); +} +#endif \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/rank.inc b/addons/sourcemod/scripting/include/rank.inc new file mode 100644 index 0000000..149a7c6 --- /dev/null +++ b/addons/sourcemod/scripting/include/rank.inc @@ -0,0 +1,2 @@ + +native Rank_GiveExp(client, amount); //Gives exp to client \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/BLOCK.sp b/addons/sourcemod/scripting/keys/BLOCK.sp new file mode 100644 index 0000000..600440f --- /dev/null +++ b/addons/sourcemod/scripting/keys/BLOCK.sp @@ -0,0 +1,90 @@ + +void Block_ClientDisconnect(int iClient) +{ + g_iAttempts[iClient] = 0; + g_bIsBlocked[iClient] = false; +} + +void Block_ClientConnect(int iClient) +{ + char szQuery[PMP], szAuth[32], szSID[64]; + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); + + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " AND `b_sid` = %d", g_CVAR_iServerID); + } + + FormatEx(SZF(szQuery), "SELECT `b_end` FROM `keys_block_players` WHERE `b_auth` = '%s'%s;", szAuth, szSID); + + g_hDatabase.Query(SQL_Callback_SearchBlockPlayer, szQuery, UID(iClient)); +} + +public void SQL_Callback_SearchBlockPlayer(Database hDB, DBResultSet hResult, const char[] szDbError, any UserID) +{ + if (hResult == null || szDbError[0]) + { + LogError("SQL_Callback_SearchBlockPlayer: %s", szDbError); + return; + } + + int iClient = CID(UserID); + if (iClient) + { + if(hResult.FetchRow()) + { + g_iAttempts[iClient] = hResult.FetchInt(0); + if(g_iAttempts[iClient] && g_iAttempts[iClient] < GetTime()) + { + Block_SetClientStatus(iClient, false); + return; + } + + g_bIsBlocked[iClient] = true; + } + } +} + +void Block_SetClientStatus(int iClient, bool bStatus) +{ + char szQuery[PMP], szAuth[32], szBuffer[64]; + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); + g_bIsBlocked[iClient] = bStatus; + if(bStatus) + { + g_iAttempts[iClient] = GetTime()+(g_CVAR_iBlockTime*60); + GetClientName(iClient, SZF(szBuffer)); + + LogToFile(g_sLogFile, "%T", "LOG_BLOCKED", LANG_SERVER, szBuffer, szAuth); + + if(!g_CVAR_iServerID) + { + FormatEx(SZF(szQuery), "INSERT INTO `keys_block_players` (`b_auth`, `b_end`) VALUES ('%s', %d);", szAuth, g_iAttempts[iClient]); + } + else + { + FormatEx(SZF(szQuery), "INSERT INTO `keys_block_players` (`b_auth`, `b_end`, `b_sid`) VALUES ('%s', %d, %d);", szAuth, g_iAttempts[iClient], g_CVAR_iServerID); + } + } + else + { + g_iAttempts[iClient] = 0; + + if(!g_CVAR_iServerID) + { + szBuffer[0] = 0; + } + else + { + FormatEx(SZF(szBuffer), " AND `b_sid` = %d", g_CVAR_iServerID); + } + + FormatEx(SZF(szQuery), "DELETE FROM `keys_block_players` WHERE `b_auth` = '%s'%s;", szAuth, szBuffer); + } + + g_hDatabase.Query(SQL_Callback_ErrorCheck, szQuery); +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/CMD.sp b/addons/sourcemod/scripting/keys/CMD.sp new file mode 100644 index 0000000..4d02d11 --- /dev/null +++ b/addons/sourcemod/scripting/keys/CMD.sp @@ -0,0 +1,491 @@ + +void CMD_Reg() +{ + // 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(int iClient, int iArgs) +{ + if (iClient) + { + ReplySource CmdReplySource = GetCmdReplySource(); + + if(g_CVAR_iAttempts && g_bIsBlocked[iClient]) + { + if(g_iAttempts[iClient] > GetTime()) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_BLOCKED"); + return Plugin_Handled; + + } + else + { + Block_SetClientStatus(iClient, false); + } + } + + if(iArgs != 1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "USAGE_ERROR_USE_KEY"); + return Plugin_Handled; + } + + char szKey[KEYS_MAX_LENGTH], szQuery[PMP*2]; + GetCmdArg(1, SZF(szKey)); + + if(!Keys_Check(szKey, SZF(szQuery))) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", szQuery); + + if(g_CVAR_iAttempts) + { + if(g_iAttempts[iClient]++ >= g_CVAR_iAttempts) + { + Block_SetClientStatus(iClient, true); + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_BLOCKED"); + return Plugin_Handled; + } + + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_KEY_LEFT", g_CVAR_iAttempts-g_iAttempts[iClient]); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_KEY"); + } + + return Plugin_Handled; + } + + Keys_Use(szKey, iClient, CmdReplySource, true, false); + } + + return Plugin_Handled; +} + +public Action AddKey_CMD(int iClient, int iArgs) +{ + ReplySource CmdReplySource = GetCmdReplySource(); + + if(iArgs < 5) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_NUM_ARGS"); + return Plugin_Handled; + } + + char szKey[KEYS_MAX_LENGTH]; + + GetCmdArg(0, SZF(szKey)); + + bool bGen = (szKey[3] == 's'); + + int iCount; + if(bGen) + { + GetCmdArg(1, SZF(szKey)); + iCount = S2I(szKey); + if(iCount < 1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_AMOUNT"); + return Plugin_Handled; + } + + szKey[0] = 0; + } + else + { + GetCmdArg(1, SZF(szKey)); + } + + char szKeyType[KEYS_MAX_LENGTH], szParam[KEYS_MAX_LENGTH], szError[PMP]; + GetCmdArg(4, SZF(szKeyType)); + + GetCmdArg(2, SZF(szParam)); + int iLifeTime = S2I(szParam); + + GetCmdArg(3, SZF(szParam)); + int iUses = S2I(szParam); + + ArrayList hParamsArr = new ArrayList(ByteCountToCells(KEYS_MAX_LENGTH)); + + for(int i = 5; i <= iArgs; ++i) + { + GetCmdArg(i, SZF(szParam)); + hParamsArr.PushString(szParam); + } + + szError[0] = 0; + + if(!bGen && !Keys_Validate(szKey, szKeyType, iUses, iLifeTime, hParamsArr, SZF(szError), iClient)) + { + delete hParamsArr; + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%s", "ERROR", szError); + return Plugin_Handled; + } + + int iExpires = iLifeTime ? (iLifeTime + GetTime()):iLifeTime; + if(bGen) + { + szKey = NULL_STRING; + LogMessage("LOOP: %d", iCount); + while(iCount > 0) + { + --iCount; + LogMessage("LOOP ITER: %d", iCount); + + Keys_Add(szKey, szKeyType, iUses, iLifeTime, iExpires, hParamsArr.Clone(), iClient, CmdReplySource); + } + } + else + { + Keys_Add(szKey, szKeyType, iUses, iLifeTime, iExpires, hParamsArr.Clone(), iClient, CmdReplySource); + } + + delete hParamsArr; + + return Plugin_Handled; +} + +public Action DelKey_CMD(int iClient, int iArgs) +{ + ReplySource CmdReplySource = GetCmdReplySource(); + + if(iArgs != 1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_NUM_ARGS"); + return Plugin_Handled; + } + + char szKey[KEYS_MAX_LENGTH], iLength; + GetCmdArg(1, SZF(szKey)); + + iLength = strlen(szKey); + if(iLength > KEYS_MAX_LENGTH || iLength < 8) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_KEY"); + return Plugin_Handled; + } + + Keys_Delete(szKey, true, iClient, CmdReplySource); + + return Plugin_Handled; +} + +public Action ClearKeys_CMD(int iClient, int iArgs) +{ + ReplySource CmdReplySource = GetCmdReplySource(); + + char szKeyType[64]; + if(iArgs == 1) + { + GetCmdArg(1, SZF(szKeyType)); + if(FindStringInArray(g_hKeysArray, szKeyType) == -1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_TYPE"); + return Plugin_Handled; + } + } + else + { + szKeyType[0] = 0; + } + + char szQuery[PMP], szSID[64]; + DataPack hDP = new DataPack(); + hDP.WriteCell(GET_UID(iClient)); + hDP.WriteCell(CmdReplySource); + if(szKeyType[0]) + { + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " AND `k_sid` = %d", g_CVAR_iServerID); + } + + FormatEx(SZF(szQuery), "DELETE FROM `keys_tokens` WHERE `k_type` = '%s'%s;", szKeyType, szSID); + + hDP.WriteCell(true); + hDP.WriteString(szKeyType); + } + else + { + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " WHERE `k_sid` = %d;", g_CVAR_iServerID); + } + + FormatEx(SZF(szQuery), "DELETE FROM `keys_tokens`%s;", szSID); + } + + g_hDatabase.Query(SQL_Callback_RemoveKeys, szQuery, hDP); + + return Plugin_Handled; +} + +public void SQL_Callback_RemoveKeys(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) + { + delete hDP; + LogError("SQL_Callback_RemoveKeys: %s", szDbError); + return; + } + + hDP.Reset(); + + int iClient = GET_CID(hDP.ReadCell()); + ReplySource CmdReplySource = view_as(hDP.ReadCell()); + + char szKeyType[64], szName[MAX_NAME_LENGTH], szAuth[32]; + + CmdReplySource = view_as(hDP.ReadCell()); + + if(iClient == -1) + { + iClient = 0; + } + + if(!iClient) + { + strcopy(SZF(szName), "CONSOLE"); + strcopy(SZF(szAuth), "STEAM_ID_SERVER"); + } + else + { + GetClientName(iClient, SZF(szName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); + } + + if(hDP.ReadCell()) + { + hDP.ReadString(SZF(szKeyType)); + } + else + { + szKeyType[0] = 0; + } + + delete hDP; + + if(hResult.AffectedRows) + { + if(szKeyType[0]) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_REMOVE_KEYS_TYPE", szKeyType); + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_REMOVE_KEYS_TYPE", LANG_SERVER, szKeyType, szName, szAuth); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_REMOVE_KEYS"); + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_REMOVE_KEYS", LANG_SERVER, szKeyType, szName, szAuth); + } + } + else + { + if(szKeyType[0]) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_REMOVE_KEYS_TYPE", szKeyType); + LogToFile(g_sLogFile, "%T", "LOG_ERROR_REMOVE_KEYS_TYPE", LANG_SERVER, szKeyType, szName, szAuth); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_REMOVE_KEYS"); + LogToFile(g_sLogFile, "%T", "LOG_ERROR_REMOVE_KEYS", LANG_SERVER, szKeyType, szName, szAuth); + } + } +} +/* +public Action KeysListDump_CMD(int iClient, int iArgs) +{ + char szQuery[PMP*2], szSID[64]; + ReplySource CmdReplySource = GetCmdReplySource(); + + int iOffset = 0; + if(iArgs) + { + GetCmdArg(2, szQuery, 16); + iOffset = StringToInt(szQuery); + if(iOffset < 0) + { + iOffset = 0; + } + } + + DataPack hDP = new DataPack(); + hDP.WriteCell(GET_UID(iClient)); + hDP.WriteCell(CmdReplySource); + GetCmdArg(0, szQuery, 32); + bool bToFile = szQuery[5] == 'd'; + hDP.WriteCell(bToFile); + + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " WHERE `k_sid` = %d;", g_CVAR_iServerID); + } + + if(!g_iServerID) + { + FormatEx(SZF(szQuery), "SELECT `k_name`, `k_type`, `k_expires`, `k_uses`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `keys_tokens` ORDER BY `k_type`, `param1`, `param2`, `param3`, `param4`, `param5`, `k_expires`, `k_uses`;"); + } + else + { + FormatEx(SZF(szQuery), "SELECT `k_name`, `k_type`, `k_expires`, `k_uses`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `keys_tokens` WHERE `k_sid` = %d ORDER BY `k_type`, `param1`, `param2`, `param3`, `param4`, `param5`, `k_expires`, `k_uses`;", g_iServerID); + } + + if(!bToFile) + { + szQuery[strlen(szQuery)-1] = 0; + Format(SZF(szQuery), "%s LIMIT %d, %d;", szQuery, iOffset, iClient ? 20:100); + } + + g_hDatabase.Query(SQL_Callback_SelectKeysList, szQuery, hDP); + + return Plugin_Handled; +} + +public void SQL_Callback_SelectKeysList(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + if (hResult == null || szDbError[0]) + { + delete hDP; + LogError("SQL_Callback_SelectKeysList: %s", szDbError); + return; + } + + hDP.Reset(); + decl iClient, ReplySource:CmdReplySource, bool:bToFile; + iClient = GET_CID(hDP.ReadCell()); + CmdReplySource = view_as(hDP.ReadCell()); + bToFile = view_as(hDP.ReadCell()); + delete hDP; + + if(!bToFile && iClient == -1) + { + return; + } + + if(SQL_GetRowCount(hResult) > 0) + { + char szKey[64], szKeyType[64], sExpires[64], iUses, iCount, iTime, iExpires, i; + decl Handle:hFile, Handle:hDataPack, Handle:hPlugin, Function:fPrintCallback, ArrayList hParamsArr, szParam[KEYS_MAX_LENGTH], sParams[PMP*2]; + + if(bToFile) + { + BuildPath(Path_SM, SZF(sParams), "data/keys_dump.txt"); + hFile = OpenFile(sParams, "w+"); + } + + iCount = 0; + iTime = GetTime(); + + while(hResult.FetchRow()) + { + hResult.FetchString(1, SZF(szKeyType)); + + if(g_hKeysTrie.GetValue(szKeyType, hDataPack)) + { + hResult.FetchString(0, SZF(szKey)); + + iExpires = hResult.FetchInt(2); + + if(iExpires) + { + if(iExpires < iTime) + { + Keys_Delete(szKey); + continue; + } + + Keys_GetTimeFromStamp(SZF(sExpires), iExpires-iTime, iClient); + } + else + { + FormatEx(SZF(sExpires), "%T", "FOREVER", iClient); + } + + iUses = hResult.FetchInt(3); + + if(!iUses) + { + Keys_Delete(szKey); + continue; + } + + hParamsArr = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); + + for(i = 4; i < 9; ++i) + { + if(SQL_IsFieldNull(hResult, i)) + { + break; + } + + hResult.FetchString(i, SZF(szParam)); + PushArrayString(hParamsArr, szParam); + } + + sParams[0] = 0; + + hDataPack.Position = DP_Plugin; + hPlugin = view_as(hDataPack.ReadCell()); + hDataPack.Position = DP_OnPrintCallback; + fPrintCallback = hDataPack.ReadFunction(); + Call_StartFunction(hPlugin, fPrintCallback); + Call_PushCell(iClient); + Call_PushString(szKeyType); + Call_PushCell(hParamsArr); + Call_PushStringEx(sParams, 256, SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushCell(256); + Call_Finish(); + + delete hParamsArr; + + if(bToFile) + { + WriteFileLine(hFile, "%d. %s\t\t%T: %12s\t\t%T: %4i\t\t%T: %s\t\t%s", ++iCount, szKey, "EXPIRES", iClient, sExpires, "USAGE_LEFT", iClient, iUses, "TYPE", iClient, szKeyType, sParams); + continue; + } + + UTIL_ReplyToCommand(iClient, CmdReplySource, "%d. %s\t\t%T: %12s\t\t%T: %4i\t\t%T: %s\t\t%s", ++iCount, szKey, "EXPIRES", iClient, sExpires, "USAGE_LEFT", iClient, iUses, "TYPE", iClient, szKeyType, sParams); + } + } + + if(bToFile) + { + delete hFile; + } + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_LIST_NO_KEYS"); + } +} +*/ \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/EVENTS.sp b/addons/sourcemod/scripting/keys/EVENTS.sp new file mode 100644 index 0000000..c9b2c9c --- /dev/null +++ b/addons/sourcemod/scripting/keys/EVENTS.sp @@ -0,0 +1,36 @@ + +public void OnMapStart() +{ +// Stats_OnMapStart(); +} + +public void OnConfigsExecuted() +{ + if(g_bIsStarted) + { + Keys_DeleteExpired(); + } +} + +public Action OnClientSayCommand(int iClient, const char[] sCommand, const char[] sArgs) +{ + if(StrContains(sArgs, "key") != -1) + { + return Plugin_Handled; + } + + return Plugin_Continue; +} + +public void OnClientDisconnect(int iClient) +{ + Block_ClientDisconnect(iClient); +} + +public void OnClientPostAdminCheck(int iClient) +{ + if(!IsFakeClient(iClient)) + { + Block_ClientConnect(iClient); + } +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/KEYS.sp b/addons/sourcemod/scripting/keys/KEYS.sp new file mode 100644 index 0000000..af4cb5a --- /dev/null +++ b/addons/sourcemod/scripting/keys/KEYS.sp @@ -0,0 +1,1006 @@ + +void Keys_DeleteExpired() +{ + char szQuery[PMP], szSID[64]; + + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " AND `k_sid` = %d", g_CVAR_iServerID); + } + + FormatEx(SZF(szQuery), "DELETE FROM `keys_tokens` WHERE `k_expires` > 0 AND `k_expires` < %d%s;", GetTime(), szSID); + + g_hDatabase.Query(SQL_Callback_ErrorCheck, szQuery); +} + +bool Keys_Check(const char[] szKey, char[] szError, int iErrLen) +{ + if(!szKey[0]) + { + strcopy(szError, iErrLen, "ERROR_KEY_EMPTY"); + return false; + } + + int iLength = strlen(szKey); + if(iLength < 8) + { + strcopy(szError, iErrLen, "ERROR_KEY_SHORT"); + return false; + } + + if(iLength > 64) + { + strcopy(szError, iErrLen, "ERROR_KEY_LONG"); + return false; + } + + int i = 0; + + while (i < iLength) + { + if((szKey[i] > 0x2F && szKey[i] < 0x3A) || + (szKey[i] > 0x40 && szKey[i] < 0x5B) || + (szKey[i] > 0x60 && szKey[i] < 0x7B) || + szKey[i] == 0x2D) + { + ++i; + continue; + } + + strcopy(szError, iErrLen, "ERROR_KEY_INVALID_CHARACTERS"); + return false; + } + + return true; +} + +void Keys_Generate(char[] szKey, int iMaxLen) +{ + szKey[0] = '\0'; + + int i = 0; + + if(g_CVAR_sKeyTemplate[0]) + { + int iLength = strlen(g_CVAR_sKeyTemplate); + while (i < iLength && i < iMaxLen) + { + szKey[i] = view_as(UTIL_GetCharTemplate(g_CVAR_sKeyTemplate[i])); + ++i; + } + } + else + { + while (i < g_CVAR_iKeyLength && i < iMaxLen) + { + szKey[i] = view_as(UTIL_GetCharTemplate(0x58)); + ++i; + } + } + + szKey[i] = '\0'; +} + +bool Keys_Validate(char[] szKey, const char[] szKeyType, int iUses, int iLifeTime, ArrayList hParamsArr, char[] szError, int iErrLen, int iClient = LANG_SERVER) +{ + DataPack hDataPack; + + if(!g_hKeysTrie.GetValue(szKeyType, hDataPack)) + { + FormatEx(szError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_TYPE", iClient); + return false; + } + + if(szKey[0]) + { + if(!Keys_Check(szKey, szError, iErrLen)) + { + return false; + } + } + + if(iLifeTime < 0) + { + FormatEx(szError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_LIFETIME", iClient); + return false; + } + + if(iUses < 1) + { + FormatEx(szError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_USES", iClient); + return false; + } + + hDataPack.Reset(); + Handle hPlugin = view_as(hDataPack.ReadCell()); + Function fCallback = hDataPack.ReadFunction(); + + strcopy(szError, iErrLen, "unknown"); + bool bResult = false; + Call_StartFunction(hPlugin, fCallback); + Call_PushCell(Validation); + Call_PushCell(iClient); + Call_PushString(szKeyType); + Call_PushCell(hParamsArr); + Call_PushStringEx(szError, iErrLen, SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushCell(iErrLen); + Call_Finish(bResult); + + if(!bResult) + { + FormatEx(szError, iErrLen, "%T%s", "ERROR", iClient, szError); + return false; + } + + return true; +} + +/* +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +********************************************************* ****************************************************** +********************************************************* ****************************************************** +********************************************************* С О З Д А Н И Е ****************************************************** +********************************************************* ****************************************************** +********************************************************* ****************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +*/ + +void Keys_Add(char[] szKeySource = NULL_STRING, + const char[] szKeyType, + int iUses, + int iLifeTime, + int iExpires = -1, + ArrayList hParamsArr, + int iClient, + ReplySource CmdReplySource, + Handle hPlugin = null, + Function fCallback = INVALID_FUNCTION, + any iData = 0) +{ + LogMessage("Keys_Add: '%s', '%s'", szKeySource, szKeyType); + + DataPack hDP = new DataPack(); + hDP.WriteCell(hParamsArr); + char szKey[KEYS_MAX_LENGTH]; + if(szKey[0]) + { + strcopy(SZF(szKey), szKeySource); + hDP.WriteString(szKey); + hDP.WriteCell(true); + } + else + { + Keys_Generate(szKey, KEYS_MAX_LENGTH); + LogMessage("Keys_Generate: '%s'", szKey); + + hDP.WriteString(szKey); + hDP.WriteCell(false); + } + + hDP.WriteCell(GET_UID(iClient)); + hDP.WriteCell(CmdReplySource); + hDP.WriteString(szKeyType); + hDP.WriteCell(iUses); + hDP.WriteCell(iLifeTime); + if(iExpires == -1) + { + iExpires = iLifeTime ? (iLifeTime + GetTime()):iLifeTime; + } + hDP.WriteCell(iExpires); + + if(hPlugin != null && fCallback != INVALID_FUNCTION) + { + hDP.WriteCell(true); + hDP.WriteCell(hPlugin); + hDP.WriteFunction(fCallback); + hDP.WriteCell(iData); + } + else + { + hDP.WriteCell(false); + } + + char szQuery[PMP], szSID[64]; + + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " AND `k_sid` = %d", g_CVAR_iServerID); + } + + FormatEx(SZF(szQuery), "SELECT `k_expires`, `k_uses` FROM `keys_tokens` WHERE `k_name` = '%s'%s;", szKey, szSID); + g_hDatabase.Query(SQL_Callback_SearchKey, szQuery, hDP); +} + +public void SQL_Callback_SearchKey(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + DataPack hDP = view_as(hCbDP); + hDP.Reset(); + ArrayList hParamsArr = view_as(hDP.ReadCell()); + + if (hResult == null || szDbError[0]) + { + LogError("SQL_Callback_SearchKey: %s", szDbError); + delete hParamsArr; + delete hDP; + return; + } + + char szQuery[1024], szKey[KEYS_MAX_LENGTH]; + int i; + hDP.ReadString(SZF(szKey)); + + if(hResult.FetchRow()) // Ключ уже есть в базе + { + i = hResult.FetchInt(0); + if(!i || i > GetTime()) + { + i = hResult.FetchInt(1); + if(i) + { + if(hDP.ReadCell()) // Отправить ошибку о сущевствующем ключе + { + i = GET_CID(hDP.ReadCell()); + if(i != -1) + { + UTIL_ReplyToCommand(i, view_as(hDP.ReadCell()), "%t%t", "ERROR", "ERROR_KEY_ALREADY_EXISTS", szKey); + } + else + { + hDP.ReadCell(); // CmdReplySource + } + + hDP.ReadString(szQuery, KEYS_MAX_LENGTH); // KeyType + // hDP.Position = hDP.Position + view_as(27); + hDP.ReadCell(); + hDP.ReadCell(); + hDP.ReadCell(); + + if(hDP.ReadCell()) // hDP.ReadCell() + { + FormatEx(szQuery, 256, "%T%T", "ERROR", LANG_SERVER, "ERROR_KEY_ALREADY_EXISTS", LANG_SERVER, szKey); + Handle hPlugin = view_as(hDP.ReadCell()); + Function fCallback = hDP.ReadFunction(); + any iData = hDP.ReadCell(); + API_Callback(Add, hPlugin, fCallback, i, szKey, false, szQuery, iData); + } + + delete hParamsArr; + delete hDP; + return; + } + else // Сгенерировать новый ключ + { + i = hDP.ReadCell(); + ReplySource CmdReplySource = view_as(hDP.ReadCell()); + //char szKeyType[KEYS_MAX_LENGTH]; + hDP.ReadString(szQuery, KEYS_MAX_LENGTH); // KeyType + + int iUses = hDP.ReadCell(); + int iLifeTime = hDP.ReadCell(); + int iExpires = hDP.ReadCell(); + + if(hDP.ReadCell()) // hDP.ReadCell() + { + Handle hPlugin = view_as(hDP.ReadCell()); + Function fCallback = hDP.ReadFunction(); + any iData = hDP.ReadCell(); + Keys_Add("", szQuery, iUses, iLifeTime, iExpires, hParamsArr, i, CmdReplySource, hPlugin, fCallback, iData); + } + else + { + Keys_Add(szKey, szQuery, iUses, iLifeTime, iExpires, hParamsArr, i, CmdReplySource); + } + + delete hDP; + } + + return; + } + } + + Keys_Delete(szKey); + } + + hDP.ReadCell(); // bDuplicateErr + hDP.ReadCell(); // Client + hDP.ReadCell(); // CmdReplySource + + char szKeyType[KEYS_MAX_LENGTH]; + + hDP.ReadString(SZF(szKeyType)); + + int iUses = hDP.ReadCell(); + hDP.ReadCell(); // iLifeTime + int iExpires = hDP.ReadCell(); + + if(!g_CVAR_iServerID) + { + FormatEx(SZF(szQuery), "INSERT INTO `keys_tokens` (`k_name`, `k_type`, `k_expires`, `k_uses`) VALUES ('%s', '%s', %d, %d);", szKey, szKeyType, iExpires, iUses); + } + else + { + FormatEx(SZF(szQuery), "INSERT INTO `keys_tokens` (`k_name`, `k_type`, `k_expires`, `k_uses`, `k_sid`) VALUES ('%s', '%s', %d, %d, %d);", szKey, szKeyType, iExpires, iUses, g_CVAR_iServerID); + } + LogMessage(szQuery); + g_hDatabase.Query(SQL_Callback_AddKey, szQuery, hDP); +} + +public void SQL_Callback_AddKey(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + DataPack hDP = view_as(hCbDP); + hDP.Reset(); + ArrayList hParamsArr = view_as(hDP.ReadCell()); + + if (hResult == null || szDbError[0]) + { + LogError("SQL_Callback_AddKey: %s", szDbError); + delete hParamsArr; + delete hDP; + return; + } + + int iKeyID = hResult.InsertId; + + char szQuery[PMP*2], sParams[KEYS_MAX_LENGTH]; + Transaction hTxn = new Transaction(); + for(int i = 0; i < hParamsArr.Length; ++i) + { + hParamsArr.GetString(i, SZF(sParams)); + FormatEx(SZF(szQuery), "INSERT INTO `keys_params` (`p_kid`, `p_num`, `p_value`) VALUES (%d, %d, '%s');", iKeyID, i+1, sParams); + hTxn.AddQuery(szQuery); + } + + SQL_FastQuery(g_hDatabase, "SET CHARSET 'utf8'"); + + g_hDatabase.Execute(hTxn, SQL_Callback_AddParamsSuccess, SQL_Callback_AddParamsFailure, hDP); +} + +public void SQL_Callback_AddParamsFailure(Database hDB, any hDataPack, int iNumQueries, const char[] szError, int iFailIndex, any[] queryData) +{ + DataPack hDP = view_as(hDataPack); + hDP.Reset(); + ArrayList hParamsArr = view_as(hDP.ReadCell()); + + delete hParamsArr; + delete hDP; + + LogError("Не удалось добавить параметры (%d): %s", iFailIndex, szError); +} + +public void SQL_Callback_AddParamsSuccess(Database hDB, any hData, int iNumQueries, DBResultSet[] hResults, any[] queryData) +{ + DataPack hDP = view_as(hData); + hDP.Reset(); + ArrayList hParamsArr = view_as(hDP.ReadCell()); + + char szKey[KEYS_MAX_LENGTH], szKeyType[KEYS_MAX_LENGTH], sParams[PMP*2], szName[MAX_NAME_LENGTH], szAuth[32], sExpires[64]; + int iUses, iLifeTime, iClient, iLangClient; + hDP.ReadString(SZF(szKey)); + hDP.ReadCell(); + iClient = GET_CID(hDP.ReadCell()); + ReplySource CmdReplySource = view_as(hDP.ReadCell()); + hDP.ReadString(SZF(szKeyType)); + + iUses = hDP.ReadCell(); + iLifeTime = hDP.ReadCell(); + hDP.ReadCell(); + + Handle hPlugin; + Function fCallback; + + if(hDP.ReadCell()) + { + hPlugin = view_as(hDP.ReadCell()); + fCallback = hDP.ReadFunction(); + any iData = hDP.ReadCell(); + API_Callback(Add, hPlugin, fCallback, iClient, szKey, true, NULL_STRING, iData); + } + + if(iClient > 0) + { + GetClientName(iClient, SZF(szName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); + iLangClient = iClient; + } + else + { + iLangClient = LANG_SERVER; + + strcopy(SZF(szName), "CONSOLE"); + strcopy(SZF(szAuth), "STEAM_ID_SERVER"); + } + + if(iLifeTime) + { + Keys_GetTimeFromStamp(SZF(sExpires), iLifeTime, iLangClient); + } + else + { + FormatEx(SZF(sExpires), "%T", "FOREVER", iLangClient); + } + + sParams[0] = 0; + + DataPack hDataPack; + g_hKeysTrie.GetValue(szKeyType, hDataPack); + hDataPack.Reset(); + hPlugin = view_as(hDataPack.ReadCell()); + fCallback = hDataPack.ReadFunction(); + Call_StartFunction(hPlugin, fCallback); + Call_PushCell(Print); + Call_PushCell(LANG_SERVER); + Call_PushString(szKeyType); + 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(hResults[0].AffectedRows) + { + if(iClient != -1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_CREATE_KEY", szKey); + } + + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_CREATE_KEY", LANG_SERVER, szName, szAuth, szKey, sExpires, iUses, szKeyType, sParams); + } + else + { + if(iClient != -1) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_CREATE_KEY", szKey); + } + + LogToFile(g_sLogFile, "%T", "LOG_ERROR_CREATE_KEY", LANG_SERVER, szKey, szName, szAuth, sExpires, iUses, szKeyType, sParams); + } + + delete hParamsArr; + delete hDP; +} + +/* +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +********************************************************* ****************************************************** +********************************************************* ****************************************************** +********************************************************* У Д А Л Е Н И Е ****************************************************** +********************************************************* ****************************************************** +********************************************************* ****************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +*/ + +void Keys_Delete(const char[] szKey, + bool bSearch = false, + int iClient = -1, + ReplySource CmdReplySource = SM_REPLY_TO_CONSOLE, + Handle hPlugin = null, + Function fCallback = INVALID_FUNCTION, + any iData = 0) +{ + DataPack hDP = new DataPack(); + hDP.WriteString(szKey); + hDP.WriteCell((iClient != -1)); + hDP.WriteCell(GET_UID(iClient)); + hDP.WriteCell(CmdReplySource); + + if(hPlugin != null && fCallback != INVALID_FUNCTION) + { + hDP.WriteCell(true); + hDP.WriteCell(hPlugin); + hDP.WriteFunction(fCallback); + hDP.WriteCell(iData); + } + else + { + hDP.WriteCell(false); + } + + char szQuery[PMP*2], szSID[64]; + + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " AND `k_sid` = %d", g_CVAR_iServerID); + } + + if(bSearch) + { + FormatEx(SZF(szQuery), "SELECT `k_id` FROM `keys_tokens` WHERE `k_name` = '%s'%s;", szKey, szSID); + g_hDatabase.Query(SQL_Callback_SearchDelKey, szQuery, hDP); + } + else + { + // DELETE FROM `keys_params` WHERE `p_kid` = IFNULL((SELECT `k_id` FROM `keys_tokens` WHERE `k_name` = '%s'), 0); + // DELETE FROM `keys_tokens` WHERE `k_name` = '%s'; + // DELETE FROM `keys_params` WHERE `p_kid` = IFNULL((SELECT `k_id` FROM `keys_tokens` WHERE `k_name` = '%s' AND `k_sid` = %d), 0); + // DELETE FROM `keys_tokens` WHERE `k_name` = '%s' AND `k_sid` = %d; + + FormatEx(SZF(szQuery), "DELETE FROM `keys_params` WHERE `p_kid` = IFNULL((SELECT `k_id` FROM `keys_tokens` WHERE `k_name` = '%s'%s), 0); \ + DELETE FROM `keys_tokens` WHERE `k_name` = '%s'%s;", szKey, szSID, szKey, szSID); + + g_hDatabase.Query(SQL_Callback_RemoveKey, szQuery, hDP); + } +} + +public void SQL_Callback_SearchDelKey(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) + { + delete hDP; + LogError("SQL_Callback_SearchDelKey: %s", szDbError); + return; + } + + if(hResult.FetchRow()) + { + int iKeyID = hResult.FetchInt(0); + + char szQuery[PMP*2]; + + FormatEx(SZF(szQuery), "DELETE FROM `keys_params` WHERE `p_kid` = %d; \ + DELETE FROM `keys_tokens` WHERE `k_id` = %d;", iKeyID, iKeyID); + + g_hDatabase.Query(SQL_Callback_RemoveKey, szQuery, hDP); + return; + } + + Keys_NotifyDelResult(hDP, false); +} + +public void SQL_Callback_RemoveKey(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) + { + delete hDP; + LogError("SQL_Callback_RemoveKey: %s", szDbError); + return; + } + + Keys_NotifyDelResult(hDP, hResult.AffectedRows != 0); +} + +void Keys_NotifyDelResult(DataPack hDP, bool bResult) +{ + hDP.Reset(); + + char szKey[KEYS_MAX_LENGTH]; + hDP.ReadString(SZF(szKey)); + + bool bReply = hDP.ReadCell(); + + int iClient = GET_CID(hDP.ReadCell()); + + if(iClient == -1) + { + iClient = 0; + } + + ReplySource CmdReplySource = view_as(hDP.ReadCell()); + + char szName[MAX_NAME_LENGTH], szAuth[32]; + if(!iClient) + { + strcopy(SZF(szName), "CONSOLE"); + strcopy(SZF(szAuth), "STEAM_ID_SERVER"); + } + else + { + GetClientName(iClient, SZF(szName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); + } + + if(bReply) + { + if(bResult) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_REMOVE_KEY", szKey); + + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_REMOVE_KEY", LANG_SERVER, szKey, szName, szAuth); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_REMOVE_KEY", szKey); + + LogToFile(g_sLogFile, "%T", "LOG_ERROR_REMOVE_KEY", LANG_SERVER, szKey, szName, szAuth); + } + } + + if(hDP.ReadCell()) + { + Handle hPlugin = view_as(hDP.ReadCell()); + Function fCallback = hDP.ReadFunction(); + any iData = hDP.ReadCell(); + if(bResult) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_REMOVE_KEY", szKey); + + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_REMOVE_KEY", LANG_SERVER, szKey, szName, szAuth); + API_Callback(Rem, hPlugin, fCallback, iClient, szKey, true, NULL_STRING, iData); + } + else + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_REMOVE_KEY", szKey); + + LogToFile(g_sLogFile, "%T", "LOG_ERROR_REMOVE_KEY", LANG_SERVER, szKey, szName, szAuth); + API_Callback(Rem, hPlugin, fCallback, iClient, szKey, false, "Keys don't search", iData); + } + } + + delete hDP; +} + +/* +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +********************************************************* ****************************************************** +********************************************************* ****************************************************** +********************************************************* И С П О Л Ь З О В А Н И Е ****************************************************** +********************************************************* ****************************************************** +********************************************************* ****************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +************************************************************************************************************************************************** +*/ + +void Keys_Use(const char[] szKey, + int iClient, + ReplySource CmdReplySource = SM_REPLY_TO_CHAT, + bool bNotify, + bool bIgnoreBlock, + Handle hPlugin = null, + Function fCallback = INVALID_FUNCTION, + any iData = 0) +{ + DataPack hDP = new DataPack(); + hDP.WriteString(szKey); + hDP.WriteCell(UID(iClient)); + hDP.WriteCell(CmdReplySource); + hDP.WriteCell(bNotify); + + if(hPlugin != null && fCallback != INVALID_FUNCTION) + { + hDP.WriteCell(true); + hDP.WriteCell(hPlugin); + hDP.WriteFunction(fCallback); + hDP.WriteCell(iData); + } + else + { + hDP.WriteCell(false); + } + + char szQuery[PMP*2], szAuth[32]; + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); + + char szSID[64]; + + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " AND `k_sid` = %d", g_CVAR_iServerID); + } + + FormatEx(SZF(szQuery), "SELECT `k_id`, `k_type`, `k_expires`, `k_uses` FROM `keys_tokens` WHERE `k_name` = '%s'%s LIMIT 1;", szKey, szSID); + + g_hDatabase.Query(SQL_Callback_SelectUseKey, szQuery, hDP); +} + +public void SQL_Callback_SelectUseKey(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) + { + delete hDP; + LogError("SQL_Callback_SelectUseKey: %s", szDbError); + return; + } + + hDP.Reset(); + + char szKey[KEYS_MAX_LENGTH], szError[PMP]; + hDP.ReadString(SZF(szKey)); + int iClient = CID(hDP.ReadCell()); + ReplySource CmdReplySource = view_as(hDP.ReadCell()); + bool bNotify = view_as(hDP.ReadCell()); + + Handle hPlugin = null; + Function fCallback = INVALID_FUNCTION; + any iData = 0; + if(hDP.ReadCell()) + { + hPlugin = view_as(hDP.ReadCell()); + fCallback = hDP.ReadFunction(); + iData = hDP.ReadCell(); + } + + delete hDP; + + if (iClient) + { + if(hResult.FetchRow()) + { + char szKeyType[KEYS_MAX_LENGTH]; + hResult.FetchString(1, SZF(szKeyType)); + DataPack hDataPack; + if(g_hKeysTrie.GetValue(szKeyType, hDataPack)) + { + int iExpires = hResult.FetchInt(2); + if(iExpires) + { + if(iExpires < GetTime()) + { + Keys_Delete(szKey); + FormatEx(SZF(szError), "%T%T", "ERROR", iClient, "ERROR_KEY_NOT_EXIST", iClient); + if(bNotify) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, szError); + } + if(fCallback) + { + API_Callback(Use, hPlugin, fCallback, iClient, szKey, false, szError, iData); + } + return; + } + } + + int iUses = hResult.FetchInt(3); + if(!iUses) + { + Keys_Delete(szKey); + FormatEx(SZF(szError), "%T%T", "%t%t", "ERROR", iClient, "ERROR_KEY_NOT_EXIST", iClient); + if(bNotify) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, szError); + } + if(fCallback) + { + API_Callback(Use, hPlugin, fCallback, iClient, szKey, false, szError, iData); + } + return; + } + + int iKeyID = hResult.FetchInt(0); + + DataPack hDP2 = new DataPack(); + hDP2.WriteString(szKey); + hDP2.WriteString(szKeyType); + hDP2.WriteCell(iKeyID); + hDP2.WriteCell(iUses); + hDP2.WriteCell(UID(iClient)); + hDP2.WriteCell(CmdReplySource); + hDP2.WriteCell(bNotify); + + if(hPlugin != null && fCallback != INVALID_FUNCTION) + { + hDP2.WriteCell(true); + hDP2.WriteCell(hPlugin); + hDP2.WriteFunction(fCallback); + hDP2.WriteCell(iData); + } + else + { + hDP2.WriteCell(false); + } + char szQuery[PMP], szAuth[32]; + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); + FormatEx(SZF(szQuery), "SELECT `u_auth` FROM `keys_players_used` WHERE `u_kid` = '%d' AND `u_auth` = '%s';", iKeyID, szAuth); + g_hDatabase.Query(SQL_Callback_CheckUseKey, szQuery, hDP2); + + return; + } + + FormatEx(SZF(szError), "%T%T", "ERROR", iClient, "ERROR_INCORRECT_TYPE", iClient); + if(fCallback) + { + API_Callback(Use, hPlugin, fCallback, iClient, szKey, false, szError, iData); + } + return; + } + + FormatEx(SZF(szError), "%T%T", "ERROR", iClient, "ERROR_KEY_NOT_EXIST", iClient); + if(fCallback) + { + API_Callback(Use, hPlugin, fCallback, iClient, szKey, false, szError, iData); + } + } +} + +public void SQL_Callback_CheckUseKey(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) + { + delete hDP; + LogError("SQL_Callback_CheckUseKey: %s", szDbError); + return; + } + + hDP.Reset(); + + char szKey[KEYS_MAX_LENGTH], szKeyType[KEYS_MAX_LENGTH]; + hDP.ReadString(SZF(szKey)); + hDP.ReadString(SZF(szKeyType)); + int iKeyID = hDP.ReadCell(); + hDP.ReadCell(); + int iClient = CID(hDP.ReadCell()); + hDP.ReadCell(); + hDP.ReadCell(); + + Handle hPlugin = null; + Function fCallback = INVALID_FUNCTION; + any iData = 0; + if(hDP.ReadCell()) + { + hPlugin = view_as(hDP.ReadCell()); + fCallback = hDP.ReadFunction(); + iData = hDP.ReadCell(); + } + + if(hResult.FetchRow()) + { + char szError[PMP]; + FormatEx(SZF(szError), "%T%T", "%t%t", "ERROR", iClient, "ERROR_KEY_ALREADY_USED", iClient); + API_Callback(Use, hPlugin, fCallback, iClient, szKey, false, szError, iData); + delete hDP; + return; + } + + char szQuery[PMP]; + FormatEx(SZF(szQuery), "SELECT `p_num`, `p_value` FROM `keys_params` WHERE `p_kid` = '%d' ORDER BY `p_num`;", iKeyID); + g_hDatabase.Query(SQL_Callback_UseKey, szQuery, hDP); +} + +public void SQL_Callback_UseKey(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) +{ + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) + { + delete hDP; + LogError("SQL_Callback_SelectUseKey: %s", szDbError); + return; + } + + hDP.Reset(); + + char szKey[KEYS_MAX_LENGTH], szKeyType[KEYS_MAX_LENGTH], szError[PMP]; + hDP.ReadString(SZF(szKey)); + hDP.ReadString(SZF(szKeyType)); + int iKeyID = hDP.ReadCell(); + int iUses = hDP.ReadCell(); + int iClient = CID(hDP.ReadCell()); + ReplySource CmdReplySource = view_as(hDP.ReadCell()); + bool bNotify = view_as(hDP.ReadCell()); + + Handle hPlugin = null; + Function fCallback = INVALID_FUNCTION; + any iData = 0; + if(hDP.ReadCell()) + { + hPlugin = view_as(hDP.ReadCell()); + fCallback = hDP.ReadFunction(); + iData = hDP.ReadCell(); + + if(!hResult.RowCount) + { + API_Callback(Use, hPlugin, fCallback, iClient, szKey, false, "Fail select key params", iData); + delete hDP; + return; + } + } + + if (iClient) + { + if(!hResult.RowCount) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "Fail select key params"); + } + + ArrayList hParamsArr = new ArrayList(ByteCountToCells(KEYS_MAX_LENGTH)); + + char szParam[KEYS_MAX_LENGTH]; + + while(hResult.FetchRow()) + { + hResult.FetchString(1, SZF(szParam)); + hParamsArr.PushString(szParam); + } + + DataPack hDataPack; + g_hKeysTrie.GetValue(szKeyType, hDataPack); + hDataPack.Reset(); + hPlugin = view_as(hDataPack.ReadCell()); + fCallback = hDataPack.ReadFunction(); + + strcopy(SZF(szError), "unknown"); + bool bResult = false; + Call_StartFunction(hPlugin, fCallback); + Call_PushCell(Activation); + Call_PushCell(iClient); + Call_PushString(szKeyType); + Call_PushCell(hParamsArr); + Call_PushStringEx(SZF(szError), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushCell(sizeof(szError)); + Call_Finish(bResult); + + delete hParamsArr; + + if(!bResult && bNotify) + { + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%s", "ERROR", szError); + return; + } + + char szName[MAX_NAME_LENGTH], szAuth[32], szQuery[PMP], szSID[64]; + GetClientName(iClient, SZF(szName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); + + if(!g_CVAR_iServerID) + { + szSID[0] = 0; + } + else + { + FormatEx(SZF(szSID), " AND `k_sid` = %d", g_CVAR_iServerID); + } + if(--iUses) + { + if(!g_CVAR_iServerID) + { + FormatEx(SZF(szQuery), "INSERT INTO `keys_players_used` (`u_auth`, `u_kid`) VALUES ('%s', %d);", szAuth, iKeyID); + } + else + { + FormatEx(SZF(szQuery), "INSERT INTO `keys_players_used` (`u_auth`, `u_kid`, `u_sid`) VALUES ('%s', %d, %d);", szAuth, iKeyID, g_CVAR_iServerID); + } + g_hDatabase.Query(SQL_Callback_ErrorCheck, szQuery); + + FormatEx(SZF(szQuery), "UPDATE `keys_tokens` SET `k_uses` = %d WHERE `u_kid` = %d%s;", iUses, iKeyID, szSID); + g_hDatabase.Query(SQL_Callback_ErrorCheck, szQuery); + } + else + { + Keys_Delete(szKey); + FormatEx(SZF(szQuery), "DELETE FROM `keys_players_used` WHERE `u_kid` = %d%s;", szKey, szSID); + g_hDatabase.Query(SQL_Callback_ErrorCheck, szQuery); + } + + UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_USE_KEY", szKey); + LogToFile(g_sLogFile, "%T", "LOG_SUCCESS_USE_KEY", LANG_SERVER, szName, szAuth, szKey); + return; + } +} diff --git a/addons/sourcemod/scripting/keys/STATS.sp b/addons/sourcemod/scripting/keys/STATS.sp new file mode 100644 index 0000000..fdd2993 --- /dev/null +++ b/addons/sourcemod/scripting/keys/STATS.sp @@ -0,0 +1,322 @@ + +#undef REQUIRE_EXTENSIONS +#tryinclude +#tryinclude +#tryinclude +#tryinclude + +#define SOCKET_ON() (GetFeatureStatus(FeatureType_Native, "SocketCreate") == FeatureStatus_Available) +#define CURL_ON() (GetFeatureStatus(FeatureType_Native, "curl_easy_init") == FeatureStatus_Available) +#define STEAMWORKS_ON() (GetFeatureStatus(FeatureType_Native, "SteamWorks_CreateHTTPRequest") == FeatureStatus_Available) +#define RIP_ON() (GetFeatureStatus(FeatureType_Native, "HTTPClient.HTTPClient") == FeatureStatus_Available) + +stock const char API_KEY[] = "35bdcf38c4dabc22028792ebb366b3b8"; +stock const char URL[] = "http://stats.tibari.ru/add_server.php"; +stock const char HOST[] = "http://stats.tibari.ru"; +stock const char SCRIPT[] = "add_server.php"; + +void Stats_Init() +{ +#if defined _socket_included + MarkNativeAsOptional("SocketCreate"); + MarkNativeAsOptional("SocketConnect"); + MarkNativeAsOptional("SocketSend"); +#endif +#if defined _cURL_included + MarkNativeAsOptional("curl_slist"); + MarkNativeAsOptional("curl_slist_append"); + MarkNativeAsOptional("curl_easy_init"); + MarkNativeAsOptional("curl_easy_setopt_string"); + MarkNativeAsOptional("curl_easy_setopt_int"); + MarkNativeAsOptional("curl_easy_setopt_handle"); + MarkNativeAsOptional("curl_easy_setopt_function"); + MarkNativeAsOptional("curl_easy_perform_thread"); + MarkNativeAsOptional("curl_easy_strerror"); +#endif + +#if defined _SteamWorks_Included + MarkNativeAsOptional("SteamWorks_CreateHTTPRequest"); + MarkNativeAsOptional("SteamWorks_SetHTTPRequestRawPostBody"); + MarkNativeAsOptional("SteamWorks_SetHTTPCallbacks"); + MarkNativeAsOptional("SteamWorks_WriteHTTPResponseBodyToFile"); + MarkNativeAsOptional("SteamWorks_SendHTTPRequest"); +#endif +#if defined _ripext_included_ + MarkNativeAsOptional("HTTPClient.HTTPClient"); + MarkNativeAsOptional("HTTPClient.SetHeader"); + MarkNativeAsOptional("HTTPClient.Post"); + MarkNativeAsOptional("HTTPResponse.Data.get"); + MarkNativeAsOptional("HTTPResponse.Status.get"); + MarkNativeAsOptional("JSONObject.JSONObject"); + MarkNativeAsOptional("JSONObject.SetString"); +#endif +} + +static ConVar g_hCvar_GetIPMethod; + +void Stats_OnPluginStart(); +{ + g_hCvar_GetIPMethod = CreateConVar("sm_keys_stats_get_ip_method", "0", " IP-\n\ + 0 - hostip\n\ + 1 - status\n\ + 123.123.123.123:12345 - "); +} + +void Stats_OnMapStart() +{ + #if defined _SteamWorks_Included + if (CanTestFeatures() && STEAMWORKS_ON()) + { + SteamWorks_SteamServersConnected(); + return; + } + #endif + + CreateTimer(4.0, Timer_Connect, _, TIMER_FLAG_NO_MAPCHANGE); +} + +public Action Timer_Connect(Handle hTimer, any iData) +{ + #if defined _ripext_included_ + if (CanTestFeatures() && RIP_ON()) + { + RiP_SendKeysInfo(); + return; + } + #endif + + #if defined _ripext_included_ + if (CanTestFeatures() && SOCKET_ON()) + { + Socket_SendKeysInfo(); + return; + } + #endif + + #if defined _ripext_included_ + if (CanTestFeatures() && CURL_ON()) + { + cURL_SendKeysInfo(); + return; + } + #endif + + SetFailState("[VIP STATS] : SteamWorks, Rest in Pawn, Socket, CURL"); +} + +#if defined _socket_included +void Socket_SendKeysInfo() +{ + Handle hSocket = SocketCreate(SOCKET_TCP, OnSocketError); + SocketConnect(hSocket, OnSocketConnected, OnSocketReceive, OnSocketDisconnected, HOST[7], 80); +} + +public int OnSocketConnected(Handle hSocket, any arg) +{ + char szBuffer[512], szBody[PMP], szIP[24]; + + GetServerIP(SZF(szIP)); + + FormatEx(SZF(szBody), "key=%s&ip=%s&version=%s&sm=%s", API_KEY, szIP, PLUGIN_VERSION, SOURCEMOD_VERSION); + FormatEx(SZF(szBuffer), "POST /%s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\nContent-Length: %d\r\nUser-Agent: Valve/Steam HTTP Client 1.0\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n%s\r\n", SCRIPT, HOST[7], strlen(szBody), szBody); + + SocketSend(hSocket, szBuffer); +} + +public int OnSocketReceive(Handle hSocket, const char[] sReceiveData, const int iDataSize, any data) +{ + CloseHandle(hSocket); + + int iStartIndex = FindCharInString(sReceiveData, ' '); + if(iStartIndex != -1) + { + char sBuffer[4]; + strcopy(SZF(sBuffer), sReceiveData[iStartIndex+1]); + sBuffer[3] = 0; + ResultNotify(StringToInt(sBuffer), "Socket"); + } +} + +public int OnSocketDisconnected(Handle hSocket, any data) +{ + CloseHandle(hSocket); +} + +public int OnSocketError(Handle hSocket, const int errorType, const int errorNum, any data) +{ + LogError("OnSocketError:: errorType %d (errorNum %d)", errorType, errorNum); + CloseHandle(hSocket); +} +#endif + +#if defined _cURL_included +void cURL_SendKeysInfo() +{ + char szIP[24], szBody[PMP]; + + GetServerIP(SZF(szIP)); + + FormatEx(SZF(szBody), "key=%s&ip=%s&version=%s&sm=%s", API_KEY, szIP, PLUGIN_VERSION, SOURCEMOD_VERSION); + + Handle hBuffer = curl_slist(); + curl_slist_append(hBuffer, "User-Agent: Valve/Steam HTTP Client 1.0"); + + Handle hCurl = curl_easy_init(); + curl_easy_setopt_string(hCurl, CURLOPT_URL, URL); + curl_easy_setopt_int(hCurl, CURLOPT_HTTPPOST, 1); + curl_easy_setopt_string(hCurl, CURLOPT_POSTFIELDS, szBody); + curl_easy_setopt_handle(hCurl, CURLOPT_HTTPHEADER, hBuffer); + curl_easy_setopt_int(hCurl, CURLOPT_POSTFIELDSIZE, strlen(szBody)); + curl_easy_setopt_function(hCurl, CURLOPT_WRITEFUNCTION, OnCurlWrite); + curl_easy_perform_thread(hCurl, OnComplete, hBuffer); +} + +public int OnCurlWrite(Handle hCurl, const char[] szBuffer, const int bytes, const int nmemb) +{ + if(!strcmp(szBuffer, "Success!")) + { + LogAction(-1, -1, "[KEYS-CORE STATS] [cURL] "); + } + else + { + LogError("[KEYS-CORE STATS] [cURL] / (%s)", szBuffer); + } + + return bytes*nmemb; +} + +public int OnComplete(Handle hCurl, CURLcode code, any hHeader) +{ + CloseHandle(hCurl); + CloseHandle(hHeader); + + if(code != CURLE_OK) + { + char error[PLATFORM_MAX_PATH]; + curl_easy_strerror(code, SZF(error)); + LogError("cURL error: (%i) '%s'", code, error); + } +} +#endif + +#if defined _ripext_included_ +void RiP_SendKeysInfo() +{ + HTTPClient g_hHTTPClient = new HTTPClient(HOST); + + char szUserAgent[64], szIP[24]; +// FormatEx(SZF(szUserAgent), "SourcePawn (VIP Stats v%s)", PLUGIN_VERSION); + FormatEx(SZF(szUserAgent), "Valve/Steam HTTP Client 1.0"); + g_hHTTPClient.SetHeader("User-Agent", szUserAgent); + + GetServerIP(SZF(szIP)); + + JSONObject hRequest = new JSONObject(); + hRequest.SetString("key", API_KEY); + hRequest.SetString("ip", szIP); + hRequest.SetString("version", PLUGIN_VERSION); + hRequest.SetString("sm", SOURCEMOD_VERSION); + + g_hHTTPClient.Post(SCRIPT, hRequest, OnRequestComplete, 0); + delete hRequest; +} + +public void OnRequestComplete(HTTPResponse hResponse, any iData) +{ + ResultNotify(view_as(hResponse.Status), "Rest in Pawn"); +} +#endif + +#if defined _SteamWorks_Included +public int SteamWorks_SteamServersConnected() +{ + int iIp[4]; + if (SteamWorks_GetPublicIP(iIp) && iIp[0] && iIp[1] && iIp[2] && iIp[3]) + { + char szIP[24], szBuffer[PMP]; + FormatEx(SZF(szIP), "%d.%d.%d.%d:%d", iIp[0], iIp[1], iIp[2], iIp[3], FindConVar("hostport").IntValue); + + FormatEx(SZF(szBuffer), "%s/%s", HOST, SCRIPT); + Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, szBuffer); + FormatEx(SZF(szBuffer), "key=%s&ip=%s&version=%s&sm=%s", API_KEY, szIP, PLUGIN_VERSION, SOURCEMOD_VERSION); + SteamWorks_SetHTTPRequestRawPostBody(hRequest, "application/x-www-form-urlencoded", SZF(szBuffer)); + SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete); + SteamWorks_SendHTTPRequest(hRequest); + } +} + +public int OnTransferComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode) +{ + delete hRequest; + + ResultNotify(view_as(eStatusCode), "SteamWorks"); +} +#endif + +void ResultNotify(int iStatusCode, const char[] szPrefix) +{ + switch(iStatusCode) + { + case 200: LogAction(-1, -1, "[KEYS-CORE STATS] [%s] /", szPrefix); + case 400: LogError("[KEYS-CORE STATS] [%s] ", szPrefix); + case 403: LogError("[KEYS-CORE STATS] [%s] IP:PORT", szPrefix); + case 404: LogError("[KEYS-CORE STATS] [%s] ", szPrefix); + case 406: LogError("[KEYS-CORE STATS] [%s] API KEY", szPrefix); + case 410: LogError("[KEYS-CORE STATS] [%s] KEYS-CORE", szPrefix); + case 413: LogError("[KEYS-CORE STATS] [%s] ", szPrefix); + default: LogError("[KEYS-CORE STATS] [%s] : %d", szPrefix, iStatusCode); + } +} + +#define GetServerIp(%1,%2) GetServerIpFunc(view_as(%1), %1, %2) + +void GetServerIpFunc(int[] array, char[] buffer, int maxlength) +{ + array[0] = FindConVar("hostip").IntValue; + FormatEx(buffer, maxlength, "%d.%d.%d.%d:%d", buffer[3] + 0, buffer[2] + 0, buffer[1] + 0, buffer[0] + 0, FindConVar("hostport").IntValue); +} + +void GetServerIP(char[] szIP, int iMaxLen) +{ + g_hCvar_GetIPMethod.GetString(szIP, iMaxLen); + if(strlen(szIP) > 12) + { + return; + } + + if(StringToInt(szIP) == 1) + { + char szResponse[512]; + ServerCommandEx(SZF(szResponse), "status"); + int index = StrContains(szResponse[50], "udp/ip", true); + if(index != -1) + { + int iIpPos, iPortPos; + index += FindCharInString(szResponse[56+index], ':')+58; + strcopy(szResponse, 64, szResponse[index]); + + iPortPos = FindCharInString(szResponse, ':'); + if(StrContains(szResponse[iPortPos+9], "public", true) != -1) + { + iIpPos = iPortPos+20; + } + else + { + iIpPos = 0; + } + + index = 0; + while((szResponse[iIpPos+index] == '.' || IsCharNumeric(szResponse[iIpPos+index])) && index < iMaxLen) + { + szIP[index] = szResponse[iIpPos+index]; + ++index; + } + + strcopy(szIP[index], 7, szResponse[iPortPos]); + szIP[index+7] = 0; + return; + } + } + + GetServerIp(szIP, iMaxLen); +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/UTIL.sp b/addons/sourcemod/scripting/keys/UTIL.sp new file mode 100644 index 0000000..bab6c2b --- /dev/null +++ b/addons/sourcemod/scripting/keys/UTIL.sp @@ -0,0 +1,70 @@ + +void UTIL_ReplyToCommand(int iClient, ReplySource CmdReplySource, const char[] szFormat, any ...) +{ + char szBuffer[2048]; + SetGlobalTransTarget(iClient); + VFormat(SZF(szBuffer), szFormat, 4); + + if(iClient) + { + switch(CmdReplySource) + { + case SM_REPLY_TO_CONSOLE: PrintToConsole(iClient, "[KEYS] %s", szBuffer); + case SM_REPLY_TO_CHAT: PrintToChat(iClient, GetEngineVersion() == Engine_CSGO ? " \x04[KEYS] \x01%s":"\x04[KEYS] \x01%s", szBuffer); + } + } + else + { + PrintToServer("[KEYS] %s", szBuffer); + } +} + +int UTIL_GetRandomInt(int iMin, int iMax) +{ + int iRandom = GetURandomInt(); + + if (iRandom == 0) + { + ++iRandom; + } + + return RoundToCeil(float(iRandom) / (float(2147483647) / float(iMax - iMin + 1))) + iMin - 1; +} + +/* +A - Буква в любом регистре +B - Цифра 0-9 +X - Цифра 0-9 либо буква в любом регистре +U - число 0-9 либо буква в верхнем регистре +L - число 0-9 либо буква в нижнем регистре +*/ + +int UTIL_GetCharTemplate(char iChar) +{ + + static const int g_iNumbers[] = {0x30, 0x39}; + static const int g_iLettersUpper[] = {0x41, 0x5A}; + static const int g_iLettersLower[] = {0x61, 0x7A}; + switch(iChar) + { + // A - буква в любом регистре + case 0x41: return UTIL_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 UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):(UTIL_GetRandomInt(1, 20) > 10 ? UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1])); + // U - число 0-9 либо буква в верхнем регистре + case 0x55: return UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]); + // L - число 0-9 либо буква в нижнем регистре + case 0x4c: return UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1]); + // Символ - + case 0x2D: return iChar; + // Другой символ + default: + { + return UTIL_GetCharTemplate(0x58); + } + } + + return iChar; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/api.sp b/addons/sourcemod/scripting/keys/api.sp index cbd21af..9357b1a 100644 --- a/addons/sourcemod/scripting/keys/api.sp +++ b/addons/sourcemod/scripting/keys/api.sp @@ -10,14 +10,28 @@ public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) CreateNative("Keys_IsCoreStarted", Native_IsCoreStarted); CreateNative("Keys_GetCoreDatabase", Native_GetCoreDatabase); + CreateNative("Keys_GetDatabaseType", Native_GetDatabaseType); CreateNative("Keys_RegKey", Native_RegKey); CreateNative("Keys_UnregKey", Native_UnregKey); - /* + CreateNative("Keys_IsValidKeyType", Native_IsValidKeyType); + CreateNative("Keys_FillArrayByKeyTypes", Native_FillArrayByKeyTypes); + + CreateNative("Keys_IsValidKey", Native_IsValidKey); + CreateNative("Keys_GetKeyData", Native_GetKeyData); CreateNative("Keys_GenerateKey", Native_GenerateKey); CreateNative("Keys_AddKey", Keys_AddKey); CreateNative("Keys_RemoveKey", Keys_RemoveKey); - CreateNative("Keys_IsValidKey", Native_IsValidKey); + CreateNative("Keys_UseKey", Keys_UseKey); + + + /* + - Проверка наличия типа ключа + - Проверка наличия ключа + - Генерация ключа + - Добавление ключа + - Получение массива с типами ключей + - Активация ключа */ RegPluginLibrary("keys_core"); @@ -41,10 +55,15 @@ public Native_GetCoreDatabase(Handle:hPlugin, iNumParams) return _:CloneHandle(g_hDatabase, hPlugin); } +public Native_GetDatabaseType(Handle:hPlugin, iNumParams) +{ + return g_bDBMySQL; +} + public Native_RegKey(Handle:hPlugin, iNumParams) { decl String:sKeyType[KEYS_MAX_LENGTH]; - GetNativeString(1, sKeyType, sizeof(sKeyType)); + GetNativeString(1, SZF(sKeyType)); if(FindStringInArray(g_hKeysArray, sKeyType) != -1) { @@ -67,7 +86,7 @@ public Native_RegKey(Handle:hPlugin, iNumParams) public Native_UnregKey(Handle:hPlugin, iNumParams) { decl String:sKeyType[KEYS_MAX_LENGTH], index; - GetNativeString(1, sKeyType, sizeof(sKeyType)); + GetNativeString(1, SZF(sKeyType)); if((index = FindStringInArray(g_hKeysArray, sKeyType)) != -1) { @@ -80,3 +99,317 @@ public Native_UnregKey(Handle:hPlugin, iNumParams) RemoveFromTrie(g_hKeysTrie, sKeyType); } } + +public Native_IsValidKeyType(Handle:hPlugin, iNumParams) +{ + decl String:sKeyType[KEYS_MAX_LENGTH], index; + GetNativeString(1, SZF(sKeyType)); + + return (FindStringInArray(g_hKeysArray, sKeyType) != -1); +} + +public Native_FillArrayByKeyTypes(Handle:hPlugin, iNumParams) +{ + return CloneArray(g_hKeysArray); +} + +public Native_IsValidKey(Handle:hPlugin, iNumParams) +{ + decl Handle:hDP, String:sKey[KEYS_MAX_LENGTH], String:sQuery[256]; + GetNativeString(1, SZF(sKey)); + + hDP = CreateDataPack(); + WritePackCell(hDP, hPlugin); + WritePackCell(hDP, GetNativeCell(2)); + WritePackString(hDP, sKey); + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s' LIMIT 1;", sKey); + } + else + { + FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d LIMIT 1;", sKey, g_iServerID); + } + + SQL_TQuery(g_hDatabase, SQL_Callback_Ntv_IsValidKey, sQuery, hDP); +} + +public SQL_Callback_Ntv_IsValidKey(Handle:hOwner, Handle:hResult, const String:sDBError[], any:hDP) +{ + if (hResult == INVALID_HANDLE || sDBError[0]) + { + CloseHandle(hDP); + LogError("SQL_Callback_Ntv_IsValidKey: %s", sDBError); + return; + } + + ResetPack(hDP); + decl Handle:hPlugin, Function:fCallback, String:sKey[KEYS_MAX_LENGTH], bool:bKeyExists; + hPlugin = Handle:ReadPackCell(hDP); + fCallback = Function:ReadPackCell(hDP); + ReadPackString(SZF(sKey)); + CloseHandle(hDP); + + bKeyExists = false; + + if(SQL_FetchRow(hResult)) + { + iExpires = SQL_FetchInt(hResult, 0); + if(iExpires) + { + if(iExpires < GetTime()) + { + DeleteKey(sKey); + } + else + { + bKeyExists = true; + } + } + else + { + bKeyExists = true; + } + } + + Call_StartFunction(hPlugin, fUseCallback); + Call_PushString(sKey); + Call_PushCell(bKeyExists); + Call_Finish(); +} + +public Native_GetKeyData(Handle:hPlugin, iNumParams) +{ + decl Handle:hDP, String:sKey[KEYS_MAX_LENGTH], String:sQuery[256]; + GetNativeString(1, SZF(sKey)); + + hDP = CreateDataPack(); + WritePackCell(hDP, hPlugin); + WritePackCell(hDP, GetNativeCell(2)); + WritePackString(hDP, sKey); + + if(!g_iServerID) + { + FormatEx(SZF(sQuery), "SELECT `type`, `expires`, `uses`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` WHERE `key_name` = '%s' LIMIT 1;", sKey); + } + else + { + FormatEx(SZF(sQuery), "SELECT `type`, `expires`, `uses`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d LIMIT 1;", sKey, g_iServerID); + } + + SQL_TQuery(g_hDatabase, SQL_Callback_Ntv_GetKeyData, sQuery, hDP); +} + +public SQL_Callback_Ntv_GetKeyData(Handle:hOwner, Handle:hResult, const String:sDBError[], any:iData) +{ + if (hResult == INVALID_HANDLE || sDBError[0]) + { + LogError("SQL_Callback_Ntv_GetKeyData: %s", sDBError); + return; + } + + ResetPack(hDP); + decl Handle:hPlugin, Function:fCallback, String:sKey[KEYS_MAX_LENGTH], bool:bKeyExists, Handle:hParamsArr, String:sKeyType[KEYS_MAX_LENGTH], String:sParam[KEYS_MAX_LENGTH], String:sError[256], iExpires, iUses, i; + hPlugin = Handle:ReadPackCell(hDP); + fCallback = Function:ReadPackCell(hDP); + ReadPackString(SZF(sKey)); + CloseHandle(hDP); + + bKeyExists = false; + + if(SQL_FetchRow(hResult)) + { + iExpires = SQL_FetchInt(hResult, 1); + if(!iExpires || iExpires > GetTime()) + { + SQL_FetchString(hResult, 1, SZF(sKeyType)); + if((FindStringInArray(g_hKeysArray, sKeyType) != -1)) + { + iUses = SQL_FetchInt(hResult, 2); + if(iUses) + { + hParamsArr = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); + for(i = 3; i < 8; ++i) + { + if(SQL_IsFieldNull(hResult, i)) + { + break; + } + + SQL_FetchString(hResult, i, SZF(sParam)); + PushArrayString(hParamsArr, sParam); + } + + bKeyExists = true; + } + else + { + DeleteKey(sKey); + } + } + } + else + { + DeleteKey(sKey); + } + } + + if(!bKeyExists) + { + sKeyType[0] = iCount = iExpires = 0; + hParamsArr = INVALID_HANDLE; + } + + Call_StartFunction(hPlugin, fCallback); + Call_PushString(sKey); + Call_PushCell(bKeyExists); + Call_PushString(sKeyType); + Call_PushCell(iCount); + Call_PushCell(iExpires); + Call_PushCell(hParamsArr); + Call_Finish(); + + if(hParamsArr) + { + CloseHandle(hParamsArr); + } +} + +public Native_GenerateKey(Handle:hPlugin, iNumParams) +{ + decl String:sKey[KEYS_MAX_LENGTH], String:sTemplate[64]; + GetNativeString(3, SZF(sTemplate)); + + UTIL_GenerateKey(SZF(sKey), g_CVAR_sKeyTemplate); + SetNativeString(1, sKey, GetNativeCell(2), true); +} + +// native Keys_AddKey(const String:sKey[] = "", const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyAddCallback:AddKeyCallback); +// functag public KeyAddCallback(const String:sKey[], bool:bSuccess, const String:sError[]); +public Native_AddKey(Handle:hPlugin, iNumParams) +{ + new iClient = GetNativeCell(1); + if(iClient && (iClient < 0 || iClient > MaxClients || !IsClientInGame(iClient) || !IsFakeClient(iClient))) + { + return; + } + + decl iHandle:hDP, String:sKey[KEYS_MAX_LENGTH], String:sKeyType[KEYS_MAX_LENGTH], String:sError[256], iLifeTime, iUses, Handle:hParamsArr, String:sQuery[256]; + GetNativeString(2, SZF(sKey)); + GetNativeString(3, SZF(sKeyType)); + hParamsArr = Handle:GetNativeCell(6); + + sError[0] = 0; + + if(!UTIL_CheckKey(sKey, sKeyType, GetNativeCell(5), GetNativeCell(4), hParamsArr, SZF(sError), iClient)) + { + CloseHandle(hParamsArr); + CreateCallback_AddKey(hPlugin, Function:GetNativeCell(7), false, sError); + + return; + + // Keys_AddKey(const String:sKey[] = "", const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyAddCallback:AddKeyCallback); + // KeyAddCallback(const String:sKey[], bool:bSuccess, const String:sError[]); + } + +// CloseHandle(hParamsArr); + + decl Handle:hDP, String:sQuery[256], iExpires; + + iClient = GET_UID(iClient); + + iExpires = iLifeTime ? (iLifeTime + GetTime()):iLifeTime; + + hDP = CreateDataPack(); + WritePackCell(hDP, CloneArray(hParamsArr)); + + if(sKey[0]) + { + WritePackString(hDP, sKey); + WritePackCell(hDP, true); + } + else + { + UTIL_GenerateKey(sKey, KEYS_MAX_LENGTH, g_CVAR_sKeyTemplate); + + WritePackString(hDP, sKey); + WritePackCell(hDP, false); + } + + WritePackCell(hDP, iClient); + WritePackCell(hDP, CmdReplySource); + WritePackString(hDP, sKeyType); + WritePackCell(hDP, iUses); + WritePackCell(hDP, iExpires); + WritePackCell(hDP, iLifeTime); + WritePackCell(hDP, hPlugin); + WritePackCell(hDP, GetNativeCell(6)); + + 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); +} + +CreateCallback_AddKey(Handle:hPlugin, Function:fCallback, const String:sKey[], bool:bSuccess = true, const String:sError[] = NULL_STRING) +{ + Call_StartFunction(hPlugin, fCallback); + Call_PushString(sKey); + Call_PushCell(bSuccess); + Call_PushString(sError); + Call_Finish(); +} + +public Native_RemoveKey(Handle:hPlugin, iNumParams) +{ + decl Handle:hDP, String:sKey[KEYS_MAX_LENGTH], String:sQuery[256]; + GetNativeString(1, SZF(sKey)); + + hDP = CreateDataPack(); + WritePackCell(hDP, hPlugin); + WritePackCell(hDP, GetNativeCell(2)); + WritePackString(hDP, sKey); + + 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_Ntv_RemoveKey, sQuery, hDP); +} + +public SQL_Callback_Ntv_RemoveKey(Handle:hOwner, Handle:hResult, const String:sDBError[], any:iData) +{ + if (hResult == INVALID_HANDLE || sDBError[0]) + { + LogError("SQL_Callback_Ntv_RemoveKey: %s", sDBError); + return; + } + + ResetPack(hDP); + decl Handle:hPlugin, Function:fCallback, String:sKey[KEYS_MAX_LENGTH]; + hPlugin = Handle:ReadPackCell(hDP); + fCallback = Function:ReadPackCell(hDP); + ReadPackString(SZF(sKey)); + CloseHandle(hDP); + + Call_StartFunction(hPlugin, fCallback); + Call_PushString(sKey); + Call_PushCell(bool:SQL_GetAffectedRows(hOwner)); + Call_Finish(); +} + +// Использует ключ игроком +public Native_UseKey(Handle:hPlugin, iNumParams) +{ + +} diff --git a/addons/sourcemod/scripting/keys/cmds.sp b/addons/sourcemod/scripting/keys/cmds.sp index 2ec5bd1..4c7a345 100644 --- a/addons/sourcemod/scripting/keys/cmds.sp +++ b/addons/sourcemod/scripting/keys/cmds.sp @@ -2,7 +2,7 @@ GET_CID(iClient) { - if(iClient) + if(iClient > 0) { iClient = CID(iClient); if(!iClient) @@ -13,7 +13,7 @@ GET_CID(iClient) return iClient; } - return 0; + return iClient; } RegAdminCmds() @@ -68,7 +68,7 @@ public Action:UseKey_CMD(iClient, iArgs) decl String:sKey[KEYS_MAX_LENGTH], String:sQuery[512]; GetCmdArg(1, SZF(sKey)); - if(!UTIL_ValidateKey(sKey, strlen(sKey), SZF(sQuery))) + if(!UTIL_ValidateKey(sKey, SZF(sQuery))) { UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", sQuery); @@ -282,19 +282,17 @@ public Action:AddKey_CMD(iClient, iArgs) 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%t", "ERROR", "ERROR_INCORRECT_TYPE"); - return Plugin_Handled; - } - - decl String:sKey[KEYS_MAX_LENGTH], String:sParam[KEYS_MAX_LENGTH], String:sError[256], iLifeTime, iUses, iCount, bool:bGen; + decl Handle:hDP, + String:sKeyType[KEYS_MAX_LENGTH], + String:sParam[KEYS_MAX_LENGTH], + String:sError[256], + iLifeTime, + iUses, + iCount, + Handle:hParamsArr, + bool:bGen; GetCmdArg(0, SZF(sKey)); - bGen = bool:(sKey[3] == 's'); if(bGen) @@ -306,60 +304,33 @@ public Action:AddKey_CMD(iClient, iArgs) UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_AMOUNT"); return Plugin_Handled; } + + sKey[0] = 0; } else { GetCmdArg(1, SZF(sKey)); - if(!UTIL_ValidateKey(sKey, strlen(sKey), SZF(sError))) - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", sError); - return Plugin_Handled; - } } + GetCmdArg(4, SZF(sKeyType)); + GetCmdArg(2, SZF(sParam)); iLifeTime = StringToInt(sParam); - if(iLifeTime < 0) - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_LIFETIME"); - return Plugin_Handled; - } GetCmdArg(3, SZF(sParam)); iUses = StringToInt(sParam); - if(iUses < 1) - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_USES"); - return Plugin_Handled; - } - - decl Handle:hParamsArr, Handle:hPlugin, Function:FuncOnValidateParams, i, bool:bResult; hParamsArr = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); - for(i = 5; i <= iArgs; ++i) + for(new 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); + sError[0] = 0; - sError = "unknown"; - 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) + if(!UTIL_CheckKey(sKey, sKeyType, iLifeTime, iUses, hParamsArr, SZF(sError))) { CloseHandle(hParamsArr); UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%s", "ERROR", sError); @@ -377,7 +348,7 @@ public Action:AddKey_CMD(iClient, iArgs) { --iCount; - UTIL_GenerateKey(sKey); + UTIL_GenerateKey(SZF(sKey), g_CVAR_sKeyTemplate); hDP = CreateDataPack(); WritePackCell(hDP, CloneArray(hParamsArr)); @@ -452,12 +423,24 @@ public SQL_Callback_SearchKey(Handle:hOwner, Handle:hResult, const String:sError if(ReadPackCell(hDP)) { i = GET_CID(ReadPackCell(hDP)); - if(i != -1) + if(i == -2) + { + i = ReadPackCell(hDP); // CmdReplySource + ReadPackString(hDP, sQuery, KEYS_MAX_LENGTH); + i = ReadPackCell(hDP); // Uses + i = ReadPackCell(hDP); // Expires + i = ReadPackCell(hDP); // LifeTime + + Format(sQuery, 256, "%T%T", "ERROR", LANG_SERVER, "ERROR_KEY_ALREADY_EXISTS", LANG_SERVER, sKey); + CreateCallback_AddKey(Handle:ReadPackCell(hDP), Function:ReadPackCell(hDP), sKey, false, sQuery); + } + else if(i != -1) { UTIL_ReplyToCommand(i, ReplySource:ReadPackCell(hDP), "%t%t", "ERROR", "ERROR_KEY_ALREADY_EXISTS", sKey); } CloseHandle(hParamsArr); + CloseHandle(hDP); return; } else @@ -465,7 +448,7 @@ public SQL_Callback_SearchKey(Handle:hOwner, Handle:hResult, const String:sError decl Handle:hDP2; hDP2 = CreateDataPack(); WritePackCell(hDP2, hParamsArr); - UTIL_GenerateKey(sKey); + UTIL_GenerateKey(SZF(sKey), g_CVAR_sKeyTemplate); WritePackString(hDP2, sKey); // New Key WritePackCell(hDP2, false); i = ReadPackCell(hDP); // Client @@ -523,12 +506,10 @@ public SQL_Callback_SearchKey(Handle:hOwner, Handle:hResult, const String:sError 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); } @@ -547,7 +528,7 @@ public SQL_Callback_AddKey(Handle:hOwner, Handle:hResult, const String:sError[], 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; + 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, iLangClient, ReplySource:CmdReplySource; ReadPackString(hDP, SZF(sKey)); ReadPackCell(hDP); iClient = GET_CID(ReadPackCell(hDP)); @@ -559,29 +540,32 @@ public SQL_Callback_AddKey(Handle:hOwner, Handle:hResult, const String:sError[], ReadPackCell(hDP); iLifeTime = ReadPackCell(hDP); - if(iClient == -1) + if(iClient == -2) { - iClient = 0; + CreateCallback_AddKey(Handle:ReadPackCell(hDP), Function:ReadPackCell(hDP), sKey); } - - if(!iClient) + + if(iClient > 0) { - strcopy(SZF(sName), "CONSOLE"); - strcopy(SZF(sAuth), "STEAM_ID_SERVER"); + GetClientName(iClient, SZF(sName)); + GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + iLangClient = iClient; } else { - GetClientName(iClient, SZF(sName)); - GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); + iLangClient = LANG_SERVER; + + strcopy(SZF(sName), "CONSOLE"); + strcopy(SZF(sAuth), "STEAM_ID_SERVER"); } if(iLifeTime) { - Keys_GetTimeFromStamp(SZF(sExpires), iLifeTime, iClient); + Keys_GetTimeFromStamp(SZF(sExpires), iLifeTime, iLangClient); } else { - FormatEx(SZF(sExpires), "%T", "FOREVER", iClient); + FormatEx(SZF(sExpires), "%T", "FOREVER", iLangClient); } sParams[0] = 0; @@ -601,7 +585,7 @@ public SQL_Callback_AddKey(Handle:hOwner, Handle:hResult, const String:sError[], if(SQL_GetAffectedRows(hOwner)) { - if(iClient != -1) + if(iClient > -1) { UTIL_ReplyToCommand(iClient, CmdReplySource, "%t", "SUCCESS_CREATE_KEY", sKey); } @@ -610,7 +594,7 @@ public SQL_Callback_AddKey(Handle:hOwner, Handle:hResult, const String:sError[], } else { - if(iClient != -1) + if(iClient > -1) { UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_CREATE_KEY", sKey); } diff --git a/addons/sourcemod/scripting/keys/utils.sp b/addons/sourcemod/scripting/keys/utils.sp index 23e822c..96cb1f9 100644 --- a/addons/sourcemod/scripting/keys/utils.sp +++ b/addons/sourcemod/scripting/keys/utils.sp @@ -20,7 +20,95 @@ UTIL_ReplyToCommand(iClient, ReplySource:CmdReplySource, const String:sFormat[], } } -UTIL_ValidateKey(String:sKey[], iLength, String:sError[], iErrLen) +bool:UTIL_CheckKey(String:sKey[], const String:sKeyType[], iLifeTime, iUses, Handle:hParamsArr, String:sError[], iErrLen, iClient = LANG_SERVER) +{ + decl Handle:hDataPack; + + if(!GetTrieValue(g_hKeysTrie, sKeyType, hDataPack)) + { + FormatEx(sError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_TYPE", iClient); + return false; + } + + if(sKey[0]) + { + if(!UTIL_ValidateKey(sKey, sError, iErrLen)) + { + return false; + } + } + + if(iLifeTime < 0) + { + FormatEx(sError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_LIFETIME", iClient); + return false; + } + + if(iUses < 1) + { + FormatEx(sError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_USES", iClient); + return false; + } + + decl Handle:hPlugin, Function:FuncOnValidateParams, bool:bResult; + + SetPackPosition(hDataPack, DP_Plugin); + hPlugin = Handle:ReadPackCell(hDataPack); + + SetPackPosition(hDataPack, DP_OnValidateCallback); + FuncOnValidateParams = Function:ReadPackCell(hDataPack); + + sError = "unknown"; + 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) + { + FormatEx(sError, iErrLen, "%T%s", "ERROR", iClient, sError); + return false; + } + + return true; +} + +bool:UTIL_AddKey(const String:sKey[], +const String:sKeyType[], +iLifeTime, +iExpires, +iUses, +Handle:hParamsArr, +iClient, +bool:bDuplicateErr) +{ + hDP = CreateDataPack(); + WritePackCell(hDP, 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); +} + +UTIL_ValidateKey(String:sKey[], String:sError[], iErrLen) { if(!sKey[0]) { @@ -28,6 +116,7 @@ UTIL_ValidateKey(String:sKey[], iLength, String:sError[], iErrLen) return false; } + new iLength = strlen(sKey); if(iLength < 8) { strcopy(sError, iErrLen, "ERROR_KEY_SHORT"); @@ -60,7 +149,7 @@ UTIL_ValidateKey(String:sKey[], iLength, String:sError[], iErrLen) return true; } -UTIL_GenerateKey(String:sKey[]) +UTIL_GenerateKey(String:sKey[], iMaxLen, const String:sTemplate[] = NULL_STRING) { sKey[0] = '\0'; @@ -69,7 +158,7 @@ UTIL_GenerateKey(String:sKey[]) if(g_CVAR_sKeyTemplate[0]) { new iLength = strlen(g_CVAR_sKeyTemplate); - while (i < iLength) + while (i < iLength && i < iMaxLen) { sKey[i] = UTIL_GetCharTemplate(g_CVAR_sKeyTemplate[i]); ++i; @@ -77,7 +166,7 @@ UTIL_GenerateKey(String:sKey[]) } else { - while (i < g_CVAR_iKeyLength) + while (i < g_CVAR_iKeyLength && i < iMaxLen) { sKey[i] = UTIL_GetCharTemplate(0x58); ++i; @@ -87,9 +176,11 @@ UTIL_GenerateKey(String:sKey[]) sKey[i] = '\0'; } /* -A - Буква в любом регистре\n\ -B - Цифра 0-9\n\ -X - Цифра 0-9 либо буква в любом регистре\n\ +A - Буква в любом регистре +B - Цифра 0-9 +X - Цифра 0-9 либо буква в любом регистре +U - число 0-9 либо буква в верхнем регистре +L - число 0-9 либо буква в нижнем регистре */ static const g_iNumbers[] = {0x30, 0x39}; @@ -101,17 +192,22 @@ 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]); + case 0x41: return UTIL_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]); + 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])); + case 0x58: return UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):(UTIL_GetRandomInt(1, 20) > 10 ? UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1])); // U - число 0-9 либо буква в верхнем регистре - case 0x55: return GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]); + case 0x55: return UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]); // L - число 0-9 либо буква в нижнем регистре - case 0x4c: return GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1]); + case 0x4c: return UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1]); + // Символ - + case 0x2D: return iChar; // Другой символ - default: return iChar; + default: + { + return UTIL_GetCharTemplate(0x58); + } } return iChar; diff --git a/addons/sourcemod/scripting/keys/vars.sp b/addons/sourcemod/scripting/keys/vars.sp index 3c839a2..e9f0b85 100644 --- a/addons/sourcemod/scripting/keys/vars.sp +++ b/addons/sourcemod/scripting/keys/vars.sp @@ -1,30 +1,52 @@ -#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; - +#define SZF(%0) %0, sizeof(%0) +#define SZFA(%0,%1) %0[%1], sizeof(%0[]) +#define CID(%0) GetClientOfUserId(%0) +#define UID(%0) GetClientUserId(%0) + +#define I2S(%0,%1) IntToString(%0, SZF(%1)) +#define S2I(%0) StringToInt(%0) + +#define PMP PLATFORM_MAX_PATH +#define MNL MAX_NAME_LENGTH +#define MPL MAXPLAYERS +#define MCL MaxClients + +char g_sLogFile[PMP]; + +bool g_bIsStarted; +Database g_hDatabase; +bool g_bDBMySQL; + +bool g_bIsBlocked[MPL+1]; +int g_iAttempts[MPL+1]; + +StringMap g_hKeysTrie; +ArrayList g_hKeysArray; + +int g_CVAR_iServerID; +int g_CVAR_iKeyLength; +char g_CVAR_sKeyTemplate[KEYS_MAX_LENGTH]; +int g_CVAR_iAttempts; +int g_CVAR_iBlockTime; + +int GET_UID(int iClient) +{ + return iClient == 0 ? 0:UID(iClient); +} + +int GET_CID(int iClient) +{ + if(iClient > 0) + { + iClient = CID(iClient); + if(!iClient) + { + return -1; + } + + return iClient; + } + + return iClient; +} \ No newline at end of file diff --git a/keys_core_logo_.png b/keys_core_logo_.png new file mode 100644 index 0000000000000000000000000000000000000000..31119f962df2f66883ddcd9a856b5ec4e47ccff1 GIT binary patch literal 63562 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 sBcnv)0BDe7TG%|_k{9f5NzMa@ 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

Lnpa3+!jZF78)Z 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 B$s_;( literal 0 HcmV?d00001 From c3408b57782f22f686e158c1ab174a8c340afa93 Mon Sep 17 00:00:00 2001 From: R1KO Date: Sun, 24 Sep 2017 11:20:20 +0300 Subject: [PATCH 3/8] deleted extra files --- addons/sourcemod/scripting/Keys_Core.sp | 465 +++------ addons/sourcemod/scripting/Keys_Example.sp | 59 ++ addons/sourcemod/scripting/Keys_Shop.sp | 218 ---- addons/sourcemod/scripting/Keys_Store.sp | 68 -- addons/sourcemod/scripting/Keys_VIP.sp | 332 ------ addons/sourcemod/scripting/Keys_WCS.sp | 137 --- .../sourcemod/scripting/include/keys_core.inc | 106 +- .../sourcemod/scripting/include/lvl_ranks.inc | 256 ----- addons/sourcemod/scripting/include/rank.inc | 2 - addons/sourcemod/scripting/include/store.inc | 100 -- addons/sourcemod/scripting/include/wcs.inc | 553 ---------- addons/sourcemod/scripting/keys/api.sp | 474 ++++----- addons/sourcemod/scripting/keys/cmds.sp | 984 ------------------ addons/sourcemod/scripting/keys/utils.sp | 226 ---- .../translations/keys_shop_module.phrases.txt | 71 -- .../keys_store_module.phrases.txt | 21 - .../translations/keys_vip_module.phrases.txt | 84 -- .../translations/keys_wcs_module.phrases.txt | 71 -- 18 files changed, 476 insertions(+), 3751 deletions(-) create mode 100644 addons/sourcemod/scripting/Keys_Example.sp delete mode 100644 addons/sourcemod/scripting/Keys_Shop.sp delete mode 100644 addons/sourcemod/scripting/Keys_Store.sp delete mode 100644 addons/sourcemod/scripting/Keys_VIP.sp delete mode 100644 addons/sourcemod/scripting/Keys_WCS.sp delete mode 100644 addons/sourcemod/scripting/include/lvl_ranks.inc delete mode 100644 addons/sourcemod/scripting/include/rank.inc delete mode 100644 addons/sourcemod/scripting/include/store.inc delete mode 100644 addons/sourcemod/scripting/include/wcs.inc delete mode 100644 addons/sourcemod/scripting/keys/cmds.sp delete mode 100644 addons/sourcemod/scripting/keys/utils.sp delete mode 100644 addons/sourcemod/translations/keys_shop_module.phrases.txt delete mode 100644 addons/sourcemod/translations/keys_store_module.phrases.txt delete mode 100644 addons/sourcemod/translations/keys_vip_module.phrases.txt delete mode 100644 addons/sourcemod/translations/keys_wcs_module.phrases.txt diff --git a/addons/sourcemod/scripting/Keys_Core.sp b/addons/sourcemod/scripting/Keys_Core.sp index c31fa45..97b1683 100644 --- a/addons/sourcemod/scripting/Keys_Core.sp +++ b/addons/sourcemod/scripting/Keys_Core.sp @@ -3,435 +3,220 @@ #include #include -public Plugin:myinfo = +#pragma newdecls required +#define PLUGIN_VERSION "2.0" + +public Plugin myinfo = { - name = "[Keys] Core", + name = "[Keys] Core", author = "R1KO", - version = "1.4", + version = PLUGIN_VERSION, url = "hlmod.ru" }; -#include "keys/vars.sp" -#include "keys/utils.sp" -#include "keys/api.sp" -#include "keys/cmds.sp" +#include "Keys/VARS.sp" +#include "Keys/UTIL.sp" +#include "Keys/KEYS.sp" +#include "Keys/API.sp" +#include "Keys/CMD.sp" +#include "Keys/EVENTS.sp" +#include "Keys/BLOCK.sp" +// #include "Keys/STATS.sp" -public OnPluginStart() +public void OnPluginStart() { LoadTranslations("keys_core.phrases"); g_bIsStarted = false; - g_iServerID = 0; BuildPath(Path_SM, SZF(g_sLogFile), "logs/Keys.log"); - g_hKeysTrie = CreateTrie(); - g_hKeysArray = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); + g_hKeysTrie = new StringMap(); + g_hKeysArray = new ArrayList(ByteCountToCells(KEYS_MAX_LENGTH)); + + CreateConVar("sm_keys_core_version", PLUGIN_VERSION, "KEYS-CORE VERSION", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_CHEAT|FCVAR_DONTRECORD); - new Handle:hCvar = CreateConVar("key_length", "32", "Длина генерируемого ключа (8-64)", _, true, 8.0, true, 64.0); - HookConVarChange(hCvar, OnKeyLengthChange); - g_CVAR_iKeyLength = GetConVarInt(hCvar); + ConVar hCvar = CreateConVar("key_length", "32", "Длина генерируемого ключа (8-64)", _, true, 8.0, true, 64.0); + hCvar.AddChangeHook(OnKeyLengthChange); + g_CVAR_iKeyLength = hCvar.IntValue; - hCvar = CreateConVar("key_template", "", "Шаблон для генерируемого ключа (Подробнее http://hlmod.ru/resources/keys-core.438/) + hCvar = CreateConVar("key_template", "", "Шаблон для генерируемого пароля (Подробнее http://hlmod.ru/resources/keys-core.438/)\n\ Пример: XXXX-XXXX-XXXX-XXXX", _, false, 0.0, true, 64.0); - HookConVarChange(hCvar, OnKeyTemplateChange); - GetConVarString(hCvar, SZF(g_CVAR_sKeyTemplate)); + hCvar.AddChangeHook(OnKeyTemplateChange); + hCvar.GetString(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_server_id", "0", "ID сервера (0 - Не использовать. 1 и больше - Использовать указанный)", _, true, 0.0); + hCvar.AddChangeHook(OnServerIDChange); + g_CVAR_iServerID = hCvar.IntValue; hCvar = CreateConVar("key_attempts", "3", "Количество попыток ввода ключа до получения блокировки (0 - Отключено)", _, true, 0.0); - HookConVarChange(hCvar, OnAttemptsChange); - g_CVAR_iAttempts = GetConVarInt(hCvar); + hCvar.AddChangeHook(OnAttemptsChange); + g_CVAR_iAttempts = hCvar.IntValue; - hCvar = CreateConVar("key_block_time", "60", "На сколько минут будет заблокирован игрок при вводе неверных ключей", _, true, 1.0); - HookConVarChange(hCvar, OnBlockTimeChange); - g_CVAR_iBlockTime = GetConVarInt(hCvar); + hCvar = CreateConVar("key_block_time", "60", "На сколько минут будет заблокирован игрок при вводе неверных ключей (0 - Навсегда)", _, true, 0.0); + hCvar.AddChangeHook(OnBlockTimeChange); + g_CVAR_iBlockTime = hCvar.IntValue; + +// Stats_OnPluginStart(); AutoExecConfig(true, "Keys_Core"); - RegAdminCmds(); + CMD_Reg(); 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_bDBMySQL && g_bIsStarted) - { - 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() +public void OnKeyLengthChange(ConVar hCvar, const char[] oldValue, const char[] newValue) { - new Handle:hTxn = SQL_CreateTransaction(); - - if (g_bDBMySQL) - { - SQL_AddQuery(hTxn, "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 default 0, \ - `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_AddQuery(hTxn, "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_AddQuery(hTxn, "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_AddQuery(hTxn, "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;"); - g_iServerID = -1; - } - else - { - SQL_AddQuery(hTxn, "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_AddQuery(hTxn, "CREATE TABLE IF NOT EXISTS `keys_blocked_players` (\ - `auth` VARCHAR(24) NOT NULL PRIMARY KEY, \ - `block_end` INTEGER UNSIGNED NOT NULL);"); - - SQL_AddQuery(hTxn, "CREATE TABLE IF NOT EXISTS `keys_players_used` (\ - `auth` VARCHAR(24) NOT NULL, \ - `key_name` VARCHAR(64) NOT NULL);"); - g_iServerID = 0; - } - - SQL_ExecuteTransaction(g_hDatabase, hTxn, SQL_Callback_TxnSuccess, SQL_Callback_TxnFailure, 0, DBPrio_High); + g_CVAR_iKeyLength = hCvar.IntValue; } -public SQL_Callback_TxnFailure(Handle:hDB, any:data, iNumQueries, const String:sError[], iFailIndex, any:queryData[]) +public void OnKeyTemplateChange(ConVar hCvar, const char[] oldValue, const char[] newValue) { - SetFailState("Не удалось создать таблицу (%i): %s", iFailIndex, sError); + hCvar.GetString(SZF(g_CVAR_sKeyTemplate)); } -public SQL_Callback_TxnSuccess(Handle:hDB, any:data, iNumQueries, Handle:hResults[], any:queryData[]) +public void OnServerIDChange(ConVar hCvar, const char[] oldValue, const char[] newValue) { - if(g_bDBMySQL) - { - SQL_SetCharset(g_hDatabase, "utf8"); - - if(g_iServerID == -1) - { - GetServerID(true); - return; - } - } - - Notify_Started(); + g_CVAR_iServerID = hCvar.IntValue; } -Notify_Started() +public void OnAttemptsChange(ConVar hCvar, const char[] oldValue, const char[] newValue) { - g_bIsStarted = true; - - CreateForward_OnCoreStarted(); - - DeleteExpiredKeys(); + g_CVAR_iAttempts = hCvar.IntValue; } -public OnConfigsExecuted() +public void OnBlockTimeChange(ConVar hCvar, const char[] oldValue, const char[] newValue) { - if(g_bIsStarted) - { - DeleteExpiredKeys(); - } + g_CVAR_iBlockTime = hCvar.IntValue; } -DeleteExpiredKeys() +void Connect_DB() { - decl String:sQuery[256]; - if(!g_iServerID) + if (SQL_CheckConfig("keys_core")) { - FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `expires` > 0 AND `expires` < %d;", GetTime()); + Database.Connect(DB_OnConnect, "keys_core", 1); } 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); + char szError[PMP]; + szError[0] = '\0'; + g_hDatabase = SQLite_UseDatabase("keys_core", SZF(szError)); + DB_OnConnect(g_hDatabase, szError, 0); } } -#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) +public void DB_OnConnect(Database hDatabase, const char[] szError, any data) { - if(g_CVAR_iServerID == -1) + if (hDatabase == null || szError[0]) { - 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); + SetFailState("Failed DB Connect %s", szError); return; } - g_iServerID = g_CVAR_iServerID; + g_hDatabase = hDatabase; - if(bNotifyStarted) - { - Notify_Started(); - } -} + char sDriver[16]; + g_hDatabase.Driver.GetIdentifier(SZF(sDriver)); -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; - } + g_bDBMySQL = (strcmp(sDriver, "mysql", false) == 0); - if(SQL_FetchRow(hResult)) + if (g_bDBMySQL) { - g_iServerID = SQL_FetchInt(hResult, 0); - - if(bNotifyStarted) - { - Notify_Started(); - } - return; + g_hDatabase.Query(SQL_Callback_ErrorCheck, "SET NAMES 'utf8'"); + g_hDatabase.Query(SQL_Callback_ErrorCheck, "SET CHARSET 'utf8'"); } - - 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); + + CreateTables(); } -public SQL_Callback_CreateServerID(Handle:hOwner, Handle:hResult, const String:sError[], any:bNotifyStarted) +void CreateTables() { - if (hResult == INVALID_HANDLE || sError[0]) - { - LogError("SQL_Callback_CreateServerID: %s", sError); - return; - } + Transaction hTxn = new Transaction(); - if(SQL_GetAffectedRows(hResult)) + if (g_bDBMySQL) { - g_iServerID = SQL_GetInsertId(g_hDatabase); - - if(bNotifyStarted) - { - Notify_Started(); - } + hTxn.AddQuery("CREATE TABLE IF NOT EXISTS `keys_tokens` (\ + `k_id` INT UNSIGNED NOT NULL AUTO_INCREMENT, \ + `k_name` VARCHAR(64) NOT NULL, \ + `k_type` VARCHAR(64) NOT NULL, \ + `k_expires` INT UNSIGNED NOT NULL default 0, \ + `k_uses` INT UNSIGNED NOT NULL default 1, \ + `k_sid` INT UNSIGNED NOT NULL default 0, \ + PRIMARY KEY (`k_id`), \ + UNIQUE (`k_name`)) DEFAULT CHARSET=utf8;"); + + hTxn.AddQuery("CREATE TABLE IF NOT EXISTS `keys_params` (\ + `p_kid` INT UNSIGNED NOT NULL, \ + `p_num` TINYINT UNSIGNED NOT NULL, \ + `p_value` VARCHAR(64) NOT NULL) DEFAULT CHARSET=utf8;"); + + hTxn.AddQuery("CREATE TABLE IF NOT EXISTS `keys_block_players` (\ + `b_auth` VARCHAR(24) NOT NULL, \ + `b_end` INT UNSIGNED NOT NULL, \ + `b_sid` INT UNSIGNED NOT NULL default 0, \ + PRIMARY KEY (`b_auth`)) DEFAULT CHARSET=utf8;"); + + hTxn.AddQuery("CREATE TABLE IF NOT EXISTS `keys_players_used` (\ + `u_auth` VARCHAR(24) NOT NULL, \ + `u_kid` INT UNSIGNED NOT NULL, \ + `u_sid` INT UNSIGNED NOT NULL default 0) DEFAULT CHARSET=utf8;"); } -} - -public Action:OnClientSayCommand(iClient, const String:sCommand[], const String:sArgs[]) -{ - if(StrContains(sArgs, "key") != -1) + else { - return Plugin_Handled; - } + hTxn.AddQuery("CREATE TABLE IF NOT EXISTS `keys_tokens` (\ + `k_id` INTEGER PRIMARY KEY AUTOINCREMENT, \ + `k_name` VARCHAR(64) NOT NULL UNIQUE, \ + `k_type` VARCHAR(64) NOT NULL, \ + `k_expires` INTEGER UNSIGNED NOT NULL default 0, \ + `k_uses` INTEGER UNSIGNED NOT NULL default 1);"); - return Plugin_Continue; -} + hTxn.AddQuery("CREATE TABLE IF NOT EXISTS `keys_params` (\ + `p_kid` INTEGER UNSIGNED NOT NULL, \ + `p_num` INTEGER UNSIGNED NOT NULL, \ + `p_value` VARCHAR(64) NOT NULL);"); -public OnClientDisconnect(iClient) -{ - g_iAttempts[iClient] = 0; - g_bIsBlocked[iClient] = false; -} + hTxn.AddQuery("CREATE TABLE IF NOT EXISTS `keys_block_players` (\ + `b_auth` VARCHAR(24) NOT NULL PRIMARY KEY, \ + `b_end` INTEGER UNSIGNED NOT NULL);"); -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)); + hTxn.AddQuery("CREATE TABLE IF NOT EXISTS `keys_players_used` (\ + `u_auth` VARCHAR(24) NOT NULL, \ + `u_kid` INTEGER UNSIGNED NOT NULL);"); } + + g_hDatabase.Execute(hTxn, SQL_Callback_CreateTablesSuccess, SQL_Callback_CreateTablesFailure, 0, DBPrio_High); } -public SQL_Callback_SearchPlayer(Handle:hOwner, Handle:hResult, const String:sError[], any:UserID) +public void SQL_Callback_CreateTablesFailure(Database hDB, any data, int iNumQueries, const char[] szError, int iFailIndex, any[] queryData) { - 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; - } - } + SetFailState("Не удалось создать таблицу (%d): %s", iFailIndex, szError); } -BlockClient(iClient) +public void SQL_Callback_CreateTablesSuccess(Database hDB, any data, int iNumQueries, DBResultSet[] hResults, any[] queryData) { - 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 + if(g_bDBMySQL) { - FormatEx(SZF(sQuery), "INSERT INTO `keys_blocked_players` (`auth`, `block_end`, `sid`) VALUES ('%s', %d, %d);", sAuth, g_iAttempts[iClient], g_iServerID); + g_hDatabase.SetCharset("utf8"); } - SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, sQuery); + Notify_Started(); } -UnBlockClient(iClient) +public void SQL_Callback_ErrorCheck(Database hDB, DBResultSet hResult, const char[] szError, any data) { - 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 + if (szError[0]) { - FormatEx(SZF(sQuery), "DELETE FROM `keys_blocked_players` WHERE `auth` = '%s' AND `sid` = %d;", sAuth, g_iServerID); + LogError("SQL_Callback_ErrorCheck: %s", szError); } - SQL_TQuery(g_hDatabase, SQL_Callback_ErrorCheck, sQuery); } -DeleteKey(const String:sKey[], iClient = -1, ReplySource:CmdReplySource = SM_REPLY_TO_CONSOLE) +void Notify_Started() { - decl String:sQuery[256], Handle:hDP; + g_bIsStarted = true; - hDP = CreateDataPack(); - WritePackString(hDP, sKey); - if(iClient == -1) - { - WritePackCell(hDP, false); - } - else - { - WritePackCell(hDP, true); - WritePackCell(hDP, GET_UID(iClient)); - WritePackCell(hDP, CmdReplySource); - } + API_CreateForward_OnCoreStarted(); - 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); + Keys_DeleteExpired(); } \ No newline at end of file diff --git a/addons/sourcemod/scripting/Keys_Example.sp b/addons/sourcemod/scripting/Keys_Example.sp new file mode 100644 index 0000000..a61114d --- /dev/null +++ b/addons/sourcemod/scripting/Keys_Example.sp @@ -0,0 +1,59 @@ +#pragma semicolon 1 + +#include +#include + +public Plugin:myinfo = +{ + name = "[Keys] Example", + author = "R1KO", + version = "1.1", + url = "hlmod.ru" +}; + +new const String:g_sKeyType[] = {"examle"}; + +public OnPluginStart() +{ + LoadTranslations("keys_core.phrases"); + + if (Keys_IsCoreStarted()) Keys_OnCoreStarted(); +} + +public OnPluginEnd() +{ + Keys_UnregKey(g_sKeyType); +} + +public Keys_OnCoreStarted() +{ + Keys_RegKey(g_sKeyType, OnKeyParamsValidate, OnKeyUse, OnKeyPrint); +} + +public bool:OnKeyParamsValidate(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) +{ + LogMessage("OnKeyParamsValidate: '%s'", sKeyType); + decl i, String:sParam[KEYS_MAX_LENGTH]; + for(i = 0; i < GetArraySize(hParamsArr); ++i) + { + GetArrayString(hParamsArr, i, sParam, sizeof(sParam)); + LogMessage("GetArrayString = '%s'", sParam); + } + + SetArrayString(hParamsArr, 1, "rep34"); + + return true; +} + +public bool:OnKeyUse(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) +{ + LogMessage("OnKeyUse: '%s'", sKeyType); + + return true; +} + +public OnKeyPrint(iClient, const String:sKeyType[], Handle:hParamsArr, String:sBuffer[], iBufLen) +{ + LogMessage("OnKeyPrint: '%s'", sKeyType); + FormatEx(sBuffer, iBufLen, "Example"); +} diff --git a/addons/sourcemod/scripting/Keys_Shop.sp b/addons/sourcemod/scripting/Keys_Shop.sp deleted file mode 100644 index 3320986..0000000 --- a/addons/sourcemod/scripting/Keys_Shop.sp +++ /dev/null @@ -1,218 +0,0 @@ -#pragma semicolon 1 - -#include -#include -#include - -public Plugin:myinfo = -{ - name = "[Keys] Shop", - author = "R1KO", - version = "1.1", - url = "hlmod.ru" -}; - -public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) -{ - MarkNativeAsOptional("Shop_GiveClientItem"); - - return APLRes_Success; -} - -new const String:g_sKeyType[][] = {"shop_credits", "shop_item"}; - -public OnPluginStart() -{ - LoadTranslations("keys_core.phrases"); - LoadTranslations("keys_shop_module.phrases"); - - if (Keys_IsCoreStarted()) Keys_OnCoreStarted(); -} - -public OnPluginEnd() -{ - Keys_UnregKey(g_sKeyType[0]); - Keys_UnregKey(g_sKeyType[1]); -} - -public Keys_OnCoreStarted() -{ - Keys_RegKey(g_sKeyType[0], OnKeyParamsValidate, OnKeyUse, OnKeyPrint); - Keys_RegKey(g_sKeyType[1], OnKeyParamsValidate, OnKeyUse, OnKeyPrint); -} - -public bool:OnKeyParamsValidate(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - if(!strcmp(sKeyType, g_sKeyType[0])) - { - if(GetArraySize(hParamsArr) != 1) - { - FormatEx(sError, iErrLen, "%T", "ERROR_NUM_ARGS", iClient); - return false; - } - - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(StringToInt(sParam) < 1) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_CREDITS", iClient); - return false; - } - - return true; - } - - new iSize = GetArraySize(hParamsArr); - if(!iSize) - { - FormatEx(sError, iErrLen, "%T", "ERROR_NUM_ARGS", iClient); - return false; - } - - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - - new CategoryId:iCatID = Shop_GetCategoryId(sParam); - if(iCatID == INVALID_CATEGORY) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_CATEGORY", iClient); - return false; - } - - if(iSize > 1) - { - GetArrayString(hParamsArr, 1, sParam, sizeof(sParam)); - new ItemId:iItemID = Shop_GetItemId(iCatID, sParam); - if(iItemID == INVALID_ITEM) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_ITEM", iClient); - return false; - } - } - - return true; -} - -GiveClientItem(iClient, ItemId:iItemID) -{ - if(CanTestFeatures() && GetFeatureStatus(FeatureType_Native, "Shop_GiveClientItem") == FeatureStatus_Available) - { - Shop_GiveClientItem(iClient, iItemID); - if(strcmp(SHOP_VERSION, "2.0.24") != 0) - { - Shop_SetClientItemTimeleft(iClient, iItemID, Shop_GetItemValue(iItemID)); - } - } - else - { - new iPrice = Shop_GetItemPrice(iItemID); - Shop_GiveClientCredits(iClient, iPrice, IGNORE_FORWARD_HOOK); - Shop_BuyClientItem(iClient, iItemID); - } -} - -public bool:OnKeyUse(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(!strcmp(sKeyType, g_sKeyType[0])) - { - Shop_GiveClientCredits(iClient, StringToInt(sParam), IGNORE_FORWARD_HOOK); - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "YOU_RECEIVED_CREDITS", StringToInt(sParam)); - return true; - } - - new CategoryId:iCatID = Shop_GetCategoryId(sParam); - if(iCatID == INVALID_CATEGORY) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_CATEGORY", iClient); - return false; - } - - if(GetArraySize(hParamsArr) > 1) - { - decl String:sItem[SHOP_MAX_STRING_LENGTH]; - GetArrayString(hParamsArr, 1, sItem, sizeof(sItem)); - new ItemId:iItemID = Shop_GetItemId(iCatID, sItem); - if(iItemID < ItemId:1) // if(iItemID == INVALID_ITEM) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_ITEM", iClient); - return false; - } - - switch(Shop_GetItemType(iItemID)) - { - case Item_Finite, Item_BuyOnly: - { - GiveClientItem(iClient, iItemID); - } - case Item_Togglable: - { - if(Shop_IsClientHasItem(iClient, iItemID)) - { - Shop_SetClientItemTimeleft(iClient, iItemID, Shop_GetClientItemTimeleft(iClient, iItemID)+Shop_GetItemValue(iItemID)); - } - else - { - GiveClientItem(iClient, iItemID); - } - } - } - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "YOU_RECEIVED_ITEM_FROM_CATEGORY", sItem, sParam); - - return true; - } - - decl ItemId:iItemID, i, iSize, Handle:hArray; - hArray = Shop_CreateArrayOfItems(iSize); - for (i = 0; i < iSize; ++i) - { - iItemID = Shop_GetArrayItem(hArray, i); - if(Shop_GetItemCategoryId(iItemID) == iCatID) - { - switch(Shop_GetItemType(iItemID)) - { - case Item_Finite, Item_BuyOnly: - { - GiveClientItem(iClient, iItemID); - } - case Item_Togglable: - { - if(Shop_IsClientHasItem(iClient, iItemID)) - { - Shop_SetClientItemTimeleft(iClient, iItemID, Shop_GetClientItemTimeleft(iClient, iItemID)+Shop_GetItemValue(iItemID)); - } - else - { - GiveClientItem(iClient, iItemID); - } - } - } - } - } - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "YOU_RECEIVED_ALL_ITEMS_FROM_CATEGORY", sParam); - - return true; -} - -public OnKeyPrint(iClient, const String:sKeyType[], Handle:hParamsArr, String:sBuffer[], iBufLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(!strcmp(sKeyType, g_sKeyType[0])) - { - FormatEx(sBuffer, iBufLen, "%T: %s", "CREDITS", iClient, sParam); - return; - } - - FormatEx(sBuffer, iBufLen, "%T: %s", "CATEGORY", iClient, sParam); - - if(GetArraySize(hParamsArr) > 1) - { - GetArrayString(hParamsArr, 1, sParam, sizeof(sParam)); - Format(sBuffer, iBufLen, "%s, %T: %s", sBuffer, "ITEM", iClient, sParam); - } - else - { - Format(sBuffer, iBufLen, "%s, %T: %T", sBuffer, "ITEM", iClient, "ALL", iClient); - } -} diff --git a/addons/sourcemod/scripting/Keys_Store.sp b/addons/sourcemod/scripting/Keys_Store.sp deleted file mode 100644 index d74b050..0000000 --- a/addons/sourcemod/scripting/Keys_Store.sp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma semicolon 1 - -#include -#include -#include - -public Plugin:myinfo = -{ - name = "[Keys] Store (Zephyrus)", - author = "R1KO", - version = "1.0", - url = "hlmod.ru" -}; - -new const String:g_sKeyType[] = "store_credits"; - -public OnPluginStart() -{ - LoadTranslations("keys_core.phrases"); - LoadTranslations("keys_store_module.phrases"); - - if (Keys_IsCoreStarted()) Keys_OnCoreStarted(); -} - -public OnPluginEnd() -{ - Keys_UnregKey(g_sKeyType); -} - -public Keys_OnCoreStarted() -{ - Keys_RegKey(g_sKeyType, OnKeyParamsValidate, OnKeyUse, OnKeyPrint); -} - -public bool:OnKeyParamsValidate(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - if(GetArraySize(hParamsArr) != 1) - { - FormatEx(sError, iErrLen, "%T", "ERROR_NUM_ARGS", iClient); - return false; - } - - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(StringToInt(sParam) < 1) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_CREDITS", iClient); - return false; - } - - return true; -} - -public bool:OnKeyUse(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - Store_SetClientCredits(iClient, Store_GetClientCredits(iClient)+StringToInt(sParam)); - PrintToChat(iClient, "%t", "YOU_RECEIVED_CREDITS", StringToInt(sParam)); - return true; -} - -public OnKeyPrint(iClient, const String:sKeyType[], Handle:hParamsArr, String:sBuffer[], iBufLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - FormatEx(sBuffer, iBufLen, "%T: %s", "CREDITS", iClient, sParam); -} diff --git a/addons/sourcemod/scripting/Keys_VIP.sp b/addons/sourcemod/scripting/Keys_VIP.sp deleted file mode 100644 index cdfa29e..0000000 --- a/addons/sourcemod/scripting/Keys_VIP.sp +++ /dev/null @@ -1,332 +0,0 @@ -#pragma semicolon 1 - -#include -#include -#include - -public Plugin:myinfo = -{ - name = "[Keys] VIP", - author = "R1KO", - version = "1.2", - url = "hlmod.ru" -}; - -#define EXT_STATUS 1 // Разрешить ли ключам типа vip_add продлевать VIP-статус -#define GC_STATUS 0 // Разрешить ли ключам типа vip_add изменять VIP-группу (работает только если включен EXT_STATUS) -#define CMP_VGRP 1 // Ключ типа vip_add может продлевать VIP-статус только если VIP-группа совпадает (работает только если включен EXT_STATUS) - // Если включено - отключает GC_STATUS - -#define USE_VIP_V3 1 // Для компиляции под ядро 3.0 - -new const String:g_sKeyType[][] = {"vip_add", "vip_ext", "vip_gc"}; - -public OnPluginStart() -{ - LoadTranslations("keys_core.phrases"); - LoadTranslations("keys_vip_module.phrases"); - - if (Keys_IsCoreStarted()) Keys_OnCoreStarted(); -} - -public OnPluginEnd() -{ - for(new i = 0; i < sizeof(g_sKeyType); ++i) - { - Keys_UnregKey(g_sKeyType[i]); - } -} - -public Keys_OnCoreStarted() -{ - for(new i = 0; i < sizeof(g_sKeyType); ++i) - { - Keys_RegKey(g_sKeyType[i], OnKeyParamsValidate, OnKeyUse, OnKeyPrint); - } -} - -public bool:OnKeyParamsValidate(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - if(!strcmp(sKeyType, g_sKeyType[0])) - { - if(GetArraySize(hParamsArr) != 2) - { - FormatEx(sError, iErrLen, "%T", "ERROR_NUM_ARGS", iClient); - return false; - } - - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(!VIP_IsValidVIPGroup(sParam)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_GROUP", iClient); - return false; - } - - GetArrayString(hParamsArr, 1, sParam, sizeof(sParam)); - new iTime = StringToInt(sParam); - if(iTime < 0) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_TIME", iClient); - return false; - } - - IntToString(VIP_TimeToSeconds(iTime), sParam, sizeof(sParam)); - SetArrayString(hParamsArr, 1, sParam); - - return true; - } - - if(GetArraySize(hParamsArr) != 1) - { - FormatEx(sError, iErrLen, "%T", "ERROR_NUM_ARGS", iClient); - return false; - } - - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - - if(!strcmp(sKeyType, g_sKeyType[1])) - { - new iTime = StringToInt(sParam); - if(iTime < 0) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_TIME", iClient); - return false; - } - - IntToString(VIP_TimeToSeconds(iTime), sParam, sizeof(sParam)); - SetArrayString(hParamsArr, 0, sParam); - - return true; - } - - if(!VIP_IsValidVIPGroup(sParam)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_GROUP", iClient); - return false; - } - - return true; -} - -#if CMP_VGRP == 1 && GC_STATUS == 1 -#undef GC_STATUS -#define GC_STATUS 0 -#endif - -#if USE_VIP_V3 == 0 -public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) -{ - MarkNativeAsOptional("VIP_GetClientID"); - - return APLRes_Success; -} -#endif - -public bool:OnKeyUse(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - new iClientID = -1; - new bool:bVip = VIP_IsClientVIP(iClient); - if(bVip) - { - #if USE_VIP_V3 == 1 - iClientID = VIP_GetClientID(iClient); - #else - if(CanTestFeatures() && GetFeatureStatus(FeatureType_Native, "VIP_GetClientID") == FeatureStatus_Available) - { - iClientID = VIP_GetClientID(iClient); - } - else - { - GetTrieValue(VIP_GetVIPClientTrie(iClient), "ClientID", iClientID); - } - #endif - } - - if(!strcmp(sKeyType, g_sKeyType[0])) - { - if(bVip) - { - // && VIP_GetClientAuthType(iClient) < VIP_AuthType:3 - if(iClientID != -1) - { - #if EXT_STATUS == 1 - new iClientTime = VIP_GetClientAccessTime(iClient); - if(!iClientTime) - { - FormatEx(sError, iErrLen, "%T", "ERROR_CAN_NOT_USE", iClient); - return false; - } - - #if GC_STATUS == 1 || CMP_VGRP == 1 - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(VIP_IsValidVIPGroup(sParam)) - { - decl String:sClientGroup[64]; - VIP_GetClientVIPGroup(iClient, sClientGroup, sizeof(sClientGroup)); - if(strcmp(sClientGroup, sParam) != 0) - { - #if CMP_VGRP == 1 - FormatEx(sError, iErrLen, "%T", "ERROR_CAN_NOT_USE", iClient); - return false; - #else - VIP_SetClientVIPGroup(iClient, sParam, true); - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GRP_CNG", sParam); - #endif - } - #if CMP_VGRP == 1 - VIP_SetClientVIPGroup(iClient, sParam, true); - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GRP_CNG", sParam); - #endif - } - #endif - - GetArrayString(hParamsArr, 1, sParam, sizeof(sParam)); - new iTime = StringToInt(sParam); - if(iTime) - { - Keys_GetTimeFromStamp(sParam, sizeof(sParam), iTime, iClient); - iTime += iClientTime; - } - else - { - FormatEx(sParam, sizeof(sParam), "%T", "FOREVER", iClient); - } - - VIP_SetClientAccessTime(iClient, iTime, true); - - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_EXT", sParam); - - return true; - #else - FormatEx(sError, iErrLen, "%T", "ERROR_VIP_ALREADY", iClient); - return false; - #endif - } - } - - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(!VIP_IsValidVIPGroup(sParam)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_GROUP", iClient); - return false; - } - - if(bVip && iClientID == -1) - { - VIP_RemoveClientVIP(iClient, false, false); - } - - decl String:sTime[64], iTime; - GetArrayString(hParamsArr, 1, sTime, sizeof(sTime)); - iTime = StringToInt(sTime); - #if USE_VIP_V3 == 1 - VIP_SetClientVIP(0, iClient, iTime, sParam, true); - #else - VIP_SetClientVIP(iClient, iTime, AUTH_STEAM, sParam, true); - #endif - - if(iTime) - { - Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); - } - else - { - FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); - } - - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GOT", sParam, sTime); - return true; - } - - if(!bVip || (bVip && iClientID == -1)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_CAN_NOT_USE", iClient); - return false; - } - - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - - if(!strcmp(sKeyType, g_sKeyType[1])) - { - new iTime = StringToInt(sParam); - VIP_SetClientAccessTime(iClient, iTime ? (VIP_GetClientAccessTime(iClient)+iTime):iTime, true); - - if(iTime) - { - Keys_GetTimeFromStamp(sParam, sizeof(sParam), iTime, iClient); - } - else - { - FormatEx(sParam, sizeof(sParam), "%T", "FOREVER", iClient); - } - - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_EXT", sParam); - - return true; - } - - if(!VIP_IsValidVIPGroup(sParam)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_GROUP", iClient); - return false; - } - - decl String:sClientGroup[64]; - VIP_GetClientVIPGroup(iClient, sClientGroup, sizeof(sClientGroup)); - if(!strcmp(sClientGroup, sParam)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_ALREADY_VIP_GROUP", iClient); - return false; - } - - VIP_SetClientVIPGroup(iClient, sParam, true); - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GRP_CNG", sParam); - - return true; -} - -public OnKeyPrint(iClient, const String:sKeyType[], Handle:hParamsArr, String:sBuffer[], iBufLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(!strcmp(sKeyType, g_sKeyType[0])) - { - decl iTime, String:sTime[32]; - GetArrayString(hParamsArr, 1, sTime, sizeof(sTime)); - iTime = StringToInt(sTime); - if(iTime) - { - Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); - } - else - { - FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); - } - - FormatEx(sBuffer, iBufLen, "%T: %s\t%T: %s", "VIP_GROUP", iClient, sParam, "TERM", iClient, sTime); - - return; - } - - if(!strcmp(sKeyType, g_sKeyType[1])) - { - decl iTime, String:sTime[32]; - iTime = StringToInt(sParam); - if(iTime) - { - Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); - } - else - { - FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); - } - - FormatEx(sBuffer, iBufLen, "%T: %s", "TERM", iClient, sTime); - - return; - } - - FormatEx(sBuffer, iBufLen, "%T: %s", "VIP_GROUP", iClient, sParam); -} diff --git a/addons/sourcemod/scripting/Keys_WCS.sp b/addons/sourcemod/scripting/Keys_WCS.sp deleted file mode 100644 index f13c0a7..0000000 --- a/addons/sourcemod/scripting/Keys_WCS.sp +++ /dev/null @@ -1,137 +0,0 @@ -#pragma semicolon 1 - -#include -#include -#include - -public Plugin:myinfo = -{ - name = "[Keys] WCS", - author = "R1KO", - version = "1.0", - url = "hlmod.ru" -}; - -new const String:g_sKeyType[][] = {"wcs_gold", "wcs_p_race", "wcs_bank_lvl"}; - -public OnPluginStart() -{ - LoadTranslations("keys_core.phrases"); - LoadTranslations("keys_wcs_module.phrases"); - - if (Keys_IsCoreStarted()) Keys_OnCoreStarted(); -} - -public OnPluginEnd() -{ - for(new i = 0; i < sizeof(g_sKeyType); ++i) - { - Keys_UnregKey(g_sKeyType[i]); - } -} - -public Keys_OnCoreStarted() -{ - for(new i = 0; i < sizeof(g_sKeyType); ++i) - { - Keys_RegKey(g_sKeyType[i], OnKeyParamsValidate, OnKeyUse, OnKeyPrint); - } -} - -public bool:OnKeyParamsValidate(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - if(GetArraySize(hParamsArr) != 1) - { - FormatEx(sError, iErrLen, "%T", "ERROR_NUM_ARGS", iClient); - return false; - } - - decl String:sParam[KEYS_MAX_LENGTH]; - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - - if(!strcmp(sKeyType, g_sKeyType[1])) - { - if(!WCS_IsRacePrivate(sParam)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_RACE", iClient); - return false; - } - - return true; - } - - if(StringToInt(sParam) < 1) - { - FormatEx(sError, iErrLen, "%T", !strcmp(sKeyType, g_sKeyType[0]) ? "ERROR_INVALID_AMONUT":"ERROR_INVALID_LVL", iClient); - return false; - } - - return true; -} - -public bool:OnKeyUse(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - - if(!strcmp(sKeyType, g_sKeyType[0])) - { - if(!WCS_GiveGold(iClient, StringToInt(sParam))) - { - FormatEx(sError, iErrLen, "%T", "ERROR_HAS_OCCURRED", iClient); - return false; - } - - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "YOU_RECEIVED_GOLD", StringToInt(sParam)); - return true; - } - - if(!strcmp(sKeyType, g_sKeyType[2])) - { - if(!WCS_GiveLBlvl(iClient, StringToInt(sParam))) - { - FormatEx(sError, iErrLen, "%T", "ERROR_HAS_OCCURRED", iClient); - return false; - } - - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "YOU_RECEIVED_BLVL", StringToInt(sParam)); - return true; - } - - if(!WCS_IsRacePrivate(sParam)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_INVALID_RACE", iClient); - return false; - } - - decl String:sAuth[32]; - GetClientAuthId(iClient, AuthId_Steam2, sAuth, sizeof(sAuth)); - if(!WCS_GivePrivateRace(sAuth, sParam)) - { - FormatEx(sError, iErrLen, "%T", "ERROR_HAS_OCCURRED", iClient); - return false; - } - - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "YOU_RECEIVED_PRIVATE_RACE", sParam); - - return true; -} - -public OnKeyPrint(iClient, const String:sKeyType[], Handle:hParamsArr, String:sBuffer[], iBufLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(!strcmp(sKeyType, g_sKeyType[0])) - { - FormatEx(sBuffer, iBufLen, "%T: %s", "GOLD", iClient, sParam); - return; - } - - if(!strcmp(sKeyType, g_sKeyType[1])) - { - FormatEx(sBuffer, iBufLen, "%T: %s", "PRIVATE_RACE", iClient, sParam); - return; - } - - FormatEx(sBuffer, iBufLen, "%T: %s", "BLVL", iClient, sParam); -} diff --git a/addons/sourcemod/scripting/include/keys_core.inc b/addons/sourcemod/scripting/include/keys_core.inc index da5024f..dff77ee 100644 --- a/addons/sourcemod/scripting/include/keys_core.inc +++ b/addons/sourcemod/scripting/include/keys_core.inc @@ -5,83 +5,85 @@ #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); - -// Прототип вызова при проверке существования ключа -functag public KeyCheckValidKeyCallback(const String:sKey[], bool:bKeyExists); +enum KeysAction +{ + Validation = 0, + Activation, + Print +}; -// Прототип вызова при получении данных ключа -functag public KeyGetDataCallback(const String:sKey[], bool:bKeyExists, const String:sKeyType[], iUses, iExpires, Handle:hParamsArr); +enum KeysNativeAction +{ + Add = 0, + Rem, + Use +}; -// Прототип вызова при добавлении ключа -functag public KeyAddCallback(iClient, const String:sKey[], bool:bSuccess, const String:sError[]); +// Прототип вызова при валидации параметров/акцивации/выводе параметров ключа +typedef KeyActionCallback = function bool (KeysAction eKeysAction, int iClient, const char[] szKeyType, ArrayList hParamsArr, char[] szBuffer, int iBuffLen); -// Прототип вызова при использоании ключа игроком -functag public KeyUseCallback(iClient, const String:sKey[], bool:bSuccess, String:sError[]); +typeset KeyInfoCallback +{ + // Прототип вызова при проверке существования ключа + function void (const char[] sKey, bool bKeyExists, any iData); + + // Прототип вызова при получении данных ключа + function void (const char[] sKey, bool bKeyExists, const char[] sKeyType, int iUses, int iExpires, ArrayList hParamsArr, any iData); +}; -// Прототип вызова при удалении ключа -functag public KeyRemoveCallback(const String:sKey[], bool:bSuccess); +// Прототип вызова при добавлении/удалении/использовании ключа +typedef KeyNativeActionCallback = function void (KeysNativeAction eKeysAction, int iClient, const char[] sKey, bool bSuccess, const char[] sError, any iData); // Вызывается когда ядро было загружено -forward Keys_OnCoreStarted(); +forward void Keys_OnCoreStarted(); // Загружено ли ядро -native bool:Keys_IsCoreStarted(); +native bool Keys_IsCoreStarted(); // Получает Handle базы данных -native Handle:Keys_GetCoreDatabase(); +native Database Keys_GetCoreDatabase(); // Получает тип базы данных (false - SQLite, true - MySQL) -native bool:Keys_GetDatabaseType(); +native bool Keys_GetDatabaseType(); // Регистрирует тип ключей -native bool:Keys_RegKey(const String:sKeyType[], - KeyParamsValidateCallback:OnKeyParamsValidate, - KeyUseCallback:OnKeyUse, - KeyPrintCallback:OnKeyPrint); +native bool Keys_RegKey(const char[] sKeyType, KeyActionCallback fCallback); // Разрегистрирует тип ключей -native Keys_UnregKey(const String:sKeyType[]); +native void Keys_UnregKey(const char[] sKeyType); // Проверяет существование типа ключей -native bool:Keys_IsValidKeyType(const String:sKeyType[]); +native bool Keys_IsValidKeyType(const char[] sKeyType); // Получает adt_array со всеми типами ключей (нужно закрывать Handle) -native Handle:Keys_FillArrayByKeyTypes(); +native ArrayList Keys_FillArrayByKeyTypes(); // Проверяет существование ключа -native Keys_IsValidKey(const String:sKey[], KeyCheckValidKeyCallback:IsValidKeyCallback); +native void Keys_IsValidKey(const char[] sKey, KeyInfoCallback IsValidKeyCallback, any iData = 0); // Получает данные ключа -native Keys_GetKeyData(const String:sKey[], KeyGetDataCallback:GetKeyDataCallback); +native void Keys_GetKeyData(const char[] sKey, KeyInfoCallback GetKeyDataCallback, any iData = 0); // Генерирует ключ -native Keys_GenerateKey(String:sBuffer[], iBufLen, const String:sTemplate[] = ""); +native void Keys_GenerateKey(char[] sBuffer, iBufLen, const char[] sTemplate = NULL_STRING); // Добавляет ключ -native Keys_AddKey(iClient = 0, const String:sKey[] = "", const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyAddCallback:AddKeyCallback); +native void Keys_AddKey(int iClient = 0, const char[] sKey = NULL_STRING, const char[] sKeyType, int iUses, iLifeTime, ArrayList hParamsArr, KeyNativeActionCallback AddKeyCallback, any iData = 0); // Удаляет ключ -native Keys_RemoveKey(const String:sKey[], KeyRemoveCallback:RemoveKeyCallback); +native void Keys_RemoveKey(int iClient = 0, const char[] sKey, KeyNativeActionCallback RemoveKeyCallback, any iData = 0); // Использует ключ игроком -native Keys_UseKey(iClient, const String:sKey[], bool:bNotify, KeyUseCallback:UseKeyCallback); - -// Для использования не забыть: +native void Keys_UseKey(int iClient, const char[] sKey, bool bNotify, bool bIgnoreBlock, KeyNativeActionCallback UseKeyCallback, any iData = 0); + +// Для использования не забыть // LoadTranslations("keys_core.phrases"); -stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = LANG_SERVER) +stock void Keys_GetTimeFromStamp(char[] sBuffer, int iMaxLength, int iTimeStamp, int iClient = LANG_SERVER) { if (iTimeStamp > 31536000) { - new iYears = iTimeStamp / 31536000; - new i = iTimeStamp - (iYears*31536000); + int iYears = iTimeStamp / 31536000; + int i = iTimeStamp - (iYears*31536000); if(i > 2592000) { FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iYears, "YEARS", iClient, i / 2592000, "MONTHS", iClient); @@ -95,8 +97,8 @@ stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = if (iTimeStamp > 2592000) { - new iMonths = iTimeStamp / 2592000; - new i = iTimeStamp - (iMonths*2592000); + int iMonths = iTimeStamp / 2592000; + int i = iTimeStamp - (iMonths*2592000); if (i > 86400) { FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iMonths, "MONTHS", iClient, i / 86400, "DAYS", iClient); @@ -110,8 +112,8 @@ stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = if (iTimeStamp > 86400) { - new iDays = iTimeStamp / 86400 % 365; - new iHours = (iTimeStamp / 3600) % 24; + int iDays = iTimeStamp / 86400 % 365; + int iHours = (iTimeStamp / 3600) % 24; if (iHours > 0) { FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iDays, "DAYS", iClient, iHours, "HOURS", iClient); @@ -123,23 +125,23 @@ stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = return; } - new iHours = (iTimeStamp / 3600); - new iMins = (iTimeStamp / 60) % 60; - new iSecs = iTimeStamp % 60; + int iHours = (iTimeStamp / 3600); + int iMins = (iTimeStamp / 60) % 60; + int iSecs = iTimeStamp % 60; if (iHours > 0) { - FormatEx(sBuffer, iMaxLength, "%02d:%02d:%02d", iHours, iMins, iSecs); + FormatEx(sBuffer, iMaxLength, "%02d %02d %02d", iHours, iMins, iSecs); } else { - FormatEx(sBuffer, iMaxLength, "%02d:%02d", iMins, iSecs); + FormatEx(sBuffer, iMaxLength, "%02d %02d", iMins, iSecs); } } -public SharedPlugin:__pl_keys_core= +public SharedPlugin __pl_keys_core= { - name = "keys_core", + name = "keys_core_v2", file = "Keys_Core.smx", #if defined REQUIRE_PLUGIN required = 1 diff --git a/addons/sourcemod/scripting/include/lvl_ranks.inc b/addons/sourcemod/scripting/include/lvl_ranks.inc deleted file mode 100644 index fbdfe82..0000000 --- a/addons/sourcemod/scripting/include/lvl_ranks.inc +++ /dev/null @@ -1,256 +0,0 @@ -#if defined _levelsranks_included_ - #endinput -#endif -#define _levelsranks_included_ - -#define PLUGIN_VERSION "v2.3.0" - -#define ST_VALUE 0 -#define ST_RANK 1 -#define ST_KILLS 2 -#define ST_DEATHS 3 -#define ST_SHOOTS 4 -#define ST_HITS 5 -#define ST_HEADSHOTS 6 -#define ST_ASSISTS 7 - -int g_iColorsOther[] = {0xFFFFFF, 0xFF0000, 0x00AD00, 0x00FF00, 0x99FF99, 0xFF4040, 0xCCCCCC, 0xFFBD6B, 0xFA8B00, 0x99CCFF, 0x3D46FF, 0xFA00FA}; -char g_sColors[][] = {"{WHITE}", "{RED}", "{GREEN}", "{LIME}", "{LIGHTGREEN}", "{LIGHTRED}", "{GRAY}", "{LIGHTOLIVE}", "{OLIVE}", "{LIGHTBLUE}", "{BLUE}", "{PURPLE}"}; -char g_sColorsCSGO[][] = {"\x01", "\x02", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x10", "\x0B", "\x0C", "\x0E"}; - -/** - * Checks if enough players are in this round to activate statistics - * - * @return bool - if true - yes, otherwise - no - */ -native bool LR_CheckCountPlayers(); - -/** - * Returns ID of statistics type - * - * @return int - ID of stastics type - */ -native int LR_GetTypeStatistics(); - -/** - * Gets the client's place in the TOP - * - * @param iClient - Client index - * @return int - Client position in TOP - */ -native int LR_GetClientPos(int iClient); - -/** - * Получает подробную статистику об игроке - * - * @param iClient - Client index - * @param iStats ID получаемых данных (Example: iStats = ST_VALUE). - * @return int Значение выбранных данных - */ -native int LR_GetClientInfo(int iClient, int iStats); - -/** - * Изменяет кол-во расчетных единиц у игрока - * - * @param iClient Индекс клиента. - * @param iAmount Кол-во расчетных единиц (очки опыта/счетчик времени) - * @return int Новое значение кол-ва расчетных единиц. - */ -native int LR_ChangeClientValue(int iClient, int iAmount); - -/** - * Принудительно выставляет очки опыта клиенту (only for lr_type_statistics 2) - * - * @param iClient Индекс клиента. - * @param iAmount Кол-во очков опыта - * @return bool Если true - выдача прошла успешно, иначе false. - */ -native bool LR_SetClientValue(int iClient, int iAmount); - -/** - * Вызывает меню инвентаря (используется модулями для возможности откатиться назад по Менюшке) - * - * @param iClient Индекс клиента. - * @noreturn - */ -native void LR_MenuInventory(int iClient); - -/** - * Проверяет на валидность VIP-группу - * - * @param sGroup Проверяемое название VIP-группы - * @return bool Если true - VIP-группа валидна, иначе false. - */ -native bool LR_IsValidGroupVIP(const char[] sGroup); - -/** - * Проверяет VIP-статус клиента - * - * @param iClient Индекс клиента. - * @return bool Если true - клиент имеет статус VIP, иначе false. - */ -native bool LR_IsClientVIP(int iClient); - -/** - * Выдает VIP-статус клиенту - * - * @param iClient Индекс клиента. - * @param iTime Время в Unix TimeStamp, до которого у клиента будет VIP-статус. - * @param sGroup Наименование присуждаемой группы - * @return bool Если true - VIP выдан, иначе false. - */ -native bool LR_SetClientVIP(int iClient, int iTime, const char[] sGroup); - -/** - * Возвращает информацию о VIP-статусе клиента - * - * @param iClient Индекс клиента. - * @return DataPack Если Валидный (1 - время окончания VIP-статуса, 2 - наименование VIP-группы) - */ -native DataPack LR_GetClientInfoVIP(int iClient); - -/** - * Изменяет параметры VIP-статуса клиента - * - * @param iClient Индекс клиента. - * @param sGroup Наименование группы , на которую вы хотите сменить (если пустое название - группа не меняется) - * @param iTime Время в UnixTime до которого будет VIP-статус (-1 - не изменять, 0 - навсегда) - * @return bool Если true - VIP изменен, иначе false. - */ -native bool LR_ChangeClientVIP(int iClient, const char[] sGroup = "", int iTime = -1); - -/** - * Удаляет VIP-статус клиента - * - * @param iClient Индекс клиента. - * @return bool Если true - VIP удален, иначе false. - */ -native bool LR_DeleteClientVIP(int iClient); - -/** - * Called when a list opens Inventory - * - * @noreturn - */ -forward void LR_OnMenuCreated(int iClient, int iRank, Menu& hMenu); - -/** - * Called when a list opens Inventory - * - * @noreturn - */ -forward void LR_OnMenuItemSelected(int iClient, int iRank, const char[] sInfo); - -/** - * Вызывается при изменении уровня игроком - * - * @param iClient ID игрока - * @param iNewLevel Новый уровень - * @param bUp Если true - то уровень поднялся, false - упал. - */ -forward void LR_OnLevelChanged(int iClient, int iNewLevel, bool bUp); - -stock bool IsValidClient(int iClient) -{ - return (1 <= iClient <= MaxClients && IsClientInGame(iClient)) ? true : false; -} - -stock void LR_PrintToChat(int iClient, char[] szMessage, any ...) -{ - if(IsValidClient(iClient) && !IsFakeClient(iClient)) - { - SetGlobalTransTarget(iClient); - char szBuffer[PLATFORM_MAX_PATH], szNewMessage[PLATFORM_MAX_PATH]; - - switch(GetEngineVersion()) - { - case Engine_CSGO: - { - Format(szBuffer, sizeof(szBuffer), " \x01%s", szMessage); - VFormat(szNewMessage, sizeof(szNewMessage), szBuffer, 3); - - for(int i = 0; i < 12; i++) - { - ReplaceString(szNewMessage, sizeof(szNewMessage), g_sColors[i], g_sColorsCSGO[i]); - } - ReplaceString(szNewMessage, sizeof(szNewMessage), "{TEAM}", "\x03"); - } - - case Engine_CSS, Engine_TF2: - { - char sBuff[64]; - Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); - VFormat(szNewMessage, sizeof(szNewMessage), szBuffer, 3); - - switch(GetClientTeam(iClient)) - { - case 1: Format(sBuff, sizeof(sBuff), "\x07%06X", g_iColorsOther[6]); - case 2: Format(sBuff, sizeof(sBuff), "\x07%06X", g_iColorsOther[5]); - case 3: Format(sBuff, sizeof(sBuff), "\x07%06X", g_iColorsOther[9]); - } - ReplaceString(szNewMessage, sizeof(szNewMessage), "{TEAM}", sBuff); - - for(int i = 0; i < 12; i++) - { - Format(sBuff, sizeof(sBuff), "\x07%06X", g_iColorsOther[i]); - ReplaceString(szNewMessage, sizeof(szNewMessage), g_sColors[i], sBuff); - } - } - } - - Handle hBf = StartMessageOne("SayText2", iClient, USERMSG_RELIABLE | USERMSG_BLOCKHOOKS); - if(hBf != null) - { - if(GetUserMessageType() == UM_Protobuf) - { - Protobuf hProtoBuffer = UserMessageToProtobuf(hBf); - hProtoBuffer.SetInt("ent_idx", iClient); - hProtoBuffer.SetBool("chat", true); - hProtoBuffer.SetString("msg_name", szNewMessage); - hProtoBuffer.AddString("params", ""); - hProtoBuffer.AddString("params", ""); - hProtoBuffer.AddString("params", ""); - hProtoBuffer.AddString("params", ""); - } - else - { - BfWrite hBfBuffer = UserMessageToBfWrite(hBf); - hBfBuffer.WriteByte(iClient); - hBfBuffer.WriteByte(true); - hBfBuffer.WriteString(szNewMessage); - } - } - EndMessage(); - } -} - -public SharedPlugin __pl_levelsranks = -{ - name = "levelsranks", - file = "levelsranks.smx", - - #if defined REQUIRE_PLUGIN - required = 1, - #else - required = 0, - #endif -}; - -#if !defined REQUIRE_PLUGIN -public __pl_levelsranks_SetNTVOptional() -{ - MarkNativeAsOptional("LR_CheckCountPlayers"); - MarkNativeAsOptional("LR_GetTypeStatistics"); - MarkNativeAsOptional("LR_GetClientPos"); - MarkNativeAsOptional("LR_GetClientInfo"); - MarkNativeAsOptional("LR_ChangeClientValue"); - MarkNativeAsOptional("LR_SetClientValue"); - MarkNativeAsOptional("LR_MenuInventory"); - MarkNativeAsOptional("LR_IsValidGroupVIP"); - MarkNativeAsOptional("LR_IsClientVIP"); - MarkNativeAsOptional("LR_SetClientVIP"); - MarkNativeAsOptional("LR_GetClientInfoVIP"); - MarkNativeAsOptional("LR_ChangeClientVIP"); - MarkNativeAsOptional("LR_DeleteClientVIP"); -} -#endif \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/rank.inc b/addons/sourcemod/scripting/include/rank.inc deleted file mode 100644 index 149a7c6..0000000 --- a/addons/sourcemod/scripting/include/rank.inc +++ /dev/null @@ -1,2 +0,0 @@ - -native Rank_GiveExp(client, amount); //Gives exp to client \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/store.inc b/addons/sourcemod/scripting/include/store.inc deleted file mode 100644 index 057fcf9..0000000 --- a/addons/sourcemod/scripting/include/store.inc +++ /dev/null @@ -1,100 +0,0 @@ -#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/include/wcs.inc b/addons/sourcemod/scripting/include/wcs.inc deleted file mode 100644 index 29f6c8d..0000000 --- a/addons/sourcemod/scripting/include/wcs.inc +++ /dev/null @@ -1,553 +0,0 @@ -#if defined _wcs_included - #endinput -#endif -#define _wcs_included - -/** - * GetRealSpeed, GetRealGravity, GetRealAlpha, GetRealHealth - * , . - * GetRealModel , - * . - */ - -/** - * Get client's Anti Ultimate Status. - * - * @param index An integer. - * @return Anti Ultimate Status. - */ -native bool:WCS_GetAntiUlt(index); - -/** - * Get client's Anti Aura Status. - * - * @param index An integer. - * @return Anti Aura Status. - */ -native bool:WCS_GetAntiAura(index); - -/** - * Get client's Anti Totem Status. - * - * @param index An integer. - * @return Anti Totem Status. - */ -native bool:WCS_GetAntiTotem(index); - -/** - * Get client's 'Hide Effects' opt Status. - * - * @param index An integer. - * @return Hide Effects opt Status. - */ -native bool:WCS_GetHideEffects(index); - -/** - * Get client's 'Mute Sound' opt Status. - * - * @param index An integer. - * @return Mute Sound opt Status. - */ -native bool:WCS_GetMuteSound(index); - -/** - * Get client's 'Hide Skill Msg' opt Status. - * - * @param index An integer. - * @return Hide Skill Msg opt Status. - */ -native bool:WCS_GetHideSkillMsg(index); - -/** - * Get client's gold. - * - * @param index An integer. - * @return Client's gold. - */ -native WCS_GetGold(index); - -/** - * Get client's lvl. - * - * @param index An integer. - * @return Client's lvl. - */ -native WCS_GetLvl(index); - -/** - * Get client's race lvl. - * - * @param index An integer. - * @return Client's race lvl. - */ -native WCS_GetCLvl(index); - -/** - * Get client's Level Bank lvl. - * - * @param index An integer. - * @return Client's Level Bank lvl. - */ -native WCS_GetLBlvl(index); - -/** - * Get client's vip status. - * - * @param index An integer. - * @return true on success, false otherwise. - */ -native bool:WCS_GetVip(index); - -/** - * Get client's reborn status. - * - * @param index An integer. - * @return true on success, false otherwise. - */ -native bool:WCS_GetReborn(index); - -/** - * Get client's xp. - * - * @param index An integer. - * @return Client's xp. - */ -native WCS_GetXp(index); - -/** - * Get client's xp difference. - * - * @param index An integer. - * @return Client's xp. - */ -native WCS_GetXpDifference(index); - -/** - * Get client's race name. - * - * @param index An integer. - * @param race An string. - * @params size An integer. - * @return true on success. - */ -native bool:WCS_GetRace(index, String:race[], size); - -/** - * Get client's real speed. - * - * @param index An integer. - * @return Client's speed. - */ -native Float:WCS_GetRealSpeed(index); - -/** - * Get client's real gravity. - * - * @param index An integer. - * @return Client's speed. - */ -native Float:WCS_GetRealGravity(index); - -/** - * Get client's real alpha. - * - * @param index An integer. - * @return Client's alpha. - */ -native WCS_GetRealAlpha(index); - -/** - * Get client's real health. - * - * @param index An integer. - * @return Client's health. - */ -native WCS_GetRealHealth(index); - -/** - * Get client's real model. - * - * @param index An integer. - * @param model An string. - * @param size An integer. - * @return true on success, false otherwise. - */ -native bool:WCS_GetRealModel(index, String:model[], size); - -/** - * Get random client between radius. - * - * @param client An integer. - * @param radius An float. - * @param checkImmun An bool. - * @return random client on true. - */ -native WCS_GetRandomPosition(client, Float:radius, bool:checkImmun); - -/** - * Get nearest client between radius. - * - * @param client An integer. - * @param radius An float. - * @param checkImmun An bool. - * @return nearest client on true. - */ -native WCS_GetBestPosition(client, Float:radius, bool:checkImmun); - -/** - * Get nearest client to aim with radius. - * - * @param client An integer. - * @param distance An float. (0.0 - ) - * @param radius An float. - * @param checkImmun An bool. - * @return nearest client on true. - */ -native WCS_GetNearAim(client, Float:distance = 0.0, Float:radius, bool:checkImmun); - -/** - * Get client is admin. - * - * @param client An integer. - * @return true if client is admin, false otherwise. - */ -native bool:WCS_GetWcsAdmin(client); - -/** - * Init Effects to client & attacker [optional]. - * If you want to use effect only for client: - * InitEffects(client, client, effect); - * - * @param client An integer. - * @param victim An integer. - * @param effect An string. - * @return true on success. - */ -native bool:WCS_InitEffects(client, victim, const String:effect[]); - -/** - * Give client gold. - * - * @param client An integer. - * @param value An integer. - * @return true on success. - */ -native bool:WCS_GiveGold(client, value); - -/** - * Give client lvl. - * - * @param client An integer. - * @param value An integer. - * @return true on success. - */ -native bool:WCS_GiveLvl(client, value); - -/** - * Give client race lvl. - * - * @param client An integer. - * @param value An integer. - * @return true on success. - */ -native bool:WCS_GiveCLvl(client, value); - -/** - * Give client Level Bank lvl. - * - * @param client An integer. - * @param value An integer. - * @return true on success. - */ -native bool:WCS_GiveLBlvl(client, value); - -/** - * Give client xp. - * - * @param client An integer. - * @param value An integer. - * @param announce An bool. - * @return true on success. - */ -native bool:WCS_GiveXp(client, value, bool:announce = true); - -/** - * Take client gold. - * - * @param client An integer. - * @param value An integer. - * @return true on success. - */ -native bool:WCS_TakeGold(client, value); - -/** - * Take client Level Bank lvl. - * - * @param client An integer. - * @param value An integer. - * @return true on success. - */ -native bool:WCS_TakeLBlvl(client, value); - -/** - * Set client real speed. - * - * @param client An integer. - * @param value An float. - * @return true on success. - */ -native bool:WCS_SetRealSpeed(client, Float:value); - -/** - * Set client real gravity. - * - * @param client An integer. - * @param value An float. - * @return true on success. - */ -native bool:WCS_SetRealGravity(client, Float:value); - -/** - * Set client real alpha. - * - * @param client An integer. - * @param value An integer. - * @return true on success, false otherwise. - */ -native bool:WCS_SetRealAlpha(client, value); - -/** - * Set client real health. - * - * @param client An integer. - * @param value An integer. - * @return true on success, false otherwise. - */ -native bool:WCS_SetRealHealth(client, value); - -/** - * Set client's real model. - * - * @param index An integer. - * @param model An string. - * @return true on success, false otherwise. - */ -native bool:WCS_SetRealModel(index, const String:model[]); - -/** - * Set client's race. - * - * @param index An integer. - * @param race An String. - * @param suicide An bool. (Check or not CVAR 'wcs_cr_suicide' = 1 then client will be killed) - * @return true on success, false otherwise. - */ -native bool:WCS_SetRace(index, const String:race[], bool:suicide = true); - -/** - * Reset client's skills. - * - * @param index An integer. - * @param fully An bool. (if true then dont reuse skills after this operation) - * @return true on success, false otherwise. - */ -native bool:WCS_ResetSkills(client, bool:fully); - -/** - * Reset client ultimate cooldown. - * - * @param client An integer. - * @return true on success. - */ -native bool:WCS_ResetUltCld(client); - -/** - * Reset client ability cooldown. - * - * @param client An integer. - * @return true on success, false otherwise. - */ -native bool:WCS_ResetAbilityCld(client); - -/** - * Checks if the race is private - * - * @param racename An string. [64] - * @return true on success, false otherwise. - */ -native bool:WCS_IsRacePrivate(const String:RaceName[]); - -/** - * Gives a private race to client - * - * @param steamid An string. [64] - * @param racename An string. [64] - * @return true on success, false otherwise. - */ -native bool:WCS_GivePrivateRace(const String:SteamId[], const String:RaceName[]); - -/** - * Take a private race from client - * - * @param steamid An string. [64] - * @param racename An string. [64] - * @return true on success, false otherwise. - */ -native bool:WCS_TakePrivateRace(const String:SteamId[], const String:RaceName[]); - -/** - * Get required lvl for race - * - * @param racename An string. [64] - * @return required lvl on success, -1 otherwise. - */ -native WCS_GetRaceReqLvl(const String:RaceName[]); - -/** - * Get client's totems count that he can use (every use totem this value will decreased and restored on special events [this value = GetClientTotemsMax on special events]) - * - * @param client An integer. - * @return Amount of totems. - */ -native WCS_GetClientTotems(client); - -/** - * Get client's totems count maximum - * - * @param client An integer. - * @return Amount of maximum totems. - */ -native WCS_GetClientTotemsMax(client); - -/** - * Get client's vip group. - * - * @param client An integer. - * @param group An string. - * @params size An integer. - * @return true on success. - */ -native bool:WCS_GetVipGroup(client, String:group[], size); - -/** - * Get client's vip premium status. - * - * @param client An integer. - * @return true on success, false otherwise. - */ -native bool:WCS_GetVipPremium(client); - -/** - * Called on client skill lvl up - * - */ -forward WCS_OnSkillLvlUp(client, skilllvl, const String:skillname[]); - -/** - * Called on client lvl up - * - */ -forward WCS_OnLvlUp(client, lvl); - -/** - * Called on client ultimate cooldown has finished - * - */ -forward WCS_OnUltimateCld(client); - -/** - * Called on client ability cooldown has finished - * - */ -forward WCS_OnAbilityCld(client); - -/** - * Called on client skills has resetted - * - */ -forward WCS_OnSkillsReset(client, bool:fully); - -/** - * Called on client xp gain - * - */ -forward WCS_OnXpGain(client, xp, bool:lvlup); - -/** - * Called on client gold gain - * - */ -forward WCS_OnGoldGain(client, gold); - -/** - * Called on client ultimate pre - * - */ -forward Action:WCS_OnUltimatePre(client, const String:ultimate[]); - -/** - * Called on client ultimate post - * - */ -forward WCS_OnUltimatePost(client); - -/** - * Called on client ability pre - * - */ -forward Action:WCS_OnAbilityPre(client, const String:ability[]); - -/** - * Called on client ability post - * - */ -forward WCS_OnAbilityPost(client); - -/** - * Called on client change race pre - * - */ -forward Action:WCS_OnRaceChangePre(client, const String:OldRace[], const String:NewRace[]); - -/** - * Called on client change race post - * - */ -forward WCS_OnRaceChangePost(client); - -/** - * Called on client item purchase pre - * - * PaymentType -- "cash" "gold" - */ -forward Action:WCS_OnItemPurchasePre(client, const String:ItemName[], const String:BlockName[], ItemCost, const String:PaymentType[]); - -/** - * Called on client item purchase post - * - */ -forward WCS_OnItemPurchasePost(client); - -/** - * Called on client skills just set pre - * - */ -forward Action:WCS_OnSkillsSetPre(client, const String:Race[]); - -/** - * Called on client skills just set - * - */ -forward WCS_OnSkillsSet(client, const String:Race[]); - -/** - * Called on client loaded - * - */ -forward WCS_OnClientLoaded(client); - -/** - * Called on race should show - * - */ -forward Action:WCS_OnRaceShouldShow(client, const String:Race[], ReqLvl); \ No newline at end of file diff --git a/addons/sourcemod/scripting/keys/api.sp b/addons/sourcemod/scripting/keys/api.sp index 9357b1a..e2357ea 100644 --- a/addons/sourcemod/scripting/keys/api.sp +++ b/addons/sourcemod/scripting/keys/api.sp @@ -1,11 +1,10 @@ -static Handle:g_hGlobalForward_OnCoreStarted; +static Handle g_hGlobalForward_OnCoreStarted; -public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +public APLRes AskPluginLoad2(Handle hMySelf, bool bLate, char[] szError, int iErr_max) { - MarkNativeAsOptional("GetClientAuthId"); - MarkNativeAsOptional("GetClientAuthString"); - +// Stats_Init(); + g_hGlobalForward_OnCoreStarted = CreateGlobalForward("Keys_OnCoreStarted", ET_Ignore); CreateNative("Keys_IsCoreStarted", Native_IsCoreStarted); @@ -20,147 +19,142 @@ public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) CreateNative("Keys_IsValidKey", Native_IsValidKey); CreateNative("Keys_GetKeyData", Native_GetKeyData); CreateNative("Keys_GenerateKey", Native_GenerateKey); - CreateNative("Keys_AddKey", Keys_AddKey); - CreateNative("Keys_RemoveKey", Keys_RemoveKey); - CreateNative("Keys_UseKey", Keys_UseKey); - - - /* - - Проверка наличия типа ключа - - Проверка наличия ключа - - Генерация ключа - - Добавление ключа - - Получение массива с типами ключей - - Активация ключа - */ + CreateNative("Keys_AddKey", Native_AddKey); + CreateNative("Keys_RemoveKey", Native_RemoveKey); + CreateNative("Keys_UseKey", Native_UseKey); RegPluginLibrary("keys_core"); return APLRes_Success; } -CreateForward_OnCoreStarted() +void API_CreateForward_OnCoreStarted() { Call_StartForward(g_hGlobalForward_OnCoreStarted); Call_Finish(); } -public Native_IsCoreStarted(Handle:hPlugin, iNumParams) +public int Native_IsCoreStarted(Handle hPlugin, int iNumParams) { - return g_bIsStarted; + return view_as(g_bIsStarted); } -public Native_GetCoreDatabase(Handle:hPlugin, iNumParams) +public int Native_GetCoreDatabase(Handle hPlugin, int iNumParams) { - return _:CloneHandle(g_hDatabase, hPlugin); + return view_as(CloneHandle(g_hDatabase, hPlugin)); } -public Native_GetDatabaseType(Handle:hPlugin, iNumParams) +public int Native_GetDatabaseType(Handle hPlugin, int iNumParams) { - return g_bDBMySQL; + return view_as(g_bDBMySQL); } -public Native_RegKey(Handle:hPlugin, iNumParams) +public int Native_RegKey(Handle hPlugin, int iNumParams) { - decl String:sKeyType[KEYS_MAX_LENGTH]; - GetNativeString(1, SZF(sKeyType)); - - if(FindStringInArray(g_hKeysArray, sKeyType) != -1) + char szKeyType[KEYS_MAX_LENGTH]; + GetNativeString(1, SZF(szKeyType)); + + if(g_hKeysArray.FindString(szKeyType) != -1) { - ThrowNativeError(SP_ERROR_NATIVE, "Тип ключа \"%s\" уже зарегистрирован!", sKeyType); + ThrowNativeError(SP_ERROR_NATIVE, "Тип ключа \"%s\" уже зарегистрирован!", szKeyType); 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); + DataPack hDataPack = new DataPack(); + hDataPack.WriteCell(hPlugin); + hDataPack.WriteFunction(GetNativeCell(2)); + + g_hKeysTrie.SetValue(szKeyType, hDataPack); + g_hKeysArray.PushString(szKeyType); return true; } -public Native_UnregKey(Handle:hPlugin, iNumParams) +public int Native_UnregKey(Handle hPlugin, int iNumParams) { - decl String:sKeyType[KEYS_MAX_LENGTH], index; - GetNativeString(1, SZF(sKeyType)); - - if((index = FindStringInArray(g_hKeysArray, sKeyType)) != -1) + char szKeyType[KEYS_MAX_LENGTH]; + GetNativeString(1, SZF(szKeyType)); + + int index; + if((index = g_hKeysArray.FindString(szKeyType)) != -1) { - RemoveFromArray(g_hKeysArray, index); - decl Handle:hDataPack; - if(GetTrieValue(g_hKeysTrie, sKeyType, hDataPack)) + g_hKeysArray.Erase(index); + DataPack hDataPack; + if(g_hKeysTrie.GetValue(szKeyType, hDataPack)) { - CloseHandle(hDataPack); + delete hDataPack; } - RemoveFromTrie(g_hKeysTrie, sKeyType); + g_hKeysTrie.Remove(szKeyType); } } -public Native_IsValidKeyType(Handle:hPlugin, iNumParams) +public int Native_IsValidKeyType(Handle hPlugin, int iNumParams) { - decl String:sKeyType[KEYS_MAX_LENGTH], index; - GetNativeString(1, SZF(sKeyType)); - - return (FindStringInArray(g_hKeysArray, sKeyType) != -1); + char szKeyType[KEYS_MAX_LENGTH]; + GetNativeString(1, SZF(szKeyType)); + + return (FindStringInArray(g_hKeysArray, szKeyType) != -1); } -public Native_FillArrayByKeyTypes(Handle:hPlugin, iNumParams) +public int Native_FillArrayByKeyTypes(Handle hPlugin, int iNumParams) { - return CloneArray(g_hKeysArray); + return view_as(g_hKeysArray.Clone()); } -public Native_IsValidKey(Handle:hPlugin, iNumParams) +public int Native_IsValidKey(Handle hPlugin, int iNumParams) { - decl Handle:hDP, String:sKey[KEYS_MAX_LENGTH], String:sQuery[256]; - GetNativeString(1, SZF(sKey)); + char szKey[KEYS_MAX_LENGTH], szQuery[PMP], szSID[64]; + GetNativeString(1, SZF(szKey)); - hDP = CreateDataPack(); - WritePackCell(hDP, hPlugin); - WritePackCell(hDP, GetNativeCell(2)); - WritePackString(hDP, sKey); + DataPack hDP = new DataPack(); + hDP.WriteCell(hPlugin); + hDP.WriteFunction(GetNativeCell(2)); + hDP.WriteString(szKey); + hDP.WriteCell(GetNativeCell(3)); - if(!g_iServerID) + if(!g_CVAR_iServerID) { - FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s' LIMIT 1;", sKey); + szSID[0] = 0; } else { - FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d LIMIT 1;", sKey, g_iServerID); + FormatEx(SZF(szSID), " AND `k_sid` = %d", g_CVAR_iServerID); } - SQL_TQuery(g_hDatabase, SQL_Callback_Ntv_IsValidKey, sQuery, hDP); + FormatEx(SZF(szQuery), "SELECT `k_id` FROM `keys_tokens` WHERE `k_name` = '%s'%s LIMIT 1;", szKey, szSID); + + g_hDatabase.Query(SQL_Callback_Ntv_IsValidKey, szQuery, hDP); } -public SQL_Callback_Ntv_IsValidKey(Handle:hOwner, Handle:hResult, const String:sDBError[], any:hDP) +public void SQL_Callback_Ntv_IsValidKey(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) { - if (hResult == INVALID_HANDLE || sDBError[0]) + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) { - CloseHandle(hDP); - LogError("SQL_Callback_Ntv_IsValidKey: %s", sDBError); + delete hDP; + LogError("SQL_Callback_Ntv_IsValidKey: %s", szDbError); return; } - ResetPack(hDP); - decl Handle:hPlugin, Function:fCallback, String:sKey[KEYS_MAX_LENGTH], bool:bKeyExists; - hPlugin = Handle:ReadPackCell(hDP); - fCallback = Function:ReadPackCell(hDP); - ReadPackString(SZF(sKey)); - CloseHandle(hDP); + hDP.Reset(); + Handle hPlugin = view_as(hDP.ReadCell()); + Function fCallback = hDP.ReadFunction(); + char szKey[KEYS_MAX_LENGTH]; + hDP.ReadString(SZF(szKey)); + any iData = hDP.ReadCell(); + delete hDP; - bKeyExists = false; + bool bKeyExists = false; - if(SQL_FetchRow(hResult)) + if(hResult.FetchRow()) { - iExpires = SQL_FetchInt(hResult, 0); + int iExpires = hResult.FetchInt(0); if(iExpires) { if(iExpires < GetTime()) { - DeleteKey(sKey); + Keys_Delete(szKey); } else { @@ -173,243 +167,251 @@ public SQL_Callback_Ntv_IsValidKey(Handle:hOwner, Handle:hResult, const String:s } } - Call_StartFunction(hPlugin, fUseCallback); - Call_PushString(sKey); + Call_StartFunction(hPlugin, fCallback); + Call_PushString(szKey); Call_PushCell(bKeyExists); + Call_PushCell(iData); Call_Finish(); } -public Native_GetKeyData(Handle:hPlugin, iNumParams) +public int Native_GetKeyData(Handle hPlugin, int iNumParams) { - decl Handle:hDP, String:sKey[KEYS_MAX_LENGTH], String:sQuery[256]; - GetNativeString(1, SZF(sKey)); + char szKey[KEYS_MAX_LENGTH], szQuery[PMP], szSID[64]; + GetNativeString(1, SZF(szKey)); - hDP = CreateDataPack(); - WritePackCell(hDP, hPlugin); - WritePackCell(hDP, GetNativeCell(2)); - WritePackString(hDP, sKey); + DataPack hDP = new DataPack(); + hDP.WriteCell(hPlugin); + hDP.WriteFunction(GetNativeCell(2)); + hDP.WriteString(szKey); + hDP.WriteCell(GetNativeCell(3)); - if(!g_iServerID) + if(!g_CVAR_iServerID) { - FormatEx(SZF(sQuery), "SELECT `type`, `expires`, `uses`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` WHERE `key_name` = '%s' LIMIT 1;", sKey); + szSID[0] = 0; } else { - FormatEx(SZF(sQuery), "SELECT `type`, `expires`, `uses`, `param1`, `param2`, `param3`, `param4`, `param5` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d LIMIT 1;", sKey, g_iServerID); + FormatEx(SZF(szSID), " AND `k_sid` = %d", g_CVAR_iServerID); } - SQL_TQuery(g_hDatabase, SQL_Callback_Ntv_GetKeyData, sQuery, hDP); + FormatEx(SZF(szQuery), "SELECT `k_id`, `k_type`, `k_expires`, `k_uses` FROM `keys_tokens` WHERE `k_name` = '%s'%s LIMIT 1;", szKey, szSID); + + g_hDatabase.Query(SQL_Callback_Ntv_GetKeyData, szQuery, hDP); } -public SQL_Callback_Ntv_GetKeyData(Handle:hOwner, Handle:hResult, const String:sDBError[], any:iData) +public void SQL_Callback_Ntv_GetKeyData(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) { - if (hResult == INVALID_HANDLE || sDBError[0]) + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) { - LogError("SQL_Callback_Ntv_GetKeyData: %s", sDBError); + delete hDP; + LogError("SQL_Callback_Ntv_GetKeyData: %s", szDbError); return; } - ResetPack(hDP); - decl Handle:hPlugin, Function:fCallback, String:sKey[KEYS_MAX_LENGTH], bool:bKeyExists, Handle:hParamsArr, String:sKeyType[KEYS_MAX_LENGTH], String:sParam[KEYS_MAX_LENGTH], String:sError[256], iExpires, iUses, i; - hPlugin = Handle:ReadPackCell(hDP); - fCallback = Function:ReadPackCell(hDP); - ReadPackString(SZF(sKey)); - CloseHandle(hDP); + hDP.Reset(); + + char szKey[KEYS_MAX_LENGTH], szKeyType[KEYS_MAX_LENGTH]; + Handle hPlugin = view_as(hDP.ReadCell()); + Function fCallback = hDP.ReadFunction(); + hDP.ReadString(SZF(szKey)); + any iData = hDP.ReadCell(); - bKeyExists = false; + bool bKeyExists = false; + int iKeyID, iExpires, iUses; - if(SQL_FetchRow(hResult)) + if(hResult.FetchRow()) { - iExpires = SQL_FetchInt(hResult, 1); + iExpires = hResult.FetchInt(2); if(!iExpires || iExpires > GetTime()) { - SQL_FetchString(hResult, 1, SZF(sKeyType)); - if((FindStringInArray(g_hKeysArray, sKeyType) != -1)) + hResult.FetchString(1, SZF(szKeyType)); + if(g_hKeysArray.FindString(szKeyType) != -1) { - iUses = SQL_FetchInt(hResult, 2); + iUses = hResult.FetchInt(3); if(iUses) { - hParamsArr = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); - for(i = 3; i < 8; ++i) - { - if(SQL_IsFieldNull(hResult, i)) - { - break; - } - - SQL_FetchString(hResult, i, SZF(sParam)); - PushArrayString(hParamsArr, sParam); - } - bKeyExists = true; + iKeyID = hResult.FetchInt(0); } else { - DeleteKey(sKey); + Keys_Delete(szKey); } } } else { - DeleteKey(sKey); + Keys_Delete(szKey); } } if(!bKeyExists) { - sKeyType[0] = iCount = iExpires = 0; - hParamsArr = INVALID_HANDLE; + Call_StartFunction(hPlugin, fCallback); + Call_PushString(szKey); + Call_PushCell(false); + Call_PushString(NULL_STRING); + Call_PushCell(0); + Call_PushCell(0); + Call_PushCell(0); + Call_PushCell(iData); + Call_Finish(); + delete hDP; + return; } - Call_StartFunction(hPlugin, fCallback); - Call_PushString(sKey); - Call_PushCell(bKeyExists); - Call_PushString(sKeyType); - Call_PushCell(iCount); - Call_PushCell(iExpires); - Call_PushCell(hParamsArr); - Call_Finish(); + hDP.WriteString(szKeyType); + hDP.WriteCell(iUses); + hDP.WriteCell(iExpires); - if(hParamsArr) - { - CloseHandle(hParamsArr); - } -} + char szQuery[PMP]; -public Native_GenerateKey(Handle:hPlugin, iNumParams) -{ - decl String:sKey[KEYS_MAX_LENGTH], String:sTemplate[64]; - GetNativeString(3, SZF(sTemplate)); + FormatEx(SZF(szQuery), "SELECT `p_num`, `p_value` FROM `keys_params` WHERE `p_kid` = '%d' ORDER BY `p_num`;", iKeyID); - UTIL_GenerateKey(SZF(sKey), g_CVAR_sKeyTemplate); - SetNativeString(1, sKey, GetNativeCell(2), true); + g_hDatabase.Query(SQL_Callback_Ntv_GetKeyDataParams, szQuery, hDP); } -// native Keys_AddKey(const String:sKey[] = "", const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyAddCallback:AddKeyCallback); -// functag public KeyAddCallback(const String:sKey[], bool:bSuccess, const String:sError[]); -public Native_AddKey(Handle:hPlugin, iNumParams) +public void SQL_Callback_Ntv_GetKeyDataParams(Database hDB, DBResultSet hResult, const char[] szDbError, any hCbDP) { - new iClient = GetNativeCell(1); - if(iClient && (iClient < 0 || iClient > MaxClients || !IsClientInGame(iClient) || !IsFakeClient(iClient))) + DataPack hDP = view_as(hCbDP); + + if (hResult == null || szDbError[0]) { + delete hDP; + LogError("SQL_Callback_Ntv_GetKeyDataParams: %s", szDbError); return; } - decl iHandle:hDP, String:sKey[KEYS_MAX_LENGTH], String:sKeyType[KEYS_MAX_LENGTH], String:sError[256], iLifeTime, iUses, Handle:hParamsArr, String:sQuery[256]; - GetNativeString(2, SZF(sKey)); - GetNativeString(3, SZF(sKeyType)); - hParamsArr = Handle:GetNativeCell(6); + hDP.Reset(); - sError[0] = 0; + char szKey[KEYS_MAX_LENGTH], szKeyType[KEYS_MAX_LENGTH], szParam[KEYS_MAX_LENGTH]; + Handle hPlugin = view_as(hDP.ReadCell()); + Function fCallback = hDP.ReadFunction(); + hDP.ReadString(SZF(szKey)); + any iData = hDP.ReadCell(); + hDP.ReadString(SZF(szKeyType)); + int iUses = hDP.ReadCell(); + int iExpires = hDP.ReadCell(); + delete hDP; - if(!UTIL_CheckKey(sKey, sKeyType, GetNativeCell(5), GetNativeCell(4), hParamsArr, SZF(sError), iClient)) + ArrayList hParamsArr = new ArrayList(ByteCountToCells(KEYS_MAX_LENGTH)); + while(hResult.FetchRow()) { - CloseHandle(hParamsArr); - CreateCallback_AddKey(hPlugin, Function:GetNativeCell(7), false, sError); - - return; - - // Keys_AddKey(const String:sKey[] = "", const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyAddCallback:AddKeyCallback); - // KeyAddCallback(const String:sKey[], bool:bSuccess, const String:sError[]); + hResult.FetchString(1, SZF(szParam)); + hParamsArr.PushString(szParam); } -// CloseHandle(hParamsArr); - - decl Handle:hDP, String:sQuery[256], iExpires; + Call_StartFunction(hPlugin, fCallback); + Call_PushString(szKey); + Call_PushCell(true); + Call_PushString(szKeyType); + Call_PushCell(iUses); + Call_PushCell(iExpires); + Call_PushCell(hParamsArr); + Call_PushCell(iData); + Call_Finish(); +} - iClient = GET_UID(iClient); +public int Native_GenerateKey(Handle hPlugin, int iNumParams) +{ + char szKey[KEYS_MAX_LENGTH], sTemplate[64]; + GetNativeString(3, SZF(sTemplate)); - iExpires = iLifeTime ? (iLifeTime + GetTime()):iLifeTime; - - hDP = CreateDataPack(); - WritePackCell(hDP, CloneArray(hParamsArr)); + Keys_Generate(SZF(szKey)); + SetNativeString(1, szKey, GetNativeCell(2), true); +} - if(sKey[0]) +// Keys_AddKey(int iClient = 0, const char[] szKey = NULL_STRING, const char[] szKeyType, int iUses, iLifeTime, ArrayList hParamsArr, KeyNativeActionCallback AddKeyCallback, any iData); +public int Native_AddKey(Handle hPlugin, int iNumParams) +{ + int iClient = GetNativeCell(1); + if(iClient && (iClient < 0 || iClient > MaxClients || !IsClientInGame(iClient) || IsFakeClient(iClient))) { - WritePackString(hDP, sKey); - WritePackCell(hDP, true); + return 0; } - else - { - UTIL_GenerateKey(sKey, KEYS_MAX_LENGTH, g_CVAR_sKeyTemplate); - WritePackString(hDP, sKey); - WritePackCell(hDP, false); - } + char szKey[KEYS_MAX_LENGTH], szKeyType[KEYS_MAX_LENGTH], szError[PMP]; + GetNativeString(2, SZF(szKey)); + GetNativeString(3, SZF(szKeyType)); + int iUses = GetNativeCell(4); + int iLifeTime = GetNativeCell(5); + ArrayList hParamsArr = view_as(GetNativeCell(6)); + Function fCallback = GetNativeFunction(7); + any iData = GetNativeCell(8); - WritePackCell(hDP, iClient); - WritePackCell(hDP, CmdReplySource); - WritePackString(hDP, sKeyType); - WritePackCell(hDP, iUses); - WritePackCell(hDP, iExpires); - WritePackCell(hDP, iLifeTime); - WritePackCell(hDP, hPlugin); - WritePackCell(hDP, GetNativeCell(6)); - - if(!g_iServerID) - { - FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s';", sKey); - } - else + szError[0] = 0; + + if(!Keys_Validate(szKey, szKeyType, iUses, iLifeTime, hParamsArr, SZF(szError), iClient)) { - FormatEx(SZF(sQuery), "SELECT `expires` FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d;", sKey, g_iServerID); + delete hParamsArr; + API_Callback(Add, hPlugin, fCallback, iClient, szKey, false, szError, iData); + + return 0; } - SQL_TQuery(g_hDatabase, SQL_Callback_SearchKey, sQuery, hDP); -} -CreateCallback_AddKey(Handle:hPlugin, Function:fCallback, const String:sKey[], bool:bSuccess = true, const String:sError[] = NULL_STRING) -{ - Call_StartFunction(hPlugin, fCallback); - Call_PushString(sKey); - Call_PushCell(bSuccess); - Call_PushString(sError); - Call_Finish(); + Keys_Add(szKey, szKeyType, iUses, iLifeTime, -1, hParamsArr, iClient, iClient ? SM_REPLY_TO_CHAT:SM_REPLY_TO_CONSOLE, hPlugin, fCallback, iData); + return 0; } -public Native_RemoveKey(Handle:hPlugin, iNumParams) +//Keys_RemoveKey(int iClient = 0, const char[] szKey, KeyNativeActionCallback RemoveKeyCallback, any iData = 0); +public int Native_RemoveKey(Handle hPlugin, int iNumParams) { - decl Handle:hDP, String:sKey[KEYS_MAX_LENGTH], String:sQuery[256]; - GetNativeString(1, SZF(sKey)); - - hDP = CreateDataPack(); - WritePackCell(hDP, hPlugin); - WritePackCell(hDP, GetNativeCell(2)); - WritePackString(hDP, sKey); - - if(!g_iServerID) - { - FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `key_name` = '%s';", sKey); - } - else + int iClient = GetNativeCell(1); + if(iClient && (iClient < 0 || iClient > MaxClients || !IsClientInGame(iClient) || IsFakeClient(iClient))) { - FormatEx(SZF(sQuery), "DELETE FROM `table_keys` WHERE `key_name` = '%s' AND `sid` = %d;", sKey, g_iServerID); + return 0; } - SQL_TQuery(g_hDatabase, SQL_Callback_Ntv_RemoveKey, sQuery, hDP); + + char szKey[KEYS_MAX_LENGTH]; + GetNativeString(2, SZF(szKey)); + + Function fCallback = GetNativeFunction(3); + any iData = GetNativeCell(4); + + Keys_Delete(szKey, true, iClient, iClient ? SM_REPLY_TO_CHAT:SM_REPLY_TO_CONSOLE, hPlugin, fCallback, iData); + return 0; } -public SQL_Callback_Ntv_RemoveKey(Handle:hOwner, Handle:hResult, const String:sDBError[], any:iData) +// Keys_UseKey(int iClient, const char[] szKey, bool bNotify, bool bIgnoreBlock, KeyNativeActionCallback UseKeyCallback, any iData = 0); +public int Native_UseKey(Handle hPlugin, int iNumParams) { - if (hResult == INVALID_HANDLE || sDBError[0]) + int iClient = GetNativeCell(1); + if(iClient < 1 || iClient > MaxClients || !IsClientInGame(iClient) || IsFakeClient(iClient)) { - LogError("SQL_Callback_Ntv_RemoveKey: %s", sDBError); - return; + return 0; } - ResetPack(hDP); - decl Handle:hPlugin, Function:fCallback, String:sKey[KEYS_MAX_LENGTH]; - hPlugin = Handle:ReadPackCell(hDP); - fCallback = Function:ReadPackCell(hDP); - ReadPackString(SZF(sKey)); - CloseHandle(hDP); + char szKey[KEYS_MAX_LENGTH]; + GetNativeString(2, SZF(szKey)); + bool bNotify = GetNativeCell(3); + bool bIgnoreBlock = GetNativeCell(4); - Call_StartFunction(hPlugin, fCallback); - Call_PushString(sKey); - Call_PushCell(bool:SQL_GetAffectedRows(hOwner)); - Call_Finish(); + Function fCallback = GetNativeFunction(5); + any iData = GetNativeCell(6); + + if(!bIgnoreBlock && g_bIsBlocked[iClient]) + { + char szError[PMP]; + FormatEx(SZF(szError), "%T%T", "ERROR", iClient, "ERROR_BLOCKED", iClient); + API_Callback(Use, hPlugin, fCallback, iClient, szKey, false, szError, iData); + return 0; + } + + Keys_Use(szKey, iClient, SM_REPLY_TO_CHAT, bNotify, bIgnoreBlock, hPlugin, fCallback, iData); + return 0; } -// Использует ключ игроком -public Native_UseKey(Handle:hPlugin, iNumParams) +// function void (int iClient, const char[] szKey, bool bSuccess, const char[] szError, any iData); +void API_Callback(KeysNativeAction eKeysAction, Handle hPlugin, Function fCallback, int iClient, const char[] szKey, bool bSuccess = true, const char[] szError = NULL_STRING, any iData) { - + Call_StartFunction(hPlugin, fCallback); + Call_PushCell(eKeysAction); + Call_PushCell(iClient); + Call_PushString(szKey); + Call_PushCell(bSuccess); + Call_PushString(szError); + Call_PushCell(iData); + Call_Finish(); } diff --git a/addons/sourcemod/scripting/keys/cmds.sp b/addons/sourcemod/scripting/keys/cmds.sp deleted file mode 100644 index 4c7a345..0000000 --- a/addons/sourcemod/scripting/keys/cmds.sp +++ /dev/null @@ -1,984 +0,0 @@ -#define GET_UID(%0) (%0 == 0 ? 0:UID(%0)) - -GET_CID(iClient) -{ - if(iClient > 0) - { - iClient = CID(iClient); - if(!iClient) - { - return -1; - } - - return iClient; - } - - return iClient; -} - -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%t", "ERROR", "ERROR_BLOCKED"); - return Plugin_Handled; - - } - else - { - UnBlockClient(iClient); - g_bIsBlocked[iClient] = false; - g_iAttempts[iClient] = 0; - } - } - - if(iArgs != 1) - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "USAGE_ERROR_USE_KEY"); - return Plugin_Handled; - } - - decl String:sKey[KEYS_MAX_LENGTH], String:sQuery[512]; - GetCmdArg(1, SZF(sKey)); - - if(!UTIL_ValidateKey(sKey, SZF(sQuery))) - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", sQuery); - - if(g_CVAR_iAttempts) - { - if(g_iAttempts[iClient]++ >= g_CVAR_iAttempts) - { - BlockClient(iClient); - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_BLOCKED"); - return Plugin_Handled; - } - - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_KEY_LEFT", g_CVAR_iAttempts-g_iAttempts[iClient]); - } - else - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "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%t", "ERROR", "ERROR_KEY_NOT_EXIST"); - return; - } - } - - iUses = SQL_FetchInt(hResult, 3); - if(!iUses) - { - DeleteKey(sKey); - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_KEY_NOT_EXIST"); - return; - } - - if(SQL_FetchInt(hResult, 4)) - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "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); - - sError = "unknown"; - 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, "%t%s", "ERROR", 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%t", "ERROR", "ERROR_BLOCKED"); - return; - } - - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_KEY_LEFT", g_CVAR_iAttempts-g_iAttempts[iClient]); - } - else - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_INCORRECT_KEY"); - } - } -} - -public Action:AddKey_CMD(iClient, iArgs) -{ - new ReplySource:CmdReplySource = GetCmdReplySource(); - - if(iArgs < 5) - { - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%t", "ERROR", "ERROR_NUM_ARGS"); - return Plugin_Handled; - } - - decl Handle:hDP, - String:sKeyType[KEYS_MAX_LENGTH], - String:sParam[KEYS_MAX_LENGTH], - String:sError[256], - iLifeTime, - iUses, - iCount, - Handle:hParamsArr, - 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%t", "ERROR", "ERROR_INCORRECT_AMOUNT"); - return Plugin_Handled; - } - - sKey[0] = 0; - } - else - { - GetCmdArg(1, SZF(sKey)); - } - - GetCmdArg(4, SZF(sKeyType)); - - GetCmdArg(2, SZF(sParam)); - iLifeTime = StringToInt(sParam); - - GetCmdArg(3, SZF(sParam)); - iUses = StringToInt(sParam); - - hParamsArr = CreateArray(ByteCountToCells(KEYS_MAX_LENGTH)); - - for(new i = 5; i <= iArgs; ++i) - { - GetCmdArg(i, SZF(sParam)); - PushArrayString(hParamsArr, sParam); - } - - sError[0] = 0; - - if(!UTIL_CheckKey(sKey, sKeyType, iLifeTime, iUses, hParamsArr, SZF(sError))) - { - CloseHandle(hParamsArr); - UTIL_ReplyToCommand(iClient, CmdReplySource, "%t%s", "ERROR", 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(SZF(sKey), g_CVAR_sKeyTemplate); - - 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 == -2) - { - i = ReadPackCell(hDP); // CmdReplySource - ReadPackString(hDP, sQuery, KEYS_MAX_LENGTH); - i = ReadPackCell(hDP); // Uses - i = ReadPackCell(hDP); // Expires - i = ReadPackCell(hDP); // LifeTime - - Format(sQuery, 256, "%T%T", "ERROR", LANG_SERVER, "ERROR_KEY_ALREADY_EXISTS", LANG_SERVER, sKey); - CreateCallback_AddKey(Handle:ReadPackCell(hDP), Function:ReadPackCell(hDP), sKey, false, sQuery); - } - else if(i != -1) - { - UTIL_ReplyToCommand(i, ReplySource:ReadPackCell(hDP), "%t%t", "ERROR", "ERROR_KEY_ALREADY_EXISTS", sKey); - } - - CloseHandle(hParamsArr); - CloseHandle(hDP); - return; - } - else - { - decl Handle:hDP2; - hDP2 = CreateDataPack(); - WritePackCell(hDP2, hParamsArr); - UTIL_GenerateKey(SZF(sKey), g_CVAR_sKeyTemplate); - 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); - } - - SQL_FastQuery(g_hDatabase, "SET CHARSET 'utf8'"); - - 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, iLangClient, 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 == -2) - { - CreateCallback_AddKey(Handle:ReadPackCell(hDP), Function:ReadPackCell(hDP), sKey); - } - - if(iClient > 0) - { - GetClientName(iClient, SZF(sName)); - GetClientAuthId(iClient, AuthId_Engine, SZF(sAuth)); - iLangClient = iClient; - } - else - { - iLangClient = LANG_SERVER; - - strcopy(SZF(sName), "CONSOLE"); - strcopy(SZF(sAuth), "STEAM_ID_SERVER"); - } - - if(iLifeTime) - { - Keys_GetTimeFromStamp(SZF(sExpires), iLifeTime, iLangClient); - } - else - { - FormatEx(SZF(sExpires), "%T", "FOREVER", iLangClient); - } - - 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%t", "ERROR", "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%t", "ERROR", "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%t", "ERROR", "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%t", "ERROR", "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%t", "ERROR", "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%t", "ERROR", "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%t", "ERROR", "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%t", "ERROR", "ERROR_LIST_NO_KEYS"); - } -} diff --git a/addons/sourcemod/scripting/keys/utils.sp b/addons/sourcemod/scripting/keys/utils.sp deleted file mode 100644 index 96cb1f9..0000000 --- a/addons/sourcemod/scripting/keys/utils.sp +++ /dev/null @@ -1,226 +0,0 @@ - - -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); - } -} - -bool:UTIL_CheckKey(String:sKey[], const String:sKeyType[], iLifeTime, iUses, Handle:hParamsArr, String:sError[], iErrLen, iClient = LANG_SERVER) -{ - decl Handle:hDataPack; - - if(!GetTrieValue(g_hKeysTrie, sKeyType, hDataPack)) - { - FormatEx(sError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_TYPE", iClient); - return false; - } - - if(sKey[0]) - { - if(!UTIL_ValidateKey(sKey, sError, iErrLen)) - { - return false; - } - } - - if(iLifeTime < 0) - { - FormatEx(sError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_LIFETIME", iClient); - return false; - } - - if(iUses < 1) - { - FormatEx(sError, iErrLen, "%T%T", "ERROR", iClient, "ERROR_INCORRECT_USES", iClient); - return false; - } - - decl Handle:hPlugin, Function:FuncOnValidateParams, bool:bResult; - - SetPackPosition(hDataPack, DP_Plugin); - hPlugin = Handle:ReadPackCell(hDataPack); - - SetPackPosition(hDataPack, DP_OnValidateCallback); - FuncOnValidateParams = Function:ReadPackCell(hDataPack); - - sError = "unknown"; - 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) - { - FormatEx(sError, iErrLen, "%T%s", "ERROR", iClient, sError); - return false; - } - - return true; -} - -bool:UTIL_AddKey(const String:sKey[], -const String:sKeyType[], -iLifeTime, -iExpires, -iUses, -Handle:hParamsArr, -iClient, -bool:bDuplicateErr) -{ - hDP = CreateDataPack(); - WritePackCell(hDP, 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); -} - -UTIL_ValidateKey(String:sKey[], String:sError[], iErrLen) -{ - if(!sKey[0]) - { - strcopy(sError, iErrLen, "ERROR_KEY_EMPTY"); - return false; - } - - new iLength = strlen(sKey); - 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[], iMaxLen, const String:sTemplate[] = NULL_STRING) -{ - sKey[0] = '\0'; - - new i = 0; - - if(g_CVAR_sKeyTemplate[0]) - { - new iLength = strlen(g_CVAR_sKeyTemplate); - while (i < iLength && i < iMaxLen) - { - sKey[i] = UTIL_GetCharTemplate(g_CVAR_sKeyTemplate[i]); - ++i; - } - } - else - { - while (i < g_CVAR_iKeyLength && i < iMaxLen) - { - sKey[i] = UTIL_GetCharTemplate(0x58); - ++i; - } - } - - sKey[i] = '\0'; -} -/* -A - Буква в любом регистре -B - Цифра 0-9 -X - Цифра 0-9 либо буква в любом регистре -U - число 0-9 либо буква в верхнем регистре -L - число 0-9 либо буква в нижнем регистре -*/ - -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 UTIL_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 UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):(UTIL_GetRandomInt(1, 20) > 10 ? UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1])); - // U - число 0-9 либо буква в верхнем регистре - case 0x55: return UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):UTIL_GetRandomInt(g_iLettersUpper[0], g_iLettersUpper[1]); - // L - число 0-9 либо буква в нижнем регистре - case 0x4c: return UTIL_GetRandomInt(0, 2) == 1 ? UTIL_GetRandomInt(g_iNumbers[0], g_iNumbers[1]):UTIL_GetRandomInt(g_iLettersLower[0], g_iLettersLower[1]); - // Символ - - case 0x2D: return iChar; - // Другой символ - default: - { - return UTIL_GetCharTemplate(0x58); - } - } - - 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; -} diff --git a/addons/sourcemod/translations/keys_shop_module.phrases.txt b/addons/sourcemod/translations/keys_shop_module.phrases.txt deleted file mode 100644 index a32e437..0000000 --- a/addons/sourcemod/translations/keys_shop_module.phrases.txt +++ /dev/null @@ -1,71 +0,0 @@ -"Phrases" -{ - "ERROR_INVALID_CREDITS" - { - "ru" "Неверное количество кредитов!" - "en" "Wrong number of credits!" - } - - "ERROR_INVALID_CATEGORY" - { - "ru" "Неверная категория!" - "en" "Invalid category!" - } - - "ERROR_INVALID_ITEM" - { - "ru" "Неверный предмет!" - "en" "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 deleted file mode 100644 index ae1b43c..0000000 --- a/addons/sourcemod/translations/keys_store_module.phrases.txt +++ /dev/null @@ -1,21 +0,0 @@ -"Phrases" -{ - "ERROR_INVALID_CREDITS" - { - "ru" "Неверное количество кредитов!" - "en" "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 deleted file mode 100644 index 21ca251..0000000 --- a/addons/sourcemod/translations/keys_vip_module.phrases.txt +++ /dev/null @@ -1,84 +0,0 @@ -"Phrases" -{ - "ERROR_INVALID_CREDITS" - { - "ru" "Неверное количество кредитов!" - "en" "Wrong number of credits!" - } - - "ERROR_INVALID_GROUP" - { - "ru" "Неверная VIP-группа!" - "en" "Invalid VIP-group!" - } - - "ERROR_INVALID_TIME" - { - "ru" "Неверное время!" - "en" "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 diff --git a/addons/sourcemod/translations/keys_wcs_module.phrases.txt b/addons/sourcemod/translations/keys_wcs_module.phrases.txt deleted file mode 100644 index 378d312..0000000 --- a/addons/sourcemod/translations/keys_wcs_module.phrases.txt +++ /dev/null @@ -1,71 +0,0 @@ -"Phrases" -{ - "ERROR_INVALID_RACE" - { - "ru" "Неверная раса!" - "en" "Invalid race!" - } - - "ERROR_INVALID_AMONUT" - { - "ru" "Неверное количество!" - "en" "Invalid amount!" - } - - "ERROR_INVALID_LVL" - { - "ru" "Неверный уровень!" - "en" "Invalid bank lvl!" - } - - "ERROR_HAS_OCCURRED" - { - "ru" "Произошла ошибка!" - "en" "An error has occurred!" - } - - "GOLD" - { - "ru" "Золото" - "en" "Gold" - } - - "PRIVATE_RACE" - { - "ru" "Приватная раса" - "en" "Private race" - } - - "BLVL" - { - "ru" "Уровень банка" - "en" "Bank lvl" - } - - "CHAT_PREFIX" - { - "ru" "[KEYS][WCS] " - "en" "[KEYS][WCS] " - } - - "YOU_RECEIVED_GOLD" - { - "#format" "{1:d}" - "ru" "Вы получили {1} золота!" - "en" "You received {1} gold!" - } - - "YOU_RECEIVED_BLVL" - { - "#format" "{1:d}" - "ru" "Вы получили {1} уровней банка!" - "en" "You received {1} bank levels!" - } - - "YOU_RECEIVED_PRIVATE_RACE" - { - "#format" "{1:s}" - "ru" "Вы получили приватную расу: {1}!" - "en" "You got a private race: {1}!" - } -} \ No newline at end of file From 48f2d0d633b3f831c74540c46bac451690729724 Mon Sep 17 00:00:00 2001 From: R1KO Date: Sun, 24 Sep 2017 13:31:44 +0300 Subject: [PATCH 4/8] ... --- .../addons}/sourcemod/plugins/Keys_Core.smx | Bin Core/addons/sourcemod/scripting/Keys_Core.smx | Bin 0 -> 37810 bytes .../addons}/sourcemod/scripting/Keys_Core.sp | 4 +- .../scripting/include/SteamWorks.inc | 370 +++++ .../sourcemod/scripting/include/cURL.inc | 566 ++++++++ .../scripting/include/cURL_header.inc | 1206 +++++++++++++++++ .../sourcemod/scripting/include/keys_core.inc | 16 +- .../sourcemod/scripting/include/ripext.inc | 26 + .../sourcemod/scripting/include/socket.inc | 462 +++++++ .../addons/sourcemod/scripting/keys/API.sp | 2 +- .../addons}/sourcemod/scripting/keys/BLOCK.sp | 0 .../addons}/sourcemod/scripting/keys/CMD.sp | 0 .../sourcemod/scripting/keys/EVENTS.sp | 2 +- .../addons}/sourcemod/scripting/keys/KEYS.sp | 0 .../addons}/sourcemod/scripting/keys/STATS.sp | 4 +- .../addons}/sourcemod/scripting/keys/UTIL.sp | 0 .../addons/sourcemod/scripting/keys/VARS.sp | 0 .../translations/keys_core.phrases.txt | 0 .../keys_core_logo_.png | Bin Modules/addons/sourcemod/plugins/Keys_VIP.smx | Bin 0 -> 6675 bytes .../addons/sourcemod/scripting/Keys_VIP.sp | 331 +++++ .../sourcemod/scripting/include/keys_core.inc | 171 +++ .../translations/keys_vip_module.phrases.txt | 84 ++ .../keys_module_logo.png | Bin addons/sourcemod/scripting/Keys_Example.sp | 59 - 25 files changed, 3230 insertions(+), 73 deletions(-) rename {addons => Core/addons}/sourcemod/plugins/Keys_Core.smx (100%) create mode 100644 Core/addons/sourcemod/scripting/Keys_Core.smx rename {addons => Core/addons}/sourcemod/scripting/Keys_Core.sp (99%) create mode 100644 Core/addons/sourcemod/scripting/include/SteamWorks.inc create mode 100644 Core/addons/sourcemod/scripting/include/cURL.inc create mode 100644 Core/addons/sourcemod/scripting/include/cURL_header.inc rename {addons => Core/addons}/sourcemod/scripting/include/keys_core.inc (92%) create mode 100644 Core/addons/sourcemod/scripting/include/ripext.inc create mode 100644 Core/addons/sourcemod/scripting/include/socket.inc rename addons/sourcemod/scripting/keys/api.sp => Core/addons/sourcemod/scripting/keys/API.sp (99%) rename {addons => Core/addons}/sourcemod/scripting/keys/BLOCK.sp (100%) rename {addons => Core/addons}/sourcemod/scripting/keys/CMD.sp (100%) rename {addons => Core/addons}/sourcemod/scripting/keys/EVENTS.sp (95%) rename {addons => Core/addons}/sourcemod/scripting/keys/KEYS.sp (100%) rename {addons => Core/addons}/sourcemod/scripting/keys/STATS.sp (99%) rename {addons => Core/addons}/sourcemod/scripting/keys/UTIL.sp (100%) rename addons/sourcemod/scripting/keys/vars.sp => Core/addons/sourcemod/scripting/keys/VARS.sp (100%) rename {addons => Core/addons}/sourcemod/translations/keys_core.phrases.txt (100%) rename keys_core_logo_.png => Core/keys_core_logo_.png (100%) create mode 100644 Modules/addons/sourcemod/plugins/Keys_VIP.smx create mode 100644 Modules/addons/sourcemod/scripting/Keys_VIP.sp create mode 100644 Modules/addons/sourcemod/scripting/include/keys_core.inc create mode 100644 Modules/addons/sourcemod/translations/keys_vip_module.phrases.txt rename keys_module_logo.png => Modules/keys_module_logo.png (100%) delete mode 100644 addons/sourcemod/scripting/Keys_Example.sp diff --git a/addons/sourcemod/plugins/Keys_Core.smx b/Core/addons/sourcemod/plugins/Keys_Core.smx similarity index 100% rename from addons/sourcemod/plugins/Keys_Core.smx rename to Core/addons/sourcemod/plugins/Keys_Core.smx diff --git a/Core/addons/sourcemod/scripting/Keys_Core.smx b/Core/addons/sourcemod/scripting/Keys_Core.smx new file mode 100644 index 0000000000000000000000000000000000000000..24ce6a26fe198cd9c26fe6711d68f3e4344dc30a GIT binary patch literal 37810 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 + + +/* +======================================== +The Following CURLOPT_* NOT support: +ERRORBUFFER // use curl_get_error_buffer +WRITEINFO // ??? +PROGRESSFUNCTION // unused +PROGRESSDATA // same +HEADERFUNCTION // unused +DEBUGFUNCTION // unused +DEBUGDATA // same +SHARE // unsupport +PRIVATE // unsupport +SSL_CTX_FUNC // unused +SSL_CTX_DATA // same +IOCTLFUNCTION // unused +IOCTLDATA // same +CONV_FROM_NETWORK_FUNC // unused +CONV_TO_NETWORK_FUNC // unused +CONV_FROM_UTF8_FUNC // unused +SOCKOPTFUNCTION // unused +SOCKOPTDATA // unused +OPENSOCKETFUNCTION // used +OPENSOCKETDATA // used +COPYPOSTFIELDS // unsupport +SEEKFUNCTION // unused +SEEKDATA // unused +SOCKS5_GSSAPI_SERVICE // unsupport +SOCKS5_GSSAPI_NEC // unsupport +SSH_KEYFUNCTION // unsupport +SSH_KEYDATA // unsupport +INTERLEAVEFUNCTION // unsupport +CHUNK_BGN_FUNC // unsupport +CHUNK_END_FUNC // unsupport +FNMATCH_FUNC // unsupport +CHUNK_DATA // unsupport +FNMATCH_DATA // unsupport +TLSAUTH_USERNAME // unsupport, require tls-srp +TLSAUTH_PASSWORD // unsupport, require tls-srp +TLSAUTH_TYPE // unsupport, require tls-srp +CLOSESOCKETFUNCTION // unsupport +CLOSESOCKETDATA // unsupport +========================================*/ + +/* +======================================== +The Following CURLOPT_* supports the "file://" notation. +COOKIEFILE +COOKIEJAR +RANDOM_FILE +EGDSOCKET +SSLKEY +CAPATH +NETRC_FILE +SSH_PUBLIC_KEYFILE +SSH_PRIVATE_KEYFILE +_CRLFILE +ISSUERCERT +SSH_KNOWNHOSTS + +========================================*/ + +/* +======================================== +The Following CURLINFO_* NOT support: +CURLINFO_SLIST + +========================================*/ + +/* +======================================== +The Following CURLFORM_* NOT support: +CURLFORM_PTRNAME +CURLFORM_PTRCONTENTS +CURLFORM_ARRAY +CURLFORM_BUFFER +CURLFORM_BUFFERPTR +CURLFORM_BUFFERLENGTH +CURLFORM_STREAM + +========================================*/ + + + + +/*************************************************************************************************/ +/******************************************** OPTIONS ********************************************/ +/*************************************************************************************************/ + + +/** + * The Send & Receive Action + * Using on CURL_OnSend, CURL_OnReceive + * SendRecv_Act_GOTO_SEND = go to send + * SendRecv_Act_GOTO_RECV = go to receive + * SendRecv_Act_GOTO_WAIT = go to wait + * SendRecv_Act_GOTO_END = end the connection + * SendRecv_Act_GOTO_SEND_NO_WAIT = go to send but no select + * SendRecv_Act_GOTO_RECV_NO_WAIT = go to receive but no select + * To see how it work? see curl_echo_test.sp & curl_rcon_test.sp examples + */ +enum SendRecv_Act { + SendRecv_Act_NOTHING = 0, + + SendRecv_Act_GOTO_SEND, + SendRecv_Act_GOTO_RECV, + SendRecv_Act_GOTO_WAIT, + SendRecv_Act_GOTO_END, + SendRecv_Act_GOTO_SEND_NO_WAIT, + SendRecv_Act_GOTO_RECV_NO_WAIT, + + SendRecv_Act_LAST, +}; + +/** + * Hash type for curl_hash_file, curl_hash_string + */ +enum Openssl_Hash { + Openssl_Hash_MD5 = 0, + Openssl_Hash_MD4, + Openssl_Hash_MD2, + Openssl_Hash_SHA, + Openssl_Hash_SHA1, + Openssl_Hash_SHA224, + Openssl_Hash_SHA256, + Openssl_Hash_SHA384, + Openssl_Hash_SHA512, + Openssl_Hash_RIPEMD160, +}; + + +/*************************************************************************************************/ +/******************************************* CALLBACKS *******************************************/ +/*************************************************************************************************/ + + +/** + * called if curl_easy_perform_thread() or curl_easy_send_recv() Complete + * @ param Handle hndl The curl handle + * @ param CURLcode code The CURLcode code, see cURL_header.inc + * @ param any data Data passed to curl_easy_perform_thread() + * @ noreturn + */ +typeset CURL_OnComplete +{ + function void (Handle hndl, CURLcode code); + function void (Handle hndl, CURLcode code, any data); +}; + +/** + * called if curl_easy_send_recv() before sending data + * @ param Handle hndl The curl handle + * @ param CURLcode code The last CURLcode code, see cURL_header.inc + * @ param cell_t last_sent_dataSize The last sent datasize + * @ param any data Data passed to curl_easy_send_recv() + * @ return SendRecv_Act + */ +typeset CURL_OnSend +{ + function SendRecv_Act (Handle hndl, CURLcode code, const int last_sent_dataSize); + function SendRecv_Act (Handle hndl, CURLcode code, const int last_sent_dataSize, any data); +} + +/** + * called if curl_easy_send_recv() after received data + * @ param Handle hndl The curl handle + * @ param CURLcode code The CURLcode code, see cURL_header.inc + * @ param String dataSize The received datasize + * @ param any data Data passed to curl_easy_send_recv() + * @ return SendRecv_Act + */ +typeset CURL_OnReceive +{ + function SendRecv_Act (Handle hndl, CURLcode code, const char[] receiveData, const int dataSize); + function SendRecv_Act (Handle hndl, CURLcode code, const char[] receiveData, const int dataSize, any data); +} + +/** + * called if Openssl_Hash_file() after hashed the file + * @ param bool success True on success, false if hash file fail + * @ param String buffer The hash string + * @ param any data Data passed to Openssl_Hash_file() + * @ noreturn + */ +typeset Openssl_Hash_Complete +{ + function void (const bool success, const char[] buffer); + function void (const bool success, const char[] buffer, any data); +} + + +typeset CURL_Function_CB +{ + // CURLOPT_WRITEFUNCTION + function void (Handle hndl, const char[] buffer, const int bytes, const int nmemb); + function void (Handle hndl, const char[] buffer, const int bytes, const int nmemb, any data); + + // CURLOPT_READFUNCTION + function void (Handle hndl, const int bytes, const int nmemb); + function void (Handle hndl, const int bytes, const int nmemb, any data); +} + +/*************************************************************************************************/ +/******************************************** NATIVES ********************************************/ +/*************************************************************************************************/ + + +/** + * Create a curl handle + * @ return Handle The curl handle. Returns INVALID_HANDLE on failure + */ +native Handle curl_easy_init(); + +/** + * Set a curl option for CURLOPTTYPE_OBJECTPOINT type + * + * @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential. + * @ param CURLoption opt The option to add (see enum CURLoption for details). + * @ param String buffer The value to set the option to. + * @ return bool 1 on success. 0 = The option not accept string or unsupport. + */ +native bool curl_easy_setopt_string(Handle hndl, CURLoption opt, const char[] buffer); + +/** + * Set a curl option for CURLOPTTYPE_LONG type + * + * @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential. + * @ param CURLoption opt The option to add (see enum CURLoption for details). + * @ param cell_t value The value to set the option to. + * @ return bool 1 on success. 0 = The option not accept integer or unsupport. + */ +native bool curl_easy_setopt_int(Handle hndl, CURLoption opt, int value); + +/** + * Set a curl option for CURLOPTTYPE_LONG type + * @ example" + new opt[][2] = { + {_:CURLOPT_NOPROGRESS,1}, + {_:CURLOPT_VERBOSE,0} + }; + * + * @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential. + * @ param cell_t array The option array to add (see enum CURLoption for details). + * @ param cell_t array_size The array size. + * @ return bool 1 on success. 0 = The option not accept integer or unsupport. + */ +native bool curl_easy_setopt_int_array(Handle hndl, array[][2], int array_size); + +/** + * Set a curl option for CURLOPTTYPE_OFF_T type + * + * @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential. + * @ param CURLoption opt The option to add (see enum CURLoption for details). + * @ param String buffer The value to set the option to. + * @ return bool 1 on success. 0 = The option not accept string or unsupport. + */ +native bool curl_easy_setopt_int64(Handle hndl, CURLoption opt, const char[] buffer); + +/** + * Set a curl option for CURLOPTTYPE_OBJECTPOINT type + * @ note only accept the following handle type + curl_OpenFile() + curl_httppost() + curl_slist() + * + * @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential. + * @ param CURLoption opt The option to add (see enum CURLoption for details). + * @ param Handle other_hndl The other handle to set the option to. + * @ return bool 1 on success. 0 = The option not accept string or unsupport. + */ +native bool curl_easy_setopt_handle(Handle hndl, CURLoption opt, Handle other_hndl); + +/** + * Set a curl option for CURLOPTTYPE_FUNCTIONPOINT type + * + * @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential. + * @ param CURLoption opt The option to add (see enum CURLoption for details). + * @ param CURL_Function_CB callback The value to set the option to. + * @ param cell_t value Value to set. + * @ return bool 1 on success. 0 = The option unsupport or invalid callback function. + */ +native bool curl_easy_setopt_function(Handle hndl, CURLoption opt, CURL_Function_CB callback, any value=0); + +/** + * Load all CURLoption to curl Handle + * @ note + * Using curl_easy_perform_thread() will load option in thread + * Use this on curl_easy_perform or check all CURLoption are valid or not + * Only can use one time for each curl handle + * @ return The CURLcode code, see cURL_header.inc + */ +native CURLcode curl_load_opt(Handle hndl); + +/** + * Perform a file transfer + * @ return The CURLcode code, see cURL_header.inc + */ +native CURLcode curl_easy_perform(Handle hndl); + +/** + * Perform a file transfer, using thread + * @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential. + * @ param CURL_OnComplete perform_callback The complete callback. + * @ param cell_t value Value to set. + * @ noreturn + */ +native void curl_easy_perform_thread(Handle hndl, CURL_OnComplete perform_callback, any value=0); + +/** + * Create a send & receive function for a connected curl handle + * @ param Handle hndl The handle of the curl to be used. + * @ param CURL_OnSend send_callback The send callback. + * @ param CURL_OnReceive receive_callback The receive callback. + * @ param CURL_OnComplete complete_callback The complete callback. + * @ param SendRecv_Act act The first SendRecv_Act action + * @ param cell_t send_timeout Send timeout value in milliseconds. + * @ param cell_t recv_timeout Receive timeout value in milliseconds. + * @ param cenn_t recv_buffer_Size Receive buffer size. + * @ param cell_t value Value to set. + * @ noreturn + */ +native void curl_easy_send_recv(Handle hndl, CURL_OnSend send_callback, CURL_OnReceive receive_callback, CURL_OnComplete complete_callback, SendRecv_Act act, int send_timeout, int recv_timeout, int recv_buffer_Size = 1024, any value=0); + +/** + * Send a signal to a send & receive curl handle + * @ param Handle hndl The handle of the send & receive curl to be used. + * @ param SendRecv_Act act The SendRecv_Act action after the singal + * @ return bool 1 on success. 0 = not a curl_easy_send_recv() curl, or not running/waiting + */ +native bool curl_send_recv_Signal(Handle hndl, SendRecv_Act act); + +/** + * Check send & receive curl handle is Waiting or not + * @ param Handle hndl The handle of the send & receive curl to be used. + * @ return bool 1 = is waiting. 0 = not a curl_easy_send_recv() curl, or not running/waiting + */ +native bool curl_send_recv_IsWaiting(Handle hndl); + +/** + * Send the send buffer for send & receive curl handle + * @ param Handle hndl The handle of the send & receive curl to be used. + * @ param cell_t data The data to send + * @ param cell_t size if specified the \0 terminator will not be included + * @ noreturn + */ +native void curl_set_send_buffer(Handle hndl, const char[] data, int size=-1); + +/** + * Send the receive data size for send & receive curl handle + * @ param Handle hndl The handle of the send & receive curl to be used. + * @ param cell_t size The receive size + * @ noreturn + */ +native void curl_set_receive_size(Handle hndl, int size); + +/** + * Set send timeout for curl_easy_send_recv() + * @ param Handle hndl The handle of the send & receive curl to be used. + * @ param cell_t timeout How long will try to send data before it timeout (milliseconds). + * @ noreturn + */ +native void curl_set_send_timeout(Handle hndl, int timeout); + +/** + * Set receive timeout for curl_easy_send_recv() + * @ param Handle hndl The handle of the send & receive curl to be used. + * @ param cell_t timeout How long will try to receive data before it timeout (milliseconds). + * @ noreturn + */ +native void curl_set_recv_timeout(Handle hndl, int timeout); + +/** + * Get CURLOPT_ERRORBUFFER error string in curl handle + * @ param Handle hndl The handle of the curl to be used. + * @ param String buffer Destination string buffer to copy to. + * @ param cell_t maxlen Destination buffer length (includes null terminator). + * @ noreturn + */ +native void curl_get_error_buffer(Handle hndl, char[] buffer, int maxlen); + +/** + * Extract information from a curl handle. (CURLINFO_STRING only) + * @ param Handle hndl The handle of the curl to be used. + * @ param CURLINFO info The enum CURLINFO, see cURL_header.inc + * @ param String buffer Destination string buffer to copy to. + * @ param cell_t maxlen Destination buffer length (includes null terminator). + * @ return The CURLcode code, see cURL_header.inc + */ +native CURLcode curl_easy_getinfo_string(Handle hndl, CURLINFO info, char[] buffer, int maxlen); + +/** + * Extract information from a curl handle. (CURLINFO_LONG, CURLINFO_DOUBLE only) + * @ param Handle hndl The handle of the curl to be used. + * @ param CURLINFO info The enum CURLINFO, see cURL_header.inc + * @ param value Variable to store the value. + * @ return The CURLcode code, see cURL_header.inc + */ +native CURLcode curl_easy_getinfo_int(Handle hndl, CURLINFO info, any &value); + +/** + * URL encodes the given string + * @ param Handle hndl The handle of the curl to be used. + * @ param String url The string to encodes. + * @ param String buffer Destination string buffer to copy to. + * @ param cell_t maxlen Destination buffer length (includes null terminator). + * @ return 1 on success. + */ +native bool curl_easy_escape(Handle hndl, const char[] url, char[] buffer, int maxlen); + +/** + * URL decodes the given string + * @ param Handle hndl The handle of the curl to be used. + * @ param String url The string to dencodes. + * @ param String buffer Destination string buffer to copy to. + * @ param cell_t maxlen Destination buffer length (includes null terminator). + * @ return The output length. + */ +native int curl_easy_unescape(Handle hndl, const char[] url, char[] buffer, int maxlen); + +/** + * Return string describing error code + * @ param CURLcode code The CURLcode code, see cURL_header.inc + * @ param String buffer Destination string buffer to copy to. + * @ param cell_t maxlen Destination buffer length (includes null terminator). + * @ noreturn + */ +native void curl_easy_strerror(CURLcode code, char[] buffer, int maxlen); + +/** + * Returns the libcurl version string + * @ param String buffer Destination string buffer to copy to. + * @ param cell_t maxlen Destination buffer length (includes null terminator). + * @ noreturn + */ +native void curl_version(char[] buffer, int maxlen); + +/** + * Returns the libcurl supported protocols string + * @ param String buffer Destination string buffer to copy to. + * @ param cell_t maxlen Destination buffer length (includes null terminator). + * @ noreturn + */ +native void curl_protocols(char[] buffer, int maxlen); + +/** + * Returns the libcurl supported features + * @ return The currently features bits. see CURL_VERSION_* + */ +native int curl_features(); + +/** + * This funcitopn same as Sourcemod OpenFile() + * For the following CUROPT_* only + * CURLOPT_WRITEDATA + * CURLOPT_HEADERDATA + * CURLOPT_READDATA + * CURLOPT_STDERR + * CURLOPT_INTERLEAVEDATA + * + * @ note + * Should not share to another threaded curl handle. + * + * @ param file File to open. + * @ param mode Open mode. + * @ return A Handle to the file, INVALID_HANDLE on open error. + */ +native Handle curl_OpenFile(const char[] file, const char[] mode); + + +/** + * Create a curl_httppost struct + * For the following CUROPT_* only + * CURLOPT_HTTPPOST + * @ note + * Should not share to another threaded curl handle. + * + * @ return A Handle to the curl_httppost, INVALID_HANDLE on error. + */ +native Handle curl_httppost(); + +/** + * Add a section to a multipart/formdata HTTP POST + * @ note + * Check enum CURLformoption (cURL_head.inc) to see which option supported + * + * @ param Handle hndl The handle of the curl_httppost to be used. + * @ param ... Variable number of format parameters. + * @ return The CURLFORMcode code, see cURL_header.inc + */ +native CURLFORMcode curl_formadd(Handle handl, any ...); + +/** + * Create a curl_slist struct + * For the following CUROPT_* only + * CURLOPT_QUOTE + * CURLOPT_HTTPHEADER + * CURLOPT_POSTQUOTE + * CURLOPT_TELNETOPTIONS + * CURLOPT_PREQUOTE + * CURLOPT_HTTP200ALIASES + * CURLOPT_MAIL_RCPT + * CURLOPT_RESOLVE + * + * @ note + * Should not share to another threaded curl handle. + * + * @ return A Handle to the curl_slist, INVALID_HANDLE on error. + */ +native Handle curl_slist(); + +/** + * Add a string to an slist + * @ param Handle hndl The handle of the curl_slist to be used. + * @ param String buffer The string to add + * @ noreturn + */ +native void curl_slist_append(Handle hndl, const char[] buffer); + +/** + * Hash a file + * @ parma String file The file path. supports the "file://" notation. + * @ param Openssl_Hash algorithm Hash Algorithm. + * @ param Openssl_Hash_Complete complete_callback The complete callback. + * @ param cell_t value Value to set. + * @ noreturn + */ +native void curl_hash_file(const char[] file, Openssl_Hash algorithm, Openssl_Hash_Complete complete_callback, any value=0); + +/** + * Hash a string + * @ parma String input The string to hash. + * @ param cell_t dataSize The input string size. + * @ param Openssl_Hash algorithm Hash Algorithm. + * @ param String buffer Destination string buffer to copy to. + * @ param cell_t maxlen Destination buffer length (includes null terminator). + * @ return 1 on success + */ +native bool curl_hash_string(const char[] input, int dataSize, Openssl_Hash algorithm, char[] buffer, int maxlength); + + +/** + * Do not edit below this line! + */ +public Extension __ext_curl = +{ + name = "curl", + file = "curl.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; diff --git a/Core/addons/sourcemod/scripting/include/cURL_header.inc b/Core/addons/sourcemod/scripting/include/cURL_header.inc new file mode 100644 index 0000000..cf51cf1 --- /dev/null +++ b/Core/addons/sourcemod/scripting/include/cURL_header.inc @@ -0,0 +1,1206 @@ +#if defined _cURL_header_included + #endinput +#endif +#define _cURL_header_included + + +/* SourceMod */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T + + +#define CURL_MAX_WRITE_SIZE 16384 + +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* kerberos auth is supported */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth support */ +#define CURL_VERSION_DEBUG (1<<6) /* built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* asynchronous dns resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth */ +#define CURL_VERSION_LARGEFILE (1<<9) /* supports files bigger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* International Domain Names support */ +#define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */ +#define CURL_VERSION_CONV (1<<12) /* character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegating to winbind helper */ + + +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +#define CINIT(%1,%2,%3) CURLOPT_%1 = %2 + %3 + + /* three convenient "aliases" that follow the name scheme better */ +#define CURLOPT_WRITEDATA CURLOPT_FILE +#define CURLOPT_READDATA CURLOPT_INFILE +#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER +#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER + +/* This was added in version 7.19.1 */ +#define CURLOPT_POST301 CURLOPT_POSTREDIR + +/* The following were added in 7.17.0 */ +#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_FTPAPPEND CURLOPT_APPEND +#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY +#define CURLOPT_FTP_SSL CURLOPT_USE_SSL + +/* The following were added earlier */ + +#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL + +enum CURLoption { + /* This is the FILE * or void * the regular output should be written to. */ + CINIT(FILE, OBJECTPOINT, 1), + + /* The full URL to get/put */ + CINIT(URL, OBJECTPOINT, 2), + + /* Port number to connect to, if other than default. */ + CINIT(PORT, LONG, 3), + + /* Name of proxy to use. */ + CINIT(PROXY, OBJECTPOINT, 4), + + /* "name:password" to use when fetching. */ + CINIT(USERPWD, OBJECTPOINT, 5), + + /* "name:password" to use with proxy. */ + CINIT(PROXYUSERPWD, OBJECTPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CINIT(RANGE, OBJECTPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CINIT(INFILE, OBJECTPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. If this is not used, error messages go to stderr instead: */ + CINIT(ERRORBUFFER, OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CINIT(READFUNCTION, FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CINIT(TIMEOUT, LONG, 13), + + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was successful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an OFF_T type, allowing platforms with larger OFF_T + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CINIT(INFILESIZE, LONG, 14), + + /* POST static input fields. */ + CINIT(POSTFIELDS, OBJECTPOINT, 15), + + /* Set the referrer page (needed by some CGIs) */ + CINIT(REFERER, OBJECTPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CINIT(FTPPORT, OBJECTPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CINIT(USERAGENT, OBJECTPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CINIT(LOW_SPEED_LIMIT, LONG, 19), + + /* Set the "low speed time" */ + CINIT(LOW_SPEED_TIME, LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * OFF_T types, allowing for large file offsets on platforms which + * use larger-than-32-bit OFF_T's. Look below for RESUME_FROM_LARGE. + */ + CINIT(RESUME_FROM, LONG, 21), + + /* Set cookie in request: */ + CINIT(COOKIE, OBJECTPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind */ + CINIT(HTTPHEADER, OBJECTPOINT, 23), + + /* This points to a linked list of post entries, struct curl_httppost */ + CINIT(HTTPPOST, OBJECTPOINT, 24), + + /* name of the file keeping your private SSL-certificate */ + CINIT(SSLCERT, OBJECTPOINT, 25), + + /* password for the SSL or SSH private key */ + CINIT(KEYPASSWD, OBJECTPOINT, 26), + + /* send TYPE parameter? */ + CINIT(CRLF, LONG, 27), + + /* send linked-list of QUOTE commands */ + CINIT(QUOTE, OBJECTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CINIT(WRITEHEADER, OBJECTPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CINIT(COOKIEFILE, OBJECTPOINT, 31), + + /* What version to specifically try to use. + See CURL_SSLVERSION defines below. */ + CINIT(SSLVERSION, LONG, 32), + + /* What kind of HTTP time condition to use, see defines */ + CINIT(TIMECONDITION, LONG, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CINIT(TIMEVALUE, LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CINIT(CUSTOMREQUEST, OBJECTPOINT, 36), + + /* HTTP request, for odd commands like DELETE, TRACE and others */ + CINIT(STDERR, OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CINIT(POSTQUOTE, OBJECTPOINT, 39), + + /* Pass a pointer to string of the output using full variable-replacement + as described elsewhere. */ + CINIT(WRITEINFO, OBJECTPOINT, 40), /* DEPRECATED, do not use! */ + + CINIT(VERBOSE, LONG, 41), /* talk a lot */ + CINIT(HEADER, LONG, 42), /* throw the header out too */ + CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ + CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 300 */ + CINIT(UPLOAD, LONG, 46), /* this is an upload */ + CINIT(POST, LONG, 47), /* HTTP POST method */ + CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ + + CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CINIT(NETRC, LONG, 51), + + CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + + CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ + CINIT(PUT, LONG, 54), /* HTTP PUT */ + + /* 55 = OBSOLETE */ + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + + /* Data passed to the progress callback */ + CINIT(PROGRESSDATA, OBJECTPOINT, 57), + + /* We want the referrer field set automatically when following locations */ + CINIT(AUTOREFERER, LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CINIT(PROXYPORT, LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CINIT(POSTFIELDSIZE, LONG, 60), + + /* tunnel non-http operations through a HTTP proxy */ + CINIT(HTTPPROXYTUNNEL, LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CINIT(INTERFACE, OBJECTPOINT, 62), + + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but doesn't match one of these, 'private' will be used. */ + CINIT(KRBLEVEL, OBJECTPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CINIT(SSL_VERIFYPEER, LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAINFO, OBJECTPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CINIT(MAXREDIRS, LONG, 68), + + /* Pass a LONG set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CINIT(FILETIME, LONG, 69), + + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + + /* Max amount of cached alive connections */ + CINIT(MAXCONNECTS, LONG, 71), + + CINIT(CLOSEPOLICY, LONG, 72), /* DEPRECATED, do not use! */ + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you're absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CINIT(FRESH_CONNECT, LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be re-used + when done. Do not use this unless you're absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CINIT(FORBID_REUSE, LONG, 75), + + /* Set to a file name that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CINIT(RANDOM_FILE, OBJECTPOINT, 76), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CINIT(EGDSOCKET, OBJECTPOINT, 77), + + /* Time-out connect operations after this amount of seconds, if connects + are OK within this time, then fine... This only aborts the connect + phase. [Only works on unix-style/SIGALRM operating systems] */ + CINIT(CONNECTTIMEOUT, LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CINIT(HTTPGET, LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CINIT(SSL_VERIFYHOST, LONG, 81), + + /* Specify which file name to write all known cookies in after completed + operation. Set file name to "-" (dash) to make it go to stdout. */ + CINIT(COOKIEJAR, OBJECTPOINT, 82), + + /* Specify which SSL ciphers to use */ + CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CINIT(HTTP_VERSION, LONG, 84), + + /* Specifically switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CINIT(FTP_USE_EPSV, LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CINIT(SSLCERTTYPE, OBJECTPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CINIT(SSLKEY, OBJECTPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CINIT(SSLKEYTYPE, OBJECTPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CINIT(SSLENGINE, OBJECTPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CINIT(SSLENGINE_DEFAULT, LONG, 90), + + /* Non-zero value means to use the global dns cache */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */ + + /* DNS cache timeout */ + CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands */ + CINIT(PREQUOTE, OBJECTPOINT, 93), + + /* set the debug function */ + CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CINIT(DEBUGDATA, OBJECTPOINT, 95), + + /* mark this as start of a cookie session */ + CINIT(COOKIESESSION, LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAPATH, OBJECTPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CINIT(BUFFERSIZE, LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CINIT(NOSIGNAL, LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CINIT(SHARE, OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. Before 7.21.6, this was known as + CURLOPT_ENCODING */ + CINIT(ACCEPT_ENCODING, OBJECTPOINT, 102), + + /* Set pointer to private data */ + CINIT(PRIVATE, OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentially send off the name + and password to whatever host the server decides. */ + CINIT(UNRESTRICTED_AUTH, LONG, 105), + + /* Specifically switch on or off the FTP engine's use of the EPRT command ( + it also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CINIT(FTP_USE_EPRT, LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(HTTPAUTH, LONG, 107), + + /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx + in second argument. The function must be matching the + curl_ssl_ctx_callback proto. */ + CINIT(SSL_CTX_FUNC, FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ + CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(PROXYAUTH, LONG, 111), + + /* FTP option that changes the timeout, in seconds, associated with + getting a response. This is different from transfer timeout time and + essentially places a demand on the FTP server to acknowledge commands + in a timely manner. */ + CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), +#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to resolve names to those IP versions only. This only has + affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CINIT(IPRESOLVE, LONG, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger OFF_T sizes. See MAXFILESIZE_LARGE below. */ + CINIT(MAXFILESIZE, LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CINIT(INFILESIZE_LARGE, OFF_T, 115), + + /* Sets the continuation offset. There is also a LONG version of this; + * look above for RESUME_FROM. + */ + CINIT(RESUME_FROM_LARGE, OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + + /* Set this option to the file name of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CINIT(NETRC_FILE, OBJECTPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLFTPSSL_TRY - try using SSL, proceed anyway otherwise + CURLFTPSSL_CONTROL - SSL for the control connection or fail + CURLFTPSSL_ALL - SSL for all communication or fail + */ + CINIT(USE_SSL, LONG, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CINIT(TCP_NODELAY, LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + + /* feed cookies into cookie engine */ + CINIT(COOKIELIST, OBJECTPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CINIT(CONNECT_ONLY, LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_FROM_NETWORK_FUNC, FUNCTIONPOINT, 142), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_TO_NETWORK_FUNC, FUNCTIONPOINT, 143), + + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CINIT(CONV_FROM_UTF8_FUNC, FUNCTIONPOINT, 144), + + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), + CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + + /* Pointer to command string to send if USER/PASS fails. */ + CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147), + + /* callback function for setting socket options */ + CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), + CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + + /* set to 0 to disable session ID re-use for this transfer, default is + enabled (== 1) */ + CINIT(SSL_SESSIONID_CACHE, LONG, 150), + + /* allowed SSH authentication methods */ + CINIT(SSH_AUTH_TYPES, LONG, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CINIT(FTP_SSL_CCC, LONG, 154), + + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CINIT(TIMEOUT_MS, LONG, 155), + CINIT(CONNECTTIMEOUT_MS, LONG, 156), + + /* set to zero to disable the libcurl's decoding and thus pass the raw body + data to the application even when it is encoded/compressed */ + CINIT(HTTP_TRANSFER_DECODING, LONG, 157), + CINIT(HTTP_CONTENT_DECODING, LONG, 158), + + /* Permission used when creating new files and directories on the remote + server for protocols that support it, SFTP/SCP/FILE */ + CINIT(NEW_FILE_PERMS, LONG, 159), + CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + + /* Set the behaviour of POST when redirecting. Values must be set to one + of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ + CINIT(POSTREDIR, LONG, 161), + + /* used by scp/sftp to verify the host's public key */ + CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), + + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), + CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + + /* POST volatile input fields. */ + CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CINIT(PROXY_TRANSFER_MODE, LONG, 166), + + /* Callback function for seeking in the input stream */ + CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), + CINIT(SEEKDATA, OBJECTPOINT, 168), + + /* CRL file */ + CINIT(CRLFILE, OBJECTPOINT, 169), + + /* Issuer certificate */ + CINIT(ISSUERCERT, OBJECTPOINT, 170), + + /* (IPv6) Address scope */ + CINIT(ADDRESS_SCOPE, LONG, 171), + + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. (Unfortunately) only + working with OpenSSL-powered builds. */ + CINIT(CERTINFO, LONG, 172), + + /* "name" and "pwd" to use when fetching. */ + CINIT(USERNAME, OBJECTPOINT, 173), + CINIT(PASSWORD, OBJECTPOINT, 174), + + /* "name" and "pwd" to use with Proxy when fetching. */ + CINIT(PROXYUSERNAME, OBJECTPOINT, 175), + CINIT(PROXYPASSWORD, OBJECTPOINT, 176), + + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CINIT(NOPROXY, OBJECTPOINT, 177), + + /* block size for TFTP transfers */ + CINIT(TFTP_BLKSIZE, LONG, 178), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_SERVICE, OBJECTPOINT, 179), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + CINIT(PROTOCOLS, LONG, 181), + + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. Defaults + to all protocols except FILE and SCP. */ + CINIT(REDIR_PROTOCOLS, LONG, 182), + + /* set the SSH knownhost file name to use */ + CINIT(SSH_KNOWNHOSTS, OBJECTPOINT, 183), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + + /* set the SSH host key callback custom pointer */ + CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + + /* set the SMTP mail originator */ + CINIT(MAIL_FROM, OBJECTPOINT, 186), + + /* set the SMTP mail receiver(s) */ + CINIT(MAIL_RCPT, OBJECTPOINT, 187), + + /* FTP: send PRET before PASV */ + CINIT(FTP_USE_PRET, LONG, 188), + + /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ + CINIT(RTSP_REQUEST, LONG, 189), + + /* The RTSP session identifier */ + CINIT(RTSP_SESSION_ID, OBJECTPOINT, 190), + + /* The RTSP stream URI */ + CINIT(RTSP_STREAM_URI, OBJECTPOINT, 191), + + /* The Transport: header to use in RTSP requests */ + CINIT(RTSP_TRANSPORT, OBJECTPOINT, 192), + + /* Manually initialize the client RTSP CSeq for this handle */ + CINIT(RTSP_CLIENT_CSEQ, LONG, 193), + + /* Manually initialize the server RTSP CSeq for this handle */ + CINIT(RTSP_SERVER_CSEQ, LONG, 194), + + /* The stream to pass to INTERLEAVEFUNCTION. */ + CINIT(INTERLEAVEDATA, OBJECTPOINT, 195), + + /* Let the application define a custom write method for RTP data */ + CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), + + /* Turn on wildcard matching */ + CINIT(WILDCARDMATCH, LONG, 197), + + /* Directory matching callback called before downloading of an + individual file (chunk) started */ + CINIT(CHUNK_BGN_FUNC, FUNCTIONPOINT, 198), + + /* Directory matching callback called after the file (chunk) + was downloaded, or skipped */ + CINIT(CHUNK_END_FUNC, FUNCTIONPOINT, 199), + + /* Change match (fnmatch-like) callback for wildcard matching */ + CINIT(FNMATCH_FUNC, FUNCTIONPOINT, 200), + + /* Let the application define custom chunk data pointer */ + CINIT(CHUNK_DATA, OBJECTPOINT, 201), + + /* FNMATCH_FUNC user pointer */ + CINIT(FNMATCH_DATA, OBJECTPOINT, 202), + + /* send linked-list of name:port:address sets */ + CINIT(RESOLVE, OBJECTPOINT, 203), + + /* Set a username for authenticated TLS */ + CINIT(TLSAUTH_USERNAME, OBJECTPOINT, 204), + + /* Set a password for authenticated TLS */ + CINIT(TLSAUTH_PASSWORD, OBJECTPOINT, 205), + + /* Set authentication type for authenticated TLS */ + CINIT(TLSAUTH_TYPE, OBJECTPOINT, 206), + + /* Set to 1 to enable the "TE:" header in HTTP requests to ask for + compressed transfer-encoded responses. Set to 0 to disable the use of TE: + in outgoing requests. The current default is 0, but it might change in a + future libcurl release. + + libcurl will ask for the compressed methods it knows of, and if that + isn't any, it will not ask for transfer-encoding at all even if this + option is set to 1. + + */ + CINIT(TRANSFER_ENCODING, LONG, 207), + + /* Callback function for closing socket (instead of close(2)). The callback + should have type curl_closesocket_callback */ + CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208), + CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209), + + /* allow GSSAPI credential delegation */ + CINIT(GSSAPI_DELEGATION, LONG, 210), + + CURLOPT_LASTENTRY /* the last unused */ +}; + + +enum CURLcode { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for + 7.17.0, reused in April 2011 for 7.21.5] */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server + due to lack of access - when login fails + this is not returned. */ + CURLE_OBSOLETE10, /* 10 - NOT USED */ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_OBSOLETE12, /* 12 - NOT USED */ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_OBSOLETE16, /* 16 - NOT USED */ + CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_OBSOLETE20, /* 20 - NOT USED */ + CURLE_QUOTE_ERROR, /* 21 - quote command failure */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_OBSOLETE24, /* 24 - NOT USED */ + CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ + CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ + CURLE_OBSOLETE29, /* 29 - NOT USED */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_OBSOLETE32, /* 32 - NOT USED */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_OBSOLETE40, /* 40 - NOT USED */ + CURLE_FUNCTION_NOT_FOUND, /* 41 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_OBSOLETE44, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_OBSOLETE46, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ + CURLE_OBSOLETE50, /* 50 - NOT USED */ + CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint + wasn't verified fine */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_OBSOLETE57, /* 57 - NOT IN USE */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ + CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_CONV_FAILED, /* 75 - conversion failed */ + CURLE_CONV_REQD, /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPT_CONV_TO_NETWORK_FUNCTION, and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ + CURLE_AGAIN, /* 81 - socket is not ready for send/recv, + wait till it's ready and try again (Added + in 7.18.2) */ + CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or + wrong format (Added in 7.19.0) */ + CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in + 7.19.0) */ + CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ + CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ + CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ + CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ + CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ + + CURL_LAST /* never use! */ +}; + +/* compatibility with older names */ +#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING + +/* The following were added in 7.21.5, April 2011 */ +#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION + +enum curl_TimeCond { + CURL_TIMECOND_NONE, + + CURL_TIMECOND_IFMODSINCE, + CURL_TIMECOND_IFUNMODSINCE, + CURL_TIMECOND_LASTMOD, + + CURL_TIMECOND_LAST +}; + +/* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +}; + +enum curl_closepolicy { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY, // CURLCLOSEPOLICY_LEAST_RECENTLY_USED + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +}; + +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd + like the library to choose the best possible + for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +}; + + +#define CURLAUTH_NONE 0 /* nothing */ +#define CURLAUTH_BASIC (1<<0) /* Basic (default) */ +#define CURLAUTH_DIGEST (1<<1) /* Digest */ +#define CURLAUTH_GSSNEGOTIATE (1<<2) /* GSS-Negotiate */ +#define CURLAUTH_NTLM (1<<3) /* NTLM */ +#define CURLAUTH_DIGEST_IE (1<<4) /* Digest with IE flavour */ +#define CURLAUTH_NTLM_WB (1<<5) /* NTLM delegating to winbind helper */ +#define CURLAUTH_ONLY (1<<31) /* used together with a single other + type to force no auth or just that + single type */ +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) /* all fine types set */ +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) + + +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +enum curl_ftpcreatedir { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +}; + + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* resolve to ipv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to ipv6 addresses */ + + +/* parameter for the CURLOPT_USE_SSL option */ +enum curl_usessl { + CURLUSESSL_NONE, /* do not attempt to use SSL */ + CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ + CURLUSESSL_ALL, /* SSL for all communication or fail */ + CURLUSESSL_LAST /* not an option, never use */ +}; + + +enum { + CURL_SSLVERSION_DEFAULT, + CURL_SSLVERSION_TLSv1, + CURL_SSLVERSION_SSLv2, + CURL_SSLVERSION_SSLv3, + + CURL_SSLVERSION_LAST /* never use, keep last */ +}; + + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +enum curl_ftpauth { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +}; + + +enum curl_ftpfile { + FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ + FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */ + FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the file */ +}; + + +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ + + +/* parameter for the CURLOPT_FTP_SSL_CCC option */ +enum curl_ftpccc { + CURLFTPSSL_CCC_NONE, /* do not send CCC */ + CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ + CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ + CURLFTPSSL_CCC_LAST /* not an option, never use */ +}; + + +/* symbols to use with CURLOPT_POSTREDIR. + CURL_REDIR_POST_301 and CURL_REDIR_POST_302 can be bitwise ORed so that + CURL_REDIR_POST_301 | CURL_REDIR_POST_302 == CURL_REDIR_POST_ALL */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_ALL (CURL_REDIR_POST_301|CURL_REDIR_POST_302) + + +/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ +#define CURLPROTO_HTTP (1<<0) +#define CURLPROTO_HTTPS (1<<1) +#define CURLPROTO_FTP (1<<2) +#define CURLPROTO_FTPS (1<<3) +#define CURLPROTO_SCP (1<<4) +#define CURLPROTO_SFTP (1<<5) +#define CURLPROTO_TELNET (1<<6) +#define CURLPROTO_LDAP (1<<7) +#define CURLPROTO_LDAPS (1<<8) +#define CURLPROTO_DICT (1<<9) +#define CURLPROTO_FILE (1<<10) +#define CURLPROTO_TFTP (1<<11) +#define CURLPROTO_IMAP (1<<12) +#define CURLPROTO_IMAPS (1<<13) +#define CURLPROTO_POP3 (1<<14) +#define CURLPROTO_POP3S (1<<15) +#define CURLPROTO_SMTP (1<<16) +#define CURLPROTO_SMTPS (1<<17) +#define CURLPROTO_RTSP (1<<18) +#define CURLPROTO_RTMP (1<<19) +#define CURLPROTO_RTMPT (1<<20) +#define CURLPROTO_RTMPE (1<<21) +#define CURLPROTO_RTMPTE (1<<22) +#define CURLPROTO_RTMPS (1<<23) +#define CURLPROTO_RTMPTS (1<<24) +#define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_ALL (~0) /* enable everything */ + + +/* + * Public API enums for RTSP requests + */ +enum { + CURL_RTSPREQ_NONE, /* first in list */ + CURL_RTSPREQ_OPTIONS, + CURL_RTSPREQ_DESCRIBE, + CURL_RTSPREQ_ANNOUNCE, + CURL_RTSPREQ_SETUP, + CURL_RTSPREQ_PLAY, + CURL_RTSPREQ_PAUSE, + CURL_RTSPREQ_TEARDOWN, + CURL_RTSPREQ_GET_PARAMETER, + CURL_RTSPREQ_SET_PARAMETER, + CURL_RTSPREQ_RECORD, + CURL_RTSPREQ_RECEIVE, + CURL_RTSPREQ_LAST /* last in list */ +}; + + + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +enum CURLINFO { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_CONTENT_LEN_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LEN_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, + CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, + CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, + CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_SLIST + 34, + CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, + CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36, + CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37, + CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG + 38, + CURLINFO_RTSP_CSEQ_RECV = CURLINFO_LONG + 39, + CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, + CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, + CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 42 +}; + + +enum curl_proxytype { + CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use + CONNECT HTTP/1.1 */ + CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT + HTTP/1.0 */ + CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already + in 7.10 */ + CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ + CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ + CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the + host name rather than the IP address. added + in 7.18.0 */ +}; + + +enum CURLformoption { + CURLFORM_NOTHING, /********* the first one is unused ************/ + /* */ + CURLFORM_COPYNAME, // char + CURLFORM_PTRNAME, // not support + CURLFORM_NAMELENGTH, // long + CURLFORM_COPYCONTENTS, // char + CURLFORM_PTRCONTENTS, // not support + CURLFORM_CONTENTSLENGTH, // long + CURLFORM_FILECONTENT, // char + CURLFORM_ARRAY, // not support + CURLFORM_OBSOLETE, + CURLFORM_FILE, // char + CURLFORM_BUFFER, // not support + CURLFORM_BUFFERPTR, // not support + CURLFORM_BUFFERLENGTH, // not support + CURLFORM_CONTENTTYPE, // char + CURLFORM_CONTENTHEADER, // curl_slist + CURLFORM_FILENAME, // char + CURLFORM_END, // !! + CURLFORM_OBSOLETE2, + CURLFORM_STREAM, // not support + CURLFORM_LASTENTRY /* the last unused */ +}; + +enum CURLFORMcode { + CURL_FORMADD_OK, /* first, no error */ + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ + CURL_FORMADD_LAST /* last */ +}; diff --git a/addons/sourcemod/scripting/include/keys_core.inc b/Core/addons/sourcemod/scripting/include/keys_core.inc similarity index 92% rename from addons/sourcemod/scripting/include/keys_core.inc rename to Core/addons/sourcemod/scripting/include/keys_core.inc index dff77ee..de159d8 100644 --- a/addons/sourcemod/scripting/include/keys_core.inc +++ b/Core/addons/sourcemod/scripting/include/keys_core.inc @@ -7,16 +7,16 @@ enum KeysAction { - Validation = 0, - Activation, - Print + Validation = 0, // Валидация параметров ключа + Activation, // Активация ключа + Print // Вывод ключа в консоль/файл }; enum KeysNativeAction { - Add = 0, - Rem, - Use + Add = 0, // Добавление/Генерация ключа + Rem, // Удаление ключа + Use // Использование ключа }; // Прототип вызова при валидации параметров/акцивации/выводе параметров ключа @@ -141,7 +141,7 @@ stock void Keys_GetTimeFromStamp(char[] sBuffer, int iMaxLength, int iTimeStamp, public SharedPlugin __pl_keys_core= { - name = "keys_core_v2", + name = "keys_core", file = "Keys_Core.smx", #if defined REQUIRE_PLUGIN required = 1 @@ -152,7 +152,7 @@ public SharedPlugin __pl_keys_core= #if !defined REQUIRE_PLUGIN -public __pl_keys_core_SetNTVOptional() +public void __pl_keys_core_SetNTVOptional() { MarkNativeAsOptional("Keys_IsCoreStarted"); MarkNativeAsOptional("Keys_GetCoreDatabase"); diff --git a/Core/addons/sourcemod/scripting/include/ripext.inc b/Core/addons/sourcemod/scripting/include/ripext.inc new file mode 100644 index 0000000..9fc02e3 --- /dev/null +++ b/Core/addons/sourcemod/scripting/include/ripext.inc @@ -0,0 +1,26 @@ +#if defined _ripext_included_ + #endinput +#endif +#define _ripext_included_ + +#include +#include + +/** + * Do not edit below this line! + */ +public Extension __ext_rip = +{ + name = "REST in Pawn", + file = "rip.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; diff --git a/Core/addons/sourcemod/scripting/include/socket.inc b/Core/addons/sourcemod/scripting/include/socket.inc new file mode 100644 index 0000000..dd432ef --- /dev/null +++ b/Core/addons/sourcemod/scripting/include/socket.inc @@ -0,0 +1,462 @@ +// socket extension include file + +#if defined _socket_included + #endinput +#endif +#define _socket_included +#include + +enum SocketType { + SOCKET_TCP = 1, + SOCKET_UDP, + SOCKET_RAW +} + +#define EMPTY_HOST 1 +#define NO_HOST 2 +#define CONNECT_ERROR 3 +#define SEND_ERROR 4 +#define BIND_ERROR 5 +#define RECV_ERROR 6 +#define LISTEN_ERROR 7 + + +/*************************************************************************************************/ +/******************************************** options ********************************************/ +/*************************************************************************************************/ + + +/** + * Options available for SocketSetOption() + * + * @note modifying these options is not required for normal operation, you can skip the whole + * section in most cases. + */ +enum SocketOption { +/** + * If this option is set the socket extension will try to concatenate SocketReceive callbacks. + * + * This will possibly lower the amount of callbacks passed to SourceMod plugins and improve the + * performance. The socket extension will preserve the packet order. + * + * @note this doesn't prevent multiple callbacks, it only reduces them for high load. + * @note this will not truncate packets below 4096 bytes, setting it lower will be ignored + * @note set this option if you expect lots of data in a short timeframe + * @note don't forget to set your buffer sizes at least to the value passed to this function, but + * always at least to 4096 + * + * @param cell_t 0(=default) to disable or max. chunk size including \0 terminator in bytes + * @return bool true on success + */ + ConcatenateCallbacks = 1, +/** + * If this option is set the socket extension will enforce a mutex lock in the GameFrame() hook. + * + * This will ensure that callbacks will be processed every gameframe as fast as possible with the + * drawback of potentially creating lag. It's not recommended to set this option for most cases. + * If this option is not set the gameframe will be skipped if quietly obtaining a lock fails. + * + * @note combine this with CallbacksPerFrame for best performance + * @note this option will affect all sockets from all plugins, use it with caution! + * + * @param bool whether to force locking or not + * @return bool true on success + */ + ForceFrameLock, +/** + * This will specify the maximum amount of callbacks processed in every gameframe. + * + * The default value for this option is 1, setting it higher will possibly increase networking + * performance but may cause lag if it's set too high. + * The amount of callbacks actually being processed is limited by not being able to quietly obtain + * a lock (see ForceFrameLock) and the amount of callbacks in the queue. + * + * @note this option will affect all sockets from all plugins, use it with caution! + * + * @param cell_t maximum amount of callbacks per gameframe + * @return bool true on success + */ + CallbacksPerFrame, +/** + * If this option is set the socket will be allowed to send broadcast messages in case the protocol + * supports it. This is a wrapper for setting SO_BROADCAST. + * + * @param bool whether to allow broadcasting or not + * @return bool true on success + */ + SocketBroadcast, +/** + * If this option is set SocketBind() will allow reusing local adresses in case the protocol + * supports it. This is a wrapper for setting SO_REUSEADDR. + * + * @param bool whether to allow broadcasting or not + * @return bool true on success + */ + SocketReuseAddr, +/** + * If this option is set the socket will try to keep the connection alive by periodically sending + * messages if the protocol supports it. This is a wrapper for setting SO_KEEPALIVE. + * + * @param bool whether to allow broadcasting or not + * @return bool true on success + */ + SocketKeepAlive, +/** + * This option specifies how long a socket will wait if it's being closed and its send buffer is + * still filled. This is a wrapper for setting SO_LINGER. + * + * @param cell_t 0 (=default) to disable or time in s + * @return bool true on success + */ + SocketLinger, +/** + * If this option is set out-of-band data will be inlined into the normal receive stream. This is a + * wrapper for setting SO_OOBINLINE. + * + * @param bool whether to inline out-of-band data or not + * @return bool true on success + */ + SocketOOBInline, +/** + * This option specifies how large the send buffer will be. This is a wrapper for setting + * SO_SNDBUF. + * + * @param cell_t size in bytes + * @return bool true on success + */ + SocketSendBuffer, +/** + * This option specifies how large the receive buffer will be. This is a wrapper for setting + * SO_RCVBUF. + * + * @param cell_t size in bytes + * @return bool true on success + */ + SocketReceiveBuffer, +/** + * If this option is set outgoing messages will ignore the default routing facilities if the + * protocol implementation supports it. The remote site should be directly connected to the sender. + * This is a wrapper for setting SO_DONTROUTE. + * + * @param bool whether to skip default routing or not + * @return bool true on success + */ + SocketDontRoute, +/** + * This option specifies the minimum amount of data to receive before processing it. This is a + * wrapper for setting SO_RCVLOWAT. + * + * @note this can probably block the extension, use it with caution! + * + * @param cell_t size in bytes + * @return bool true on success + */ + SocketReceiveLowWatermark, +/** + * This option specifies how long a socket will try to receive data before it times out and + * processes the data. This is a wrapper for setting SO_RCVTIMEO. + * + * @param cell_t 0 (=default) to disable or time in ms + * @return bool true on success + */ + SocketReceiveTimeout, +/** + * This option specifies the minimum amount of data required in the send buffer before starting to + * send it. This is a wrapper for setting SO_SNDLOWAT. + * + * @note this can probably block the extension, use it with caution! + * + * @param cell_t size in bytes + * @return bool true on success + */ + SocketSendLowWatermark, +/** + * This option specifies how long a socket will try to send data before it times out and + * retries it later. This is a wrapper for setting SO_SNDTIMEO. + * + * @param cell_t 0 (=default) to disable or time in ms + * @return bool true on success + */ + SocketSendTimeout, +/** + * If this option is set the socket extension will display debugging messages in the server console/logs. + * + * @param bool whether to enable debugging or not + * @return bool true on success + */ + DebugMode +} + + +/*************************************************************************************************/ +/******************************************* callbacks *******************************************/ +/*************************************************************************************************/ + + +/** + * triggered if a normal sockets finished connecting and is ready to be used + * + * @param socket The socket handle pointing to the calling socket + * @param arg The argument set by SocketSetArg() + * @noreturn + */ +typedef SocketConnectCB = function void (Handle hSocket, any arg); + + +/** + * triggered if a listening socket received an incoming connection and is ready to be used + * + * @note The child-socket won't work until receive-, disconnect-, and errorcallback for it are set. + * + * @param Handle socket The socket handle pointing to the calling listen-socket + * @param Handle newSocket The socket handle to the newly spawned child socket + * @param String remoteIP The remote IP + * @param any arg The argument set by SocketSetArg() for the listen-socket + * @noreturn + */ +typedef SocketIncomingCB = function void (Handle hSocket, Handle hNewSocket, const char[] remoteIP, int remotePort, any arg); + +/** + * triggered if a socket receives data + * + * @note This is binary safe if you always use dataSize for operations on receiveData[] + * @note packets may be split up into multiple chunks -> multiple calls to the receive callback + * @note if not set otherwise by SocketSetOption(..., ConcatenateCallbacks, ...) receiveData will + * never be longer than 4096 characters including \0 terminator + * + * @param Handle socket The socket handle pointing to the calling socket + * @param String receiveData The data which arrived, 0-terminated at receiveData[dataSize] + * @param cell_t dataSize The length of the arrived data excluding the 0-termination + * @param any arg The argument set by SocketSetArg() for the socket + * @noreturn + */ +typedef SocketReceiveCB = function void (Handle hSocket, const char[] receiveData, const int dataSize, any arg); + +/** + * called after a socket sent all items in its send queue successfully + * + * @param Handle socket The socket handle pointing to the calling socket + * @param any arg The argument set by SocketSetArg() for the socket + * @noreturn + */ +typedef SocketSendqueueEmptyCB = function void (Handle hSocket, any arg); + +/** + * called if a socket has been properly disconnected by the remote side + * + * @note You should call CloseHandle(socket) or reuse the socket before this function ends + * + * @param Handle socket The socket handle pointing to the calling socket + * @param any arg The argument set by SocketSetArg() for the socket + * @noreturn + */ +typedef SocketDisconnectCB = function void (Handle hSocket, any arg); + +/** + * called if an unrecoverable error occured, close the socket without an additional call to a disconnect callback + * + * @note You should call CloseHandle(socket) or reuse the socket before this function ends + * + * @param Handle socket The socket handle pointing to the calling socket + * @param cell_t errorType The error type, see defines above + * @param cell_t errorNum The errno, see errno.h for details + * @param any arg The argument set by SocketSetArg() for the socket + * @noreturn + */ +typedef SocketErrorCB = function void (Handle hSocket, const int errorType, const int errorNum, any arg); + + +/*************************************************************************************************/ +/******************************************** natives ********************************************/ +/*************************************************************************************************/ + +/** + * Returns whether a socket is connected or not. + * + * @param socket Socket handle to check + * @return bool The connection status + */ +native bool SocketIsConnected(Handle hSocket); + + +/** + * Creates a new socket. + * + * @note this function may be relatively expensive, reuse sockets if possible + * + * @param SocketType protocol The protocol to use, SOCKET_TCP is default + * @param SocketErrorCB efunc The error callback + * @return Handle The socket handle. Returns INVALID_HANDLE on failure + */ +native Handle SocketCreate(SocketType protocol=SOCKET_TCP, SocketErrorCB efunc); + +/** + * Binds the socket to a local address + * + * @param Handle socket The handle of the socket to be used. * @param String hostname The hostname (or IP) to bind the socket to. + * @param cell_t port The port to bind the socket to. + * @return bool true on success + */ +native bool SocketBind(Handle hSocket, const char[] hostname, int port); + +/** + * Connects a socket + * + * @note this native is threaded, it may be still running after it executed, use the connect callback + * @note invokes the SocketError callback with errorType = CONNECT_ERROR or EMPTY_HOST if it fails + * @note invokes the SocketConnect callback if it succeeds + * + * @param Handle socket The handle of the socket to be used. + * @param SocketConnectCB cfunc The connect callback + * @param SocketReceiveCB rfunc The receive callback + * @param SocketDisconnectCB dfunc The disconnect callback * @param String hostname The hostname (or IP) to connect to. + * @param cell_t port The port to connect to. + * @noreturn + */ +native void SocketConnect(Handle hSocket, SocketConnectCB cfunc, SocketReceiveCB rfunc, SocketDisconnectCB dfunc, const char[] hostname, int port); + +/** + * Disconnects a socket + * + * @note this will not close the handle, the socket will be reset to a state similar to after SocketCreate() + * @note this won't trigger any disconnect/error callbacks + * + * @noreturn + */ +native bool SocketDisconnect(Handle hSocket); + +/** + * Makes a socket listen for incoming connections + * + * @param Handle socket The handle of the socket to be used. + * @param SocketIncomingCB ifunc The callback for incoming connections + * @return bool true on success + */ +native bool SocketListen(Handle hSocket, SocketIncomingCB ifunc); + +/** + * Sends data through the socket. + * + * @note specify size for binary safe operation + * @note if size is not specified the \0 terminator will not be included + * @note This native is threaded, it may be still running after it executed (not atomic). + * @note Use the SendqueueEmpty callback to determine when all data has been successfully sent. + * @note The socket extension will ensure that the data will be send in the correct order and split + * the data if required. + * + * @param Handle socket The handle of the socket to be used. + * @param String data The data to send. + * @noreturn */ +native void SocketSend(Handle hSocket, const char[] data, int size=-1); + +/** * Sends UDP data through the socket to a specific destination. + * + * @note specify size for binary safe operation + * @note if size is not specified the \0 terminator will not be included + * @note This native is threaded, it may be still running after it executed (not atomic). + * @note Use the SendqueueEmpty callback to determine when all data has been successfully sent. + * @note The socket extension will ensure that the data will be send in the correct order and split + * the data if required. + * + * @param Handle socket The handle of the socket to be used. + * @param String data The data to send. + * @param String hostname The hostname (or IP) to send to. + * @param cell_t port The port to send to. + * @noreturn */ +native void SocketSendTo(Handle hSocket, const char[] data, int size=-1, const char[] hostname, int port); + +/** + * Set a socket option. + * + * @param Handle socket The handle of the socket to be used. May be INVALID_HANDLE if not essential. + * @param SocketOption option The option to modify (see enum SocketOption for details). + * @param cellt_ value The value to set the option to. + * @return cell_t 1 on success. */ +native void SocketSetOption(Handle hSocket, SocketOption option, any value); + +/** + * Defines the callback function for when the socket receives data + * + * @note this is only useful and required for child-sockets spawned by listen-sockets + * (otherwise you already set it in SocketConnect()) + * + * @param Handle socket The handle of the socket to be used. + * @param SocketReceiveCB rfunc The receive callback + * @noreturn + */ +native void SocketSetReceiveCallback(Handle hSocket, SocketReceiveCB rfunc); + +/** + * Defines the callback function for when the socket sent all items in its send queue + * + * @note this must be called AFTER sending (queueing) the data + * @note if no send-data is queued this will fire the callback itself + * @note the callback is guaranteed to fire + * + * @param Handle socket The handle of the socket to be used. + * @param SocketDisconnectCB dfunc The disconnect callback + * @noreturn + */ +native void SocketSetSendqueueEmptyCallback(Handle hSocket, SocketSendqueueEmptyCB sfunc); + +/** + * Defines the callback function for when the socket was properly disconnected by the remote side + * + * @note this is only useful and required for child-sockets spawned by listen-sockets + * (otherwise you already set it in SocketConnect()) + * + * @param Handle socket The handle of the socket to be used. + * @param SocketDisconnectCB dfunc The disconnect callback + * @noreturn + */ +native void SocketSetDisconnectCallback(Handle hSocket, SocketDisconnectCB dfunc); + +/** + * Defines the callback function for when the socket triggered an error + * + * @note this is only useful and required for child-sockets spawned by listen-sockets + * (otherwise you already set it in SocketCreate()) + * + * @param Handle socket The handle of the socket to be used. + * @param SocketErrorCB efunc The error callback + * @noreturn + */ +native void SocketSetErrorCallback(Handle hSocket, SocketErrorCB efunc); + +/** + * Sets the argument being passed to callbacks + * + * @param Handle socket The handle of the socket to be used. + * @param any arg The argument to set + * @noreturn + */ +native void SocketSetArg(Handle hSocket, any arg); + +/** + * Retrieve the local system's hostname as the command "hostname" does. + * + * @param dest Destination string buffer to copy to. + * @param destLen Destination buffer length (includes null terminator). + * + * @return 1 on success + */ +native void SocketGetHostName(char[] dest, int destLen); + +/** + * _________________Do not edit below this line!_______________________ + */ +public Extension __ext_smsock = +{ + name = "Socket", + file = "socket.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; diff --git a/addons/sourcemod/scripting/keys/api.sp b/Core/addons/sourcemod/scripting/keys/API.sp similarity index 99% rename from addons/sourcemod/scripting/keys/api.sp rename to Core/addons/sourcemod/scripting/keys/API.sp index e2357ea..0bbc2d1 100644 --- a/addons/sourcemod/scripting/keys/api.sp +++ b/Core/addons/sourcemod/scripting/keys/API.sp @@ -3,7 +3,7 @@ static Handle g_hGlobalForward_OnCoreStarted; public APLRes AskPluginLoad2(Handle hMySelf, bool bLate, char[] szError, int iErr_max) { -// Stats_Init(); + Stats_Init(); g_hGlobalForward_OnCoreStarted = CreateGlobalForward("Keys_OnCoreStarted", ET_Ignore); diff --git a/addons/sourcemod/scripting/keys/BLOCK.sp b/Core/addons/sourcemod/scripting/keys/BLOCK.sp similarity index 100% rename from addons/sourcemod/scripting/keys/BLOCK.sp rename to Core/addons/sourcemod/scripting/keys/BLOCK.sp diff --git a/addons/sourcemod/scripting/keys/CMD.sp b/Core/addons/sourcemod/scripting/keys/CMD.sp similarity index 100% rename from addons/sourcemod/scripting/keys/CMD.sp rename to Core/addons/sourcemod/scripting/keys/CMD.sp diff --git a/addons/sourcemod/scripting/keys/EVENTS.sp b/Core/addons/sourcemod/scripting/keys/EVENTS.sp similarity index 95% rename from addons/sourcemod/scripting/keys/EVENTS.sp rename to Core/addons/sourcemod/scripting/keys/EVENTS.sp index c9b2c9c..a0e0643 100644 --- a/addons/sourcemod/scripting/keys/EVENTS.sp +++ b/Core/addons/sourcemod/scripting/keys/EVENTS.sp @@ -1,7 +1,7 @@ public void OnMapStart() { -// Stats_OnMapStart(); + Stats_OnMapStart(); } public void OnConfigsExecuted() diff --git a/addons/sourcemod/scripting/keys/KEYS.sp b/Core/addons/sourcemod/scripting/keys/KEYS.sp similarity index 100% rename from addons/sourcemod/scripting/keys/KEYS.sp rename to Core/addons/sourcemod/scripting/keys/KEYS.sp diff --git a/addons/sourcemod/scripting/keys/STATS.sp b/Core/addons/sourcemod/scripting/keys/STATS.sp similarity index 99% rename from addons/sourcemod/scripting/keys/STATS.sp rename to Core/addons/sourcemod/scripting/keys/STATS.sp index fdd2993..fd0b993 100644 --- a/addons/sourcemod/scripting/keys/STATS.sp +++ b/Core/addons/sourcemod/scripting/keys/STATS.sp @@ -54,7 +54,7 @@ void Stats_Init() static ConVar g_hCvar_GetIPMethod; -void Stats_OnPluginStart(); +void Stats_OnPluginStart() { g_hCvar_GetIPMethod = CreateConVar("sm_keys_stats_get_ip_method", "0", " IP-\n\ 0 - hostip\n\ @@ -228,7 +228,7 @@ public void OnRequestComplete(HTTPResponse hResponse, any iData) #endif #if defined _SteamWorks_Included -public int SteamWorks_SteamServersConnected() +public void SteamWorks_SteamServersConnected() { int iIp[4]; if (SteamWorks_GetPublicIP(iIp) && iIp[0] && iIp[1] && iIp[2] && iIp[3]) diff --git a/addons/sourcemod/scripting/keys/UTIL.sp b/Core/addons/sourcemod/scripting/keys/UTIL.sp similarity index 100% rename from addons/sourcemod/scripting/keys/UTIL.sp rename to Core/addons/sourcemod/scripting/keys/UTIL.sp diff --git a/addons/sourcemod/scripting/keys/vars.sp b/Core/addons/sourcemod/scripting/keys/VARS.sp similarity index 100% rename from addons/sourcemod/scripting/keys/vars.sp rename to Core/addons/sourcemod/scripting/keys/VARS.sp diff --git a/addons/sourcemod/translations/keys_core.phrases.txt b/Core/addons/sourcemod/translations/keys_core.phrases.txt similarity index 100% rename from addons/sourcemod/translations/keys_core.phrases.txt rename to Core/addons/sourcemod/translations/keys_core.phrases.txt diff --git a/keys_core_logo_.png b/Core/keys_core_logo_.png similarity index 100% rename from keys_core_logo_.png rename to Core/keys_core_logo_.png diff --git a/Modules/addons/sourcemod/plugins/Keys_VIP.smx b/Modules/addons/sourcemod/plugins/Keys_VIP.smx new file mode 100644 index 0000000000000000000000000000000000000000..5f851707f34a1423169d9e3aa8dde492ce3e5f77 GIT binary patch literal 6675 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 +#include "keys_core.inc" +#include + +public Plugin:myinfo = +{ + name = "[Keys] VIP", + author = "R1KO", + version = "1.3", + url = "hlmod.ru" +}; + +#define EXT_STATUS 1 // Разрешить ли ключам типа vip_add продлевать VIP-статус +#define GC_STATUS 0 // Разрешить ли ключам типа vip_add изменять VIP-группу (работает только если включен EXT_STATUS) +#define CMP_VGRP 1 // Ключ типа vip_add может продлевать VIP-статус только если VIP-группа совпадает (работает только если включен EXT_STATUS) + // Если включено - отключает GC_STATUS + +#define USE_VIP_V3 0 // Для компиляции под ядро 3.0 + +#if CMP_VGRP == 1 && GC_STATUS == 1 +#undef GC_STATUS +#define GC_STATUS 0 +#endif + +new const String:g_sKeyType[][] = {"vip_add", "vip_ext", "vip_gc"}; + +public OnPluginStart() +{ + LoadTranslations("keys_core.phrases"); + LoadTranslations("keys_vip_module.phrases"); + + if (Keys_IsCoreStarted()) Keys_OnCoreStarted(); +} + +public OnPluginEnd() +{ + for(new i = 0; i < sizeof(g_sKeyType); ++i) + { + Keys_UnregKey(g_sKeyType[i]); + } +} + +public Keys_OnCoreStarted() +{ + for(new i = 0; i < sizeof(g_sKeyType); ++i) + { + Keys_RegKey(g_sKeyType[i], KeyCallback); + } +} + +#if USE_VIP_V3 == 0 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + MarkNativeAsOptional("VIP_GetClientID"); + + return APLRes_Success; +} +#endif + +public bool:KeyCallback(KeysAction:eKeysAction, iClient, const String:szKeyType[], Handle:hParamsArr, String:szBuffer[], iBuffLen) +{ + decl String:sParam[KEYS_MAX_LENGTH]; + switch(eKeysAction) + { + case Validation: + { + if(!strcmp(szKeyType, g_sKeyType[0])) + { + if(GetArraySize(hParamsArr) != 2) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_NUM_ARGS", iClient); + return false; + } + + GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); + if(!VIP_IsValidVIPGroup(sParam)) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_GROUP", iClient); + return false; + } + + GetArrayString(hParamsArr, 1, sParam, sizeof(sParam)); + new iTime = StringToInt(sParam); + if(iTime < 0) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_TIME", iClient); + return false; + } + + IntToString(VIP_TimeToSeconds(iTime), sParam, sizeof(sParam)); + SetArrayString(hParamsArr, 1, sParam); + + return true; + } + + if(GetArraySize(hParamsArr) != 1) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_NUM_ARGS", iClient); + return false; + } + + GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); + + if(!strcmp(szKeyType, g_sKeyType[1])) + { + new iTime = StringToInt(sParam); + if(iTime < 0) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_TIME", iClient); + return false; + } + + IntToString(VIP_TimeToSeconds(iTime), sParam, sizeof(sParam)); + SetArrayString(hParamsArr, 0, sParam); + + return true; + } + + if(!VIP_IsValidVIPGroup(sParam)) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_GROUP", iClient); + return false; + } + + return true; + } + case Activation: + { + decl String:sGroup[KEYS_MAX_LENGTH]; + new iClientID = -1; + new bool:bVip = VIP_IsClientVIP(iClient); + if(bVip) + { + #if USE_VIP_V3 == 1 + iClientID = VIP_GetClientID(iClient); + #else + if(CanTestFeatures() && GetFeatureStatus(FeatureType_Native, "VIP_GetClientID") == FeatureStatus_Available) + { + iClientID = VIP_GetClientID(iClient); + } + else + { + GetTrieValue(VIP_GetVIPClientTrie(iClient), "ClientID", iClientID); + } + #endif + } + + if(!strcmp(szKeyType, g_sKeyType[0])) + { + GetArrayString(hParamsArr, 0, sGroup, sizeof(sGroup)); + if(!VIP_IsValidVIPGroup(sGroup)) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_GROUP", iClient); + return false; + } + + if(bVip) + { + // && VIP_GetClientAuthType(iClient) < VIP_AuthType:3 + if(iClientID != -1) + { + #if EXT_STATUS == 1 + new iClientTime = VIP_GetClientAccessTime(iClient); + if(!iClientTime) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_CAN_NOT_USE", iClient); + return false; + } + + #if GC_STATUS == 1 || CMP_VGRP == 1 + if(VIP_IsValidVIPGroup(sGroup)) + { + decl String:sClientGroup[64]; + VIP_GetClientVIPGroup(iClient, sClientGroup, sizeof(sClientGroup)); + if(strcmp(sClientGroup, sGroup) != 0) + { + #if CMP_VGRP == 1 + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_CAN_NOT_USE", iClient); + return false; + #elseif GC_STATUS == 1 + VIP_SetClientVIPGroup(iClient, sGroup, true); + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GRP_CNG", sGroup); + #endif + } + } + #endif + + GetArrayString(hParamsArr, 1, sParam, sizeof(sParam)); + new iTime = StringToInt(sParam); + if(iTime) + { + Keys_GetTimeFromStamp(sParam, sizeof(sParam), iTime, iClient); + iTime += iClientTime; + } + else + { + FormatEx(sParam, sizeof(sParam), "%T", "FOREVER", iClient); + } + + VIP_SetClientAccessTime(iClient, iTime, true); + + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_EXT", sParam); + + return true; + #else + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_VIP_ALREADY", iClient); + return false; + #endif + } + + VIP_RemoveClientVIP(iClient, false, false); + } + + decl String:sTime[64], iTime; + GetArrayString(hParamsArr, 1, sTime, sizeof(sTime)); + iTime = StringToInt(sTime); + #if USE_VIP_V3 == 1 + VIP_SetClientVIP(0, iClient, iTime, sParam, true); + #else + VIP_SetClientVIP(iClient, iTime, AUTH_STEAM, sParam, true); + #endif + + if(iTime) + { + Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); + } + else + { + FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); + } + + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GOT", sParam, sTime); + return true; + } + + if(!bVip || (bVip && iClientID == -1)) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_CAN_NOT_USE", iClient); + return false; + } + + GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); + + if(!strcmp(szKeyType, g_sKeyType[1])) + { + new iTime = StringToInt(sParam); + VIP_SetClientAccessTime(iClient, iTime ? (VIP_GetClientAccessTime(iClient)+iTime):iTime, true); + + if(iTime) + { + Keys_GetTimeFromStamp(sParam, sizeof(sParam), iTime, iClient); + } + else + { + FormatEx(sParam, sizeof(sParam), "%T", "FOREVER", iClient); + } + + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_EXT", sParam); + + return true; + } + + if(!VIP_IsValidVIPGroup(sParam)) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_GROUP", iClient); + return false; + } + + decl String:sClientGroup[64]; + VIP_GetClientVIPGroup(iClient, sClientGroup, sizeof(sClientGroup)); + if(!strcmp(sClientGroup, sParam)) + { + FormatEx(szBuffer, iBuffLen, "%T", "ERROR_ALREADY_VIP_GROUP", iClient); + return false; + } + + VIP_SetClientVIPGroup(iClient, sParam, true); + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GRP_CNG", sParam); + + return true; + } + case Print: + { + GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); + if(!strcmp(szKeyType, g_sKeyType[0])) + { + decl iTime, String:sTime[32]; + GetArrayString(hParamsArr, 1, sTime, sizeof(sTime)); + iTime = StringToInt(sTime); + if(iTime) + { + Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); + } + else + { + FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); + } + + FormatEx(szBuffer, iBuffLen, "%T: %s\t%T: %s", "VIP_GROUP", iClient, sParam, "TERM", iClient, sTime); + + return true; + } + + if(!strcmp(szKeyType, g_sKeyType[1])) + { + decl iTime, String:sTime[32]; + iTime = StringToInt(sParam); + if(iTime) + { + Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); + } + else + { + FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); + } + + FormatEx(szBuffer, iBuffLen, "%T: %s", "TERM", iClient, sTime); + + return true; + } + + FormatEx(szBuffer, iBuffLen, "%T: %s", "VIP_GROUP", iClient, sParam); + + return true; + } + } + + return false; +} \ No newline at end of file diff --git a/Modules/addons/sourcemod/scripting/include/keys_core.inc b/Modules/addons/sourcemod/scripting/include/keys_core.inc new file mode 100644 index 0000000..7a4ef58 --- /dev/null +++ b/Modules/addons/sourcemod/scripting/include/keys_core.inc @@ -0,0 +1,171 @@ +#if defined _keys_core_included + #endinput +#endif +#define _keys_core_included + +#define KEYS_MAX_LENGTH 64 + +enum KeysAction +{ + Validation = 0, // Валидация параметров ключа + Activation, // Активация ключа + Print // Вывод ключа в консоль/файл +}; + +enum KeysNativeAction +{ + Add = 0, // Добавление/Генерация ключа + Rem, // Удаление ключа + Use // Использование ключа +}; + +// Прототип вызова при валидации параметров/акцивации/выводе параметров ключа +functag public bool:KeyActionCallback(KeysAction:eKeysAction, iClient, const String:szKeyType[], Handle:hParamsArr, String:szBuffer[], iBuffLen); + +funcenum KeyInfoCallback +{ + // Прототип вызова при проверке существования ключа + public(const String:sKey[], bool:bKeyExists, any:iData), + + // Прототип вызова при получении данных ключа + public(const String:sKey[], bool:bKeyExists, const String:sKeyType[], iUses, iExpires, Handle:hParamsArr, any:iData) +}; + +// Прототип вызова при добавлении/удалении/использовании ключа +functag public KeyNativeActionCallback (KeysNativeAction:eKeysAction, iClient, const String:sKey[], bool:bSuccess, const String:sError[], any:iData); + +// Вызывается когда ядро было загружено +forward Keys_OnCoreStarted(); + +// Загружено ли ядро +native bool:Keys_IsCoreStarted(); + +// Получает Handle базы данных +native Handle:Keys_GetCoreDatabase(); + +// Получает тип базы данных (false - SQLite, true - MySQL) +native bool:Keys_GetDatabaseType(); + +// Регистрирует тип ключей +native bool:Keys_RegKey(const String:sKeyType[], KeyActionCallback:fCallback); + +// Разрегистрирует тип ключей +native Keys_UnregKey(const String:sKeyType[]); + +// Проверяет существование типа ключей +native bool:Keys_IsValidKeyType(const String:sKeyType[]); + +// Получает adt_array со всеми типами ключей (нужно закрывать Handle) +native Handle:Keys_FillArrayByKeyTypes(); + +// Проверяет существование ключа +native Keys_IsValidKey(const String:sKey[], KeyInfoCallback:IsValidKeyCallback, any:iData = 0); + +// Получает данные ключа +native Keys_GetKeyData(const String:sKey[], KeyInfoCallback:GetKeyDataCallback, any:iData = 0); + +// Генерирует ключ +native Keys_GenerateKey(String:sBuffer[], iBufLen, const String:sTemplate[] = NULL_STRING); + +// Добавляет ключ +native Keys_AddKey(iClient = 0, const String:sKey[] = NULL_STRING, const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyNativeActionCallback:AddKeyCallback, any:iData = 0); + +// Удаляет ключ +native Keys_RemoveKey(iClient = 0, const String:sKey[], KeyNativeActionCallback:RemoveKeyCallback, any:iData = 0); + +// Использует ключ игроком +native Keys_UseKey(iClient, const String:sKey[], bool:bNotify, bool:bIgnoreBlock, KeyNativeActionCallback:UseKeyCallback, any:iData = 0); + +// Для использования не забыть +// 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 +}; + +#if !defined REQUIRE_PLUGIN + +public __pl_keys_core_SetNTVOptional() +{ + MarkNativeAsOptional("Keys_IsCoreStarted"); + MarkNativeAsOptional("Keys_GetCoreDatabase"); + MarkNativeAsOptional("Keys_GetDatabaseType"); + MarkNativeAsOptional("Keys_RegKey"); + MarkNativeAsOptional("Keys_UnregKey"); + MarkNativeAsOptional("Keys_IsValidKeyType"); + MarkNativeAsOptional("Keys_FillArrayByKeyTypes"); + MarkNativeAsOptional("Keys_IsValidKey"); + MarkNativeAsOptional("Keys_GetKeyData"); + MarkNativeAsOptional("Keys_GenerateKey"); + MarkNativeAsOptional("Keys_AddKey"); + MarkNativeAsOptional("Keys_RemoveKey"); + MarkNativeAsOptional("Keys_UseKey"); +} +#endif diff --git a/Modules/addons/sourcemod/translations/keys_vip_module.phrases.txt b/Modules/addons/sourcemod/translations/keys_vip_module.phrases.txt new file mode 100644 index 0000000..21ca251 --- /dev/null +++ b/Modules/addons/sourcemod/translations/keys_vip_module.phrases.txt @@ -0,0 +1,84 @@ +"Phrases" +{ + "ERROR_INVALID_CREDITS" + { + "ru" "Неверное количество кредитов!" + "en" "Wrong number of credits!" + } + + "ERROR_INVALID_GROUP" + { + "ru" "Неверная VIP-группа!" + "en" "Invalid VIP-group!" + } + + "ERROR_INVALID_TIME" + { + "ru" "Неверное время!" + "en" "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 diff --git a/keys_module_logo.png b/Modules/keys_module_logo.png similarity index 100% rename from keys_module_logo.png rename to Modules/keys_module_logo.png diff --git a/addons/sourcemod/scripting/Keys_Example.sp b/addons/sourcemod/scripting/Keys_Example.sp deleted file mode 100644 index a61114d..0000000 --- a/addons/sourcemod/scripting/Keys_Example.sp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma semicolon 1 - -#include -#include - -public Plugin:myinfo = -{ - name = "[Keys] Example", - author = "R1KO", - version = "1.1", - url = "hlmod.ru" -}; - -new const String:g_sKeyType[] = {"examle"}; - -public OnPluginStart() -{ - LoadTranslations("keys_core.phrases"); - - if (Keys_IsCoreStarted()) Keys_OnCoreStarted(); -} - -public OnPluginEnd() -{ - Keys_UnregKey(g_sKeyType); -} - -public Keys_OnCoreStarted() -{ - Keys_RegKey(g_sKeyType, OnKeyParamsValidate, OnKeyUse, OnKeyPrint); -} - -public bool:OnKeyParamsValidate(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - LogMessage("OnKeyParamsValidate: '%s'", sKeyType); - decl i, String:sParam[KEYS_MAX_LENGTH]; - for(i = 0; i < GetArraySize(hParamsArr); ++i) - { - GetArrayString(hParamsArr, i, sParam, sizeof(sParam)); - LogMessage("GetArrayString = '%s'", sParam); - } - - SetArrayString(hParamsArr, 1, "rep34"); - - return true; -} - -public bool:OnKeyUse(iClient, const String:sKeyType[], Handle:hParamsArr, String:sError[], iErrLen) -{ - LogMessage("OnKeyUse: '%s'", sKeyType); - - return true; -} - -public OnKeyPrint(iClient, const String:sKeyType[], Handle:hParamsArr, String:sBuffer[], iBufLen) -{ - LogMessage("OnKeyPrint: '%s'", sKeyType); - FormatEx(sBuffer, iBufLen, "Example"); -} From 05199ff672b4da14a2afcc42b7bcdc5a7f233a49 Mon Sep 17 00:00:00 2001 From: T1MOXA Date: Thu, 16 Aug 2018 00:14:33 +0300 Subject: [PATCH 5/8] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=BD=D0=BA=D0=BB=D1=8E=D0=B4?= =?UTF-8?q?=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20=D1=81=D0=B8=D0=BD=D1=82=D0=B0=D0=BA=D1=81=D0=B8=D1=81?= =?UTF-8?q?=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/addons/sourcemod/scripting/include/keys_core.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/addons/sourcemod/scripting/include/keys_core.inc b/Core/addons/sourcemod/scripting/include/keys_core.inc index de159d8..3290698 100644 --- a/Core/addons/sourcemod/scripting/include/keys_core.inc +++ b/Core/addons/sourcemod/scripting/include/keys_core.inc @@ -65,10 +65,10 @@ native void Keys_IsValidKey(const char[] sKey, KeyInfoCallback IsValidKeyCallbac native void Keys_GetKeyData(const char[] sKey, KeyInfoCallback GetKeyDataCallback, any iData = 0); // Генерирует ключ -native void Keys_GenerateKey(char[] sBuffer, iBufLen, const char[] sTemplate = NULL_STRING); +native void Keys_GenerateKey(char[] sBuffer, int iBufLen, const char[] sTemplate = NULL_STRING); // Добавляет ключ -native void Keys_AddKey(int iClient = 0, const char[] sKey = NULL_STRING, const char[] sKeyType, int iUses, iLifeTime, ArrayList hParamsArr, KeyNativeActionCallback AddKeyCallback, any iData = 0); +native void Keys_AddKey(int iClient = 0, const char[] sKey = NULL_STRING, const char[] sKeyType, int iUses, int iLifeTime, ArrayList hParamsArr, KeyNativeActionCallback AddKeyCallback, any iData = 0); // Удаляет ключ native void Keys_RemoveKey(int iClient = 0, const char[] sKey, KeyNativeActionCallback RemoveKeyCallback, any iData = 0); From 550958cc88713b4e1a505b56c20436ed2a49e7a8 Mon Sep 17 00:00:00 2001 From: T1MOXA Date: Thu, 16 Aug 2018 00:37:27 +0300 Subject: [PATCH 6/8] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B9=20=D1=81?= =?UTF-8?q?=D0=B8=D0=BD=D1=82=D0=B0=D0=BA=D1=81=D0=B8=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Обновление VIP нативов для версии 3.0 > --- .../addons/sourcemod/scripting/Keys_VIP.sp | 270 +++++++----------- 1 file changed, 104 insertions(+), 166 deletions(-) diff --git a/Modules/addons/sourcemod/scripting/Keys_VIP.sp b/Modules/addons/sourcemod/scripting/Keys_VIP.sp index 45c0bc2..bbd88a6 100644 --- a/Modules/addons/sourcemod/scripting/Keys_VIP.sp +++ b/Modules/addons/sourcemod/scripting/Keys_VIP.sp @@ -1,14 +1,13 @@ +#pragma newdecls required #pragma semicolon 1 -#include -#include "keys_core.inc" +#include #include -public Plugin:myinfo = -{ +public Plugin myinfo = { name = "[Keys] VIP", - author = "R1KO", - version = "1.3", + author = "R1KO & T1MOX4", + version = "1.4", url = "hlmod.ru" }; @@ -17,165 +16,124 @@ public Plugin:myinfo = #define CMP_VGRP 1 // Ключ типа vip_add может продлевать VIP-статус только если VIP-группа совпадает (работает только если включен EXT_STATUS) // Если включено - отключает GC_STATUS -#define USE_VIP_V3 0 // Для компиляции под ядро 3.0 - #if CMP_VGRP == 1 && GC_STATUS == 1 #undef GC_STATUS #define GC_STATUS 0 #endif -new const String:g_sKeyType[][] = {"vip_add", "vip_ext", "vip_gc"}; +static const char g_sKeyType[][] = {"vip_add", "vip_ext", "vip_gc"}; -public OnPluginStart() -{ +public void OnPluginStart() { LoadTranslations("keys_core.phrases"); LoadTranslations("keys_vip_module.phrases"); if (Keys_IsCoreStarted()) Keys_OnCoreStarted(); } -public OnPluginEnd() -{ - for(new i = 0; i < sizeof(g_sKeyType); ++i) - { +public void OnPluginEnd() { + for(int i; i < sizeof(g_sKeyType); ++i) { Keys_UnregKey(g_sKeyType[i]); } } -public Keys_OnCoreStarted() -{ - for(new i = 0; i < sizeof(g_sKeyType); ++i) - { +public void Keys_OnCoreStarted() { + for(int i; i < sizeof(g_sKeyType); ++i) { Keys_RegKey(g_sKeyType[i], KeyCallback); } } -#if USE_VIP_V3 == 0 -public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) -{ - MarkNativeAsOptional("VIP_GetClientID"); - - return APLRes_Success; -} -#endif - -public bool:KeyCallback(KeysAction:eKeysAction, iClient, const String:szKeyType[], Handle:hParamsArr, String:szBuffer[], iBuffLen) -{ - decl String:sParam[KEYS_MAX_LENGTH]; - switch(eKeysAction) - { - case Validation: - { - if(!strcmp(szKeyType, g_sKeyType[0])) - { - if(GetArraySize(hParamsArr) != 2) - { +public bool KeyCallback(KeysAction eKeysAction, int iClient, const char[] szKeyType, ArrayList hParamsArr, char[] szBuffer, int iBuffLen) { + char sGroup[KEYS_MAX_LENGTH]; + switch(eKeysAction) { + case Validation: { + if (!strcmp(szKeyType, g_sKeyType[0])) { + if (hParamsArr.Length != 2) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_NUM_ARGS", iClient); return false; } - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(!VIP_IsValidVIPGroup(sParam)) - { + hParamsArr.GetString(0, sGroup, sizeof(sGroup)); + + if (!VIP_IsValidVIPGroup(sGroup)) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_GROUP", iClient); return false; } - GetArrayString(hParamsArr, 1, sParam, sizeof(sParam)); - new iTime = StringToInt(sParam); - if(iTime < 0) - { + hParamsArr.GetString(1, sGroup, sizeof(sGroup)); + int iTime = StringToInt(sGroup); + + if (iTime < 0) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_TIME", iClient); return false; } - IntToString(VIP_TimeToSeconds(iTime), sParam, sizeof(sParam)); - SetArrayString(hParamsArr, 1, sParam); + IntToString(VIP_TimeToSeconds(iTime), sGroup, sizeof(sGroup)); + hParamsArr.SetString(1, sGroup); return true; } - if(GetArraySize(hParamsArr) != 1) - { + if (hParamsArr.Length != 1) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_NUM_ARGS", iClient); return false; } - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); + hParamsArr.GetString(0, sGroup, sizeof(sGroup)); - if(!strcmp(szKeyType, g_sKeyType[1])) - { - new iTime = StringToInt(sParam); - if(iTime < 0) - { + if (!strcmp(szKeyType, g_sKeyType[1])) { + int iTime = StringToInt(sGroup); + + if (iTime < 0) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_TIME", iClient); return false; } - IntToString(VIP_TimeToSeconds(iTime), sParam, sizeof(sParam)); - SetArrayString(hParamsArr, 0, sParam); + IntToString(VIP_TimeToSeconds(iTime), sGroup, sizeof(sGroup)); + hParamsArr.SetString(0, sGroup); return true; } - if(!VIP_IsValidVIPGroup(sParam)) - { + if (!VIP_IsValidVIPGroup(sGroup)) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_GROUP", iClient); return false; } return true; } - case Activation: - { - decl String:sGroup[KEYS_MAX_LENGTH]; - new iClientID = -1; - new bool:bVip = VIP_IsClientVIP(iClient); - if(bVip) - { - #if USE_VIP_V3 == 1 + + case Activation: { + int iClientID = -1; + bool bVip = VIP_IsClientVIP(iClient); + + if (bVip) { iClientID = VIP_GetClientID(iClient); - #else - if(CanTestFeatures() && GetFeatureStatus(FeatureType_Native, "VIP_GetClientID") == FeatureStatus_Available) - { - iClientID = VIP_GetClientID(iClient); - } - else - { - GetTrieValue(VIP_GetVIPClientTrie(iClient), "ClientID", iClientID); - } - #endif } - if(!strcmp(szKeyType, g_sKeyType[0])) - { - GetArrayString(hParamsArr, 0, sGroup, sizeof(sGroup)); - if(!VIP_IsValidVIPGroup(sGroup)) - { + if (!strcmp(szKeyType, g_sKeyType[0])) { + hParamsArr.GetString(0, sGroup, sizeof(sGroup)); + + if (!VIP_IsValidVIPGroup(sGroup)) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_GROUP", iClient); return false; } - if(bVip) - { + if (bVip) { // && VIP_GetClientAuthType(iClient) < VIP_AuthType:3 - if(iClientID != -1) - { + if (iClientID != -1) { #if EXT_STATUS == 1 - new iClientTime = VIP_GetClientAccessTime(iClient); - if(!iClientTime) - { + int iClientTime = VIP_GetClientAccessTime(iClient); + if (!iClientTime) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_CAN_NOT_USE", iClient); return false; } #if GC_STATUS == 1 || CMP_VGRP == 1 - if(VIP_IsValidVIPGroup(sGroup)) - { - decl String:sClientGroup[64]; + if (VIP_IsValidVIPGroup(sGroup)) { + char sClientGroup[64]; VIP_GetClientVIPGroup(iClient, sClientGroup, sizeof(sClientGroup)); - if(strcmp(sClientGroup, sGroup) != 0) - { + + if (strcmp(sClientGroup, sGroup)) { #if CMP_VGRP == 1 FormatEx(szBuffer, iBuffLen, "%T", "ERROR_CAN_NOT_USE", iClient); return false; @@ -187,21 +145,19 @@ public bool:KeyCallback(KeysAction:eKeysAction, iClient, const String:szKeyType[ } #endif - GetArrayString(hParamsArr, 1, sParam, sizeof(sParam)); - new iTime = StringToInt(sParam); - if(iTime) - { - Keys_GetTimeFromStamp(sParam, sizeof(sParam), iTime, iClient); + hParamsArr.GetString(1, sGroup, sizeof(sGroup)); + int iTime = StringToInt(sGroup); + + if (iTime) { + Keys_GetTimeFromStamp(sGroup, sizeof(sGroup), iTime, iClient); iTime += iClientTime; - } - else - { - FormatEx(sParam, sizeof(sParam), "%T", "FOREVER", iClient); + } else { + FormatEx(sGroup, sizeof(sGroup), "%T", "FOREVER", iClient); } VIP_SetClientAccessTime(iClient, iTime, true); - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_EXT", sParam); + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_EXT", sGroup); return true; #else @@ -210,109 +166,91 @@ public bool:KeyCallback(KeysAction:eKeysAction, iClient, const String:szKeyType[ #endif } - VIP_RemoveClientVIP(iClient, false, false); + VIP_RemoveClientVIP2(-1, iClient, false, false); } - decl String:sTime[64], iTime; - GetArrayString(hParamsArr, 1, sTime, sizeof(sTime)); - iTime = StringToInt(sTime); - #if USE_VIP_V3 == 1 - VIP_SetClientVIP(0, iClient, iTime, sParam, true); - #else - VIP_SetClientVIP(iClient, iTime, AUTH_STEAM, sParam, true); - #endif + char sTime[64]; + hParamsArr.GetString(1, sTime, sizeof(sTime)); + int iTime = StringToInt(sTime); + VIP_GiveClientVIP(-1, iClient, iTime, sGroup, true); - if(iTime) - { + if (iTime) { Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); - } - else - { + } else { FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); } - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GOT", sParam, sTime); + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GOT", sGroup, sTime); return true; } - if(!bVip || (bVip && iClientID == -1)) - { + if (!bVip || (bVip && iClientID == -1)) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_CAN_NOT_USE", iClient); return false; } - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); + hParamsArr.GetString(0, sGroup, sizeof(sGroup)); - if(!strcmp(szKeyType, g_sKeyType[1])) - { - new iTime = StringToInt(sParam); + if (!strcmp(szKeyType, g_sKeyType[1])) { + int iTime = StringToInt(sGroup); VIP_SetClientAccessTime(iClient, iTime ? (VIP_GetClientAccessTime(iClient)+iTime):iTime, true); - if(iTime) - { - Keys_GetTimeFromStamp(sParam, sizeof(sParam), iTime, iClient); - } - else - { - FormatEx(sParam, sizeof(sParam), "%T", "FOREVER", iClient); + if (iTime) { + Keys_GetTimeFromStamp(sGroup, sizeof(sGroup), iTime, iClient); + } else { + FormatEx(sGroup, sizeof(sGroup), "%T", "FOREVER", iClient); } - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_EXT", sParam); + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_EXT", sGroup); return true; } - if(!VIP_IsValidVIPGroup(sParam)) - { + if (!VIP_IsValidVIPGroup(sGroup)) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_INVALID_GROUP", iClient); return false; } - decl String:sClientGroup[64]; + char sClientGroup[64]; VIP_GetClientVIPGroup(iClient, sClientGroup, sizeof(sClientGroup)); - if(!strcmp(sClientGroup, sParam)) - { + + if (!strcmp(sClientGroup, sGroup)) { FormatEx(szBuffer, iBuffLen, "%T", "ERROR_ALREADY_VIP_GROUP", iClient); return false; } - VIP_SetClientVIPGroup(iClient, sParam, true); - PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GRP_CNG", sParam); + VIP_SetClientVIPGroup(iClient, sGroup, true); + PrintToChat(iClient, "%t%t", "CHAT_PREFIX", "USE_KEY_GRP_CNG", sGroup); return true; } - case Print: - { - GetArrayString(hParamsArr, 0, sParam, sizeof(sParam)); - if(!strcmp(szKeyType, g_sKeyType[0])) - { - decl iTime, String:sTime[32]; - GetArrayString(hParamsArr, 1, sTime, sizeof(sTime)); - iTime = StringToInt(sTime); - if(iTime) - { + + case Print: { + hParamsArr.GetString(0, sGroup, sizeof(sGroup)); + + if (!strcmp(szKeyType, g_sKeyType[0])) { + char sTime[64]; + hParamsArr.GetString(1, sTime, sizeof(sTime)); + int iTime = StringToInt(sTime); + + if (iTime) { Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); - } - else - { + } else { FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); } - FormatEx(szBuffer, iBuffLen, "%T: %s\t%T: %s", "VIP_GROUP", iClient, sParam, "TERM", iClient, sTime); + FormatEx(szBuffer, iBuffLen, "%T: %s\t%T: %s", "VIP_GROUP", iClient, sGroup, "TERM", iClient, sTime); return true; } - if(!strcmp(szKeyType, g_sKeyType[1])) - { - decl iTime, String:sTime[32]; - iTime = StringToInt(sParam); - if(iTime) - { + if (!strcmp(szKeyType, g_sKeyType[1])) { + char sTime[64]; + int iTime = StringToInt(sGroup); + + if (iTime) { Keys_GetTimeFromStamp(sTime, sizeof(sTime), iTime, iClient); - } - else - { + } else { FormatEx(sTime, sizeof(sTime), "%T", "FOREVER", iClient); } @@ -321,11 +259,11 @@ public bool:KeyCallback(KeysAction:eKeysAction, iClient, const String:szKeyType[ return true; } - FormatEx(szBuffer, iBuffLen, "%T: %s", "VIP_GROUP", iClient, sParam); + FormatEx(szBuffer, iBuffLen, "%T: %s", "VIP_GROUP", iClient, sGroup); return true; } } return false; -} \ No newline at end of file +} From 141c001623b7b0729bf7bef4ad9ed6c939c0fb89 Mon Sep 17 00:00:00 2001 From: T1MOXA Date: Thu, 16 Aug 2018 00:38:44 +0300 Subject: [PATCH 7/8] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20Keys=5FGenerateKey=20=D0=B8=20Keys?= =?UTF-8?q?=5FAddKey=20=D0=BF=D0=BE=D0=B4=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9?= =?UTF-8?q?=20=D1=81=D0=B8=D0=BD=D1=82=D0=B0=D0=BA=D1=81=D0=B8=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sourcemod/scripting/include/keys_core.inc | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Modules/addons/sourcemod/scripting/include/keys_core.inc b/Modules/addons/sourcemod/scripting/include/keys_core.inc index 7a4ef58..3290698 100644 --- a/Modules/addons/sourcemod/scripting/include/keys_core.inc +++ b/Modules/addons/sourcemod/scripting/include/keys_core.inc @@ -20,70 +20,70 @@ enum KeysNativeAction }; // Прототип вызова при валидации параметров/акцивации/выводе параметров ключа -functag public bool:KeyActionCallback(KeysAction:eKeysAction, iClient, const String:szKeyType[], Handle:hParamsArr, String:szBuffer[], iBuffLen); +typedef KeyActionCallback = function bool (KeysAction eKeysAction, int iClient, const char[] szKeyType, ArrayList hParamsArr, char[] szBuffer, int iBuffLen); -funcenum KeyInfoCallback +typeset KeyInfoCallback { // Прототип вызова при проверке существования ключа - public(const String:sKey[], bool:bKeyExists, any:iData), + function void (const char[] sKey, bool bKeyExists, any iData); // Прототип вызова при получении данных ключа - public(const String:sKey[], bool:bKeyExists, const String:sKeyType[], iUses, iExpires, Handle:hParamsArr, any:iData) + function void (const char[] sKey, bool bKeyExists, const char[] sKeyType, int iUses, int iExpires, ArrayList hParamsArr, any iData); }; // Прототип вызова при добавлении/удалении/использовании ключа -functag public KeyNativeActionCallback (KeysNativeAction:eKeysAction, iClient, const String:sKey[], bool:bSuccess, const String:sError[], any:iData); +typedef KeyNativeActionCallback = function void (KeysNativeAction eKeysAction, int iClient, const char[] sKey, bool bSuccess, const char[] sError, any iData); // Вызывается когда ядро было загружено -forward Keys_OnCoreStarted(); +forward void Keys_OnCoreStarted(); // Загружено ли ядро -native bool:Keys_IsCoreStarted(); +native bool Keys_IsCoreStarted(); // Получает Handle базы данных -native Handle:Keys_GetCoreDatabase(); +native Database Keys_GetCoreDatabase(); // Получает тип базы данных (false - SQLite, true - MySQL) -native bool:Keys_GetDatabaseType(); +native bool Keys_GetDatabaseType(); // Регистрирует тип ключей -native bool:Keys_RegKey(const String:sKeyType[], KeyActionCallback:fCallback); +native bool Keys_RegKey(const char[] sKeyType, KeyActionCallback fCallback); // Разрегистрирует тип ключей -native Keys_UnregKey(const String:sKeyType[]); +native void Keys_UnregKey(const char[] sKeyType); // Проверяет существование типа ключей -native bool:Keys_IsValidKeyType(const String:sKeyType[]); +native bool Keys_IsValidKeyType(const char[] sKeyType); // Получает adt_array со всеми типами ключей (нужно закрывать Handle) -native Handle:Keys_FillArrayByKeyTypes(); +native ArrayList Keys_FillArrayByKeyTypes(); // Проверяет существование ключа -native Keys_IsValidKey(const String:sKey[], KeyInfoCallback:IsValidKeyCallback, any:iData = 0); +native void Keys_IsValidKey(const char[] sKey, KeyInfoCallback IsValidKeyCallback, any iData = 0); // Получает данные ключа -native Keys_GetKeyData(const String:sKey[], KeyInfoCallback:GetKeyDataCallback, any:iData = 0); +native void Keys_GetKeyData(const char[] sKey, KeyInfoCallback GetKeyDataCallback, any iData = 0); // Генерирует ключ -native Keys_GenerateKey(String:sBuffer[], iBufLen, const String:sTemplate[] = NULL_STRING); +native void Keys_GenerateKey(char[] sBuffer, int iBufLen, const char[] sTemplate = NULL_STRING); // Добавляет ключ -native Keys_AddKey(iClient = 0, const String:sKey[] = NULL_STRING, const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyNativeActionCallback:AddKeyCallback, any:iData = 0); +native void Keys_AddKey(int iClient = 0, const char[] sKey = NULL_STRING, const char[] sKeyType, int iUses, int iLifeTime, ArrayList hParamsArr, KeyNativeActionCallback AddKeyCallback, any iData = 0); // Удаляет ключ -native Keys_RemoveKey(iClient = 0, const String:sKey[], KeyNativeActionCallback:RemoveKeyCallback, any:iData = 0); +native void Keys_RemoveKey(int iClient = 0, const char[] sKey, KeyNativeActionCallback RemoveKeyCallback, any iData = 0); // Использует ключ игроком -native Keys_UseKey(iClient, const String:sKey[], bool:bNotify, bool:bIgnoreBlock, KeyNativeActionCallback:UseKeyCallback, any:iData = 0); +native void Keys_UseKey(int iClient, const char[] sKey, bool bNotify, bool bIgnoreBlock, KeyNativeActionCallback UseKeyCallback, any iData = 0); // Для использования не забыть // LoadTranslations("keys_core.phrases"); -stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = LANG_SERVER) +stock void Keys_GetTimeFromStamp(char[] sBuffer, int iMaxLength, int iTimeStamp, int iClient = LANG_SERVER) { if (iTimeStamp > 31536000) { - new iYears = iTimeStamp / 31536000; - new i = iTimeStamp - (iYears*31536000); + int iYears = iTimeStamp / 31536000; + int i = iTimeStamp - (iYears*31536000); if(i > 2592000) { FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iYears, "YEARS", iClient, i / 2592000, "MONTHS", iClient); @@ -97,8 +97,8 @@ stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = if (iTimeStamp > 2592000) { - new iMonths = iTimeStamp / 2592000; - new i = iTimeStamp - (iMonths*2592000); + int iMonths = iTimeStamp / 2592000; + int i = iTimeStamp - (iMonths*2592000); if (i > 86400) { FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iMonths, "MONTHS", iClient, i / 86400, "DAYS", iClient); @@ -112,8 +112,8 @@ stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = if (iTimeStamp > 86400) { - new iDays = iTimeStamp / 86400 % 365; - new iHours = (iTimeStamp / 3600) % 24; + int iDays = iTimeStamp / 86400 % 365; + int iHours = (iTimeStamp / 3600) % 24; if (iHours > 0) { FormatEx(sBuffer, iMaxLength, "%d %T %d %T", iDays, "DAYS", iClient, iHours, "HOURS", iClient); @@ -125,9 +125,9 @@ stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = return; } - new iHours = (iTimeStamp / 3600); - new iMins = (iTimeStamp / 60) % 60; - new iSecs = iTimeStamp % 60; + int iHours = (iTimeStamp / 3600); + int iMins = (iTimeStamp / 60) % 60; + int iSecs = iTimeStamp % 60; if (iHours > 0) { @@ -139,7 +139,7 @@ stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = } } -public SharedPlugin:__pl_keys_core= +public SharedPlugin __pl_keys_core= { name = "keys_core", file = "Keys_Core.smx", @@ -152,7 +152,7 @@ public SharedPlugin:__pl_keys_core= #if !defined REQUIRE_PLUGIN -public __pl_keys_core_SetNTVOptional() +public void __pl_keys_core_SetNTVOptional() { MarkNativeAsOptional("Keys_IsCoreStarted"); MarkNativeAsOptional("Keys_GetCoreDatabase"); From 70b90e16ba5cca1fde359c7228a8792b60d46a58 Mon Sep 17 00:00:00 2001 From: T1MOXA Date: Thu, 16 Aug 2018 00:49:09 +0300 Subject: [PATCH 8/8] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=BD=D0=BA=D0=BB=D1=8E=D0=B4=D0=B0?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20=D1=81?= =?UTF-8?q?=D0=B8=D0=BD=D1=82=D0=B0=D0=BA=D1=81=D0=B8=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sourcemod/scripting/include/keys_core.inc | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Modules/addons/sourcemod/scripting/include/keys_core.inc b/Modules/addons/sourcemod/scripting/include/keys_core.inc index 3290698..7a4ef58 100644 --- a/Modules/addons/sourcemod/scripting/include/keys_core.inc +++ b/Modules/addons/sourcemod/scripting/include/keys_core.inc @@ -20,70 +20,70 @@ enum KeysNativeAction }; // Прототип вызова при валидации параметров/акцивации/выводе параметров ключа -typedef KeyActionCallback = function bool (KeysAction eKeysAction, int iClient, const char[] szKeyType, ArrayList hParamsArr, char[] szBuffer, int iBuffLen); +functag public bool:KeyActionCallback(KeysAction:eKeysAction, iClient, const String:szKeyType[], Handle:hParamsArr, String:szBuffer[], iBuffLen); -typeset KeyInfoCallback +funcenum KeyInfoCallback { // Прототип вызова при проверке существования ключа - function void (const char[] sKey, bool bKeyExists, any iData); + public(const String:sKey[], bool:bKeyExists, any:iData), // Прототип вызова при получении данных ключа - function void (const char[] sKey, bool bKeyExists, const char[] sKeyType, int iUses, int iExpires, ArrayList hParamsArr, any iData); + public(const String:sKey[], bool:bKeyExists, const String:sKeyType[], iUses, iExpires, Handle:hParamsArr, any:iData) }; // Прототип вызова при добавлении/удалении/использовании ключа -typedef KeyNativeActionCallback = function void (KeysNativeAction eKeysAction, int iClient, const char[] sKey, bool bSuccess, const char[] sError, any iData); +functag public KeyNativeActionCallback (KeysNativeAction:eKeysAction, iClient, const String:sKey[], bool:bSuccess, const String:sError[], any:iData); // Вызывается когда ядро было загружено -forward void Keys_OnCoreStarted(); +forward Keys_OnCoreStarted(); // Загружено ли ядро -native bool Keys_IsCoreStarted(); +native bool:Keys_IsCoreStarted(); // Получает Handle базы данных -native Database Keys_GetCoreDatabase(); +native Handle:Keys_GetCoreDatabase(); // Получает тип базы данных (false - SQLite, true - MySQL) -native bool Keys_GetDatabaseType(); +native bool:Keys_GetDatabaseType(); // Регистрирует тип ключей -native bool Keys_RegKey(const char[] sKeyType, KeyActionCallback fCallback); +native bool:Keys_RegKey(const String:sKeyType[], KeyActionCallback:fCallback); // Разрегистрирует тип ключей -native void Keys_UnregKey(const char[] sKeyType); +native Keys_UnregKey(const String:sKeyType[]); // Проверяет существование типа ключей -native bool Keys_IsValidKeyType(const char[] sKeyType); +native bool:Keys_IsValidKeyType(const String:sKeyType[]); // Получает adt_array со всеми типами ключей (нужно закрывать Handle) -native ArrayList Keys_FillArrayByKeyTypes(); +native Handle:Keys_FillArrayByKeyTypes(); // Проверяет существование ключа -native void Keys_IsValidKey(const char[] sKey, KeyInfoCallback IsValidKeyCallback, any iData = 0); +native Keys_IsValidKey(const String:sKey[], KeyInfoCallback:IsValidKeyCallback, any:iData = 0); // Получает данные ключа -native void Keys_GetKeyData(const char[] sKey, KeyInfoCallback GetKeyDataCallback, any iData = 0); +native Keys_GetKeyData(const String:sKey[], KeyInfoCallback:GetKeyDataCallback, any:iData = 0); // Генерирует ключ -native void Keys_GenerateKey(char[] sBuffer, int iBufLen, const char[] sTemplate = NULL_STRING); +native Keys_GenerateKey(String:sBuffer[], iBufLen, const String:sTemplate[] = NULL_STRING); // Добавляет ключ -native void Keys_AddKey(int iClient = 0, const char[] sKey = NULL_STRING, const char[] sKeyType, int iUses, int iLifeTime, ArrayList hParamsArr, KeyNativeActionCallback AddKeyCallback, any iData = 0); +native Keys_AddKey(iClient = 0, const String:sKey[] = NULL_STRING, const String:sKeyType[], iUses, iLifeTime, Handle:hParamsArr, KeyNativeActionCallback:AddKeyCallback, any:iData = 0); // Удаляет ключ -native void Keys_RemoveKey(int iClient = 0, const char[] sKey, KeyNativeActionCallback RemoveKeyCallback, any iData = 0); +native Keys_RemoveKey(iClient = 0, const String:sKey[], KeyNativeActionCallback:RemoveKeyCallback, any:iData = 0); // Использует ключ игроком -native void Keys_UseKey(int iClient, const char[] sKey, bool bNotify, bool bIgnoreBlock, KeyNativeActionCallback UseKeyCallback, any iData = 0); +native Keys_UseKey(iClient, const String:sKey[], bool:bNotify, bool:bIgnoreBlock, KeyNativeActionCallback:UseKeyCallback, any:iData = 0); // Для использования не забыть // LoadTranslations("keys_core.phrases"); -stock void Keys_GetTimeFromStamp(char[] sBuffer, int iMaxLength, int iTimeStamp, int iClient = LANG_SERVER) +stock Keys_GetTimeFromStamp(String:sBuffer[], iMaxLength, iTimeStamp, iClient = LANG_SERVER) { if (iTimeStamp > 31536000) { - int iYears = iTimeStamp / 31536000; - int i = iTimeStamp - (iYears*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); @@ -97,8 +97,8 @@ stock void Keys_GetTimeFromStamp(char[] sBuffer, int iMaxLength, int iTimeStamp, if (iTimeStamp > 2592000) { - int iMonths = iTimeStamp / 2592000; - int i = iTimeStamp - (iMonths*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); @@ -112,8 +112,8 @@ stock void Keys_GetTimeFromStamp(char[] sBuffer, int iMaxLength, int iTimeStamp, if (iTimeStamp > 86400) { - int iDays = iTimeStamp / 86400 % 365; - int iHours = (iTimeStamp / 3600) % 24; + 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); @@ -125,9 +125,9 @@ stock void Keys_GetTimeFromStamp(char[] sBuffer, int iMaxLength, int iTimeStamp, return; } - int iHours = (iTimeStamp / 3600); - int iMins = (iTimeStamp / 60) % 60; - int iSecs = iTimeStamp % 60; + new iHours = (iTimeStamp / 3600); + new iMins = (iTimeStamp / 60) % 60; + new iSecs = iTimeStamp % 60; if (iHours > 0) { @@ -139,7 +139,7 @@ stock void Keys_GetTimeFromStamp(char[] sBuffer, int iMaxLength, int iTimeStamp, } } -public SharedPlugin __pl_keys_core= +public SharedPlugin:__pl_keys_core= { name = "keys_core", file = "Keys_Core.smx", @@ -152,7 +152,7 @@ public SharedPlugin __pl_keys_core= #if !defined REQUIRE_PLUGIN -public void __pl_keys_core_SetNTVOptional() +public __pl_keys_core_SetNTVOptional() { MarkNativeAsOptional("Keys_IsCoreStarted"); MarkNativeAsOptional("Keys_GetCoreDatabase");