Raised This Month: $51 Target: $400
 12% 

Questions about developing big plugins.


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Chanz
Veteran Member
Join Date: Aug 2008
Location: Germany - Stuttgart
Old 11-14-2010 , 07:23   Questions about developing big plugins.
Reply With Quote #1

Hi,

I'm about to rewrite a rather big plugin (around 10000 lines of code).

A question is about local static variables vs. global variables - what is better?

As example I could use only functions to handle variables, as example:
Code:
Client_Money(client,money=0){

    static client_Money[MAXPLAYERS+1] = {0,...};
    client_Money[client] += money;
    return client_Money[client];
}
I think this would make the code much cleaner.

Another way is to use dynamic arrays but they are slow and I don't know if I'm sure to use them in a big plugin, since it would make it maybe very slow.

I need many variables for a client to keep track of money,money on the bank, hp, armor, etc. and I need to save them on our mysql server.

I hope anyone has a bit of information to save time, to make sure the server can crash at any time without loosing information about a client, like how much money he has currently and to make sure everything isn't slow.

cya Chanz
__________________
[ SourceModPlugins.org ][ My Plugins ]

Thank you for donations: [ Paypal ]

Video Tutorial (German): [ Gameserver & SourceMod Plugins mit HLSW verwalten ]
Chanz is offline
FaTony
Veteran Member
Join Date: Aug 2008
Old 11-14-2010 , 07:52   Re: Questions about developing big plugins.
Reply With Quote #2

A rule of thumb is always to use local variables instead of global if you can. Also note, if you want to make plugin crash proof, you will hog MySQL server and will have to write many additional code. I suggest to save data like every 5 minutes or so.
__________________
FaTony is offline
Chanz
Veteran Member
Join Date: Aug 2008
Location: Germany - Stuttgart
Old 11-14-2010 , 08:36   Re: Questions about developing big plugins.
Reply With Quote #3

I read somewhere that the mysql implementation in sm can only handle 20 querys per sec this means a burst every 5 min can overflow this, that some querys are lost?

Isn't it better to save everything when it occurs? Of course if clients on the server gone mad and want to flood it by doing actions that require a save into the database, there can be a lost of querys too.

I checked out the 'Concept: Project base' and its quite interesting, as far I get it you develop a module and if you release it everyone can use it.
So a perfect solution (as you said write many additional code) for saving variables into a database, which is fast and 100% reliable, is one of the first things I'd like to make.
I don't mind, if I have to write once much code, as long it would be useful for many other projects.

EDIT:
Has anyone used prepared statements with sm? http://dev.mysql.com/tech-resources/...tatements.html
Then I'd like to hear what your experience about it.
__________________
[ SourceModPlugins.org ][ My Plugins ]

Thank you for donations: [ Paypal ]

Video Tutorial (German): [ Gameserver & SourceMod Plugins mit HLSW verwalten ]
Chanz is offline
FaTony
Veteran Member
Join Date: Aug 2008
Old 11-15-2010 , 03:15   Re: Questions about developing big plugins.
Reply With Quote #4

You can wrap many queries together. I was saving everything when it occurs and it still lagged when 16 players earned money on the same frame. I didn't use threaded queries though.
__________________
FaTony is offline
asherkin
SourceMod Developer
Join Date: Aug 2009
Location: OnGameFrame()
Old 11-15-2010 , 03:37   Re: Questions about developing big plugins.
Reply With Quote #5

Quote:
Originally Posted by Chanz View Post
Has anyone used prepared statements with sm? http://dev.mysql.com/tech-resources/...tatements.html
Then I'd like to hear what your experience about it.
The major issue with using prepared statements in SM is the fact that you cannot use threading with them. I think there is a bug open somewhere to add support for this, if there isn't you could file one.
__________________
asherkin is offline
retsam
Veteran Member
Join Date: Aug 2008
Location: so-cal
Old 11-15-2010 , 04:32   Re: Questions about developing big plugins.
Reply With Quote #6

