mirror of
https://github.com/DarkflameUniverse/DarkflameServer
synced 2024-08-30 18:43:58 +00:00
455f9470a5
* Move EntityManager to Game namespace * move initialization to later Need to wait for dZoneManager to be initialized. * Fix bugs - Cannot delete from a RandomAccessIterator while in a range based for loop. Touchup zone manager initialize replace magic numbers with better named constants replace magic zonecontrol id with a more readable hex alternative condense stack variables move initializers closer to their use initialize entity manager with zone control change initialize timings If zone is not zero we expect to initialize the entity manager during zone manager initialization Add constexpr for zone control LOT * Add proper error handling * revert vanity changes * Update WorldServer.cpp * Update dZoneManager.cpp
626 lines
23 KiB
C++
626 lines
23 KiB
C++
#include "UserManager.h"
|
|
#include <fstream>
|
|
#include <future>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
#include "Database.h"
|
|
#include "Game.h"
|
|
#include "dLogger.h"
|
|
#include "User.h"
|
|
#include <WorldPackets.h>
|
|
#include "Character.h"
|
|
#include <BitStream.h>
|
|
#include "PacketUtils.h"
|
|
#include "../dWorldServer/ObjectIDManager.h"
|
|
#include "dLogger.h"
|
|
#include "GeneralUtils.h"
|
|
#include "ZoneInstanceManager.h"
|
|
#include "dServer.h"
|
|
#include "Entity.h"
|
|
#include "EntityManager.h"
|
|
#include "SkillComponent.h"
|
|
#include "AssetManager.h"
|
|
#include "CDClientDatabase.h"
|
|
#include "eObjectBits.h"
|
|
#include "eGameMasterLevel.h"
|
|
#include "eCharacterCreationResponse.h"
|
|
#include "eRenameResponse.h"
|
|
#include "eConnectionType.h"
|
|
#include "eChatInternalMessageType.h"
|
|
|
|
UserManager* UserManager::m_Address = nullptr;
|
|
|
|
//Local functions as they aren't needed by anything else, leave the implementations at the bottom!
|
|
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle);
|
|
uint32_t FindCharPantsID(uint32_t pantsColor);
|
|
|
|
inline void StripCR(std::string& str) {
|
|
str.erase(std::remove(str.begin(), str.end(), '\r'), str.end());
|
|
}
|
|
|
|
void UserManager::Initialize() {
|
|
std::string line;
|
|
|
|
AssetMemoryBuffer fnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_first.txt");
|
|
if (!fnBuff.m_Success) {
|
|
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_first.txt").string().c_str());
|
|
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
|
|
}
|
|
std::istream fnStream = std::istream(&fnBuff);
|
|
while (std::getline(fnStream, line, '\n')) {
|
|
std::string name = line;
|
|
StripCR(name);
|
|
m_FirstNames.push_back(name);
|
|
}
|
|
fnBuff.close();
|
|
|
|
AssetMemoryBuffer mnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_middle.txt");
|
|
if (!mnBuff.m_Success) {
|
|
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_middle.txt").string().c_str());
|
|
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
|
|
}
|
|
std::istream mnStream = std::istream(&mnBuff);
|
|
while (std::getline(mnStream, line, '\n')) {
|
|
std::string name = line;
|
|
StripCR(name);
|
|
m_MiddleNames.push_back(name);
|
|
}
|
|
mnBuff.close();
|
|
|
|
AssetMemoryBuffer lnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_last.txt");
|
|
if (!lnBuff.m_Success) {
|
|
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_last.txt").string().c_str());
|
|
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
|
|
}
|
|
std::istream lnStream = std::istream(&lnBuff);
|
|
while (std::getline(lnStream, line, '\n')) {
|
|
std::string name = line;
|
|
StripCR(name);
|
|
m_LastNames.push_back(name);
|
|
}
|
|
lnBuff.close();
|
|
|
|
//Load our pre-approved names:
|
|
AssetMemoryBuffer chatListBuff = Game::assetManager->GetFileAsBuffer("chatplus_en_us.txt");
|
|
if (!chatListBuff.m_Success) {
|
|
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str());
|
|
throw std::runtime_error("Aborting initialization due to missing chat whitelist file.");
|
|
}
|
|
std::istream chatListStream = std::istream(&chatListBuff);
|
|
while (std::getline(chatListStream, line, '\n')) {
|
|
StripCR(line);
|
|
m_PreapprovedNames.push_back(line);
|
|
}
|
|
chatListBuff.close();
|
|
}
|
|
|
|
UserManager::~UserManager() {
|
|
|
|
}
|
|
|
|
User* UserManager::CreateUser(const SystemAddress& sysAddr, const std::string& username, const std::string& sessionKey) {
|
|
User* user = new User(sysAddr, username, sessionKey);
|
|
if (user && Game::server->IsConnected(sysAddr))
|
|
m_Users.insert(std::make_pair(sysAddr, user));
|
|
else {
|
|
if (user) {
|
|
delete user;
|
|
user = nullptr;
|
|
}
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
User* UserManager::GetUser(const SystemAddress& sysAddr) {
|
|
auto it = m_Users.find(sysAddr);
|
|
if (it != m_Users.end() && it->second) return it->second;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
User* UserManager::GetUser(const std::string& username) {
|
|
for (auto p : m_Users) {
|
|
if (p.second) {
|
|
if (p.second->GetUsername() == username) return p.second;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool UserManager::DeleteUser(const SystemAddress& sysAddr) {
|
|
const auto& it = m_Users.find(sysAddr);
|
|
|
|
if (it != m_Users.end()) {
|
|
if (std::count(m_UsersToDelete.begin(), m_UsersToDelete.end(), it->second)) return false;
|
|
|
|
m_UsersToDelete.push_back(it->second);
|
|
|
|
m_Users.erase(it);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UserManager::DeletePendingRemovals() {
|
|
for (auto* user : m_UsersToDelete) {
|
|
Game::logger->Log("UserManager", "Deleted user %i", user->GetAccountID());
|
|
|
|
delete user;
|
|
}
|
|
|
|
m_UsersToDelete.clear();
|
|
}
|
|
|
|
bool UserManager::IsNameAvailable(const std::string& requestedName) {
|
|
bool toReturn = false; //To allow for a clean exit
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? OR pending_name=? LIMIT 1;");
|
|
stmt->setString(1, requestedName.c_str());
|
|
stmt->setString(2, requestedName.c_str());
|
|
|
|
sql::ResultSet* res = stmt->executeQuery();
|
|
if (res->rowsCount() == 0) toReturn = true;
|
|
|
|
delete stmt;
|
|
delete res;
|
|
return toReturn;
|
|
}
|
|
|
|
std::string UserManager::GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex) {
|
|
if (firstNameIndex > m_FirstNames.size() || middleNameIndex > m_MiddleNames.size() || lastNameIndex > m_LastNames.size()) return std::string("INVALID");
|
|
return std::string(m_FirstNames[firstNameIndex] + m_MiddleNames[middleNameIndex] + m_LastNames[lastNameIndex]);
|
|
}
|
|
|
|
bool UserManager::IsNamePreapproved(const std::string& requestedName) {
|
|
for (std::string& s : m_PreapprovedNames) {
|
|
if (s == requestedName) return true;
|
|
}
|
|
|
|
for (std::string& s : m_FirstNames) {
|
|
if (s == requestedName) return true;
|
|
}
|
|
|
|
for (std::string& s : m_MiddleNames) {
|
|
if (s == requestedName) return true;
|
|
}
|
|
|
|
for (std::string& s : m_LastNames) {
|
|
if (s == requestedName) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
|
|
User* u = GetUser(sysAddr);
|
|
if (!u) return;
|
|
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 4;");
|
|
stmt->setUInt(1, u->GetAccountID());
|
|
|
|
sql::ResultSet* res = stmt->executeQuery();
|
|
if (res->rowsCount() > 0) {
|
|
std::vector<Character*>& chars = u->GetCharacters();
|
|
|
|
for (size_t i = 0; i < chars.size(); ++i) {
|
|
if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save
|
|
{
|
|
delete chars[i];
|
|
|
|
continue;
|
|
}
|
|
|
|
auto* skillComponent = chars[i]->GetEntity()->GetComponent<SkillComponent>();
|
|
|
|
if (skillComponent != nullptr) {
|
|
skillComponent->Reset();
|
|
}
|
|
|
|
Game::entityManager->DestroyEntity(chars[i]->GetEntity());
|
|
|
|
chars[i]->SaveXMLToDatabase();
|
|
|
|
chars[i]->GetEntity()->SetCharacter(nullptr);
|
|
|
|
delete chars[i];
|
|
}
|
|
|
|
chars.clear();
|
|
|
|
while (res->next()) {
|
|
LWOOBJID objID = res->getUInt64(1);
|
|
Character* character = new Character(uint32_t(objID), u);
|
|
character->SetIsNewLogin();
|
|
chars.push_back(character);
|
|
}
|
|
}
|
|
|
|
delete res;
|
|
delete stmt;
|
|
|
|
WorldPackets::SendCharacterList(sysAddr, u);
|
|
}
|
|
|
|
void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) {
|
|
User* u = GetUser(sysAddr);
|
|
if (!u) return;
|
|
|
|
std::string name = PacketUtils::ReadString(8, packet, true);
|
|
|
|
uint32_t firstNameIndex = PacketUtils::ReadPacketU32(74, packet);
|
|
uint32_t middleNameIndex = PacketUtils::ReadPacketU32(78, packet);
|
|
uint32_t lastNameIndex = PacketUtils::ReadPacketU32(82, packet);
|
|
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
|
|
|
|
uint32_t shirtColor = PacketUtils::ReadPacketU32(95, packet);
|
|
uint32_t shirtStyle = PacketUtils::ReadPacketU32(99, packet);
|
|
uint32_t pantsColor = PacketUtils::ReadPacketU32(103, packet);
|
|
uint32_t hairStyle = PacketUtils::ReadPacketU32(107, packet);
|
|
uint32_t hairColor = PacketUtils::ReadPacketU32(111, packet);
|
|
uint32_t lh = PacketUtils::ReadPacketU32(115, packet);
|
|
uint32_t rh = PacketUtils::ReadPacketU32(119, packet);
|
|
uint32_t eyebrows = PacketUtils::ReadPacketU32(123, packet);
|
|
uint32_t eyes = PacketUtils::ReadPacketU32(127, packet);
|
|
uint32_t mouth = PacketUtils::ReadPacketU32(131, packet);
|
|
|
|
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
|
|
LOT pantsLOT = FindCharPantsID(pantsColor);
|
|
|
|
if (name != "" && !UserManager::IsNameAvailable(name)) {
|
|
Game::logger->Log("UserManager", "AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str());
|
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE);
|
|
return;
|
|
}
|
|
|
|
if (!IsNameAvailable(predefinedName)) {
|
|
Game::logger->Log("UserManager", "AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE);
|
|
return;
|
|
}
|
|
|
|
if (name == "") {
|
|
Game::logger->Log("UserManager", "AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
|
} else {
|
|
Game::logger->Log("UserManager", "AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.c_str(), predefinedName.c_str());
|
|
}
|
|
|
|
//Now that the name is ok, we can get an objectID from Master:
|
|
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) {
|
|
sql::PreparedStatement* overlapStmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE id = ?");
|
|
overlapStmt->setUInt(1, objectID);
|
|
|
|
auto* overlapResult = overlapStmt->executeQuery();
|
|
|
|
if (overlapResult->next()) {
|
|
Game::logger->Log("UserManager", "Character object id unavailable, check objectidtracker!");
|
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
|
return;
|
|
}
|
|
|
|
std::stringstream xml;
|
|
xml << "<obj v=\"1\"><mf hc=\"" << hairColor << "\" hs=\"" << hairStyle << "\" hd=\"0\" t=\"" << shirtColor << "\" l=\"" << pantsColor;
|
|
xml << "\" hdc=\"0\" cd=\"" << shirtStyle << "\" lh=\"" << lh << "\" rh=\"" << rh << "\" es=\"" << eyebrows << "\" ";
|
|
xml << "ess=\"" << eyes << "\" ms=\"" << mouth << "\"/>";
|
|
|
|
xml << "<char acct=\"" << u->GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" ";
|
|
xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" ";
|
|
xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\"></char>";
|
|
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
|
|
xml << "<inv><bag><b t=\"0\" m=\"20\"/><b t=\"1\" m=\"40\"/><b t=\"2\" m=\"240\"/><b t=\"3\" m=\"240\"/><b t=\"14\" m=\"40\"/></bag><items><in t=\"0\">";
|
|
std::string xmlSave1 = xml.str();
|
|
|
|
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforshirt) {
|
|
std::stringstream xml2;
|
|
|
|
LWOOBJID lwoidforshirt = idforshirt;
|
|
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER);
|
|
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT);
|
|
xml2 << xmlSave1 << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
|
|
|
|
std::string xmlSave2 = xml2.str();
|
|
|
|
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforpants) {
|
|
LWOOBJID lwoidforpants = idforpants;
|
|
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
|
|
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
|
|
|
|
std::stringstream xml3;
|
|
xml3 << xmlSave2 << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
|
|
|
|
xml3 << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
|
|
|
|
//Check to see if our name was pre-approved:
|
|
bool nameOk = IsNamePreapproved(name);
|
|
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
|
|
|
if (name != "") {
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
|
|
stmt->setUInt(1, objectID);
|
|
stmt->setUInt(2, u->GetAccountID());
|
|
stmt->setString(3, predefinedName.c_str());
|
|
stmt->setString(4, name.c_str());
|
|
stmt->setBoolean(5, false);
|
|
stmt->setUInt64(6, time(NULL));
|
|
|
|
if (nameOk) {
|
|
stmt->setString(3, name.c_str());
|
|
stmt->setString(4, "");
|
|
}
|
|
|
|
stmt->execute();
|
|
delete stmt;
|
|
} else {
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
|
|
stmt->setUInt(1, objectID);
|
|
stmt->setUInt(2, u->GetAccountID());
|
|
stmt->setString(3, predefinedName.c_str());
|
|
stmt->setString(4, "");
|
|
stmt->setBoolean(5, false);
|
|
stmt->setUInt64(6, time(NULL));
|
|
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
|
|
//Now finally insert our character xml:
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charxml`(`id`, `xml_data`) VALUES (?,?)");
|
|
stmt->setUInt(1, objectID);
|
|
stmt->setString(2, xml3.str().c_str());
|
|
stmt->execute();
|
|
delete stmt;
|
|
|
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
|
|
UserManager::RequestCharacterList(sysAddr);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) {
|
|
User* u = GetUser(sysAddr);
|
|
if (!u) {
|
|
Game::logger->Log("UserManager", "Couldn't get user to delete character");
|
|
return;
|
|
}
|
|
|
|
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
|
|
uint32_t charID = static_cast<uint32_t>(objectID);
|
|
|
|
Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)", objectID, charID);
|
|
|
|
//Check if this user has this character:
|
|
bool hasCharacter = false;
|
|
std::vector<Character*>& characters = u->GetCharacters();
|
|
for (size_t i = 0; i < characters.size(); ++i) {
|
|
if (characters[i]->GetID() == charID) { hasCharacter = true; }
|
|
}
|
|
|
|
if (!hasCharacter) {
|
|
Game::logger->Log("UserManager", "User %i tried to delete a character that it does not own!", u->GetAccountID());
|
|
WorldPackets::SendCharacterDeleteResponse(sysAddr, false);
|
|
} else {
|
|
Game::logger->Log("UserManager", "Deleting character %i", charID);
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charxml WHERE id=? LIMIT 1;");
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM command_log WHERE character_id=?;");
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;");
|
|
stmt->setUInt(1, charID);
|
|
stmt->setUInt(2, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
CBITSTREAM;
|
|
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
|
|
bitStream.Write(objectID);
|
|
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;");
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
|
"DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);"
|
|
);
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM properties WHERE owner_id=?;");
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM ugc WHERE character_id=?;");
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM activity_log WHERE character_id=?;");
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM mail WHERE receiver_id=?;");
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
{
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charinfo WHERE id=? LIMIT 1;");
|
|
stmt->setUInt64(1, charID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
}
|
|
|
|
WorldPackets::SendCharacterDeleteResponse(sysAddr, true);
|
|
}
|
|
}
|
|
|
|
void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) {
|
|
User* u = GetUser(sysAddr);
|
|
if (!u) {
|
|
Game::logger->Log("UserManager", "Couldn't get user to delete character");
|
|
return;
|
|
}
|
|
|
|
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
|
|
GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER);
|
|
GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT);
|
|
|
|
uint32_t charID = static_cast<uint32_t>(objectID);
|
|
Game::logger->Log("UserManager", "Received char rename request for ID: %llu (%u)", objectID, charID);
|
|
|
|
std::string newName = PacketUtils::ReadString(16, packet, true);
|
|
|
|
Character* character = nullptr;
|
|
|
|
//Check if this user has this character:
|
|
bool hasCharacter = false;
|
|
std::vector<Character*>& characters = u->GetCharacters();
|
|
for (size_t i = 0; i < characters.size(); ++i) {
|
|
if (characters[i]->GetID() == charID) { hasCharacter = true; character = characters[i]; }
|
|
}
|
|
|
|
if (!hasCharacter || !character) {
|
|
Game::logger->Log("UserManager", "User %i tried to rename a character that it does not own!", u->GetAccountID());
|
|
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
|
|
} else if (hasCharacter && character) {
|
|
if (newName == character->GetName()) {
|
|
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE);
|
|
return;
|
|
}
|
|
|
|
if (IsNameAvailable(newName)) {
|
|
if (IsNamePreapproved(newName)) {
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET name=?, pending_name='', needs_rename=0, last_login=? WHERE id=? LIMIT 1");
|
|
stmt->setString(1, newName);
|
|
stmt->setUInt64(2, time(NULL));
|
|
stmt->setUInt(3, character->GetID());
|
|
stmt->execute();
|
|
delete stmt;
|
|
|
|
Game::logger->Log("UserManager", "Character %s now known as %s", character->GetName().c_str(), newName.c_str());
|
|
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
|
UserManager::RequestCharacterList(sysAddr);
|
|
} else {
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET pending_name=?, needs_rename=0, last_login=? WHERE id=? LIMIT 1");
|
|
stmt->setString(1, newName);
|
|
stmt->setUInt64(2, time(NULL));
|
|
stmt->setUInt(3, character->GetID());
|
|
stmt->execute();
|
|
delete stmt;
|
|
|
|
Game::logger->Log("UserManager", "Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str());
|
|
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
|
UserManager::RequestCharacterList(sysAddr);
|
|
}
|
|
} else {
|
|
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_IN_USE);
|
|
}
|
|
} else {
|
|
Game::logger->Log("UserManager", "Unknown error occurred when renaming character, either hasCharacter or character variable != true.");
|
|
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
|
|
}
|
|
}
|
|
|
|
void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID) {
|
|
User* u = GetUser(sysAddr);
|
|
if (!u) {
|
|
Game::logger->Log("UserManager", "Couldn't get user to log in character");
|
|
return;
|
|
}
|
|
|
|
Character* character = nullptr;
|
|
bool hasCharacter = false;
|
|
std::vector<Character*>& characters = u->GetCharacters();
|
|
|
|
for (size_t i = 0; i < characters.size(); ++i) {
|
|
if (characters[i]->GetID() == playerID) { hasCharacter = true; character = characters[i]; }
|
|
}
|
|
|
|
if (hasCharacter && character) {
|
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET last_login=? WHERE id=? LIMIT 1");
|
|
stmt->setUInt64(1, time(NULL));
|
|
stmt->setUInt(2, playerID);
|
|
stmt->execute();
|
|
delete stmt;
|
|
|
|
uint32_t zoneID = character->GetZoneID();
|
|
if (zoneID == LWOZONEID_INVALID) zoneID = 1000; //Send char to VE
|
|
|
|
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneID, character->GetZoneClone(), false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
|
Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
|
if (character) {
|
|
character->SetZoneID(zoneID);
|
|
character->SetZoneInstance(zoneInstance);
|
|
character->SetZoneClone(zoneClone);
|
|
}
|
|
WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift);
|
|
return;
|
|
});
|
|
} else {
|
|
Game::logger->Log("UserManager", "Unknown error occurred when logging in a character, either hasCharacter or character variable != true.");
|
|
}
|
|
}
|
|
|
|
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
|
|
try {
|
|
std::string shirtQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"character create shirt\" AND icc.color1 == ";
|
|
shirtQuery += std::to_string(shirtColor);
|
|
shirtQuery += " AND icc.decal == ";
|
|
shirtQuery = shirtQuery + std::to_string(shirtStyle);
|
|
auto tableData = CDClientDatabase::ExecuteQuery(shirtQuery);
|
|
auto shirtLOT = tableData.getIntField(0, -1);
|
|
tableData.finalize();
|
|
return shirtLOT;
|
|
} catch (const std::exception&) {
|
|
Game::logger->Log("Character Create", "Failed to execute query! Using backup...");
|
|
// in case of no shirt found in CDServer, return problematic red vest.
|
|
return 4069;
|
|
}
|
|
}
|
|
|
|
uint32_t FindCharPantsID(uint32_t pantsColor) {
|
|
try {
|
|
std::string pantsQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"cc pants\" AND icc.color1 == ";
|
|
pantsQuery += std::to_string(pantsColor);
|
|
auto tableData = CDClientDatabase::ExecuteQuery(pantsQuery);
|
|
auto pantsLOT = tableData.getIntField(0, -1);
|
|
tableData.finalize();
|
|
return pantsLOT;
|
|
} catch (const std::exception&) {
|
|
Game::logger->Log("Character Create", "Failed to execute query! Using backup...");
|
|
// in case of no pants color found in CDServer, return red pants.
|
|
return 2508;
|
|
}
|
|
}
|
|
|
|
void UserManager::SaveAllActiveCharacters() {
|
|
for (auto user : m_Users) {
|
|
if (user.second) {
|
|
auto character = user.second->GetLastUsedChar();
|
|
if (character) character->SaveXMLToDatabase();
|
|
}
|
|
}
|
|
}
|