Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 117 additions & 18 deletions addons/sourcemod/scripting/VIP_Test.sp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
1.0.7 - Upgrade to utf8mb4
1.0.8 - No need to lock/unlock database - All queries are asynchronous.
1.0.9 - Simplify connect db logic - Use async connect.
1.0.10 - Use SourceMod 1.12 methodmaps.
1.0.11 - SQL auto reconnect: retry on connect failure and on lost connection.
*/
#pragma semicolon 1
#pragma newdecls required
Expand All @@ -51,12 +53,15 @@ public Plugin myinfo =
name = "[VIP] Test",
author = "Loneypro",
description = "Players can test vip features for a set of time",
version = "1.0.10",
version = "1.0.11",
url = ""
};

Database g_hDatabase;
Handle g_hReconnectTimer;
bool g_bDBMySQL;
bool g_bConnecting;
float g_fReconnectInterval;
int g_iTestTime;
int g_iTestInterval;
char g_sTestGroup[64];
Expand All @@ -75,6 +80,10 @@ public void OnPluginStart()
hCvar2.AddChangeHook(OnTestIntervalChange);
g_iTestInterval = hCvar2.IntValue;

ConVar hCvar3 = CreateConVar("sm_vip_test_reconnect_interval", "30.0", "Через сколько секунд повторять попытку подключения к базе данных / Delay in seconds between database reconnection attempts", 0, true, 5.0);
hCvar3.AddChangeHook(OnReconnectIntervalChange);
g_fReconnectInterval = hCvar3.FloatValue;

AutoExecConfig(true, "vip_test", "vip");

RegConsoleCmd("sm_testvip", TestVIP_CMD);
Expand All @@ -96,29 +105,81 @@ public void OnTestIntervalChange(ConVar hCvar, const char[] oldVal, const char[]
{
g_iTestInterval = hCvar.IntValue;
}
public void OnReconnectIntervalChange(ConVar hCvar, const char[] oldVal, const char[] newVal)
{
g_fReconnectInterval = hCvar.FloatValue;
}
public void OnTestGroupChange(ConVar hCvar, const char[] oldVal, const char[] newVal)
{
strcopy(g_sTestGroup, sizeof(g_sTestGroup), newVal);
}

stock void Connect_DB()
{
if (g_bConnecting || g_hDatabase != null)
{
return;
}

g_bConnecting = true;

// Database.Connect() implicitly falls back to a local SQLite database
// named "vip_test" when no matching entry exists in databases.cfg, so
// this single async call covers both the MySQL and SQLite cases.
Database.Connect(DB_OnConnect, "vip_test");
}

stock void ScheduleReconnect()
{
if (g_hReconnectTimer == null)
{
g_hReconnectTimer = CreateTimer(g_fReconnectInterval, Timer_Reconnect);
}
}

public Action Timer_Reconnect(Handle hTimer)
{
g_hReconnectTimer = null;
Connect_DB();
return Plugin_Stop;
}

// Returns true if the error means the connection to the database was lost.
stock bool IsConnectionLostError(const char[] sError)
{
return (StrContains(sError, "gone away", false) != -1
|| StrContains(sError, "Lost connection", false) != -1
|| StrContains(sError, "Can't connect", false) != -1
|| StrContains(sError, "Broken pipe", false) != -1);
}

// Logs a query error and triggers a reconnect if the connection was lost.
stock void HandleQueryError(const char[] sCallback, const char[] sError)
{
LogError("%s: %s", sCallback, sError);

if (g_hDatabase != null && IsConnectionLostError(sError))
{
LogError("Lost connection to the database, reconnecting...");
delete g_hDatabase;
Connect_DB();
}
}

public void DB_OnConnect(Database db, const char[] sError, any data)
{
g_hDatabase = db;
g_bConnecting = false;

if (g_hDatabase == null || sError[0])
if (db == null || sError[0])
{
SetFailState("DB Connect %s", sError);
delete db;
LogError("DB Connect failed: %s. Retrying in %.0f seconds.", sError, g_fReconnectInterval);
ScheduleReconnect();
return;
}

g_hDatabase = db;

char sDriver[16];
db.Driver.GetIdentifier(sDriver, sizeof(sDriver));

Expand All @@ -135,13 +196,22 @@ public void DB_OnConnect(Database db, const char[] sError, any data)
}

CreateTables();

// Load players who joined while the database was unavailable
for (int i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && IsClientAuthorized(i))
{
OnClientPostAdminCheck(i);
}
}
}

