Skip to content

Commit

Permalink
Implement trial restrictions for unverified accounts.
Browse files Browse the repository at this point in the history
  • Loading branch information
ratkosrb committed Jul 1, 2024
1 parent 50fa052 commit 60a555f
Show file tree
Hide file tree
Showing 20 changed files with 409 additions and 144 deletions.
25 changes: 25 additions & 0 deletions sql/migrations/20240701043632_world.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
SET NAMES utf8;
DROP PROCEDURE IF EXISTS add_migration;
DELIMITER ??
CREATE PROCEDURE `add_migration`()
BEGIN
DECLARE v INT DEFAULT 1;
SET v = (SELECT COUNT(*) FROM `migrations` WHERE `id`='20240701043632');
IF v = 0 THEN
INSERT INTO `migrations` VALUES ('20240701043632');
-- Add your query below.


-- Add texts for trial account restrictions.
SET NAMES utf8;
INSERT INTO `mangos_string` (`entry`, `content_default`, `content_loc1`, `content_loc2`, `content_loc3`, `content_loc4`, `content_loc5`, `content_loc6`, `content_loc7`, `content_loc8`) VALUES (182, 'You may only whisper to players who have you on their friend list.', '당신을 친구 목록에 추가한 플레이어에게만 귓속말을 할 수 있습니다.', 'Vous ne pouvez chuchoter qu\'aux joueurs qui vous ont sur leur liste d\'amis.', 'Sie dürfen nur mit Spielern flüstern, die Sie auf ihrer Freundesliste haben.', '您只能与将您添加到好友列表中的玩家私聊。', '您只能對好友清單中有您的玩家耳語。', 'Sólo puedes susurrarle a los jugadores que te tengan en su lista de amigos.', 'Sólo puedes susurrarle a los jugadores que te tengan en su lista de amigos.', 'Вы можете шептаться только с игроками, у которых вы есть в списке друзей.');
INSERT INTO `mangos_string` (`entry`, `content_default`, `content_loc1`, `content_loc2`, `content_loc3`, `content_loc4`, `content_loc5`, `content_loc6`, `content_loc7`, `content_loc8`) VALUES (183, 'You may only send messages in private chat channels.', '비공개 채팅 채널에서만 메시지를 보낼 수 있습니다.', 'Vous ne pouvez envoyer des messages que sur des canaux de discussion privés.', 'Sie können nur in privaten Chat-Kanälen Nachrichten senden.', '您只能在私人聊天频道中发送消息。', '您只能在私人聊天頻道中發送訊息。', 'Sólo puedes enviar mensajes en canales de chat privados.', 'Sólo puedes enviar mensajes en canales de chat privados.', 'Вы можете отправлять сообщения только в приватные каналы чата.');
INSERT INTO `mangos_string` (`entry`, `content_default`, `content_loc1`, `content_loc2`, `content_loc3`, `content_loc4`, `content_loc5`, `content_loc6`, `content_loc7`, `content_loc8`) VALUES (184, 'Trial accounts cannot perform that action.', '무료 체험 계정으로는 해당 기능을 사용할 수 없습니다.', 'Les comptes d\'essai ne peuvent pas accomplir cette action.', 'Diese Aktion ist mit Gästeaccounts nicht durchführbar.', '试玩帐号无法进行该动作', '試玩帳號無法進行此動作', 'Las cuentas de prueba no pueden realizar esta acción.', 'Las cuentas de prueba no pueden realizar esta acción.', 'Пользователи пробной версии не могут выполнить это действие.');


-- End of migration.
END IF;
END??
DELIMITER ;
CALL add_migration();
DROP PROCEDURE IF EXISTS add_migration;
136 changes: 82 additions & 54 deletions src/game/AccountMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname,
if (!update_sv)
return AOR_DB_INTERNAL_ERROR; // unexpected error

GetAccountPersistentData(accid).m_username = new_uname;

return AOR_OK;
}

Expand Down Expand Up @@ -205,72 +207,47 @@ uint32 AccountMgr::GetId(std::string username)

void AccountMgr::Load()
{
m_accountSecurity.clear();

std::unique_ptr<QueryResult> result(LoginDatabase.PQuery("SELECT `id`, `gmlevel` FROM `account_access` WHERE (`RealmID` = '%u' OR `RealmID`='-1')", realmID));

if (!result)
{
BarGoLink bar(1);
bar.step();

sLog.Out(LOG_BASIC, LOG_LVL_MINIMAL, "");
sLog.Out(LOG_BASIC, LOG_LVL_MINIMAL, ">> Loaded 0 GM ranks");
return;
}

Field* fields = nullptr;
BarGoLink bar(result->GetRowCount());
do
{
bar.step();
fields = result->Fetch();
uint32 accountId = fields[0].GetUInt32();
AccountTypes secu = AccountTypes(fields[1].GetUInt32());
switch (secu)
{
case SEC_PLAYER:
break;
case SEC_MODERATOR:
case SEC_TICKETMASTER:
case SEC_GAMEMASTER:
case SEC_BASIC_ADMIN:
case SEC_DEVELOPER:
case SEC_ADMINISTRATOR:
// Peut etre deja dans la liste ? On prend le plus haut gmlevel.
if (m_accountSecurity.find(accountId) == m_accountSecurity.end() ||
m_accountSecurity[accountId] < secu)
m_accountSecurity[accountId] = secu;
break;
}
} while (result->NextRow());

sLog.Out(LOG_BASIC, LOG_LVL_MINIMAL, "");
sLog.Out(LOG_BASIC, LOG_LVL_MINIMAL, ">> %u GM ranks loaded for realm %u", m_accountSecurity.size(), realmID);
sLog.Out(LOG_BASIC, LOG_LVL_MINIMAL, "");
LoadAccountData();
LoadAccountBanList();
LoadIPBanList(std::move(LoginDatabase.Query(LOAD_IP_BANS_QUERY)));
LoadAccountWarnings();
}

