Core/Bnet: Changed login REST service to use async db queries instead of spawning a thread for each request
This commit is contained in:
@@ -163,7 +163,7 @@ int main(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!sLoginService.Start(*ioService))
|
||||
if (!sLoginService.Start(ioService.get()))
|
||||
{
|
||||
TC_LOG_ERROR("server.bnetserver", "Failed to initialize login service");
|
||||
return 1;
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include "LoginRESTService.h"
|
||||
#include "Configuration/Config.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Errors.h"
|
||||
#include "ProtobufJSON.h"
|
||||
#include "Optional.h"
|
||||
#include "Realm.h"
|
||||
#include "SessionManager.h"
|
||||
#include "SHA1.h"
|
||||
@@ -31,6 +33,37 @@
|
||||
|
||||
int ns1__executeCommand(soap*, char*, char**) { return SOAP_OK; }
|
||||
|
||||
class AsyncLoginRequest
|
||||
{
|
||||
public:
|
||||
AsyncLoginRequest(std::shared_ptr<soap> client)
|
||||
: _client(std::move(client)) { }
|
||||
|
||||
AsyncLoginRequest(AsyncLoginRequest const&) = delete;
|
||||
AsyncLoginRequest& operator=(AsyncLoginRequest const&) = delete;
|
||||
AsyncLoginRequest(AsyncLoginRequest&&) = default;
|
||||
AsyncLoginRequest& operator=(AsyncLoginRequest&&) = default;
|
||||
|
||||
bool InvokeIfReady()
|
||||
{
|
||||
ASSERT(_callback.is_initialized());
|
||||
return _callback->InvokeIfReady() == QueryCallback::Completed;
|
||||
}
|
||||
|
||||
soap* GetClient() const { return _client.get(); }
|
||||
void SetCallback(QueryCallback val) { _callback = std::move(val); }
|
||||
std::unique_ptr<Battlenet::Session::AccountInfo>& GetResult() { return _result; }
|
||||
void SetResult(std::unique_ptr<Battlenet::Session::AccountInfo> val) { _result = std::move(val); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<soap> _client;
|
||||
Optional<QueryCallback> _callback;
|
||||
std::unique_ptr<Battlenet::Session::AccountInfo> _result;
|
||||
};
|
||||
|
||||
/* Codes 600 to 999 are user definable */
|
||||
#define SOAP_CUSTOM_STATUS_ASYNC 600
|
||||
|
||||
int32 handle_get_plugin(soap* soapClient)
|
||||
{
|
||||
return sLoginService.HandleGet(soapClient);
|
||||
@@ -41,8 +74,9 @@ int32 handle_post_plugin(soap* soapClient)
|
||||
return sLoginService.HandlePost(soapClient);
|
||||
}
|
||||
|
||||
bool LoginRESTService::Start(boost::asio::io_service& ioService)
|
||||
bool LoginRESTService::Start(boost::asio::io_service* ioService)
|
||||
{
|
||||
_ioService = ioService;
|
||||
_bindIP = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
|
||||
_port = sConfigMgr->GetIntDefault("LoginREST.Port", 8081);
|
||||
if (_port < 0 || _port > 0xFFFF)
|
||||
@@ -52,7 +86,7 @@ bool LoginRESTService::Start(boost::asio::io_service& ioService)
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ip::tcp::resolver resolver(ioService);
|
||||
boost::asio::ip::tcp::resolver resolver(*ioService);
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
|
||||
std::string configuredAddress = sConfigMgr->GetStringDefault("LoginREST.ExternalAddress", "127.0.0.1");
|
||||
@@ -97,7 +131,7 @@ bool LoginRESTService::Start(boost::asio::io_service& ioService)
|
||||
input->set_type("submit");
|
||||
input->set_label("Log In");
|
||||
|
||||
_loginTicketCleanupTimer = new boost::asio::deadline_timer(ioService);
|
||||
_loginTicketCleanupTimer = new boost::asio::deadline_timer(*ioService);
|
||||
_loginTicketCleanupTimer->expires_from_now(boost::posix_time::seconds(10));
|
||||
_loginTicketCleanupTimer->async_wait(std::bind(&LoginRESTService::CleanupLoginTickets, this, std::placeholders::_1));
|
||||
|
||||
@@ -172,10 +206,13 @@ void LoginRESTService::Run()
|
||||
|
||||
TC_LOG_DEBUG("server.rest", "Accepted connection from IP=%s", address.to_string().c_str());
|
||||
|
||||
std::thread([soapClient]
|
||||
_ioService->post([soapClient]()
|
||||
{
|
||||
soap_serve(soapClient.get());
|
||||
}).detach();
|
||||
soapClient->user = (void*)&soapClient; // this allows us to make a copy of pointer inside GET/POST handlers to increment reference count
|
||||
soap_begin(soapClient.get());
|
||||
if (soap_begin_recv(soapClient.get()) != SOAP_CUSTOM_STATUS_ASYNC)
|
||||
soap_closesock(soapClient.get());
|
||||
});
|
||||
}
|
||||
|
||||
// and release the context handle here - soap does not own it so it should not free it on exit
|
||||
@@ -214,7 +251,6 @@ int32 LoginRESTService::HandlePost(soap* soapClient)
|
||||
soap_http_body(soapClient, &buf, &len);
|
||||
|
||||
Battlenet::JSON::Login::LoginForm loginForm;
|
||||
Battlenet::JSON::Login::LoginResult loginResult;
|
||||
if (!JSON::Deserialize(buf, &loginForm))
|
||||
{
|
||||
if (soap_register_plugin_arg(soapClient, &ResponseCodePlugin::Init, nullptr) != SOAP_OK)
|
||||
@@ -225,6 +261,7 @@ int32 LoginRESTService::HandlePost(soap* soapClient)
|
||||
|
||||
responseCode->ErrorCode = 400;
|
||||
|
||||
Battlenet::JSON::Login::LoginResult loginResult;
|
||||
loginResult.set_authentication_state(Battlenet::JSON::Login::LOGIN);
|
||||
loginResult.set_error_code("UNABLE_TO_DECODE");
|
||||
loginResult.set_error_message("There was an internal error while connecting to Battle.net. Please try again later.");
|
||||
@@ -248,99 +285,127 @@ int32 LoginRESTService::HandlePost(soap* soapClient)
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_INFO);
|
||||
stmt->setString(0, login);
|
||||
|
||||
if (PreparedQueryResult result = LoginDatabase.Query(stmt))
|
||||
std::string sentPasswordHash = CalculateShaPassHash(login, password);
|
||||
|
||||
std::shared_ptr<AsyncLoginRequest> request = std::make_shared<AsyncLoginRequest>(*reinterpret_cast<std::shared_ptr<soap>*>(soapClient->user));
|
||||
request->SetCallback(LoginDatabase.AsyncQuery(stmt)
|
||||
.WithChainingPreparedCallback([request, login, sentPasswordHash](QueryCallback& callback, PreparedQueryResult result)
|
||||
{
|
||||
std::string pass_hash = result->Fetch()[13].GetString();
|
||||
|
||||
std::unique_ptr<Battlenet::Session::AccountInfo> accountInfo = Trinity::make_unique<Battlenet::Session::AccountInfo>();
|
||||
accountInfo->LoadResult(result);
|
||||
|
||||
if (CalculateShaPassHash(login, std::move(password)) == pass_hash)
|
||||
if (result)
|
||||
{
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID);
|
||||
stmt->setUInt32(0, accountInfo->Id);
|
||||
if (PreparedQueryResult characterCountsResult = LoginDatabase.Query(stmt))
|
||||
std::string pass_hash = result->Fetch()[13].GetString();
|
||||
|
||||
request->SetResult(Trinity::make_unique<Battlenet::Session::AccountInfo>());
|
||||
request->GetResult()->LoadResult(result);
|
||||
|
||||
if (sentPasswordHash == pass_hash)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = characterCountsResult->Fetch();
|
||||
accountInfo->GameAccounts[fields[0].GetUInt32()]
|
||||
.CharacterCounts[Battlenet::RealmHandle{ fields[3].GetUInt8(), fields[4].GetUInt8(), fields[2].GetUInt32() }.GetAddress()] = fields[1].GetUInt8();
|
||||
|
||||
} while (characterCountsResult->NextRow());
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID);
|
||||
stmt->setUInt32(0, request->GetResult()->Id);
|
||||
callback.SetNextQuery(LoginDatabase.AsyncQuery(stmt));
|
||||
return;
|
||||
}
|
||||
else if (!request->GetResult()->IsBanned)
|
||||
{
|
||||
std::string ip_address = boost::asio::ip::address_v4(request->GetClient()->ip).to_string();
|
||||
uint32 maxWrongPassword = uint32(sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0));
|
||||
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_LAST_PLAYER_CHARACTERS);
|
||||
stmt->setUInt32(0, accountInfo->Id);
|
||||
if (PreparedQueryResult lastPlayerCharactersResult = LoginDatabase.Query(stmt))
|
||||
if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false))
|
||||
TC_LOG_DEBUG("server.rest", "[%s, Account %s, Id %u] Attempted to connect with wrong password!", ip_address.c_str(), login.c_str(), request->GetResult()->Id);
|
||||
|
||||
if (maxWrongPassword)
|
||||
{
|
||||
SQLTransaction trans = LoginDatabase.BeginTransaction();
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_FAILED_LOGINS);
|
||||
stmt->setUInt32(0, request->GetResult()->Id);
|
||||
trans->Append(stmt);
|
||||
|
||||
++request->GetResult()->FailedLogins;
|
||||
|
||||
TC_LOG_DEBUG("server.rest", "MaxWrongPass : %u, failed_login : %u", maxWrongPassword, request->GetResult()->Id);
|
||||
|
||||
if (request->GetResult()->FailedLogins >= maxWrongPassword)
|
||||
{
|
||||
BanMode banType = BanMode(sConfigMgr->GetIntDefault("WrongPass.BanType", uint16(BanMode::BAN_IP)));
|
||||
int32 banTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
|
||||
|
||||
if (banType == BanMode::BAN_ACCOUNT)
|
||||
{
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ACCOUNT_AUTO_BANNED);
|
||||
stmt->setUInt32(0, request->GetResult()->Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
|
||||
stmt->setString(0, ip_address);
|
||||
}
|
||||
|
||||
stmt->setUInt32(1, banTime);
|
||||
trans->Append(stmt);
|
||||
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_RESET_FAILED_LOGINS);
|
||||
stmt->setUInt32(0, request->GetResult()->Id);
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
Battlenet::JSON::Login::LoginResult loginResult;
|
||||
loginResult.set_authentication_state(Battlenet::JSON::Login::DONE);
|
||||
sLoginService.SendResponse(request->GetClient(), loginResult);
|
||||
})
|
||||
.WithChainingPreparedCallback([request](QueryCallback& callback, PreparedQueryResult characterCountsResult)
|
||||
{
|
||||
if (characterCountsResult)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = characterCountsResult->Fetch();
|
||||
request->GetResult()->GameAccounts[fields[0].GetUInt32()]
|
||||
.CharacterCounts[Battlenet::RealmHandle{ fields[3].GetUInt8(), fields[4].GetUInt8(), fields[2].GetUInt32() }.GetAddress()] = fields[1].GetUInt8();
|
||||
|
||||
} while (characterCountsResult->NextRow());
|
||||
}
|
||||
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_LAST_PLAYER_CHARACTERS);
|
||||
stmt->setUInt32(0, request->GetResult()->Id);
|
||||
callback.SetNextQuery(LoginDatabase.AsyncQuery(stmt));
|
||||
})
|
||||
.WithPreparedCallback([request](PreparedQueryResult lastPlayerCharactersResult)
|
||||
{
|
||||
if (lastPlayerCharactersResult)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = lastPlayerCharactersResult->Fetch();
|
||||
Battlenet::RealmHandle realmId{ fields[1].GetUInt8(), fields[2].GetUInt8(), fields[3].GetUInt32() };
|
||||
Battlenet::Session::LastPlayedCharacterInfo& lastPlayedCharacter = accountInfo->GameAccounts[fields[0].GetUInt32()]
|
||||
Battlenet::Session::LastPlayedCharacterInfo& lastPlayedCharacter = request->GetResult()->GameAccounts[fields[0].GetUInt32()]
|
||||
.LastPlayedCharacters[realmId.GetSubRegionAddress()];
|
||||
|
||||
lastPlayedCharacter.RealmId = realmId;
|
||||
lastPlayedCharacter.CharacterName = fields[4].GetString();
|
||||
lastPlayedCharacter.CharacterGUID = fields[5].GetUInt64();
|
||||
lastPlayedCharacter.LastPlayedTime = fields[6].GetUInt32();
|
||||
}
|
||||
|
||||
BigNumber ticket;
|
||||
ticket.SetRand(20 * 8);
|
||||
|
||||
loginResult.set_login_ticket("TC-" + ByteArrayToHexStr(ticket.AsByteArray(20).get(), 20));
|
||||
|
||||
AddLoginTicket(loginResult.login_ticket(), std::move(accountInfo));
|
||||
} while (lastPlayerCharactersResult->NextRow());
|
||||
}
|
||||
else if (!accountInfo->IsBanned)
|
||||
{
|
||||
uint32 maxWrongPassword = uint32(sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0));
|
||||
|
||||
if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false))
|
||||
TC_LOG_DEBUG("server.rest", "[%s, Account %s, Id %u] Attempted to connect with wrong password!", ip_address.c_str(), login.c_str(), accountInfo->Id);
|
||||
BigNumber ticket;
|
||||
ticket.SetRand(20 * 8);
|
||||
|
||||
if (maxWrongPassword)
|
||||
{
|
||||
SQLTransaction trans = LoginDatabase.BeginTransaction();
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_FAILED_LOGINS);
|
||||
stmt->setUInt32(0, accountInfo->Id);
|
||||
trans->Append(stmt);
|
||||
Battlenet::JSON::Login::LoginResult loginResult;
|
||||
loginResult.set_authentication_state(Battlenet::JSON::Login::DONE);
|
||||
loginResult.set_login_ticket("TC-" + ByteArrayToHexStr(ticket.AsByteArray(20).get(), 20));
|
||||
sLoginService.SendResponse(request->GetClient(), loginResult);
|
||||
|
||||
++accountInfo->FailedLogins;
|
||||
sLoginService.AddLoginTicket(loginResult.login_ticket(), std::move(request->GetResult()));
|
||||
}));
|
||||
|
||||
TC_LOG_DEBUG("server.rest", "MaxWrongPass : %u, failed_login : %u", maxWrongPassword, accountInfo->Id);
|
||||
_ioService->post(std::bind(&LoginRESTService::HandleAsyncRequest, this, std::move(request)));
|
||||
|
||||
if (accountInfo->FailedLogins >= maxWrongPassword)
|
||||
{
|
||||
BanMode banType = BanMode(sConfigMgr->GetIntDefault("WrongPass.BanType", uint16(BanMode::BAN_IP)));
|
||||
int32 banTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
|
||||
|
||||
if (banType == BanMode::BAN_ACCOUNT)
|
||||
{
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ACCOUNT_AUTO_BANNED);
|
||||
stmt->setUInt32(0, accountInfo->Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
|
||||
stmt->setString(0, ip_address);
|
||||
}
|
||||
|
||||
stmt->setUInt32(1, banTime);
|
||||
trans->Append(stmt);
|
||||
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_RESET_FAILED_LOGINS);
|
||||
stmt->setUInt32(0, accountInfo->Id);
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loginResult.set_authentication_state(Battlenet::JSON::Login::DONE);
|
||||
return SendResponse(soapClient, loginResult);
|
||||
return SOAP_CUSTOM_STATUS_ASYNC;
|
||||
}
|
||||
|
||||
int32 LoginRESTService::SendResponse(soap* soapClient, google::protobuf::Message const& response)
|
||||
@@ -352,6 +417,12 @@ int32 LoginRESTService::SendResponse(soap* soapClient, google::protobuf::Message
|
||||
return soap_end_send(soapClient);
|
||||
}
|
||||
|
||||
void LoginRESTService::HandleAsyncRequest(std::shared_ptr<AsyncLoginRequest> request)
|
||||
{
|
||||
if (!request->InvokeIfReady())
|
||||
_ioService->post(std::bind(&LoginRESTService::HandleAsyncRequest, this, std::move(request)));
|
||||
}
|
||||
|
||||
std::string LoginRESTService::CalculateShaPassHash(std::string const& name, std::string const& password)
|
||||
{
|
||||
SHA256Hash email;
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
#ifndef LoginRESTService_h__
|
||||
#define LoginRESTService_h__
|
||||
|
||||
#include "Session.h"
|
||||
#include "Define.h"
|
||||
#include "Session.h"
|
||||
#include "Login.pb.h"
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
class AsyncLoginRequest;
|
||||
struct soap;
|
||||
struct soap_plugin;
|
||||
|
||||
@@ -41,11 +42,11 @@ enum class BanMode
|
||||
class LoginRESTService
|
||||
{
|
||||
public:
|
||||
LoginRESTService() : _stopped(false), _port(0), _loginTicketCleanupTimer(nullptr) { }
|
||||
LoginRESTService() : _ioService(nullptr), _stopped(false), _port(0), _loginTicketCleanupTimer(nullptr) { }
|
||||
|
||||
static LoginRESTService& Instance();
|
||||
|
||||
bool Start(boost::asio::io_service& ioService);
|
||||
bool Start(boost::asio::io_service* ioService);
|
||||
void Stop();
|
||||
|
||||
boost::asio::ip::tcp::endpoint const& GetAddressForClient(boost::asio::ip::address const& address) const;
|
||||
@@ -63,6 +64,8 @@ private:
|
||||
|
||||
int32 SendResponse(soap* soapClient, google::protobuf::Message const& response);
|
||||
|
||||
void HandleAsyncRequest(std::shared_ptr<AsyncLoginRequest> request);
|
||||
|
||||
std::string CalculateShaPassHash(std::string const& name, std::string const& password);
|
||||
|
||||
void AddLoginTicket(std::string const& id, std::unique_ptr<Battlenet::Session::AccountInfo> accountInfo);
|
||||
@@ -99,6 +102,7 @@ private:
|
||||
char const* ContentType;
|
||||
};
|
||||
|
||||
boost::asio::io_service* _ioService;
|
||||
std::thread _thread;
|
||||
std::atomic<bool> _stopped;
|
||||
Battlenet::JSON::Login::FormInputs _formInputs;
|
||||
|
||||
@@ -121,12 +121,12 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
||||
|
||||
PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT " BnetAccountInfo ", " BnetGameAccountInfo ", ba.sha_pass_hash"
|
||||
" FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account"
|
||||
" LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? ORDER BY a.id", CONNECTION_SYNCH);
|
||||
" LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? ORDER BY a.id", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_BNET_GAME_ACCOUNT_LOGIN_INFO, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_ACCOUNT_ID, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id LEFT JOIN account a ON rc.acctid = a.id WHERE a.battlenet_account = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_BNET_LAST_PLAYER_CHARACTERS, "SELECT lpc.accountId, lpc.region, lpc.battlegroup, lpc.realmId, lpc.characterName, lpc.characterGUID, lpc.lastPlayedTime FROM account_last_played_character lpc LEFT JOIN account a ON lpc.accountId = a.id WHERE a.battlenet_account = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id LEFT JOIN account a ON rc.acctid = a.id WHERE a.battlenet_account = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_BNET_LAST_PLAYER_CHARACTERS, "SELECT lpc.accountId, lpc.region, lpc.battlegroup, lpc.realmId, lpc.characterName, lpc.characterGUID, lpc.lastPlayedTime FROM account_last_played_character lpc LEFT JOIN account a ON lpc.accountId = a.id WHERE a.battlenet_account = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_DEL_BNET_LAST_PLAYER_CHARACTERS, "DELETE FROM account_last_played_character WHERE accountId = ? AND region = ? AND battlegroup = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_INS_BNET_LAST_PLAYER_CHARACTERS, "INSERT INTO account_last_played_character (accountId, region, battlegroup, realmId, characterName, characterGUID, lastPlayedTime) VALUES (?,?,?,?,?,?,?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_INS_BNET_ACCOUNT, "INSERT INTO battlenet_accounts (`email`,`sha_pass_hash`) VALUES (?, ?)", CONNECTION_SYNCH);
|
||||
|
||||
Reference in New Issue
Block a user