I was thinking about shared plat the other night and how to keep people from duping shared plat on servers that allow more than one login per account like Knightly mentioned. I wrote up a simple hack to make it use the account table, but it does allow duping when you login two toons on the same account, as expected.
I thought I'd post this and see what you guys think is the best way to fix it.
Basically, it's two functions which get and set the shared plat value in the profile from the shared_plat field in the account table. When you zone or click on coins in the shared bank slot, the profile value is updated prior to the 'move coin' action happening. The plat is still stored in the profile, it's just updated from the DB before calculating how much to move.
This all works fine for single logins, but again, will allow duping with multiple logins. I tested it with 1018 and 1019 and it seems to work fine. Don't run this on your live server though, as it will set everyone's shared plat to whatever is in their shared_plat field in the DB.
To eliminate duping, I'm thinking we need to send a packet telling the client to delete the coins on the cursor when the server says the count is wrong. I'm guessing that the reason shared items go poof if you try to dupe them in the bank window is because they are instanced in the DB. For coins, the client seems to ignore what is in the profile and will 'pick up' whatever amount the bank window shows even if the shared plat value is zero.
1. Prototype the two new functions. Add the parts in red into common/shareddb.h at line 40.
Code:
sint32 DeleteStalePlayerCorpses();
sint32 DeleteStalePlayerBackups();
bool SetSharedPlatinum(uint32 account_id, sint32 amount_to_add);
uint32 GetSharedPlatinum(uint32 account_id);
2. Then add the functions to common/shareddb.cpp at line 46
Code:
uint32 SharedDatabase::GetSharedPlatinum(int32 account_id)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT sharedplat FROM account WHERE id='%i'", account_id), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1)
{
row = mysql_fetch_row(result);
uint32 shared_platinum = atoi(row[0]);
mysql_free_result(result);
return shared_platinum;
}
else
{
mysql_free_result(result);
return 0;
}
mysql_free_result(result);
}
else
{
cerr << "Error in GetSharedPlatinum query '" << query << "' " << errbuf << endl;
safe_delete_array(query);
return false;
}
return 0;
}
bool SharedDatabase::SetSharedPlatinum(uint32 account_id, sint32 amount_to_add)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id), errbuf)) {
cerr << "Error in SetSharedPlatinum query '" << query << "' " << errbuf << endl;
safe_delete_array(query);
return false;
}
safe_delete_array(query);
return true;
}
3. In zone/clientprocess.cpp at line 1280, add
Code:
if(to_bucket)
{
if(*to_bucket + amount_to_add > *to_bucket) // overflow check
*to_bucket += amount_to_add;
if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared)
{
if (from_bucket == &m_pp.platinum_shared)
amount_to_add = 0 - amount_to_take;
database.SetSharedPlatinum(AccountID(),amount_to_add);
}
}
4. In zone/zonedb.cpp at line 706, add
Code:
uint32 char_id = atoi(row[0]);
pp->platinum_shared = database.GetSharedPlatinum(GetAccountIDByChar(char_id));
Any thoughts ?