AccountTypes AccountMgr::GetSecurity(uint32 acc_id)
AccountTypes AccountMgr::GetSecurity(uint32 accountId)
{
std::map<uint32, AccountTypes>::const_iterator it = m_accountSecurity.find(acc_id);
if (it == m_accountSecurity.end())
return SEC_PLAYER;
return it->second;
return GetAccountPersistentData(accountId).m_security;
}

void AccountMgr::SetSecurity(uint32 accId, AccountTypes sec)
void AccountMgr::SetSecurity(uint32 accountId, AccountTypes security)
{
m_accountSecurity[accId] = sec;
LoginDatabase.PExecute("DELETE FROM `account_access` WHERE `RealmID`=%u AND `id`=%u", realmID, accId);
LoginDatabase.PExecute("INSERT INTO `account_access` SET `RealmID`=%u, `id`=%u, `gmlevel`=%u", realmID, accId, sec);
GetAccountPersistentData(accountId).m_security = security;
LoginDatabase.PExecute("REPLACE INTO `account_access` (`id`, `gmlevel`, `RealmID`) VALUES (%u, %u, %u)", accountId, security, realmID);
}

bool AccountMgr::GetName(uint32 acc_id, std::string &name)
bool AccountMgr::HasTrialRestrictions(uint32 accountId)
{
std::unique_ptr<QueryResult> result = LoginDatabase.PQuery("SELECT `username` FROM `account` WHERE `id` = '%u'", acc_id);
if (sWorld.getConfig(CONFIG_BOOL_RESTRICT_UNVERIFIED_ACCOUNTS))
{
AccountPersistentData const& data = GetAccountPersistentData(accountId);
return !data.m_verifiedEmail && data.m_security <= SEC_PLAYER;
}

return false;
}

bool AccountMgr::GetName(uint32 accountId, std::string &name)
{
{
std::shared_lock<std::shared_timed_mutex> guard(m_accountPersistentDataMutex);
auto itr = m_accountPersistentData.find(accountId);
if (itr != m_accountPersistentData.end() && !itr->second.m_email.empty())
{
name = itr->second.m_email;
return true;
}
}

std::unique_ptr<QueryResult> result = LoginDatabase.PQuery("SELECT `username` FROM `account` WHERE `id` = '%u'", accountId);
if (result)
{
name = (*result)[0].GetCppString();
Expand Down Expand Up @@ -567,3 +544,54 @@ bool AccountPersistentData::CanMail(uint32 targetAccount)
uint32 allowedScore = sWorld.getConfig(CONFIG_UINT32_MAILSPAM_MAX_MAILS);
return totalScore < allowedScore;
}

AccountPersistentData& AccountMgr::GetAccountPersistentData(uint32 accountId)
{
{
std::shared_lock<std::shared_timed_mutex> guard(m_accountPersistentDataMutex);
auto itr = m_accountPersistentData.find(accountId);
if (itr != m_accountPersistentData.end())
return itr->second;
}

{
std::lock_guard<std::shared_timed_mutex> guard(m_accountPersistentDataMutex);
return m_accountPersistentData[accountId];
}
}

void AccountMgr::LoadAccountData()
{
std::unique_ptr<QueryResult> result(LoginDatabase.PQuery("SELECT a.`id`, a.`username`, a.`email`, a.`email_verif`, aa.`gmlevel` FROM `account` a LEFT JOIN `account_access` aa ON a.`id` = aa.`id` AND aa.`RealmID` IN (-1, %u)", realmID));

if (!result)
return;

do
{
Field* fields = result->Fetch();
uint32 id = fields[0].GetUInt32();
AccountPersistentData& data = m_accountPersistentData[id];
data.m_username = fields[1].GetCppString();
data.m_email = fields[2].GetCppString();
data.m_verifiedEmail = fields[3].GetBool() || data.m_email.empty(); // treat no email as verified (created from console)

// gmlevel can be null
if (fields[4].GetString())
{
AccountTypes security = AccountTypes(fields[4].GetUInt32());
if (data.m_security < security)
data.m_security = security;
}

} while (result->NextRow());
}

void AccountMgr::UpdateAccountData(uint32 accountId, std::string const& username, std::string const& email, bool verifiedEmail, AccountTypes security)
{
AccountPersistentData& data = m_accountPersistentData[accountId];
data.m_username = username;
data.m_email = email;
data.m_verifiedEmail = verifiedEmail;
data.m_security = security;
}
44 changes: 27 additions & 17 deletions src/game/AccountMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ enum AccountOpResult
AOR_DB_INTERNAL_ERROR
};

