I need to implement a verification if the user is already logged in. At the moment my game doesn't verify this, so an account can log-in several times and I want to avoid this.
This is how I'm doing log-in authentication on game server:
void Session::authLogin(uint32_t id, std::string username, std::string token)
{
// Return a valid alive connection, if doesn't exist, return nullptr
auto dbcon = DatabaseManager::getFreeConnection();
// Wait for a free connection
if(!dbcon || dbcon->is_locked())
{
dbWait->expires_from_now(
boost::posix_time::microseconds(
DatabaseManager::DB_WAIT_TIME));
dbWait->async_wait(boost::bind(
&awaitAuthLogin,
boost::asio::placeholders::error,
weak_from_this(),
id, username, token));
return;
}
// Lock connection
DatabaseManager::Locker lock(dbcon);
// Get Account Data
auto pstmt = dbcon->prepare_statement(
"SELECT `username`,`storage_gold`,`coins`,`ctl_code`,`vip_level`,"
"unix_timestamp(`vip_expire_date`) FROM `account_data` WHERE `id`=? AND `username`=?");
pstmt->push_uint(id);
pstmt->push_string(username);
uint32_t storage_gold = 0;
uint32_t coins = 0;
int32_t ctl_code = 0;
uint32_t vip_level = 0;
uint32_t vip_expire_date = 0;
auto result = pstmt->execute_query();
if(result->next())
{
//Check token
pstmt = dbcon->prepare_statement(
"SELECT COUNT(*) FROM `token` WHERE `account_id`=? AND `token`=?");
pstmt->push_uint(id);
pstmt->push_string(token);
auto token_result = pstmt->execute_query();
if(!token_result->next())
{
// Invalid Token
sendMessage(strdef::INVALID_TOKEN);
closeSession();
return;
}
storage_gold = result.get_int("storage_gold"),
coins = result.get_int("coins"),
ctl_code = result.get_int("ctl_code"),
vip_level = result.get_int("vip_level");
vip_expire_date = result.get_uint("unix_timestamp(`vip_expire_date`)");
}
else
{
// Invalid Login
sendMessage(strdef::INVALID_USERNAME);
closeSession();
return;
}
// TODO: Check if account is logged in
// *** I was thinking of doing an INSERT and
// if I get an ER_DUP_ENTRY error it's because the account is logged in.
try {
// Insert account_stat
pstmt = dbcon->prepare_statement(
"INSERT INTO `account_stat` (`account_id`,`server_id`,`endpoint`,`mac`) VALUES(?,?,?,?)");
pstmt->push_uint(id);
pstmt->push_uint(getServerId());
pstmt->push_string(getEndpoint());
pstmt->push_string(getMacAddress());
pstmt->execute();
}
catch(const mysql::exception& e) {
// Already logged in
if(e.error_code() == ER_DUP_ENTRY){
sendMessage(strdef::ALREADY_LOGGED_IN);
closeSession();
return;
}
}
// ***
authLoginTimeout->cancel();
// Make AccountInfo
accountInfo = std::make_unique<AccountInfo>(id,
username,
token,
storage_gold,
coins,
ctl_code,
vip_level;
vip_expire_date);
// Load account storage
loadStorage(id);
// Send character list
sendToCharacterLobby(id);
}
Can I have a problem doing this? Is there another way? I need it to be safe for multiple gameservers/dataservers.
If you have any tips to optimize it I accept :-)
Thanks in advance for helping me with this.