From 4ad21efbda89a73daab8e1fb5b06604985a40936 Mon Sep 17 00:00:00 2001 From: R1KO Date: Sat, 10 Oct 2026 16:20:14 +0300 Subject: [PATCH 1/6] Add AGENTS.md with build, layout, API and database guidance for coding agents. --- AGENTS.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..62d6676 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,41 @@ +# AGENTS.md + +[VIP] Core — SourceMod (SourcePawn) plugin that grants players VIP status (timed or permanent). Extended by separate "module" plugins via natives/forwards. No tests; correctness = clean compile. + +## Build +- CI (`.github/workflows/build.yml`) compiles against SM 1.11 (must pass) and 1.12 (allowed to fail): + `cd addons/sourcemod/scripting && mkdir -p ../plugins && spcomp VIP_Core.sp -E -o ../plugins/VIP_Core.smx -i./include` +- `-E` = warnings are errors. Fix every warning, including deprecation ones. +- Alt: `sourceknight build` (`sourceknight.yaml`, SM 1.13). Neither tool is installed locally by default. +- Never commit `*.smx`, `plugins/`, `.sourceknight`. + +## Layout +- `scripting/VIP_Core.sp` — entry point; `#include`s every `vip/*.sp` into ONE compilation unit (order matters: Global → Debug → ... → API → CMD → Clients). New file = add `#include` there. +- Compile-time switches in `VIP_Core.sp`: `DEBUG_MODE`, `USE_ADMINMENU` (gates `AdminMenu.sp` + `vip/adminmenu/*`), `USE_MORE_SERVERS`. +- `vip/Global.sp` — all globals, macros (`SZF`, `UID/CID`, `SET_BIT/UNSET_BIT`), client flag bits (`IS_VIP`, `IS_LOADED`, ...), core state bits (`IS_STARTED`, `IS_MySQL`, `IS_LOADING`). +- `vip/API.sp` — forwards (`API_SetupForwards`, `CreateForward_*`) and natives (`AskPluginLoad2`, `RegNative(X)` → `VIP_X` bound to `Native_X`). +- `vip/Database.sp` — connection, schema, queries. `UTIL.sp` — shared helpers; check here before writing new ones. +- `vip/Clients.sp` — client load/unload lifecycle, spawn handling. `Features.sp` — feature registry. +- `include/vip_core.inc` — PUBLIC API for third-party modules. +- `data/vip/cfg/*.ini` — runtime configs (groups, info, sort_menu, times). `translations/vip_core.phrases.txt` — all user-facing strings. + +## Public API rules (vip_core.inc) +- Third-party modules depend on it: never change existing native/forward signatures or semantics. Add new params with defaults or add new natives. +- New native: implement `Native_X` in API.sp, `RegNative(X)` in `AskPluginLoad2`, declare in `.inc`, add `MarkNativeAsOptional("VIP_X")` in `__pl_vip_core_SetNTVOptional`. +- New forward: `CreateGlobalForward` in `API_SetupForwards` + `CreateForward_*` wrapper + `forward` declaration with doc comment in `.inc`. +- Deprecate via `#pragma deprecated`, don't delete (see `update_modules.md`). + +## Database +- Config name `vip_core` in databases.cfg → MySQL; otherwise falls back to local SQLite. Every query must work on BOTH. +- Schemas differ: MySQL has `sid` column (PK `account_id, sid`), SQLite has no `sid`. Use `g_szSID` (empty on SQLite) when filtering by server. +- MySQL uses `utf8mb4`. Escape user strings with `g_hDatabase.Escape`. All queries are async (`g_hDatabase.Query` + callback); check `szError` in callbacks. +- Players are keyed by `account_id` (SteamID AccountID), not userid/client index. + +## Conventions +- `#pragma semicolon 1`, `#pragma newdecls required`. Tabs. Hungarian-ish names: `iClient`, `szName`, `hMenu`, `bFlag`, globals `g_*`, cvars `g_CVAR_*`. +- Pass client across async boundaries as userid (`UID`/`CID`), never raw index. +- `GLOBAL_TRIE` / `GLOBAL_INFO` are index `[0]` of per-client arrays — slot 0 = global, not a player. +- Debug logging via `DBG_*` macros from `Debug.sp` (no-ops unless `DEBUG_MODE 1`). +- Any new user-facing text → new phrase in `vip_core.phrases.txt` with at least `en` and `ru`. +- Comments and cvar descriptions are mostly Russian; either language is fine, keep the surrounding style. +- Bump `VIP_VERSION` in `VIP_Core.sp` only when asked; changelog lives in `updates.md`. From 0e1ac9bc148753fda99055c338e8a4ca898ec9a3 Mon Sep 17 00:00:00 2001 From: R1KO Date: Sat, 10 Oct 2026 16:41:18 +0300 Subject: [PATCH 2/6] Fix compilation on SourceMod 1.10 by adding missing semicolons after debug calls. Add SM 1.10 to the CI build matrix to catch such regressions. --- .github/workflows/build.yml | 2 +- addons/sourcemod/scripting/vip/AdminMenu.sp | 4 +-- addons/sourcemod/scripting/vip/Clients.sp | 2 +- addons/sourcemod/scripting/vip/Database.sp | 2 +- addons/sourcemod/scripting/vip/VipMenu.sp | 36 +++++++++---------- .../sourcemod/scripting/vip/adminmenu/List.sp | 32 ++++++++--------- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a65ad3a..f1ef3c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: continue-on-error: ${{ matrix.sm-version == '1.12.x' }} strategy: matrix: - sm-version: [ '1.11.x', '1.12.x' ] + sm-version: [ '1.10.x', '1.11.x', '1.12.x' ] name: "Build SM ${{ matrix.sm-version }}" steps: diff --git a/addons/sourcemod/scripting/vip/AdminMenu.sp b/addons/sourcemod/scripting/vip/AdminMenu.sp index 926125c..d9b59db 100644 --- a/addons/sourcemod/scripting/vip/AdminMenu.sp +++ b/addons/sourcemod/scripting/vip/AdminMenu.sp @@ -601,14 +601,14 @@ public int MenuHandler_GroupsList(Menu hMenu, MenuAction action, int iClient, in public void SQL_Callback_ChangeTime(Database hOwner, DBResultSet hResult, const char[] szError, any UserID) { - DBG_SQL_Response("SQL_Callback_ChangeTime") + DBG_SQL_Response("SQL_Callback_ChangeTime"); if (szError[0]) { LogError("SQL_Callback_ChangeTime: %s", szError); return; } - DBG_SQL_Response("hResult.AffectedRows = %d", hResult.AffectedRows) + DBG_SQL_Response("hResult.AffectedRows = %d", hResult.AffectedRows); if (hResult.AffectedRows) { diff --git a/addons/sourcemod/scripting/vip/Clients.sp b/addons/sourcemod/scripting/vip/Clients.sp index 7cf5d01..431eb6b 100644 --- a/addons/sourcemod/scripting/vip/Clients.sp +++ b/addons/sourcemod/scripting/vip/Clients.sp @@ -319,7 +319,7 @@ void Clients_LoadVIPFeature(int iClient, const char[] szFeature) } } /* - DBG_Clients("Clients_OnVIPClientLoaded: %d %N", iClient, iClient) + DBG_Clients("Clients_OnVIPClientLoaded: %d %N", iClient, iClient); Clients_OnVIPClientLoaded(iClient); */ diff --git a/addons/sourcemod/scripting/vip/Database.sp b/addons/sourcemod/scripting/vip/Database.sp index 10e4a29..267dcdb 100644 --- a/addons/sourcemod/scripting/vip/Database.sp +++ b/addons/sourcemod/scripting/vip/Database.sp @@ -200,7 +200,7 @@ void DB_RemoveClientFromID(int iAdmin = 0, const char[] szSourceGroup = NULL_STRING, const char[] szByWho = NULL_STRING) { - DebugMessage("DB_RemoveClientFromID %N (%d): - > iClientID: %d, : bNotify: %b", iClient, iClient, iClientID, bNotify) + DebugMessage("DB_RemoveClientFromID %N (%d): - > iClientID: %d, : bNotify: %b", iClient, iClient, iClientID, bNotify); char szQuery[256], szName[MNL], szGroup[64]; DataPack hDataPack = new DataPack(); diff --git a/addons/sourcemod/scripting/vip/VipMenu.sp b/addons/sourcemod/scripting/vip/VipMenu.sp index 58b0a74..e481d00 100644 --- a/addons/sourcemod/scripting/vip/VipMenu.sp +++ b/addons/sourcemod/scripting/vip/VipMenu.sp @@ -7,7 +7,7 @@ void VIPMenu_Setup() void AddFeatureToVIPMenu(const char[] szFeature) { - DebugMessage("AddFeatureToVIPMenu: %s", szFeature) + DebugMessage("AddFeatureToVIPMenu: %s", szFeature); if (g_hSortArray != null) { ResortFeaturesArray(); @@ -23,21 +23,21 @@ void AddFeatureToVIPMenu(const char[] szFeature) g_hFeaturesArray.GetString(i, SZF(szItemInfo)); if (GLOBAL_TRIE.GetValue(szItemInfo, hArray) && view_as(hArray.Get(FEATURES_ITEM_TYPE)) != HIDE) { - DebugMessage("AddMenuItem: %s", szItemInfo) + DebugMessage("AddMenuItem: %s", szItemInfo); g_hVIPMenu.AddItem(szItemInfo, szItemInfo); } } } else { - DebugMessage("AddMenuItem") + DebugMessage("AddMenuItem"); g_hVIPMenu.AddItem(szFeature, szFeature); } } void ResortFeaturesArray() { - DebugMessage("ResortFeaturesArray\n \n ") + DebugMessage("ResortFeaturesArray\n \n "); if ((g_hFeaturesArray).Length < 2) { @@ -57,14 +57,14 @@ void ResortFeaturesArray() for (i = 0; i < iSize; ++i) { g_hSortArray.GetString(i, SZF(szItemInfo)); - DebugMessage("GetSortArrayString: %s (i: %i, x: %i)", szItemInfo, i, x) + DebugMessage("GetSortArrayString: %s (i: %i, x: %i)", szItemInfo, i, x); index = g_hFeaturesArray.FindString(szItemInfo); - DebugMessage("FindStringInGlobalArray: index: %i", index) + DebugMessage("FindStringInGlobalArray: index: %i", index); if (index != -1) { if (index != x) { - DebugMessage("SwapArrayItems") + DebugMessage("SwapArrayItems"); g_hFeaturesArray.SwapAt(index, x); /*#if DEBUG_MODE PrintArray(g_hFeaturesArray); @@ -79,7 +79,7 @@ void ResortFeaturesArray() #if DEBUG_MODE stock void PrintArray(ArrayList &hArray) { - DebugMessage("PrintArray") + DebugMessage("PrintArray"); int i, iSize; iSize = hArray.Length; if (iSize) @@ -88,7 +88,7 @@ stock void PrintArray(ArrayList &hArray) for (i = 0; i < iSize; ++i) { hArray.GetString(i, SZF(szItemInfo)); - DebugMessage("%i: %s", i, szItemInfo) + DebugMessage("%i: %s", i, szItemInfo); } } } @@ -136,7 +136,7 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti g_hFeatures[iClient].Remove(KEY_MENUITEM); - DebugMessage("MenuAction_Display: Client: %i", iClient) + DebugMessage("MenuAction_Display: Client: %i", iClient); char szTitle[256]; int iExp; if (g_hFeatures[iClient].GetValue(KEY_EXPIRES, iExp) && iExp > 0) @@ -169,14 +169,14 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti int iStyle; g_hVIPMenu.GetItem(iOption, SZF(szItemInfo), iStyle); - DebugMessage("MenuAction_DrawItem: Client: %i, Feature: %s, iStyle: %i", iClient, szItemInfo, iStyle) + DebugMessage("MenuAction_DrawItem: Client: %i, Feature: %s, iStyle: %i", iClient, szItemInfo, iStyle); if (GLOBAL_TRIE.GetValue(szItemInfo, hBuffer)) { if (view_as(hBuffer.Get(FEATURES_VALUE_TYPE)) != VIP_NULL && Features_GetStatus(iClient, szItemInfo) == NO_ACCESS) { iStyle = g_CVAR_bHideNoAccessItems ? ITEMDRAW_RAWLINE:ITEMDRAW_DISABLED; - DebugMessage("NO_ACCESS -> iStyle: %i", iStyle) + DebugMessage("NO_ACCESS -> iStyle: %i", iStyle); } DataPack hDataPack = hBuffer.Get(FEATURES_MENU_CALLBACKS); @@ -190,11 +190,11 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti Call_PushString(szItemInfo); Call_PushCell(iStyle); Call_Finish(iStyle); - DebugMessage("Function_Draw -> iStyle: %i", iStyle) + DebugMessage("Function_Draw -> iStyle: %i", iStyle); } } - DebugMessage("return iStyle: %i", iStyle) + DebugMessage("return iStyle: %i", iStyle); return iStyle; } @@ -203,7 +203,7 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti { g_hVIPMenu.GetItem(iOption, SZF(szItemInfo)); - DebugMessage("MenuAction_DisplayItem: Client: %i, Feature: %s", iClient, szItemInfo) + DebugMessage("MenuAction_DisplayItem: Client: %i, Feature: %s", iClient, szItemInfo); char szDisplay[128]; @@ -225,7 +225,7 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti Call_PushCell(sizeof(szDisplay)); Call_Finish(bResult); - DebugMessage("Function_Display: bResult: %b", bResult) + DebugMessage("Function_Display: bResult: %b", bResult); if (bResult) { @@ -274,7 +274,7 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti if (GLOBAL_TRIE.GetValue(szItemInfo, hBuffer)) { PlaySound(iClient, ITEM_TOGGLE_SOUND); - DebugMessage("MenuAction_Select: Client: %i, Feature: %s", iClient, szItemInfo) + DebugMessage("MenuAction_Select: Client: %i, Feature: %s", iClient, szItemInfo); DataPack hDataPack = view_as(hBuffer.Get(FEATURES_MENU_CALLBACKS)); hDataPack.Position = ITEM_SELECT; @@ -321,7 +321,7 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti bool IsValidFeature(const char[] szFeature) { - DebugMessage("IsValidFeature:: FindStringInArray -> %i", g_hFeaturesArray.FindString(szFeature)) + DebugMessage("IsValidFeature:: FindStringInArray -> %i", g_hFeaturesArray.FindString(szFeature)); return (g_hFeaturesArray.FindString(szFeature) != -1); } diff --git a/addons/sourcemod/scripting/vip/adminmenu/List.sp b/addons/sourcemod/scripting/vip/adminmenu/List.sp index b1b60a3..34fff2b 100644 --- a/addons/sourcemod/scripting/vip/adminmenu/List.sp +++ b/addons/sourcemod/scripting/vip/adminmenu/List.sp @@ -238,14 +238,14 @@ void ShowVipPlayersFromDBMenu(int iClient, int iOffset = 0) } } - DBG_SQL_Query(szQuery) + DBG_SQL_Query(szQuery); g_hDatabase.Query(SQL_Callback_SelectVipPlayers, szQuery, UID(iClient)); } public void SQL_Callback_SelectVipPlayers(Database hOwner, DBResultSet hResult, const char[] szError, any UserID) { - DBG_SQL_Response("SQL_Callback_SelectVipPlayers") + DBG_SQL_Response("SQL_Callback_SelectVipPlayers"); if (hResult == null || szError[0]) { LogError("SQL_Callback_SelectVipPlayers: %s", szError); @@ -269,7 +269,7 @@ public void SQL_Callback_SelectVipPlayers(Database hOwner, DBResultSet hResult, hMenu.SetTitle("%T:\n ", "MENU_LIST_VIP", iClient); } - DBG_SQL_Response("hResult.RowCount = %d", hResult.RowCount) + DBG_SQL_Response("hResult.RowCount = %d", hResult.RowCount); if (!hResult.RowCount) { @@ -283,12 +283,12 @@ public void SQL_Callback_SelectVipPlayers(Database hOwner, DBResultSet hResult, int iClientID; while (hResult.FetchRow()) { - DBG_SQL_Response("hResult.FetchRow()") + DBG_SQL_Response("hResult.FetchRow()"); iClientID = hResult.FetchInt(0); IntToString(iClientID, SZF(szUserID)); hResult.FetchString(1, SZF(szName)); - DBG_SQL_Response("hResult.FetchInt(0) = %d", iClientID) - DBG_SQL_Response("hResult.FetchString(1) = '%s", szName) + DBG_SQL_Response("hResult.FetchInt(0) = %d", iClientID); + DBG_SQL_Response("hResult.FetchString(1) = '%s", szName); if(IsClientOnline(iClientID)) { @@ -321,13 +321,13 @@ void ShowTargetInfo(int iClient) WHERE `account_id` = %d%s LIMIT 1;", iClientID, g_szSID); - DBG_SQL_Query(szQuery) + DBG_SQL_Query(szQuery); g_hDatabase.Query(SQL_Callback_SelectVipClientInfo, szQuery, UID(iClient)); } public void SQL_Callback_SelectVipClientInfo(Database hOwner, DBResultSet hResult, const char[] szError, any UserID) { - DBG_SQL_Response("SQL_Callback_SelectVipClientInfo") + DBG_SQL_Response("SQL_Callback_SelectVipClientInfo"); if (hResult == null || szError[0]) { LogError("SQL_Callback_SelectVipClientInfo: %s", szError); @@ -344,25 +344,25 @@ public void SQL_Callback_SelectVipClientInfo(Database hOwner, DBResultSet hResul return; } - DBG_SQL_Response("hResult.FetchRow()") + DBG_SQL_Response("hResult.FetchRow()"); char szGroup[64], szName[32]; hResult.FetchString(0, SZF(szGroup)); // GROUP g_hClientData[iClient].SetString(DATA_KEY_Group, szGroup); - DBG_SQL_Response("hResult.FetchString(0) = '%s", szGroup) + DBG_SQL_Response("hResult.FetchString(0) = '%s", szGroup); int iExpires = hResult.FetchInt(1); // Expires g_hClientData[iClient].SetValue(DATA_KEY_Time, iExpires); - DBG_SQL_Response("hResult.FetchInt(1) = %d", iExpires) + DBG_SQL_Response("hResult.FetchInt(1) = %d", iExpires); hResult.FetchString(2, SZF(szName)); // Name g_hClientData[iClient].SetString(DATA_KEY_Name, szName); - DBG_SQL_Response("hResult.FetchString(2) = '%s", szName) + DBG_SQL_Response("hResult.FetchString(2) = '%s", szName); int iAccountID = hResult.FetchInt(3); // Auth g_hClientData[iClient].SetValue(DATA_KEY_Auth, iAccountID); - DBG_SQL_Response("hResult.FetchInt(3) = %d", iAccountID) + DBG_SQL_Response("hResult.FetchInt(3) = %d", iAccountID); ShowTargetInfoMenu(iClient); } @@ -403,7 +403,7 @@ void ShowTargetInfoMenu(int iClient) g_hClientData[iClient].GetString(DATA_KEY_Name, SZF(szName)); int iExpires, iClientID, iAccountID; g_hClientData[iClient].GetValue(DATA_KEY_Time, iExpires); - DebugMessage("GetValue(%s) = %d", DATA_KEY_Time, iExpires) + DebugMessage("GetValue(%s) = %d", DATA_KEY_Time, iExpires); g_hClientData[iClient].GetValue(DATA_KEY_TargetID, iClientID); g_hClientData[iClient].GetValue(DATA_KEY_Auth, iAccountID); UTIL_GetSteamIDFromAccountID(iAccountID, SZF(szAuth)); @@ -457,10 +457,10 @@ public int MenuHandler_VipClientInfoMenu(Menu hMenu, MenuAction action, int iCli { if (iItem == MenuCancel_ExitBack) { - DebugMessage("MenuHandler_VipClientInfoMenu->MenuCancel_ExitBack") + DebugMessage("MenuHandler_VipClientInfoMenu->MenuCancel_ExitBack"); AdminMenuItem eItem; g_hClientData[iClient].GetValue(DATA_KEY_MenuListType, eItem); - DebugMessage("GetValue(%s) = %d", DATA_KEY_MenuListType, eItem) + DebugMessage("GetValue(%s) = %d", DATA_KEY_MenuListType, eItem); switch(eItem) { case MENU_TYPE_ONLINE_LIST: From 576c12267188eb3e5d408a6bcec9881fb9f5846c Mon Sep 17 00:00:00 2001 From: R1KO Date: Sat, 10 Oct 2026 16:47:30 +0300 Subject: [PATCH 3/6] Normalize line endings to LF across the repository. Add eol=lf to .gitattributes so CRLF does not come back. --- .gitattributes | 3 +- addons/sourcemod/scripting/vip/Clients.sp | 1144 ++++++++++---------- addons/sourcemod/scripting/vip/Database.sp | 780 ++++++------- updates.md | 582 +++++----- 4 files changed, 1255 insertions(+), 1254 deletions(-) diff --git a/.gitattributes b/.gitattributes index 4868048..e672eb2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ +* text=auto eol=lf *.sp gitlab-language=cpp -*.inc gitlab-language=cpp \ No newline at end of file +*.inc gitlab-language=cpp diff --git a/addons/sourcemod/scripting/vip/Clients.sp b/addons/sourcemod/scripting/vip/Clients.sp index 431eb6b..d2d0c80 100644 --- a/addons/sourcemod/scripting/vip/Clients.sp +++ b/addons/sourcemod/scripting/vip/Clients.sp @@ -1,574 +1,574 @@ -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; - DBG_Clients("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); - // DBG_Clients("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); - - DBG_Clients("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 - 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; - - DBG_Clients("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); - } - - DBG_Clients("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 -public void OnClientCookiesCached(int iClient) -{ - DBG_Clients("OnClientCookiesCached %d %N", iClient, iClient); - - DBG_Clients("AreClientCookiesCached %b", AreClientCookiesCached(iClient)); -} -#endif - -void Clients_LoadVIPFeaturesPre(int iClient, const char[] szFeature = NULL_STRING) -{ - DBG_Clients("Clients_LoadVIPFeaturesPre %N", iClient); - - DBG_Clients("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()); - - DBG_Clients("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) -{ - DBG_Clients("LoadVIPFeatures %N", iClient); - - int iFeatures = g_hFeaturesArray.Length; - DBG_Clients("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); - } - } - } - - DBG_Clients("Clients_OnVIPClientLoaded: %d %N", iClient, iClient); - - Clients_OnVIPClientLoaded(iClient); -} - -void Clients_LoadVIPFeature(int iClient, const char[] szFeature) -{ - DBG_Clients("LoadVIPFeature %N", iClient); - - int iFeatures = g_hFeaturesArray.Length; - DBG_Clients("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); - } - } -/* +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; + DBG_Clients("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); + // DBG_Clients("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); + + DBG_Clients("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 + 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; + + DBG_Clients("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); + } + + DBG_Clients("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 +public void OnClientCookiesCached(int iClient) +{ + DBG_Clients("OnClientCookiesCached %d %N", iClient, iClient); + + DBG_Clients("AreClientCookiesCached %b", AreClientCookiesCached(iClient)); +} +#endif + +void Clients_LoadVIPFeaturesPre(int iClient, const char[] szFeature = NULL_STRING) +{ + DBG_Clients("Clients_LoadVIPFeaturesPre %N", iClient); + + DBG_Clients("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()); + + DBG_Clients("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) +{ + DBG_Clients("LoadVIPFeatures %N", iClient); + + int iFeatures = g_hFeaturesArray.Length; + DBG_Clients("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); + } + } + } + DBG_Clients("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; - - DBG_Clients("LoadClientFeature: %s", szFeature); - - if (!GetValue(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) - { - static char szBuffer[4]; - static Handle hCookie; - hCookie = view_as(hArray.Get(FEATURES_COOKIE)); - GetClientCookie(iClient, hCookie, SZF(szBuffer)); - eStatus = view_as(StringToInt(szBuffer)); - DBG_Clients("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) -{ - DBG_Clients("GetValue: %d - %s", ValueType, szFeature); - switch (ValueType) - { - case VIP_NULL: - { - return false; - } - 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[256]; - g_hGroups.GetString(szFeature, SZF(szBuffer)); - if (szBuffer[0]) - { - DBG_Clients("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); - DBG_Clients("Clients_CreateExpiredTimer %N (%d):\tiTimeLeft: %d", iClient, iClient, iTimeLeft); - if (iTimeLeft > 0) - { - DBG_Clients("Clients_CreateExpiredTimer %N (%d):\tiTimeLeft+iTime: %d", iClient, iClient, iTimeLeft + iTime); - if ((iTimeLeft + iTime) > iExp) - { - DBG_Clients("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); - 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 (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) -{ - 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 ((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) -{ - DBG_Clients("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()) - { - DBG_Clients("Timer_VIP_Expired %N:", iClient); - - Clients_ExpiredClient(iClient); - } - } - - return Plugin_Continue; -} - -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) - { - 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"); + + Clients_OnVIPClientLoaded(iClient); +} + +void Clients_LoadVIPFeature(int iClient, const char[] szFeature) +{ + DBG_Clients("LoadVIPFeature %N", iClient); + + int iFeatures = g_hFeaturesArray.Length; + DBG_Clients("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); + } + } +/* + DBG_Clients("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; + + DBG_Clients("LoadClientFeature: %s", szFeature); + + if (!GetValue(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) + { + static char szBuffer[4]; + static Handle hCookie; + hCookie = view_as(hArray.Get(FEATURES_COOKIE)); + GetClientCookie(iClient, hCookie, SZF(szBuffer)); + eStatus = view_as(StringToInt(szBuffer)); + DBG_Clients("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) +{ + DBG_Clients("GetValue: %d - %s", ValueType, szFeature); + switch (ValueType) + { + case VIP_NULL: + { + return false; + } + 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[256]; + g_hGroups.GetString(szFeature, SZF(szBuffer)); + if (szBuffer[0]) + { + DBG_Clients("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); + DBG_Clients("Clients_CreateExpiredTimer %N (%d):\tiTimeLeft: %d", iClient, iClient, iTimeLeft); + if (iTimeLeft > 0) + { + DBG_Clients("Clients_CreateExpiredTimer %N (%d):\tiTimeLeft+iTime: %d", iClient, iClient, iTimeLeft + iTime); + if ((iTimeLeft + iTime) > iExp) + { + DBG_Clients("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); + 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 (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) +{ + 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 ((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) +{ + DBG_Clients("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()) + { + DBG_Clients("Timer_VIP_Expired %N:", iClient); + + Clients_ExpiredClient(iClient); + } + } + + return Plugin_Continue; +} + +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) + { + 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"); } \ No newline at end of file diff --git a/addons/sourcemod/scripting/vip/Database.sp b/addons/sourcemod/scripting/vip/Database.sp index 267dcdb..1d297b1 100644 --- a/addons/sourcemod/scripting/vip/Database.sp +++ b/addons/sourcemod/scripting/vip/Database.sp @@ -1,391 +1,391 @@ -#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); - DBG_Database("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); - } - } - - DBG_Database("OnDBConnect %x, %u - > (MySQL: %b)", g_hDatabase, g_hDatabase, GLOBAL_INFO & IS_MySQL); - - CreateTables(); -} - -void CreateTables() -{ - DBG_Database("CreateTables"); - - if (GLOBAL_INFO & IS_MySQL) - { - #if USE_MORE_SERVERS - 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 ... ";", _, DBPrio_High); - } - 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);", _, DBPrio_High); - } -} - -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 ... "'", _, DBPrio_High); - g_hDatabase.Query(SQL_Callback_ErrorCheck, "SET CHARSET '" ... CHARSET ... "'", _, DBPrio_High); - - 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, DBPrio_Low); - } - - 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, DBPrio_Low); - } -} - -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, DBPrio_Low); -} - -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) -{ +#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); + DBG_Database("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); + } + } + + DBG_Database("OnDBConnect %x, %u - > (MySQL: %b)", g_hDatabase, g_hDatabase, GLOBAL_INFO & IS_MySQL); + + CreateTables(); +} + +void CreateTables() +{ + DBG_Database("CreateTables"); + + if (GLOBAL_INFO & IS_MySQL) + { + #if USE_MORE_SERVERS + 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 ... ";", _, DBPrio_High); + } + 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);", _, DBPrio_High); + } +} + +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 ... "'", _, DBPrio_High); + g_hDatabase.Query(SQL_Callback_ErrorCheck, "SET CHARSET '" ... CHARSET ... "'", _, DBPrio_High); + + 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, DBPrio_Low); + } + + 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, DBPrio_Low); + } +} + +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, DBPrio_Low); +} + +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, DBPrio_Low); -} - -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, DBPrio_Low); -} - -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); - } - } -} + 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, DBPrio_Low); +} + +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, DBPrio_Low); +} + +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); + } + } +} 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 или выше. From b22831a2945506f66abf5e142ae49e2d4f82984a Mon Sep 17 00:00:00 2001 From: R1KO Date: Sat, 10 Oct 2026 16:47:30 +0300 Subject: [PATCH 4/6] Replace legacy SourceMod calls with methodmaps: GlobalForward for forwards, Cookie, StringMap, KeyValues.Import and delete. Remove the redundant UTIL_CloseHandleEx helper; behavior is unchanged. --- addons/sourcemod/scripting/vip/API.sp | 54 +++++++++---------- addons/sourcemod/scripting/vip/Clients.sp | 18 +++---- addons/sourcemod/scripting/vip/Configs.sp | 2 +- addons/sourcemod/scripting/vip/Features.sp | 4 +- addons/sourcemod/scripting/vip/Info.sp | 2 +- addons/sourcemod/scripting/vip/UTIL.sp | 9 ---- addons/sourcemod/scripting/vip/VipMenu.sp | 2 +- .../sourcemod/scripting/vip/adminmenu/Del.sp | 2 +- 8 files changed, 40 insertions(+), 53 deletions(-) diff --git a/addons/sourcemod/scripting/vip/API.sp b/addons/sourcemod/scripting/vip/API.sp index f48c36b..6512523 100644 --- a/addons/sourcemod/scripting/vip/API.sp +++ b/addons/sourcemod/scripting/vip/API.sp @@ -1,31 +1,31 @@ -static Handle g_hGlobalForward_OnVIPLoaded; -static Handle g_hGlobalForward_OnClientPreLoad; -static Handle g_hGlobalForward_OnClientLoaded; -static Handle g_hGlobalForward_OnVIPClientLoaded; -static Handle g_hGlobalForward_OnVIPClientAdded; -static Handle g_hGlobalForward_OnVIPClientRemoved; -static Handle g_hGlobalForward_OnPlayerSpawn; -static Handle g_hGlobalForward_OnShowClientInfo; -static Handle g_hGlobalForward_OnFeatureToggle; -static Handle g_hGlobalForward_OnFeatureRegistered; -static Handle g_hGlobalForward_OnFeatureUnregistered; -static Handle g_hGlobalForward_OnClientDisconnect; +static GlobalForward g_hGlobalForward_OnVIPLoaded; +static GlobalForward g_hGlobalForward_OnClientPreLoad; +static GlobalForward g_hGlobalForward_OnClientLoaded; +static GlobalForward g_hGlobalForward_OnVIPClientLoaded; +static GlobalForward g_hGlobalForward_OnVIPClientAdded; +static GlobalForward g_hGlobalForward_OnVIPClientRemoved; +static GlobalForward g_hGlobalForward_OnPlayerSpawn; +static GlobalForward g_hGlobalForward_OnShowClientInfo; +static GlobalForward g_hGlobalForward_OnFeatureToggle; +static GlobalForward g_hGlobalForward_OnFeatureRegistered; +static GlobalForward g_hGlobalForward_OnFeatureUnregistered; +static GlobalForward g_hGlobalForward_OnClientDisconnect; 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); - g_hGlobalForward_OnVIPClientLoaded = CreateGlobalForward("VIP_OnVIPClientLoaded", ET_Ignore, Param_Cell); - g_hGlobalForward_OnVIPClientAdded = CreateGlobalForward("VIP_OnVIPClientAdded", ET_Ignore, Param_Cell, Param_Cell); - 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_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_OnClientPreLoad = new GlobalForward("VIP_OnClientPreLoad", ET_Hook, Param_Cell); + g_hGlobalForward_OnVIPLoaded = new GlobalForward("VIP_OnVIPLoaded", ET_Ignore); + g_hGlobalForward_OnClientLoaded = new GlobalForward("VIP_OnClientLoaded", ET_Ignore, Param_Cell, Param_Cell); + g_hGlobalForward_OnVIPClientLoaded = new GlobalForward("VIP_OnVIPClientLoaded", ET_Ignore, Param_Cell); + g_hGlobalForward_OnVIPClientAdded = new GlobalForward("VIP_OnVIPClientAdded", ET_Ignore, Param_Cell, Param_Cell); + g_hGlobalForward_OnVIPClientRemoved = new GlobalForward("VIP_OnVIPClientRemoved", ET_Ignore, Param_Cell, Param_String, Param_Cell); + g_hGlobalForward_OnPlayerSpawn = new GlobalForward("VIP_OnPlayerSpawn", ET_Ignore, Param_Cell, Param_Cell, Param_Cell); + g_hGlobalForward_OnShowClientInfo = new GlobalForward("VIP_OnShowClientInfo", ET_Hook, Param_Cell, Param_String, Param_String, Param_Cell); + g_hGlobalForward_OnFeatureToggle = new GlobalForward("VIP_OnFeatureToggle", ET_Hook, Param_Cell, Param_String, Param_Cell, Param_CellByRef); + g_hGlobalForward_OnFeatureRegistered = new GlobalForward("VIP_OnFeatureRegistered", ET_Ignore, Param_String); + g_hGlobalForward_OnFeatureUnregistered = new GlobalForward("VIP_OnFeatureUnregistered", ET_Ignore, Param_String); + g_hGlobalForward_OnClientDisconnect = new GlobalForward("VIP_OnClientDisconnect", ET_Ignore, Param_Cell, Param_Cell); } // Global Forwards @@ -767,11 +767,11 @@ public int Native_RegisterFeature(Handle hPlugin, int iNumParams) if (eType != HIDE) { - Handle hCookie = null; + Cookie hCookie = null; bool bRegisterCookie = (eType == SELECTABLE && iNumParams > 7 && (bCookie != false)); if (eType == TOGGLABLE || bRegisterCookie) { - hCookie = RegClientCookie(szFeature, szFeature, CookieAccess_Private); + hCookie = new Cookie(szFeature, szFeature, CookieAccess_Private); } hArray.Push(hCookie); @@ -1033,7 +1033,7 @@ public int Native_SetClientFeatureStatus(Handle hPlugin, int iNumParams) if (iNumParams > 4 && GetNativeCell(5)) { IntToString(view_as(eNewStatus), SZF(szFeature)); - SetClientCookie(iClient, view_as(GetArrayCell(hArray, FEATURES_COOKIE)), szFeature); + view_as(hArray.Get(FEATURES_COOKIE)).Set(iClient, szFeature); } return true; } diff --git a/addons/sourcemod/scripting/vip/Clients.sp b/addons/sourcemod/scripting/vip/Clients.sp index d2d0c80..6ad5ba8 100644 --- a/addons/sourcemod/scripting/vip/Clients.sp +++ b/addons/sourcemod/scripting/vip/Clients.sp @@ -2,8 +2,8 @@ void ResetClient(int iClient) { g_iClientInfo[iClient] = IS_LOADED; - UTIL_CloseHandleEx(g_hFeatures[iClient]); - UTIL_CloseHandleEx(g_hFeatureStatus[iClient]); + delete g_hFeatures[iClient]; + delete g_hFeatureStatus[iClient]; } public void OnClientPutInServer(int iClient) @@ -30,7 +30,7 @@ public void OnClientDisconnect(int iClient) } ResetClient(iClient); - UTIL_CloseHandleEx(g_hClientData[iClient]); + delete g_hClientData[iClient]; g_iClientInfo[iClient] = 0; } @@ -202,12 +202,8 @@ void Clients_OnVIPClientLoaded(int 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); } @@ -341,9 +337,9 @@ void Clients_LoadFeature(int iClient, const char[] szFeature) 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)); + static Cookie hCookie; + hCookie = view_as(hArray.Get(FEATURES_COOKIE)); + hCookie.Get(iClient, SZF(szBuffer)); eStatus = view_as(StringToInt(szBuffer)); DBG_Clients("GetFeatureCookie: '%s'", szBuffer); if (szBuffer[0] == '\0' || !Features_IsValidStatus(eStatus)) @@ -356,7 +352,7 @@ void Clients_LoadFeature(int iClient, const char[] szFeature) } IntToString(view_as(eStatus), SZF(szBuffer)); - SetClientCookie(iClient, hCookie, szBuffer); + hCookie.Set(iClient, szBuffer); // Features_SaveStatus(iClient, szFeature, hCookie, eStatus); } } diff --git a/addons/sourcemod/scripting/vip/Configs.sp b/addons/sourcemod/scripting/vip/Configs.sp index 184d699..6a0daac 100644 --- a/addons/sourcemod/scripting/vip/Configs.sp +++ b/addons/sourcemod/scripting/vip/Configs.sp @@ -34,7 +34,7 @@ void ReadConfigs() ReadSortingMenu(); - UTIL_CloseHandleEx(g_hGroups); + delete g_hGroups; g_hGroups = CreateConfig("data/vip/cfg/groups.ini", "VIP_GROUPS"); g_hInfo = CreateConfig("data/vip/cfg/info.ini", "VIP_INFO"); diff --git a/addons/sourcemod/scripting/vip/Features.sp b/addons/sourcemod/scripting/vip/Features.sp index 8d7bb56..6b72a14 100644 --- a/addons/sourcemod/scripting/vip/Features.sp +++ b/addons/sourcemod/scripting/vip/Features.sp @@ -47,7 +47,7 @@ void Features_TurnOnAll(int iClient) for(int i = 0; i < iFeatures; ++i) { - GetArrayString(g_hFeaturesArray, i, SZF(szFeature)); + g_hFeaturesArray.GetString(i, SZF(szFeature)); if(!GLOBAL_TRIE.GetValue(szFeature, hArray)) continue; @@ -71,7 +71,7 @@ void Features_TurnOnAll(int iClient) void Features_SetStatus(int iClient, const char[] szFeature, VIP_ToggleState eStatus) { DebugMessage("Features_SetStatus: %N (%i) -> Feature: %s, eStatus: %i", iClient, iClient, szFeature, eStatus); - SetTrieValue(g_hFeatureStatus[iClient], szFeature, eStatus); + g_hFeatureStatus[iClient].SetValue(szFeature, eStatus); } VIP_ToggleState Features_GetStatus(const int &iClient, const char[] szFeature) diff --git a/addons/sourcemod/scripting/vip/Info.sp b/addons/sourcemod/scripting/vip/Info.sp index 3b2b152..3575152 100644 --- a/addons/sourcemod/scripting/vip/Info.sp +++ b/addons/sourcemod/scripting/vip/Info.sp @@ -29,7 +29,7 @@ void DisplayInfo(int iClient, const char[] szEvent, const char[] szType, char[] if (g_hInfo.JumpToKey(szEvent) && g_hInfo.JumpToKey(szType)) { KeyValues hKeyValues = new KeyValues(szType); - KvCopySubkeys(g_hInfo, hKeyValues); + hKeyValues.Import(g_hInfo); switch(CreateForward_OnShowClientInfo(iClient, szEvent, szType, hKeyValues)) { case Plugin_Stop, Plugin_Handled: diff --git a/addons/sourcemod/scripting/vip/UTIL.sp b/addons/sourcemod/scripting/vip/UTIL.sp index 76be1aa..3d04dfa 100644 --- a/addons/sourcemod/scripting/vip/UTIL.sp +++ b/addons/sourcemod/scripting/vip/UTIL.sp @@ -17,15 +17,6 @@ int GET_CID(int iClient) return iClient; } -void UTIL_CloseHandleEx(Handle &hValue) -{ - if (hValue != null) - { - delete hValue; - hValue = null; - } -} - stock int UTIL_ReplaceChars(char[] szBuffer, int InChar, int OutChar) { int iNum = 0; diff --git a/addons/sourcemod/scripting/vip/VipMenu.sp b/addons/sourcemod/scripting/vip/VipMenu.sp index e481d00..5a9762b 100644 --- a/addons/sourcemod/scripting/vip/VipMenu.sp +++ b/addons/sourcemod/scripting/vip/VipMenu.sp @@ -299,7 +299,7 @@ public int Handler_VIPMenu(Menu hMenu, MenuAction action, int iClient, int iOpti { Features_SetStatus(iClient, szItemInfo, eNewStatus); IntToString(view_as(eNewStatus), SZF(szBuffer)); - SetClientCookie(iClient, view_as(GetArrayCell(hBuffer, FEATURES_COOKIE)), szBuffer); + view_as(hBuffer.Get(FEATURES_COOKIE)).Set(iClient, szBuffer); } } diff --git a/addons/sourcemod/scripting/vip/adminmenu/Del.sp b/addons/sourcemod/scripting/vip/adminmenu/Del.sp index a14d946..8527e5d 100644 --- a/addons/sourcemod/scripting/vip/adminmenu/Del.sp +++ b/addons/sourcemod/scripting/vip/adminmenu/Del.sp @@ -21,7 +21,7 @@ public int MenuHandler_DeleteVipPlayerMenu(Menu hMenu, MenuAction action, int iC { switch (action) { - case MenuAction_End: CloseHandle(hMenu); + case MenuAction_End: delete hMenu; case MenuAction_Cancel: { if (iItem == MenuCancel_ExitBack) ShowTargetInfoMenu(iClient); From 5f25ec8b8390efcf4fe55e001e1e027ad35edbc1 Mon Sep 17 00:00:00 2001 From: R1KO Date: Sat, 10 Oct 2026 16:54:40 +0300 Subject: [PATCH 5/6] Update AGENTS.md: SM 1.10 in CI, dev-based branching, LF line endings and methodmap conventions. --- AGENTS.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 62d6676..83626e2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,12 +3,16 @@ [VIP] Core — SourceMod (SourcePawn) plugin that grants players VIP status (timed or permanent). Extended by separate "module" plugins via natives/forwards. No tests; correctness = clean compile. ## Build -- CI (`.github/workflows/build.yml`) compiles against SM 1.11 (must pass) and 1.12 (allowed to fail): +- CI (`.github/workflows/build.yml`) compiles against SM 1.10 and 1.11 (must pass) and 1.12 (allowed to fail): `cd addons/sourcemod/scripting && mkdir -p ../plugins && spcomp VIP_Core.sp -E -o ../plugins/VIP_Core.smx -i./include` -- `-E` = warnings are errors. Fix every warning, including deprecation ones. -- Alt: `sourceknight build` (`sourceknight.yaml`, SM 1.13). Neither tool is installed locally by default. +- `-E` = warnings are errors. Fix every warning, including deprecation ones. SM 1.10 is the strictest parser (e.g. missing `;`), so code must build there too. +- Alt: `sourceknight build` (`sourceknight.yaml`, SM 1.13). It ignores spcomp's exit code — check output for `error`. - Never commit `*.smx`, `plugins/`, `.sourceknight`. +## Git +- `dev` is the integration branch: branch from `dev`, merge back into `dev`. `master` is for releases. +- LF line endings only (enforced by `.gitattributes`); don't introduce CRLF. + ## Layout - `scripting/VIP_Core.sp` — entry point; `#include`s every `vip/*.sp` into ONE compilation unit (order matters: Global → Debug → ... → API → CMD → Clients). New file = add `#include` there. - Compile-time switches in `VIP_Core.sp`: `DEBUG_MODE`, `USE_ADMINMENU` (gates `AdminMenu.sp` + `vip/adminmenu/*`), `USE_MORE_SERVERS`. @@ -22,7 +26,7 @@ ## Public API rules (vip_core.inc) - Third-party modules depend on it: never change existing native/forward signatures or semantics. Add new params with defaults or add new natives. - New native: implement `Native_X` in API.sp, `RegNative(X)` in `AskPluginLoad2`, declare in `.inc`, add `MarkNativeAsOptional("VIP_X")` in `__pl_vip_core_SetNTVOptional`. -- New forward: `CreateGlobalForward` in `API_SetupForwards` + `CreateForward_*` wrapper + `forward` declaration with doc comment in `.inc`. +- New forward: `static GlobalForward` field + `new GlobalForward(...)` in `API_SetupForwards` + `CreateForward_*` wrapper + `forward` declaration with doc comment in `.inc`. - Deprecate via `#pragma deprecated`, don't delete (see `update_modules.md`). ## Database @@ -35,7 +39,8 @@ - `#pragma semicolon 1`, `#pragma newdecls required`. Tabs. Hungarian-ish names: `iClient`, `szName`, `hMenu`, `bFlag`, globals `g_*`, cvars `g_CVAR_*`. - Pass client across async boundaries as userid (`UID`/`CID`), never raw index. - `GLOBAL_TRIE` / `GLOBAL_INFO` are index `[0]` of per-client arrays — slot 0 = global, not a player. -- Debug logging via `DBG_*` macros from `Debug.sp` (no-ops unless `DEBUG_MODE 1`). +- Use methodmaps (`StringMap`, `Cookie`, `KeyValues`, `delete`), not legacy procedural calls (`CreateTrie`, `SetClientCookie`, `CloseHandle`, `Kv*`). +- Debug logging via `DBG_*` / `DebugMessage` functions from `Debug.sp` (empty unless `DEBUG_MODE 1`); calls still need a trailing `;`. - Any new user-facing text → new phrase in `vip_core.phrases.txt` with at least `en` and `ru`. - Comments and cvar descriptions are mostly Russian; either language is fine, keep the surrounding style. - Bump `VIP_VERSION` in `VIP_Core.sp` only when asked; changelog lives in `updates.md`. From 718da68c4b87e16f61436ab5b68c75698c904368 Mon Sep 17 00:00:00 2001 From: R1KO Date: Sat, 10 Oct 2026 17:00:44 +0300 Subject: [PATCH 6/6] Bump version to 3.0.7 R and replace the archived release actions with gh release create. Release assets now include every SM version that compiled, including 1.10. --- .github/workflows/build.yml | 43 ++++---------------------- addons/sourcemod/scripting/VIP_Core.sp | 2 +- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f1ef3c5..44647c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,48 +56,17 @@ jobs: if: github.ref_type == 'tag' needs: compile runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Download artifacts uses: actions/download-artifact@v8 - - name: Find Assets - shell: bash - run: | - 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 - + # One zip per SM version that compiled (1.12 is allowed to fail) - name: Archive Assets - run: | - zip -r ${{ env.artifact_1_11 }}.zip ${{ env.artifact_1_11 }} - zip -r ${{ env.artifact_1_12 }}.zip ${{ env.artifact_1_12 }} + run: for d in */; do zip -r "${d%/}.zip" "$d"; done - name: Create Release - id: create_release - uses: actions/create-release@v1.1.4 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref_name }} - release_name: ${{ github.ref_name }} - draft: true - prerelease: false - - - name: Upload Asset 1.11 - uses: actions/upload-release-asset@v1.0.2 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 - - - name: Upload Asset 1.12 - uses: actions/upload-release-asset@v1.0.2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ${{ env.artifact_1_12 }}.zip - asset_name: ${{ env.artifact_1_12 }}.zip - asset_content_type: application/zip + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release create "${{ github.ref_name }}" *.zip --repo "${{ github.repository }}" --title "${{ github.ref_name }}" --draft --generate-notes diff --git a/addons/sourcemod/scripting/VIP_Core.sp b/addons/sourcemod/scripting/VIP_Core.sp index 45a1746..d2f9f58 100644 --- a/addons/sourcemod/scripting/VIP_Core.sp +++ b/addons/sourcemod/scripting/VIP_Core.sp @@ -7,7 +7,7 @@ #include #if !defined VIP_VERSION -#define VIP_VERSION "3.0.6 R" +#define VIP_VERSION "3.0.7 R" #endif