IMO, if the server is crashing in the first place, you got other issues you should be worrying about anyways.

The smarter way to do it imo, is use local variables for everything while they are on the server. Query the database when they connect and then add those values to local variables. Use the local variables for the most part while they are on the server and then save the values back to the database when they disconnect. That will save a lot of database queries and avoid some lag issues I see on some peoples servers with lots of database stuff going on. Then, you can have some random timer, like save to database every 10-15 mins or something(i'm not sure what youre doing but..). I dont have much experience with threaded queries, but if youre gonna make lots of database queries to make it "crash proof", you should look into that.....
__________________
retsam is offline
Chanz
Veteran Member
Join Date: Aug 2008
Location: Germany - Stuttgart
Old 11-15-2010 , 06:23   Re: Questions about developing big plugins.
Reply With Quote #7

@FaTony: I use only threaded queries, this is the only way to provide a lag free server.
But its very interesting that non threaded queries and 16 players already lag.
What server did you use? Was the DB server local?

I read a thread in the bug-tracker, about that wrapped queries don't work.
So "SELECT 1;SELECT * FROM xyz WHERE x=1;UPDATE ..." doesn't work.
Did you use wrapped queries only with the non threaded method?

@asherkin: Yeah I found that one: https://bugs.alliedmods.net/show_bug.cgi?id=3519
But I don't know if I really should request this, since I still gather information about mysql and servers.

@retsam: I would use the way you recommend, if it only be me, who is developing the game, extensions and all its plugins on it. I've seen in about 3 years more annoying bugs and crashes because of developers (game,mod,srcds-plugin,extension,plugin), that are to lazy to provide a high quality of code. So in order to keep the data consistent, I need to assume that server crashes, connection problems, etc. can happen any time, on any server.


I'm going to recode our RP plugin and I just can't tell 32 players that they lost their items, money, apartments,..., of the last 2 hours, because of a little crash bug. This happens once and they are pissed this happens twice and they just leave our server.

I thought about the Concept: Project Base it uses modules and seems to be very handy. So I would like to start of with a high quality database module, that can be used by every other plugin using Project Base.
__________________
[ SourceModPlugins.org ][ My Plugins ]

Thank you for donations: [ Paypal ]

Video Tutorial (German): [ Gameserver & SourceMod Plugins mit HLSW verwalten ]
Chanz is offline
FaTony
Veteran Member
Join Date: Aug 2008
Old 11-15-2010 , 08:10   Re: Questions about developing big plugins.
Reply With Quote #8

I had MySQL server, dedicated server and client on the same machine. Athlon 64 X2 6000+, 2 Gb RAM, Seagate Barracuda 7200 750 Gb.
I've used single non-threaded queries.
__________________
FaTony is offline
Chanz
Veteran Member
Join Date: Aug 2008
Location: Germany - Stuttgart
Old 11-15-2010 , 12:44   Re: Questions about developing big plugins.
Reply With Quote #9

ok thank you, seems non-threaded queries died for me now at all. :-)
__________________
[ SourceModPlugins.org ][ My Plugins ]

Thank you for donations: [ Paypal ]

Video Tutorial (German): [ Gameserver & SourceMod Plugins mit HLSW verwalten ]
Chanz is offline
naris
AlliedModders Donor
Join Date: Dec 2006
Old 11-15-2010 , 14:00   Re: Questions about developing big plugins.
Reply With Quote #10

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.

Here are some snippets as an example:
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", \
              
clientValidClientIndex(client), race);

    new 
xp 0;
    if (
race >= && 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", \
                
clientValidClientIndex(client), \
                
racexp);

    return 
xp;
}

SetXP(client,race,xp)
{
    
TraceInto("PlayerTracking""SetXP""client=%d:%N, race=%d, xp=%d", \
              
clientValidClientIndex(client), racexp);

    if (
race >= && 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", \
                      
clientValidClientIndex(client), \
                      
racexp);

                
SetRaceInfoXP(raceInfo,xp);
                
SetDatabaseSaved(clientfalse);
            }
        }
    }

    