public void SQL_Callback_ErrorCheck(Database owner, DBResultSet hndl, const char[] sError, any data)
{
if (sError[0])
{
LogError("SQL_Callback_ErrorCheck: %s", sError);
HandleQueryError("SQL_Callback_ErrorCheck", sError);
}
}

Expand Down Expand Up @@ -169,6 +239,12 @@ public Action ClearTestVIP_CMD(int iClient, int args)
{
if (iClient)
{
if (g_hDatabase == null)
{
VIP_PrintToChatClient(iClient, "%t", "VIP_DB_UNAVAILABLE");
return Plugin_Handled;
}

g_hDatabase.Query(SQL_Callback_DropTable, "DROP TABLE `vip_test`;");
}
return Plugin_Handled;
Expand All @@ -178,7 +254,7 @@ public void SQL_Callback_DropTable(Database hOwner, DBResultSet hQuery, const ch
{
if (hQuery == null)
{
LogError("SQL_Callback_DropTable: %s", sError);
HandleQueryError("SQL_Callback_DropTable", sError);
return;
}

Expand All @@ -195,6 +271,12 @@ public Action TestVIP_CMD(int iClient, int args)
return Plugin_Handled;
}

if (g_hDatabase == null)
{
VIP_PrintToChatClient(iClient, "%t", "VIP_DB_UNAVAILABLE");
return Plugin_Handled;
}

char sQuery[256], sAuth[32];
// GetClientAuthString(iClient, sAuth, sizeof(sAuth));
// GetClientAuthId(iClient, AuthId_Steam2, sAuth, sizeof(sAuth));
Expand All @@ -215,14 +297,18 @@ public Action TestVIP_CMD(int iClient, int args)
public void SQL_Callback_SelectClient(Database hOwner, DBResultSet hQuery, const char[] sError, any UserID)
{
int iClient = GetClientOfUserId(UserID);
if (iClient)
if (hQuery == null)
{
if (hQuery == null)
HandleQueryError("SQL_Callback_SelectClient", sError);
if (iClient)
{
LogError("SQL_Callback_SelectClient: %s", sError);
return;
VIP_PrintToChatClient(iClient, "%t", "VIP_DB_UNAVAILABLE");
}

return;
}

if (iClient)
{
if(hQuery.FetchRow())
{
if(g_iTestInterval > 0)
Expand Down Expand Up @@ -258,13 +344,19 @@ public void SQL_Callback_InsertClient(Database hOwner, DBResultSet hQuery, const
{
if (hQuery == null)
{
LogError("SQL_Callback_InsertClient: %s", sError);
HandleQueryError("SQL_Callback_InsertClient", sError);
return;
}
}

public void OnClientPostAdminCheck(int iClient)
{
if (g_hDatabase == null)
{
// Client will be loaded once the database connection is back
return;
}

if(IsFakeClient(iClient) == false)
{
char sQuery[256], sAuth[32];
Expand All @@ -277,14 +369,14 @@ public void OnClientPostAdminCheck(int iClient)
public void SQL_Callback_SelectClientAuthorized(Database hOwner, DBResultSet hQuery, const char[] sError, any UserID)
{
int iClient = GetClientOfUserId(UserID);
if (hQuery == null)
{
HandleQueryError("SQL_Callback_SelectClientAuthorized", sError);
return;
}

if (iClient)
{
if (hQuery == null)
{
LogError("SQL_Callback_SelectClientAuthorized: %s", sError);
return;
}

if(hQuery.FetchRow())
{
int iEnd, iTime;
Expand All @@ -304,6 +396,13 @@ stock void GiveVIPToClient(int iClient, bool bUpdate = false)
return;
}

// Don't give VIP status if we can't save it
if (g_hDatabase == null)
{
VIP_PrintToChatClient(iClient, "%t", "VIP_DB_UNAVAILABLE");
return;
}

int iSeconds;
char sQuery[256], sAuth[32];
iSeconds = VIP_TimeToSeconds(g_iTestTime);
Expand Down
8 changes: 8 additions & 0 deletions addons/sourcemod/translations/vip_test.phrases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,12 @@
"fi" "SteamID:n vahvistaminen epäonnistui. Yritä myöhemmin."
"fr" "Impossible de vérifier votre SteamID. Essayer plus tard."
}

"VIP_DB_UNAVAILABLE"
{
"ru" "База данных недоступна. Попробуйте позже."
"en" "The database is unavailable. Try later."
"fi" "Tietokanta ei ole käytettävissä. Yritä myöhemmin."
"fr" "La base de données est indisponible. Réessayez plus tard."
}
}
Loading