I declare a global 2D array to hold player's data, define macros to access said data and also have some dynamic arrays to hold variable length data. I also don't ever access the array directly and use the accessor macros and functions.
I load the player's data when they connect (in OnClientPreAdminCheck() when I know that are both in-game and have a steamid) and save the values when the client disconnects for for a few other events that happens in the mod (when they change race or gain a level). I also maintain a "dirty bit" that gets turned on when something gets modified and is checked at save time do that I don't save data that hasn't altered. I also use threaded queries except for when the map is changing.
I also break the main engine up into multiple .inc and .sp files. The total source is over 8MB of code in 438 files.
PHP Code:
#define INFO_IMMUNITIES 0
#define INFO_ATTRIBUTES 1
#define INFO_RACE 2
#define INFO_ENERGY 3
#define INFO_ENERGY_RATE 4
#define INFO_ENERGY_LIMIT 5
#define INFO_INITIAL_ENERGY 6
#define INFO_CRYSTALS 7
#define INFO_VESPENE 8
#define INFO_PENDING_RACE 9
#define INFO_PENDING_UPGRADE_RESET 10
#define INFO_OVERALL_LEVEL 11
#define INFO_DATABASE_IDENT 12
#define INFO_DATABASE_LOADED 13
#define INFO_DATABASE_SAVED 14
#define INFO_PLAYER_STATUS 15
#define INFO_PROPERTIES 16
#define INFO_SHOPITEMS 17
#define INFO_RACEDATA 18
#define INFO_TECHDATA 19
#define INFO_COUNT 20
#define RACEINFO_XP 0
#define RACEINFO_LEVEL 1
#define RACEINFO_ACCUMULATED_ENERGY 2
#define RACEINFO_ACTIVE_ULTIMATE_1 3
#define RACEINFO_ACTIVE_ULTIMATE_2 4
#define RACEINFO_ACTIVE_ULTIMATE_3 5
#define RACEINFO_ACTIVE_ULTIMATE_4 6
#define RACEINFO_DATABASE_LOADED 7
#define RACEINFO_COUNT 8
new any:playerArray[MAXPLAYERS+1][INFO_COUNT];
// Accessor Macros for playerArray
#define GetDatabaseIdent(%1) playerArray[%1][INFO_DATABASE_IDENT]
#define SetDatabaseIdent(%1,%2) playerArray[%1][INFO_DATABASE_IDENT] = %2
#define GetDatabaseLoaded(%1) playerArray[%1][INFO_DATABASE_LOADED]
#define SetDatabaseLoaded(%1,%2) playerArray[%1][INFO_DATABASE_LOADED] = %2
#define GetDatabaseSaved(%1) playerArray[%1][INFO_DATABASE_SAVED]
#define SetDatabaseSaved(%1,%2) playerArray[%1][INFO_DATABASE_SAVED] = %2
#define GetPlayerStatus(%1) playerArray[%1][INFO_PLAYER_STATUS]
#define SetPlayerStatus(%1,%2) playerArray[%1][INFO_PLAYER_STATUS] = %2
#define GetRace(%1) playerArray[%1][INFO_RACE]
#define GetPendingRace(%1) playerArray[%1][INFO_PENDING_RACE]
#define SetPendingRace(%1,%2) playerArray[%1][INFO_PENDING_RACE] = %2
#define GetPendingUpgradeReset(%1) playerArray[%1][INFO_PENDING_UPGRADE_RESET]
#define SetPendingUpgradeReset(%1,%2) playerArray[%1][INFO_PENDING_UPGRADE_RESET] = %2
#define GetEnergy(%1) playerArray[%1][INFO_ENERGY]
#define SetEnergy(%1,%2) playerArray[%1][INFO_ENERGY] = %2
#define GetEnergyRate(%1) playerArray[%1][INFO_ENERGY_RATE]
#define SetEnergyRate(%1,%2) playerArray[%1][INFO_ENERGY_RATE] = %2
#define GetEnergyLimit(%1) playerArray[%1][INFO_ENERGY_LIMIT]
#define SetEnergyLimit(%1,%2) playerArray[%1][INFO_ENERGY_LIMIT] = %2
#define GetInitialEnergy(%1) playerArray[%1][INFO_INITIAL_ENERGY]
#define SetInitialEnergy(%1,%2) playerArray[%1][INFO_INITIAL_ENERGY] = %2
#define GetCrystals(%1) playerArray[%1][INFO_CRYSTALS]
#define GetVespene(%1) playerArray[%1][INFO_VESPENE]
#define GetOverallLevel(%1) playerArray[%1][INFO_OVERALL_LEVEL]
#define GetImmunityBits(%1) playerArray[%1][INFO_IMMUNITIES]
#define SetImmunityBits(%1,%2) playerArray[%1][INFO_IMMUNITIES] = %2
#define GetImmunity(%1,%2) ((playerArray[%1][INFO_IMMUNITIES] & %2) == %2)
#define GetAttributeBits(%1) playerArray[%1][INFO_ATTRIBUTES]
#define SetAttributeBits(%1,%2) playerArray[%1][INFO_ATTRIBUTES] = %2
#define GetAttribute(%1,%2) ((playerArray[%1][INFO_ATTRIBUTES] & %2) == %2)
#define GetPropertyInfo(%1) playerArray[%1][INFO_PROPERTIES]
#define GetShopitems(%1) playerArray[%1][INFO_SHOPITEMS]
#define GetRaceData(%1) playerArray[%1][INFO_RACEDATA]
#define GetTechData(%1) playerArray[%1][INFO_TECHDATA]
// Accessor Macros for raceInfo
#if !defined _TRACE
#define GetRaceInfo(%1,%2) GetArrayCell(GetRaceData(%1),%2)
#endif
#define GetRaceInfoXP(%1) GetArrayCell(%1,RACEINFO_XP)
#define SetRaceInfoXP(%1,%2) SetArrayCell(%1,RACEINFO_XP,%2)
#define GetRaceInfoLevel(%1) GetArrayCell(%1,RACEINFO_LEVEL)
#define SetRaceInfoLevel(%1,%2) SetArrayCell(%1,RACEINFO_LEVEL,%2)
#define GetRaceInfoAccumulatedEnergy(%1) GetArrayCell(%1,RACEINFO_ACCUMULATED_ENERGY)
#define SetRaceInfoAccumulatedEnergy(%1,%2) SetArrayCell(%1,RACEINFO_ACCUMULATED_ENERGY,%2)
#define GetRaceInfoLoaded(%1) GetArrayCell(%1,RACEINFO_DATABASE_LOADED)
#define SetRaceInfoLoaded(%1,%2) SetArrayCell(%1,RACEINFO_DATABASE_LOADED,%2)
#define GetRaceInfoUpgradeLevel(%1,%2) GetArrayCell(%1,RACEINFO_COUNT+(%2*UPGRADEINFO_COUNT))
#define SetRaceInfoUpgradeLevel(%1,%2,%3) SetArrayCell(%1,RACEINFO_COUNT+(%2*UPGRADEINFO_COUNT),%3)
#define GetRaceInfoSavedUpgradeLevel(%1,%2) GetArrayCell(%1,RACEINFO_COUNT+(%2*UPGRADEINFO_COUNT)+UPGRADEINFO_SAVED_LEVEL)
#define SetRaceInfoSavedUpgradeLevel(%1,%2,%3) SetArrayCell(%1,RACEINFO_COUNT+(%2*UPGRADEINFO_COUNT)+UPGRADEINFO_SAVED_LEVEL,%3)
// Accessor Functions for playerArray
SetCrystals(client,crystals)
{
playerArray[client][INFO_CRYSTALS] = crystals;
playerArray[client][INFO_DATABASE_SAVED] = false;
}
SetVespene(client,vespene)
{
playerArray[client][INFO_VESPENE] = vespene;
playerArray[client][INFO_DATABASE_SAVED] = false;
}
SetOverallLevel(client,level)
{
playerArray[client][INFO_OVERALL_LEVEL] = level;
playerArray[client][INFO_DATABASE_SAVED] = false;
}
GetXP(client,race)
{
TraceInto("PlayerTracking", "GetXP", "client=%d:%N, race=%d", \
client, ValidClientIndex(client), race);
new xp = 0;
if (race >= 0 && GetRaceCount() > 1)
{
new Handle:raceData=GetRaceData(client);
if (raceData != INVALID_HANDLE)
{
new Handle:raceInfo=GetArrayCell(raceData,race);
if (raceInfo != INVALID_HANDLE)
xp = GetRaceInfoXP(raceInfo);
}
}
TraceReturn("Get %d:%N's race %d XP=%d", \
client, ValidClientIndex(client), \
race, xp);
return xp;
}
SetXP(client,race,xp)
{
TraceInto("PlayerTracking", "SetXP", "client=%d:%N, race=%d, xp=%d", \
client, ValidClientIndex(client), race, xp);
if (race >= 0 && GetRaceCount() > 1)
{
new Handle:raceData=GetRaceData(client);
if (raceData != INVALID_HANDLE)
{
new Handle:raceInfo=GetArrayCell(raceData,race);
if (raceInfo != INVALID_HANDLE)
{
Trace("Set %d:%N's race %d XP=%d", \
client, ValidClientIndex(client), \
race, xp);
SetRaceInfoXP(raceInfo,xp);
SetDatabaseSaved(client, false);
}
}
}
TraceReturn();
}
GetLevel(client,race)
{
TraceInto("PlayerTracking", "GetLevel", "client=%d:%N, race=%d", \
client, ValidClientIndex(client), race);
new level = 0;
if (race >= 0 && GetRaceCount() > 1)
{
new Handle:raceData=GetRaceData(client);
if (raceData != INVALID_HANDLE)
{
new Handle:raceInfo=GetArrayCell(raceData,race);
if (raceInfo != INVALID_HANDLE)
level = GetRaceInfoLevel(raceInfo);
}
}
TraceReturn("Get %d:%N's race %d level=%d", \
client, ValidClientIndex(client), \
race, level);
return level;
}
SetLevel(client,race,level,bool:update=false)
{
TraceInto("PlayerTracking", "SetLevel", "client=%d:%N, race=%d, level=%d", \
client, ValidClientIndex(client), race, level);
new delta = 0;
if (race >= 0 && GetRaceCount() > 1)
{
new Handle:raceData=GetRaceData(client);
if (raceData != INVALID_HANDLE)
{
new Handle:raceInfo=GetArrayCell(raceData,race);
if (raceInfo != INVALID_HANDLE)
{
new oldLevel = GetRaceInfoLevel(raceInfo);
Trace("Set %d:%N's race %d level=%d", \
client, ValidClientIndex(client), \
race, level);
delta = level-oldLevel;
if (delta != 0)
{
SetRaceInfoLevel(raceInfo,level);
SetDatabaseSaved(client, false);
if (update)
{
new oLevel = GetOverallLevel(client);
SetOverallLevel(client,oLevel+delta);
new Handle:raceHandle=GetRaceHandle(race);
if (raceHandle != INVALID_HANDLE)
{
new Faction:techId = GetRaceFaction(raceHandle);
if (techId > Generic)
{
IncrementTechLevel(client,techId,delta);
if (oldLevel <= 0 && level > 0)
IncrementTechCount(client,techId,1);
else if (oldLevel > 0 && level <= 0)
IncrementTechCount(client,techId,-1);
}
}
}
}
}
}
}
TraceReturn("delta=%d", delta);
return delta;
}
LoadPlayerData(client)
{
TraceInto("DB", "LoadPlayerData", "client=%d:%L", \
client, ValidClientIndex(client));
decl String:steamid[CLASS_STRING_LENGTH];
if (GetClientAuthString(client,steamid,sizeof(steamid)))
{
// Generate the 'other' steamid and check that one also.
decl String:steamidalt[CLASS_STRING_LENGTH];
strcopy(steamidalt, sizeof(steamidalt), steamid);
steamidalt[6] = (steamid[6] == '0') ? '1' : '0';
// Check if there are any in flight save threads
new threads;
if (GetTrieValue(g_SaveThreadsInFlight, steamid, threads) && threads > 0)
SetTrieValue(g_PlayersToReload, steamid, GetClientUserId(client), true);
else if (GetTrieValue(g_SaveThreadsInFlight, steamidalt, threads) && threads > 0)
SetTrieValue(g_PlayersToReload, steamidalt, GetClientUserId(client), true);
decl String:SQLString[QUERY_STRING_LENGTH];
Format(SQLString,sizeof(SQLString),
"SELECT player_ident, status+0, crystals, vespene, overall_level, settings, name, race_ident FROM sc_players WHERE (steamid = '%s' OR steamid = '%s') AND del_rec_date IS NULL",
steamid, steamidalt);
if (DatabaseAvailable(DB_THREAD))
{
SetDatabaseLoaded(client,DATA_LOADING);
SQL_TQuery(g_DbHandle[DB_THREAD], SQL_LoadPlayerData,
SQLString, GetClientUserId(client));
}
else
{
SetDatabaseLoaded(client,DATA_NOT_LOADED);
LogError("Unable to connect to DB to load %d:%L's player data\n",
client, ValidClientIndex(client));
}
}
else
{
SetDatabaseLoaded(client,DATA_NOT_LOADED);
LogError("Unable to obtain steamid to load %d:%L!",
client, ValidClientIndex(client));
}
TraceReturn();
}
public SQL_LoadPlayerData(Handle:owner, Handle:query, const String:error[], any:userid)
{
TraceInto("DB", "SQL_LoadPlayerData");
new client = GetClientOfUserId(userid);
if (query == INVALID_HANDLE || error[0] != '\0')
{
// Retry Access denied or Lost connection errors.
if (StrContains(error, "denied") >= 0 || StrContains(error, "connection") >= 0)
{
LogError("Querying %d:%L's player data failed: %s",
client, ValidClientIndex(client), error);
// Invalidate and close the DB_THREAD handle (in the main thread)
CreateTimer(0.1, CloseDatabaseTimer, g_DbHandle[DB_THREAD],TIMER_FLAG_NO_MAPCHANGE);
g_DbHandle[DB_THREAD] = INVALID_HANDLE;
if (client > 0)
{
// Retry loading the client's data
CreateTimer(0.1, Retry_LoadPlayerData, userid,TIMER_FLAG_NO_MAPCHANGE);
}
}
else
{
LogError("Unable to query %d:%L's player data: %s",
client, ValidClientIndex(client), error);
if (client > 0)
{
SetDatabaseLoaded(client,DATA_NOT_LOADED);
if (IsClientInGame(client))
{
PrintToConsole(client, "Unable to load your SourceCraft Data! %s", error);
PrintToChat(client, "Unable to load your SourceCraft Data! %s", error);
SetHudTextParams(-1.0, 0.8, 5.0, 255, 0, 0, 255, 2, 0.5, 0.0, 0.5);
ShowHudText(client, 1, "Unable to load your SourceCraft Data! %s", error);
}
}
}
}
else
{
if (client > 0)
{
if (SQL_HasResultSet(query) &&
SQL_FetchRow(query))
{
new player_ident = SQL_FetchInt(query,0);
SetDatabaseIdent(client,player_ident);
// Check if there are any in flight save threads
new threads;
decl String:key[CLASS_STRING_LENGTH];
Format(key, sizeof(key), "%d", player_ident);
if (GetTrieValue(g_SaveThreadsInFlight, key, threads) && threads > 0)
SetTrieValue(g_PlayersToReload, key, GetClientUserId(client), true);
new player_status = SQL_FetchInt(query,1);
SetPlayerStatus(client,player_status);
if (player_status != STATUS_DISABLED)
{
SetCrystals(client,SQL_FetchInt(query,2));
SetVespene(client,SQL_FetchInt(query,3));
SetOverallLevel(client,SQL_FetchInt(query,4));
SetSettingsBits(client,Settings:SQL_FetchInt(query,5));
SQL_FetchString(query, 6, playerPrevName[client], sizeof(playerPrevName[]));
#if defined _TRACE
decl String:name[MAX_NAME_LENGTH];
name[0] = '\0';
if (GetClientName(client,name,sizeof(name)) &&
name[0] == '\0')
{
Trace("Loading: Unable to retrieve %d:%L's name (DB has %s)", \
client, ValidClientIndex(client), playerPrevName[client]);
}
else if (!StrEqual(playerPrevName[client], name))
{
Trace("Loading: %d:%L changed name from what's in the DB(%s) to:%s", \
client, ValidClientIndex(client), playerPrevName[client], name);
}
#endif
Trace("Loaded %d:%L's Player Data; player_ident=%d, name='%s', race=%d, race_ident=%d, overall_level=%d, crystals=%d, vespene=%d", \
client, ValidClientIndex(client), player_ident, playerPrevName[client], GetRace(client), GetDatabaseIdent(client), \
GetOverallLevel(client), GetCrystals(client), GetVespene(client));
// Load the current Race.
new race_ident = SQL_FetchInt(query,7);
new raceId = FindRaceForIdent(race_ident);
if (raceId >= 0)
{
new Handle:raceHandle = GetRaceHandle(raceId);
if (GetRaceRequiredLevel(raceHandle) < 0)
{
#if defined _TRACE
new child = raceId;
#endif
// If the race is restricted,
// Change to the Parent Race instead (if any)
decl String:parentName[NAME_STRING_LENGTH];
GetRaceParentName(raceHandle, parentName, sizeof(parentName));
raceId = FindRace(parentName);
#if defined _TRACE
Trace("Parent of %d:%L's locked race=%d is %d:%s", \
client, ValidClientIndex(client), \
child, raceId, parentName);
#endif
}
if (raceId >= 0)
{
if (GetRaceLoaded(client,raceId) < DATA_OK)
{
Trace("Load %d:%L's race=%d Data", \
client, ValidClientIndex(client), \
raceId);
LoadPlayerRaceData(client, raceId);
}
Trace("Set %d:%L's race=%d", \
client, ValidClientIndex(client), \
raceId);
SetRace(client,raceId);
}
}
}
SetDatabaseLoaded(client,DATA_LOADED);
SetDatabaseSaved(client, true);
}
else
{
SetDatabaseLoaded(client,DATA_NOT_FOUND);
new raceId = GetRace(client);
if (raceId >= 0)
{
new Handle:raceData = GetRaceData(client);
new Handle:raceInfo = GetArrayCell(raceData,raceId);
SetRaceInfoLoaded(raceInfo,DATA_NOT_FOUND);
}
}
}
CloseHandle(query);
}
TraceReturn();
}
SavePlayerData(client)
{
TraceInto("DB", "SavePlayerData", "client=%d:%L", \
client, ValidClientIndex(client));
new timeleft;
new bool:threaded;
if (g_MapChanging)
{
// Don't use threaded saves when the map is changing!
threaded = false;
}
else if (GetMapTimeLeft(timeleft))
{
// or the map is about to end
threaded = (timeleft > 10);
}
else
threaded = true;
new player_ident = GetDatabaseIdent(client);
if (player_ident > 0)
{
if (DatabaseAvailable(DB_MAIN))
{
new race = GetRace(client);
new Handle:raceHandle = GetRaceHandle(race);
new race_ident = GetRaceIdent(raceHandle);
new Settings:bits = GetSettingsBits(client);
new overall_level = GetOverallLevel(client);
new crystals = GetCrystals(client);
new vespene = GetVespene(client);
Trace("Save %d:%L's data, race=%d, race_ident=%d, overall_level=%d", \
client, ValidClientIndex(client), race, race_ident, overall_level);
decl String:key[CLASS_STRING_LENGTH];
decl String:steamid[CLASS_STRING_LENGTH];
if (IsClientConnected(client) &&
GetClientAuthString(client,steamid,sizeof(steamid)))
{
strcopy(key, sizeof(key), steamid);
}
else
{
Format(key, sizeof(key), "%d", player_ident);
steamid[0] = '\0';
}
// Start the thread count at 0
SetTrieValue(g_SaveThreadsInFlight, key, 0, true);
decl String:name[MAX_NAME_LENGTH];
decl String:escName[sizeof(name)*2+1];
name[0] = escName[0] = '\0';
if (IsClientInGame(client))
GetClientName(client,name,sizeof(name));
else
strcopy(name, sizeof(name), playerName[client]);
if (name[0] == '\0')
{
strcopy(name, sizeof(name), playerName[client]);
if (name[0] == '\0')
strcopy(name, sizeof(name), playerPrevName[client]);
}
if (!SQL_EscapeString(g_DbHandle[DB_MAIN], name, escName, sizeof(escName)))
{
LogError("Unable to escape %s!", name);
strcopy(escName, sizeof(escName), name);
}
decl String:SQLString[QUERY_STRING_LENGTH];
Format(SQLString,sizeof(SQLString), "UPDATE sc_players SET race_ident=%d, crystals=%d, vespene=%d, overall_level=%d, settings=%d, name='%s', last_update=current_timestamp WHERE player_ident = %d", race_ident, crystals, vespene, overall_level, bits, escName, player_ident);
Trace("Saving %d:%L's Player Data; player_ident=%d, name='%s', race=%d, race_ident=%d, overall_level=%d, crystals=%d, vespene=%d", client, ValidClientIndex(client), player_ident, name, race, race_ident, overall_level, crystals, vespene);
if (threaded && DatabaseAvailable(DB_THREAD))
{
// Start threaded SQL update to update player table
new Handle:dataPack = CreateDataPack();
WritePackCell(dataPack, GetClientUserId(client));
WritePackCell(dataPack, player_ident);
WritePackCell(dataPack, crystals);
WritePackCell(dataPack, vespene);
WritePackString(dataPack, key);
UpdateThreadsInFlight(key, 1);
SQL_TQuery(g_DbHandle[DB_THREAD], SQL_UpdatePlayer, SQLString, dataPack);
}
else if (DatabaseAvailable(DB_MAIN))
{
if (!SQL_FastQuery(g_DbHandle[DB_MAIN],SQLString))
{
new bool:success = false;
decl String:error[ERROR_STRING_LENGTH]; error[0] = '\0';
SQL_GetError(g_DbHandle[DB_MAIN], error, sizeof(error));
// Retry Access denied or Lost connection errors.
if (StrContains(error, "denied") >= 0 || StrContains(error, "connection") >= 0)
{
LogError("Initial attempt to update %d:%L's (ident=%d) player data failed: %s\nSQL:%s",
client, ValidClientIndex(client), player_ident, error, SQLString);
CloseDbHandle(DB_MAIN);
if (DatabaseAvailable(DB_MAIN))
{
success = SQL_FastQuery(g_DbHandle[DB_MAIN],SQLString);
if (success)
{
error[0] = '\0';
LogError("Second attempt to update %d:%L's (ident=%d) player data succeeded!",
client, ValidClientIndex(client), player_ident);
}
else
SQL_GetError(g_DbHandle[DB_MAIN], error, sizeof(error));
}
}
if (!success)
{
SetDatabaseSaved(client, false);
LogError("Unable to update %d:%L's (ident=%d) player data (crystals=%d,vespene=%d): %s\nSQL:%s",
client, ValidClientIndex(client), player_ident, crystals, vespene, error, SQLString);
TraceReturn();
return;
}
}
}
else
{
LogError("Unable to connect to DB to update %d:%L's (ident=%d) player data",
client, ValidClientIndex(client), player_ident);
TraceReturn();
return;
}
if (race > 0)
{
Trace("Save %d:%L's race data, race=%d Data", \
client, ValidClientIndex(client), race);
if (SavePlayerRaceData(client, player_ident, race, threaded, key))
SetDatabaseSaved(client, true);
}
else
SetDatabaseSaved(client, true);
// Check the thread count and cleanup
// the entry if there aren't any
UpdateThreadsInFlight(key, 0);
if (!StrEqual(playerPrevName[client], name))
{
Format(SQLString,sizeof(SQLString), "INSERT INTO sc_player_alias (player_ident,steamid,name,last_used) VALUES (%d,'%s','%s',current_timestamp) ON DUPLICATE KEY UPDATE last_used=current_timestamp", player_ident, steamid, escName);
if (threaded && DatabaseAvailable(DB_THREAD))
{
new Handle:dataPack = CreateDataPack();
WritePackCell(dataPack, GetClientUserId(client));
WritePackCell(dataPack, player_ident);
// Start threaded SQL update to update alias table
SQL_TQuery(g_DbHandle[DB_THREAD], SQL_UpdateAlias, SQLString, dataPack);
// Assume it worked.
strcopy(playerPrevName[client], sizeof(playerPrevName[]), name);
}
else if (DatabaseAvailable(DB_MAIN))
{
if (!SQL_FastQuery(g_DbHandle[DB_MAIN],SQLString))
{
new bool:success = false;
decl String:error[ERROR_STRING_LENGTH]; error[0] = '\0';
SQL_GetError(g_DbHandle[DB_MAIN], error, sizeof(error));
// Retry Access denied or Lost connection errors.
if (StrContains(error, "denied") >= 0 || StrContains(error, "connection") >= 0)
{
LogError("Initial attempt to update %d:%L's (ident=%d) alias data failed: %s\nSQL:%s",
client, ValidClientIndex(client), player_ident, error, SQLString);
CloseDbHandle(DB_MAIN);
if (DatabaseAvailable(DB_MAIN))
{
success = SQL_FastQuery(g_DbHandle[DB_MAIN],SQLString);
if (success)
{
error[0] = '\0';
LogError("Second attempt to update %d:%L's (ident=%d) alias data succeeded!",
client, ValidClientIndex(client), player_ident);
}
else
SQL_GetError(g_DbHandle[DB_MAIN], error, sizeof(error));
}
}
else if (StrContains(error, "constraint") >= 0)
{
if (StrContains(error, "player_ident") >= 0)
{
// Invalidate player_ident for constraint failed errors.
SetDatabaseIdent(client,0);
}
}
if (!success)
{
LogError("Unable to update %d:%L's (ident=%d) alias data: %s\nSQL:%s",
client, ValidClientIndex(client), player_ident, error, SQLString);
}
}
else
{
strcopy(playerPrevName[client], sizeof(playerPrevName[]), name);
}
}
else
{
LogError("Unable to connect to DB to update %d:%L's (ident=%d) alias data.",
client, ValidClientIndex(client), player_ident);
}
}
}
else
{
LogError("Unable to connect to DB to update %d:%L's (ident=%d) player data.",
client, ValidClientIndex(client), player_ident);
}
}
else
InsertPlayerData(client, threaded);
TraceReturn();
}
public SQL_UpdatePlayer(Handle:owner, Handle:hndl, const String:error[], any:data)
{
TraceInto("DB", "SQL_UpdatePlayer");
if (data == INVALID_HANDLE)
LogError("Invalid Datapack Passed to SQL_UpdatePlayer!");
else
{
ResetPack(data);
new userid = ReadPackCell(data);
new client = GetClientOfUserId(userid);
new player_ident = ReadPackCell(data);
new crystals = ReadPackCell(data);
new vespene = ReadPackCell(data);
decl String:key[CLASS_STRING_LENGTH];
ReadPackString(data, key, sizeof(key));
if (hndl != INVALID_HANDLE && error[0] == '\0')
UpdateThreadsInFlight(key, -1);
else
{
if (client > 0)
SetDatabaseSaved(client, false);
// Retry Access denied or Lost connection errors.
if (StrContains(error, "denied") >= 0 || StrContains(error, "connection") >= 0)
{
// Invalidate and close the DB_THREAD handle (in the main thread)
CreateTimer(0.1, CloseDatabaseTimer, g_DbHandle[DB_THREAD],TIMER_FLAG_NO_MAPCHANGE);
g_DbHandle[DB_THREAD] = INVALID_HANDLE;
if (client > 0)
{
// Retry saving the client's data
CreateTimer(0.1, Retry_SavePlayerData, userid,TIMER_FLAG_NO_MAPCHANGE);
}
}
else
{
RemoveFromTrie(g_SaveThreadsInFlight, key);
LogError("Unable to update %d:%L's (ident=%d) player data (crystals=%d,vespene=%d,key=%s): %s",
client, ValidClientIndex(client), player_ident, crystals, vespene, key, error);
if (IsValidClient(client))
{
PrintToConsole(client, "Unable to save your SourceCraft Data! %s", error);
PrintToChat(client, "Unable to save your SourceCraft Data! %s", error);
SetHudTextParams(-1.0, 0.8, 5.0, 255, 0, 0, 255, 2, 0.5, 0.0, 0.5);
ShowHudText(client, 1, "Unable to save your SourceCraft Data! %s", error);
}
}
}
CloseHandle(data);
}
TraceReturn();
}