TraceReturn();
}

GetLevel(client,race)
{
    
TraceInto("PlayerTracking""GetLevel""client=%d:%N, race=%d", \
              
clientValidClientIndex(client), race);

    new 
level 0;
    if (
race >= && 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", \
                
clientValidClientIndex(client), \
                
racelevel);

    return 
level;
}

SetLevel(client,race,level,bool:update=false)
{
    
TraceInto("PlayerTracking""SetLevel""client=%d:%N, race=%d, level=%d", \
              
clientValidClientIndex(client), racelevel);

    new 
delta 0;
    if (
race >= && 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", \
                      
clientValidClientIndex(client), \
                      
racelevel);

                
delta level-oldLevel;
                if (
delta != 0)
                {
                    
SetRaceInfoLevel(raceInfo,level);
                    
SetDatabaseSaved(clientfalse);

                    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 <= && level 0)
                                    
IncrementTechCount(client,techId,1);
                                else if (
oldLevel && level <= 0)
                                    
IncrementTechCount(client,techId,-1);
                            }
                        }
                    }
                }
            }
        }
    }

    
TraceReturn("delta=%d"delta);
    return 
delta;
}

LoadPlayerData(client)
{
    
TraceInto("DB""LoadPlayerData""client=%d:%L", \
              
clientValidClientIndex(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(steamidaltsizeof(steamidalt), steamid);
        
steamidalt[6] = (steamid[6] == '0') ? '1' '0';

        
// Check if there are any in flight save threads

        
new threads;
        if (
GetTrieValue(g_SaveThreadsInFlightsteamidthreads) && threads 0)
            
SetTrieValue(g_PlayersToReloadsteamidGetClientUserId(client), true);
        else if (
GetTrieValue(g_SaveThreadsInFlightsteamidaltthreads) && threads 0)
            
SetTrieValue(g_PlayersToReloadsteamidaltGetClientUserId(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",
               
steamidsteamidalt);

        if (
DatabaseAvailable(DB_THREAD))
        {
            
SetDatabaseLoaded(client,DATA_LOADING);
            
SQL_TQuery(g_DbHandle[DB_THREAD], SQL_LoadPlayerData,
                       
SQLStringGetClientUserId(client));
        }
        else
        {
            
SetDatabaseLoaded(client,DATA_NOT_LOADED);
            
LogError("Unable to connect to DB to load %d:%L's player data\n",
                     
clientValidClientIndex(client));
        }
    }
    else
    {
        
SetDatabaseLoaded(client,DATA_NOT_LOADED);
        
LogError("Unable to obtain steamid to load %d:%L!",
                 
clientValidClientIndex(client));
    }

    
TraceReturn();
}

public 
SQL_LoadPlayerData(Handle:ownerHandle: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") >= || StrContains(error"connection") >= 0)
        {
            
LogError("Querying %d:%L's player data failed: %s",
                     
clientValidClientIndex(client), error);

            
// Invalidate and close the DB_THREAD handle (in the main thread)
            
CreateTimer(0.1CloseDatabaseTimerg_DbHandle[DB_THREAD],TIMER_FLAG_NO_MAPCHANGE);
            
g_DbHandle[DB_THREAD] = INVALID_HANDLE;

            if (
client 0)
            {
                
// Retry loading the client's data
                
CreateTimer(0.1Retry_LoadPlayerDatauserid,TIMER_FLAG_NO_MAPCHANGE);
            }
        }
        else
        {
            
LogError("Unable to query %d:%L's player data: %s",
                     
clientValidClientIndex(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.00.85.02550025520.50.00.5);
                    
ShowHudText(client1"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(keysizeof(key), "%d"player_ident);
                if (
GetTrieValue(g_SaveThreadsInFlightkeythreads) && threads 0)
                    
SetTrieValue(g_PlayersToReloadkeyGetClientUserId(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(query6playerPrevName[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)", \
                                   
clientValidClientIndex(client), playerPrevName[client]);
                        }
                        else if (!
StrEqual(playerPrevName[client], name))
                        {
                            
Trace("Loading: %d:%L changed name from what's in the DB(%s) to:%s", \
                                  
clientValidClientIndex(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", \
                          
clientValidClientIndex(client), player_identplayerPrevName[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(raceHandleparentNamesizeof(parentName));
                            
raceId FindRace(parentName);

                            
#if defined _TRACE
                                
Trace("Parent of %d:%L's locked race=%d is %d:%s", \
                                      
clientValidClientIndex(client), \
                                      
childraceIdparentName);
                            
#endif
                        
}

                        if (
raceId >= 0)
                        {
                            if (
GetRaceLoaded(client,raceId) < DATA_OK)
                            {
                                
Trace("Load %d:%L's race=%d Data", \
                                      
clientValidClientIndex(client), \
                                      
raceId);

                                
LoadPlayerRaceData(clientraceId);
                            }

                            
Trace("Set %d:%L's race=%d", \
                                  
clientValidClientIndex(client), \
                                  
raceId);

                            
SetRace(client,raceId);
                        }
                    }
                }

                
SetDatabaseLoaded(client,DATA_LOADED);
                
SetDatabaseSaved(clienttrue);
            }
            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", \
              
clientValidClientIndex(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", \
                  
clientValidClientIndex(client), racerace_identoverall_level);

            
decl String:key[CLASS_STRING_LENGTH];
            
decl String:steamid[CLASS_STRING_LENGTH];
            if (
IsClientConnected(client) &&
                
GetClientAuthString(client,steamid,sizeof(steamid)))
            {
                
strcopy(keysizeof(key), steamid);
            }
            else                
            {
                
Format(keysizeof(key), "%d"player_ident);
                
steamid[0] = '\0';
            }

            
// Start the thread count at 0
            
SetTrieValue(g_SaveThreadsInFlightkey0true);

            
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(namesizeof(name), playerName[client]);

            if (
name[0] == '\0')
            {
                
strcopy(namesizeof(name), playerName[client]);
                if (
name[0] == '\0')
                    
strcopy(namesizeof(name), playerPrevName[client]);
            }

            if (!
SQL_EscapeString(g_DbHandle[DB_MAIN], nameescNamesizeof(escName)))
            {
                
LogError("Unable to escape %s!"name);
                
strcopy(escNamesizeof(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_identcrystalsvespeneoverall_levelbitsescNameplayer_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"clientValidClientIndex(client), player_identnameracerace_identoverall_levelcrystalsvespene);

            if (
threaded && DatabaseAvailable(DB_THREAD))
            {
                
// Start threaded SQL update to update player table
                
new Handle:dataPack CreateDataPack();
                
WritePackCell(dataPackGetClientUserId(client));
                
WritePackCell(dataPackplayer_ident);
                
WritePackCell(dataPackcrystals);
                
WritePackCell(dataPackvespene);
                
WritePackString(dataPackkey);

                
UpdateThreadsInFlight(key1);
                
SQL_TQuery(g_DbHandle[DB_THREAD], SQL_UpdatePlayerSQLStringdataPack);
            }
            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], errorsizeof(error));

                    
// Retry Access denied or Lost connection errors.
                    
if (StrContains(error"denied") >= || StrContains(error"connection") >= 0)
                    {
                        
LogError("Initial attempt to update %d:%L's (ident=%d) player data failed: %s\nSQL:%s",
                                 
clientValidClientIndex(client), player_identerrorSQLString);

                        
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!",
                                         
clientValidClientIndex(client), player_ident);
                            }
                            else
                                
SQL_GetError(g_DbHandle[DB_MAIN], errorsizeof(error));
                        }
                    }

                    if (!
success)
                    {
                        
SetDatabaseSaved(clientfalse);
                        
LogError("Unable to update %d:%L's (ident=%d) player data (crystals=%d,vespene=%d): %s\nSQL:%s",
                                 
clientValidClientIndex(client), player_identcrystalsvespeneerrorSQLString);

                        
TraceReturn();
                        return;
                    }
                }
            }
            else
            {
                
LogError("Unable to connect to DB to update %d:%L's (ident=%d) player data",
                        
clientValidClientIndex(client), player_ident);

                
TraceReturn();
                return;
            }

            if (
race 0)
            {
                
Trace("Save %d:%L's race data, race=%d Data", \
                      
clientValidClientIndex(client), race);

                if (
SavePlayerRaceData(clientplayer_identracethreadedkey))
                    
SetDatabaseSaved(clienttrue);
            }
            else
                
SetDatabaseSaved(clienttrue);

            
// Check the thread count and cleanup
            // the entry if there aren't any
            
UpdateThreadsInFlight(key0);

            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_identsteamidescName);
                if (
threaded && DatabaseAvailable(DB_THREAD))
                {
                    new 
Handle:dataPack CreateDataPack();
                    
WritePackCell(dataPackGetClientUserId(client));
                    
WritePackCell(dataPackplayer_ident);

                    
// Start threaded SQL update to update alias table
                    
SQL_TQuery(g_DbHandle[DB_THREAD], SQL_UpdateAliasSQLStringdataPack);

                    
// 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], errorsizeof(error));

                        
// Retry Access denied or Lost connection errors.
                        
if (StrContains(error"denied") >= || StrContains(error"connection") >= 0)
                        {
                            
LogError("Initial attempt to update %d:%L's (ident=%d) alias data failed: %s\nSQL:%s",
                                     
clientValidClientIndex(client), player_identerrorSQLString);

                            
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!",
                                             
clientValidClientIndex(client), player_ident);
                                }
                                else
                                    
SQL_GetError(g_DbHandle[DB_MAIN], errorsizeof(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",
                                     
clientValidClientIndex(client), player_identerrorSQLString);
                        }
                    }
                    else
                    {
                        
strcopy(playerPrevName[client], sizeof(playerPrevName[]), name);
                    }
                }
                else
                {
                    
LogError("Unable to connect to DB to update %d:%L's (ident=%d) alias data.",
                             
clientValidClientIndex(client), player_ident);
                }
            }
        }
        else
        {
            
LogError("Unable to connect to DB to update %d:%L's (ident=%d) player data.",
                     
clientValidClientIndex(client), player_ident);
        }
    }
    else
        