class AccountMgr;
class WorldSession;
class ChatHandler;
class MasterPlayer;
Expand All @@ -48,8 +49,10 @@ class QueryResult;

class AccountPersistentData
{
// WHISP FLOOD
friend class AccountMgr;
public:

// WHISP FLOOD
struct WhisperData
{
WhisperData() : first_whisp(time(nullptr)), score(0), whispers_count(0) {}
Expand All @@ -63,15 +66,20 @@ class AccountPersistentData
uint32 GetWhisperScore(MasterPlayer* from, MasterPlayer* player) const;
uint32 CountDifferentWhispTargets() const { return m_whisperTargets.size(); }

typedef std::map<uint32 /*lowguid*/, WhisperData> WhispersMap;
WhispersMap m_whisperTargets;

// MAIL FLOOD
public:
// MAIL FLOOD
void JustMailed(uint32 toAccount);
bool CanMail(uint32 targetAccount);

protected:
typedef std::map<uint32, time_t> MailsSentMap;
std::string m_username;
std::string m_email;
bool m_verifiedEmail = true;
AccountTypes m_security = SEC_PLAYER;

typedef std::map<uint32 /*lowguid*/, WhisperData> WhispersMap;
WhispersMap m_whisperTargets;

typedef std::map<uint32 /*account*/, time_t> MailsSentMap;
MailsSentMap m_mailsSent;
};

Expand All @@ -86,19 +94,22 @@ class AccountMgr
AccountOpResult ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd);
AccountOpResult ChangePassword(uint32 accid, std::string new_passwd, std::string username="");
bool CheckPassword(uint32 accid, std::string passwd, std::string username="");

uint32 GetId(std::string username);
std::string CalculateShaPassHash(std::string& name, std::string& password);

void Load();
AccountTypes GetSecurity(uint32 acc_id);
void SetSecurity(uint32 accId, AccountTypes sec);
void LoadAccountData();
AccountPersistentData& GetAccountPersistentData(uint32 accountId);
void UpdateAccountData(uint32 accountId, std::string const& username, std::string const& email, bool verifiedEmail, AccountTypes security);

bool GetName(uint32 acc_id, std::string &name);
uint32 GetId(std::string username);
AccountTypes GetSecurity(uint32 accountId);
void SetSecurity(uint32 accountId, AccountTypes security);
bool GetName(uint32 accountId, std::string &name);
uint32 GetCharactersCount(uint32 acc_id);
std::string CalculateShaPassHash(std::string& name, std::string& password);
bool HasTrialRestrictions(uint32 accountId);

static bool normalizeString(std::string& utf8str);
// Nostalrius

void Update(uint32 diff);
void LoadIPBanList(std::unique_ptr<QueryResult> result, bool silent=false);
void LoadAccountBanList(bool silent=false);
Expand All @@ -122,11 +133,8 @@ class AccountMgr
// Max instance reset per account per hour
bool CheckInstanceCount(uint32 accountId, uint32 instanceId, uint32 maxCount);
void AddInstanceEnterTime(uint32 accountId, uint32 instanceId, time_t enterTime);

AccountPersistentData& GetAccountPersistentData(uint32 accountId) { return m_accountPersistentData[accountId]; }
protected:
std::map<uint32, std::string> m_accountWarnings;
std::map<uint32, AccountTypes> m_accountSecurity;
uint32 m_banlistUpdateTimer;
std::map<std::string, uint32> m_ipBanned;
mutable std::shared_timed_mutex m_ipBannedMutex;
Expand All @@ -135,7 +143,9 @@ class AccountMgr
typedef std::map<uint32 /* accountId */, InstanceEnterTimesMap> AccountInstanceEnterTimesMap;
AccountInstanceEnterTimesMap m_instanceEnterTimes;
std::map<uint32, AccountPersistentData> m_accountPersistentData;
std::shared_timed_mutex m_accountPersistentDataMutex;
};

#define sAccountMgr MaNGOS::Singleton<AccountMgr>::Instance()

#endif
3 changes: 3 additions & 0 deletions src/game/Database/DBCEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
// also see MAX_LEVEL and GT_MAX_LEVEL define
#define PLAYER_STRONG_MAX_LEVEL 255

// Max level for trial accounts.
#define TRIAL_MAX_LEVEL 20

// Max creature level in vanilla (included some bosses and elite) (no cls data above this level)
#define CREATURE_MAX_LEVEL 63

Expand Down
Loading

0 comments on commit 60a555f

Please sign in to comment.