diff --git a/.gitattributes b/.gitattributes index 4868048..b786d55 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.sp gitlab-language=cpp -*.inc gitlab-language=cpp \ No newline at end of file +*.inc gitlab-language=cpp +* text eol=lf diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 971efeb..e6a0b6c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,13 +5,10 @@ on: [push, pull_request, workflow_dispatch] jobs: compile: runs-on: ubuntu-latest - continue-on-error: ${{ matrix.sm-version == '1.11.x' }} + continue-on-error: ${{ matrix.sm-version == '1.12.x' }} strategy: matrix: - sm-version: [ '1.9.x', '1.10.x', '1.11.x' ] - include: - - sm-version: "1.11.x" # https://github.com/rumblefrog/setup-sp/issues/5 - compiler-options: "-i=$includePath" + sm-version: [ '1.10.x', '1.11.x', '1.12.x' ] name: "Build SM ${{ matrix.sm-version }}" steps: @@ -35,7 +32,7 @@ jobs: mkdir plugins cd scripting spcomp VIP_Core.sp -E -o ../plugins/VIP_Core.smx -iinclude ${{ matrix.compiler-options }} - + - name: Upload artifact uses: actions/upload-artifact@v3 with: @@ -44,7 +41,7 @@ jobs: addons LICENSE retention-days: 2 - + release: name: Release if: github.ref_type == 'tag' @@ -57,15 +54,15 @@ jobs: - name: Find Assets shell: bash run: | - echo "artifact-1_9=$(find * -maxdepth 0 -type d -name "*1.9*")" >> $GITHUB_ENV echo "artifact-1_10=$(find * -maxdepth 0 -type d -name "*1.10*")" >> $GITHUB_ENV echo "artifact-1_11=$(find * -maxdepth 0 -type d -name "*1.11*")" >> $GITHUB_ENV - + echo "artifact-1_12=$(find * -maxdepth 0 -type d -name "*1.12*")" >> $GITHUB_ENV + - name: Arhive Assets run: | - zip -r ${{ env.artifact-1_9 }}.zip ${{ env.artifact-1_9 }} zip -r ${{ env.artifact-1_10 }}.zip ${{ env.artifact-1_10 }} zip -r ${{ env.artifact-1_11 }}.zip ${{ env.artifact-1_11 }} + zip -r ${{ env.artifact-1_12 }}.zip ${{ env.artifact-1_12 }} - name: Create Release id: create_release @@ -77,33 +74,33 @@ jobs: release_name: ${{ github.ref_name }} draft: true prerelease: false - - - name: Upload Asset + + - name: Upload Asset 1.10 uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ${{ env.artifact-1_9 }}.zip - asset_name: ${{ env.artifact-1_9 }}.zip + asset_path: ${{ env.artifact-1_10 }}.zip + asset_name: ${{ env.artifact-1_10 }}.zip asset_content_type: application/zip - - name: Upload Asset + - name: Upload Asset 1.11 uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ${{ env.artifact-1_10 }}.zip - asset_name: ${{ env.artifact-1_10 }}.zip + asset_path: ${{ env.artifact-1_11 }}.zip + asset_name: ${{ env.artifact-1_11 }}.zip asset_content_type: application/zip - - name: Upload Asset + - name: Upload Asset 1.12 uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ${{ env.artifact-1_11 }}.zip - asset_name: ${{ env.artifact-1_11 }}.zip - asset_content_type: application/zip \ No newline at end of file + asset_path: ${{ env.artifact-1_12 }}.zip + asset_name: ${{ env.artifact-1_12 }}.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore index 81cca17..b6f6890 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ *.smx +*.code-workspace diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e07092a..0000000 --- a/.travis.yml +++ /dev/null @@ -1,59 +0,0 @@ -#General settings -dist: xenial -os: linux - -#Install some apt packages needed for spcomp -addons: - apt_packages: - - lib32z1 - - lib32stdc++6 - -language: c - -#Set the build environment -env: - global: - - INCLUDE=addons/sourcemod/scripting/include - - SCRIPTING=addons/sourcemod/scripting - - PLUGINS=addons/sourcemod/plugins - jobs: - - SMVERSION=1.9 - - SMVERSION=1.10 - - SMVERSION=1.11 - -jobs: - allow_failures: - - env: SMVERSION=1.11 - -install: - # Sourcemod download - - wget --input-file=http://sourcemod.net/smdrop/$SMVERSION/sourcemod-latest-linux - - tar -xzf $(cat sourcemod-latest-linux) -before_script: - - chmod +x $SCRIPTING/spcomp - - mkdir $SCRIPTING/compiled - - mkdir build - - cp -R addons build - - cp LICENSE build -script: - - $SCRIPTING/spcomp -E -o$SCRIPTING'/compiled/VIP_Core' -v0 $SCRIPTING'/VIP_Core.sp' - -#Release -before_deploy: - - ls -lR - - cp $SCRIPTING/compiled/VIP_Core.smx build/$PLUGINS - - zip -rq VIP_Core.zip build - - tar -czf VIP_Core.tar.gz build -deploy: - provider: releases - token: ${GH_TOKEN} - file: - - VIP_Core.zip - - VIP_Core.tar.gz - draft: true - on: - tags: true - -#Notifications -notifications: - email: false diff --git a/addons/sourcemod/data/vip/cfg/info.ini b/addons/sourcemod/data/vip/cfg/info.ini index ff02670..af64262 100644 --- a/addons/sourcemod/data/vip/cfg/info.ini +++ b/addons/sourcemod/data/vip/cfg/info.ini @@ -18,7 +18,7 @@ "time" "12" // Через сколько секунд меню будет закрыто автоматически (0 - пока игрок сам не закроет) | After how many seconds the menu will be closed automatically (0 - until the player closes himself) "ru" { - // Пукнты меню | Menu Items + // Пункты меню | Menu Items "item" "Вы имеете VIP-статус" "item" "SPACER" "item" "Ник: {NAME}" @@ -62,7 +62,7 @@ "time" "30" // Через сколько секунд меню будет закрыто автоматически (0 - пока игрок сам не закроет) | After how many seconds the menu will be closed automatically (0 - until the player closes himself) "ru" { - // Пукнты меню | Menu Items + // Пункты меню | Menu Items "item" "Вы имеете VIP-статус" "item" "Группа: {GROUP}" } @@ -85,7 +85,7 @@ "time" "12" // Через сколько секунд меню будет закрыто автоматически (0 - пока игрок сам не закроет) | After how many seconds the menu will be closed automatically (0 - until the player closes himself) "ru" { - // Пукнты меню | Menu Items + // Пункты меню | Menu Items "item" "У вас нет доступа к этому меню" "item" "Чтобы приобрести VIP-статус" "item" "Oбратитесь к администратору" @@ -118,7 +118,7 @@ "time" "12" // Через сколько секунд меню будет закрыто автоматически (0 - пока игрок сам не закроет) | After how many seconds the menu will be closed automatically (0 - until the player closes himself) "ru" { - // Пукнты меню | Menu Items + // Пункты меню | Menu Items "item" "Срок вашего VIP-статуса истек" "item" "Чтобы вновь приобрести VIP-статус" "item" "Oбратитесь к администратору" diff --git a/addons/sourcemod/scripting/VIP_Core.sp b/addons/sourcemod/scripting/VIP_Core.sp index f5955b6..182d0ce 100644 --- a/addons/sourcemod/scripting/VIP_Core.sp +++ b/addons/sourcemod/scripting/VIP_Core.sp @@ -4,33 +4,37 @@ #include #include -#include -#if !defined VIP_VERSION -#define VIP_VERSION "3.0.3 R" +#if !defined VIP_CORE_VERSION +#define VIP_CORE_VERSION "3.1.0 DEV" #endif - -#define DEBUG_MODE 0 // Режим отладки +#define DEBUG_MODE 0 // Режим отладки #define USE_ADMINMENU 1 // Включение админ-меню для управления VIP #define USE_MORE_SERVERS 1 // Включить/Отключить режим при котором если ID сервера у игрока 0 - то VIP будет работать на всех серверах +#define USE_CLIENTPREFS 0 // Использовать ли стандартные куки для хранения данных игроков + +#if USE_CLIENTPREFS 1 +#include +#endif public Plugin myinfo = { name = "[VIP] Core", author = "R1KO", - version = VIP_VERSION, + version = VIP_CORE_VERSION, url = "https://github.com/R1KO/VIP-Core" }; #include "vip/Global.sp" -#include "vip/Debug.sp" +#include "vip/Debugger.sp" #include "vip/Downloads.sp" #include "vip/Colors.sp" #include "vip/UTIL.sp" +#include "vip/Storage.sp" #include "vip/Features.sp" #include "vip/Sounds.sp" #include "vip/Info.sp" @@ -74,6 +78,11 @@ public void OnPluginStart() ITEM_DRAW = hDataPack.Position; delete hDataPack; + + g_bIsTranslationPhraseExistsAvailable = (CanTestFeatures() && + GetFeatureStatus(FeatureType_Native, "TranslationPhraseExists") == FeatureStatus_Available); + + API_SetupForwards(); ReadConfigs(); VIPMenu_Setup(); @@ -82,7 +91,6 @@ public void OnPluginStart() #endif Cvars_Setup(); - API_SetupForwards(); HookEvent("player_spawn", Event_PlayerSpawn); HookEvent("player_death", Event_PlayerDeath); diff --git a/addons/sourcemod/scripting/include/vip_core.inc b/addons/sourcemod/scripting/include/vip_core.inc index e35690d..49a2881 100644 --- a/addons/sourcemod/scripting/include/vip_core.inc +++ b/addons/sourcemod/scripting/include/vip_core.inc @@ -3,6 +3,8 @@ #endif #define _vip_core_included +#define VIP_CORE_INTERFACE 310 + /* Типы данных VIP-функций */ enum VIP_ValueType { @@ -113,6 +115,15 @@ forward void VIP_OnPlayerSpawn(int iClient, int iTeam, bool bIsVIP); */ forward Action VIP_OnShowClientInfo(int iClient, const char[] szEvent, const char[] szType, KeyValues hKeyValues); +/** + * Вызывается когда загружены данные из хранилища игрока + * + * @param iClient Индекс игрока. + * + * @noreturn +*/ +forward void VIP_OnClientStorageLoaded(int iClient); + /** * Вызывается когда у VIP-игрока изменяется статус функции. * @@ -136,6 +147,15 @@ forward Action VIP_OnFeatureToggle(int iClient, const char[] szFeature, VIP_Togg */ forward void VIP_OnVIPLoaded(); +/** + * Вызывается когда VIP-плагин загрузил настройки. + * + * @noparams + * + * @noreturn +*/ +forward void VIP_OnConfigsLoaded(); + /** * Вызывается когда VIP-функция была зарегистрирована. * @@ -159,7 +179,6 @@ forward void VIP_OnFeatureUnregistered(const char[] szFeature); * * @param iClient Индекс игрока. * - * @return true - продолжить. * false - не выполнять проверку. */ @@ -198,7 +217,7 @@ forward void VIP_OnClientDisconnect(int iClient, bool bIsVIP); * Вызывается когда игрок получает VIP-статус. * * @param iClient Индекс игрока. - * @param iAdmin Индекс админа (0 - сервер). + * @param iAdmin Индекс админа (0 - сервер, -1 - плагин). * * @noreturn */ @@ -212,7 +231,7 @@ forward void VIP_OnVIPClientAdded(int iClient, int iAdmin); * "Expired" - Истек; * "Removed by Admin" - Удален админом; * "Removed by native" - Удален нативом. - * @param iAdmin Индекс админа (0 - сервер). + * @param iAdmin Индекс админа (0 - сервер, -1 - плагин). * * @noreturn */ @@ -270,7 +289,7 @@ native int VIP_GetClientAccessTime(int iClient); native bool VIP_SetClientAccessTime(int iClient, int iTime, bool bInDB = true); /** - * Получает VIP-группу игрока. + * Получает группу игрока. * * @param iClient Индекс игрока. * @param szGroup Буфер, в который будет помещен результат. @@ -282,10 +301,10 @@ native bool VIP_SetClientAccessTime(int iClient, int iTime, bool bInDB = true); native bool VIP_GetClientVIPGroup(int iClient, char[] szGroup, int iMaxLength); /** - * Устанавливает игроку VIP-группу. + * Устанавливает игроку группу. * * @param iClient Индекс игрока. - * @param szGroup VIP-группа. + * @param szGroup группа. * @param bInDBB Изменять ли в базе данных. * * @return true - Успешно. @@ -303,15 +322,39 @@ native bool VIP_SetClientVIPGroup(int iClient, const char[] szGroup, bool bInDB native StringMap VIP_GetVIPClientTrie(int iClient); /** - * Проверяет наличие VIP-группы. + * Проверяет наличие группы. * - * @param szGroup Имя VIP-группы. + * @param szGroup Имя группы. * - * @return true - VIP-группа существует. - * false - VIP-группа не существует. + * @return true - группа существует. + * false - группа не существует. */ +native bool VIP_IsGroupExists(const char[] szGroup); + +#pragma deprecated Use VIP_IsGroupExists() instead native bool VIP_IsValidVIPGroup(const char[] szGroup); +/** + * Добавляет группу. + * + * @param szGroup Имя группы. + * @param hFeatures Параметры группы. + * + * @return true - успешно. + * false - ошибка. +*/ +native bool VIP_AddGroup(const char[] szGroup, KeyValues hFeatures); + +/** + * Удаляет группу. + * + * @param szGroup Имя группы. + * + * @return true - успешно. + * false - ошибка. +*/ +native bool VIP_RemoveGroup(const char[] szGroup); + /** * Выводит игроку сообщение в чат с приставкой (фраза в переводе "VIP_CHAT_PREFIX"). * @@ -361,12 +404,12 @@ native void VIP_SendClientVIPMenu(int iClient, bool bSelection = false); * @param iAdmin Индекс админа (0 - сервер, -1 - плагин). * @param iClient Индекс игрока. * @param iTime Время в секундах. - * @param szGroup Имя VIP-группы. + * @param szGroup Имя группы. * @param bAddToDB Добавлять ли в базу данных. * * @noreturn */ -native void VIP_GiveClientVIP(int iAdmin = 0, int iClient, int iTime, const char[] szGroup, bool bAddToDB = true); +native void VIP_GiveClientVIP(int iAdmin = -1, int iClient, int iTime, const char[] szGroup, bool bAddToDB = true); #pragma deprecated Use VIP_GiveClientVIP() instead native bool VIP_SetClientVIP(int iClient, int iTime, any AuthType, const char[] szGroup, bool bAddToDB = true); @@ -382,7 +425,7 @@ native bool VIP_SetClientVIP(int iClient, int iTime, any AuthType, const char[] * @return true - Успешно. * false - Не удалось выполнить. */ -native bool VIP_RemoveClientVIP2(int iAdmin = 0, int iClient, bool bInDB, bool bNotify); +native bool VIP_RemoveClientVIP2(int iAdmin = -1, int iClient, bool bInDB, bool bNotify); #pragma deprecated Use VIP_RemoveClientVIP2() instead native bool VIP_RemoveClientVIP(int iClient, bool bInDB, bool bNotify); @@ -448,7 +491,7 @@ native void VIP_UnregisterMe(); * @param szFeature Уникальное имя ф-и. * * @return true - VIP-функция существует. - * false - VIP-функция не существует. + * false - VIP-функция не существует. */ native bool VIP_IsValidFeature(const char[] szFeature); @@ -579,6 +622,29 @@ native bool VIP_GiveClientFeature(int iClient, const char[] szFeature, const cha */ native bool VIP_RemoveClientFeature(int iClient, const char[] szFeature); + +/** + * Сохраняет значение в хранилище игрока (сохраняется при переподключении) + * + * @param iClient Индекс игрока. + * @param szKey Ключ + * @param szValue Значение + * + * @noreturn +*/ +native void VIP_SetClientStorageValue(int iClient, const char[] szKey, const char[] szValue); + +/** + * Получает значение из хранилища игрока + * + * @param szKey Ключ + * @param szValue Буфер, в который будет помещен результат. + * @param iMaxLength Размер буфера. + * + * @noreturn +*/ +native void VIP_GetClientStorageValue(int iClient, const char[] szKey, char[] szValue, int iMaxLength); + /** * Получает Handle базы данных. После работы необходимо закрыть с помощью CloseHandle() или delete * @@ -642,6 +708,25 @@ native bool VIP_GetTimeFromStamp(char[] szBuffer, int iMaxLength, int iTimeStamp */ native void VIP_AddStringToggleStatus(const char[] szInput, char[] szBuffer, int iMaxLength, const char[] szFeature, int iClient); +/** + * Формирует путь к файлу с настройками модуля. + * + * @param szBuffer Буфер, в который будет помещен результат. + * @param iMaxLength Размер буфера. + * @param szFileName Имя файла. + * + * @noreturn +*/ +stock void VIP_BuildModulesPath(char[] szBuffer, int iMaxLen, const char[] szFileName) +{ + BuildPath(Path_SM, szBuffer, iMaxLen, "data/vip/modules/%s", szFileName); +} + +stock bool IsVipCoreAvailable() +{ + return CanTestFeatures() && GetFeatureStatus(FeatureType_Native, "VIP_UnregisterFeature") == FeatureStatus_Available; +} + public SharedPlugin __pl_vip_core= { name = "vip_core", @@ -661,6 +746,7 @@ public void __pl_vip_core_SetNTVOptional() MarkNativeAsOptional("VIP_GetDatabaseType"); MarkNativeAsOptional("VIP_RegisterFeature"); MarkNativeAsOptional("VIP_UnregisterFeature"); + MarkNativeAsOptional("VIP_UnregisterMe"); MarkNativeAsOptional("VIP_IsValidFeature"); MarkNativeAsOptional("VIP_GetFeatureType"); MarkNativeAsOptional("VIP_GetFeatureValueType"); @@ -679,6 +765,9 @@ public void __pl_vip_core_SetNTVOptional() MarkNativeAsOptional("VIP_GetVIPClientTrie"); MarkNativeAsOptional("VIP_SendClientVIPMenu"); MarkNativeAsOptional("VIP_IsValidVIPGroup"); + MarkNativeAsOptional("VIP_IsGroupExists"); + MarkNativeAsOptional("VIP_AddGroup"); + MarkNativeAsOptional("VIP_RemoveGroup"); MarkNativeAsOptional("VIP_GetClientFeatureStatus"); MarkNativeAsOptional("VIP_SetClientFeatureStatus"); MarkNativeAsOptional("VIP_IsClientFeatureUse"); @@ -694,8 +783,9 @@ public void __pl_vip_core_SetNTVOptional() MarkNativeAsOptional("VIP_GetTimeFromStamp"); MarkNativeAsOptional("VIP_AddStringToggleStatus"); MarkNativeAsOptional("VIP_GiveClientVIP"); - MarkNativeAsOptional("VIP_UnregisterMe"); MarkNativeAsOptional("VIP_GiveClientFeature"); MarkNativeAsOptional("VIP_RemoveClientFeature"); + MarkNativeAsOptional("VIP_SetClientStorageValue"); + MarkNativeAsOptional("VIP_GetClientStorageValue"); } #endif diff --git a/addons/sourcemod/scripting/vip/API.sp b/addons/sourcemod/scripting/vip/API.sp index 6640869..f674efa 100644 --- a/addons/sourcemod/scripting/vip/API.sp +++ b/addons/sourcemod/scripting/vip/API.sp @@ -1,3 +1,4 @@ +#define VIP_CLIENT(%0) (g_hFeatures[%0] && IS_CLIENT_VIP(%0) && IS_CLIENT_LOADED(%0)) static Handle g_hGlobalForward_OnVIPLoaded; static Handle g_hGlobalForward_OnClientPreLoad; @@ -11,10 +12,11 @@ static Handle g_hGlobalForward_OnFeatureToggle; static Handle g_hGlobalForward_OnFeatureRegistered; static Handle g_hGlobalForward_OnFeatureUnregistered; static Handle g_hGlobalForward_OnClientDisconnect; +static Handle g_hGlobalForward_OnClientStorageLoaded; +static Handle g_hGlobalForward_OnConfigsLoaded; void API_SetupForwards() { - // Global Forwards g_hGlobalForward_OnClientPreLoad = CreateGlobalForward("VIP_OnClientPreLoad", ET_Hook, Param_Cell); g_hGlobalForward_OnVIPLoaded = CreateGlobalForward("VIP_OnVIPLoaded", ET_Ignore); g_hGlobalForward_OnClientLoaded = CreateGlobalForward("VIP_OnClientLoaded", ET_Ignore, Param_Cell, Param_Cell); @@ -23,23 +25,31 @@ void API_SetupForwards() g_hGlobalForward_OnVIPClientRemoved = CreateGlobalForward("VIP_OnVIPClientRemoved", ET_Ignore, Param_Cell, Param_String, Param_Cell); g_hGlobalForward_OnPlayerSpawn = CreateGlobalForward("VIP_OnPlayerSpawn", ET_Ignore, Param_Cell, Param_Cell, Param_Cell); g_hGlobalForward_OnShowClientInfo = CreateGlobalForward("VIP_OnShowClientInfo", ET_Hook, Param_Cell, Param_String, Param_String, Param_Cell); - g_hGlobalForward_OnFeatureToggle = CreateGlobalForward("VIP_OnFeatureToggle", ET_Ignore, Param_Cell, Param_String, Param_Cell, Param_CellByRef); + g_hGlobalForward_OnFeatureToggle = CreateGlobalForward("VIP_OnFeatureToggle", ET_Hook, Param_Cell, Param_String, Param_Cell, Param_CellByRef); g_hGlobalForward_OnFeatureRegistered = CreateGlobalForward("VIP_OnFeatureRegistered", ET_Ignore, Param_String); g_hGlobalForward_OnFeatureUnregistered = CreateGlobalForward("VIP_OnFeatureUnregistered", ET_Ignore, Param_String); g_hGlobalForward_OnClientDisconnect = CreateGlobalForward("VIP_OnClientDisconnect", ET_Ignore, Param_Cell, Param_Cell); + g_hGlobalForward_OnClientStorageLoaded = CreateGlobalForward("VIP_OnClientStorageLoaded", ET_Ignore, Param_Cell); + g_hGlobalForward_OnConfigsLoaded = CreateGlobalForward("VIP_OnConfigsLoaded", ET_Ignore); } -// Global Forwards -void CreateForward_OnVIPLoaded() +void CallForward_OnVIPLoaded() { - DBG_API("CreateForward_OnVIPLoaded()") + DBG_API("CallForward_OnVIPLoaded()") Call_StartForward(g_hGlobalForward_OnVIPLoaded); Call_Finish(); } -bool CreateForward_OnClientPreLoad(int iClient) +void CallForward_OnConfigsLoaded() { - DBG_API("g_hGlobalForward_OnClientPreLoad(%N (%d), %b)", iClient, iClient, g_iClientInfo[iClient] & IS_VIP) + DBG_API("CallForward_OnConfigsLoaded()") + Call_StartForward(g_hGlobalForward_OnConfigsLoaded); + Call_Finish(); +} + +bool CallForward_OnClientPreLoad(int iClient) +{ + DBG_API("g_hGlobalForward_OnClientPreLoad(%N (%d), %b)", iClient, iClient, IS_CLIENT_VIP(iClient)) bool bResult = true; Call_StartForward(g_hGlobalForward_OnClientPreLoad); Call_PushCell(iClient); @@ -49,44 +59,44 @@ bool CreateForward_OnClientPreLoad(int iClient) return bResult; } -void CreateForward_OnClientLoaded(int iClient) +void CallForward_OnClientLoaded(int iClient) { - DBG_API("CreateForward_OnClientLoaded(%N (%d), %b)", iClient, iClient, g_iClientInfo[iClient] & IS_VIP) + DBG_API("CallForward_OnClientLoaded(%N (%d), %b)", iClient, iClient, IS_CLIENT_VIP(iClient)) Call_StartForward(g_hGlobalForward_OnClientLoaded); Call_PushCell(iClient); - Call_PushCell(g_iClientInfo[iClient] & IS_VIP); + Call_PushCell(IS_CLIENT_VIP(iClient)); Call_Finish(); } -void CreateForward_OnVIPClientLoaded(int iClient) +void CallForward_OnVIPClientLoaded(int iClient) { - DBG_API("CreateForward_OnVIPClientLoaded(%N (%d))", iClient, iClient) + DBG_API("CallForward_OnVIPClientLoaded(%N (%d))", iClient, iClient) Call_StartForward(g_hGlobalForward_OnVIPClientLoaded); Call_PushCell(iClient); Call_Finish(); } -void CreateForward_OnClientDisconnect(int iClient) +void CallForward_OnClientDisconnect(int iClient) { - DBG_API("CreateForward_OnClientDisconnect(%N (%d), %b)", iClient, iClient, g_iClientInfo[iClient] & IS_VIP) + DBG_API("CallForward_OnClientDisconnect(%N (%d), %b)", iClient, iClient, IS_CLIENT_VIP(iClient)) Call_StartForward(g_hGlobalForward_OnClientDisconnect); Call_PushCell(iClient); - Call_PushCell(g_iClientInfo[iClient] & IS_VIP); + Call_PushCell(IS_CLIENT_VIP(iClient)); Call_Finish(); } -void CreateForward_OnVIPClientAdded(int iClient, int iAdmin = 0) +void CallForward_OnVIPClientAdded(int iClient, int iAdmin = OWNER_PLUGIN) { - DBG_API("CreateForward_OnVIPClientAdded(%N (%d), %d)", iClient, iClient, iAdmin) + DBG_API("CallForward_OnVIPClientAdded(%N (%d), %d)", iClient, iClient, iAdmin) Call_StartForward(g_hGlobalForward_OnVIPClientAdded); Call_PushCell(iClient); Call_PushCell(iAdmin); Call_Finish(); } -void CreateForward_OnVIPClientRemoved(int iClient, const char[] sReason, int iAdmin = 0) +void CallForward_OnVIPClientRemoved(int iClient, const char[] sReason, int iAdmin = OWNER_PLUGIN) { - DBG_API("CreateForward_OnVIPClientRemoved(%N (%d), %d, '%s')", iClient, iClient, iAdmin, sReason) + DBG_API("CallForward_OnVIPClientRemoved(%N (%d), %d, '%s')", iClient, iClient, iAdmin, sReason) Call_StartForward(g_hGlobalForward_OnVIPClientRemoved); Call_PushCell(iClient); Call_PushString(sReason); @@ -94,19 +104,19 @@ void CreateForward_OnVIPClientRemoved(int iClient, const char[] sReason, int iAd Call_Finish(); } -void CreateForward_OnPlayerSpawn(int iClient, int iTeam) +void CallForward_OnPlayerSpawn(int iClient, int iTeam) { - DBG_API("CreateForward_OnPlayerSpawn(%N (%d), %d, %b)", iClient, iClient, iTeam, g_iClientInfo[iClient] & IS_VIP) + DBG_API("CallForward_OnPlayerSpawn(%N (%d), %d, %b)", iClient, iClient, iTeam, IS_CLIENT_VIP(iClient)) Call_StartForward(g_hGlobalForward_OnPlayerSpawn); Call_PushCell(iClient); Call_PushCell(iTeam); - Call_PushCell(g_iClientInfo[iClient] & IS_VIP); + Call_PushCell(IS_CLIENT_VIP(iClient)); Call_Finish(); } -Action CreateForward_OnShowClientInfo(int iClient, const char[] szEvent, const char[] szType, KeyValues hKeyValues) +Action CallForward_OnShowClientInfo(int iClient, const char[] szEvent, const char[] szType, KeyValues hKeyValues) { - DBG_API("CreateForward_OnShowClientInfo(%N (%d), '%s', '%s')", iClient, iClient, szEvent, szType) + DBG_API("CallForward_OnShowClientInfo(%N (%d), '%s', '%s')", iClient, iClient, szEvent, szType) Action eResult = Plugin_Continue; Call_StartForward(g_hGlobalForward_OnShowClientInfo); Call_PushCell(iClient); @@ -114,14 +124,22 @@ Action CreateForward_OnShowClientInfo(int iClient, const char[] szEvent, const c Call_PushString(szType); Call_PushCell(hKeyValues); Call_Finish(eResult); - DBG_API("CreateForward_OnShowClientInfo = %b", eResult) + DBG_API("CallForward_OnShowClientInfo = %d", eResult) return eResult; } -VIP_ToggleState CreateForward_OnFeatureToggle(int iClient, const char[] szFeature, VIP_ToggleState eOldStatus, VIP_ToggleState eNewStatus) +void CallForward_OnClientStorageLoaded(int iClient) { - DBG_API("CreateForward_OnFeatureToggle(%N (%d), '%s', %d, %d)", iClient, iClient, szFeature, eOldStatus, eNewStatus) + DBG_API("CallForward_OnClientStorageLoaded(%N (%d))", iClient, iClient) + Call_StartForward(g_hGlobalForward_OnClientStorageLoaded); + Call_PushCell(iClient); + Call_Finish(); +} + +VIP_ToggleState CallForward_OnFeatureToggle(int iClient, const char[] szFeature, VIP_ToggleState eOldStatus, VIP_ToggleState eNewStatus) +{ + DBG_API("CallForward_OnFeatureToggle(%N (%d), '%s', %d, %d)", iClient, iClient, szFeature, eOldStatus, eNewStatus) Action aResult = Plugin_Continue; VIP_ToggleState eResultStatus = eNewStatus; @@ -131,7 +149,7 @@ VIP_ToggleState CreateForward_OnFeatureToggle(int iClient, const char[] szFeatur Call_PushCell(eOldStatus); Call_PushCellRef(eResultStatus); Call_Finish(aResult); - DBG_API("CreateForward_OnFeatureToggle = %b", aResult) + DBG_API("CallForward_OnFeatureToggle = %b", bResult) switch (aResult) { @@ -152,17 +170,17 @@ VIP_ToggleState CreateForward_OnFeatureToggle(int iClient, const char[] szFeatur return eResultStatus; } -void CreateForward_OnFeatureRegistered(const char[] szFeature) +void CallForward_OnFeatureRegistered(const char[] szFeature) { - DBG_API("CreateForward_OnFeatureRegistered('%s')", szFeature) + DBG_API("CallForward_OnFeatureRegistered('%s')", szFeature) Call_StartForward(g_hGlobalForward_OnFeatureRegistered); Call_PushString(szFeature); Call_Finish(); } -void CreateForward_OnFeatureUnregistered(const char[] szFeature) +void CallForward_OnFeatureUnregistered(const char[] szFeature) { - DBG_API("CreateForward_OnFeatureUnregistered('%s')", szFeature) + DBG_API("CallForward_OnFeatureUnregistered('%s')", szFeature) Call_StartForward(g_hGlobalForward_OnFeatureUnregistered); Call_PushString(szFeature); Call_Finish(); @@ -251,6 +269,9 @@ public APLRes AskPluginLoad2(Handle myself, bool bLate, char[] szError, int err_ RegNative(SendClientVIPMenu); RegNative(IsValidVIPGroup); + RegNative(IsGroupExists); + RegNative(AddGroup); + RegNative(RemoveGroup); RegNative(GetClientFeatureStatus); RegNative(SetClientFeatureStatus); @@ -265,6 +286,10 @@ public APLRes AskPluginLoad2(Handle myself, bool bLate, char[] szError, int err_ RegNative(GiveClientFeature); RegNative(RemoveClientFeature); + // Storage + RegNative(SetClientStorageValue); + RegNative(GetClientStorageValue); + // Helpers RegNative(PrintToChatClient); RegNative(PrintToChatAll); @@ -289,8 +314,6 @@ public APLRes AskPluginLoad2(Handle myself, bool bLate, char[] szError, int err_ return APLRes_Success; } -#define VIP_CLIENT(%0) (g_hFeatures[%0] && (g_iClientInfo[%0] & IS_VIP)) - public int Native_CheckClient(Handle hPlugin, int iNumParams) { DBG_API("Native_CheckClient(%d)", iNumParams) @@ -311,11 +334,12 @@ public int Native_IsClientVIP(Handle hPlugin, int iNumParams) DBG_API("iClient = %d", iClient) if (CheckValidClient(iClient, false)) { - DBG_API("IS_VIP = %b", (g_iClientInfo[iClient] & IS_VIP)) - return view_as(g_iClientInfo[iClient] & IS_VIP); + DBG_API("IS_VIP = %b", IS_CLIENT_VIP(iClient)) + DBG_API("IS_CLIENT_LOADED = %b", (g_iClientInfo[iClient] & IS_LOADED)) + return IS_CLIENT_VIP(iClient) && IS_CLIENT_LOADED(iClient); } - - return false; + + return 0; } public int Native_PrintToChatClient(Handle hPlugin, int iNumParams) @@ -325,7 +349,7 @@ public int Native_PrintToChatClient(Handle hPlugin, int iNumParams) DBG_API("iClient = %d", iClient) if (CheckValidClient(iClient, false)) { - char szMessage[256]; + char szMessage[PMP]; SetGlobalTransTarget(iClient); FormatNativeString(0, 2, 3, sizeof(szMessage), _, szMessage); @@ -338,7 +362,7 @@ public int Native_PrintToChatClient(Handle hPlugin, int iNumParams) public int Native_PrintToChatAll(Handle hPlugin, int iNumParams) { DBG_API("Native_PrintToChatAll(%d)", iNumParams) - char szMessage[256]; + char szMessage[PMP]; for (int i = 1; i <= MCL; ++i) { @@ -352,17 +376,16 @@ public int Native_PrintToChatAll(Handle hPlugin, int iNumParams) return 0; } + public int Native_LogMessage(Handle hPlugin, int iNumParams) { DBG_API("Native_LogMessage(%d)", iNumParams) - if (g_CVAR_bLogsEnable) - { - char szMessage[512]; - SetGlobalTransTarget(LANG_SERVER); - FormatNativeString(0, 1, 2, sizeof(szMessage), _, szMessage); - - LogToFile(g_szLogFile, szMessage); - } + + char szMessage[512]; + SetGlobalTransTarget(LANG_SERVER); + FormatNativeString(0, 1, 2, sizeof(szMessage), _, szMessage); + + LogToFile(g_szLogFile, szMessage); return 0; } @@ -409,46 +432,46 @@ public int Native_GetClientVIPGroup(Handle hPlugin, int iNumParams) public int Native_SetClientVIPGroup(Handle hPlugin, int iNumParams) { int iClient = GetNativeCell(1); - if (CheckValidClient(iClient)) + if (!CheckValidClient(iClient)) { - char szGroup[64]; - GetNativeString(2, SZF(szGroup)); - if (UTIL_CheckValidVIPGroup(szGroup)) - { - if (g_hFeatures[iClient].SetString(KEY_GROUP, szGroup)) - { - if (view_as(GetNativeCell(3))) - { - int iClientID; - if (g_hFeatures[iClient].GetValue(KEY_CID, iClientID) && iClientID != -1) - { - char szQuery[256]; - FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `group` = '%s' WHERE `account_id` = %d%s;", szGroup, iClientID, g_szSID); - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_ChangeClientSettings, szQuery, UID(iClient)); - - if (g_CVAR_bLogsEnable) - { - char szName[MNL], szAdmin[128], szPluginName[128], szOldGroup[64]; - GetClientName(iClient, SZF(szName)); - GetPluginInfo(hPlugin, PlInfo_Name, SZF(szPluginName)); - FormatEx(SZF(szAdmin), "%T %s", "BY_PLUGIN", LANG_SERVER, szPluginName); - g_hFeatures[iClient].GetString(KEY_GROUP, SZF(szOldGroup)); - g_hFeatures[iClient].GetValue(KEY_CID, iClientID); - LogToFile(g_szLogFile, "%T", "LOG_CHANGE_GROUP", LANG_SERVER, szName, iClientID, szOldGroup, szGroup, szAdmin); - } - } - } - - return true; - } - } - else + return 0; + } + char szGroup[64]; + GetNativeString(2, SZF(szGroup)); + if (!UTIL_CheckValidVIPGroup(szGroup)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid group (%s)", szGroup); + } + + if (!g_hFeatures[iClient].SetString(KEY_GROUP, szGroup)) + { + return 0; + } + if (view_as(GetNativeCell(3))) + { + int iClientID; + if (g_hFeatures[iClient].GetValue(KEY_CID, iClientID) && iClientID != -1) { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid group/Некорректная VIP-группа (%s)", szGroup); + char szQuery[PMP]; + FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `group` = '%s' WHERE `account_id` = %d%s;", szGroup, iClientID, g_szServerID); + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_ChangeClientSettings, szQuery, UID(iClient)); + + char szName[MNL], szAdmin[128], szPluginName[128], szOldGroup[64]; + GetClientName(iClient, SZF(szName)); + GetPluginInfo(hPlugin, PlInfo_Name, SZF(szPluginName)); + FormatEx(SZF(szAdmin), "%T %s", "BY_PLUGIN", LANG_SERVER, szPluginName); + g_hFeatures[iClient].GetString(KEY_GROUP, SZF(szOldGroup)); + g_hFeatures[iClient].GetValue(KEY_CID, iClientID); + LogToFile(g_szLogFile, "%T", "LOG_CHANGE_GROUP", LANG_SERVER, szName, iClientID, szOldGroup, szGroup, szAdmin); } + + return 1; } - return false; + + Clients_LoadFeatures(iClient); + + return 1; } public int Native_GetClientAccessTime(Handle hPlugin, int iNumParams) @@ -475,8 +498,7 @@ public int Native_SetClientAccessTime(Handle hPlugin, int iNumParams) if (iTime < 0 || (iTime != 0 && iTime < GetTime())) { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid time/Некорректное время (%i)", iTime); - return false; + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid time (%i)", iTime); } if (g_hFeatures[iClient].SetValue(KEY_EXPIRES, iTime)) @@ -486,8 +508,8 @@ public int Native_SetClientAccessTime(Handle hPlugin, int iNumParams) int iClientID; if (g_hFeatures[iClient].GetValue(KEY_CID, iClientID) && iClientID != -1) { - char szQuery[256]; - FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `expires` = %d WHERE `account_id` = %d%s;", iTime, iClientID, g_szSID); + char szQuery[PMP]; + FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `expires` = %d WHERE `account_id` = %d%s;", iTime, iClientID, g_szServerID); DBG_SQL_Query(szQuery) g_hDatabase.Query(SQL_Callback_ChangeClientSettings, szQuery, UID(iClient)); } @@ -519,7 +541,7 @@ public void SQL_Callback_ChangeClientSettings(Database hOwner, DBResultSet hResu public int Native_GetVIPClientTrie(Handle hPlugin, int iNumParams) { int iClient = GetNativeCell(1); - if (CheckValidClient(iClient, false) && VIP_CLIENT(iClient)) + if (CheckValidClient(iClient, false) && g_hFeatures[iClient]) { return view_as(g_hFeatures[iClient]); } @@ -530,26 +552,28 @@ public int Native_GetVIPClientTrie(Handle hPlugin, int iNumParams) public int Native_SendClientVIPMenu(Handle hPlugin, int iNumParams) { int iClient = GetNativeCell(1); - if (CheckValidClient(iClient)) + if (!CheckValidClient(iClient)) { - bool bSelection = false; + return 0; + } - if(iNumParams == 2) - { - bSelection = view_as(GetNativeCell(2)); - } - - if(bSelection) - { - g_hVIPMenu.Display(iClient, MENU_TIME_FOREVER); - return 0; - } - - int iItem = 0; - g_hFeatures[iClient].GetValue(KEY_MENUITEM, iItem); + bool bSelection = false; - g_hVIPMenu.DisplayAt(iClient, iItem, MENU_TIME_FOREVER); + if (iNumParams == 2) + { + bSelection = view_as(GetNativeCell(2)); } + + if (bSelection) + { + DisplayVipMenu(iClient); + return 0; + } + + int iItem = 0; + g_hFeatures[iClient].GetValue(KEY_MENUITEM, iItem); + + DisplayVipMenu(iClient, iItem); return 0; } @@ -576,7 +600,7 @@ public int Native_SetClientVIP(Handle hPlugin, int iNumParams) char szGroup[64]; GetNativeString(4, SZF(szGroup)); - return API_GiveClientVIP(hPlugin, REASON_PLUGIN, iClient, iTime, szGroup, bAddToDB); + return API_GiveClientVIP(hPlugin, OWNER_PLUGIN, iClient, iTime, szGroup, bAddToDB); } int API_GiveClientVIP(Handle hPlugin, @@ -588,56 +612,55 @@ int API_GiveClientVIP(Handle hPlugin, { if (CheckValidClient(iClient, false) && (iAdmin < 1 || CheckValidClient(iAdmin, false))) { - if (g_iClientInfo[iClient] & IS_VIP) + + if (!UTIL_CheckValidVIPGroup(szGroup)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid VIP-group (%s)", szGroup); + } + + if (iTime < 0) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid time (%d)", iTime); + } + + if (IS_CLIENT_VIP(iClient)) { int iClientID; g_hFeatures[iClient].GetValue(KEY_CID, iClientID); if (iClientID == -1 && bAddToDB) { - ResetClient(iClient); + Clients_ResetClient(iClient); + SET_BIT(g_iClientInfo[iClient], IS_LOADED); - CreateForward_OnVIPClientRemoved(iClient, "Removed for VIP-status change", iAdmin); + CallForward_OnVIPClientRemoved(iClient, "Removed for VIP-status change", iAdmin); } else { - return ThrowNativeError(SP_ERROR_NATIVE, "The player %L is already a VIP/Игрок %L уже является VIP-игроком", iClient, iClient); + return ThrowNativeError(SP_ERROR_NATIVE, "The player %L is already a VIP", iClient, iClient); } } - - if (!UTIL_CheckValidVIPGroup(szGroup)) - { - return ThrowNativeError(SP_ERROR_NATIVE, "Invalid VIP-group/Некорректная VIP-группа (%s)", szGroup); - } - if (iTime < 0) - { - return ThrowNativeError(SP_ERROR_NATIVE, "Invalid time/Некорректное время (%d)", iTime); - } if (bAddToDB) { char szPluginName[128]; GetPluginInfo(hPlugin, PlInfo_Name, SZF(szPluginName)); - UTIL_ADD_VIP_PLAYER(iAdmin, iClient, _, iTime, szGroup, szPluginName); + Clients_AddVipPlayer(iAdmin, iClient, _, iTime, szGroup, szPluginName); return 0; } - if (iTime == 0) - { - Clients_CreateClientVIPSettings(iClient, iTime); - } - else + + int iExpires = iTime; + + if (iTime != 0) { int iCurrentTime = GetTime(); - Clients_CreateClientVIPSettings(iClient, iTime+iCurrentTime); - Clients_CreateExpiredTimer(iClient, iTime+iCurrentTime, iCurrentTime); + iExpires = iTime + iCurrentTime; + Clients_CreateExpiredTimer(iClient, iExpires, iCurrentTime); } - g_hFeatures[iClient].SetString(KEY_GROUP, szGroup); - g_hFeatures[iClient].SetValue(KEY_CID, -1); - g_iClientInfo[iClient] |= IS_VIP; - g_iClientInfo[iClient] |= IS_LOADED; + Clients_InitVIPClient(iClient, -1, szGroup, iExpires); - Clients_LoadVIPFeatures(iClient); + Clients_TryLoadFeatures(iClient); DisplayClientInfo(iClient, iTime == 0 ? "connect_info_perm":"connect_info_time"); @@ -690,20 +713,24 @@ int API_RemoveClientVIP(Handle hPlugin, int iClientID; if (g_hFeatures[iClient].GetValue(KEY_CID, iClientID) && iClientID != -1) { - DB_RemoveClientFromID(REASON_PLUGIN, iClient, _, true, _, _, szPluginName); + Clients_RemoveVipPlayer(OWNER_PLUGIN, iClient, iClientID, bNotify, szPluginName); + return 1; } } - - if(g_iClientInfo[iClient] & IS_MENU_OPEN) + + // TODO: remake this + if (g_iClientInfo[iClient] & IS_MENU_OPEN) { CancelClientMenu(iClient); } - ResetClient(iClient); + Features_TurnOffAll(iClient); + Clients_ResetClient(iClient); + SET_BIT(g_iClientInfo[iClient], IS_LOADED); char szBuffer[PMP]; FormatEx(SZF(szBuffer), "Removed by %s", szPluginName); - CreateForward_OnVIPClientRemoved(iClient, szBuffer, iAdmin); + CallForward_OnVIPClientRemoved(iClient, szBuffer, iAdmin); if (bNotify) { @@ -723,6 +750,49 @@ public int Native_IsValidVIPGroup(Handle hPlugin, int iNumParams) return UTIL_CheckValidVIPGroup(szGroup); } +public int Native_IsGroupExists(Handle hPlugin, int iNumParams) +{ + char szGroup[64]; + GetNativeString(1, SZF(szGroup)); + return UTIL_CheckValidVIPGroup(szGroup); +} + +public int Native_AddGroup(Handle hPlugin, int iNumParams) +{ + char szGroup[64]; + GetNativeString(1, SZF(szGroup)); + if (UTIL_CheckValidVIPGroup(szGroup)) + { + return 0; + } + + g_hGroups.Rewind(); + if (g_hGroups.JumpToKey(szGroup, true)) + { + KeyValues hGroupKv = view_as(GetNativeCell(2)); + KvCopySubkeys(hGroupKv, g_hGroups); + g_hGroups.Rewind(); + return 1; + } + + return 0; +} + + +public int Native_RemoveGroup(Handle hPlugin, int iNumParams) +{ + char szGroup[64]; + GetNativeString(1, SZF(szGroup)); + if (UTIL_CheckValidVIPGroup(szGroup)) + { + g_hGroups.DeleteThis(); + g_hGroups.Rewind(); + return 1; + } + + return 0; +} + public int Native_IsVIPLoaded(Handle hPlugin, int iNumParams) { return ((GLOBAL_INFO & IS_STARTED) && g_hDatabase); @@ -739,67 +809,74 @@ public int Native_RegisterFeature(Handle hPlugin, int iNumParams) DebugMessage("Register feature \"%s\" (%s)", szFeature, sPluginName) #endif - if (IsValidFeature(szFeature) == false) + if (IsValidFeature(szFeature)) { - if (g_hFeaturesArray.Length == 0) - { - g_hVIPMenu.RemoveItem(0); - } - - g_hFeaturesArray.PushString(szFeature); - DebugMessage("PushArrayString -> %i", g_hFeaturesArray.FindString(szFeature)) + return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" already defined", szFeature); + } - VIP_FeatureType eType = view_as(GetNativeCell(3)); - DebugMessage("FeatureType -> %i", eType) + if (g_hFeaturesArray.Length == 0) + { + g_hVIPMenu.RemoveItem(0); + } - ArrayList hArray = new ArrayList(); - GLOBAL_TRIE.SetValue(szFeature, hArray); - - hArray.Push(hPlugin); - hArray.Push(GetNativeCell(2)); - hArray.Push(eType); + g_hFeaturesArray.PushString(szFeature); + DebugMessage("PushArrayString -> %i", g_hFeaturesArray.FindString(szFeature)) - if (eType != HIDE) - { - Handle hCookie = null; - if (eType == TOGGLABLE || (eType == SELECTABLE && iNumParams > 7 && GetNativeCell(8))) - { - hCookie = RegClientCookie(szFeature, szFeature, CookieAccess_Private); - } + VIP_FeatureType eType = view_as(GetNativeCell(3)); + DebugMessage("FeatureType -> %i", eType) - hArray.Push(hCookie); + ArrayList hArray = new ArrayList(); + GLOBAL_TRIE.SetValue(szFeature, hArray); + + hArray.Push(hPlugin); + hArray.Push(GetNativeCell(2)); + hArray.Push(eType); - DataPack hDataPack = new DataPack(); - hDataPack.WriteFunction(GetNativeCell(4)); - hDataPack.WriteFunction(GetNativeCell(5)); - hDataPack.WriteFunction(GetNativeCell(6)); - hArray.Push(hDataPack); + if (eType != HIDE) + { + Handle hCookie = null; - if(eType == TOGGLABLE) - { - hArray.Push(iNumParams > 6 ? GetNativeCell(7):NO_ACCESS); - } + #if USE_CLIENTPREFS 1 + if (eType == TOGGLABLE || (eType == SELECTABLE && iNumParams > 7 && GetNativeCell(8))) + { + hCookie = RegClientCookie(szFeature, szFeature, CookieAccess_Private); + } + #endif - AddFeatureToVIPMenu(szFeature); + Function fCallback = GetNativeCell(4); + if (eType == SELECTABLE && fCallback == INVALID_FUNCTION) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Undefined callback for SELECTABLE feature \"%s\"", szFeature); } - for (int iClient = 1; iClient <= MaxClients; ++iClient) + hArray.Push(hCookie); + + DataPack hDataPack = new DataPack(); + hDataPack.WriteFunction(fCallback); + hDataPack.WriteFunction(GetNativeCell(5)); + hDataPack.WriteFunction(GetNativeCell(6)); + hArray.Push(hDataPack); + + if (eType == TOGGLABLE) { - if (IsClientInGame(iClient) && g_iClientInfo[iClient] & IS_VIP) - { - Clients_LoadVIPFeaturesPre(iClient, szFeature); - } + hArray.Push(iNumParams > 6 ? GetNativeCell(7) : NO_ACCESS); } - CreateForward_OnFeatureRegistered(szFeature); - DebugMessage("Feature \"%s\" registered", szFeature) + AddFeatureToVIPMenu(szFeature); } - else + + CallForward_OnFeatureRegistered(szFeature); + DebugMessage("Feature \"%s\" registered", szFeature) + + for (int iClient = 1; iClient <= MaxClients; ++iClient) { - ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" already defined/Функция \"%s\" уже существует", szFeature, szFeature); + if (IsClientInGame(iClient) && VIP_CLIENT(iClient)) + { + Clients_TryLoadFeature(iClient, szFeature); + } } - return 0; + return 1; } public int Native_UnregisterFeature(Handle hPlugin, int iNumParams) @@ -807,57 +884,57 @@ public int Native_UnregisterFeature(Handle hPlugin, int iNumParams) char szFeature[FEATURE_NAME_LENGTH]; GetNativeString(1, SZF(szFeature)); - if (IsValidFeature(szFeature)) + if (!IsValidFeature(szFeature)) { - ArrayList hArray; - if (GLOBAL_TRIE.GetValue(szFeature, hArray)/* && view_as(hArray.Get(FEATURES_PLUGIN)) == hPlugin*/) - { - UnregisterFeature(szFeature, hArray); - - int i = g_hFeaturesArray.FindString(szFeature); - if (i != -1) - { - g_hFeaturesArray.Erase(i); - } - } + return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid", szFeature); } - else + + ArrayList hArray; + if (GLOBAL_TRIE.GetValue(szFeature, hArray)) { - ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid/Функция \"%s\" не существует", szFeature, szFeature); - } + UnregisterFeature(szFeature, hArray); - return 0; + int i = g_hFeaturesArray.FindString(szFeature); + if (i != -1) + { + g_hFeaturesArray.Erase(i); + } + } + + return 1; } public int Native_UnregisterMe(Handle hPlugin, int iNumParams) { DebugMessage("FeaturesArraySize: %d", g_hFeaturesArray.Length) - if (g_hFeaturesArray.Length > 0) + if (!g_hFeaturesArray.Length) { - char szFeature[FEATURE_NAME_LENGTH]; - ArrayList hArray; + return 0; + } - for (int i = 0, iSize = g_hFeaturesArray.Length; i < iSize; ++i) - { - g_hFeaturesArray.GetString(i, SZF(szFeature)); + char szFeature[FEATURE_NAME_LENGTH]; + ArrayList hArray; + + for (int i = 0, iSize = g_hFeaturesArray.Length; i < iSize; ++i) + { + g_hFeaturesArray.GetString(i, SZF(szFeature)); - if (GLOBAL_TRIE.GetValue(szFeature, hArray)) + if (GLOBAL_TRIE.GetValue(szFeature, hArray)) + { + if (view_as(hArray.Get(FEATURES_PLUGIN)) != hPlugin) { - if (view_as(hArray.Get(FEATURES_PLUGIN)) != hPlugin) - { - continue; - } + continue; + } - UnregisterFeature(szFeature, hArray); + UnregisterFeature(szFeature, hArray); - g_hFeaturesArray.Erase(i); - --i; - --iSize; - } + g_hFeaturesArray.Erase(i); + --i; + --iSize; } } - return 0; + return 1; } void UnregisterFeature(const char[] szFeature, ArrayList hArray) @@ -889,23 +966,18 @@ void UnregisterFeature(const char[] szFeature, ArrayList hArray) break; } } - - if (g_hVIPMenu.ItemCount == 0) - { - g_hVIPMenu.AddItem("NO_FEATURES", "NO_FEATURES", ITEMDRAW_DISABLED); - } } - + for (int j = 1; j <= MaxClients; ++j) { - if (IsClientInGame(j) && g_iClientInfo[j] & IS_VIP) + if (IsClientInGame(j) && IS_CLIENT_VIP(j)) { g_hFeatures[j].Remove(szFeature); g_hFeatureStatus[j].Remove(szFeature); } } - CreateForward_OnFeatureUnregistered(szFeature); + CallForward_OnFeatureUnregistered(szFeature); DebugMessage("Feature \"%s\" unregistered", szFeature) } @@ -928,7 +1000,7 @@ public int Native_GetFeatureType(Handle hPlugin, int iNumParams) return hArray.Get(FEATURES_ITEM_TYPE); } - return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid/Функция \"%s\" не существует", szFeature, szFeature); + return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid", szFeature); } public int Native_GetFeatureValueType(Handle hPlugin, int iNumParams) @@ -942,7 +1014,7 @@ public int Native_GetFeatureValueType(Handle hPlugin, int iNumParams) return hArray.Get(FEATURES_VALUE_TYPE); } - return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid/Функция \"%s\" не существует", szFeature, szFeature); + return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid", szFeature); } public int Native_FillArrayByFeatures(Handle hPlugin, int iNumParams) @@ -1007,7 +1079,7 @@ public int Native_SetClientFeatureStatus(Handle hPlugin, int iNumParams) { if (view_as(hArray.Get(FEATURES_ITEM_TYPE)) == TOGGLABLE) { - if(iNumParams > 3 && GetNativeCell(4)) + if (iNumParams > 3 && GetNativeCell(4)) { DataPack hDataPack = view_as(hArray.Get(FEATURES_MENU_CALLBACKS)); hDataPack.Position = ITEM_SELECT; @@ -1021,10 +1093,9 @@ public int Native_SetClientFeatureStatus(Handle hPlugin, int iNumParams) if (eOldStatus != eNewStatus) { Features_SetStatus(iClient, szFeature, eNewStatus); - if(iNumParams > 4 && GetNativeCell(5)) + if (iNumParams > 4 && GetNativeCell(5)) { - IntToString(view_as(eNewStatus), SZF(szFeature)); - SetClientCookie(iClient, view_as(GetArrayCell(hArray, FEATURES_COOKIE)), szFeature); + Features_SetStatusToStorage(iClient, szFeature, eNewStatus); } return true; } @@ -1088,7 +1159,7 @@ public int Native_GetClientFeatureString(Handle hPlugin, int iNumParams) int iLen = GetNativeCell(4); if (CheckValidClient(iClient, false) && VIP_CLIENT(iClient)) { - char szFeature[64], szBuffer[256]; + char szFeature[64], szBuffer[PMP]; GetNativeString(2, SZF(szFeature)); if (g_hFeatures[iClient].GetString(szFeature, SZF(szBuffer))) @@ -1112,25 +1183,25 @@ public int Native_GiveClientFeature(Handle hPlugin, int iNumParams) ArrayList hArray; if (!IsValidFeature(szFeature) || !GLOBAL_TRIE.GetValue(szFeature, hArray)) { - ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid/Функция \"%s\" не существует", szFeature, szFeature); - return 0; + return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid", szFeature); } - char szValue[256]; + char szValue[PMP]; GetNativeString(3, SZF(szValue)); - if (!(g_iClientInfo[iClient] & IS_VIP)) + if (IS_CLIENT_VIP(iClient)) { - Clients_CreateClientVIPSettings(iClient, 0); + Clients_InitVIPClient(iClient, -1, NULL_STRING, 0); g_hFeatures[iClient].SetValue(KEY_CID, -1); - g_iClientInfo[iClient] |= IS_VIP|IS_LOADED; + SET_BIT(g_iClientInfo[iClient], IS_VIP); + SET_BIT(g_iClientInfo[iClient], IS_LOADED); } switch (view_as(hArray.Get(FEATURES_VALUE_TYPE))) { case BOOL: { - g_hFeatures[iClient].SetValue(szFeature, view_as(StringToInt(szValue))); + g_hFeatures[iClient].SetValue(szFeature, !!StringToInt(szValue)); } case INT: { @@ -1158,7 +1229,7 @@ public int Native_GiveClientFeature(Handle hPlugin, int iNumParams) { Function_OnItemToggle(view_as(hArray.Get(FEATURES_PLUGIN)), fCallback, iClient, szFeature, NO_ACCESS, ENABLED); } - CreateForward_OnFeatureToggle(iClient, szFeature, NO_ACCESS, ENABLED); + CallForward_OnFeatureToggle(iClient, szFeature, NO_ACCESS, ENABLED); } return 1; @@ -1170,43 +1241,79 @@ public int Native_GiveClientFeature(Handle hPlugin, int iNumParams) public int Native_RemoveClientFeature(Handle hPlugin, int iNumParams) { int iClient = GetNativeCell(1); - if (CheckValidClient(iClient)) + if (!CheckValidClient(iClient)) { - char szFeature[64]; - GetNativeString(1, SZF(szFeature)); - ArrayList hArray; - if (!IsValidFeature(szFeature) || !GLOBAL_TRIE.GetValue(szFeature, hArray)) - { - ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid/Функция \"%s\" не существует", szFeature, szFeature); - return 0; - } + return 0; + } - VIP_ToggleState eToggleState = Features_GetStatus(iClient, szFeature); + char szFeature[64]; + GetNativeString(2, SZF(szFeature)); + ArrayList hArray; + if (!IsValidFeature(szFeature) || !GLOBAL_TRIE.GetValue(szFeature, hArray)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid", szFeature); + } - g_hFeatures[iClient].Remove(szFeature); - g_hFeatureStatus[iClient].Remove(szFeature); + VIP_ToggleState eToggleState = Features_GetStatus(iClient, szFeature); - if (!g_hFeatures[iClient].Size) + g_hFeatures[iClient].Remove(szFeature); + g_hFeatureStatus[iClient].Remove(szFeature); + + /* + if (!g_hFeatures[iClient].Size) + { + Clients_ResetClient(iClient); + SET_BIT(g_iClientInfo[iClient], IS_LOADED); + } + */ + + if (eToggleState != NO_ACCESS && view_as(hArray.Get(FEATURES_ITEM_TYPE)) == TOGGLABLE) + { + DataPack hDataPack = view_as(hArray.Get(FEATURES_MENU_CALLBACKS)); + hDataPack.Position = ITEM_SELECT; + Function fCallback = hDataPack.ReadFunction(); + + if (fCallback != INVALID_FUNCTION) { - ResetClient(iClient); + Function_OnItemToggle(view_as(hArray.Get(FEATURES_PLUGIN)), fCallback, iClient, szFeature, eToggleState, NO_ACCESS); } + CallForward_OnFeatureToggle(iClient, szFeature, eToggleState, NO_ACCESS); + } - if (eToggleState != NO_ACCESS && view_as(hArray.Get(FEATURES_ITEM_TYPE)) == TOGGLABLE) - { - DataPack hDataPack = view_as(hArray.Get(FEATURES_MENU_CALLBACKS)); - hDataPack.Position = ITEM_SELECT; - Function fCallback = hDataPack.ReadFunction(); + return 1; +} - if (fCallback != INVALID_FUNCTION) - { - Function_OnItemToggle(view_as(hArray.Get(FEATURES_PLUGIN)), fCallback, iClient, szFeature, eToggleState, NO_ACCESS); - } - CreateForward_OnFeatureToggle(iClient, szFeature, eToggleState, NO_ACCESS); - } +public int Native_SetClientStorageValue(Handle hPlugin, int iNumParams) +{ + int iClient = GetNativeCell(1); + if (!CheckValidClient(iClient, false)) + { + return 0; + } - return 1; + char szKey[128], szValue[PMP]; + GetNativeString(2, SZF(szKey)); + GetNativeString(3, SZF(szValue)); + + Storage_SetClientValue(iClient, szKey, szValue); + + return 0; +} + +public int Native_GetClientStorageValue(Handle hPlugin, int iNumParams) +{ + int iClient = GetNativeCell(1); + if (!CheckValidClient(iClient, false)) + { + return 0; } + char szKey[128], szValue[PMP]; + GetNativeString(2, SZF(szKey)); + + Storage_GetClientValue(iClient, szKey, SZF(szValue)); + SetNativeString(3, szValue, GetNativeCell(4), true); + return 0; } @@ -1217,7 +1324,7 @@ public int Native_GetDatabase(Handle hPlugin, int iNumParams) public int Native_GetDatabaseType(Handle hPlugin, int iNumParams) { - return (GLOBAL_INFO & IS_MySQL); + return DB_IsMysql(); } public int Native_TimeToSeconds(Handle hPlugin, int iNumParams) @@ -1236,7 +1343,7 @@ public int Native_GetTimeFromStamp(Handle hPlugin, int iNumParams) if (iTimeStamp > 0) { int iClient = GetNativeCell(4); - if (iClient == 0 || CheckValidClient(iClient, false)) + if (iClient == LANG_SERVER || CheckValidClient(iClient, false)) { char szBuffer[64]; UTIL_GetTimeFromStamp(SZF(szBuffer), iTimeStamp, iClient); @@ -1254,7 +1361,7 @@ public int Native_AddStringToggleStatus(Handle hPlugin, int iNumParams) GetNativeString(4, SZF(szFeature)); if (!IsValidFeature(szFeature)) { - return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid/Функция \"%s\" не существует", szFeature, szFeature); + return ThrowNativeError(SP_ERROR_NATIVE, "Feature \"%s\" is invalid", szFeature); } int iClient = GetNativeCell(5); @@ -1274,12 +1381,12 @@ bool CheckValidClient(const int &iClient, bool bCheckVIP = true) { if (iClient < 1 || iClient > MaxClients) { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index/Некорректный индекс игрока (%i)", iClient); + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%i)", iClient); return false; } if (IsClientInGame(iClient) == false) { - ThrowNativeError(SP_ERROR_NATIVE, "Client %i is not connected/Игрок %i не подключен", iClient, iClient); + ThrowNativeError(SP_ERROR_NATIVE, "Client %i is not connected", iClient); return false; } if (bCheckVIP) @@ -1290,14 +1397,14 @@ bool CheckValidClient(const int &iClient, bool bCheckVIP = true) ThrowNativeError(SP_ERROR_NATIVE, "Client %i is not loaded", iClient); return false; } - if (!(g_iClientInfo[iClient] & IS_VIP) || !(g_iClientInfo[iClient] & IS_AUTHORIZED)) + if (!IS_CLIENT_VIP(iClient) || !(g_iClientInfo[iClient] & IS_AUTHORIZED)) { ThrowNativeError(SP_ERROR_NATIVE, "Client %i is not VIP", iClient); return false; } */ - return view_as(g_iClientInfo[iClient] & IS_VIP); + return IS_CLIENT_VIP(iClient); } return true; diff --git a/addons/sourcemod/scripting/vip/AdminMenu.sp b/addons/sourcemod/scripting/vip/AdminMenu.sp index 0b9b4cd..2550656 100644 --- a/addons/sourcemod/scripting/vip/AdminMenu.sp +++ b/addons/sourcemod/scripting/vip/AdminMenu.sp @@ -71,16 +71,6 @@ void InitiateDataMap(int iClient) } } -int IsClientOnline(int ID) -{ - int iClientID; - for (int i = 1; i <= MaxClients; ++i) - { - if (IsClientInGame(i) && g_hFeatures[i] != null && g_hFeatures[i].GetValue(KEY_CID, iClientID) && iClientID == ID) return i; - } - return 0; -} - // ************************ ADMIN_MENU ************************ void AdminMenu_Setup() { @@ -310,7 +300,7 @@ void ShowTimeMenu(int iClient) if (iMenuType != TIME_SET && szTime[0] == '0') continue; - hKv.GetString(szClientLang, SZF(szBuffer), "LangError"); + hKv.GetString(szClientLang, SZF(szBuffer)); if (!szBuffer[0]) { hKv.GetString(szServerLang, SZF(szBuffer), "LangError"); @@ -395,10 +385,7 @@ public int MenuHandler_TimeMenu(Menu hMenu, MenuAction action, int iClient, int FormatTime(SZF(szTime), "%d/%m/%Y - %H:%M", iExpires); VIP_PrintToChatClient(iClient, "%t", "ADMIN_SET_EXPIRATION", szName, szTime); - if (g_CVAR_bLogsEnable) - { - LogToFile(g_szLogFile, "%T", "LOG_ADMIN_SET_EXPIRATION", LANG_SERVER, iClient, szName, szTime); - } + LogToFile(g_szLogFile, "%T", "LOG_ADMIN_SET_EXPIRATION", LANG_SERVER, iClient, szName, szTime); } case TIME_ADD: { @@ -415,10 +402,7 @@ public int MenuHandler_TimeMenu(Menu hMenu, MenuAction action, int iClient, int UTIL_GetTimeFromStamp(SZF(szTime), iTime, iClient); VIP_PrintToChatClient(iClient, "%t", "ADMIN_EXTENDED", szName, szTime); - if (g_CVAR_bLogsEnable) - { - LogToFile(g_szLogFile, "%T", "LOG_ADMIN_EXTENDED", LANG_SERVER, iClient, szName, szTime); - } + LogToFile(g_szLogFile, "%T", "LOG_ADMIN_EXTENDED", LANG_SERVER, iClient, szName, szTime); } case TIME_TAKE: { @@ -443,10 +427,7 @@ public int MenuHandler_TimeMenu(Menu hMenu, MenuAction action, int iClient, int VIP_PrintToChatClient(iClient, "%t", "ADMIN_REDUCED", szName, szTime); - if (g_CVAR_bLogsEnable) - { - LogToFile(g_szLogFile, "%T", "LOG_ADMIN_REDUCED", LANG_SERVER, iClient, szName, szTime); - } + LogToFile(g_szLogFile, "%T", "LOG_ADMIN_REDUCED", LANG_SERVER, iClient, szName, szTime); } } @@ -454,7 +435,7 @@ public int MenuHandler_TimeMenu(Menu hMenu, MenuAction action, int iClient, int g_hClientData[iClient].SetValue(DATA_KEY_Time, iExpires); char szQuery[512]; - FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `expires` = '%d' WHERE `account_id` = '%d'%s;", iExpires, iTarget, g_szSID); + FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `expires` = '%d' WHERE `account_id` = '%d'%s;", iExpires, iTarget, g_szServerID); DBG_SQL_Query(szQuery) g_hDatabase.Query(SQL_Callback_ChangeTime, szQuery, UID(iClient)); @@ -479,7 +460,7 @@ void ShowGroupsMenu(int iClient, const char[] sTargetGroup = NULL_STRING) { if (g_hGroups.GetSectionName(SZF(szGroup))) { - if(sTargetGroup[0] && !strcmp(sTargetGroup, szGroup)) + if (sTargetGroup[0] && !strcmp(sTargetGroup, szGroup)) { Format(SZF(szGroup), "%s [X]", szGroup); hMenu.AddItem(szGroup, szGroup, ITEMDRAW_DISABLED); @@ -540,7 +521,7 @@ public int MenuHandler_GroupsList(Menu hMenu, MenuAction action, int iClient, in { g_hClientData[iClient].GetValue(DATA_KEY_Time, iBuffer); g_hClientData[iClient].Clear(); - UTIL_ADD_VIP_PLAYER(iClient, iTarget, _, iBuffer, szGroup); + Clients_AddVipPlayer(iClient, iTarget, _, iBuffer, szGroup); } else { @@ -551,14 +532,14 @@ public int MenuHandler_GroupsList(Menu hMenu, MenuAction action, int iClient, in } case MENU_TYPE_EDIT: { - char szQuery[256], szName[MNL], szOldGroup[64]; + char szQuery[PMP], szName[MNL], szOldGroup[64]; hMenu.GetItem(iItem, SZF(szGroup)); int iTargetID; g_hClientData[iClient].GetValue(DATA_KEY_TargetID, iTargetID); g_hClientData[iClient].GetString(DATA_KEY_Name, SZF(szName)); g_hClientData[iClient].GetString(DATA_KEY_Group, SZF(szOldGroup)); - FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `group` = '%s' WHERE `account_id` = %d%s;", szGroup, iTargetID, g_szSID); + FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `group` = '%s' WHERE `account_id` = %d%s;", szGroup, iTargetID, g_szServerID); DBG_SQL_Query(szQuery) g_hDatabase.Query(SQL_Callback_ErrorCheck, szQuery); @@ -568,24 +549,23 @@ public int MenuHandler_GroupsList(Menu hMenu, MenuAction action, int iClient, in iTarget = CID(iTarget); if (!iTarget) { - iTarget = IsClientOnline(iTargetID); + iTarget = UTIL_GetVipClientByAccountID(iTargetID); } if (iTarget) { - ResetClient(iTarget); - CreateForward_OnVIPClientRemoved(iTarget, "VIP-Group Changed", iClient); + //Clients_ResetClient(iTarget); + //SET_BIT(g_iClientInfo[iTarget], IS_LOADED); + //CallForward_OnVIPClientRemoved(iTarget, "VIP-Group Changed", iClient); Clients_CheckVipAccess(iTarget, false); } VIP_PrintToChatClient(iClient, "%t", "ADMIN_SET_GROUP", szName, szGroup); - if (g_CVAR_bLogsEnable) - { - char szAdmin[256], szAdminInfo[128]; - UTIL_GetClientInfo(iClient, SZF(szAdminInfo)); - FormatEx(SZF(szAdmin), "%T %s", "BY_ADMIN", LANG_SERVER, szAdminInfo); - LogToFile(g_szLogFile, "%T", "LOG_CHANGE_GROUP", LANG_SERVER, szName, iTargetID, szOldGroup, szGroup, szAdmin); - } + + char szAdmin[PMP], szAdminInfo[128]; + UTIL_GetClientInfo(iClient, SZF(szAdminInfo)); + FormatEx(SZF(szAdmin), "%T %s", "BY_ADMIN", LANG_SERVER, szAdminInfo); + LogToFile(g_szLogFile, "%T", "LOG_CHANGE_GROUP", LANG_SERVER, szName, iTargetID, szOldGroup, szGroup, szAdmin); ShowTargetInfo(iClient); } @@ -615,10 +595,10 @@ public void SQL_Callback_ChangeTime(Database hOwner, DBResultSet hResult, const int iTarget; g_hClientData[iClient].GetValue(DATA_KEY_TargetUID, iTarget); iTarget = CID(iTarget); - if(!iTarget) + if (!iTarget) { g_hClientData[iClient].GetValue(DATA_KEY_TargetID, iTarget); - iTarget = IsClientOnline(iTarget); + iTarget = UTIL_GetVipClientByAccountID(iTarget); } if (iTarget) diff --git a/addons/sourcemod/scripting/vip/CMD.sp b/addons/sourcemod/scripting/vip/CMD.sp index e30ce8a..4c9f9ca 100644 --- a/addons/sourcemod/scripting/vip/CMD.sp +++ b/addons/sourcemod/scripting/vip/CMD.sp @@ -1,4 +1,3 @@ - void CMD_Setup() { RegAdminCmd("sm_refresh_vips", ReloadVIPPlayers_CMD, ADMFLAG_ROOT); @@ -15,7 +14,7 @@ void CMD_Setup() #endif } -public void OnConfigsExecuted() +public void CMD_Register() { static bool bIsRegistered; if (bIsRegistered == false) @@ -40,7 +39,7 @@ public Action VIPAdmin_CMD(int iClient, int iArgs) public Action ReloadVIPPlayers_CMD(int iClient, int iArgs) { - UTIL_ReloadVIPPlayers(iClient, true); + Clients_ReloadVipPlayers(iClient, true); return Plugin_Handled; } @@ -48,7 +47,7 @@ public Action ReloadVIPPlayers_CMD(int iClient, int iArgs) public Action ReloadVIPCfg_CMD(int iClient, int iArgs) { ReadConfigs(); - UTIL_ReloadVIPPlayers(iClient, false); + Clients_ReloadVipPlayers(iClient, false); UTIL_Reply(iClient, "%t", "VIP_CFG_REFRESHED"); return Plugin_Handled; @@ -69,7 +68,7 @@ public Action AddVIP_CMD(int iClient, int iArgs) bool bIsMulti; int iTargets, iAccountID = 0; - if((iTargets = ProcessTargetString( + if ((iTargets = ProcessTargetString( szBuffer, iClient, iTargetList, @@ -79,7 +78,7 @@ public Action AddVIP_CMD(int iClient, int iArgs) bIsMulti)) < 1) { iAccountID = UTIL_GetAccountIDFromSteamID(szBuffer); - if(!iAccountID) + if (!iAccountID) { ReplyToTargetError(iClient, iTargets); return Plugin_Handled; @@ -103,26 +102,26 @@ public Action AddVIP_CMD(int iClient, int iArgs) return Plugin_Handled; } - if(iTargets > 0) + if (iTargets > 0) { for(int i = 0; i < iTargets; ++i) { - if(IsClientInGame(iTargetList[i])) + if (IsClientInGame(iTargetList[i])) { - if (g_iClientInfo[iTargetList[i]] & IS_VIP) + if (IS_CLIENT_VIP(iTargetList[i])) { ReplyToCommand(iClient, "[VIP] %t", "ALREADY_HAS_VIP"); continue; } - UTIL_ADD_VIP_PLAYER(iClient, iTargetList[i], _, UTIL_TimeToSeconds(iTime), szGroup); + Clients_AddVipPlayer(iClient, iTargetList[i], _, UTIL_TimeToSeconds(iTime), szGroup); } } return Plugin_Handled; } - UTIL_ADD_VIP_PLAYER(iClient, _, iAccountID, UTIL_TimeToSeconds(iTime), szGroup); + Clients_AddVipPlayer(iClient, _, iAccountID, UTIL_TimeToSeconds(iTime), szGroup); return Plugin_Handled; } @@ -139,7 +138,7 @@ public Action DelVIP_CMD(int iClient, int iArgs) GetCmdArg(1, SZF(szAuth)); int iAccountID = UTIL_GetAccountIDFromSteamID(szAuth); - if(!iAccountID) + if (!iAccountID) { ReplyToTargetError(iClient, COMMAND_TARGET_NONE); return Plugin_Handled; @@ -147,7 +146,7 @@ public Action DelVIP_CMD(int iClient, int iArgs) FormatEx(SZF(szQuery), "SELECT `account_id`, `name`, `group` \ FROM `vip_users` \ - WHERE `account_id` = %d%s LIMIT 1;", iAccountID, g_szSID); + WHERE `account_id` = %d%s LIMIT 1;", iAccountID, g_szServerID); DebugMessage(szQuery) if (iClient) @@ -177,14 +176,31 @@ public void SQL_Callback_OnSelectRemoveClient(Database hOwner, DBResultSet hResu if (hResult.FetchRow()) { - DBG_SQL_Response("hResult.FetchRow()") - int iAccountID = hResult.FetchInt(0); - DBG_SQL_Response("hResult.FetchInt(0) = %d", iAccountID) - char szName[MNL], szGroup[64]; + int iAccountID; + char szName[MNL*2], szGroup[64], szAdminInfo[128], szTargetInfo[128], szAuth[32]; + UTIL_GetClientInfo(iClient, SZF(szTargetInfo)); + FormatEx(SZF(szAdminInfo), "%T %s", "BY_ADMIN", LANG_SERVER, szTargetInfo); + + iAccountID = hResult.FetchInt(0); hResult.FetchString(1, SZF(szName)); hResult.FetchString(2, SZF(szGroup)); - DBG_SQL_Response("hResult.FetchString(1) = '%s", szName) - DB_RemoveClientFromID(iClient, _, iAccountID, true, szName, szGroup); + + DBG_SQL_Response("hResult.FetchInt(0) = %d", iAccountID) + DBG_SQL_Response("hResult.FetchString(1) = '%s'", szName) + DBG_SQL_Response("hResult.FetchString(2) = '%s'", szGroup) + + UTIL_GetSteamIDFromAccountID(iAccountID, SZF(szAuth)); + FormatEx(SZF(szTargetInfo), "%s (%s, unknown)", szName, szAuth); + + DB_RemoveVipPlayerByData( + iClient, + szAdminInfo, + 0, + iAccountID, + szTargetInfo, + szGroup, + true + ); } else { @@ -196,13 +212,13 @@ public void SQL_Callback_OnSelectRemoveClient(Database hOwner, DBResultSet hResu public Action DumpFeatures_CMD(int iClient, int iArgs) { int iFeatures = g_hFeaturesArray.Length; - if(iFeatures != 0) + if (iFeatures != 0) { char szBuffer[PLATFORM_MAX_PATH]; BuildPath(Path_SM, SZF(szBuffer), "data/vip/features_dump.txt"); File hFile = OpenFile(szBuffer, "w"); - if(hFile != null) + if (hFile != null) { char szPluginName[64]; char szPluginPath[PLATFORM_MAX_PATH]; @@ -216,7 +232,7 @@ public Action DumpFeatures_CMD(int iClient, int iArgs) for(int i = 0; i < iFeatures; ++i) { g_hFeaturesArray.GetString(i, SZF(szFeature)); - if(GLOBAL_TRIE.GetValue(szFeature, hArray)) + if (GLOBAL_TRIE.GetValue(szFeature, hArray)) { hPlugin = view_as(hArray.Get(FEATURES_PLUGIN)); GetPluginInfo(hPlugin, PlInfo_Name, SZF(szPluginName)); @@ -253,24 +269,17 @@ public Action DumpFeatures_CMD(int iClient, int iArgs) public Action VIPMenu_CMD(int iClient, int iArgs) { - if (iClient) + if (iClient && !IsVipMenuFlood(iClient)) { - if (OnVipMenuFlood(iClient) == false) + if (!IS_CLIENT_VIP(iClient)) { - if (g_iClientInfo[iClient] & IS_VIP) - { - g_hVIPMenu.Display(iClient, MENU_TIME_FOREVER); - } - else - { - /* - PrintToChat(iClient, "%t%t", "VIP_CHAT_PREFIX", "COMMAND_NO_ACCESS"); - */ - - PlaySound(iClient, NO_ACCESS_SOUND); - DisplayClientInfo(iClient, "no_access_info"); - } + PlaySound(iClient, NO_ACCESS_SOUND); + DisplayClientInfo(iClient, "no_access_info"); + return Plugin_Handled; } + + DisplayVipMenu(iClient); } + return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/vip/Clients.sp b/addons/sourcemod/scripting/vip/Clients.sp index 208c9dd..c2c702e 100644 --- a/addons/sourcemod/scripting/vip/Clients.sp +++ b/addons/sourcemod/scripting/vip/Clients.sp @@ -1,575 +1,808 @@ - -void ResetClient(int iClient) -{ - g_iClientInfo[iClient] = IS_LOADED; - - UTIL_CloseHandleEx(g_hFeatures[iClient]); - UTIL_CloseHandleEx(g_hFeatureStatus[iClient]); -} - -public void OnClientPutInServer(int iClient) -{ - // g_iClientInfo[iClient] = 0; - DebugMessage("OnClientPutInServer %N (%d): %b", iClient, iClient, g_iClientInfo[iClient]) - - if(!IsFakeClient(iClient) && !IsClientSourceTV(iClient)) - { - Clients_CheckVipAccess(iClient, true, true); - } -} - -public void OnClientDisconnect(int iClient) -{ - /* if (g_bIsClientVIP[iClient]) - { - SaveClient(iClient); - }*/ - - if(!IsFakeClient(iClient)) - { - CreateForward_OnClientDisconnect(iClient); - } - - ResetClient(iClient); - UTIL_CloseHandleEx(g_hClientData[iClient]); - g_iClientInfo[iClient] = 0; -} - -void Clients_CheckVipAccess(int iClient, bool bNotify = false, bool bForward = false) -{ - if(bForward && !CreateForward_OnClientPreLoad(iClient)) - { - return; - } - - g_iClientInfo[iClient] &= ~IS_LOADED; - - ResetClient(iClient); - - if (IsFakeClient(iClient) == false && (GLOBAL_INFO & IS_STARTED) && g_hDatabase) - { - Clients_LoadClient(iClient, bNotify); - // DebugMessage("Clients_CheckVipAccess %N:\tИгрок %sявляется VIP игроком", iClient, g_bIsClientVIP[iClient] ? "":"не ") - } - else - { - g_iClientInfo[iClient] |= IS_LOADED; - CreateForward_OnClientLoaded(iClient); - } -} - -void Clients_LoadClient(int iClient, bool bNotify) -{ - char szQuery[512]; - - int iAccountID = GetSteamAccountID(iClient); - - DebugMessage("Clients_LoadClient %N (%d), %b: - > %x, %u", iClient, iClient, g_iClientInfo[iClient], g_hDatabase, g_hDatabase) - - char szWhere[64]; - if(g_szSID[0]) - { - #if USE_MORE_SERVERS 1 - FormatEx(SZF(szWhere), " AND (`sid` = %d OR `sid` = 0)", g_CVAR_iServerID); - #else - strcopy(SZF(szWhere), g_szSID); - #endif - } - - FormatEx(SZF(szQuery), "SELECT `expires`, `group`, `name` \ - FROM `vip_users` \ - WHERE `account_id` = %d%s LIMIT 1;", - iAccountID, szWhere); - - DataPack hDataPack = new DataPack(); - hDataPack.WriteCell(UID(iClient)); - hDataPack.WriteCell(iAccountID); - hDataPack.WriteCell(bNotify); - - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_OnClientAuthorized, szQuery, hDataPack); -} - -public void SQL_Callback_OnClientAuthorized(Database hOwner, DBResultSet hResult, const char[] szError, any hPack) -{ - DBG_SQL_Response("SQL_Callback_OnClientAuthorized") - DataPack hDataPack = view_as(hPack); - if (hResult == null || szError[0]) - { - LogError("SQL_Callback_OnClientAuthorized: %s", szError); - delete hDataPack; - return; - } - - hDataPack.Reset(); - - int iClient = CID(hDataPack.ReadCell()); - int iAccountID = hDataPack.ReadCell(); - bool bNotify = view_as(hDataPack.ReadCell()); - delete hDataPack; - - DebugMessage("SQL_Callback_OnClientAuthorized: %d", iClient) - if (!iClient || !IsClientInGame(iClient)) - { - return; - } - - if (hResult.FetchRow()) - { - DBG_SQL_Response("hResult.FetchRow()") - - int iExpires = hResult.FetchInt(0); - DBG_SQL_Response("hResult.FetchInt(0) = %d", iExpires) - if (iExpires > 0) - { - int iTime = GetTime(); - - if (iTime > iExpires) - { - if (g_CVAR_iDeleteExpired == 0 || (g_CVAR_iDeleteExpired > 0 && iTime >= ((g_CVAR_iDeleteExpired * 86400) + iExpires))) - { - if (g_CVAR_bLogsEnable) - { - LogToFile(g_szLogFile, "%T", "REMOVING_PLAYER", LANG_SERVER, iClient); - } - - DebugMessage("Clients_LoadClient %N (%d):\tDelete", iClient, iClient) - - char szGroup[64]; - hResult.FetchString(1, SZF(szGroup)); - - DB_RemoveClientFromID(REASON_EXPIRED, iClient, iAccountID, false, _, szGroup); - } - - CreateForward_OnVIPClientRemoved(iClient, "Expired"); - - DisplayClientInfo(iClient, "expired_info"); - - g_iClientInfo[iClient] |= IS_LOADED; - CreateForward_OnClientLoaded(iClient); - return; - } - - Clients_CreateExpiredTimer(iClient, iExpires, iTime); - } - - char szGroup[64]; - hResult.FetchString(1, SZF(szGroup)); - DBG_SQL_Response("hResult.FetchString(1) = '%s", szGroup) - if (szGroup[0] && UTIL_CheckValidVIPGroup(szGroup)) - { - Clients_CreateClientVIPSettings(iClient, iExpires); - - g_hFeatures[iClient].SetValue(KEY_CID, iAccountID); - - g_hFeatures[iClient].SetString(KEY_GROUP, szGroup); - - g_iClientInfo[iClient] |= IS_VIP|IS_LOADED; - - CreateForward_OnClientLoaded(iClient); - - char szName[MAX_NAME_LENGTH*2+1]; - hResult.FetchString(2, SZF(szName)); - DB_UpdateClient(iClient, szName); - - if (bNotify) - { - if (g_CVAR_bAutoOpenMenu) - { - g_hVIPMenu.Display(iClient, MENU_TIME_FOREVER); - } - - DisplayClientInfo(iClient, iExpires == 0 ? "connect_info_perm":"connect_info_time"); - } - - Clients_LoadVIPFeaturesPre(iClient); - } - else - { - LogError("Invalid VIP-Group/Некорректная VIP-группа: %s (Игрок: %d)", szGroup, iAccountID); - } - return; - } - - CreateForward_OnClientLoaded(iClient); -} - -void Clients_OnVIPClientLoaded(int iClient) -{ - Features_TurnOnAll(iClient); - - CreateForward_OnVIPClientLoaded(iClient); -} - -void Clients_CreateClientVIPSettings(int iClient, int iExp) -{ - /* - g_hFeatures[iClient] = new StringMap(); - g_hFeatureStatus[iClient] = new StringMap(); - */ - g_hFeatures[iClient] = CreateTrie(); - g_hFeatureStatus[iClient] = CreateTrie(); - - g_hFeatures[iClient].SetValue(KEY_EXPIRES, iExp); -} - -#if DEBUG_MODE 1 -public void OnClientCookiesCached(int iClient) -{ - DebugMessage("OnClientCookiesCached %d %N", iClient, iClient) - - DebugMessage("AreClientCookiesCached %b", AreClientCookiesCached(iClient)) -} -#endif - -void Clients_LoadVIPFeaturesPre(int iClient, const char[] szFeature = NULL_STRING) -{ - DebugMessage("Clients_LoadVIPFeaturesPre %N", iClient) - - DebugMessage("AreClientCookiesCached %b", AreClientCookiesCached(iClient)) - - if (!AreClientCookiesCached(iClient)) - { - DataPack hDataPack = new DataPack(); - hDataPack.WriteCell(UID(iClient)); - if(szFeature[0]) - { - hDataPack.WriteCell(true); - hDataPack.WriteString(szFeature); - } - else - { - hDataPack.WriteCell(false); - } - CreateTimer(0.5, Timer_CheckCookies, hDataPack, TIMER_FLAG_NO_MAPCHANGE|TIMER_DATA_HNDL_CLOSE); - } - - if(szFeature[0]) - { - Clients_LoadVIPFeature(iClient, szFeature); - return; - } - - Clients_LoadVIPFeatures(iClient); -} - -public Action Timer_CheckCookies(Handle hTimer, Handle hDP) -{ - DataPack hDataPack = view_as(hDP); - hDataPack.Reset(); - int iClient = CID(hDataPack.ReadCell()); - - DebugMessage("Timer_CheckCookies -> iClient: %N (%d), IsClientVIP: %b,", iClient, iClient, view_as(g_iClientInfo[iClient] & IS_VIP)) - if (iClient && g_iClientInfo[iClient] & IS_VIP) - { - char szFeature[FEATURE_NAME_LENGTH]; - if(hDataPack.ReadCell()) - { - hDataPack.ReadString(SZF(szFeature)); - } - else - { - szFeature[0] = 0; - } - Clients_LoadVIPFeaturesPre(iClient, szFeature); - } - - return Plugin_Stop; -} - -void Clients_LoadVIPFeatures(int iClient) -{ - DebugMessage("LoadVIPFeatures %N", iClient) - - int iFeatures = g_hFeaturesArray.Length; - DebugMessage("FeaturesArraySize: %d", iFeatures) - if (iFeatures > 0) - { - char szFeature[FEATURE_NAME_LENGTH]; - - g_hFeatures[iClient].GetString(KEY_GROUP, SZF(szFeature)); - if (UTIL_CheckValidVIPGroup(szFeature)) - { - for (int i = 0; i < iFeatures; ++i) - { - g_hFeaturesArray.GetString(i, SZF(szFeature)); - Clients_LoadFeature(iClient, szFeature); - } - } - } - - DebugMessage("Clients_OnVIPClientLoaded: %d %N", iClient, iClient) - - Clients_OnVIPClientLoaded(iClient); -} - -void Clients_LoadVIPFeature(int iClient, const char[] szFeature) -{ - DebugMessage("LoadVIPFeature %N", iClient) - - int iFeatures = g_hFeaturesArray.Length; - DebugMessage("FeaturesArraySize: %d", iFeatures) - if (iFeatures > 0) - { - char szGroup[FEATURE_NAME_LENGTH]; - - g_hFeatures[iClient].GetString(KEY_GROUP, SZF(szGroup)); - if (UTIL_CheckValidVIPGroup(szGroup)) - { - Clients_LoadFeature(iClient, szFeature); - } - } -/* - DebugMessage("Clients_OnVIPClientLoaded: %d %N", iClient, iClient) - - Clients_OnVIPClientLoaded(iClient); - */ -} - -void Clients_LoadFeature(int iClient, const char[] szFeature) -{ - static ArrayList hArray; - if (!GLOBAL_TRIE.GetValue(szFeature, hArray)) - return; - - DebugMessage("LoadClientFeature: %s", szFeature) - - if (!GetValue(iClient, view_as(hArray.Get(FEATURES_VALUE_TYPE)), szFeature)) - return; - - static VIP_ToggleState eStatus; - DebugMessage("GetValue: == true") - if (view_as(hArray.Get(FEATURES_ITEM_TYPE)) == TOGGLABLE) - { - static char szBuffer[4]; - static Handle hCookie; - hCookie = view_as(hArray.Get(FEATURES_COOKIE)); - GetClientCookie(iClient, hCookie, SZF(szBuffer)); - eStatus = view_as(StringToInt(szBuffer)); - DebugMessage("GetFeatureCookie: '%s'", szBuffer) - if (szBuffer[0] == '\0' || !Features_IsValidStatus(eStatus)) - { - switch(hArray.Get(FEATURES_DEF_STATUS)) - { - case NO_ACCESS: eStatus = g_CVAR_bDefaultStatus ? ENABLED : DISABLED; - case ENABLED: eStatus = ENABLED; - case DISABLED: eStatus = DISABLED; - } - - IntToString(view_as(eStatus), SZF(szBuffer)); - SetClientCookie(iClient, hCookie, szBuffer); - // Features_SaveStatus(iClient, szFeature, hCookie, eStatus); - } - } - else - { - eStatus = ENABLED; - } - - Features_SetStatus(iClient, szFeature, eStatus); - // Function_OnItemToggle(view_as(hArray.Get(FEATURES_PLUGIN)), Function:hArray.Get(FEATURES_ITEM_SELECT), iClient, szFeature, NO_ACCESS, ENABLED); -} - -bool Features_IsValidStatus(VIP_ToggleState eStatus) -{ - return ( - eStatus == NO_ACCESS || - eStatus == ENABLED || - eStatus == DISABLED - ); -} - -bool GetValue(int iClient, VIP_ValueType ValueType, const char[] szFeature) -{ - DebugMessage("GetValue: %d - %s", ValueType, szFeature) - switch (ValueType) - { - case VIP_NULL: - { - return false; - } - case BOOL: - { - if (g_hGroups.GetNum(szFeature)) - { - DebugMessage("value: 1") - return g_hFeatures[iClient].SetValue(szFeature, true); - } - return false; - } - case INT: - { - int iValue; - iValue = g_hGroups.GetNum(szFeature); - if (iValue != 0) - { - DebugMessage("value: %d", iValue) - return g_hFeatures[iClient].SetValue(szFeature, iValue); - } - return false; - } - case FLOAT: - { - float fValue; - fValue = g_hGroups.GetFloat(szFeature); - if (fValue != 0.0) - { - DebugMessage("value: %f", fValue) - return g_hFeatures[iClient].SetValue(szFeature, fValue); - } - - return false; - } - case STRING: - { - char szBuffer[256]; - g_hGroups.GetString(szFeature, SZF(szBuffer)); - if (szBuffer[0]) - { - DebugMessage("value: %s", szBuffer) - return g_hFeatures[iClient].SetString(szFeature, szBuffer); - } - return false; - } - } - - return false; -} - -void Clients_CreateExpiredTimer(int iClient, int iExp, int iTime) -{ - int iTimeLeft; - GetMapTimeLeft(iTimeLeft); - DebugMessage("Clients_CreateExpiredTimer %N (%d):\tiTimeLeft: %d", iClient, iClient, iTimeLeft) - if (iTimeLeft > 0) - { - DebugMessage("Clients_CreateExpiredTimer %N (%d):\tiTimeLeft+iTime: %d", iClient, iClient, iTimeLeft + iTime) - if ((iTimeLeft + iTime) > iExp) - { - DebugMessage("Clients_CreateExpiredTimer %N (%d):\tTimerDealy: %f", iClient, iClient, float((iExp - iTime) + 3)) - - CreateTimer(float((iExp - iTime) + 3), Timer_VIP_Expired, UID(iClient), TIMER_FLAG_NO_MAPCHANGE); - } - } -} - -public void Event_MatchEndRestart(Event hEvent, const char[] sEvName, bool bDontBroadcast) -{ - if (g_CVAR_iDeleteExpired != -1 || g_CVAR_iOutdatedExpired != -1) - { - RemoveExpAndOutPlayers(); - } -} - -public void Event_PlayerSpawn(Event hEvent, const char[] sEvName, bool bDontBroadcast) -{ - int UserID = hEvent.GetInt("userid"); - int iClient = CID(UserID); - DebugMessage("Event_PlayerSpawn: %N (%d)", iClient, iClient) - if (!(g_iClientInfo[iClient] & IS_SPAWNED)) - { - CreateTimer(g_CVAR_fSpawnDelay, Timer_OnPlayerSpawn, UserID, TIMER_FLAG_NO_MAPCHANGE); - } -} - -public void Event_PlayerDeath(Event hEvent, const char[] sEvName, bool bDontBroadcast) -{ - int iClient = CID(hEvent.GetInt("userid")); - DebugMessage("Event_PlayerDeath: %N (%d)", iClient, iClient) - g_iClientInfo[iClient] &= ~IS_SPAWNED; -} - -public Action Timer_OnPlayerSpawn(Handle hTimer, any UserID) -{ - int iClient = CID(UserID); - if (iClient && IsClientInGame(iClient)) - { - int iTeam = GetClientTeam(iClient); - if (iTeam > 1 && IsPlayerAlive(iClient)) - { - DebugMessage("Timer_OnPlayerSpawn: %N (%d)", iClient, iClient) - - if (g_iClientInfo[iClient] & IS_VIP) - { - int iExp; - if (g_hFeatures[iClient].GetValue(KEY_EXPIRES, iExp) && iExp > 0 && iExp < GetTime()) - { - Clients_ExpiredClient(iClient); - } - } - - g_iClientInfo[iClient] |= IS_SPAWNED; - CreateForward_OnPlayerSpawn(iClient, iTeam); - } - } - return Plugin_Stop; -} - -public void Event_RoundEnd(Event hEvent, const char[] sEvName, bool bDontBroadcast) -{ - DebugMessage("Event_RoundEnd") - int iTime, iExp, i; - iTime = GetTime(); - for (i = 1; i <= MaxClients; ++i) - { - if (IsClientInGame(i)) - { - g_iClientInfo[i] &= ~IS_SPAWNED; - if ((g_iClientInfo[i] & IS_VIP) && g_hFeatures[i].GetValue(KEY_EXPIRES, iExp) && iExp > 0 && iExp < iTime) - { - Clients_ExpiredClient(i); - } - } - } -} - -public Action Timer_VIP_Expired(Handle hTimer, any UserID) -{ - DebugMessage("Timer_VIP_Expired %d:", UserID) - - int iClient = CID(UserID); - if (iClient && g_iClientInfo[iClient] & IS_VIP) - { - int iExp; - if (g_hFeatures[iClient].GetValue(KEY_EXPIRES, iExp) && iExp > 0 && iExp < GetTime()) - { - DebugMessage("Timer_VIP_Expired %N:", iClient) - - Clients_ExpiredClient(iClient); - } - } - - return Plugin_Continue; -} - -void Clients_ExpiredClient(int iClient) -{ - DebugMessage("Clients_ExpiredClient %N:", iClient) - Features_TurnOffAll(iClient); - - int iClientID; - g_hFeatures[iClient].GetValue(KEY_EXPIRES, iClientID); - if (g_CVAR_iDeleteExpired == 0 || GetTime() >= ((g_CVAR_iDeleteExpired*86400) + iClientID)) - { - if (g_hFeatures[iClient].GetValue(KEY_CID, iClientID) && iClientID != -1) - { - if (g_CVAR_bLogsEnable) - { - LogToFile(g_szLogFile, "%T", "REMOVING_PLAYER", LANG_SERVER, iClient); - } - - DB_RemoveClientFromID(REASON_EXPIRED, iClient, _, false); - } - } - - if(g_iClientInfo[iClient] & IS_MENU_OPEN) - { - CancelClientMenu(iClient); - } - - ResetClient(iClient); - - CreateForward_OnVIPClientRemoved(iClient, "Expired"); - - DisplayClientInfo(iClient, "expired_info"); -} + +void Clients_ResetClient(int iClient) +{ + g_iClientInfo[iClient] = 0; + + UTIL_CloseHandleEx(g_hFeatures[iClient]); + UTIL_CloseHandleEx(g_hFeatureStatus[iClient]); +} + +public void OnClientPutInServer(int iClient) +{ + // g_iClientInfo[iClient] = 0; + DBG_CLIENTS("OnClientPutInServer %N (%d): %b", iClient, iClient, g_iClientInfo[iClient]) + + if (IsFakeClient(iClient) || IsClientSourceTV(iClient)) + { + return; + } + + Storage_LoadClient(iClient); + Clients_CheckVipAccess(iClient, true, true); +} + +public void OnClientDisconnect(int iClient) +{ + /* if (g_bIsClientVIP[iClient]) + { + SaveClient(iClient); + }*/ + + if (!IsFakeClient(iClient)) + { + CallForward_OnClientDisconnect(iClient); + Storage_SaveClient(iClient); + } + + Clients_ResetClient(iClient); + UTIL_CloseHandleEx(g_hClientData[iClient]); + g_iClientInfo[iClient] = 0; + + Storage_ResetClient(iClient); +} + +void Clients_CheckVipAccess(int iClient, bool bNotify = false, bool bForward = false) +{ + if (bForward && !CallForward_OnClientPreLoad(iClient)) + { + return; + } + + Clients_ResetClient(iClient); + + // UNSET_BIT(g_iClientInfo[iClient], IS_LOADED); + + if (IsFakeClient(iClient) == false && (GLOBAL_INFO & IS_STARTED) && g_hDatabase) + { + Clients_LoadClient(iClient, bNotify); + // DBG_CLIENTS("Clients_CheckVipAccess %N:\tИгрок %sявляется VIP игроком", iClient, g_bIsClientVIP[iClient] ? "":"не ") + } + else + { + SET_BIT(g_iClientInfo[iClient], IS_LOADED); + CallForward_OnClientLoaded(iClient); + } +} + +void Clients_LoadClient(int iClient, bool bNotify) +{ + char szQuery[512]; + + int iAccountID = GetSteamAccountID(iClient); + + DBG_CLIENTS("Clients_LoadClient %N (%d), %b: - > %x, %u", iClient, iClient, g_iClientInfo[iClient], g_hDatabase, g_hDatabase) + + FormatEx(SZF(szQuery), "SELECT `expires`, `group`, `name` \ + FROM `vip_users` \ + WHERE `account_id` = %d%s LIMIT 1;", + iAccountID, g_szServerID); + + DataPack hDataPack = new DataPack(); + hDataPack.WriteCell(UID(iClient)); + hDataPack.WriteCell(iAccountID); + hDataPack.WriteCell(bNotify); + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_OnClientAuthorized, szQuery, hDataPack); +} + +public void SQL_Callback_OnClientAuthorized(Database hOwner, DBResultSet hResult, const char[] szError, any hPack) +{ + DBG_SQL_Response("SQL_Callback_OnClientAuthorized") + DataPack hDataPack = view_as(hPack); + if (hResult == null || szError[0]) + { + LogError("SQL_Callback_OnClientAuthorized: %s", szError); + delete hDataPack; + return; + } + + hDataPack.Reset(); + + int iClient = CID(hDataPack.ReadCell()); + int iAccountID = hDataPack.ReadCell(); + bool bNotify = view_as(hDataPack.ReadCell()); + delete hDataPack; + + DBG_CLIENTS("SQL_Callback_OnClientAuthorized: %d", iClient) + if (!iClient || !IsClientInGame(iClient)) + { + return; + } + + if (!hResult.FetchRow()) + { + OnClientLoaded(iClient); + return; + } + DBG_SQL_Response("hResult.FetchRow()") + + int iExpires = hResult.FetchInt(0); + DBG_SQL_Response("hResult.FetchInt(0) = %d", iExpires) + char szGroup[64]; + hResult.FetchString(1, SZF(szGroup)); + DBG_SQL_Response("hResult.FetchString(1) = '%s", szGroup) + + + LoadClient(iClient, iAccountID, szGroup, iExpires); + + if (IS_CLIENT_VIP(iClient)) + { + char szName[MAX_NAME_LENGTH*2+1]; + hResult.FetchString(2, SZF(szName)); + DB_UpdateClient(iClient, szName); + + if (bNotify) + { + if (g_CVAR_bAutoOpenMenu) + { + g_hVIPMenu.Display(iClient, MENU_TIME_FOREVER); + } + + DisplayClientInfo(iClient, iExpires == 0 ? "connect_info_perm":"connect_info_time"); + } + + Clients_TryLoadFeatures(iClient); + return; + } + + OnClientLoaded(iClient); +} + +void LoadClient(int iClient, int iAccountID, const char[] szGroup, int iExpires) +{ + DBG_CLIENTS("LoadClient: %d, %d, %s, %d", iClient, iAccountID, szGroup, iExpires) + if (!szGroup[0] || !UTIL_CheckValidVIPGroup(szGroup)) + { + LogError("Invalid VIP-Group/Некорректная VIP-группа: %s (Игрок: %d)", szGroup, iAccountID); + return; + } + + if (iExpires > 0) + { + int iTime = GetTime(); + + if (iTime > iExpires) + { + if (g_CVAR_iDeleteExpired == 0 || (g_CVAR_iDeleteExpired > 0 && iTime >= ((g_CVAR_iDeleteExpired * 86400) + iExpires))) + { + LogToFile(g_szLogFile, "%T", "REMOVING_PLAYER", LANG_SERVER, iClient); + + DBG_CLIENTS("Clients_LoadClient %N (%d): >>> Delete", iClient, iClient) + + Clients_RemoveVipPlayer(REASON_EXPIRED, iClient, iAccountID, true); + } + + CallForward_OnVIPClientRemoved(iClient, "Expired"); + + DisplayClientInfo(iClient, "expired_info"); + + return; + } + + Clients_CreateExpiredTimer(iClient, iExpires, iTime); + } + + Clients_InitVIPClient(iClient, iAccountID, szGroup, iExpires); +} + + +void OnClientLoaded(int iClient) +{ + SET_BIT(g_iClientInfo[iClient], IS_LOADED); + CallForward_OnClientLoaded(iClient); +} + +void OnVIPClientLoaded(int iClient) +{ + SET_BIT(g_iClientInfo[iClient], IS_LOADED); + CallForward_OnVIPClientLoaded(iClient); +} + +stock void Clients_OnVIPClientLoaded(int iClient) +{ + Features_TurnOnAll(iClient); + + CallForward_OnVIPClientLoaded(iClient); +} + +void Clients_InitVIPClient(int iClient, int iAccountID = -1, const char[] szGroup = NULL_STRING, int iExpires = 0) +{ + DBG_CLIENTS("Clients_InitVIPClient: %d, %d, %s, %d", iClient, iAccountID, szGroup, iExpires) + g_hFeatures[iClient] = new StringMap(); + g_hFeatureStatus[iClient] = new StringMap(); + + g_hFeatures[iClient].SetValue(KEY_EXPIRES, iExpires); + g_hFeatures[iClient].SetString(KEY_GROUP, szGroup); + g_hFeatures[iClient].SetValue(KEY_CID, iAccountID); + + SET_BIT(g_iClientInfo[iClient], IS_VIP); +} + +#if USE_CLIENTPREFS 1 +public void OnClientCookiesCached(int iClient) +{ + DBG_CLIENTS("OnClientCookiesCached %d %N", iClient, iClient) + + DBG_CLIENTS("AreClientCookiesCached %b", AreClientCookiesCached(iClient)) + //OnClientStorageLoaded(iClient); +} +#else +public void VIP_OnClientStorageLoaded(int iClient) +{ + DBG_CLIENTS("VIP_OnClientStorageLoaded: %d %N", iClient, iClient) + //OnClientStorageLoaded(iClient); +} +#endif +/* +void OnClientStorageLoaded(int iClient) +{ + DBG_CLIENTS("OnClientStorageLoaded: %d %N", iClient, iClient) +} +*/ + +bool IsClientStorageLoaded(int iClient) +{ + #if USE_CLIENTPREFS 1 + DBG_CLIENTS("AreClientCookiesCached: %d %N", iClient, iClient) + return AreClientCookiesCached(iClient); + #else + DBG_CLIENTS("Storage_IsClientLoaded: %d %N", iClient, iClient) + return Storage_IsClientLoaded(iClient); + #endif +} + +void Clients_TryLoadFeatures(int iClient) +{ + DBG_CLIENTS("Clients_TryLoadFeatures %L", iClient) + DBG_CLIENTS("IsClientStorageLoaded %b", IsClientStorageLoaded(iClient)) + + if (!IsClientStorageLoaded(iClient)) + { + // TODO: may be will add attempts counter + DataPack hDataPack = new DataPack(); + hDataPack.WriteCell(UID(iClient)); + CreateTimer(1.0, Timer_CheckStorageLoadFeatures, hDataPack, TIMER_FLAG_NO_MAPCHANGE|TIMER_DATA_HNDL_CLOSE); + return; + } + + Clients_LoadFeatures(iClient); +} + +public Action Timer_CheckStorageLoadFeatures(Handle hTimer, DataPack hDataPack) +{ + hDataPack.Reset(); + int iClient = CID(hDataPack.ReadCell()); + + DBG_CLIENTS("Timer_CheckStorageLoadFeatures -> iClient: %N (%d), IsClientVIP: %b", iClient, iClient, IS_CLIENT_VIP(iClient)) + if (iClient && IS_CLIENT_VIP(iClient)) + { + Clients_TryLoadFeatures(iClient); + } + + return Plugin_Stop; +} + + +void Clients_TryLoadFeature(int iClient, const char[] szFeature) +{ + DBG_CLIENTS("Clients_TryLoadFeature %L", iClient) + + if (!IsClientStorageLoaded(iClient)) + { + DataPack hDataPack = new DataPack(); + hDataPack.WriteCell(UID(iClient)); + hDataPack.WriteString(szFeature); + CreateTimer(1.0, Timer_CheckStorageLoadFeature, hDataPack, TIMER_FLAG_NO_MAPCHANGE|TIMER_DATA_HNDL_CLOSE); + } + + Clients_LoadFeature(iClient, szFeature); +} + +public Action Timer_CheckStorageLoadFeature(Handle hTimer, DataPack hDataPack) +{ + hDataPack.Reset(); + int iClient = CID(hDataPack.ReadCell()); + + DBG_CLIENTS("Timer_CheckStorageLoadFeature -> iClient: %N (%d), IsClientVIP: %b", iClient, iClient, IS_CLIENT_VIP(iClient)) + if (iClient && IS_CLIENT_VIP(iClient)) + { + char szFeature[FEATURE_NAME_LENGTH]; + hDataPack.ReadString(SZF(szFeature)); + Clients_LoadFeature(iClient, szFeature); + } + + return Plugin_Stop; +} + +void Clients_LoadFeatures(int iClient) +{ + DBG_CLIENTS("LoadVIPFeatures %N", iClient) + + int iFeaturesCount = g_hFeaturesArray.Length; + DBG_CLIENTS("FeaturesArraySize: %d", iFeaturesCount) + if (iFeaturesCount > 0) + { + char szFeature[FEATURE_NAME_LENGTH]; + + g_hFeatures[iClient].GetString(KEY_GROUP, SZF(szFeature)); + if (UTIL_CheckValidVIPGroup(szFeature)) + { + for (int i = 0; i < iFeaturesCount; ++i) + { + g_hFeaturesArray.GetString(i, SZF(szFeature)); + Clients_LoadFeatureValue(iClient, szFeature); + } + } + } + + DBG_CLIENTS("Clients_OnVIPClientLoaded: %d %N", iClient, iClient) + + OnClientLoaded(iClient); + OnVIPClientLoaded(iClient); + Features_TurnOnAll(iClient); +} + +void Clients_LoadFeature(int iClient, const char[] szFeature) +{ + DBG_CLIENTS("LoadVIPFeature %N", iClient) + + int iFeaturesCount = g_hFeaturesArray.Length; + DBG_CLIENTS("FeaturesArraySize: %d", iFeaturesCount) + if (iFeaturesCount > 0) + { + char szGroup[FEATURE_NAME_LENGTH]; + + g_hFeatures[iClient].GetString(KEY_GROUP, SZF(szGroup)); + if (UTIL_CheckValidVIPGroup(szGroup)) + { + Clients_LoadFeatureValue(iClient, szFeature); + } + } +/* + DBG_CLIENTS("Clients_OnVIPClientLoaded: %d %N", iClient, iClient) + + Clients_OnVIPClientLoaded(iClient); + */ +} + +void Clients_LoadFeatureValue(int iClient, const char[] szFeature) +{ + static ArrayList hArray; + if (!GLOBAL_TRIE.GetValue(szFeature, hArray)) + return; + + DBG_CLIENTS("Clients_LoadFeatureValue: %s", szFeature) + + if (!GetFeatureValue(iClient, view_as(hArray.Get(FEATURES_VALUE_TYPE)), szFeature)) + return; + + static VIP_ToggleState eStatus; + DBG_CLIENTS("GetValue: == true") + if (view_as(hArray.Get(FEATURES_ITEM_TYPE)) == TOGGLABLE) + { + eStatus = Features_GetStatusFromStorage(iClient, szFeature, hArray); + DBG_CLIENTS("Features_GetStatusFromStorage: '%d'", eStatus) + } + else + { + eStatus = ENABLED; + } + + Features_SetStatus(iClient, szFeature, eStatus); +} + +bool GetFeatureValue(int iClient, VIP_ValueType ValueType, const char[] szFeature) +{ + DBG_CLIENTS("GetFeatureValue: %d - %s", ValueType, szFeature) + switch (ValueType) + { + case BOOL: + { + if (g_hGroups.GetNum(szFeature)) + { + DBG_CLIENTS("value: 1") + return g_hFeatures[iClient].SetValue(szFeature, true); + } + return false; + } + case INT: + { + int iValue; + iValue = g_hGroups.GetNum(szFeature); + if (iValue != 0) + { + DBG_CLIENTS("value: %d", iValue) + return g_hFeatures[iClient].SetValue(szFeature, iValue); + } + return false; + } + case FLOAT: + { + float fValue; + fValue = g_hGroups.GetFloat(szFeature); + if (fValue != 0.0) + { + DBG_CLIENTS("value: %f", fValue) + return g_hFeatures[iClient].SetValue(szFeature, fValue); + } + + return false; + } + case STRING: + { + char szBuffer[PMP]; + g_hGroups.GetString(szFeature, SZF(szBuffer)); + if (szBuffer[0]) + { + DBG_CLIENTS("value: %s", szBuffer) + return g_hFeatures[iClient].SetString(szFeature, szBuffer); + } + return false; + } + case VIP_NULL: + { + return false; + } + } + + return false; +} + +void Clients_CreateExpiredTimer(int iClient, int iExp, int iTime) +{ + int iTimeLeft; + GetMapTimeLeft(iTimeLeft); + DBG_CLIENTS("Clients_CreateExpiredTimer %N (%d): iTimeLeft: %d", iClient, iClient, iTimeLeft) + if (iTimeLeft > 0) + { + DBG_CLIENTS("Clients_CreateExpiredTimer %N (%d): iTimeLeft+iTime: %d", iClient, iClient, iTimeLeft + iTime) + if ((iTimeLeft + iTime) < iExp) + { + DBG_CLIENTS("Skip timer") + return; + } + } + + DBG_CLIENTS("Clients_CreateExpiredTimer %N (%d): TimerDealy: %f", iClient, iClient, float((iExp - iTime) + 3)) + CreateTimer(float((iExp - iTime) + 3), Timer_VIP_Expired, UID(iClient), TIMER_FLAG_NO_MAPCHANGE); +} + +public void Event_MatchEndRestart(Event hEvent, const char[] sEvName, bool bDontBroadcast) +{ + RemoveExpAndOutPlayers(); +} + +public void Event_PlayerSpawn(Event hEvent, const char[] sEvName, bool bDontBroadcast) +{ + int UserID = hEvent.GetInt("userid"); + int iClient = CID(UserID); + DBG_CLIENTS("Event_PlayerSpawn: %N (%d)", iClient, iClient) + if (!(g_iClientInfo[iClient] & IS_SPAWNED)) + { + CreateTimer(g_CVAR_fSpawnDelay, Timer_OnPlayerSpawn, UserID, TIMER_FLAG_NO_MAPCHANGE); + } +} + +public void Event_PlayerDeath(Event hEvent, const char[] sEvName, bool bDontBroadcast) +{ + int iClient = CID(hEvent.GetInt("userid")); + DBG_CLIENTS("Event_PlayerDeath: %N (%d)", iClient, iClient) + g_iClientInfo[iClient] &= ~IS_SPAWNED; +} + +public Action Timer_OnPlayerSpawn(Handle hTimer, any UserID) +{ + int iClient = CID(UserID); + if (iClient && IsClientInGame(iClient)) + { + int iTeam = GetClientTeam(iClient); + if (iTeam > 1 && IsPlayerAlive(iClient)) + { + DBG_CLIENTS("Timer_OnPlayerSpawn: %N (%d)", iClient, iClient) + + if (IS_CLIENT_VIP(iClient)) + { + int iExp; + if (g_hFeatures[iClient].GetValue(KEY_EXPIRES, iExp) && iExp > 0 && iExp < GetTime()) + { + Clients_ExpiredClient(iClient); + } + } + + g_iClientInfo[iClient] |= IS_SPAWNED; + CallForward_OnPlayerSpawn(iClient, iTeam); + } + } + return Plugin_Stop; +} + +public void Event_RoundEnd(Event hEvent, const char[] sEvName, bool bDontBroadcast) +{ + DBG_CLIENTS("Event_RoundEnd") + int iTime, iExp, i; + iTime = GetTime(); + for (i = 1; i <= MaxClients; ++i) + { + if (IsClientInGame(i)) + { + g_iClientInfo[i] &= ~IS_SPAWNED; + if (IS_CLIENT_VIP(i) && g_hFeatures[i].GetValue(KEY_EXPIRES, iExp) && iExp > 0 && iExp < iTime) + { + Clients_ExpiredClient(i); + } + } + } +} + +public Action Timer_VIP_Expired(Handle hTimer, any UserID) +{ + DBG_CLIENTS("Timer_VIP_Expired %d:", UserID) + + int iClient = CID(UserID); + if (iClient && IS_CLIENT_VIP(iClient)) + { + int iExp; + if (g_hFeatures[iClient].GetValue(KEY_EXPIRES, iExp) && iExp > 0 && iExp < GetTime()) + { + DBG_CLIENTS("Timer_VIP_Expired %N:", iClient) + + Clients_ExpiredClient(iClient); + } + } + + return Plugin_Stop; +} + +void Clients_ExpiredClient(int iClient) +{ + DBG_CLIENTS("Clients_ExpiredClient %N:", iClient) + Features_TurnOffAll(iClient); + + int iClientID; + g_hFeatures[iClient].GetValue(KEY_EXPIRES, iClientID); + if (g_CVAR_iDeleteExpired == 0 || GetTime() >= ((g_CVAR_iDeleteExpired*86400) + iClientID)) + { + if (g_hFeatures[iClient].GetValue(KEY_CID, iClientID) && iClientID != -1) + { + LogToFile(g_szLogFile, "%T", "REMOVING_PLAYER", LANG_SERVER, iClient); + + Clients_RemoveVipPlayer(REASON_EXPIRED, iClient, _, true); + return; + } + } + + if (g_iClientInfo[iClient] & IS_MENU_OPEN) + { + CancelClientMenu(iClient); + } + + Clients_ResetClient(iClient); + SET_BIT(g_iClientInfo[iClient], IS_LOADED); + + CallForward_OnVIPClientRemoved(iClient, "Expired"); + + DisplayClientInfo(iClient, "expired_info"); +} + +void Clients_AddVipPlayer( + int iAdmin = OWNER_SERVER, + int iTarget = 0, + int iTargetAccountID = 0, + int iDuration, + const char[] szGroup, + const char[] szByWho = NULL_STRING +) +{ + char szAdminInfo[PMP], szTargetInfo[PMP]; + int iExpires; + + if (iDuration) + { + iExpires = iDuration + GetTime(); + } + else + { + iExpires = iDuration; + } + + if (iTarget) + { + iTargetAccountID = GetSteamAccountID(iTarget); + UTIL_GetClientInfo(iTarget, SZF(szTargetInfo)); + } + else + { + char szAuth[32]; + UTIL_GetSteamIDFromAccountID(iTargetAccountID, SZF(szAuth)); + FormatEx(SZF(szTargetInfo), "unknown (%s, unknown)", szAuth); + } + + switch(iAdmin) + { + case OWNER_PLUGIN: + { + FormatEx(SZF(szAdminInfo), "%T %s", "BY_PLUGIN", LANG_SERVER, szByWho); + } + case OWNER_SERVER: + { + FormatEx(SZF(szAdminInfo), "%T", "BY_SERVER", LANG_SERVER); + } + default: + { + char szAdmin[128]; + UTIL_GetClientInfo(iAdmin, SZF(szAdmin)); + FormatEx(SZF(szAdminInfo), "%T %s", "BY_ADMIN", LANG_SERVER, szAdmin); + iAdmin = UID(iAdmin); + } + } + + DB_AddVipPlayer( + iAdmin, + szAdminInfo, + iTarget, + iTargetAccountID, + szTargetInfo, + iDuration, + iExpires, + szGroup + ); +} + +void Clients_OnVipPlayerAdded( + const int iAdmin, + const char[] szAdminInfo, + const int iTarget, + const int iTargetAccountID, + const char[] szTargetInfo, + const int iDuration, + const int iExpires, + const char[] szGroup +) +{ + char szExpires[64], szDuration[64]; + + if (iTarget) + { + Clients_CheckVipAccess(iTarget, true); + CallForward_OnVIPClientAdded(iTarget, iAdmin); + } + + if (iAdmin >= 0) + { + if (iDuration) + { + UTIL_GetTimeFromStamp(SZF(szDuration), iDuration, iAdmin); + FormatTime(SZF(szExpires), "%d/%m/%Y - %H:%M", iExpires); + } + else + { + FormatEx(SZF(szDuration), "%T", "PERMANENT", iAdmin); + FormatEx(SZF(szExpires), "%T", "NEVER", iAdmin); + } + UTIL_Reply(iAdmin, "%t", "ADMIN_VIP_ADD_SUCCESS", szTargetInfo, szGroup); + } + + if (iDuration) + { + UTIL_GetTimeFromStamp(SZF(szExpires), iDuration, LANG_SERVER); + } + else + { + FormatEx(SZF(szExpires), "%T", "PERMANENT", LANG_SERVER); + FormatEx(SZF(szExpires), "%T", "NEVER", LANG_SERVER); + } + + LogToFile(g_szLogFile, "%T", "LOG_VIP_ADDED", LANG_SERVER, szTargetInfo, iTargetAccountID, szDuration, szExpires, szGroup, szAdminInfo); +} + +void Clients_RemoveVipPlayer( + int iAdmin = 0, + int iTarget = 0, + int iTargetAccountID = 0, + bool bNotify, + const char[] szByWho = NULL_STRING +) +{ + DBG_CLIENTS("Clients_RemoveVipPlayer %d: - > iTargetAccountID: %d, : bNotify: %b", iTarget, iTargetAccountID, bNotify) + + if (iTarget) { + iTargetAccountID = GetSteamAccountID(iTarget); + } + + char szAdminInfo[PMP]; + switch(iAdmin) + { + case REASON_EXPIRED: + { + FormatEx(SZF(szAdminInfo), "%T", "REASON_EXPIRED", LANG_SERVER); + } + case REASON_OUTDATED: + { + FormatEx(SZF(szAdminInfo), "%T", "REASON_INACTIVITY", LANG_SERVER); + } + case OWNER_PLUGIN: + { + FormatEx(SZF(szAdminInfo), "%T %s", "BY_PLUGIN", LANG_SERVER, szByWho); + } + case OWNER_SERVER: + { + FormatEx(SZF(szAdminInfo), "%T", "BY_SERVER", LANG_SERVER); + } + default: + { + char szAdmin[128]; + UTIL_GetClientInfo(iAdmin, SZF(szAdmin)); + FormatEx(SZF(szAdminInfo), "%T %s", "BY_ADMIN", LANG_SERVER, szAdmin); + } + } + + DB_RemoveVipPlayer( + iAdmin, + szAdminInfo, + iTarget, + iTargetAccountID, + bNotify + ); +} + +void Clients_OnVipPlayerRemoved( + const int iAdmin, + const char[] szAdminInfo, + const int iTarget, + const int iTargetAccountID, + const char[] szTargetInfo, + const char[] szGroup, + const bool bNotify +) +{ + LogToFile(g_szLogFile, "%T", "LOG_VIP_DELETED", LANG_SERVER, szTargetInfo, iTargetAccountID, szGroup, szAdminInfo); + + DebugMessage("Clients_OnVipPlayerRemoved(iAdmin: %d, szAdminInfo: %s, iTarget: %d, iTargetAccountID: %d, szTargetInfo: %s", iAdmin, szAdminInfo, iTarget, iTargetAccountID, szTargetInfo) + + if (iTarget > 0) + { + if (g_iClientInfo[iTarget] & IS_MENU_OPEN) + { + CancelClientMenu(iTarget); + } + + if (g_hFeatureStatus[iTarget]) + { + Features_TurnOffAll(iTarget); + } + Clients_ResetClient(iTarget); + SET_BIT(g_iClientInfo[iTarget], IS_LOADED); + + // TODO: Fix this + CallForward_OnVIPClientRemoved(iTarget, "Expired"); + + DisplayClientInfo(iTarget, "expired_info"); + + if (bNotify) + { + // TODO: notify player + } + } + + if (iAdmin > 0) + { + UTIL_Reply(iAdmin, "%t", "ADMIN_VIP_PLAYER_DELETED", szTargetInfo, szGroup); + } +} + +void Clients_ReloadVipPlayers(int iClient, bool bNotify) +{ + for (int i = 1; i <= MaxClients; ++i) + { + if (IsClientInGame(i)) + { + Clients_CheckVipAccess(i, false, true); + } + } + + if (bNotify) + { + UTIL_Reply(iClient, "%t", "VIP_CACHE_REFRESHED"); + } +} diff --git a/addons/sourcemod/scripting/vip/Colors.sp b/addons/sourcemod/scripting/vip/Colors.sp index a0e0706..5e596e6 100644 --- a/addons/sourcemod/scripting/vip/Colors.sp +++ b/addons/sourcemod/scripting/vip/Colors.sp @@ -142,16 +142,16 @@ void Colors_RemoveColors(char[] szBuffer) for(; i < iLen; ++i) { - if(bIgnore) + if (bIgnore) { - if(szTemp[i] == '}') + if (szTemp[i] == '}') { bIgnore = false; } continue; } - if(szTemp[i] == '{') + if (szTemp[i] == '{') { bIgnore = true; continue; diff --git a/addons/sourcemod/scripting/vip/Configs.sp b/addons/sourcemod/scripting/vip/Configs.sp index 74a9fb9..204756a 100644 --- a/addons/sourcemod/scripting/vip/Configs.sp +++ b/addons/sourcemod/scripting/vip/Configs.sp @@ -2,8 +2,14 @@ public void OnMapStart() { LoadSounds(); ReadDownloads(); +} + +public void OnConfigsExecuted() +{ + DebugMessage("OnConfigsExecuted: %x", g_hDatabase) + CMD_Register(); - if (g_hDatabase && (g_CVAR_iDeleteExpired != -1 || g_CVAR_iOutdatedExpired != -1)) + if (g_hDatabase && (GLOBAL_INFO & IS_STARTED)) { RemoveExpAndOutPlayers(); } @@ -16,7 +22,7 @@ void OnReadyToStart() { GLOBAL_INFO |= IS_STARTED; - CreateForward_OnVIPLoaded(); + CallForward_OnVIPLoaded(); for (int iClient = 1; iClient <= MaxClients; ++iClient) { @@ -71,6 +77,8 @@ void ReadConfigs() g_hGroups = CreateConfig("data/vip/cfg/groups.ini", "VIP_GROUPS"); g_hInfo = CreateConfig("data/vip/cfg/info.ini", "VIP_INFO"); + + CallForward_OnConfigsLoaded(); } KeyValues CreateConfig(const char[] szFile, const char[] szKvName) diff --git a/addons/sourcemod/scripting/vip/Cvars.sp b/addons/sourcemod/scripting/vip/Cvars.sp index c5c6b92..9e49ee1 100644 --- a/addons/sourcemod/scripting/vip/Cvars.sp +++ b/addons/sourcemod/scripting/vip/Cvars.sp @@ -1,7 +1,7 @@ void Cvars_Setup() { - CreateConVar("sm_vip_core_version", VIP_VERSION, "VIP-CORE VERSION", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_CHEAT|FCVAR_DONTRECORD); + CreateConVar("sm_vip_core_version", VIP_CORE_VERSION, "VIP-CORE VERSION", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_CHEAT|FCVAR_DONTRECORD); ConVar hCvar = CreateConVar("sm_vip_admin_flag", "z", "Флаг админа, необходимый чтобы иметь доступ к управлению VIP-игроками."); hCvar.AddChangeHook(OnAdminFlagChange); @@ -9,9 +9,15 @@ void Cvars_Setup() g_CVAR_hVIPMenu_CMD = CreateConVar("sm_vip_menu_commands", "vip;sm_vip;sm_vipmenu", "Команды для вызова VIP-меню (разделять ;)"); - hCvar = CreateConVar("sm_vip_server_id", "0", "ID сервера при приспользовании MySQL базы данных", _, true, 0.0); + hCvar = CreateConVar("sm_vip_server_id", "0", "ID сервера при использовании MySQL базы данных", _, true, 0.0); hCvar.AddChangeHook(OnServerIDChange); - OnServerIDChange(hCvar, NULL_STRING, NULL_STRING); + g_CVAR_iServerID = hCvar.IntValue; + SetupServerID(); + + hCvar = CreateConVar("sm_vip_storage_id", "0", "ID группы серверов для хранилища данных игроков", _, true, 0.0); + hCvar.AddChangeHook(OnStorageIDChange); + g_CVAR_iStorageID = hCvar.IntValue; + SetupStorageID(); hCvar = CreateConVar("sm_vip_auto_open_menu", "0", "Автоматически открывать VIP-меню при входе (0 - Выключено, 1 - Включено)", _, true, 0.0, true, 1.0); hCvar.AddChangeHook(OnAutoOpenMenuChange); @@ -21,11 +27,11 @@ void Cvars_Setup() hCvar.AddChangeHook(OnTimeModeChange); g_CVAR_iTimeMode = hCvar.IntValue; - hCvar = CreateConVar("sm_vip_delete_expired", "1", "Удалять VIP-игроков у которых истек срок (-1 - Не удалять, 0 - Удалять сразу, > 0 - Количество дней, по истечению которых удалять)", _, true, -1.0, true, 365.0); + hCvar = CreateConVar("sm_vip_delete_expired", "1", "Удалять VIP-игроков у которых истек срок и они не заходили на сервер (-1 - Не удалять, 0 - Удалять сразу, > 0 - Количество дней, по истечению которых удалять)", _, true, -1.0, true, 365.0); hCvar.AddChangeHook(OnDeleteExpiredChange); g_CVAR_iDeleteExpired = hCvar.IntValue; - hCvar = CreateConVar("sm_vip_delete_outdated", "-1", "Удалять VIP-игроков которые не заходили на сервер X дней (-1 - Не удалять, > 0 - Количество дней, по истечению которых удалять (минимум 3 суток))", _, true, -1.0, true, 365.0); + hCvar = CreateConVar("sm_vip_delete_outdated", "-1", "Удалять VIP-игроков которые не заходили на сервер X дней (-1 или 0 - Не удалять, > 0 - Количество дней, по истечению которых удалять (минимум 3 суток))", _, true, -1.0, true, 365.0); hCvar.AddChangeHook(OnDeleteOutdatedChange); g_CVAR_iOutdatedExpired = hCvar.IntValue; @@ -44,10 +50,6 @@ void Cvars_Setup() hCvar = CreateConVar("sm_vip_features_default_status", "1", "Статус функций по-умолчанию (0 - Выключены, 1 - Включены)", _, true, 0.0, true, 1.0); hCvar.AddChangeHook(OnDefaultStatusChange); g_CVAR_bDefaultStatus = hCvar.BoolValue; - - hCvar = CreateConVar("sm_vip_logs_enable", "1", "Вести ли лог logs/VIP_Logs.log (0 - Выключено, 1 - Включено)", _, true, 0.0, true, 1.0); - hCvar.AddChangeHook(OnLogsEnableChange); - g_CVAR_bLogsEnable = hCvar.BoolValue; AutoExecConfig(true, "VIP_Core", "vip"); } @@ -69,17 +71,40 @@ public void OnAdminFlagChange(ConVar hCvar, const char[] szOldValue, const char[ public void OnServerIDChange(ConVar hCvar, const char[] szOldValue, const char[] szNewValue) { g_CVAR_iServerID = hCvar.IntValue; - if (GLOBAL_INFO & IS_MySQL) + SetupServerID(); +} + +void SetupServerID() +{ + if (DB_IsMysql()) { #if USE_MORE_SERVERS 1 - FormatEx(SZF(g_szSID), " AND (`sid` = %d OR `sid` = 0)", g_CVAR_iServerID); + FormatEx(SZF(g_szServerID), " AND (`sid` = %d OR `sid` = 0)", g_CVAR_iServerID); #else - FormatEx(SZF(g_szSID), " AND `sid` = %d", g_CVAR_iServerID); + FormatEx(SZF(g_szServerID), " AND `sid` = %d", g_CVAR_iServerID); #endif } else { - g_szSID[0] = 0; + g_szServerID[0] = 0; + } +} + +public void OnStorageIDChange(ConVar hCvar, const char[] szOldValue, const char[] szNewValue) +{ + g_CVAR_iStorageID = hCvar.IntValue; + SetupStorageID(); +} + +void SetupStorageID() +{ + if (DB_IsMysql()) + { + FormatEx(SZF(g_szStorageID), " AND `sid` = %d", g_CVAR_iStorageID); + } + else + { + g_szStorageID[0] = 0; } } @@ -96,25 +121,24 @@ public void OnTimeModeChange(ConVar hCvar, const char[] szOldValue, const char[] public void OnDeleteExpiredChange(ConVar hCvar, const char[] szOldValue, const char[] szNewValue) { g_CVAR_iDeleteExpired = hCvar.IntValue; - if(g_CVAR_iDeleteExpired < -1) + if (g_CVAR_iDeleteExpired < -1) { g_CVAR_iDeleteExpired = -1; - return; } } public void OnDeleteOutdatedChange(ConVar hCvar, const char[] szOldValue, const char[] szNewValue) { g_CVAR_iOutdatedExpired = hCvar.IntValue; - if(g_CVAR_iOutdatedExpired != -1) + if (g_CVAR_iOutdatedExpired != -1) { - if(g_CVAR_iOutdatedExpired < 1) + if (g_CVAR_iOutdatedExpired < 1) { g_CVAR_iOutdatedExpired = -1; return; } - if(g_CVAR_iOutdatedExpired < 3) + if (g_CVAR_iOutdatedExpired < 3) { g_CVAR_iOutdatedExpired = 3; } @@ -140,8 +164,3 @@ public void OnDefaultStatusChange(ConVar hCvar, const char[] szOldValue, const c { g_CVAR_bDefaultStatus = hCvar.BoolValue; } - -public void OnLogsEnableChange(ConVar hCvar, const char[] szOldValue, const char[] szNewValue) -{ - g_CVAR_bLogsEnable = hCvar.BoolValue; -} diff --git a/addons/sourcemod/scripting/vip/Database.sp b/addons/sourcemod/scripting/vip/Database.sp index 344a092..14695a6 100644 --- a/addons/sourcemod/scripting/vip/Database.sp +++ b/addons/sourcemod/scripting/vip/Database.sp @@ -1,391 +1,718 @@ -#define CHARSET "utf8mb4" -#define COLLATION "utf8mb4_unicode_ci" - -//#define CHARSET "utf8" -//#define COLLATION "utf8_unicode_ci" - -void DB_OnPluginStart() -{ - DB_Connect(); -} - -void DB_Connect() -{ - // DebugMessage("DB_Connect: %b", g_bIsVIPLoaded) - DebugMessage("DB_Connect") - - if (GLOBAL_INFO & IS_LOADING) - { - return; - } - - if (g_hDatabase != null) - { - UNSET_BIT(GLOBAL_INFO, IS_LOADING); - return; - } - - SET_BIT(GLOBAL_INFO, IS_LOADING); - - if (SQL_CheckConfig("vip_core")) // "vip_core" - { - Database.Connect(OnDBConnect, "vip_core", 0); - } - else - { - char szError[256]; - g_hDatabase = SQLite_UseDatabase("vip_core", SZF(szError)); - OnDBConnect(g_hDatabase, szError, 1); - } -} - -public void OnDBConnect(Database hDatabase, const char[] szError, any data) -{ - if (hDatabase == null || szError[0]) - { - SetFailState("OnDBConnect %s", szError); - UNSET_BIT(GLOBAL_INFO, IS_MySQL); - // CreateTimer(5.0, Timer_DB_Reconnect); - return; - } - - g_hDatabase = hDatabase; - - if (data == 1) - { - UNSET_BIT(GLOBAL_INFO, IS_MySQL); - } - else - { - char szDriver[8]; - g_hDatabase.Driver.GetIdentifier(SZF(szDriver)); - - if (strcmp(szDriver, "mysql", false) == 0) - { - SET_BIT(GLOBAL_INFO, IS_MySQL); - } - else - { - UNSET_BIT(GLOBAL_INFO, IS_MySQL); - } - } - - DebugMessage("OnDBConnect %x, %u - > (MySQL: %b)", g_hDatabase, g_hDatabase, GLOBAL_INFO & IS_MySQL) - - CreateTables(); -} - -void CreateTables() -{ - DebugMessage("CreateTables") - - if (GLOBAL_INFO & IS_MySQL) - { - #if USE_MORE_SERVERS 1 - FormatEx(SZF(g_szSID), " AND (`sid` = %d OR `sid` = 0)", g_CVAR_iServerID); - #else - FormatEx(SZF(g_szSID), " AND `sid` = %d", g_CVAR_iServerID); - #endif - g_hDatabase.Query(SQL_Callback_TableCreate, "CREATE TABLE IF NOT EXISTS `vip_users` (\ - `account_id` INT NOT NULL, \ - `name` VARCHAR(64) NOT NULL default 'unknown' COLLATE '" ... COLLATION ... "', \ - `lastvisit` INT UNSIGNED NOT NULL default 0, \ - `sid` INT UNSIGNED NOT NULL, \ - `group` VARCHAR(64) NOT NULL, \ - `expires` INT UNSIGNED NOT NULL default 0, \ - CONSTRAINT pk_PlayerID PRIMARY KEY (`account_id`, `sid`) \ - ) DEFAULT CHARSET=" ... CHARSET ... ";"); - } - else - { - g_szSID[0] = 0; - g_hDatabase.Query(SQL_Callback_TableCreate, "CREATE TABLE IF NOT EXISTS `vip_users` (\ - `account_id` INTEGER PRIMARY KEY NOT NULL, \ - `name` VARCHAR(64) NOT NULL default 'unknown', \ - `lastvisit` INTEGER UNSIGNED NOT NULL default 0, \ - `group` VARCHAR(64) NOT NULL, \ - `expires` INTEGER UNSIGNED NOT NULL default 0);"); - } -} - -public void SQL_Callback_TableCreate(Database hOwner, DBResultSet hResult, const char[] szError, any data) -{ - DBG_SQL_Response("SQL_Callback_TableCreate") - - if (szError[0]) - { - SetFailState("SQL_Callback_TableCreate: %s", szError); - return; - } - - if (GLOBAL_INFO & IS_MySQL) - { - g_hDatabase.Query(SQL_Callback_ErrorCheck, "SET NAMES '" ... CHARSET ... "'"); - g_hDatabase.Query(SQL_Callback_ErrorCheck, "SET CHARSET '" ... CHARSET ... "'"); - - g_hDatabase.SetCharset(CHARSET); - } - - UNSET_BIT(GLOBAL_INFO, IS_LOADING); - - OnReadyToStart(); - - UTIL_ReloadVIPPlayers(0, false); - - if (g_CVAR_iDeleteExpired != -1 || g_CVAR_iOutdatedExpired != -1) - { - RemoveExpAndOutPlayers(); - } -} - -void RemoveExpAndOutPlayers() -{ - if (g_CVAR_iDeleteExpired >= 0) - { - char szQuery[256]; - FormatEx(SZF(szQuery), "SELECT `account_id`, `name`, `group` FROM `vip_users` WHERE `expires` > 0 AND `expires` < %d%s;", GetTime() - (g_CVAR_iDeleteExpired == 0 ? 1:g_CVAR_iDeleteExpired)*86400, g_szSID); - - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_SelectExpiredAndOutdated, szQuery, REASON_EXPIRED); - } - - if (g_CVAR_iOutdatedExpired > 0) - { - char szQuery[256]; - FormatEx(SZF(szQuery), "SELECT `account_id`, `name`, `group` FROM `vip_users` WHERE `lastvisit` > 0 AND `lastvisit` < %d%s;", (GetTime() - g_CVAR_iOutdatedExpired*86400), g_szSID); - - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_SelectExpiredAndOutdated, szQuery, REASON_OUTDATED); - } -} - -public void SQL_Callback_ErrorCheck(Database hOwner, DBResultSet hResult, const char[] szError, any data) -{ - DBG_SQL_Response("SQL_Callback_ErrorCheck") - - if (szError[0]) - { - LogError("SQL_Callback_ErrorCheck: %s", szError); - } -} - -void DB_UpdateClient(int iClient, const char[] szDbName = NULL_STRING) -{ - int iClientID; - g_hFeatures[iClient].GetValue(KEY_CID, iClientID); - - char szQuery[256]; - - if (g_CVAR_bUpdateName || !strcmp(szDbName, "unknown")) - { - char szName[MNL*2+1]; - GetClientName(iClient, szQuery, MNL); - g_hDatabase.Escape(szQuery, SZF(szName)); - FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `name` = '%s', `lastvisit` = %d WHERE `account_id` = %d%s;", szName, GetTime(), iClientID, g_szSID); - } - else - { - FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `lastvisit` = %d WHERE `account_id` = %d%s;", GetTime(), iClientID, g_szSID); - } - - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_ErrorCheck, szQuery); -} - -void DB_RemoveClientFromID(int iAdmin = 0, - int iClient = 0, - int iClientID = 0, - bool bNotify, - const char[] szSourceName = NULL_STRING, - const char[] szSourceGroup = NULL_STRING, - const char[] szByWho = NULL_STRING) -{ - DebugMessage("DB_RemoveClientFromID %N (%d): - > iClientID: %d, : bNotify: %b", iClient, iClient, iClientID, bNotify) - char szQuery[256], szName[MNL], szGroup[64]; - DataPack hDataPack = new DataPack(); - - if(iClient) - { - if(szSourceName[0]) - { - strcopy(SZF(szName), szSourceName); - } - else - { - GetClientName(iClient, SZF(szName)); - } - - if(szSourceGroup[0]) - { - strcopy(SZF(szGroup), szSourceGroup); - } - else if(g_hFeatures[iClient]) - { - g_hFeatures[iClient].GetString(KEY_GROUP, SZF(szGroup)); - - if(!iClientID) - { - g_hFeatures[iClient].GetValue(KEY_CID, iClientID); - } - } - } - hDataPack.WriteCell(iClientID); - hDataPack.WriteCell(GET_UID(iAdmin)); - hDataPack.WriteCell(bNotify); - - char szAdmin[PMP]; - switch(iAdmin) - { - case REASON_EXPIRED: - { - FormatEx(SZF(szAdmin), "%T", "REASON_EXPIRED", LANG_SERVER); - } - case REASON_OUTDATED: - { - FormatEx(SZF(szAdmin), "%T", "REASON_INACTIVITY", LANG_SERVER); - } - case REASON_PLUGIN: - { - FormatEx(SZF(szAdmin), "%T %s", "BY_PLUGIN", LANG_SERVER, szByWho); - } - case 0: - { - FormatEx(SZF(szAdmin), "%T", "BY_SERVER", LANG_SERVER); - } - default: - { - char szAdminInfo[128]; - UTIL_GetClientInfo(iAdmin, SZF(szAdminInfo)); - FormatEx(SZF(szAdmin), "%T %s", "BY_ADMIN", LANG_SERVER, szAdminInfo); - } - } - - hDataPack.WriteString(szAdmin); - - if(szName[0] && szGroup[0]) - { - hDataPack.WriteString(szName); - hDataPack.WriteString(szGroup); - DB_RemoveClient(hDataPack, iClientID); - return; - } - - FormatEx(SZF(szQuery), "SELECT `name`, `group` FROM `vip_users` WHERE `account_id` = %d%s;", iClientID, g_szSID); - - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_SelectRemoveClient, szQuery, hDataPack); -} - -public void SQL_Callback_SelectRemoveClient(Database hOwner, DBResultSet hResult, const char[] szError, DataPack hPack) -{ - DBG_SQL_Response("SQL_Callback_SelectRemoveClient") - - if (szError[0]) - { - delete hPack; - LogError("SQL_Callback_SelectRemoveClient: %s", szError); - return; - } - - if (hResult.FetchRow()) - { - DBG_SQL_Response("hResult.FetchRow()") - hPack.Reset(); - int iClientID = hPack.ReadCell(); - hPack.ReadCell(); - hPack.ReadCell(); - char szName[MAX_NAME_LENGTH*2+1]; - hPack.ReadString(SZF(szName)); - hResult.FetchString(0, SZF(szName)); - DBG_SQL_Response("hResult.FetchString(0) = '%s", szName) - hPack.WriteString(szName); - hResult.FetchString(1, SZF(szName)); - DBG_SQL_Response("hResult.FetchString(1) = '%s", szName) - hPack.WriteString(szName); - - DB_RemoveClient(hPack, iClientID); - } -} - -void DB_RemoveClient(DataPack hDataPack, int iClientID) -{ - char szQuery[256]; - FormatEx(SZF(szQuery), "DELETE FROM `vip_users` WHERE `account_id` = %d%s;", iClientID, g_szSID); - - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_RemoveClient, szQuery, hDataPack); -} - -public void SQL_Callback_RemoveClient(Database hOwner, DBResultSet hResult, const char[] szError, DataPack hPack) -{ - DBG_SQL_Response("SQL_Callback_SelectRemoveClient") - - if (szError[0]) - { - delete hPack; - LogError("SQL_Callback_RemoveClient: %s", szError); - return; - } - - DBG_SQL_Response("hResult.AffectedRows = %d", hResult.AffectedRows) - - if (hResult.AffectedRows) - { - hPack.Reset(); - - int iClientID = hPack.ReadCell(); - int iAdmin = GET_CID(hPack.ReadCell()); - bool bNotify = view_as(hPack.ReadCell()); - char szAdmin[128], szName[MNL], szGroup[64]; - hPack.ReadString(SZF(szAdmin)); - hPack.ReadString(SZF(szName)); - hPack.ReadString(SZF(szGroup)); - - if(iAdmin == -1) - { - return; - } - - if (g_CVAR_bLogsEnable) - { - LogToFile(g_szLogFile, "%T", "LOG_VIP_DELETED", LANG_SERVER, szName, iClientID, szGroup, szAdmin); - } - - if (bNotify && iAdmin > 0) - { - ReplyToCommand(iAdmin, "%t", "ADMIN_VIP_PLAYER_DELETED", szName, iClientID); - } - } - - delete hPack; -} - -public void SQL_Callback_SelectExpiredAndOutdated(Database hOwner, DBResultSet hResult, const char[] szError, int iReason) -{ - DBG_SQL_Response("SQL_Callback_SelectExpiredAndOutdated") - - if (szError[0]) - { - LogError("SQL_Callback_SelectExpiredAndOutdated: %s", szError); - return; - } - - DBG_SQL_Response("hResult.RowCount = %d", hResult.RowCount) - - if (hResult.RowCount) - { - int iClientID; - char szName[MNL*2], szGroup[64]; - while (hResult.FetchRow()) - { - DBG_SQL_Response("hResult.FetchRow()") - iClientID = hResult.FetchInt(0); - DBG_SQL_Response("hResult.FetchInt(0) = %d", iClientID) - hResult.FetchString(1, SZF(szName)); - DBG_SQL_Response("hResult.FetchString(1) = '%s'", szName) - hResult.FetchString(2, SZF(szGroup)); - DBG_SQL_Response("hResult.FetchString(2) = '%s'", szGroup) - DB_RemoveClientFromID(iReason, _, iClientID, false, szName, szGroup); - } - } -} +#define CHARSET "utf8mb4" +#define COLLATION "utf8mb4_unicode_ci" + +//#define CHARSET "utf8" +//#define COLLATION "utf8_unicode_ci" + +void DB_OnPluginStart() +{ + DB_Connect(); +} + +void DB_Connect() +{ + // DebugMessage("DB_Connect: %b", g_bIsVIPLoaded) + DebugMessage("DB_Connect") + + if (GLOBAL_INFO & IS_LOADING) + { + return; + } + + if (g_hDatabase != null) + { + UNSET_BIT(GLOBAL_INFO, IS_LOADING); + return; + } + + SET_BIT(GLOBAL_INFO, IS_LOADING); + + if (SQL_CheckConfig("vip_core")) // "vip_core" + { + Database.Connect(OnDBConnect, "vip_core", 0); + } + else + { + char szError[PMP]; + g_hDatabase = SQLite_UseDatabase("vip_core", SZF(szError)); + OnDBConnect(g_hDatabase, szError, 1); + } +} + +public void OnDBConnect(Database hDatabase, const char[] szError, int data) +{ + if (hDatabase == null || szError[0]) + { + SetFailState("OnDBConnect %s", szError); + UNSET_BIT(GLOBAL_INFO, IS_MySQL); + // CreateTimer(5.0, Timer_DB_Reconnect); + return; + } + + g_hDatabase = hDatabase; + + if (data == 1) + { + UNSET_BIT(GLOBAL_INFO, IS_MySQL); + } + else + { + char szDriver[8]; + g_hDatabase.Driver.GetIdentifier(SZF(szDriver)); + + if (strcmp(szDriver, "mysql", false) == 0) + { + SET_BIT(GLOBAL_INFO, IS_MySQL); + } + else + { + UNSET_BIT(GLOBAL_INFO, IS_MySQL); + } + } + + DebugMessage("OnDBConnect %x, %u - > (MySQL: %b)", g_hDatabase, g_hDatabase, DB_IsMysql()) + + SetupServerID(); + CreateTables(); +} + +void CreateTables() +{ + DebugMessage("CreateTables") + + if (DB_IsMysql()) + { + g_hDatabase.Query(SQL_Callback_TableCreate, "CREATE TABLE IF NOT EXISTS `vip_users` (\ + `account_id` INT NOT NULL, \ + `name` VARCHAR(64) NOT NULL default 'unknown' COLLATE '" ... COLLATION ... "', \ + `lastvisit` INT UNSIGNED NOT NULL default 0, \ + `sid` INT UNSIGNED NOT NULL, \ + `group` VARCHAR(64) NOT NULL, \ + `expires` INT UNSIGNED NOT NULL default 0, \ + CONSTRAINT pk_PlayerID PRIMARY KEY (`account_id`, `sid`) \ + ) DEFAULT CHARSET=" ... CHARSET ... ";"); + + g_hDatabase.Query(SQL_Callback_StorageTableCreate, "CREATE TABLE IF NOT EXISTS `vip_storage` (\ + `account_id` INT NOT NULL, \ + `key` VARCHAR(128) NOT NULL, \ + `value` varchar(256) NOT NULL default '', \ + `sid` INT UNSIGNED NOT NULL, \ + `updated` INT UNSIGNED NOT NULL default 0, \ + PRIMARY KEY (`account_id`, `key`, `sid`) \ + ) DEFAULT CHARSET=" ... CHARSET ... ";"); + } + else + { + g_szServerID[0] = 0; + + g_hDatabase.Query(SQL_Callback_TableCreate, "CREATE TABLE IF NOT EXISTS `vip_users` (\ + `account_id` INTEGER PRIMARY KEY NOT NULL, \ + `name` VARCHAR(64) NOT NULL default 'unknown', \ + `lastvisit` INTEGER UNSIGNED NOT NULL default 0, \ + `group` VARCHAR(64) NOT NULL, \ + `expires` INTEGER UNSIGNED NOT NULL default 0);"); + + g_hDatabase.Query(SQL_Callback_StorageTableCreate, "CREATE TABLE IF NOT EXISTS `vip_storage` (\ + `account_id` INTEGER NOT NULL, \ + `key` VARCHAR(128) NOT NULL, \ + `value` TEXT NOT NULL default '', \ + `updated` INTEGER UNSIGNED NOT NULL default 0, \ + PRIMARY KEY (`account_id`, `key`) \ + );"); + } +} + +public void SQL_Callback_TableCreate(Database hOwner, DBResultSet hResult, const char[] szError, any data) +{ + DBG_SQL_Response("SQL_Callback_TableCreate") + + if (!hResult || szError[0]) + { + SetFailState("SQL_Callback_TableCreate: %s", szError); + return; + } + + if (DB_IsMysql()) + { + g_hDatabase.Query(SQL_Callback_ErrorCheck, "SET NAMES '" ... CHARSET ... "'"); + g_hDatabase.Query(SQL_Callback_ErrorCheck, "SET CHARSET '" ... CHARSET ... "'"); + + g_hDatabase.SetCharset(CHARSET); + } + + UNSET_BIT(GLOBAL_INFO, IS_LOADING); + + OnReadyToStart(); + + Clients_ReloadVipPlayers(0, false); + + RemoveExpAndOutPlayers(); +} + +public void SQL_Callback_StorageTableCreate(Database hOwner, DBResultSet hResult, const char[] szError, any data) +{ + DBG_SQL_Response("SQL_Callback_StorageTableCreate") + + if (!hResult || szError[0]) + { + SetFailState("SQL_Callback_StorageTableCreate: %s", szError); + return; + } +} + +void RemoveExpAndOutPlayers() +{ + DebugMessage("RemoveExpAndOutPlayers") + DebugMessage("g_CVAR_iDeleteExpired: %d", g_CVAR_iDeleteExpired) + DebugMessage("g_CVAR_iOutdatedExpired: %d", g_CVAR_iOutdatedExpired) + + if (g_CVAR_iDeleteExpired >= 0) + { + char szQuery[PMP]; + FormatEx(SZF(szQuery), "SELECT `account_id`, `name`, `group` FROM `vip_users` WHERE `expires` > 0 AND `expires` < %d%s;", (GetTime() - g_CVAR_iDeleteExpired*86400), g_szServerID); + + DBG_SQL_Query(szQuery) + DebugMessage(szQuery) + g_hDatabase.Query(SQL_Callback_SelectExpiredAndOutdated, szQuery, REASON_EXPIRED); + } + + if (g_CVAR_iOutdatedExpired > 0) + { + char szQuery[PMP]; + FormatEx(SZF(szQuery), "SELECT `account_id`, `name`, `group` FROM `vip_users` WHERE `lastvisit` > 0 AND `lastvisit` < %d%s;", (GetTime() - g_CVAR_iOutdatedExpired*86400), g_szServerID); + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_SelectExpiredAndOutdated, szQuery, REASON_OUTDATED); + } +} + +public void SQL_Callback_ErrorCheck(Database hOwner, DBResultSet hResult, const char[] szError, any data) +{ + DBG_SQL_Response("SQL_Callback_ErrorCheck") + + if (szError[0]) + { + LogError("SQL_Callback_ErrorCheck: %s", szError); + } +} + +void DB_UpdateClient(int iClient, const char[] szDbName = NULL_STRING) +{ + int iClientID; + g_hFeatures[iClient].GetValue(KEY_CID, iClientID); + + char szQuery[PMP]; + + if (g_CVAR_bUpdateName || !strcmp(szDbName, "unknown")) + { + char szName[MNL*2+1]; + GetClientName(iClient, szQuery, MNL); + g_hDatabase.Escape(szQuery, SZF(szName)); + FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `name` = '%s', `lastvisit` = %d WHERE `account_id` = %d%s;", szName, GetTime(), iClientID, g_szServerID); + } + else + { + FormatEx(SZF(szQuery), "UPDATE `vip_users` SET `lastvisit` = %d WHERE `account_id` = %d%s;", GetTime(), iClientID, g_szServerID); + } + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_ErrorCheck, szQuery); +} + +public void SQL_Callback_SelectExpiredAndOutdated(Database hOwner, DBResultSet hResult, const char[] szError, int iReason) +{ + DBG_SQL_Response("SQL_Callback_SelectExpiredAndOutdated") + + if (szError[0]) + { + LogError("SQL_Callback_SelectExpiredAndOutdated: %s", szError); + return; + } + + DBG_SQL_Response("hResult.RowCount = %d", hResult.RowCount) + DebugMessage("hResult.RowCount = %d", hResult.RowCount) + + if (hResult.RowCount) + { + int iAccountID; + char szName[MNL*2], szGroup[64], szAdminInfo[128], szTargetInfo[128], szAuth[32]; + FormatEx(SZF(szAdminInfo), "%T", "REASON_EXPIRED", LANG_SERVER); + while (hResult.FetchRow()) + { + DBG_SQL_Response("hResult.FetchRow()") + iAccountID = hResult.FetchInt(0); + hResult.FetchString(1, SZF(szName)); + hResult.FetchString(2, SZF(szGroup)); + + DBG_SQL_Response("hResult.FetchInt(0) = %d", iAccountID) + DBG_SQL_Response("hResult.FetchString(1) = '%s'", szName) + DBG_SQL_Response("hResult.FetchString(2) = '%s'", szGroup) + + UTIL_GetSteamIDFromAccountID(iAccountID, SZF(szAuth)); + FormatEx(SZF(szTargetInfo), "%s (%s, unknown)", szName, szAuth); + + DB_RemoveVipPlayerByData( + REASON_EXPIRED, + szAdminInfo, + 0, + iAccountID, + szTargetInfo, + szGroup, + true + ); + } + } +} + +void DB_AddVipPlayer( + const int iAdmin, + const char[] szAdminInfo, + const int iTarget, + const int iTargetAccountID, + const char[] szTargetInfo, + const int iDuration, + const int iExpires, + const char[] szGroup +) +{ + DataPack hDataPack = new DataPack(); + + // Admin + hDataPack.WriteCell(iAdmin); + hDataPack.WriteString(szAdminInfo); + + // Target + hDataPack.WriteCell(GET_UID(iTarget)); + hDataPack.WriteCell(iTargetAccountID); + hDataPack.WriteString(szTargetInfo); + + // Data + hDataPack.WriteCell(iDuration); + hDataPack.WriteCell(iExpires); + hDataPack.WriteString(szGroup); + + int iLastVisit = iTarget ? GetTime() : 0; + + char szQuery[512], szName[MNL*2+1]; + if (iTarget) + { + GetClientName(iTarget, SZF(szQuery)); + g_hDatabase.Escape(szQuery, SZF(szName)); + } + else + { + strcopy(SZF(szName), "unknown"); + } + + if (DB_IsMysql()) + { + FormatEx(SZF(szQuery), "INSERT INTO `vip_users` (`account_id`, `sid`, `expires`, `group`, `name`, `lastvisit`) VALUES (%d, %d, %d, '%s', '%s', %d) \ + ON DUPLICATE KEY UPDATE `expires` = %d, `group` = '%s';", iTargetAccountID, g_CVAR_iServerID, iExpires, szGroup, szName, iLastVisit, iExpires, szGroup); + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_OnVIPClientAdded, szQuery, hDataPack); + + return; + } + + FormatEx(SZF(szQuery), "INSERT OR REPLACE INTO `vip_users` (`account_id`, `name`, `expires`, `group`, `lastvisit`) VALUES (%d, '%s', %d, '%s', %d);", iTargetAccountID, szName, iExpires, szGroup, iLastVisit); + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_OnVIPClientAdded, szQuery, hDataPack); +} + +public void SQL_Callback_OnVIPClientAdded(Database hOwner, DBResultSet hResult, const char[] szError, DataPack hDataPack) +{ + DBG_SQL_Response("SQL_Callback_OnVIPClientAdded") + hDataPack.Reset(); + + // Admin + int iAdmin = GET_CID(hDataPack.ReadCell()); + + if (hResult == null || szError[0]) + { + delete hDataPack; + + if (iAdmin >= 0) + { + UTIL_Reply(iAdmin, "%t", "ADMIN_VIP_ADD_FAILED"); + } + + LogError("SQL_Callback_OnVIPClientAdded: %s", szError); + return; + } + + DBG_SQL_Response("hResult.AffectedRows = %d", hResult.AffectedRows) + + if (!hResult.AffectedRows) + { + delete hDataPack; + + if (iAdmin >= 0) + { + UTIL_Reply(iAdmin, "%t", "ADMIN_VIP_ADD_FAILED"); + } + return; + } + + int iTarget, iDuration, iExpires, iAccountID; + char szAdminInfo[PMP], szTargetInfo[PMP], szGroup[64]; + + hDataPack.ReadString(SZF(szAdminInfo)); + + // Target + iTarget = GET_CID(hDataPack.ReadCell()); + iAccountID = hDataPack.ReadCell(); + hDataPack.ReadString(SZF(szTargetInfo)); + + // Data + iDuration = hDataPack.ReadCell(); + iExpires = hDataPack.ReadCell(); + hDataPack.ReadString(SZF(szGroup)); + + delete hDataPack; + + Clients_OnVipPlayerAdded( + iAdmin, + szAdminInfo, + iTarget, + iAccountID, + szTargetInfo, + iDuration, + iExpires, + szGroup + ); +} + +void DB_RemoveVipPlayer( + const int iAdmin, + const char[] szAdminInfo, + const int iTarget, + const int iTargetAccountID, + const bool bNotify +) +{ + DataPack hDataPack = new DataPack(); + hDataPack.WriteCell(GET_UID(iAdmin)); + hDataPack.WriteString(szAdminInfo); + hDataPack.WriteCell(GET_UID(iTarget)); + hDataPack.WriteCell(iTargetAccountID); + hDataPack.WriteCell(bNotify); + + char szQuery[512]; + FormatEx(SZF(szQuery), "SELECT `name`, `group` FROM `vip_users` WHERE `account_id` = %d%s;", iTargetAccountID, g_szServerID); + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_SelectForRemoveClient, szQuery, hDataPack); +} + +public void SQL_Callback_SelectForRemoveClient(Database hOwner, DBResultSet hResult, const char[] szError, DataPack hDataPack) +{ + DBG_SQL_Response("SQL_Callback_SelectForRemoveClient") + + if (szError[0]) + { + delete hDataPack; + LogError("SQL_Callback_SelectForRemoveClient: %s", szError); + return; + } + + if (!hResult.FetchRow()) + { + delete hDataPack; + return; + } + + DBG_SQL_Response("hResult.FetchRow()") + hDataPack.Reset(); + + int iAdmin, iTarget, iTargetAccountID; + char szAdminInfo[128], szTargetInfo[128]; + + iAdmin = GET_CID(hDataPack.ReadCell()); + hDataPack.ReadString(SZF(szAdminInfo)); + iTarget = GET_CID(hDataPack.ReadCell()); + iTargetAccountID = hDataPack.ReadCell(); + bool bNotify = hDataPack.ReadCell(); + + char szGroup[64]; + hResult.FetchString(1, SZF(szGroup)); + + if (iTarget > 0) + { + UTIL_GetClientInfo(iTarget, SZF(szTargetInfo)); + } + else + { + char szName[MAX_NAME_LENGTH*2+1], szAuth[32]; + UTIL_GetSteamIDFromAccountID(iTargetAccountID, SZF(szAuth)); + hResult.FetchString(0, SZF(szName)); + FormatEx(SZF(szTargetInfo), "%s (%s, unknown)", szName, szAuth); + } + + DB_RemoveVipPlayerByData( + iAdmin, + szAdminInfo, + iTarget, + iTargetAccountID, + szTargetInfo, + szGroup, + bNotify + ); +} + +void DB_RemoveVipPlayerByData( + const int iAdmin, + const char[] szAdminInfo, + const int iTarget, + const int iTargetAccountID, + const char[] szTargetInfo, + const char[] szGroup, + const bool bNotify +) +{ + DataPack hDataPack = new DataPack(); + + // Admin + hDataPack.WriteCell(GET_UID(iAdmin)); + hDataPack.WriteString(szAdminInfo); + + // Target + hDataPack.WriteCell(GET_UID(iTarget)); + hDataPack.WriteCell(iTargetAccountID); + hDataPack.WriteString(szTargetInfo); + + // Data + hDataPack.WriteString(szGroup); + hDataPack.WriteCell(bNotify); + + char szQuery[PMP]; + FormatEx(SZF(szQuery), "DELETE FROM `vip_users` WHERE `account_id` = %d%s;", iTargetAccountID, g_szServerID); + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_RemoveClient, szQuery, hDataPack); +} + +public void SQL_Callback_RemoveClient(Database hOwner, DBResultSet hResult, const char[] szError, DataPack hDataPack) +{ + DBG_SQL_Response("SQL_Callback_SelectRemoveClient") + + if (szError[0]) + { + delete hDataPack; + LogError("SQL_Callback_RemoveClient: %s", szError); + return; + } + + DBG_SQL_Response("hResult.AffectedRows = %d", hResult.AffectedRows) + + if (!hResult.AffectedRows) + { + delete hDataPack; + return; + } + hDataPack.Reset(); + + + int iAdmin, iTarget, iAccountID; + char szAdminInfo[PMP], szTargetInfo[PMP], szGroup[64]; + + // Admin + iAdmin = GET_CID(hDataPack.ReadCell()); + hDataPack.ReadString(SZF(szAdminInfo)); + + // Target + iTarget = GET_CID(hDataPack.ReadCell()); + iAccountID = hDataPack.ReadCell(); + hDataPack.ReadString(SZF(szTargetInfo)); + + // Data + hDataPack.ReadString(SZF(szGroup)); + bool bNotify = hDataPack.ReadCell(); + + delete hDataPack; + + if (!iTarget) + { + iTarget = UTIL_GetVipClientByAccountID(iAccountID); + } + + Clients_OnVipPlayerRemoved( + iAdmin, + szAdminInfo, + iTarget, + iAccountID, + szTargetInfo, + szGroup, + bNotify + ); +} +/** +void DB_RemoveClientFromID(int iAdmin = 0, + int iClient = 0, + int iClientID = 0, + bool bNotify, + const char[] szSourceName = NULL_STRING, + const char[] szSourceGroup = NULL_STRING, + const char[] szByWho = NULL_STRING) +{ + DebugMessage("DB_RemoveClientFromID %N (%d): - > iClientID: %d, : bNotify: %b", iClient, iClient, iClientID, bNotify) + char szQuery[PMP], szName[MNL], szGroup[64]; + DataPack hDataPack = new DataPack(); + + if (iClient) + { + if (szSourceName[0]) + { + strcopy(SZF(szName), szSourceName); + } + else + { + GetClientName(iClient, SZF(szName)); + } + + if (szSourceGroup[0]) + { + strcopy(SZF(szGroup), szSourceGroup); + } + else if (g_hFeatures[iClient]) + { + g_hFeatures[iClient].GetString(KEY_GROUP, SZF(szGroup)); + + if (!iClientID) + { + g_hFeatures[iClient].GetValue(KEY_CID, iClientID); + } + } + } + hDataPack.WriteCell(iClientID); + hDataPack.WriteCell(GET_UID(iAdmin)); + hDataPack.WriteCell(bNotify); + + char szAdmin[PMP]; + switch(iAdmin) + { + case REASON_EXPIRED: + { + FormatEx(SZF(szAdmin), "%T", "REASON_EXPIRED", LANG_SERVER); + } + case REASON_OUTDATED: + { + FormatEx(SZF(szAdmin), "%T", "REASON_INACTIVITY", LANG_SERVER); + } + case REASON_PLUGIN: + { + FormatEx(SZF(szAdmin), "%T %s", "BY_PLUGIN", LANG_SERVER, szByWho); + } + case 0: + { + FormatEx(SZF(szAdmin), "%T", "BY_SERVER", LANG_SERVER); + } + default: + { + char szAdminInfo[128]; + UTIL_GetClientInfo(iAdmin, SZF(szAdminInfo)); + FormatEx(SZF(szAdmin), "%T %s", "BY_ADMIN", LANG_SERVER, szAdminInfo); + } + } + + hDataPack.WriteString(szAdmin); + + if (szName[0] && szGroup[0]) + { + hDataPack.WriteString(szName); + hDataPack.WriteString(szGroup); + DB_RemoveClient(hDataPack, iClientID); + return; + } + + FormatEx(SZF(szQuery), "SELECT `name`, `group` FROM `vip_users` WHERE `account_id` = %d%s;", iClientID, g_szServerID); + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_SelectRemoveClient, szQuery, hDataPack); +} + +public void SQL_Callback_SelectRemoveClient(Database hOwner, DBResultSet hResult, const char[] szError, DataPack hDataPack) +{ + DBG_SQL_Response("SQL_Callback_SelectRemoveClient") + + if (szError[0]) + { + delete hDataPack; + LogError("SQL_Callback_SelectRemoveClient: %s", szError); + return; + } + + if (!hResult.FetchRow()) + { + delete hDataPack; + return; + } + + DBG_SQL_Response("hResult.FetchRow()") + hDataPack.Reset(); + int iClientID = hDataPack.ReadCell(); + hDataPack.ReadCell(); + hDataPack.ReadCell(); + char szName[MAX_NAME_LENGTH*2+1]; + hDataPack.ReadString(SZF(szName)); + hResult.FetchString(0, SZF(szName)); + DBG_SQL_Response("hResult.FetchString(0) = '%s", szName) + hDataPack.WriteString(szName); + hResult.FetchString(1, SZF(szName)); + DBG_SQL_Response("hResult.FetchString(1) = '%s", szName) + hDataPack.WriteString(szName); + + DB_RemoveClient(hDataPack, iClientID); +} + +void DB_RemoveClient(int iClientID, DataPack hDataPack) +{ + char szQuery[PMP]; + FormatEx(SZF(szQuery), "DELETE FROM `vip_users` WHERE `account_id` = %d%s;", iClientID, g_szServerID); + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_RemoveClient, szQuery, hDataPack); +} + +public void SQL_Callback_RemoveClient(Database hOwner, DBResultSet hResult, const char[] szError, DataPack hDataPack) +{ + DBG_SQL_Response("SQL_Callback_SelectRemoveClient") + + if (szError[0]) + { + delete hDataPack; + LogError("SQL_Callback_RemoveClient: %s", szError); + return; + } + + DBG_SQL_Response("hResult.AffectedRows = %d", hResult.AffectedRows) + + if (!hResult.AffectedRows) + { + delete hDataPack; + return; + } + hDataPack.Reset(); + + int iClientID = hDataPack.ReadCell(); + int iAdmin = GET_CID(hDataPack.ReadCell()); + bool bNotify = view_as(hDataPack.ReadCell()); + char szAdmin[128], szName[MNL], szGroup[64]; + hDataPack.ReadString(SZF(szAdmin)); + hDataPack.ReadString(SZF(szName)); + hDataPack.ReadString(SZF(szGroup)); + delete hDataPack; + + Clients_OnVipPlayerRemoved( + iAdmin, + szAdminInfo, + iTarget, + iTargetAccountID, + szTargetInfo, + szGroup + ); +} +*/ +bool DB_IsMysql() +{ + return (GLOBAL_INFO & IS_MySQL) == IS_MySQL; +} diff --git a/addons/sourcemod/scripting/vip/Debug.sp b/addons/sourcemod/scripting/vip/Debugger.sp similarity index 58% rename from addons/sourcemod/scripting/vip/Debug.sp rename to addons/sourcemod/scripting/vip/Debugger.sp index 433b082..87a87d7 100644 --- a/addons/sourcemod/scripting/vip/Debug.sp +++ b/addons/sourcemod/scripting/vip/Debugger.sp @@ -12,12 +12,13 @@ void DebugMsg(const char[] sMsg, any ...) // Детальность логов // #define LOG_DOWNLOADS // SQL Запросы -#define LOG_QUERIES // SQL Запросы -#define LOG_RESPONSE // Ответы SQL запросов -// #define LOG_API // API -// #define LOG_FEATURES // API -// #define LOG_CLIENTS // API -// #define LOG_DB // API +// #define LOG_QUERIES // SQL Запросы +// #define LOG_RESPONSE // Ответы SQL запросов +// #define LOG_API // API +// #define LOG_FEATURES // API +// #define LOG_CLIENTS // API +// #define LOG_DB // API +// #define LOG_STORAGE #else #define DebugMessage(%0) @@ -46,3 +47,23 @@ void DebugMsg(const char[] sMsg, any ...) #else #define DBG_API(%0) #endif + +#if defined LOG_FEATURES +#define DBG_FEATURES(%0) DebugMsg("FEATURES: " ... %0); +#else +#define DBG_FEATURES(%0) +#endif + + +#if defined LOG_STORAGE +#define DBG_STORAGE(%0) DebugMsg("STORAGE: " ... %0); +#else +#define DBG_STORAGE(%0) +#endif + + +#if defined LOG_CLIENTS +#define DBG_CLIENTS(%0) DebugMsg("STORAGE: " ... %0); +#else +#define DBG_CLIENTS(%0) +#endif diff --git a/addons/sourcemod/scripting/vip/Downloads.sp b/addons/sourcemod/scripting/vip/Downloads.sp index 5fb2890..77e9236 100644 --- a/addons/sourcemod/scripting/vip/Downloads.sp +++ b/addons/sourcemod/scripting/vip/Downloads.sp @@ -6,48 +6,52 @@ void ReadDownloads() BuildPath(Path_SM, SZF(szBuffer), "data/vip/modules/downloadlist.txt"); File hFile = OpenFile(szBuffer, "r"); - if(hFile != null) + if (hFile == null) { - DBG_Download("OpenFile('%s')", szBuffer) - int iEndPos; - while (!hFile.EndOfFile() && hFile.ReadLine(SZF(szBuffer))) + return; + } + DBG_Download("OpenFile('%s')", szBuffer) + int iEndPos; + while (!hFile.EndOfFile() && hFile.ReadLine(SZF(szBuffer))) + { + DBG_Download("ReadLine = '%s'", szBuffer) + if (!szBuffer[0]) { - DBG_Download("ReadLine = '%s'", szBuffer) - if(szBuffer[0]) - { - iEndPos = StrContains(szBuffer, "//"); - DBG_Download("iEndPos = %d", iEndPos) - if(iEndPos != -1) - { - szBuffer[iEndPos] = 0; - } + continue; + } + iEndPos = StrContains(szBuffer, "//"); + DBG_Download("iEndPos = %d", iEndPos) + if (iEndPos != -1) + { + szBuffer[iEndPos] = 0; + } - if(szBuffer[0] && IsCharAlpha(szBuffer[0])) - { - DBG_Download("ReadFileLine: '%s'", szBuffer) - - TrimString(szBuffer); + if (szBuffer[0] && IsCharAlpha(szBuffer[0])) + { + DBG_Download("ReadFileLine: '%s'", szBuffer) + + TrimString(szBuffer); - File_AddToDownloadsTable(szBuffer); - } - } + File_AddToDownloadsTable(szBuffer); } - - delete hFile; } + + delete hFile; } void File_AddToDownloadsTable(const char[] szPath) { DBG_Download("File_AddToDownloadsTable: '%s'", szPath) - if(FileExists(szPath)) + if (FileExists(szPath)) { DBG_Download("File '%s' Loaded", szPath) AddFileToDownloadsTable(szPath); + return; } - else if(DirExists(szPath)) + + if (DirExists(szPath)) { Dir_AddToDownloadsTable(szPath); } @@ -57,22 +61,31 @@ void Dir_AddToDownloadsTable(const char[] szPath) { DBG_Download("Dir_AddToDownloadsTable: '%s'", szPath) - if(DirExists(szPath)) + if (!DirExists(szPath)) { - DirectoryListing hDir = OpenDirectory(szPath); - if(hDir != null) - { - char szDirEntry[PLATFORM_MAX_PATH]; - while (hDir.GetNext(SZF(szDirEntry))) - { - if ((UTIL_StrCmpEx(szDirEntry, ".") || UTIL_StrCmpEx(szDirEntry, "..") || UTIL_StrCmpEx(szDirEntry[strlen(szDirEntry)-4], ".bz2")) == false) - { - Format(SZF(szDirEntry), "%s/%s", szPath, szDirEntry); + return; + } + + DirectoryListing hDir = OpenDirectory(szPath); + if (hDir == null) + { + return; + } - File_AddToDownloadsTable(szDirEntry); - } - } - delete hDir; + char szDirEntry[PMP]; + while (hDir.GetNext(SZF(szDirEntry))) + { + if ( + UTIL_StrCmpEx(szDirEntry, ".") || + UTIL_StrCmpEx(szDirEntry, "..") || + UTIL_StrCmpEx(szDirEntry[strlen(szDirEntry)-4], ".bz2")) + { + continue; } + + Format(SZF(szDirEntry), "%s/%s", szPath, szDirEntry); + File_AddToDownloadsTable(szDirEntry); } -} \ No newline at end of file + + delete hDir; +} diff --git a/addons/sourcemod/scripting/vip/Features.sp b/addons/sourcemod/scripting/vip/Features.sp index d1396ae..26d26d3 100644 --- a/addons/sourcemod/scripting/vip/Features.sp +++ b/addons/sourcemod/scripting/vip/Features.sp @@ -1,90 +1,198 @@ +bool IsValidFeature(const char[] szFeature) +{ + DBG_FEATURES("IsValidFeature:: FindStringInArray -> %i", g_hFeaturesArray.FindString(szFeature)) + return (g_hFeaturesArray.FindString(szFeature) != -1); +} + void Features_TurnOffAll(int iClient) { - DebugMessage("Features_TurnOffAll %N (%i)", iClient, iClient) - int iFeatures = g_hFeaturesArray.Length; - if(iFeatures == 0) + DBG_FEATURES("Features_TurnOffAll %N (%i)", iClient, iClient) + int iFeaturesCount = g_hFeaturesArray.Length; + if (!iFeaturesCount) return; - char szFeature[FEATURE_NAME_LENGTH]; - VIP_ToggleState eOldStatus; - Function Function_Toggle; - ArrayList hArray; - DataPack hDataPack; + char szFeature[FEATURE_NAME_LENGTH]; + VIP_ToggleState eOldStatus; + Function fnToggleCallback; + ArrayList hArray; - for(int i = 0; i < iFeatures; ++i) + for(int i = 0; i < iFeaturesCount; ++i) { g_hFeaturesArray.GetString(i, SZF(szFeature)); - if(!GLOBAL_TRIE.GetValue(szFeature, hArray)) + if (!GetTrieValue(GLOBAL_TRIE, szFeature, hArray)) continue; - if(view_as(hArray.Get(FEATURES_ITEM_TYPE)) != TOGGLABLE) + if (view_as(hArray.Get(FEATURES_ITEM_TYPE)) != TOGGLABLE) + continue; + + fnToggleCallback = Feature_GetSelectCallback(hArray); + if (fnToggleCallback == INVALID_FUNCTION) continue; eOldStatus = Features_GetStatus(iClient, szFeature); - hDataPack = view_as(hArray.Get(FEATURES_MENU_CALLBACKS)); - hDataPack.Position = ITEM_SELECT; - Function_Toggle = hDataPack.ReadFunction(); - if(Function_Toggle != INVALID_FUNCTION) - { - Function_OnItemToggle(view_as(hArray.Get(FEATURES_PLUGIN)), Function_Toggle, iClient, szFeature, eOldStatus, NO_ACCESS); - } + if (eOldStatus == NO_ACCESS) + continue; + + CallForward_OnFeatureToggle(iClient, szFeature, eOldStatus, NO_ACCESS); + Function_OnItemToggle(view_as(hArray.Get(FEATURES_PLUGIN)), fnToggleCallback, iClient, szFeature, eOldStatus, NO_ACCESS); + Features_SetStatus(iClient, szFeature, NO_ACCESS); } } void Features_TurnOnAll(int iClient) { - DebugMessage("Features_TurnOnAll %N (%i)", iClient, iClient) + DBG_FEATURES("Features_TurnOnAll %N (%i)", iClient, iClient) - int iFeatures = g_hFeaturesArray.Length; - if(iFeatures == 0) + int iFeaturesCount = g_hFeaturesArray.Length; + if (!iFeaturesCount) return; - char szFeature[FEATURE_NAME_LENGTH]; - VIP_ToggleState eNewStatus; - Function Function_Toggle; - ArrayList hArray; - DataPack hDataPack; + char szFeature[FEATURE_NAME_LENGTH]; + VIP_ToggleState eStatus, eNewStatus; + Function fnToggleCallback; + ArrayList hArray; - for(int i = 0; i < iFeatures; ++i) + for(int i = 0; i < iFeaturesCount; ++i) { GetArrayString(g_hFeaturesArray, i, SZF(szFeature)); - if(!GLOBAL_TRIE.GetValue(szFeature, hArray)) + if (!GetTrieValue(GLOBAL_TRIE, szFeature, hArray)) + continue; + + if (view_as(hArray.Get(FEATURES_ITEM_TYPE)) != TOGGLABLE) + continue; + + fnToggleCallback = Feature_GetSelectCallback(hArray); + if (fnToggleCallback == INVALID_FUNCTION) continue; - if(view_as(hArray.Get(FEATURES_ITEM_TYPE)) != TOGGLABLE) + eStatus = Features_GetStatus(iClient, szFeature); + if (eStatus == NO_ACCESS) continue; - hDataPack = view_as(hArray.Get(FEATURES_MENU_CALLBACKS)); - hDataPack.Position = ITEM_SELECT; - Function_Toggle = hDataPack.ReadFunction(); - if(Function_Toggle != INVALID_FUNCTION) + eNewStatus = CallForward_OnFeatureToggle(iClient, szFeature, NO_ACCESS, eStatus); + eNewStatus = Function_OnItemToggle(view_as(hArray.Get(FEATURES_PLUGIN)), fnToggleCallback, iClient, szFeature, NO_ACCESS, eStatus); + + if (eNewStatus != eStatus) { - eNewStatus = Features_GetStatus(iClient, szFeature); - if(eNewStatus != NO_ACCESS) - { - Function_OnItemToggle(view_as(hArray.Get(FEATURES_PLUGIN)), Function_Toggle, iClient, szFeature, NO_ACCESS, eNewStatus); - } + Features_SetStatus(iClient, szFeature, eStatus); } } } void Features_SetStatus(int iClient, const char[] szFeature, VIP_ToggleState eStatus) { - DebugMessage("Features_SetStatus: %N (%i) -> Feature: %s, eStatus: %i", iClient, iClient, szFeature, eStatus) + DBG_FEATURES("Features_SetStatus: %N (%i) -> Feature: %s, eStatus: %i", iClient, iClient, szFeature, eStatus) SetTrieValue(g_hFeatureStatus[iClient], szFeature, eStatus); } VIP_ToggleState Features_GetStatus(const int &iClient, const char[] szFeature) { static VIP_ToggleState eStatus; - if(g_hFeatureStatus[iClient].GetValue(szFeature, eStatus)) + if (g_hFeatureStatus[iClient].GetValue(szFeature, eStatus)) { - DebugMessage("Features_GetStatus: %N (%i) -> Feature: %s, eStatus: %i", iClient, iClient, szFeature, eStatus) + DBG_FEATURES("Features_GetStatus: %N (%i) -> Feature: %s, eStatus: %i", iClient, iClient, szFeature, eStatus) return eStatus; } - - DebugMessage("Features_GetStatus: %N (%i) -> Feature: %s, eStatus: %i", iClient, iClient, szFeature, NO_ACCESS) + + DBG_FEATURES("Features_GetStatus: %N (%i) -> Feature: %s, eStatus: %i", iClient, iClient, szFeature, NO_ACCESS) return NO_ACCESS; -} \ No newline at end of file +} + + +#if USE_CLIENTPREFS == 0 +void Features_GetStorageKeyName(const char[] szFeature, char[] szValue, int iMaxLength) +{ + FormatEx(szValue, iMaxLength, "FeatureStatus-%s", szFeature); +} +#endif + +#if USE_CLIENTPREFS 1 +void Features_GetValueFromStorage(int iClient, const char[] szFeature, char[] szValue, int iMaxLength) +{ + DBG_FEATURES("Features_GetValueFromStorage %N (%d): '%s'", iClient, iClient, szFeature) + + ArrayList hArray; + GetTrieValue(GLOBAL_TRIE, szFeature, hArray); + Handle hCookie = view_as(hArray.Get(FEATURES_COOKIE)); + GetClientCookie(iClient, hCookie, szValue, iMaxLength); + +} +#else +void Features_GetValueFromStorage(int iClient, const char[] szFeature, char[] szValue, int iMaxLength) +{ + DBG_FEATURES("Features_GetValueFromStorage %N (%d): '%s'", iClient, iClient, szFeature) + char szKey[128]; + Features_GetStorageKeyName(szFeature, SZF(szKey)); + Storage_GetClientValue(iClient, szKey, szValue, iMaxLength); +} +#endif + +#if USE_CLIENTPREFS 1 +void Features_SetValueToStorage(int iClient, const char[] szFeature, const char[] szValue) +{ + DBG_FEATURES("Features_SetValueToStorage %N (%d): '%s' -> '%s'", iClient, iClient, szFeature, szValue) + + ArrayList hArray; + GetTrieValue(GLOBAL_TRIE, szFeature, hArray); + Handle hCookie = view_as(hArray.Get(FEATURES_COOKIE)); + SetClientCookie(iClient, hCookie, szValue); +} +#else +void Features_SetValueToStorage(int iClient, const char[] szFeature, const char[] szValue) +{ + DBG_FEATURES("Features_SetValueToStorage %L: '%s' -> '%s'", iClient, szFeature, szValue) + char szKey[128]; + Features_GetStorageKeyName(szFeature, SZF(szKey)); + Storage_SetClientValue(iClient, szKey, szValue); +} +#endif + + +void Features_SetStatusToStorage(int iClient, const char[] szFeature, VIP_ToggleState eStatus) +{ + DBG_FEATURES("Features_SetStatusToStorage %L: '%s' -> %d", iClient, szFeature, eStatus) + char szValue[4]; + IntToString(view_as(eStatus), SZF(szValue)); + Features_SetValueToStorage(iClient, szFeature, szValue); +} + +VIP_ToggleState Features_GetStatusFromStorage(int iClient, const char[] szFeature, ArrayList hArray) +{ + char szValue[4]; + Features_GetValueFromStorage(iClient, szFeature, SZF(szValue)); + + DBG_FEATURES("Features_GetStatusFromStorage %L: '%s' -> '%s'", iClient, szFeature, szValue) + VIP_ToggleState eStatus = view_as(StringToInt(szValue)); + if (szValue[0] == '\0' || (view_as(eStatus) > 2 || view_as(eStatus) < 0)) + { + switch(hArray.Get(FEATURES_DEF_STATUS)) + { + case NO_ACCESS: + { + eStatus = g_CVAR_bDefaultStatus ? ENABLED : DISABLED; + } + case ENABLED: + { + eStatus = ENABLED; + } + case DISABLED: + { + eStatus = DISABLED; + } + } + } + DBG_FEATURES("Features_GetStatusFromStorage %L: '%s' -> %d", iClient, szFeature, eStatus) + + return eStatus; +} + + +Function Feature_GetSelectCallback(ArrayList hFeature) +{ + DataPack hDataPack = view_as(hFeature.Get(FEATURES_MENU_CALLBACKS)); + hDataPack.Position = ITEM_SELECT; + return hDataPack.ReadFunction(); +} + diff --git a/addons/sourcemod/scripting/vip/Global.sp b/addons/sourcemod/scripting/vip/Global.sp index 48d3b30..2cbc6b9 100644 --- a/addons/sourcemod/scripting/vip/Global.sp +++ b/addons/sourcemod/scripting/vip/Global.sp @@ -20,20 +20,28 @@ #define SET_BIT(%0,%1) %0 |= %1 #define UNSET_BIT(%0,%1) %0 &= ~%1 -#define IS_VIP (1<<0) // VIP ли игрок -#define IS_LOADED (1<<1) // Загружен ли игрок -#define IS_WAIT_CHAT_PASS (1<<2) // Ожидается ввод пароля в чат -#define IS_WAIT_CHAT_SEARCH (1<<3) // Ожидается ввод значения для поиска в чат -#define IS_SPAWNED (1<<4) // Игрок возродился -#define IS_MENU_OPEN (1<<5) // VIP-меню открыто +#define IS_VIP (1<<0) // VIP ли игрок +#define IS_LOADED (1<<1) // Загружен ли игрок +#define IS_WAIT_CHAT_PASS (1<<2) // Ожидается ввод пароля в чат +#define IS_WAIT_CHAT_SEARCH (1<<3) // Ожидается ввод значения для поиска в чат +#define IS_SPAWNED (1<<4) // Игрок возродился +#define IS_MENU_OPEN (1<<5) // VIP-меню открыто + +#define IS_CLIENT_VIP(%0) (g_iClientInfo[%0] & IS_VIP == IS_VIP) +#define IS_CLIENT_LOADED(%0) (g_iClientInfo[%0] & IS_LOADED == IS_LOADED) #define IS_STARTED (1<<0) #define IS_MySQL (1<<1) #define IS_LOADING (1<<2) -#define REASON_PLUGIN -2 -#define REASON_EXPIRED -3 -#define REASON_OUTDATED -4 +#define REASON_SERVER 0 +#define REASON_PLUGIN -1 +#define REASON_EXPIRED -2 +#define REASON_OUTDATED -3 + + +#define OWNER_SERVER 0 +#define OWNER_PLUGIN -1 stock const char KEY_CID[] = "Core->ClientID"; stock const char KEY_EXPIRES[] = "Core->Expires"; @@ -48,6 +56,8 @@ enum FEATURES_COOKIE, FEATURES_MENU_CALLBACKS, FEATURES_DEF_STATUS + // TODO + // FEATURES_STORAGE_KEY, } DataPackPos ITEM_SELECT = view_as(0); @@ -85,6 +95,7 @@ ArrayList g_hSortArray; StringMap g_hFeatures[MAXPLAYERS+1]; StringMap g_hFeatureStatus[MAXPLAYERS+1]; +StringMap g_hCache[MAXPLAYERS+1]; StringMap g_hClientData[MAXPLAYERS+1]; @@ -97,6 +108,7 @@ ConVar g_CVAR_hVIPMenu_CMD; int g_CVAR_iAdminFlag; int g_CVAR_iServerID; +int g_CVAR_iStorageID; int g_CVAR_iTimeMode; int g_CVAR_iDeleteExpired; int g_CVAR_iOutdatedExpired; @@ -105,8 +117,9 @@ bool g_CVAR_bAutoOpenMenu; bool g_CVAR_bUpdateName; bool g_CVAR_bHideNoAccessItems; bool g_CVAR_bDefaultStatus; -bool g_CVAR_bLogsEnable; EngineVersion g_EngineVersion; +bool g_bIsTranslationPhraseExistsAvailable; -char g_szSID[64]; +char g_szServerID[64]; +char g_szStorageID[64]; diff --git a/addons/sourcemod/scripting/vip/Info.sp b/addons/sourcemod/scripting/vip/Info.sp index b8a083f..f906a45 100644 --- a/addons/sourcemod/scripting/vip/Info.sp +++ b/addons/sourcemod/scripting/vip/Info.sp @@ -30,7 +30,7 @@ void DisplayInfo(int iClient, const char[] szEvent, const char[] szType, char[] { KeyValues hKeyValues = new KeyValues(szType); KvCopySubkeys(g_hInfo, hKeyValues); - switch(CreateForward_OnShowClientInfo(iClient, szEvent, szType, hKeyValues)) + switch(CallForward_OnShowClientInfo(iClient, szEvent, szType, hKeyValues)) { case Plugin_Stop, Plugin_Handled: { @@ -71,7 +71,7 @@ void DisplayInfo(int iClient, const char[] szEvent, const char[] szType, char[] { if (!g_hInfo.GotoFirstSubKey()) { - if(hKeyValues != g_hInfo) + if (hKeyValues != g_hInfo) { delete hKeyValues; } @@ -143,7 +143,7 @@ void DisplayInfo(int iClient, const char[] szEvent, const char[] szType, char[] } } - if(hKeyValues != g_hInfo) + if (hKeyValues != g_hInfo) { delete hKeyValues; } diff --git a/addons/sourcemod/scripting/vip/Storage.sp b/addons/sourcemod/scripting/vip/Storage.sp new file mode 100644 index 0000000..987c990 --- /dev/null +++ b/addons/sourcemod/scripting/vip/Storage.sp @@ -0,0 +1,165 @@ + +#define IS_CLIENT_CACHE_LOADED(%0) g_bIsCacheLoaded[%0] + +static bool g_bIsCacheLoaded[MPL+1]; + +bool Storage_IsClientLoaded(int iClient) +{ + DBG_STORAGE("Storage_IsClientLoaded: %N (%d): %b", iClient, iClient, IS_CLIENT_CACHE_LOADED(iClient) && g_hCache[iClient]) + return IS_CLIENT_CACHE_LOADED(iClient) && g_hCache[iClient]; +} + +void Storage_SetClientValue(int iClient, const char[] szKey, const char[] szValue) +{ + if (Storage_IsClientLoaded(iClient)) + { + g_hCache[iClient].SetString(szKey, szValue); + } +} + +void Storage_GetClientValue(int iClient, const char[] szKey, char[] szValue, int iMaxLength) +{ + if (Storage_IsClientLoaded(iClient)) + { + g_hCache[iClient].GetString(szKey, szValue, iMaxLength); + } +} + +void Storage_LoadClient(int iClient) +{ + char szQuery[PMP]; + + int iAccountID = GetSteamAccountID(iClient); + DBG_STORAGE("Storage_LoadClient: %N (%d): %d", iClient, iClient, iAccountID) + + FormatEx(SZF(szQuery), "SELECT `key`, `value` \ + FROM `vip_storage` \ + WHERE `account_id` = %d%s;", + iAccountID, g_szStorageID); + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_OnClientLoadStorage, szQuery, UID(iClient)); +} + +public void SQL_Callback_OnClientLoadStorage(Database hOwner, DBResultSet hResult, const char[] szError, any iUserID) +{ + DBG_SQL_Response("SQL_Callback_OnClientLoadStorage") + if (hResult == null || szError[0]) + { + LogError("SQL_Callback_OnClientLoadStorage: %s", szError); + return; + } + + int iClient = CID(iUserID); + if (!iClient || !IsClientInGame(iClient)) + { + return; + } + + g_hCache[iClient] = new StringMap(); + + DBG_STORAGE("SQL_Callback_OnClientLoadStorage: %N (%d)", iClient, iClient) + DBG_STORAGE("RowCount: %d", hResult.RowCount) + if (!hResult.RowCount) + { + Storage_OnClientLoaded(iClient); + return; + } + + char szKey[128], szValue[PMP]; + + while (hResult.FetchRow()) + { + hResult.FetchString(0, SZF(szKey)); + hResult.FetchString(1, SZF(szValue)); + DBG_STORAGE("SetString: '%s' -> '%s'", szKey, szValue) + + g_hCache[iClient].SetString(szKey, szValue); + } + + Storage_OnClientLoaded(iClient); +} + +void Storage_OnClientLoaded(int iClient) +{ + DBG_STORAGE("Storage_OnClientLoaded: %N (%d)", iClient, iClient) + g_bIsCacheLoaded[iClient] = true; + + CallForward_OnClientStorageLoaded(iClient); +} + +void Storage_ResetClient(int iClient) +{ + DBG_STORAGE("Storage_ResetClient: %N (%d)", iClient, iClient) + + if (g_hCache[iClient]) + { + delete g_hCache[iClient]; + g_hCache[iClient] = null; + } + + g_bIsCacheLoaded[iClient] = false; +} + +void Storage_SaveClient(int iClient) +{ + DBG_STORAGE("Storage_SaveClient: %N (%d)", iClient, iClient) + + if (!Storage_IsClientLoaded(iClient) || !g_hCache[iClient]) + { + return; + } + int iAccountID = GetSteamAccountID(iClient); + int iUpdated = GetTime(); + + StringMapSnapshot hStorageSnapshot = g_hCache[iClient].Snapshot(); + + char szKey[128], szValue[PMP]; + + for(int i = 0, iSize = hStorageSnapshot.Length; i < iSize; ++i) + { + hStorageSnapshot.GetKey(i, SZF(szKey)); + g_hCache[iClient].GetString(szKey, SZF(szValue)); + DBG_STORAGE("GetString: '%s' -> '%s'", szKey, szValue) + + + Storage_SaveClientValue(iAccountID, szKey, szValue, iUpdated); + } + + delete hStorageSnapshot; +} + +void Storage_SaveClientValue(int iAccountID, const char[] szKey, const char[] szValue, int iUpdated) +{ + char szQuery[512]; + if (DB_IsMysql()) + { + g_hDatabase.Format(SZF(szQuery), "INSERT INTO `vip_storage` (`account_id`, `sid`, `key`, `value`, `updated`) \ + VALUES (%d, %d, \"%s\", \"%s\", %d) \ + ON DUPLICATE KEY UPDATE \ + `value` = \"%s\", `updated` = %d;", + iAccountID, g_CVAR_iStorageID, szKey, szValue, iUpdated, szValue, iUpdated); + } + else + { + g_hDatabase.Format(SZF(szQuery), "INSERT OR REPLACE INTO `vip_storage` (`account_id`, `key`, `value`, `updated`) \ + VALUES (%d, \"%s\", \"%s\", %d);", + iAccountID, szKey, szValue, iUpdated); + } + + DBG_SQL_Query(szQuery) + g_hDatabase.Query(SQL_Callback_OnClientSaveStorage, szQuery); +} + +public void SQL_Callback_OnClientSaveStorage(Database hOwner, DBResultSet hResult, const char[] szError, any iData) +{ + DBG_SQL_Response("SQL_Callback_OnClientSaveStorage") + if (hResult == null || szError[0]) + { + LogError("SQL_Callback_OnClientSaveStorage: %s", szError); + return; + } +} + +// TODO: add clear outdated + diff --git a/addons/sourcemod/scripting/vip/UTIL.sp b/addons/sourcemod/scripting/vip/UTIL.sp index 812b315..28d8c38 100644 --- a/addons/sourcemod/scripting/vip/UTIL.sp +++ b/addons/sourcemod/scripting/vip/UTIL.sp @@ -1,5 +1,4 @@ - int GET_UID(int iClient) { return iClient > 0 ? UID(iClient):iClient; @@ -36,7 +35,7 @@ stock int UTIL_ReplaceChars(char[] szBuffer, int InChar, int OutChar) if (szBuffer[i] == InChar) { szBuffer[i] = OutChar; - iNum++; + ++iNum; } } @@ -48,13 +47,10 @@ bool UTIL_StrCmpEx(const char[] szString1, const char[] szString2) int iLen = strlen(szString1); if (iLen != strlen(szString2)) { - // i don't see any reason, why we should compare strings, - // if length is different because this fact means: they - // different too. return false; } - for (int i = 0; i < iLen; i++) + for (int i = 0; i < iLen; ++i) { if (szString1[i] != szString2[i]) { @@ -226,7 +222,6 @@ void UTIL_GetSteamIDFromAccountID(int iAccountID, char[] szSteamID, int iMaxLen) iAccountID -= iPart; FormatEx(szSteamID, iMaxLen, "STEAM_%d:%d:%d", g_EngineVersion == Engine_CSGO ? 1 : 0, iPart, iAccountID/2); } - } } @@ -234,48 +229,12 @@ void UTIL_GetClientInfo(int iClient, char[] szBuffer, int iMaxLen) { char szName[MNL], szAuth[32], szIP[24]; GetClientName(iClient, SZF(szName)); - GetClientAuthId(iClient, AuthId_Steam2, SZF(szAuth)); + GetClientAuthId(iClient, AuthId_Engine, SZF(szAuth)); GetClientIP(iClient, SZF(szIP)); - FormatEx(szBuffer, iMaxLen, "%s (%s, %s)", szName, szAuth, szIP); + FormatEx(szBuffer, iMaxLen, "%s <%s, %s>", szName, szAuth, szIP); } -void UTIL_ReloadVIPPlayers(int iClient, bool bNotify) -{ - for (int i = 1; i <= MaxClients; ++i) - { - if (IsClientInGame(i)) - { - Clients_CheckVipAccess(i, false, true); - } - } - - if (bNotify) - { - UTIL_Reply(iClient, "%t", "VIP_CACHE_REFRESHED"); - } -} -/* -void UTIL_REM_VIP_PLAYER(int iClient = 0, int iTarget = 0, int iAccID = 0, int iClientID, const char[] szReason) -{ - if (g_CVAR_bLogsEnable) - { - if(iTarget) - { - LogToFile(g_szLogFile, "%T", "REMOVING_PLAYER", LANG_SERVER, iTarget); - } - } - - DB_RemoveClientFromID(iClient, iClientID, false); - - ResetClient(iTarget); - - CreateForward_OnVIPClientRemoved(iTarget, szReason); - - DisplayClientInfo(iTarget, "expired_info"); -} -*/ - void UTIL_Reply(int iClient, const char[] szMsg, any ...) { if(iClient < 0) @@ -297,180 +256,12 @@ void UTIL_Reply(int iClient, const char[] szMsg, any ...) } } -void UTIL_ADD_VIP_PLAYER(int iAdmin = 0, - int iTarget = 0, - int iAccID = 0, - int iDuration, - const char[] szGroup, - const char[] szByWho = NULL_STRING) +int UTIL_GetVipClientByAccountID(int iAccountID) { - char szQuery[PMP*2], szName[MNL*2+1]; - char szAdmin[PMP], szTargetInfo[PMP]; - int iExpires, iAccountID; - - if (iDuration) - { - iExpires = iDuration + GetTime(); - } - else - { - iExpires = iDuration; - } - - if (iTarget) - { - GetClientName(iTarget, SZF(szQuery)); - g_hDatabase.Escape(szQuery, SZF(szName)); - iAccountID = GetSteamAccountID(iTarget); - UTIL_GetClientInfo(iTarget, SZF(szTargetInfo)); - } - else - { - strcopy(SZF(szName), "unknown"); - iAccountID = iAccID; - UTIL_GetSteamIDFromAccountID(iAccountID, SZF(szQuery)); - FormatEx(SZF(szTargetInfo), "unknown (%s, unknown)", szQuery); - } - - DataPack hDataPack = new DataPack(); - - // Admin - - switch(iAdmin) - { - case REASON_PLUGIN: - { - FormatEx(SZF(szAdmin), "%T %s", "BY_PLUGIN", LANG_SERVER, szByWho); - } - case 0: - { - FormatEx(SZF(szAdmin), "%T", "BY_SERVER", LANG_SERVER); - } - default: - { - char szAdminInfo[128]; - UTIL_GetClientInfo(iAdmin, SZF(szAdminInfo)); - FormatEx(SZF(szAdmin), "%T %s", "BY_ADMIN", LANG_SERVER, szAdminInfo); - iAdmin = UID(iAdmin); - } - } - hDataPack.WriteCell(iAdmin); - hDataPack.WriteString(szAdmin); - - // Target - hDataPack.WriteCell(GET_UID(iTarget)); - hDataPack.WriteCell(iAccountID); - hDataPack.WriteString(szTargetInfo); - - // Data - hDataPack.WriteCell(iDuration); - hDataPack.WriteCell(iExpires); - hDataPack.WriteString(szGroup); - - int iLastVisit = iTarget ? GetTime():0; - - if (GLOBAL_INFO & IS_MySQL) + int iClientID; + for (int i = 1; i <= MaxClients; ++i) { - FormatEx(SZF(szQuery), "INSERT INTO `vip_users` (`account_id`, `sid`, `expires`, `group`, `name`, `lastvisit`) VALUES (%d, %d, %d, '%s', '%s', %d) \ - ON DUPLICATE KEY UPDATE `expires` = %d, `group` = '%s';", iAccountID, g_CVAR_iServerID, iExpires, szGroup, szName, iLastVisit, iExpires, szGroup); - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_OnVIPClientAdded, szQuery, hDataPack); - - return; + if (IsClientInGame(i) && g_hFeatures[i] != null && g_hFeatures[i].GetValue(KEY_CID, iClientID) && iClientID == iAccountID) return i; } - - FormatEx(SZF(szQuery), "INSERT OR REPLACE INTO `vip_users` (`account_id`, `name`, `expires`, `group`, `lastvisit`) VALUES (%d, '%s', %d, '%s', %d);", iAccountID, szName, iExpires, szGroup, iLastVisit); - DBG_SQL_Query(szQuery) - g_hDatabase.Query(SQL_Callback_OnVIPClientAdded, szQuery, hDataPack); + return 0; } - -public void SQL_Callback_OnVIPClientAdded(Database hOwner, DBResultSet hResult, const char[] szError, any hPack) -{ - DBG_SQL_Response("SQL_Callback_OnVIPClientAdded") - DataPack hDataPack = view_as(hPack); - hDataPack.Reset(); - - // Admin - int iAdmin = GET_CID(hDataPack.ReadCell()); - - if (hResult == null || szError[0]) - { - delete hDataPack; - - if (iAdmin >= 0) - { - UTIL_Reply(iAdmin, "%t", "ADMIN_VIP_ADD_FAILED"); - } - - LogError("SQL_Callback_OnVIPClientAdded: %s", szError); - return; - } - - DBG_SQL_Response("hResult.AffectedRows = %d", hResult.AffectedRows) - - if (!hResult.AffectedRows) - { - delete hDataPack; - - if (iAdmin >= 0) - { - UTIL_Reply(iAdmin, "%t", "ADMIN_VIP_ADD_FAILED"); - } - return; - } - - int iTarget, iDuration, iExpires, iAccountID; - char szAdmin[PMP], szTargetInfo[PMP], szExpires[64], szDuration[64], szGroup[64]; - - hDataPack.ReadString(SZF(szAdmin)); - - // Target - iTarget = GET_CID(hDataPack.ReadCell()); - iAccountID = hDataPack.ReadCell(); - hDataPack.ReadString(SZF(szTargetInfo)); - - // Data - iDuration = hDataPack.ReadCell(); - iExpires = hDataPack.ReadCell(); - hDataPack.ReadString(SZF(szGroup)); - - delete hDataPack; - - if (iTarget) - { - Clients_CheckVipAccess(iTarget, true); - CreateForward_OnVIPClientAdded(iTarget, iAdmin); - } - - char szAuth[32]; - I2S(iAccountID, szAuth); - - if (iAdmin >= 0) - { - if (iDuration) - { - UTIL_GetTimeFromStamp(SZF(szDuration), iDuration, iAdmin); - FormatTime(SZF(szExpires), "%d/%m/%Y - %H:%M", iExpires); - } - else - { - FormatEx(SZF(szDuration), "%T", "PERMANENT", iAdmin); - FormatEx(SZF(szExpires), "%T", "NEVER", iAdmin); - } - UTIL_Reply(iAdmin, "%t", "ADMIN_VIP_ADD_SUCCESS", szTargetInfo, iAccountID); - } - - if (g_CVAR_bLogsEnable) - { - if (iDuration) - { - UTIL_GetTimeFromStamp(SZF(szExpires), iDuration, LANG_SERVER); - } - else - { - FormatEx(SZF(szExpires), "%T", "PERMANENT", LANG_SERVER); - FormatEx(SZF(szExpires), "%T", "NEVER", LANG_SERVER); - } - LogToFile(g_szLogFile, "%T", "LOG_VIP_ADDED", LANG_SERVER, szTargetInfo, iAccountID, szDuration, szExpires, szGroup, szAdmin); - } -} \ No newline at end of file diff --git a/addons/sourcemod/scripting/vip/VipMenu.sp b/addons/sourcemod/scripting/vip/VipMenu.sp index 0e1efe9..6fc7191 100644 --- a/addons/sourcemod/scripting/vip/VipMenu.sp +++ b/addons/sourcemod/scripting/vip/VipMenu.sp @@ -1,37 +1,34 @@ void VIPMenu_Setup() { g_hVIPMenu = new Menu(Handler_VIPMenu, MenuAction_Start | MenuAction_Display | MenuAction_Cancel | MenuAction_Select | MenuAction_DisplayItem | MenuAction_DrawItem); - - g_hVIPMenu.AddItem("NO_FEATURES", "NO_FEATURES", ITEMDRAW_DISABLED); } void AddFeatureToVIPMenu(const char[] szFeature) { DebugMessage("AddFeatureToVIPMenu: %s", szFeature) - if (g_hSortArray != null) - { - ResortFeaturesArray(); - - g_hVIPMenu.RemoveAllItems(); - - int i, iSize; - char szItemInfo[128]; - ArrayList hArray; - iSize = g_hFeaturesArray.Length; - for (i = 0; i < iSize; ++i) - { - g_hFeaturesArray.GetString(i, SZF(szItemInfo)); - if (GLOBAL_TRIE.GetValue(szItemInfo, hArray) && view_as(hArray.Get(FEATURES_ITEM_TYPE)) != HIDE) - { - DebugMessage("AddMenuItem: %s", szItemInfo) - g_hVIPMenu.AddItem(szItemInfo, szItemInfo); - } - } - } - else + if (g_hSortArray == null) { DebugMessage("AddMenuItem") g_hVIPMenu.AddItem(szFeature, szFeature); + return; + } + + ResortFeaturesArray(); + + g_hVIPMenu.RemoveAllItems(); + + int i, iSize; + char szMenuFeature[FEATURE_NAME_LENGTH]; + ArrayList hArray; + iSize = g_hFeaturesArray.Length; + for (i = 0; i < iSize; ++i) + { + g_hFeaturesArray.GetString(i, SZF(szMenuFeature)); + if (GLOBAL_TRIE.GetValue(szMenuFeature, hArray) && view_as(hArray.Get(FEATURES_ITEM_TYPE)) != HIDE) + { + DebugMessage("AddMenuItem: %s", szMenuFeature) + g_hVIPMenu.AddItem(szMenuFeature, szMenuFeature); + } } } @@ -39,7 +36,7 @@ void ResortFeaturesArray() { DebugMessage("ResortFeaturesArray\n \n ") - if ((g_hFeaturesArray).Length < 2) + if (g_hFeaturesArray.Length < 2) { return; } @@ -53,12 +50,12 @@ void ResortFeaturesArray() #endif*/ x = 0; - char szItemInfo[128]; + char szFeature[128]; for (i = 0; i < iSize; ++i) { - g_hSortArray.GetString(i, SZF(szItemInfo)); - DebugMessage("GetSortArrayString: %s (i: %i, x: %i)", szItemInfo, i, x) - index = g_hFeaturesArray.FindString(szItemInfo); + g_hSortArray.GetString(i, SZF(szFeature)); + DebugMessage("GetSortArrayString: %s (i: %i, x: %i)", szFeature, i, x) + index = g_hFeaturesArray.FindString(szFeature); DebugMessage("FindStringInGlobalArray: index: %i", index) if (index != -1) { @@ -84,46 +81,33 @@ stock void PrintArray(ArrayList &hArray) iSize = hArray.Length; if (iSize) { - char szItemInfo[128]; + char szFeature[128]; for (i = 0; i < iSize; ++i) { - hArray.GetString(i, SZF(szItemInfo)); - DebugMessage("%i: %s", i, szItemInfo) + hArray.GetString(i, SZF(szFeature)); + DebugMessage("%i: %s", i, szFeature) } } } #endif */ + public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOption) { - if(action == MenuAction_Display || + if ((action == MenuAction_Display || action == MenuAction_DisplayItem || action == MenuAction_DrawItem || - action == MenuAction_Select) - { - if(!(g_iClientInfo[iClient] & IS_VIP) || !g_hFeatures[iClient]) + action == MenuAction_Select) && + (!IS_CLIENT_VIP(iClient) || !g_hFeatures[iClient])) { - return 0; - } + return 0; } - static char szItemInfo[FEATURE_NAME_LENGTH]; - ArrayList hBuffer; - Function fCallback; - Handle hPlugin; - /* - switch (action) - { - case MenuAction_Display, MenuAction_DrawItem, MenuAction_DisplayItem, MenuAction_Select: - { - if (!(g_iClientInfo[iClient] & IS_VIP)) - { - (g_hVIPMenu).Cancel(); - DisplayClientInfo(iClient, "expired_info"); - return 0; - } - } - } - */ + + static char szFeature[FEATURE_NAME_LENGTH]; + static ArrayList hBuffer; + static Function fCallback; + static Handle hPlugin; + switch (action) { case MenuAction_Cancel: @@ -137,43 +121,23 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti g_hFeatures[iClient].Remove(KEY_MENUITEM); DebugMessage("MenuAction_Display: Client: %i", iClient) - char szTitle[256]; - int iExp; - if (g_hFeatures[iClient].GetValue(KEY_EXPIRES, iExp) && iExp > 0) + char szTitle[PMP]; + if (FormatMenuTitle(iClient, SZF(szTitle))) { - int iTime = GetTime(); - if (iTime < iExp) - { - char szExpires[64]; - UTIL_GetTimeFromStamp(SZF(szExpires), iExp - iTime, iClient); - FormatEx(SZF(szTitle), "%T\n \n%T: %s\n \n", "VIP_MENU_TITLE", iClient, "EXPIRES_IN", iClient, szExpires); - } - else - { - // FakeClientCommand(iClient, "menuselect 0"); - // DisplayClientInfo(iClient, "expired_info"); - Clients_ExpiredClient(iClient); - return 0; - } + (view_as(iOption)).SetTitle(szTitle); } - else - { - FormatEx(SZF(szTitle), "%T\n \n", "VIP_MENU_TITLE", iClient); - } - - (view_as(iOption)).SetTitle(szTitle); } case MenuAction_DrawItem: { int iStyle; - g_hVIPMenu.GetItem(iOption, SZF(szItemInfo), iStyle); + g_hVIPMenu.GetItem(iOption, SZF(szFeature), iStyle); - DebugMessage("MenuAction_DrawItem: Client: %i, Feature: %s, iStyle: %i", iClient, szItemInfo, iStyle) + DebugMessage("MenuAction_DrawItem: Client: %i, Feature: %s, iStyle: %i", iClient, szFeature, iStyle) - if (GLOBAL_TRIE.GetValue(szItemInfo, hBuffer)) + if (GLOBAL_TRIE.GetValue(szFeature, hBuffer)) { - if (view_as(hBuffer.Get(FEATURES_VALUE_TYPE)) != VIP_NULL && Features_GetStatus(iClient, szItemInfo) == NO_ACCESS) + if (view_as(hBuffer.Get(FEATURES_VALUE_TYPE)) != VIP_NULL && Features_GetStatus(iClient, szFeature) == NO_ACCESS) { iStyle = g_CVAR_bHideNoAccessItems ? ITEMDRAW_RAWLINE:ITEMDRAW_DISABLED; DebugMessage("NO_ACCESS -> iStyle: %i", iStyle) @@ -187,7 +151,7 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti hPlugin = view_as(hBuffer.Get(FEATURES_PLUGIN)); Call_StartFunction(hPlugin, fCallback); Call_PushCell(iClient); - Call_PushString(szItemInfo); + Call_PushString(szFeature); Call_PushCell(iStyle); Call_Finish(iStyle); DebugMessage("Function_Draw -> iStyle: %i", iStyle) @@ -201,13 +165,12 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti case MenuAction_DisplayItem: { - g_hVIPMenu.GetItem(iOption, SZF(szItemInfo)); - - DebugMessage("MenuAction_DisplayItem: Client: %i, Feature: %s", iClient, szItemInfo) + g_hVIPMenu.GetItem(iOption, SZF(szFeature)); - char szDisplay[128]; - - if (GLOBAL_TRIE.GetValue(szItemInfo, hBuffer)) + DebugMessage("MenuAction_DisplayItem: Client: %i, Feature: %s", iClient, szFeature) + + static char szDisplay[128]; + if (GLOBAL_TRIE.GetValue(szFeature, hBuffer)) { DataPack hDataPack = view_as(hBuffer.Get(FEATURES_MENU_CALLBACKS)); hDataPack.Position = ITEM_DISPLAY; @@ -216,11 +179,10 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti { hPlugin = view_as(hBuffer.Get(FEATURES_PLUGIN)); - szDisplay[0] = 0; bool bResult; Call_StartFunction(hPlugin, fCallback); Call_PushCell(iClient); - Call_PushString(szItemInfo); + Call_PushString(szFeature); Call_PushStringEx(SZF(szDisplay), SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); Call_PushCell(sizeof(szDisplay)); Call_Finish(bResult); @@ -232,49 +194,33 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti return RedrawMenuItem(szDisplay); } } - - if (view_as(hBuffer.Get(FEATURES_ITEM_TYPE)) == TOGGLABLE) + + if (IsTranslationPhraseExists(szFeature)) { - if(CanTestFeatures() && GetFeatureStatus(FeatureType_Native, "TranslationPhraseExists") == FeatureStatus_Available) - { - if(!TranslationPhraseExists(szItemInfo)) - { - FormatEx(SZF(szDisplay), "%s [%T]", szItemInfo, g_szToggleStatus[view_as(Features_GetStatus(iClient, szItemInfo))], iClient); - return RedrawMenuItem(szDisplay); - } - } - FormatEx(SZF(szDisplay), "%T [%T]", szItemInfo, iClient, g_szToggleStatus[view_as(Features_GetStatus(iClient, szItemInfo))], iClient); - return RedrawMenuItem(szDisplay); + FormatEx(SZF(szDisplay), "%T", szFeature, iClient); } - - if(CanTestFeatures() && GetFeatureStatus(FeatureType_Native, "TranslationPhraseExists") == FeatureStatus_Available) + else { - if(!TranslationPhraseExists(szItemInfo)) - { - strcopy(SZF(szDisplay), szItemInfo); - return RedrawMenuItem(szDisplay); - } + strcopy(SZF(szDisplay), szFeature); } - FormatEx(SZF(szDisplay), "%T", szItemInfo, iClient); + if (view_as(hBuffer.Get(FEATURES_ITEM_TYPE)) == TOGGLABLE) + { + Format(SZF(szDisplay), "%s [%T]", szDisplay, g_szToggleStatus[view_as(Features_GetStatus(iClient, szFeature))], iClient); + } + return RedrawMenuItem(szDisplay); } - if (strcmp(szItemInfo, "NO_FEATURES") == 0) - { - FormatEx(SZF(szItemInfo), "%T", "NO_FEATURES", iClient); - } - - return RedrawMenuItem(szItemInfo); } case MenuAction_Select: { - g_hVIPMenu.GetItem(iOption, SZF(szItemInfo)); + g_hVIPMenu.GetItem(iOption, SZF(szFeature)); - if (GLOBAL_TRIE.GetValue(szItemInfo, hBuffer)) + if (GLOBAL_TRIE.GetValue(szFeature, hBuffer)) { PlaySound(iClient, ITEM_TOGGLE_SOUND); - DebugMessage("MenuAction_Select: Client: %i, Feature: %s", iClient, szItemInfo) + DebugMessage("MenuAction_Select: Client: %i, Feature: %s", iClient, szFeature) DataPack hDataPack = view_as(hBuffer.Get(FEATURES_MENU_CALLBACKS)); hDataPack.Position = ITEM_SELECT; @@ -282,33 +228,28 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti hPlugin = view_as(hBuffer.Get(FEATURES_PLUGIN)); if (view_as(hBuffer.Get(FEATURES_ITEM_TYPE)) == TOGGLABLE) { - char szBuffer[4]; VIP_ToggleState eOldStatus, eNewStatus; - eOldStatus = Features_GetStatus(iClient, szItemInfo); - eNewStatus = (eOldStatus == ENABLED) ? DISABLED:ENABLED; + eOldStatus = Features_GetStatus(iClient, szFeature); + eNewStatus = (eOldStatus == ENABLED) ? DISABLED : ENABLED; + eNewStatus = CallForward_OnFeatureToggle(iClient, szFeature, eOldStatus, eNewStatus); if (fCallback != INVALID_FUNCTION) { - eNewStatus = Function_OnItemToggle(hPlugin, fCallback, iClient, szItemInfo, eOldStatus, eNewStatus); + eNewStatus = Function_OnItemToggle(hPlugin, fCallback, iClient, szFeature, eOldStatus, eNewStatus); } if (eNewStatus != eOldStatus) { - eNewStatus = CreateForward_OnFeatureToggle(iClient, szItemInfo, eOldStatus, eNewStatus); - if (eNewStatus != eOldStatus) - { - Features_SetStatus(iClient, szItemInfo, eNewStatus); - IntToString(view_as(eNewStatus), SZF(szBuffer)); - SetClientCookie(iClient, view_as(GetArrayCell(hBuffer, FEATURES_COOKIE)), szBuffer); - } + Features_SetStatus(iClient, szFeature, eNewStatus); + Features_SetStatusToStorage(iClient, szFeature, eNewStatus); } hMenu.DisplayAt(iClient, hMenu.Selection, MENU_TIME_FOREVER); return 0; } - + g_hFeatures[iClient].SetValue(KEY_MENUITEM, hMenu.Selection); - if (Function_OnItemSelect(hPlugin, fCallback, iClient, szItemInfo)) + if (Function_OnItemSelect(hPlugin, fCallback, iClient, szFeature)) { hMenu.DisplayAt(iClient, hMenu.Selection, MENU_TIME_FOREVER); } @@ -319,13 +260,30 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti return 0; } -bool IsValidFeature(const char[] szFeature) +bool FormatMenuTitle(int iClient, char[] szTitle, int iMaxLen) { - DebugMessage("IsValidFeature:: FindStringInArray -> %i", g_hFeaturesArray.FindString(szFeature)) - return (g_hFeaturesArray.FindString(szFeature) != -1); + int iExp; + if (g_hFeatures[iClient].GetValue(KEY_EXPIRES, iExp) && iExp > 0) + { + int iTime = GetTime(); + if (iTime > iExp) + { + Clients_ExpiredClient(iClient); + return false; + } + + char szExpires[64]; + UTIL_GetTimeFromStamp(SZF(szExpires), iExp - iTime, iClient); + FormatEx(szTitle, iMaxLen, "%T\n \n%T: %s\n ", "VIP_MENU_TITLE", iClient, "EXPIRES_IN", iClient, szExpires); + return true; + } + + FormatEx(szTitle, iMaxLen, "%T\n ", "VIP_MENU_TITLE", iClient); + + return true; } -bool OnVipMenuFlood(int iClient) +bool IsVipMenuFlood(int iClient) { static float fLastTime[MAXPLAYERS + 1]; if (fLastTime[iClient] > 0.0) @@ -335,4 +293,54 @@ bool OnVipMenuFlood(int iClient) fLastTime[iClient] = fSec; } return false; -} \ No newline at end of file +} + +bool IsTranslationPhraseExists(const char[] szPhrase) +{ + if (g_bIsTranslationPhraseExistsAvailable) + { + return TranslationPhraseExists(szPhrase); + } + + return true; +} + +void DisplayVipMenu(int iClient, int iItem = 0) +{ + bool bResult = g_hVIPMenu.DisplayAt(iClient, iItem, MENU_TIME_FOREVER); + if (!bResult) + { + DisplayEmptyFeaturesMenu(iClient); + } +} + +void DisplayEmptyFeaturesMenu(int iClient) +{ + char szBuffer[PMP]; + if (!FormatMenuTitle(iClient, SZF(szBuffer))) + { + return; + } + + Menu hMenu = new Menu(Handler_EmptyVIPMenu, MenuAction_End); + + hMenu.SetTitle(szBuffer); + + FormatEx(SZF(szBuffer), "%T", "NO_FEATURES", iClient); + hMenu.AddItem(NULL_STRING, szBuffer, ITEMDRAW_DISABLED); + + hMenu.Display(iClient, MENU_TIME_FOREVER); +} + +public int Handler_EmptyVIPMenu(Menu hMenu, MenuAction action, int iClient, int iOption) +{ + switch (action) + { + case MenuAction_End: + { + delete hMenu; + } + } + + return 0; +} diff --git a/addons/sourcemod/scripting/vip/adminmenu/Add.sp b/addons/sourcemod/scripting/vip/adminmenu/Add.sp index cc76324..cbb6a3d 100644 --- a/addons/sourcemod/scripting/vip/adminmenu/Add.sp +++ b/addons/sourcemod/scripting/vip/adminmenu/Add.sp @@ -11,7 +11,7 @@ void ShowAddVIPMenu(int iClient) { if (IsClientInGame(i) && IsFakeClient(i) == false && GetClientName(i, SZF(szName))) { - if (g_iClientInfo[i] & IS_VIP) + if (IS_CLIENT_VIP(i)) { g_hFeatures[i].GetValue(KEY_CID, iClientID); if (iClientID != -1) @@ -43,20 +43,26 @@ public int MenuHandler_AddVip_PlayerList(Menu hMenu, MenuAction action, int iCli case MenuAction_End:delete hMenu; case MenuAction_Cancel: { - if (iItem == MenuCancel_ExitBack) BackToAdminMenu(iClient); + if (iItem == MenuCancel_ExitBack) + { + BackToAdminMenu(iClient); + } } case MenuAction_Select: { char szUserID[16]; hMenu.GetItem(iItem, SZF(szUserID)); int UserID = StringToInt(szUserID); - if (CID(UserID)) + if (!CID(UserID)) { - g_hClientData[iClient].SetValue(DATA_KEY_TargetUID, UserID); - g_hClientData[iClient].SetValue(DATA_KEY_TimeType, TIME_SET); - g_hClientData[iClient].SetValue(DATA_KEY_MenuType, MENU_TYPE_ADD); - ShowTimeMenu(iClient); - } else VIP_PrintToChatClient(iClient, "%t", "PLAYER_NO_LONGER_AVAILABLE"); + VIP_PrintToChatClient(iClient, "%t", "PLAYER_NO_LONGER_AVAILABLE"); + return 0; + } + + g_hClientData[iClient].SetValue(DATA_KEY_TargetUID, UserID); + g_hClientData[iClient].SetValue(DATA_KEY_TimeType, TIME_SET); + g_hClientData[iClient].SetValue(DATA_KEY_MenuType, MENU_TYPE_ADD); + ShowTimeMenu(iClient); } } diff --git a/addons/sourcemod/scripting/vip/adminmenu/Del.sp b/addons/sourcemod/scripting/vip/adminmenu/Del.sp index a14d946..5831a97 100644 --- a/addons/sourcemod/scripting/vip/adminmenu/Del.sp +++ b/addons/sourcemod/scripting/vip/adminmenu/Del.sp @@ -1,4 +1,4 @@ -void ShowDeleteVipPlayerMenu(int iClient) +void ShowConfirmDeleteVipPlayerMenu(int iClient) { char szBuffer[128]; @@ -13,6 +13,8 @@ void ShowDeleteVipPlayerMenu(int iClient) hMenu.AddItem(NULL_STRING, szBuffer); ReductionMenu(hMenu, 4); + + // TODO: add back button hMenu.Display(iClient, MENU_TIME_FOREVER); } @@ -36,32 +38,15 @@ public int MenuHandler_DeleteVipPlayerMenu(Menu hMenu, MenuAction action, int iC g_hClientData[iClient].GetValue(DATA_KEY_TargetID, iTargetID); int iTarget = 0; - if(g_hClientData[iClient].GetValue(DATA_KEY_TargetUID, iTarget)) + if (g_hClientData[iClient].GetValue(DATA_KEY_TargetUID, iTarget)) { iTarget = CID(iTarget); - if (!iTarget && iTargetID != -1) - { - iTarget = IsClientOnline(iTargetID); - } - - if (iTarget) - { - DB_RemoveClientFromID(iClient, iTarget, _, true); - ResetClient(iTarget); - CreateForward_OnVIPClientRemoved(iTarget, "Removed by Admin", iClient); - DisplayClientInfo(iTarget, "expired_info"); - BackToAdminMenu(iClient); - return 0; - } } - - if(iTargetID != -1) + if (iTarget) { - char szGroup[64], szName[MAX_NAME_LENGTH]; - g_hClientData[iClient].GetString(DATA_KEY_Name, SZF(szName)); - g_hClientData[iClient].GetString(DATA_KEY_Group, SZF(szGroup)); - DB_RemoveClientFromID(iClient, _, iTargetID, true, szName, szGroup); + CallForward_OnVIPClientRemoved(iTarget, "Removed by Admin", iClient); } + Clients_RemoveVipPlayer(iClient, iTarget, iTargetID, true); BackToAdminMenu(iClient); } diff --git a/addons/sourcemod/scripting/vip/adminmenu/Edit.sp b/addons/sourcemod/scripting/vip/adminmenu/Edit.sp index c9ca842..9655177 100644 --- a/addons/sourcemod/scripting/vip/adminmenu/Edit.sp +++ b/addons/sourcemod/scripting/vip/adminmenu/Edit.sp @@ -25,7 +25,7 @@ public int MenuHandler_EditTimeMenu(Menu hMenu, MenuAction action, int iClient, case MenuAction_End: delete hMenu; case MenuAction_Cancel: { - if(iItem == MenuCancel_ExitBack) + if (iItem == MenuCancel_ExitBack) { ShowTargetInfoMenu(iClient); } diff --git a/addons/sourcemod/scripting/vip/adminmenu/List.sp b/addons/sourcemod/scripting/vip/adminmenu/List.sp index b1b60a3..8f9200e 100644 --- a/addons/sourcemod/scripting/vip/adminmenu/List.sp +++ b/addons/sourcemod/scripting/vip/adminmenu/List.sp @@ -21,7 +21,7 @@ void ShowVipPlayersListMenu(int iClient) szUserID[0] = 0; for (i = 1; i <= MaxClients; ++i) { - if (IsClientInGame(i) && (g_iClientInfo[i] & IS_VIP) && !IsFakeClient(i) && GetClientName(i, SZF(szName))) + if (IsClientInGame(i) && IS_CLIENT_VIP(i) && !IsFakeClient(i) && GetClientName(i, SZF(szName))) { g_hFeatures[i].GetValue(KEY_CID, iClientID); FormatEx(SZF(szUserID), "u%d", UID(i)); @@ -98,7 +98,7 @@ public int MenuHandler_VipPlayersListMenu(Menu hMenu, MenuAction action, int iCl g_hFeatures[iTarget].GetValue(KEY_CID, UserID); g_hClientData[iClient].SetValue(DATA_KEY_TargetID, UserID); - if(UserID == -1) + if (UserID == -1) { ShowTemporaryTargetInfo(iClient); return 0; @@ -192,17 +192,16 @@ public int MenuHandler_SearchPlayersListMenu(Menu hMenu, MenuAction action, int void ShowVipPlayersFromDBMenu(int iClient, int iOffset = 0) { - LogMessage("ShowVipPlayersFromDBMenu"); g_hClientData[iClient].SetValue(DATA_KEY_MenuListType, MENU_TYPE_DB_LIST); g_hClientData[iClient].SetValue(DATA_KEY_Offset, iOffset); char szQuery[1024], szSearch[64], szWhere[128]; szSearch[0] = 0; szWhere[0] = 0; - if(g_hClientData[iClient].GetString(DATA_KEY_Search, SZF(szSearch)) && szSearch[0]) + if (g_hClientData[iClient].GetString(DATA_KEY_Search, SZF(szSearch)) && szSearch[0]) { int iAccountID = UTIL_GetAccountIDFromSteamID(szSearch); - if(iAccountID) + if (iAccountID) { FormatEx(SZF(szWhere), " AND `account_id` = %d", iAccountID); } @@ -212,18 +211,18 @@ void ShowVipPlayersFromDBMenu(int iClient, int iOffset = 0) } } - if (GLOBAL_INFO & IS_MySQL) + if (DB_IsMysql()) { FormatEx(SZF(szQuery), "SELECT `account_id`, \ `name` \ FROM `vip_users` \ WHERE %s%s \ LIMIT %d, %d;", - g_szSID[5], szWhere, iOffset, LIST_OFFSET); + g_szServerID[5], szWhere, iOffset, LIST_OFFSET); } else { - if(szWhere[0]) + if (szWhere[0]) { FormatEx(SZF(szQuery), "SELECT `account_id`, `name` \ FROM `vip_users` \ @@ -260,7 +259,7 @@ public void SQL_Callback_SelectVipPlayers(Database hOwner, DBResultSet hResult, hMenu.ExitBackButton = true; szSearch[0] = 0; g_hClientData[iClient].GetString(DATA_KEY_Search, SZF(szSearch)); - if(szSearch[0]) + if (szSearch[0]) { hMenu.SetTitle("%T:\n%T:\n ", "MENU_LIST_VIP", iClient, "MENU_SEARCH", iClient, szSearch, hResult.RowCount); } @@ -290,7 +289,7 @@ public void SQL_Callback_SelectVipPlayers(Database hOwner, DBResultSet hResult, DBG_SQL_Response("hResult.FetchInt(0) = %d", iClientID) DBG_SQL_Response("hResult.FetchString(1) = '%s", szName) - if(IsClientOnline(iClientID)) + if (UTIL_GetVipClientByAccountID(iClientID)) { Format(SZF(szName), "• %s", szName); } @@ -319,7 +318,7 @@ void ShowTargetInfo(int iClient) `account_id` \ FROM `vip_users` \ WHERE `account_id` = %d%s LIMIT 1;", - iClientID, g_szSID); + iClientID, g_szServerID); DBG_SQL_Query(szQuery) g_hDatabase.Query(SQL_Callback_SelectVipClientInfo, szQuery, UID(iClient)); @@ -374,7 +373,7 @@ void ShowTemporaryTargetInfo(int iClient) int iTarget; g_hClientData[iClient].GetValue(DATA_KEY_TargetUID, iTarget); iTarget = CID(iTarget); - if(!iTarget) + if (!iTarget) { VIP_PrintToChatClient(iClient, "%t", "PLAYER_NO_LONGER_AVAILABLE"); ShowVipPlayersListMenu(iClient); @@ -425,8 +424,8 @@ void ShowTargetInfoMenu(int iClient) { FormatEx(SZF(szBuffer), "%T", "NEVER", iClient); } - - if(iClientID == -1) + + if (iClientID == -1) { Format(SZF(szBuffer), "%s (%T)", szBuffer, "TEMPORARY", iClient); } @@ -480,7 +479,7 @@ public int MenuHandler_VipClientInfoMenu(Menu hMenu, MenuAction action, int iCli { switch (iItem) { - case 0: ShowDeleteVipPlayerMenu(iClient); + case 0: ShowConfirmDeleteVipPlayerMenu(iClient); case 1: ShowEditTimeMenu(iClient); case 2: { diff --git a/addons/sourcemod/translations/vip_core.phrases.txt b/addons/sourcemod/translations/vip_core.phrases.txt index 63ed0a4..6cc4c27 100644 --- a/addons/sourcemod/translations/vip_core.phrases.txt +++ b/addons/sourcemod/translations/vip_core.phrases.txt @@ -12,13 +12,13 @@ } "VIP_MENU_TITLE" { - "en" "| VIP MENU |" - "fi" "| VIP VALIKKO |" - "de" "| VIP MENÜ |" - "pt" "| MENU VIP |" - "pt_p" "| MENU VIP |" - "ru" "| VIP МЕНЮ |" - "ua" "| VIP МЕНЮ |" + "en" "|VIP MENU|" + "fi" "|VIP VALIKKO|" + "de" "|VIP MENÜ|" + "pt" "|MENU VIP|" + "pt_p" "|MENU VIP|" + "ru" "|VIP МЕНЮ|" + "ua" "|VIP МЕНЮ|" } "EXPIRES_IN" { @@ -35,7 +35,7 @@ { "en" "Disabled" "fi" "Pois Käytöstä" - "de" "Aus" + "de" "Deaktiviert" "pt" "Desativado" "pt_p" "Desativado" "ru" "Выключено" @@ -45,7 +45,7 @@ { "en" "Enabled" "fi" "Käytössä" - "de" "Ein" + "de" "Aktivieren" "pt" "Ativado" "pt_p" "Ativado" "ru" "Включено" @@ -67,7 +67,7 @@ "fi" "Ei saatavilla ominaisuuksia!" "de" "Es gibt keine Privilegien!" "pt" "Sem recursos disponíveis!" - "pt_p" "Sem recursos disponíveis!" + "pt_p" "Sem recursos disponíveis" "ru" "Нет доступных привилегий!" "ua" "Немає доступних функцій!" } @@ -75,7 +75,7 @@ { "en" "You do not have permission to access this command!" "fi" "Sinulla ei ole lupaa käyttää tätä komentoa!" - "de" "Sie haben kein Zugriff auf diesen Command!" + "de" "Sie haben kein Zugriff auf diesen Befehl!" "pt" "Você não tem permissão para acessar este comando!" "pt_p" "Não tens permissão para aceder a este comando!" "ru" "У вас нет прав для доступа к этой команде!" @@ -83,9 +83,9 @@ } "NO_VIP_PLAYERS_ONLINE" { - "en" "No VIP-Players online" + "en" "No VIP-players online" "fi" "Ei ole VIP-Pelaajia onlinessä" - "de" "Da sind keine VIP-Spieler dreizeit online" + "de" "Derzeit sind keine VIP-Spieler online" "pt" "Nenhum VIP Online" "pt_p" "Nenhum VIP Online" "ru" "Нет VIP-игроков онлайн" @@ -96,17 +96,17 @@ "#format" "{1:L}" "en" "{1}'s VIP has expired, removing player..." "fi" "{1} VIP oikeudet ovat päättyneet, poistetaan pelaajaa..." - "de" "VIP des {1} hat abgelaufen, Spieler wird entfernt..." + "de" "VIP von {1} ist abgelaufen, Spieler wird entfernt..." "pt" "VIP do {1} expirou, removendo o player..." - "pt_p" "{1} o teu VIP expirou, a remover jogador..." + "pt_p" "{1} o teu VIP expirou, a remover jogador.." "ru" "Права VIP-игрока {1} истекли, удаление игрока..." "ua" "Термін дії VIP гравця {1} закінчився, видалення гравця..." } "VIP_ADMIN_MENU_TITLE" { - "en" "Manage VIP System" + "en" "Manage VIP system" "fi" "Hallinta VIP" - "de" "VIP Administrieren" + "de" "VIP-System Verwalten" "pt" "Gerenciar Sistema VIP" "pt_p" "Gerir Sistema VIP" "ru" "Администрирование VIP" @@ -114,10 +114,10 @@ } "MENU_ADD_VIP" { - "en" "Add VIP-Player" + "en" "Add VIP-player" "fi" "Lisätä VIP-pelaaja" "de" "VIP-Spieler hinzufügen" - "pt" "Adicionar VIP" + "pt" "Adicionar VIP do Player" "pt_p" "Adicionar VIP" "ru" "Добавить VIP-игрока" "ua" "Додати VIP-гравця" @@ -134,22 +134,25 @@ } "ADMIN_VIP_ADD_SUCCESS" { - "#format" "{1:s},{2:d}" - "en" "VIP-Player {1} ({2}, ID: {3}) successfully added!" - "fi" "VIP-Pelaaja {1} ({2}, ID: {3}) onnistuneesti lisätty!" - "de" "VIP-Spieler {1} ({2}, ID: {3}) erfolgreich hinzugefügt!" - "pt" "VIP adicionado {1} ({2}, ID: {3}) com sucesso!" - "pt_p" "VIP adicionado {1} ({2}, ID: {3}) com sucesso!" - "ru" "{DEFAULT}VIP-Игрок {GREEN}{1} {DEFAULT}(ID: {GREEN}{2}{DEFAULT}) успешно добавлен!" - "ua" "{DEFAULT}VIP-Гравець {GREEN}{1} {DEFAULT}(ID: {GREEN}{2}{DEFAULT}) успішно доданий!" - } + "#format" "{1:s},{2:s}" + "en" "{DEFAULT}VIP-player {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) successfully added!" + "fi" "{DEFAULT}VIP-Pelaaja {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) onnistuneesti lisätty!" + "de" "{DEFAULT}VIP-Spieler {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) erfolgreich hinzugefügt!" + "pt" "{DEFAULT}VIP adicionado {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) com sucesso!" + "pt_p" "{DEFAULT}VIP adicionado {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) com sucesso!" + "ru" "{DEFAULT}VIP-Игрок {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) успешно добавлен!" + "ua" "{DEFAULT}VIP-Гравець {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) успішно доданий!" + } + "LOG_VIP_ADDED" { "#format" "{1:s},{2:d},{3:s},{4:s},{5:s},{6:s}" "en" "VIP-Player {1} (ID: {2}, Duration: {3}, Expires: {4}, Group: {5}) added {6}" - "pt" "Player VIP {1} (ID: {2}, Duração: {3}, Expira: {4}, Grupo: {5}) adicionado {6}" + "de" "VIP-Spieler {1} (ID: {2}, Laufzeit: {3}, Läuft ab: {4}, Gruppe: {5}) Hinzugefügt {6}" + "pt" "VIP do Player {1} (ID: {2}, Duração: {3}, Expira: {4}, Grupo: {5}) adicionado {6}" "ru" "VIP-Игрок {1} (ID: {2}, Длительность: {3}, Истекает: {4}, Группа: {5}) добавлен {6}" } + "ADMIN_VIP_ADD_FAILED" { "en" "Failed to add VIP-ID!" @@ -160,33 +163,34 @@ "ru" "Ошибка добавления VIP-игрока!" "ua" "Помилка створення VIP-гравця!" } + "MENU_EDIT_VIP" { - "en" "Edit VIP-Player" + "en" "Edit VIP-player" "fi" "Muokaa VIP-pelaaja" "de" "VIP-Spieler ändern" - "pt" "Editar Player VIP" + "pt" "Editar VIP do Player" "pt_p" "Editar VIP" "ru" "Редактировать VIP-игрока" "ua" "Редагувати VIP-гравця" } "MENU_EDIT_TIME" { - "en" "Edit Time" + "en" "Edit time" "fi" "Muokaa aikaa" "de" "Zeit ändern" "pt" "Editar Tempo" - "pt_p" "Editar Tempo" + "pt_p" "Editar tempo" "ru" "Изменить срок" "ua" "Змінити термін" } "MENU_TIME_SET" { - "en" "Set Period" + "en" "Set period" "fi" "Aseta ajanjaksoa" "de" "Frist festlegen" "pt" "Definir Período" - "pt_p" "Definir Período" + "pt_p" "Definir período" "ru" "Установить срок" "ua" "Встановити термін" } @@ -195,7 +199,7 @@ "#format" "{1:s},{2:s}" "en" "The player {1} set the duration of VIP-status to {2}" "fi" "Pelaajalle {1} on asetettu VIP-statuksen kesto {2}" - "de" "Geben Sie in den Chat die Name, SteamID oder die IP-Adresse" + "de" "Der Spieler {1} hat die Dauer des VIP-Status auf {2} gesetzt" "pt" "Player {1} definiu a duração do VIP como {2}" "pt_p" "O jogador {1} definiu a duração de VIP para {2}" "ru" "Игроку {1} установлена длительность VIP-статуса до {2}" @@ -203,9 +207,10 @@ } "LOG_ADMIN_SET_EXPIRATION" { + "#format" "{1:L},{2:s},{3:s}" "en" "Admin {1} has set {2} VIP's duration to {3}" "fi" "Admin {1} on asettanut VIP-pelaajalle {2} VIP keston tilalle {3}" - "de" "Admin {1} {2}s VIP-Dauer um {3} eingestellt" + "de" "Admin {1} hat {2} VIPs Dauer auf {3} eingestellt" "pt" "Administrador {1} definiu a duração de VIP {2} para {3}" "pt_p" "Admin {1} definiu a duração de VIP {2} para {3}" "ru" "Админ {1} установил длительность VIP-статуса игроку {2} до {3}" @@ -213,11 +218,11 @@ } "MENU_TIME_ADD" { - "en" "Add Time" + "en" "Add time" "fi" "Lisää aikaa" - "de" "Zeiten verlängern" + "de" "Zeit hinzufügen" "pt" "Adicionar Tempo" - "pt_p" "Adicionar Tempo" + "pt_p" "Adicionar tempo" "ru" "Добавить время" "ua" "Додати час" } @@ -226,7 +231,8 @@ "#format" "{1:s},{2:s}" "en" "{1}'s VIP was extended for {2}" "fi" "VIP-pelaajan kesto pidenetään {1} tilalle {2}" - "de" "VIP des {1} war verlängt für {2}" + "de" "{1} VIP wurde verlängert für {2}" + "pt" "VIP de {1} foi estendido para {2}" "pt_p" "{1}'s VIP foi extendido por {2}" "ru" "Продлен срок VIP-игроку {1} на {2}" "ua" "Продовжено термін VIP-гравцю {1} на {2}" @@ -236,8 +242,8 @@ "#format" "{1:L},{2:s},{3:s}" "en" "Admin {1} has extented {2}'s VIP for {3}" "fi" "Admin {1} Pidensi keston VIP-pelaajalle {2} tilalle {3}" - "de" "Admin {1} hat verlängt VIP des {2} für {3}" - "pt" "Administrador {1} extendeu o VIP de {2} por {3}" + "de" "Admin {1} verlängert das VIP von {2} für {3}" + "pt" "Administrador {1} estendeu o VIP de {2} por {3}" "pt_p" "Admin {1} extendeu o VIP de {2}'s por {3}" "ru" "Админ {1} продлил срок VIP-игроку {2} на {3}" "ua" "Адмін {1} продовжив термін VIP для гравця {2} на {3}" @@ -246,18 +252,19 @@ { "en" "Unable to extend this player's VIP" "fi" "Et voi pidentää aikaa VIP-pelaajalle" - "de" "Fehler beim VIP des Spielers Fortsetzung" + "de" "VIP dieses Spielers kann nicht verlängert werden" + "pt" "Incapaz de estender o VIP deste player" "pt_p" "Não é possível prolongar o VIP deste jogador" "ru" "Нельзя продлить длительность этому VIP-игроку" "ua" "Неможливо продовжити VIP цього гравця" } "MENU_TIME_TAKE" { - "en" "Reduce Time" + "en" "Reduce time" "fi" "Vähennä aikaa" - "de" "Zeiten verkürzen" + "de" "Zeit verkürzen" "pt" "Reduzir Tempo" - "pt_p" "Reduzir Tempo" + "pt_p" "Reduzir período" "ru" "Отнять время" "ua" "Скоротити термін" } @@ -265,7 +272,7 @@ { "en" "Unable to reduce this player's VIP" "fi" "Et voi vähentää aikaa tältä VIP-pelaajalta" - "de" "Fehler beim VIP des Spielers Verkürzung" + "de" "VIP dieses Spielers kann nicht reduziert werden" "pt" "Não é possível reduzir o VIP deste player" "pt_p" "Não é possível reduzir o VIP deste jogador" "ru" "Нельзя отнять время у этого VIP-игрока" @@ -276,7 +283,7 @@ "#format" "{1:s},{2:s}" "en" "{1}'s VIP was reduces for {2}" "fi" "Vähenetty aikaa VIP-pelaajalle {1} tilalle {2}" - "de" "VIP des {1} war verkürzt für {2}" + "de" "VIP des {1} wurde verkürzt für {2}" "pt" "VIP de {1} foi reduzido para {2}" "pt_p" "{1}'s VIP foi reduzido para {2}" "ru" "Сокращен срок VIP-игроку {1} на {2}" @@ -287,7 +294,7 @@ "#format" "{1:L},{2:s},{3:s}" "en" "Admin {1} has reduced {2}'s VIP for {3}" "fi" "Admin {1} Vähensi aikaa VIP-pelaajalle {2} tilalle {3}" - "de" "Админ {1} hat verkürzt VIP des {2} für {3}" + "de" "Admin {1} reduziert VIP {2} für {3}" "pt" "Administrador {1} reduziu o VIP de {2} para {3}" "pt_p" "O Admin {1} reduziu o VIP de {2} para {3}" "ru" "Админ {1} сократил срок VIP-игроку {2} на {3}" @@ -295,7 +302,7 @@ } "MENU_EDIT_GROUP" { - "en" "Edit VIP-Group" + "en" "Edit VIP-group" "fi" "Muuta VIP-Ryhmää" "de" "VIP-Gruppe ändern" "pt" "Editar Grupo VIP" @@ -318,7 +325,7 @@ "#format" "{1:s},{2:s}" "en" "{1}'s VIP-group was changed to {2}" "fi" "Ryhmä VIP-pelaajalle {1} vaihdettu tilalle {2}" - "de" "VIP-Gruppe des {1} war geändert auf {2}" + "de" "VIP-Gruppe {1} wurde geändert auf {2}" "pt" "Grupo VIP de {1} foi alterado para {2}" "pt_p" "{1}'s Grupo VIP alterado para {2}" "ru" "Группа VIP-игроку {1} изменена на {2}" @@ -328,6 +335,8 @@ { "#format" "{1:s},{2:d},{3:s},{4:s},{5:s}" "en" "VIP-Player {1} (ID: {2}, Group: {3}) changed the group to {4} {5}" + "de" "VIP-Spieler {1} (ID: {2}, Gruppe: {3}) änderte die Gruppe auf {4} {5}" + "pt" "VIP do Player {1} (ID: {2}, Grupo: {3}) mudou o grupo para {4} {5}" "ru" "VIP-Игроку {1} (ID: {2}, Группа: {3}) изменена группа на {4} {5}" } "MENU_DEL_VIP" @@ -342,35 +351,58 @@ } "ADMIN_VIP_PLAYER_DELETED" { - "#format" "{1:s},{2:i}" - "en" "VIP-player {1} (ID: {2}) was successfully removed" - "fi" "VIP-Pelaaja {1} (ID: {2}) on onnistuneesti poistettu" - "de" "VIP-Spieler {1} (ID: {2}) war erfolgreich gelöscht" - "pt" "VIP do player {1} (ID: {2}) foi removido com sucesso" - "pt_p" "O VIP do jogador {1} (ID: {2}) foi removido com sucesso" - "ru" "VIP-игрок {1} (ID: {2}) успешно удален" - "ua" "VIP-гравець {1} (ID: {2}) успішно видалений" + "#format" "{1:s},{2:s}" + "en" "{DEFAULT}VIP-player {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) was successfully removed" + "fi" "{DEFAULT}VIP-Pelaaja {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) on onnistuneesti poistettu" + "de" "{DEFAULT}VIP-Spieler {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) wurde erfolgreich gelöscht" + "pt" "{DEFAULT}VIP do player {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) foi removido com sucesso" + "pt_p" "{DEFAULT}O VIP do jogador {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) foi removido com sucesso" + "ru" "{DEFAULT}VIP-игрок {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) успешно удален" + "ua" "{DEFAULT}VIP-гравець {GREEN}{1} {DEFAULT}({GREEN}{2}{DEFAULT}) успішно видалений" } "LOG_VIP_DELETED" { "#format" "{1:s},{2:d},{3:s},{4:s}" "en" "VIP-Player {1} (ID: {2}, Group: {3}) removed {4}" + "de" "VIP-Spieler {1} (ID: {2}, Gruppe: {3}) entfernt {4}" + "pt" "VIP do Player {1} (ID: {2}, Grupo: {3}) removido {4}" "ru" "VIP-Игрок {1} (ID: {2}, Группа: {3}) удален {4}" } "MENU_LIST_VIP" { "en" "List VIP-players" "fi" "Lista VIP-Pelaajista" - "de" "Liste der Spieler" + "de" "VIP Spieler Liste" "pt" "Lista de Players VIP" "pt_p" "Listar VIP's" "ru" "Список VIP-игроков" "ua" "Список VIP-гравців" } + "FIND_PLAYER" + { + "en" "Find a player" + "fi" "Etsi pelaaja" + "de" "Finde einen Spieler" + "pt" "Encontre um player" + "pt_p" "Encontre um jogador" + "ru" "Найти игрока" + "ua" "Знайти гравця" + } + "SHOW_ALL" + { + "en" "Show all" + "fi" "Näytä kaikki" + "de" "Zeige alles" + "pt" "Mostre todos players" + "pt_p" "Mostre tudo" + "ru" "Показать всех" + "ua" "Показати всіх" + } "MENU_SEARCH" { "#format" "{1:s},{2:d}" "en" "On request: {1}\nFound: {2} players" + "de" "Auf Anfrage: {1}\nGefunden: {2} Spieler" "ru" "По запросу: {1}\nНайдено: {2} игроков" "ua" "За запитом: {1}\nЗнайдено: {2} гравців" } @@ -379,8 +411,8 @@ "#format" "{1:s},{2:s},{3:s},{4:s}" "en" "Information:\n \nName:\n{1}\nSteamID:\n{2}\nGroup:\n{3}\nExpires:\n{4}" "fi" "Tiedotus:\n \nNimi:\n{1}\nSteamID:\n{2}\nRyhmä:\n{3}\nErääntyy:\n{4}" - "de" "Accountinformationen:\n \nDer Name:\n{1}\nSteamID:\n{2}\nDie Gruppe:\n{3}\nLäuft ab:\n{4}" - "pt" "Informação:\n \nNome:\n{1}\nSteamID:\n{2}\nGrupo:\n{3}\nExpira:\n{4}" + "de" "Accountinformationen:\n \nName:\n{1}\nSteamID:\n{2}\nGruppe:\n{3}\nLäuft ab:\n{4}" + "pt" "Informação:\n \nNick:\n{1}\nSteamID:\n{2}\nGrupo:\n{3}\nExpira:\n{4}" "pt_p" "Informação:\n \nNome:\n{1}\nSteamID:\n{2}\nGrupo:\n{3}\nExpira:\n{4}" "ru" "Информация:\n \nИмя:\n{1}\nSteamID:\n{2}\nГруппа:\n{3}\nИстекает:\n{4}" "ua" "Інформація:\n \nІм'я:\n{1}\nSteamID:\n{2}\nГрупа:\n{3}\nЗакінчується:\n{4}" @@ -388,6 +420,7 @@ "TEMPORARY" { "en" "Temporary" + "de" "Temporär" "pt" "Temporário" "ru" "Временный" "ua" "Тимчасовий" @@ -396,7 +429,7 @@ { "en" "Information" "fi" "Tiedotus" - "de" "Information" + "de" "Informationen" "pt" "Informação" "pt_p" "Informação" "ru" "Информация" @@ -426,7 +459,7 @@ { "en" "ERROR" "fi" "Virhe" - "de" "ERROR" + "de" "FEHLER" "pt" "ERRO" "pt_p" "ERRO" "ru" "Произошла ошибка" @@ -437,7 +470,8 @@ { "en" "No players available!" "fi" "Ei ole pelaajia saatavilla!" - "de" "Es gibt keine ferfügbare Spieler!" + "de" "Keine Spieler vorhanden!" + "pt" "Nenhum player disponível!" "pt_p" "Sem jogadores disponíveis" "ru" "Нет доступных игроков!" "ua" "Немає доступних гравців!" @@ -446,28 +480,29 @@ { "en" "Couldn't load player!" "fi" "Ei voitu ladata pelaajaa!" - "de" "Fehler beim Laden der Spieler!" + "de" "Konnte Spieler nicht laden!" + "pt" "Não foi possível carregar o player!" "pt_p" "Falha a carregar jogador!" "ru" "Не удалось загрузить игрока!" "ua" "Не вдалося завантажити гравця!" } "VIP_PLAYERS" { - "en" "VIP Players" - "fi" "VIP Pelaajat" - "de" "VIP Spieler" + "en" "VIP-players" + "fi" "VIP-Pelaajat" + "de" "VIP-Spieler" "pt" "Players VIP" "pt_p" "VIP's" - "ru" "VIP игроки" - "ua" "VIP гравці" + "ru" "VIP-игроки" + "ua" "VIP-гравці" } "NO_GROUPS_AVAILABLE" { "en" "No groups available!" "fi" "Ei ole ryhmiä saatavilla!" - "de" "Es gibt keine Gruppen!" + "de" "Keine Gruppen verfügbar!" "pt" "Sem grupos disponíveis!" - "pt_p" "Sem grupos disponíveis!" + "pt_p" "Sem grupos disponíveis" "ru" "Нет доступных групп!" "ua" "Немає доступних груп!" } @@ -475,7 +510,7 @@ { "en" "Reload VIP-players" "fi" "Ladata uudelleen VIP-pelaajat" - "de" "VIP-Spieler neu starten" + "de" "VIP-Spieler neu laden" "pt" "Recarregar Players com VIP" "pt_p" "Recarregar Jogadores VIP" "ru" "Перезагрузить VIP-игроков" @@ -485,18 +520,18 @@ { "en" "VIP-cache has been refreshed!" "fi" "Luettelo VIP-pelaajille on käynnistetty uudelleen!" - "de" "Liste der VIP-Spieler neu gestartet wurde!" + "de" "VIP-Cache wurde aktualisiert!" "pt" "Cache dos VIPS foi atualizado!" - "pt_p" "A cache dos VIP's foi actualizada!" + "pt_p" "A cache dos VIP's foi actualizada" "ru" "Список VIP-игроков перезагружен!" "ua" "Кеш VIP було оновлено!" } "ADMIN_MENU_RELOAD_VIP_CFG" { - "en" "Reload VIP Settings" + "en" "Reload the VIP settings" "fi" "Päivitetty VIP asetukset" - "de" "VIP neu starten" - "pt" "Recarregar Configurações VIP" + "de" "VIP-Einstellungen neu laden" + "pt" "Recarregar Configurações do VIP" "pt_p" "Recarregar as configurações VIP" "ru" "Перезагрузить настройки VIP" "ua" "Перезавантажити налаштування VIP" @@ -505,39 +540,49 @@ { "en" "VIP-settings has been reloaded!" "fi" "VIP-asetukset on käynnistetty uudelleen!" - "de" "VIP-Einstellungen neu gestartet wurden!" - "pt" "As definições VIP foram atualizadas!" + "de" "Die VIP-Einstellungen wurden neu geladen!" + "pt" "As configurações do VIP foram atualizadas!" "pt_p" "As definições VIP foram actualizadas!" "ru" "Настройки VIP перезагружены!" "ua" "Налаштування VIP були перезавантажені!" } + "BY_ADMIN" { "en" "by admin" + "de" "von admin" "pt" "por administrador" "ru" "админом" } + "BY_SERVER" { "en" "by server" + "de" "von Server" "pt" "por servidor" "ru" "сервером" } + "BY_PLUGIN" { "en" "by plugin" + "de" "durch Plugin" "pt" "por plugin" "ru" "плагином" } + "REASON_EXPIRED" { "en" "Expired" + "de" "Abgelaufen" "pt" "Expirado" "ru" "Истек срок" } + "REASON_INACTIVITY" { "en" "For inactivity" + "de" "Für Inaktivität" "pt" "Por inatividade" "ru" "По неактивности" } @@ -555,7 +600,7 @@ { "en" "None" "fi" "Ei Mitään" - "de" "Kein(e)" + "de" "Keine" "pt" "Nenhum" "pt_p" "Nenhum" "ru" "Нет" @@ -565,7 +610,7 @@ { "en" "Never" "fi" "Ei koskaan" - "de" "Nie" + "de" "Niemals" "pt" "Nunca" "pt_p" "Nunca" "ru" "Никогда" @@ -594,11 +639,11 @@ } "ENTER_AUTH" { - "en" "Enter the chat Name/SteamID\n and press" + "en" "Enter the chat Name/SteamID\nand press" "fi" "Kirjoita chattiin Nimi/SteamID\nja paina" - "de" "Gehen Sie in den Chat Der Name/Nach SteamID" - "pt" "Digite Nome/SteamID\nno chat e confirme" - "pt_p" "Introduz no chat Nome/SteamID\ne confirmar" + "de" "Gehen Sie in den Chat den Namen/SteamID\nund drücken Sie" + "pt" "Digite Nick/SteamID\nno chat e confirme" + "pt_p" "Introduz no chat Nome/SteamID\nand Confirmar" "ru" "Введите в чат Имя/SteamId\nи нажмите" "ua" "Введіть в чат Ім'я/SteamId\nі натисніть" } @@ -616,7 +661,7 @@ { "en" "Incorrect time" "fi" "Virhellinen aika" - "de" "Falsches Zeit" + "de" "Falsche Zeit" "pt" "Período incorrecto" "pt_p" "Período incorrecto" "ru" "Некорректное время" @@ -624,9 +669,9 @@ } "FIND_THE_ID_FAIL" { - "en" "Synax: sm_delvip " + "en" "Syntax: sm_delvip " "fi" "Käyttö: sm_delvip " - "de" "Synax: sm_delvip " + "de" "Synax: sm_delvip " "pt" "Sintaxe: sm_delvip " "pt_p" "Sintaxe: sm_delvip " "ru" "Используйте: sm_delvip <идентификатор>" @@ -636,7 +681,7 @@ { "en" "Incorrect usage" "fi" "virheellinen käyttö" - "de" "Falscheingabe" + "de" "Falsche Eingabe" "pt" "Uso incorreto" "pt_p" "Uso incorreto" "ru" "Неверное использование" @@ -657,7 +702,7 @@ { "en" "This player already has VIP!" "fi" "Tämä pelaaja on jo VIP!" - "de" "Dieser Spieler schon hat VIP-Zugriff!" + "de" "Dieser Spieler hat schon VIP-Zugriff!" "pt" "Este player já tem VIP!" "pt_p" "Este jogador já tem VIP!" "ru" "Игрок уже является VIP-игроком!" @@ -667,7 +712,7 @@ { "en" "Specified admin-group does not exist!" "fi" "Määritelty admin-ryhmä ei ole olemassa!" - "de" "Diese admin-Guppe existiert nicht!" + "de" "Diese Admin-Gruppe existiert nicht!" "pt" "Grupo de administração especificado não existe!" "pt_p" "O grupo-admin especificado não existe!" "ru" "Указанная админ-группа не существует!" @@ -677,31 +722,31 @@ { "en" "Specified VIP-group does not exist!" "fi" "Määritelty VIP-ryhmä ei ole olemassa!" - "de" "Diese VIP-Guppe existiert nicht!" + "de" "Diese VIP-Gruppe existiert nicht!" "pt" "Grupo VIP especificado não existe!" "pt_p" "O grupo VIP especificado não existe!" "ru" "Указанная VIP-группа не существует!" "ua" "Зазначена VIP-група не існує!" } "FIND_PLAYER" - { - "en" "Search for a player" - "pt" "Buscar por um jogador" + { + "en" "Search for a player" + "pt" "Buscar por um jogador" "ru" "Найти игрока" - } - "SHOW_ALL" - { - "en" "Show all players" - "pt" "Mostrar todos jogadores" + } + "SHOW_ALL" + { + "en" "Show all players" + "pt" "Mostrar todos jogadores" "ru" "Показать всех" - } + } "y." { "en" "y." "fi" "v." "de" "j." "pt" "a." - "pt_p" "a." + "pt_p" "ano." "ru" "г." "es" "a." "ua" "р." diff --git a/addons/sourcemod/translations/vip_modules.phrases.txt b/addons/sourcemod/translations/vip_modules.phrases.txt new file mode 100644 index 0000000..71956af --- /dev/null +++ b/addons/sourcemod/translations/vip_modules.phrases.txt @@ -0,0 +1,2046 @@ +"Phrases" +{ + "additional_menu" + { + "ru" "Дополнительное VIP" + "en" "Additional VIP" + "pt" "VIP Adicional" + "fi" "Lisää VIP" + "ua" "Додаткове VIP" + "es" "VIP Adicional" + "ar" "إضافي VIP" + } + "AutoRespawn" + { + "ru" "Авто возрождение" + "en" "Auto Respawn" + "pt" "Respawn Automático" + "fi" "Automaattinen palautuminen" + "ua" "Авто відродження" + "es" "Reaparición automática" + "ar" "بيضة السيارات" + } + "AimTeleport" + { + "ru" "AIM Телепорт" + "en" "AIM Teleport" + "pt" "AIM Teleport" + "fi" "AIM Teleport" + "ua" "AIM Телепорт" + "es" "AIM Teleport" + "ar" "النقل الفضائي AIM" + } + "AustraliumWeapons" + { + "ru" "Оружие из Австралия" + "en" "Weapons from Australium" + "pt" "Armas de Australium" + "fi" "Aseita australiasta" + "ua" "Зброя з Австралія" + "es" "Armas de Australium" + "ar" "أسلحة من أستراليا" + } + "AutoBuy" + { + "ru" "Автозакупка" + "en" "Auto Buy" + "pt" "Auto Buy" + "fi" "Automaattinen ostos" + "ua" "Автозакупівля" + "es" "Compra automática" + "ar" "شراء تلقائي" + } + "AutoBuyMenu" + { + "ru" "Настройка автозакупки" + "en" "Auto Buy Setting" + "pt" "Configurações do Auto Buy" + "fi" "Automaattisen ostoiden valikko" + "ua" "Налаштування автозакупівлі" + "es" "Configuración de compra automática" + "ar" "إعداد شراء السيارات" + } + "AutoSilencer_usp" + { + "ru" "Авто глушитель на USP" + "en" "Auto Silencer for USP" + "pt" "Silenciador Automático USP" + "fi" "Automaatti äänenvaimennin USP varten" + "ua" "Авто глушник на USP-S" + "es" "Silenciador automático para USP" + "ar" "كاتم الصوت التلقائي لـ USP" + } + "AutoSilencer_m4a1" + { + "ru" "Авто глушитель на M4A1" + "en" "Auto Silencer for M4A1" + "pt" "Silenciador Automático M4A1" + "fi" "Automaatti äänenvaimennin M4A1 varten" + "ua" "Авто глушник на M4A1-S" + "es" "Silenciador automático para M4A1" + "ar" "كاتم الصوت التلقائي لـ M4A1" + } + "AntiFlash" + { + "ru" "Анти-флеш" + "en" "Anti-Flash" + "pt" "Anti-Flash" + "fi" "Anti-Flash" + "ua" "Анти-Флеш" + "es" "Anti-Flash" + "ar" "مضاد للفلاش" + } + "Armor" + { + "ru" "Бронь" + "en" "Armor" + "pt" "Colete" + "fi" "Suojaliivi" + "ua" "Броня" + "es" "Armadura" + "ar" "درع" + } + "AURA" + { + "ru" "Аура" + "en" "Aura" + "pt" "Aura" + "fi" "Säteily" + "ua" "Аура" + "es" "Aura" + "ar" "هالة" + } + "AURA_M" + { + "ru" "Цвет ауры" + "en" "Color Aura" + "pt" "Cor da Aura" + "fi" "Säteilyn väri" + "ua" "Колір аури" + "es" "Aura de color" + "ar" "هالة اللون" + } + "Burning" + { + "ru" "Горение" + "en" "Burning" + "pt" "Burning" + "fi" "Polttava" + "ua" "Горіння" + "es" "Ardiente" + "ar" "احتراق" + } + "Breachcharge" + { + "ru" "Взрывчатка" + "en" "Explosive" + "pt" "Explosivo" + "fi" "Räjähtävä" + "ua" "Вибухівка" + "es" "Explosiva" + "ar" "مادة متفجرة" + } + "BunnyHop" + { + "ru" "БанниХоп" + "en" "BunnyHop" + "fi" "BunnyHop" + "ua" "БанніХоп" + "es" "BunnyHop" + "ar" "باني هوب" + } + "BHOP_TIME" + { + "#format" "{1:f}" + "ru" "Банихоп будет доступен через {1} секунд" + "ua" "Банніхоп буде доступний через {1} секунд" + "en" "BunnyHop will be available in {1} seconds" + "pt" "BunnyHop estará disponível em {1} segundos" + "fi" "Bunnyhop sera disponible dans {1} secondes" + "fi" "BunnyHop sera disponible dans {1} secondes" + "ar" "سوف يكون Bunnyhop متاحًا في {1} ثواني" + } + "BuyZona" + { + "ru" "Зона закупки" + "en" "Buy Zona" + "pt" "Buy Zona" + "fi" "Hankinta-alue" + "ua" "Зона закупівлі" + "es" "Comprar Zona" + "ar" "شراء المنطقة" + } + "BoostLadder" + { + "ru" "Ускорение подъема по лестнице" + "en" "Boost Ladder" + "pt" "Boost Ladder" + "fi" "Nopeutuva porraskiipeily" + "ua" "Прискорення підйому по драбині" + "es" "Escalera de refuerzo" + "ar" "رفع السلم" + } + "Chat" + { + "ru" "Чат" + "en" "Chat" + "pt" "Chat" + "fi" "Chatti" + "ua" "Чат" + "es" "Charla" + "ar" "دردشة" + } + "Chat_Menu" + { + "ru" "Настройка чата" + "en" "Chat Settings" + "pt" "Configurações do Chat" + "fi" "Inställning av chatt" + "ua" "Налаштування чату" + "es" "Configuraciones de chat" + "ar" "إعدادات المحادثة" + } + "CT_C4" + { + "ru" "Бомба за КТ" + "en" "C4 CT" + "pt" "C4 CT" + "fi" "C4 Erikoisjoukot" + "ua" "С4 за Спецназ" + "es" "C4 CT" + "ar" "قنبلة CT" + } + "CausedDamage" + { + "ru" "Наносимый урон" + "en" "Caused Damage" + "pt" "Dano Causado" + "fi" "Aiheuttanut vahinkoa" + "ua" "Завдана шкода" + "es" "Daño causado" + "ar" "تسببت في الضرر" + } + "COIN" + { + "ru" "Монеты" + "en" "Coins" + "pt" "Medalhas" + "fi" "Mitaleiden asetukset" + "ua" "Монети" + "es" "Monedas" + "ar" "عملات معدنية" + } + "CarryC4" + { + "ru" "Переносная бомба" + "en" "Carry C4" + "fi" "Kannettava pommi" + "ua" "Переносна С4" + "es" "Llevar C4" + "ar" "حمل C4" + } + "change_color_props" + { + "ru" "Смена цвета пропов" + "en" "Сhange Сolors Props" + "fi" "Värien muutos" + "ua" "Зміна кольорів пропів" + "es" "Сhange Сolors Props" + "ar" "دعائم تغيير الألوان" + } + "Changeteam" + { + "ru" "Смена команды" + "en" "Change Team" + "pt" "Change Team" + "fi" "Tiimin muutos" + "ua" "Зміна команди" + "es" "Cambiar equipo" + "ar" "فريق التغيير" + } + "ColoredFlash" + { + "ru" "Разноцветное ослепление" + "en" "Colored Flash" + "pt" "Flash Colorida" + "fi" "Värikäs sokaisin" + "ua" "Різнокольоровий осліплення" + "es" "Flash de Color" + "ar" "فلاش ملون" + } + "CustomWeapons" + { + "ru" "Модели оружия" + "en" "Weapon Models" + "pt" "Modelos de Armas" + "fi" "Ase Mallit" + "ua" "Моделі зброї" + "es" "Modelos de Armas" + "ar" "نماذج الأسلحة" + } + "C4_Model" + { + "ru" "Измененная бомба" + "en" "C4 Model" + "pt" "Modelo de C4" + "fi" "Muunnettu Pommi" + "ua" "Модель С4" + "es" "Modelo C4" + "ar" "نموذج C4" + } + "CCC_Settings" + { + "ru" "Настройки чата" + "en" "Chat Settings" + "pt" "Configurações do Chat" + "fi" "Chat-asetukset" + "ua" "Налаштування чату" + "es" "Configuraciones de chat" + "ar" "إعدادات المحادثة" + } + "С4" + { + "ru" "Бесплатная бомба" + "en" "Free Bomb" + "fi" "Ilmainen C4" + "ua" "Безкоштовна С4" + "es" "Libre bomba" + "ar" "قنبلة مجانية" + } + "CF_BuyVip" + { + "ru" "Данный функционал доступен только VIP-игрокам!" + "en" "This is feature available only for VIP-players!" + "fi" "Tämä toiminto on käytettävissä vain VIP-pelaajille!" + "ua" "Цей функціонал доступний лише VIP-гравцям!" + "es" "¡Esta función está disponible solo para jugadores VIP!" + "ar" "هذه الميزة متاحة فقط للاعبين VIP!" + } + "CCC_Enabler" + { + "ru" "Активация настроек чата" + "en" "Activate chat settings" + "fi" "Aktivoi chat-asetukset" + "ua" "Активація налаштувань чату" + "es" "Activa la configuración de chat" + "ar" "تنشيط إعدادات الدردشة" + } + "CAUSED_DAMAGE" + { + "ru" "Наносимый урон" + "en" "Caused Damage" + "pt" "Dano Causado" + "fi" "Aiheuttanut vahinkoa" + "ua" "Завдана шкода" + "es" "Daño causado" + "ar" "تسببت في الضرر" + } + "Defuser" + { + "ru" "Набор обезвреживания" + "en" "Defuse Kit" + "en" "Kit Defuse" + "fi" "Defuse Kit" + "ua" "Набір знешкодження" + "es" "Defuse Kit" + "ar" "طقم نزع الفتيل" + } + "Decoy" + { + "ru" "Телепорт-граната" + "en" "Decoy Teleport" + "fi" "Teleportti-kranaatti" + "ua" "Телепортаційна граната" + "es" "Teletransporte señuelo" + "ar" "شرك التخاطر" + } + "DissolveBody" + { + "ru" "Растворение тела" + "en" "Dissolution of the body" + "fi" "Liukeneminen elimistössä" + "ua" "Розчинення тіла" + "es" "Disolución del cuerpo" + "ar" "انحلال الجسم" + } + "DissolveBodyNew" + { + "ru" "Растворение тела" + "en" "Dissolution of the body" + "pt" "Dissolução do corpo" + "fi" "Liukeneminen elimistössä" + "ua" "Розчинення тіла" + "es" "Disolución del cuerpo" + "ar" "انحلال الجسم" + } + "ElectroEffects" + { + "ru" "Электрические эффекты" + "en" "Electro Effects" + "fi" "Salama Effekti" + "ua" "Електричні ефекти" + "es" "Efectos electricos" + "ar" "التأثيرات الكهربائية" + } + "evade" + { + "ru" "Уворот" + "en" "Dodge" + "fi" "Väistää" + "ua" "Ухилення" + "es" "Esquivar" + "ar" "يتملص" + } + "Endurance" + { + "ru" "Выносливость" + "en" "Endurance" + "fi" "Kestävyys" + "ua" "Витривалість" + "es" "Resistencia" + "ar" "قدرة التحمل" + } + "ExplosiveBullets" + { + "ru" "Взрывные пули" + "en" "Explosive Bullets" + "pt" "Balas Explosivas" + "fi" "Räjähtävät luodit" + "ua" "Вибухові кулі" + "es" "Balas explosivas" + "ar" "الرصاصات المتفجرة" + } + "ExtendedAmmoClip" + { + "ru" "Расширенные патроны в обойме" + "en" "Extended Ammo In Clip" + "fi" "Ammuksien lisäystä pääasiassa aseeseen" + "ua" "Розширені патрони в обоймі" + "es" "Munición extendida en el clip" + "ar" "ذخيرة ممتدة في مقطع" + } + "ExtendedAmmoReserve" + { + "ru" "Расширенные патроны в запасе" + "en" "Extended Ammo In Stock" + "fi" "Ammuksien lisäystä lippaaseen" + "ua" "Розширені патрони в запасі" + "es" "Munición extendida en stock" + "ar" "ممتد الذخيرة في المخزون" + } + "Fzi_Rescued" + { + "#format" "{1:N}" + "ru" "Вы были выбраны первым зомби, но были спасены благодаря VIP. {1} заражен вместо вас." + "en" "You were chosen as the first zombie, but were saved thanks to the VIP. {1} is infected for you." + "fi" "Olet valittu ensimmäiseksi zombiksi, mutta olit tallennettu VIP: n ansiosta. {1} tartunnan saaneet." + "ua" "Ви обрані в якості першого зомбі, але були врятовані завдяки VIP. {1} заражений замість вас." + "es" "Fuiste elegido como el primer zombie, pero te salvaste gracias al VIP. {1} está infectado por ti." + "ar" "تم اختيارك كأول غيبوبة ، ولكن تم إنقاذك بفضل VIP. {1} مصاب لك." + } + "Fzi" + { + "ru" "Иммунитет от первого заражения" + "en" "First Inf. Immunity" + "fi" "Immuniteetti ensimmäisestä infektiosta" + "ua" "Імунітет від першого зараження" + "es" "First Inf. Inmunidad" + "ar" "أول إنف. حصانة" + } + "FastReload" + { + "ru" "Быстрая перезарядка" + "en" "Fast Reload" + "fi" "Nopea lataus" + "ua" "Швидка перезарядка" + "es" "Recarga rápida" + "ar" "إعادة تحميل سريعة" + } + "FastSwitch" + { + "ru" "Быстрая смена оружия" + "en" "Fast Weapon Switch" + "fi" "Nopea aseenvaihto" + "ua" "Швидка заміна зброї" + "es" "Cambio rápido de arma" + "ar" "التبديل سلاح سريع" + } + "Fastplant" + { + "ru" "Быстрый плент бомбы" + "en" "Fast Plant Bomb" + "fi" "Nopean pommin pläntti" + "ua" "Швидкий плент бомби" + "es" "Bomba de planta rápida" + "ar" "قنبلة نباتية سريعة" + } + "FireDamage" + { + "ru" "Поджигающие повреждения" + "en" "Fire Damage" + "fi" "Sytytys Vahinko" + "ua" "Запалювальні пошкодження" + "es" "Daño por fuego" + "ar" "الخسائر الناجمة عن الحرائق" + } + "Gloves" + { + "ru" "Перчатки" + "en" "Gloves" + "pt" "Luvas" + "fi" "Hanskat" + "ua" "Рукавички" + "es" "Guantes" + "ar" "قفاز" + } + "Gloves_MENU" + { + "ru" "Выбор перчаток" + "en" "Choose Gloves" + "pt" "Escolha Luvas" + "fi" "Hanskojen valikko" + "ua" "Вибір рукавичок" + "es" "Elige guantes" + "ar" "اختر القفازات" + } + "GOD" + { + "ru" "Бессмертие" + "en" "GOD" + "fi" "Kuolemattomuus" + "ua" "Безсмертя" + "es" "DIOS" + "ar" "خلود" + } + "GrenadeTrails" + { + "ru" "Трейлы гранат" + "en" "Grenade Trails" + "pt" "Trilhas de Granadas" + "fi" "Kranaattijälki" + "ua" "Сліди гранат" + "es" "Granada Senderos" + "ar" "ممرات القنابل اليدوية" + } + "GrenadeTrails_MENU" + { + "ru" "Цвет трейлов гранат" + "en" "Color Grenade Trails" + "pt" "Cores das Trilhas de Grenadas" + "fi" "Kranaattijäljen väri" + "ua" "Колір слідів гранат" + "es" "Granada de color" + "ar" "مسارات القنابل الملونة" + } + "Grenades" + { + "ru" "Гранаты" + "en" "Grenades" + "pt" "Granadas" + "fi" "Kranaatit" + "ua" "Гранати" + "es" "Granadas" + "ar" "قنبلة يدوية" + } + "GoldStatues" + { + "ru" "Золотые статуи" + "en" "Gold Statues" + "fi" "Kulta patsaat" + "ua" "Золоті статуї" + "es" "Estatuas de oro" + "ar" "التماثيل الذهبية" + } + "Gravity" + { + "ru" "Гравитация" + "en" "Gravity" + "pt" "Gravidade" + "fi" "Gravitaatio" + "ua" "Гравітація" + "es" "Gravedad" + "ar" "الجاذبية" + } + "HP" + { + "ru" "ХП" + "en" "HP" + "fi" "Terveys" + "ua" "Здоров'я" + "es" "Salud" + "ar" "الصحة" + } + "HealCube" + { + "ru" "Регенерационный куб" + "en" "Heal Cube" + "fi" "Paranna kuutio" + "ua" "Відновлювальний куб" + "es" "Curar el cubo" + "ar" "شفاء المكعب" + } + "Healthshot" + { + "ru" "Укол здоровья" + "en" "Healthshot" + "pt" "Healthshot" + "fi" "Terveyden piikki" + "ua" "Укол здоров'я" + "es" "Disparo de salud" + "ar" "لقطة صحية" + } + "HeavyArmor" + { + "ru" "Тяжелая броня" + "en" "Heavy Armor" + "fi" "Kova suojaliivi" + "ua" "Важка броня" + "es" "Armadura pesada" + "ar" "درع ثقيل" + } + "HitMarker" + { + "ru" "Хитмаркер" + "en" "Hit Marker" + "fi" "Hit Marker" + "ua" "Хітмаркер" + "es" "Dar en el objetivo" + "ar" "ضرب علامة" + } + "JoinSound" + { + "ru" "Музыка при входе" + "en" "Join Sound" + "fi" "Musiikki tervehdys" + "ua" "Музика при підключенні" + "es" "Unirse al sonido" + "ar" "انضم إلى الصوت" + } + "jumps" + { + "ru" "Несколько прыжков" + "en" "Some Jumps" + "fi" "Useita hyppyjä" + "ua" "Декілька стрибків" + "es" "Algunos saltos" + "ar" "بعض القفزات" + } + "JoinMessage" + { + "ru" "Оповещение о входе" + "en" "Join Message" + "fi" "Kirjaudu sisään" + "ua" "Оповіщення про вхід" + "es" "Unirse al mensaje" + "ar" "الانضمام إلى الرسالة" + } + "KillScreen" + { + "ru" "Эффект при убийстве" + "en" "Kill Effect" + "fi" "Tappaus effekti" + "ua" "Ефект під час вбивства" + "es" "Efecto de Matar" + "ar" "تأثير القتل" + } + "Kick" + { + "ru" "Кикнуть игрока" + "en" "Kick Player" + "pt" "Kick Player" + "fi" "Potkia pelaaja" + "ua" "Вигнати гравця" + "es" "Jugador de Patada" + "ar" "لاعب ركلة" + } + "Language" + { + "ru" "Изменить язык" + "en" "Change Language" + "pt" "Mudar Idioma" + "fi" "Vaihda kieli" + "ua" "Змінити мову" + "es" "Cambiar Idioma" + "ar" "تغيير اللغة" + } + "LongJump" + { + "ru" "Длинный прыжок" + "en" "Long Jump" + "fi" "Pitkä hyppy" + "ua" "Довгий стрибок" + "es" "Salto Largo" + "ar" "قفزة طويلة" + } + "Left" + { + "#format" "{1:i}" + "ru" "Осталось: {1}" + "en" "Left: {1}" + "fi" "Jäljellä: {1}" + "ua" "Залишилось: {1}" + "es" "Lzquierda: {1}" + "ar" "اليسار: {1}" + } + "MKIT" + { + "ru" "Музыкальный комплект" + "en" "Music Kit" + "pt" "Kit de Música" + "fi" "Music Kit" + "ua" "Музичний набір" + "es" "Kit de Música" + "ar" "إعدادات مجموعة الموسيقى" + } + "Medkit" + { + "ru" "Аптечка" + "en" "Medic Kit" + "pt" "Kit Médico" + "fi" "Lääkkeet" + "ua" "Аптечка" + "es" "Kit Medico" + "ar" "عدة طبية" + } + "MuteGagSilence" + { + "ru" "Чат/Микрофон" + "en" "Mute/Gag/Silence" + "pt" "Mute/Gag/Silence" + "fi" "Chatti/Mikrofoni" + "ua" "Чат/Мікрофон" + "es" "Mute/Gag/Silence" + "ar" "كتم الصوت / الكمامة / الصمت" + } + "Money" + { + "ru" "Деньги" + "en" "Money" + "pt" "Dinheiro" + "fi" "Raha" + "ua" "Гроші" + "es" "Dinero" + "ar" "مال" + } + "mvp" + { + "ru" "MVP значки" + "en" "MVP" + "pt" "MVP" + "fi" "MVP merkit" + "ua" "MVP значки" + "es" "MVP" + "ar" "أفضل لاعب" + } + "NoRecoil" + { + "ru" "Стрельба без отдачи" + "en" "No Recoil" + "fi" "Kuvaaminen ilman palautusta" + "ua" "Стрілянина без віддачі" + "es" "Sin retroceso" + "ar" "لا نكص" + } + "NoSelfDamage" + { + "ru" "Нет урона от себя" + "en" "No Self Damage" + "fi" "Ei vaurioita itseään" + "ua" "Без самопошкодження" + "es" "Sin autolesiones" + "ar" "لا ضرر على النفس" + } + "NEON" + { + "ru" "Неон" + "en" "Neon" + "pt" "Neon" + "fi" "Neon" + "ua" "Неон" + "es" "Neón" + "ar" "نيون" + } + "Non_Lethal_Damage" + { + "ru" "Не смертельный урон" + "en" "No Lethal Damage" + "fi" "Ei tappavaa vahinkoa" + "ua" "Не смертельна шкода" + "es" "Daño no letal" + "ar" "ضرر غير مميت" + } + "NEON_MENU" + { + "ru" "Цвет неона" + "en" "Color Neon" + "pt" "Cor do Neon" + "fi" "Neon Väri" + "ua" "Колір неону" + "es" "Color neón" + "ar" "لون النيون" + } + "nightvision" + { + "ru" "Ночное зрение" + "en" "Night Vision" + "pt" "Visão Noturna" + "fi" "Yö visio" + "ua" "Нічне бачення" + "es" "Vision nocturna" + "ar" "رؤية ليلية" + } + "NoFallDamage" + { + "ru" "Нет урона от мира" + "en" "No Fall Damage" + "fi" "Ei vaurioita maailmalta" + "ua" "Без шкоди від світу" + "es" "No hay daño por caida" + "ar" "لا ضرر السقوط" + } + "OOImmunity" + { + "ru" "Иммунитет от ограничения оружия" + "en" "Weapon restriction immunity" + "fi" "Gun limit immunity" + "ua" "Імунітет від обмеження зброї" + "es" "Inmunidad de restricción de armas" + "ar" "حصانة تقييد السلاح" + } + "paint" + { + "ru" "Рисовалка" + "en" "Paint" + "pt" "Paint" + "fi" "Maalaus" + "ua" "Малювалка" + "es" "Pintar" + "ar" "رسم" + } + "paint_menu" + { + "ru" "Настройка рисовалки" + "en" "Paint Menu" + "pt" "Paint Menu" + "fi" "Maalaus Menu" + "ua" "Налаштування малювалки" + "es" "Menú de pintura" + "ar" "رسم قائمة طعام" + } + "Parachute" + { + "ru" "Парашют" + "en" "Parachute" + "pt" "Paraquedas" + "fi" "Laskuvarjo" + "ua" "Парашут" + "es" "Paracaídas" + "ar" "المظلة" + } + "Pets_Display" + { + "ru" "Питомцы" + "en" "Pets" + "pt" "Pets" + "fi" "Lemmikkieläimet" + "ua" "Вихованці" + "es" "Mascotas" + "ar" "حيوان اليف" + } + "PaintBall" + { + "ru" "Пейнтбол" + "en" "Paintball" + "pt" "Paintball" + "fi" "Paintball" + "ua" "Пейнтбол" + "es" "Paintball" + "ar" "كرات الطلاء" + } + "Pets_Title" + { + "ru" "Выбор питомца" + "en" "Choosing Pet" + "pt" "Escolha um Pet" + "fi" "Lemmikkien valinta" + "ua" "Обрати вихованця" + "es" "Elegir una mascota" + "ar" "اختيار حيوان أليف" + } + "Pets_Disable" + { + "ru" "Выключить" + "en" "Disable" + "pt" "Desabilitar" + "fi" "Sammuta" + "ua" "Вимкнути" + "es" "Inhabilitar" + "ar" "تعطيل" + } + "QuickDefuse" + { + "ru" "Быстрое обезвреживание" + "en" "Quick Defuse" + "fi" "Nopea Purkaus" + "ua" "Швидке знешкодження" + "es" "Desactivación rápida" + "ar" "تحييد سريع" + } + "QHR" + { + "ru" "Быстрое спасение заложников" + "en" "Quick Hostage Rescue" + "fi" "Nopean panttivangien pelastus" + "ua" "Швидке рятування заручників" + "es" "Rescate rápido de rehenes" + "ar" "الإنقاذ السريع للرهائن" + } + "RegenArmor" + { + "ru" "Регенерация брони" + "en" "Regeneration Armor" + "fi" "Suojaliivin uudistaminen" + "ua" "Регенерація броні" + "es" "Armadura de regeneración" + "ar" "تجديد درع" + } + "Radar-Invis" + { + "ru" "Инвиз на радаре" + "en" "Radar Invis" + "fi" "Inviz tutkalla" + "ua" "Невидимість на радарі" + "es" "Radar Invis" + "ar" "رادار إينفيس" + } + "Respawn" + { + "ru" "Возрождение" + "en" "Respawn" + "fi" "Uudestisyntyminen" + "ua" "Відродження" + "es" "Reaparecer" + "ar" "إعادة" + } + "RegenHP" + { + "ru" "Регенерация здоровья" + "en" "HP Regeneration" + "fi" "Terveyden uudistaminen" + "ua" "Регенерація здоров'я" + "es" "Regeneración HP" + "ar" "تجديد الصحة" + } + "ReceivedDamage" + { + "ru" "Получаемый урон" + "en" "Received Damage" + "fi" "Aiheutuvat vahingot" + "ua" "Отримана шкода" + "es" "Daño recibido" + "ar" "تلقى الضرر" + } + "RADAR" + { + "ru" "Невидимость на радаре" + "en" "Hide in Radar" + "pt" "Esconder no Radar" + "fi" "Näkymättömyys tutka" + "ua" "Невидимість на радарі" + "es" "Esconderse en el radar" + "ar" "اختبئ في الرادار" + } + "RANK" + { + "ru" "Настройка ранга" + "en" "Rank Settings" + "fi" "Rank Asetukset" + "ua" "Налаштування рангу" + "es" "Configuraciones de rango" + "ar" "إعدادات الترتيب" + } + "RECEIVED_DAMAGE" + { + "ru" "Получаемый урон" + "en" "Received Damage" + "fi" "Aiheutuvat vahingot" + "ua" "Отримана шкода" + "es" "Daño recibido" + "ar" "تلقى الضرر" + } + "skybox" + { + "ru" "Небо" + "en" "Skybox" + "pt" "SkyBox" + "fi" "Taivas" + "ua" "Небо" + "es" "Skybox" + "ar" "سكايبوكس" + } + "skybox_share" + { + "ru" "Поделиться SkyBox'ом" + "en" "Share SkyBox" + "pt" "Compartilhar SkyBox" + } + "SmokeColors" + { + "ru" "Цвет дыма" + "en" "Smoke Color" + "pt" "Cor da Smoke" + "fi" "Savun väri" + "ua" "Колір диму" + "es" "Color de humo" + "ar" "لون الدخان" + } + "SWGM_VIP_Give" + { + "ru" "Вы получили VIP за вступление в нашу Steam группу." + "en" "You got VIP because you join our Steam group." + "fi" "Sait VIP-jäsenen Steam-ryhmään liittymisestä." + "ua" "Ви отримали VIP за приєднання до нашої групи Steam." + "es" "Tienes VIP porque te unes a nuestro grupo Steam." + "ar" "لقد حصلت على VIP لأنك انضممت إلى مجموعة Steam الخاصة بنا." + } + "SWGM_VIP_Take" + { + "ru" "Вы потеряли VIP, потому что вышли из нашей Steam группы." + "en" "You lost VIP because you leave our Steam group." + "fi" "Menetit VIP, koska jätit Steam-ryhmämme." + "ua" "Ви втратили VIP, тому що покинули нашу групу Steam." + "es" "Perdiste VIP porque dejaste nuestro grupo Steam." + "ar" "لقد فقدت VIP لأنك تركت مجموعة Steam الخاصة بنا." + } + "Skins" + { + "ru" "Скины" + "en" "Skins" + "pt" "Skins" + "fi" "Skinit" + "ua" "Персонаж" + "es" "Piel" + "ar" "بشرة" + } + "Skins_Menu" + { + "ru" "Скины Mеню" + "en" "Skins Menu" + "pt" "Menu Skins" + "fi" "Skini Valikko" + "ua" "Меню Cкінів" + "es" "Menú Skins" + "ar" "اختيار الجلد" + } + "Speed" + { + "ru" "Повышенная скорость" + "en" "Speed" + "fi" "Nopeus" + "ua" "Підвищена швидкість" + "es" "Velocidad" + "ar" "سرعة" + } + "SmokeColors_MENU" + { + "ru" "Выбрать цвет дыма" + "en" "Select Smoke Color" + "fi" "Valitse savun väri" + "ua" "Обрати колір диму" + "es" "Seleccionar color de humo" + "ar" "حدد لون الدخان" + } + "status" + { + "ru" "Показ премиум статуса" + "en" "Show Pemium Status" + "fi" "Näytetään premium-tila" + "ua" "Показ преміум статусу" + "es" "Mostrar estado premium" + "ar" "عرض حالة قسط" + } + "Showdamagehud" + { + "ru" "Показ урона (HUD)" + "en" "Show Damage (HUD)" + "fi" "Vahinkonäyttö (HUD)" + "ua" "Показ пошкоджень (HUD)" + "es" "Mostrar daño (HUD)" + "ar" "إظهار الضرر (HUD)" + } + "ShowDamageExtended" + { + "ru" "Показ урона" + "en" "Show Damage" + "fi" "Näytetään vaurioita" + "ua" "Показ пошкоджень" + "es" "Mostrar daño" + "ar" "عرض الضرر" + } + "Spawn_Effects" + { + "ru" "Эффекты при возрождении" + "en" "Spawn Effects" + "fi" "Uudestisyntymisen effektit" + "ua" "Ефекти при відродженні" + "es" "Efectos de engendro" + "ar" "تأثيرات النشر" + } + "Sparks" + { + "en" "Sparks" + "ru" "Искры" + "fi" "Kipinät" + "ua" "Іскри" + "es" "Chispas" + "ar" "شرارات" + } + "Switchteam" + { + "ru" "Изменить команду" + "en" "Switch Team" + "fi" "Muuta komentoa" + "ua" "Змінити команду" + "es" "Cambiar de equipo" + "ar" "فريق التبديل" + } + "Survivability" + { + "ru" "Выживаемость" + "en" "Survivability" + "fi" "Eloonjäämisaste" + "ua" "Життєздатність" + "es" "Supervivencia" + "ar" "البقاء على قيد الحياة" + } + "ShowDamage" + { + "ru" "Показ урона" + "en" "Displaying Damage" + "fi" "Näytetään vahinkopaneeli" + "ua" "Показ пошкодження" + "es" "Mostrar daño" + "ar" "عرض الضرر" + } + "Trails" + { + "ru" "Трейлы" + "en" "Trails" + "fi" "Jäljet" + "ua" "Трейли" + "es" "Caminos" + "ar" "الممرات" + } + "Trails_MENU" + { + "ru" "Выбор трейла" + "en" "Select Trail" + "fi" "Jäljen Valitseminen" + "ua" "Вибір трейлу" + "es" "Selección de caminos" + "ar" "اختيار الممر" + } + "Tracers" + { + "ru" "Трасеры" + "en" "Tracers" + "fi" "Merkkiaineet" + "ua" "Трасери" + "es" "Trazadores" + "ar" "الراسمات" + } + "Tracers_MENU" + { + "ru" "Цвет трасеров" + "en" "Color Tracers" + "fi" "Merkkiaineiden väri" + "ua" "Колір трасерів" + "es" "Trazadores de color" + "ar" "متتبعات اللون" + } + "Tagrenade" + { + "ru" "Таговая граната" + "en" "TA Grenade" + "pt" "TA Grenade" + "fi" "Tag Granaattiomena" + "ua" "Tag граната" + "es" "Tagrenade" + "ar" "تاجريناد" + } + "TopIcon" + { + "ru" "VIP Mодель над головой" + "en" "VIP Model over the head" + "fi" "VIP Malli päässä" + "ua" "VIP Mодель над головою" + "es" "Modelo VIP sobre la cabeza" + "ar" "نموذج VIP فوق الرأس" + } + "ThrowingKnives" + { + "ru" "Метательные ножи" + "en" "Throwing Knives" + "fi" "Heitto veitset" + "ua" "Метальні ножі" + "es" "Lanzando cuchillos" + "ar" "رمي السكاكين" + } + "TEAMMATES_HEAL" + { + "ru" "Лечение союзников" + "en" "Teammates Heal" + "fi" "Ally-hoito" + "ua" "Лікування союзників" + "es" "Compañeros de equipo sanan" + "ar" "زملاء الفريق شفاء" + } + "Tag" + { + "ru" "Тег" + "en" "TAG" + "fi" "TAG" + "ua" "Тег" + "es" "Etiqueta" + "ar" "بطاقة شعار" + } + "Tag_Menu" + { + "ru" "Настройки тега" + "en" "TAG Settings" + "fi" "TAG Asetukset" + "ua" "Налаштування тегу" + "es" "Configuraciones de etiqueta" + "ar" "إعدادات العلامات" + } + "UnlimitedAmmo" + { + "ru" "Бесконечные Патроны" + "en" "Unlimited Ammo" + "fi" "Rajoittamattomat ammukset" + "ua" "Нескінченні набої" + "es" "Munición ilimitada" + "ar" "إعدادات العلامات" + } + "UnlimPrimaryAmmo" + { + "ru" "Бесконечные патроны в основной обойме" + "en" "Infinite Ammo in the main clip" + "fi" "Loputtomasti ammuksia pääasiassa aseeseen" + "ua" "Нескінченні набої в основній обоймі" + "es" "Munición infinita en el clip principal" + "ar" "الذخيرة اللانهائية في المقطع الرئيسي" + } + "UnlimReserveAmmo" + { + "ru" "Бесконечные патроны в запасе" + "en" "Infinite ammo in stock" + "fi" "Loputtomasti ammuksia lippaaseen" + "ua" "Нескінченні набої у запасі" + "es" "Munición infinita en stock" + "ar" "ذخيرة لانهائية في المخزون" + } + "VIP_SWAPPOSITION" + { + "ru" "Смена позиций" + "en" "Swap Positions" + "fi" "Asemien muuttaminen" + "ua" "Зміна позицій" + "es" "Posiciones de intercambio" + "ar" "مراكز المبادلة" + } + "VIP_ICE" + { + "ru" "Заморозка" + "en" "Freeze" + "fi" "Jäätymispiste" + "ua" "Заморозка" + "es" "Congelar" + "ar" "تجميد" + } + "VIP_PLAYERCOLOR" + { + "ru" "Смена цветов" + "en" "Color Changing" + "fi" "Väri muuttuu" + "ua" "Зміна кольорів" + "es" "Cambio de color" + "ar" "تغيير اللون" + } + "VIP_DROPWEAPON" + { + "ru" "Выбить оружие" + "en" "Drop Weapon" + "fi" "Katkaise aseet" + "ua" "Вибити зброю" + "es" "Soltar arma" + "ar" "إسقاط السلاح" + } + "Vampirism" + { + "ru" "Вампиризм" + "en" "Vampirism" + "pt" "Vampirismo" + "fi" "Vampirismi" + "ua" "Вампіризм" + "es" "Vampirismo" + "ar" "مصاص دماء" + } + "ValveGloves" + { + "ru" "Перчатки (Valve)" + "en" "Gloves (Valve)" + "pt" "Luvas (Valve)" + "fi" "Käsineet (Valve)" + "ua" "Рукавички (Valve)" + "es" "Guantes (Valve)" + "ar" "القفازات (Valve)" + } + "ValveGloves_MENU" + { + "ru" "Выбор перчаток (Valve)" + "en" "Select Gloves (Valve)" + "pt" "Selecione Luvas (Valve)" + "fi" "Käsineiden valinta (Valve)" + "ua" "Вибір рукавичок (Valve)" + "es" "Seleccionar guantes (Valve)" + "ar" "حدد القفازات (Valve)" + } + "VoiceSoundPlayer" + { + "ru" "Фразы" + "en" "Voice Sound" + "fi" "Äänen ääni" + "ua" "Фрази" + "es" "Sonido de voz" + "ar" "صوت الصوت" + } + "WaterEffect" + { + "ru" "Брызги воды" + "en" "Water Splashing" + "fi" "Veden roiskeita" + "ua" "Бризки води" + "es" "Salpicaduras de agua" + "ar" "رش الماء" + } + "Weaponpack" + { + "ru" "Комплект оружий | WP" + "en" "Weapon Pack | WP" + "pt" "Pack de Armas | WP" + "fi" "Aseiden setti | WP" + "ua" "Комплект зброї | WP" + "es" "Set de armas | WP" + "ar" "مجموعة سلاح } WP" + } + "WeaponJumping" + { + "ru" "Прыжок от выстрела" + "en" "Jump from the shot" + "fi" "Shot Jump" + "ua" "Стрибок від пострілу" + "es" "Salta desde el tiro" + "ar" "القفز من اللقطة" + } + "ExoJumpBoots" + { + "ru" "Exo Jump ботинки" + "en" "Exo Jump Boots" + "pt" "Botas Exo Jump" + "fi" "Exo Jump stövlar" + "ua" "Exo Jump черевики" + "es" "Botas Exo Jump" + "ar" "القفز إكسو الروبوتات" + } + "BuyTeamWeapon" + { + "ru" "Покупка оружия команды" + "en" "Buy Team Weapon" + "fi" "Köp lagsvapen" + "ua" "Купівля командої зброї" + "es" "Comprar arma de equipo" + "ar" "شراء سلاح الفريق" + } + "Bumpmine" + { + "ru" "Толчковые мины" + "en" "Bump Mine" + "pt" "Bump Mine" + "fi" "Bump Min" + "ua" "Відштовхувальні міни" + "es" "Golpear la mía" + "ar" "عثرة الألغام" + } + "CashBack" + { + "ru" "Возврат денег" + "en" "Cash Back" + "fi" "Pengar tillbaka" + "ua" "Повернення грошей" + "es" "Devolución de dinero" + "ar" "كاش باك" + } + "BHOP" + { + "ru" "Банихоп" + "ua" "Банніхоп" + "en" "BunnyHop" + "pt" "BunnyHop" + "fi" "BunnyHop" + "es" "BunnyHop" + "ar" "بانيهوب" + } + "TF2_HalloweenFootprints" + { + "ru" "Следы под ногами (Хэллоуин)" + "en" "Footsteps (Halloween)" + "fi" "Fotspår (Halloween)" + "ua" "Сліди під ногами (Хеллоуїн)" + "es" "Pasos (Halloween)" + "ar" "خطى (هالوين)" + } + "TF2_DistortedHalloweenVoice" + { + "ru" "Искажённый голос (Хэллоуин)" + "en" "Distorted Voice (Halloween)" + "fi" "Förvrängd röst (Halloween)" + "ua" "Спотворений голос (Хеллоуїн)" + "es" "Voz distorsionada (Halloween)" + "ar" "صوت مشوه (الهالوين)" + } + "ShowTriggers" + { + "ru" "Показ триггеров" + "en" "Show Triggers" + "fi" "Visa triggers" + "ua" "Показати тригери" + "es" "Mostrar disparadores" + "ar" "إظهار المشغلات" + } + "color_management" + { + "ru" "Управление цветами" + "en" "Color Management" + "fi" "Färghantering" + "ua" "Керування кольорами" + "es" "Manejo del color" + "ar" "إدارة الألوان" + } + "AutoBandage" + { + "ru" "Авто бинтование" + "en" "Auto Bandage" + "fi" "Automaattinen Sidonta" + "ua" "Автоматичне бинтування" + "es" "Vendaje automático" + "ar" "الضمادة التلقائية" + } + "Shield" + { + "ru" "Щит" + "en" "Shield" + "fi" "Skydda" + "ua" "Щит" + "es" "Proteger" + "ar" "درع" + } + "ReWeapon" + { + "ru" "Восстановление боеприпасов" + "en" "Switch Weapon" + "fi" "Byt vapen" + "ua" "Відновлення боєприпасів" + "es" "Cambiar de arma" + "ar" "تبديل السلاح" + } + "FakeRanks" + { + "ru" "Фейк Ранг:" + "en" "Fake Ranks:" + "fi" "Fake Ranks:" + "ua" "Фейк Ранг:" + "es" "Fake Ranks:" + "ar" "الرتب الوهمية:" + } + + "FakeRanks_Menu" + { + "ru" "Меню Фейк-Ранга:" + "en" "Fake Ranks Menu:" + "fi" "Fake Ranks Menyn:" + "ua" "Меню Фейк Рангу:" + "es" "Menú FakeRanks:" + "ar" "قائمة الرتب الوهمية:" + } + "Instruments" + { + "ru" "Инструменты" + "en" "Instruments" + "fi" "instrument" + "ua" "Інструменти" + "es" "Instrumentos" + "ar" "الادوات" + } + "jump_effect" + { + "en" "Jump Effect" + "ru" "Эффект от прыжков" + "fi" "Hoppeffekt" + "ua" "Ефект від стрибків" + "es" "Efecto de salto" + "ar" "تأثير القفز" + } + "nade_wave_effect" + { + "en" "Nade Wave Effect" + "ru" "Волны от гранат" + "fi" "Nade våg effekt" + "ua" "Хвилі від гранат" + "es" "Efecto de onda Nade" + "ar" "تأثير موجة نادى" + } + "HALO" + { + "ru" "Нимб" + "en" "Halo" + "fi" "Halo" + "ua" "Німб" + "es" "Aureola" + "ar" "هالو" + } + "HALO_M" + { + "ru" "Цвет нимба" + "en" "Color Halo" + "fi" "Halo väri" + "ua" "Колір німбу" + "es" "Aureola de color" + "ar" "هالة ملونة" + } + "HALO_BLACK" + { + "ru" "Нимб Черный" + "en" "Halo Black" + "fi" "Halo Musta" + "ua" "Німб Чорний" + "es" "Aureola Svarti" + "ar" "هالو بلاك" + } + "VIP_SOUNDEFFECTS" + { + "ru" "Звуки" + "en" "Sounds" + "fi" "äänet" + "ua" "Звуки" + "es" "Suena" + "ar" "اصوات" + } + "NVD" + { + "ru" "ПНВ" + "en" "NVD" + "fi" "NVD" + "ua" "ПНБ" + "es" "NVD" + "ar" "NVG" + } + "Pets_Display" + { + "en" "Pets" + "ru" "Питомцы" + "fi" "Lemmikkieläimet" + "ua" "Вихованці" + "es" "Mascotas" + "ar" "الحيوانات الأليفة" + } + "Pets_Title" + { + "en" "Choosing Pet" + "ru" "Выбор питомца" + "fi" "Lemmikin valitseminen" + "ua" "Вибір вихованця" + "es" "Elegir una mascota" + "ar" "اختيار الحيوانات الأليفة" + } + "Pets_Disable" + { + "ru" "Выключить" + "en" "Disable" + "fi" "Poista käytöstä" + "ua" "Вимкнути" + "es" "Inhabilitar" + "ar" "تعطيل" + } + "bullet_effect" + { + "ru" "Эффект от выстрела" + "en" "Bullet Effect" + "fi" "Luodin vaikutus" + "ua" "Ефект від пострілу" + "es" "Efecto Bala" + "ar" "تأثير رصاصة" + } + "DuckSpeed" + { + "ru" "Отключить замедление скорости при приседании" + "en" "Disable slowdown when use duck" + "fi" "Poista hidastus käytöstä ankkaa käytettäessä" + "ua" "Відключити уповільнення швидкості під час присідання" + "es" "Desactiva la ralentización cuando usas pato" + "ar" "تعطيل التباطؤ عند استخدام البطة" + } + "vip_name_color" + { + "ru" "Цвет никнейма" + "en" "Client Username Color" + "fi" "Asiakkaan käyttäjänimen väri" + "ua" "Колір нікнейму" + "es" "Color del nombre de usuario del cliente" + "ar" "لون اسم مستخدم العميل" + } + "vip_message_color" + { + "ru" "Цвет сообщения" + "en" "Client Message Color" + "fi" "Asiakkaan viestin väri" + "ua" "Колір повідомлення" + "es" "Color del mensaje del cliente" + "ar" "لون رسالة العميل" + } + "vip_prefix" + { + "ru" "Префикс" + "en" "Client Prefix" + "fi" "Asiakkaan etuliite" + "ua" "Префікс" + "es" "Prefijo de cliente" + "ar" "بادئة العميل" + } + "vip_prefix_color" + { + "ru" "Цвет префикса" + "en" "Client Prefix Color" + "fi" "Asiakkaan etuliitteen väri" + "ua" "Колір префіксу" + "es" "Color del prefijo del cliente" + "ar" "لون بادئة العميل" + } + "vip_chat_nameco" + { + "ru" "Цвет никнейма" + "en" "Client Username Color" + "fi" "Asiakkaan käyttäjänimen väri" + "ua" "Колір нікнейму" + "es" "Color del nombre de usuario del cliente" + "ar" "لون اسم مستخدم العميل" + } + "vip_chat_msgco" + { + "ru" "Цвет сообщения" + "en" "Client Message Color" + "fi" "Asiakkaan viestin väri" + "ua" "Колір повідомлення" + "es" "Color del mensaje del cliente" + "ar" "لون رسالة العميل" + } + "vip_chat_prefix" + { + "ru" "Префикс" + "en" "Client Prefix" + "pt" "Prefixo do Client" + "fi" "Asiakkaan etuliite" + "ua" "Префікс" + "es" "Prefijo de cliente" + "ar" "بادئة العميل" + } + "vip_chat_prefixco" + { + "ru" "Цвет префикса" + "en" "Client Prefix Color" + "fi" "Asiakkaan etuliitteen väri" + "ua" "Колір префіксу" + "es" "Color del prefijo del cliente" + "ar" "لون بادئة العميل" + } + "advanced_message" + { + "ru" "Продвинутые сообщения" + "en" "Advanced Message" + "fi" "Lisäasetukset-viesti" + "ua" "Розширені повідомлення" + "es" "Mensaje avanzado" + "ar" "رسالة متقدمة" + } + "HintShowDamage" + { + "ru" "Отображение урона" + "en" "Damage Display" + "fi" "Skada display" + "ua" "Відображення пошкоджень" + "es" "Pantalla de daños" + "ar" "عرض الضرر" + } + "Rainbow_Player" + { + "ru" "Радужная модель" + "en" "Rainbow Pattern" + "fi" "Sateenkaarikuvio" + "ua" "Райдужна модель" + "es" "Padrão de arco-íris" + "ar" "نمط قوس قزح" + } + "RoundEndBhop" + { + "ru" "Банихоп в конце раунда" + "en" "BunnyHop at the end of the round" + "pt" "BunnyHop no final do round" + "fi" "BunnyHop kierroksen lopussa" + "ua" "Баніхоп у кінці раунду" + "es" "BunnyHop al final de la ronda" + "ar" "بانيهوب في نهاية الجولة" + } + "Xscope" + { + "ru" "Улучшенный снайперский прицел" + "en" "Advanced Sniper Sight" + "fi" "Advanced sniper näky" + "ua" "Покращений снайперський приціл" + "es" "Mira de francotirador avanzada" + "ar" "مشهد قناص متقدم" + } + "AmmoWeapons" + { + "ru" "Расширенная обойма" + "en" "Extended Clip" + "fi" "Laajennettu leike" + "ua" "Розширена обойма" + "es" "Clip extendido" + "ar" "كليب موسع" + } + "Invis" + { + "ru" "Прозрачность" + "en" "Invis" + "fi" "Läpikuultavuus" + "ua" "Прозорість" + "es" "Transparencia" + "ar" "إينفيس" + } + "Equipments" + { + "ru" "Экипировка" + "en" "Equipments" + "pt" "Equipamentos" + "fi" "Tamineet" + "ua" "Спорядження" + "es" "Transparencia" + "ar" "المعدات" + } + "EquipmentsMenu" + { + "ru" "Настройка экипировки" + "en" "Equipment Setting" + "en" "Configuração do Equipamento" + "fi" "Laitteiden asettaminen" + "ua" "Налаштування екіпіровки" + "es" "Configuración del equipo" + "ar" "إعداد المعدات" + } + "Icon" + { + "ru" "Иконка" + "en" "Icon" + "pt" "Ícone" + "fi" "Ikoni" + "ua" "Іконка" + "es" "Icono" + "ar" "أيقونة" + } + "NadeModels" + { + "ru" "Модели гранат" + "en" "Grenade Models" + "pt" "Modelos de Granada" + "fi" "Kranaatti malli" + "ua" "Моделі гранат" + "es" "Modelos granate" + "ar" "نماذج القنابل اليدوية" + } + "NadeModels_Menu" + { + "ru" "Настройка моделей гранат" + "en" "Grenade Models Settings" + "pt" "Configuração dos Modelos de Granada" + "fi" "Kranaatti mallin asetukset" + "ua" "Налаштування моделей гранат" + "es" "Configuración de modelos granadas" + "ar" "إعدادات نماذج القنبلة" + } + "NoDamageVelocity" + { + "ru" "Не терять скорость, когда в вас стреляют" + "en" "Don't lose speed when you are being shot at" + "fi" "Älä menetä nopeutta, kun olet ammuttu" + "ua" "Не втрачати швидкість, коли по вас стріляють" + "es" "No pierdas la velocidad cuando te disparan" + "ar" "لا تفقد السرعة عندما يتم إطلاق النار عليك" + } + "X16Walls" + { + "ru" "Прострел стен" + "en" "Wall Shot" + "fi" "Seinä laukaus" + "ua" "Простріл стін" + "es" "Muro de tiro" + "ar" "طلقة الجدار" + } + "ReloadProgressBar" + { + "ru" "Индикатор выполнения перезарядки" + "en" "Reload Progress Bar" + "fi" "Latauksen ilmaisin" + "ua" "Індикатор виконання перезарядки" + "es" "Indicador de progreso de recarga" + "ar" "إعادة تحميل شريط التقدم" + } + "Killcredits" + { + "ru" "Кредиты за убийство" + "en" "Credits for Kill" + "pt" "Créditos por Kill" + "fi" "Hyvitykset tappaa" + "ua" "Кредити за вбивство" + "es" "Créditos por matar" + "ar" "قروض للقتل" + } + "HEADcredits" + { + "ru" "Кредиты за HS" + "en" "Credits for HS" + "pt" "Créditos por HS" + "fi" "Hyvitykset HS: lle" + "ua" "Кредити за HS" + "es" "Créditos para HS" + "ar" "قروض لضربة رأس" + } + "KNIFEcredits" + { + "ru" "Кредиты за нож" + "en" "Credits for Knife" + "pt" "Créditos por Faca" + "fi" "Laajuus veitsi" + "ua" "Кредити за ніж" + "es" "Créditos para el cuchillo" + "ar" "قروض للسكين" + } + "two_guns" + { + "ru" "Два оружия в одном слоте" + "en" "Two weapons in one slot" + "fi" "Kaksi asetta yhdessä korttipaikassa" + "ua" "Дві зброї в одному слоті" + "es" "Dos armas en una ranura" + "ar" "سلاحان في فتحة واحدة" + } + "Fists" + { + "ru" "Кулаки" + "en" "Fists" + "pt" "Punhos" + "fi" "Nyrkki" + "ua" "Кулаки" + "es" "Puños" + "ar" "القبضات" + } + "mu_GiveLoots" + { + "ru" "Дополнительные улики" + "en" "Additional Evidence" + "pt" "Evidência Adicional" + "fi" "Lisätodisteet" + "ua" "Додаткові докази" + "es" "Evidencia adicional" + "ar" "أدلة إضافية" + } + "ColoredSmoke" + { + "ru" "Цветной дым" + "en" "Colored Smoke" + "pt" "Smoke Colorida" + "fi" "Värillinen Savu" + "ua" "Кольоровий дим" + "es" "Humo de color" + "ar" "دخان ملون" + } + "SmokeColor" + { + "ru" "Указать цвет" + "en" "Select Color" + "pt" "Selecione a Cor" + "fi" "Valitse väri" + "ua" "Вказати колір" + "es" "Seleccionar color" + "ar" "إختر لون" + } + "Ban" + { + "ru" "Забанить игрока" + "en" "Ban Player" + "pt" "Banir Player" + "fi" "Kieltää pelaaja" + "ua" "Забанити гравця" + "es" "Prohibir al jugador" + "ar" "حظر لاعب" + } + "Agents" + { + "ru" "Агенты" + "en" "Agents" + "pt" "Agentes" + "fi" "Agentti" + "ua" "Агенти" + "es" "Agente" + "ar" "عملاء" + } + "VIP_Agents_M" + { + "ru" "Меню Агентов" + "en" "Agents Menu" + "pt" "Menu Agentes" + "fi" "Agenttien Valikko" + "ua" "Меню Агентів" + "es" "Menú Agentes" + "ar" "قائمة الوكلاء" + } + "resimm" + { + "ru" "Иммунитет от ограничения оружия" + "en" "Weapon Restriction Immunity" + "fi" "Gun limit immunity" + "ua" "Імунітет від обмеження зброї" + "es" "Inmunidad de restricción de armas" + "ar" "حصانة تقييد السلاح" + } + "switch_speed" + { + "ru" "Быстрая смена оружия" + "en" "Quick change of weapons" + "fi" "Nopea aseiden vaihto" + "ua" "Швидка зміна зброї" + "es" "Cambio rápido de armas" + "ar" "تغيير سريع للأسلحة" + } + "Disable" + { + "ru" "Выключить" + "en" "Disable" + "pt" "Desabilitar" + "fi" "Sammuta" + "ua" "Вимкнути" + "es" "Inhabilitar" + "ar" "تعطيل" + } + "FOV" + { + "ru" "Угол обзора" + "en" "Viewing Angle | FOV" + "pt" "Ângulo de Visão | FOV" + "fi" "Katselukulma" + "ua" "Кут огляду" + "es" "Ángulo de visión" + "ar" "زاوية عرض" + } + "ChatSound" + { + "ru" "Звук чата" + "en" "Chat message sound" + "pt" "Som da mensagem do bate-papo" + "fi" "Keskusteluviestin ääni" + "ua" "Звук чату" + "es" "Chat mensaje sonido" + "ar" "رسالة الدردشة الصوتية" + } + "lr_select_fakerank" + { + "ru" "Выбрать ранги" + "en" "Select Ranks" + "pt" "Selecionar Ranks" + "fi" "Valitse rivit" + "ua" "Вибрати ранги" + "es" "Seleccionar rangos" + "ar" "اختر الرتب" + } + "VIP_NO_LIMIT" + { + "#format" "{1:s}" + "ru" "Лимит использования {1} исчерпан!" + "en" "The limit of use {1} has been reached!" + "pt" "O limite de uso {1} foi atingido!" + "fi" "Käyttöraja {1} on saavutettu!" + "ua" "Межа використання {1} досягнута!" + "es" "¡Se ha alcanzado el límite de uso {1}!" + "ar" "تم الوصول إلى حد الاستخدام {1}!" + } + "VIP_END_TIME_USE" + { + "#format" "{1:s}" + "ru" "Время действия {1} закончилось!" + "en" "Action time {1} is over!" + "pt" "O tempo de ação {1} acabou!" + "fi" "Toiminta-aika {1} on ohi!" + "ua" "Час дії {1} закінчився!" + "es" "¡El tiempo de acción {1} ha terminado!" + "ar" "وقت العمل {1} هو أكثر!" + } + "Setclass" + { + "ru" "Смена Классов" + "en" "Set Class" + "pt" "Definir Classe" + "fi" "Luokanvaihto" + "ua" "Зміна КЛАСІВ" + "es" "Cambio De Clases" + "ar" "تغيير الفصول" + } + "sprays" + { + "ru" "Спрей" + "en" "Spray" + "pt" "Spray" + "fi" "Spray" + "ua" "Спрей" + "es" "Spray" + "ar" "رذاذ" + } + "sprays_menu" + { + "ru" "Выбор спрея" + "en" "Spray selection" + "pt" "Selecção da pulverização" + "fi" "Spray valinta" + "ua" "Вибір спрею" + "es" "Selección de Spray" + "ar" "رذاذ اختيار" + } + "AntiKnifeDamage" + { + "ru" "Защита от урона ножом" + "en" "Protection from knife damage" + "pt" "Proteção contra danos com uma faca" + "fi" "Suojaa veitsi vahinko" + "ua" "Захист від шкоди ножем" + "es" "Protección contra daños con cuchillo" + "ar" "الحماية من تلف السكين" + } + "ClanCreate" + { + "ru" "Возможность создавать клан" + "en" "Clan create permission" + "pt" "Clã criar permissão" + "fi" "Klaanin luontioikeus" + "ua" "Дозвіл на створення клану" + "es" "Permiso para crear clan" + "ar" "عشيرة إنشاء إذن" + } +} diff --git a/updates.md b/updates.md index 7c9e8d8..f28b50b 100644 --- a/updates.md +++ b/updates.md @@ -1,291 +1,291 @@ -# [VIP] Core 3.0 DEV #41 - -### Изменения: -- Доработана формировка sql запросов для `USE_MORE_SERVERS`. - -# [VIP] Core 3.0 DEV #40 - -### Изменения: -- Изменен натив `VIP_RegisterFeature` (Добавлен параметр `bCookie` - Регистрировать ли куки для ф-и (действительно только для типа SELECTABLE)). - -# [VIP] Core 3.0 DEV #39 - -### Изменения: -- Исправлена ошибка на SQLite `SQL_Callback_RemoveClient: no such column: sid`. - -# [VIP] Core 3.0 DEV #38 - -### Изменения: -- Исправлена ошибка на SQLite `SQL_Callback_ErrorCheck: no such column: sid`. - -# [VIP] Core 3.0 DEV #37 - -### Изменения: -- Исправлена ошибка когда не удалялись истекшие VIP-игроки. - -# [VIP] Core 3.0 DEV #36 - -### Изменения: -- Теперь точно исправлена ошибка `Invalid Handle 0 (error 4)` (`DB_RemoveClientFromID`). - -# [VIP] Core 3.0 DEV #35 - -### Изменения: -- Исправлена ошибка `Invalid Handle 0 (error 4)` (`DB_RemoveClientFromID`). - -# [VIP] Core 3.0 DEV #34 - -### Изменения: -- Натив `VIP_RemoveClientVIP` помечен как устаревший и будет удален в будущем. -- Добавлен новый натив `VIP_RemoveClientVIP2`. -- Исправлена ошибка `Client index 0 is invalid` при добавлении VIP-игрока через `sm_addvip`. - -# [VIP] Core 3.0 DEV #33 - -### Изменения: -- Изменени натив `VIP_SetClientFeatureStatus`. -- Исправлена ошибка `SQL_Callback_SelectExpiredAndOutdated: Unknown column 'iTime' in 'where clause'`. -- Переработана система логов. - -# [VIP] Core 3.0 DEV #32 (спасибо DarklSide за обнаруженные ошибок) - -### Изменения: -- Исправлено описание форварда `VIP_OnClientPreLoad`. -- Исправлен натив `VIP_GiveClientFeature`. -- Исправлена ошибка `SQL_Callback_SelectExpiredAndOutdated: no such function: UNIX_TIMESTAMP` на SQLite. -- Исправлено обновление ников игроков. -- Исправлена работа DataPack на sm 1.10. - -# [VIP] Core 3.0 DEV #31 - -### Изменения: -- Добавлен форвард `VIP_OnClientPreLoad`. -- Исправлен и включен натив `VIP_GiveClientFeature`. -- Добавлен натив `VIP_RemoveClientFeature`. -- Изменен натив `VIP_CheckClient`. -- Значение по-умолчанию для `sm_vip_delete_outdated` изменено на `-1`. -- Исправлено удаление истекших и не активных игроков. - -# [VIP] Core 3.0 DEV #30 - -### Изменения: -- Исправлена работа списка VIP-игроков. - -# [VIP] Core 3.0 DEV #29 - -### Изменения: -- Исправлены SQL ошибки. -- Небольшая оптимизация. -- Исправлена отладка. - -# [VIP] Core 3.0 DEV #28 - -### Изменения: -- Исправлена ошибка из-за которой плагин не запускался `Fatal error creating dynamic native!/unexpected error 23 in AskPluginLoad callback`. -- Изменено имя базы данных с `"vip"` на `"vip_core"` (еще в версии 3.0 DEV #25, просто забыл упомянуть). -- Исправлена попытка установки кодировки для SQLite базы. - -# [VIP] Core 3.0 DEV #27 - -### Изменения: -- Активирована работа квара `sm_vip_delete_outdated`. -- Полный переход на новую структуру базы данных. - -# [VIP] Core 3.0 DEV #26 - -### Изменения: -- Вернул прежний вид натива VIP_SetClientVIP и пометил как устаревший. -- Добавлен новый натив `VIP_GiveClientVIP`, как замена устаревшему `VIP_SetClientVIP`. -- Добавлена обратная совместимость натива `VIP_RemoveClientVIP`. -- Изменента структура таблиц (MySQL). - -# [VIP] Core 3.0 DEV #25 - -### Изменения: -- Добавлено использование `TranslationPhraseExists` для предовтращения ошибок (При отсутствии фразы модуля в переводе). -- Начал переводить отладку более расширенный и гибкий вид. -- Добавлен квар `sm_vip_delete_outdated` (пока не функционирует). - -# [VIP] Core 3.0 DEV #24 - -### Изменения: -- Исправлены SQL ошибки. - -# [VIP] Core 3.0 DEV #23 - -### Изменения: -- Удален столбец `id` из базы данных. -- Исправлена ошибка `SQL_Callback_SelectVipClientInfo` . -- Исправлена ошибка `SQL_Callback_ChangeTime: Unknown column 'user_id' in 'where clause'`. - -# [VIP] Core 3.0 DEV #22 - -### Изменения: -- Попытка исправления ошибки `Invalid Handle 0 (error 4)`. -- Исправлена ошибка когда у no-steam игроков значение `account_id` в базе данных становилось равным 0. - -# [VIP] Core 3.0 DEV #21 - -### Изменения: -- Исправлена ошибка `SQL_Callback_SelectVipPlayers: near ")": syntax error`. - -# [VIP] Core 3.0 DEV #20 - -### Изменения: -- Исправлена ошибка когда значение `name` в базе данных становилось равным 0. - -# [VIP] Core 3.0 DEV #19 - -### Изменения: -- Добавлена мультиязычность кнопки `"Выход"` в инфо-меню. - -# [VIP] Core 3.0 DEV #18 - -### Изменения: -- Исправлена ошибка `SQL_Callback_SelectVipPlayers: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '0, 60' at line 1`. - -# [VIP] Core 3.0 DEV #17 - -### Изменения: -- Добавлено сохранение последнего входа игрока на сервер. - -# [VIP] Core 3.0 DEV #16 - -### Изменения: -- Теперь игроки хранятся в базе по AccountID. -- Исправлена работа команды `sm_addvip`: - - Теперь она может принимать: SteamID2, SteamID3 и ник. - - Исправлена выдача VIP-статуса оффлайн. - -# [VIP] Core 3.0 DEV #15 - -### Изменения: -- Добавлено исправление работы MOTD окна на CS:GO. -- Добавлена директива FIX_CSGO_MOTD, которая позволяет компилировать ядро с фиксом MOTD и без. - -# [VIP] Core 3.0 DEV #14 - -### Изменения: -- Обновлено API: - - Добавлен форвард `VIP_OnShowClientInfo`. - - Изменен натив `VIP_RemoveClientVIP`. - - Доработано и исправлено описание всех нативов и форвадов. -- Обновлен файл `addons/sourcemod/data/vip/cfg/info.ini` (Исправлено описание и опечатки, добавлен английский перевод). - -# [VIP] Core 3.0 DEV #13 - -### Изменения: -- Исправлена логическая ошибка в работе `VIP_UnregisterFeature` когда добавлялись лишние пункты в VIP-меню. -- Изменен натив `VIP_RegisterFeature` для устранения конфликта с `sm_vip_features_default_status`. -- Добавлен натив `VIP_UnregisterMe()`. - -# [VIP] Core 3.0 DEV #12 - -### Изменения: -- Исправлена ошибка `Invalid data pack handle 1f4 (error 1)`. - -# [VIP] Core 3.0 DEV #11 - -### Изменения: -- Удален натив `VIP_SetFeatureDefStatus`. -- Добавлен параметр в натив `VIP_RegisterFeature`: -``` -@param bDefStatus Значение по-умолчанию (true - Включена, false - Выключена). -``` -- Исправлена ошибка работы натива `VIP_SetClientFeatureStatus`. -- Исправлена ошибка при отправке rcon команд для работы с вип от имени сервера. - -# [VIP] Core 3.0 DEV #10 - -### Изменения: -- Исправлена ошибка `DataPack operation is out of bounds`. - -# [VIP] Core 3.0 DEV #9 - -### Изменения: -- Исправлены ошибки компиляции. - -# [VIP] Core 3.0 DEV #8 - -### Изменения: -- Добавлен параметр в натив `VIP_SetClientFeatureStatus`: -``` -@param bCallback Вызывать ли toggle каллбэк. -``` - -# [VIP] Core 3.0 DEV #7 - -### Изменения: -- Доработано меню управления VIP-игроками. -- Добавлены форварды: -``` -forward void VIP_OnFeatureRegistered(const char[] sFeatureName); -forward void VIP_OnFeatureUnregistered(const char[] sFeatureName); -``` -- Теперь `VIP_OnVIPClientLoaded` вызывается когда игрок уже полностью был загружен. -- Изменены нативы: -``` -native void VIP_GetClientID(int iClient); -// На -native int VIP_GetClientID(int iClient); - -native void VIP_SendClientVIPMenu(int iClient); -// На -native void VIP_SendClientVIPMenu(int iClient, bool bSelection = false); -``` -- Добавлены нативы: -``` -native VIP_FeatureType VIP_GetFeatureType(const char[] sFeatureName); -native VIP_ValueType VIP_GetFeatureValueType(const char[] sFeatureName); -native void VIP_SetFeatureDefStatus(const char[] sFeatureName, bool bStatus); -native int VIP_FillArrayByFeatures(ArrayList hArray); -``` - -# [VIP] Core 3.0 DEV #6 - -### Изменения: -- Исправлены ошибки. - -# [VIP] Core 3.0 DEV #5 - -### Изменения: -- Исправлены ошибки. - -# [VIP] Core 3.0 DEV #4 - -### Изменения: -- Полный перевод на новый синтаксис (`newdecls required`) -- VIP_OnVIPClientLoaded теперь вызывается когда полностью загружены настройки игрока. -- Изменен натив `VIP_SetClientVIP`. -- Удалены лишние фразы из файла перевода. - - -# [VIP] Core 3.0 DEV #3 - -### Изменения: -- Удалены все типы авторизации кроме AUTH_STEAM. -- Изменена команда sm_addvip. -- Временно отключены все функции админ-меню, кроме добавления VIP-игроков. - - -# [VIP] Core 3.0 DEV #2 - -### Изменения: -- Перевод на новый синтаксис файла vars.sp и частичный перевод других. -- Удалены пароли (из базы, запросов, api, cvars). - -# [VIP] Core 3.0 DEV #1 - -### Изменения: -- Корректная компиляция на см >= 1.7. -- Добавлена переменная time в информационных сообщениях типа меню. Отвечает за время отображения меню игроку. -- Исправлен сброс настроек при перезаходе в случае когда куки не успели загрузится. - -# [VIP] Core 3.0 DEV - -### Что изменено: -- Добавлена директива USE_ADMINMENU, которая позволяет компилировать ядро с админкой vip и без неё. -- Добавлен квар sm_vip_add_item_to_admin_menu - Добавить пункт "Управление VIP" в админ-меню. -- Добавлено отдельно меню администрирования VIP, команда для открытия sm_vipadmin, !vipadmin, /vipadmin. -- Удален частный форвард спавна игрока (и нативы хука/анхука). -- Теперь для работы требуется SM 1.5.0 или выше. +# [VIP] Core 3.0 DEV #41 + +### Изменения: +- Доработана формировка sql запросов для `USE_MORE_SERVERS`. + +# [VIP] Core 3.0 DEV #40 + +### Изменения: +- Изменен натив `VIP_RegisterFeature` (Добавлен параметр `bCookie` - Регистрировать ли куки для ф-и (действительно только для типа SELECTABLE)). + +# [VIP] Core 3.0 DEV #39 + +### Изменения: +- Исправлена ошибка на SQLite `SQL_Callback_RemoveClient: no such column: sid`. + +# [VIP] Core 3.0 DEV #38 + +### Изменения: +- Исправлена ошибка на SQLite `SQL_Callback_ErrorCheck: no such column: sid`. + +# [VIP] Core 3.0 DEV #37 + +### Изменения: +- Исправлена ошибка когда не удалялись истекшие VIP-игроки. + +# [VIP] Core 3.0 DEV #36 + +### Изменения: +- Теперь точно исправлена ошибка `Invalid Handle 0 (error 4)` (`DB_RemoveClientFromID`). + +# [VIP] Core 3.0 DEV #35 + +### Изменения: +- Исправлена ошибка `Invalid Handle 0 (error 4)` (`DB_RemoveClientFromID`). + +# [VIP] Core 3.0 DEV #34 + +### Изменения: +- Натив `VIP_RemoveClientVIP` помечен как устаревший и будет удален в будущем. +- Добавлен новый натив `VIP_RemoveClientVIP2`. +- Исправлена ошибка `Client index 0 is invalid` при добавлении VIP-игрока через `sm_addvip`. + +# [VIP] Core 3.0 DEV #33 + +### Изменения: +- Изменени натив `VIP_SetClientFeatureStatus`. +- Исправлена ошибка `SQL_Callback_SelectExpiredAndOutdated: Unknown column 'iTime' in 'where clause'`. +- Переработана система логов. + +# [VIP] Core 3.0 DEV #32 (спасибо DarklSide за обнаруженные ошибок) + +### Изменения: +- Исправлено описание форварда `VIP_OnClientPreLoad`. +- Исправлен натив `VIP_GiveClientFeature`. +- Исправлена ошибка `SQL_Callback_SelectExpiredAndOutdated: no such function: UNIX_TIMESTAMP` на SQLite. +- Исправлено обновление ников игроков. +- Исправлена работа DataPack на sm 1.10. + +# [VIP] Core 3.0 DEV #31 + +### Изменения: +- Добавлен форвард `VIP_OnClientPreLoad`. +- Исправлен и включен натив `VIP_GiveClientFeature`. +- Добавлен натив `VIP_RemoveClientFeature`. +- Изменен натив `VIP_CheckClient`. +- Значение по-умолчанию для `sm_vip_delete_outdated` изменено на `-1`. +- Исправлено удаление истекших и не активных игроков. + +# [VIP] Core 3.0 DEV #30 + +### Изменения: +- Исправлена работа списка VIP-игроков. + +# [VIP] Core 3.0 DEV #29 + +### Изменения: +- Исправлены SQL ошибки. +- Небольшая оптимизация. +- Исправлена отладка. + +# [VIP] Core 3.0 DEV #28 + +### Изменения: +- Исправлена ошибка из-за которой плагин не запускался `Fatal error creating dynamic native!/unexpected error 23 in AskPluginLoad callback`. +- Изменено имя базы данных с `"vip"` на `"vip_core"` (еще в версии 3.0 DEV #25, просто забыл упомянуть). +- Исправлена попытка установки кодировки для SQLite базы. + +# [VIP] Core 3.0 DEV #27 + +### Изменения: +- Активирована работа квара `sm_vip_delete_outdated`. +- Полный переход на новую структуру базы данных. + +# [VIP] Core 3.0 DEV #26 + +### Изменения: +- Вернул прежний вид натива VIP_SetClientVIP и пометил как устаревший. +- Добавлен новый натив `VIP_GiveClientVIP`, как замена устаревшему `VIP_SetClientVIP`. +- Добавлена обратная совместимость натива `VIP_RemoveClientVIP`. +- Изменента структура таблиц (MySQL). + +# [VIP] Core 3.0 DEV #25 + +### Изменения: +- Добавлено использование `TranslationPhraseExists` для предовтращения ошибок (При отсутствии фразы модуля в переводе). +- Начал переводить отладку более расширенный и гибкий вид. +- Добавлен квар `sm_vip_delete_outdated` (пока не функционирует). + +# [VIP] Core 3.0 DEV #24 + +### Изменения: +- Исправлены SQL ошибки. + +# [VIP] Core 3.0 DEV #23 + +### Изменения: +- Удален столбец `id` из базы данных. +- Исправлена ошибка `SQL_Callback_SelectVipClientInfo` . +- Исправлена ошибка `SQL_Callback_ChangeTime: Unknown column 'user_id' in 'where clause'`. + +# [VIP] Core 3.0 DEV #22 + +### Изменения: +- Попытка исправления ошибки `Invalid Handle 0 (error 4)`. +- Исправлена ошибка когда у no-steam игроков значение `account_id` в базе данных становилось равным 0. + +# [VIP] Core 3.0 DEV #21 + +### Изменения: +- Исправлена ошибка `SQL_Callback_SelectVipPlayers: near ")": syntax error`. + +# [VIP] Core 3.0 DEV #20 + +### Изменения: +- Исправлена ошибка когда значение `name` в базе данных становилось равным 0. + +# [VIP] Core 3.0 DEV #19 + +### Изменения: +- Добавлена мультиязычность кнопки `"Выход"` в инфо-меню. + +# [VIP] Core 3.0 DEV #18 + +### Изменения: +- Исправлена ошибка `SQL_Callback_SelectVipPlayers: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '0, 60' at line 1`. + +# [VIP] Core 3.0 DEV #17 + +### Изменения: +- Добавлено сохранение последнего входа игрока на сервер. + +# [VIP] Core 3.0 DEV #16 + +### Изменения: +- Теперь игроки хранятся в базе по AccountID. +- Исправлена работа команды `sm_addvip`: + - Теперь она может принимать: SteamID2, SteamID3 и ник. + - Исправлена выдача VIP-статуса оффлайн. + +# [VIP] Core 3.0 DEV #15 + +### Изменения: +- Добавлено исправление работы MOTD окна на CS:GO. +- Добавлена директива FIX_CSGO_MOTD, которая позволяет компилировать ядро с фиксом MOTD и без. + +# [VIP] Core 3.0 DEV #14 + +### Изменения: +- Обновлено API: + - Добавлен форвард `VIP_OnShowClientInfo`. + - Изменен натив `VIP_RemoveClientVIP`. + - Доработано и исправлено описание всех нативов и форвадов. +- Обновлен файл `addons/sourcemod/data/vip/cfg/info.ini` (Исправлено описание и опечатки, добавлен английский перевод). + +# [VIP] Core 3.0 DEV #13 + +### Изменения: +- Исправлена логическая ошибка в работе `VIP_UnregisterFeature` когда добавлялись лишние пункты в VIP-меню. +- Изменен натив `VIP_RegisterFeature` для устранения конфликта с `sm_vip_features_default_status`. +- Добавлен натив `VIP_UnregisterMe()`. + +# [VIP] Core 3.0 DEV #12 + +### Изменения: +- Исправлена ошибка `Invalid data pack handle 1f4 (error 1)`. + +# [VIP] Core 3.0 DEV #11 + +### Изменения: +- Удален натив `VIP_SetFeatureDefStatus`. +- Добавлен параметр в натив `VIP_RegisterFeature`: +``` +@param bDefStatus Значение по-умолчанию (true - Включена, false - Выключена). +``` +- Исправлена ошибка работы натива `VIP_SetClientFeatureStatus`. +- Исправлена ошибка при отправке rcon команд для работы с вип от имени сервера. + +# [VIP] Core 3.0 DEV #10 + +### Изменения: +- Исправлена ошибка `DataPack operation is out of bounds`. + +# [VIP] Core 3.0 DEV #9 + +### Изменения: +- Исправлены ошибки компиляции. + +# [VIP] Core 3.0 DEV #8 + +### Изменения: +- Добавлен параметр в натив `VIP_SetClientFeatureStatus`: +``` +@param bCallback Вызывать ли toggle каллбэк. +``` + +# [VIP] Core 3.0 DEV #7 + +### Изменения: +- Доработано меню управления VIP-игроками. +- Добавлены форварды: +``` +forward void VIP_OnFeatureRegistered(const char[] sFeatureName); +forward void VIP_OnFeatureUnregistered(const char[] sFeatureName); +``` +- Теперь `VIP_OnVIPClientLoaded` вызывается когда игрок уже полностью был загружен. +- Изменены нативы: +``` +native void VIP_GetClientID(int iClient); +// На +native int VIP_GetClientID(int iClient); + +native void VIP_SendClientVIPMenu(int iClient); +// На +native void VIP_SendClientVIPMenu(int iClient, bool bSelection = false); +``` +- Добавлены нативы: +``` +native VIP_FeatureType VIP_GetFeatureType(const char[] sFeatureName); +native VIP_ValueType VIP_GetFeatureValueType(const char[] sFeatureName); +native void VIP_SetFeatureDefStatus(const char[] sFeatureName, bool bStatus); +native int VIP_FillArrayByFeatures(ArrayList hArray); +``` + +# [VIP] Core 3.0 DEV #6 + +### Изменения: +- Исправлены ошибки. + +# [VIP] Core 3.0 DEV #5 + +### Изменения: +- Исправлены ошибки. + +# [VIP] Core 3.0 DEV #4 + +### Изменения: +- Полный перевод на новый синтаксис (`newdecls required`) +- VIP_OnVIPClientLoaded теперь вызывается когда полностью загружены настройки игрока. +- Изменен натив `VIP_SetClientVIP`. +- Удалены лишние фразы из файла перевода. + + +# [VIP] Core 3.0 DEV #3 + +### Изменения: +- Удалены все типы авторизации кроме AUTH_STEAM. +- Изменена команда sm_addvip. +- Временно отключены все функции админ-меню, кроме добавления VIP-игроков. + + +# [VIP] Core 3.0 DEV #2 + +### Изменения: +- Перевод на новый синтаксис файла vars.sp и частичный перевод других. +- Удалены пароли (из базы, запросов, api, cvars). + +# [VIP] Core 3.0 DEV #1 + +### Изменения: +- Корректная компиляция на см >= 1.7. +- Добавлена переменная time в информационных сообщениях типа меню. Отвечает за время отображения меню игроку. +- Исправлен сброс настроек при перезаходе в случае когда куки не успели загрузится. + +# [VIP] Core 3.0 DEV + +### Что изменено: +- Добавлена директива USE_ADMINMENU, которая позволяет компилировать ядро с админкой vip и без неё. +- Добавлен квар sm_vip_add_item_to_admin_menu - Добавить пункт "Управление VIP" в админ-меню. +- Добавлено отдельно меню администрирования VIP, команда для открытия sm_vipadmin, !vipadmin, /vipadmin. +- Удален частный форвард спавна игрока (и нативы хука/анхука). +- Теперь для работы требуется SM 1.5.0 или выше.