InsertPlayerData(clientthreaded);

    
TraceReturn();
}

public 
SQL_UpdatePlayer(Handle:ownerHandle: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(datakeysizeof(key));

        if (
hndl != INVALID_HANDLE && error[0] == '\0')
            
UpdateThreadsInFlight(key, -1);
        else
        {
            if (
client 0)
                
SetDatabaseSaved(clientfalse);

            
// Retry Access denied or Lost connection errors.
            
if (StrContains(error"denied") >= || StrContains(error"connection") >= 0)
            {
                
// Invalidate and close the DB_THREAD handle (in the main thread)
                
CreateTimer(0.1CloseDatabaseTimerg_DbHandle[DB_THREAD],TIMER_FLAG_NO_MAPCHANGE);
                
g_DbHandle[DB_THREAD] = INVALID_HANDLE;

                if (
client 0)
                {
                    
// Retry saving the client's data
                    
CreateTimer(0.1Retry_SavePlayerDatauserid,TIMER_FLAG_NO_MAPCHANGE);
                }
            }
            else
            {
                
RemoveFromTrie(g_SaveThreadsInFlightkey);
                
LogError("Unable to update %d:%L's (ident=%d) player data (crystals=%d,vespene=%d,key=%s): %s",
                         
clientValidClientIndex(client), player_identcrystalsvespenekeyerror);

                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.00.85.02550025520.50.00.5);
                    
ShowHudText(client1"Unable to save your SourceCraft Data! %s"error);
                }
            }
        }
        
CloseHandle(data);
    }

    
TraceReturn();


Last edited by naris; 11-15-2010 at 14:11.
naris is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 03:39.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode