2021-12-05 17:54:36 +00:00
# include "UserManager.h"
# include <fstream>
# include <future>
# include <sstream>
2021-12-06 06:04:18 +00:00
# include <algorithm>
2021-12-05 17:54:36 +00:00
# include "Database.h"
# include "Game.h"
2022-03-24 07:09:18 +00:00
# include "dLogger.h"
2021-12-05 17:54:36 +00:00
# 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"
2022-11-26 10:29:53 +00:00
# include "AssetManager.h"
2022-12-18 15:46:04 +00:00
# include "CDClientDatabase.h"
2023-04-25 18:17:40 +00:00
# include "eObjectBits.h"
2023-03-24 23:16:45 +00:00
# include "eGameMasterLevel.h"
2023-05-02 22:39:21 +00:00
# include "eCharacterCreationResponse.h"
# include "eRenameResponse.h"
2023-05-03 21:38:32 +00:00
# include "eConnectionType.h"
# include "eChatInternalMessageType.h"
2023-09-21 01:06:28 +00:00
# include "BitStreamUtils.h"
2023-09-28 17:16:11 +00:00
# include "CheatDetection.h"
2021-12-05 17:54:36 +00:00
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 ) ;
2021-12-06 06:04:18 +00:00
inline void StripCR ( std : : string & str ) {
str . erase ( std : : remove ( str . begin ( ) , str . end ( ) , ' \r ' ) , str . end ( ) ) ;
}
2021-12-05 17:54:36 +00:00
void UserManager : : Initialize ( ) {
2021-12-06 06:04:18 +00:00
std : : string line ;
2021-12-05 17:54:36 +00:00
2022-11-26 10:29:53 +00:00
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 ' ) ) {
2021-12-05 17:54:36 +00:00
std : : string name = line ;
2021-12-06 06:04:18 +00:00
StripCR ( name ) ;
m_FirstNames . push_back ( name ) ;
}
2022-11-26 10:29:53 +00:00
fnBuff . close ( ) ;
2021-12-05 17:54:36 +00:00
2022-11-26 10:29:53 +00:00
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 ' ) ) {
2021-12-05 17:54:36 +00:00
std : : string name = line ;
2021-12-06 06:04:18 +00:00
StripCR ( name ) ;
m_MiddleNames . push_back ( name ) ;
}
2022-11-26 10:29:53 +00:00
mnBuff . close ( ) ;
2021-12-05 17:54:36 +00:00
2022-11-26 10:29:53 +00:00
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 ' ) ) {
2021-12-05 17:54:36 +00:00
std : : string name = line ;
2021-12-06 06:04:18 +00:00
StripCR ( name ) ;
m_LastNames . push_back ( name ) ;
}
2022-11-26 10:29:53 +00:00
lnBuff . close ( ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
//Load our pre-approved names:
2022-11-26 10:29:53 +00:00
AssetMemoryBuffer chatListBuff = Game : : assetManager - > GetFileAsBuffer ( " chatplus_en_us.txt " ) ;
if ( ! chatListBuff . m_Success ) {
2022-11-27 09:06:17 +00:00
Game : : logger - > Log ( " UserManager " , " Failed to load %s " , ( Game : : assetManager - > GetResPath ( ) / " chatplus_en_us.txt " ) . string ( ) . c_str ( ) ) ;
2022-11-26 10:29:53 +00:00
throw std : : runtime_error ( " Aborting initialization due to missing chat whitelist file. " ) ;
}
std : : istream chatListStream = std : : istream ( & chatListBuff ) ;
while ( std : : getline ( chatListStream , line , ' \n ' ) ) {
2021-12-06 06:04:18 +00:00
StripCR ( line ) ;
2021-12-05 17:54:36 +00:00
m_PreapprovedNames . push_back ( line ) ;
}
2022-11-26 10:29:53 +00:00
chatListBuff . close ( ) ;
2021-12-05 17:54:36 +00:00
}
UserManager : : ~ UserManager ( ) {
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
}
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 ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
m_Users . erase ( it ) ;
return true ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
return false ;
}
2022-07-25 02:26:51 +00:00
void UserManager : : DeletePendingRemovals ( ) {
2021-12-05 17:54:36 +00:00
for ( auto * user : m_UsersToDelete ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Deleted user %i " , user - > GetAccountID ( ) ) ;
2021-12-05 17:54:36 +00:00
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 ( ) ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
sql : : ResultSet * res = stmt - > executeQuery ( ) ;
if ( res - > rowsCount ( ) = = 0 ) toReturn = true ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
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 ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
for ( std : : string & s : m_FirstNames ) {
if ( s = = requestedName ) return true ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
for ( std : : string & s : m_MiddleNames ) {
if ( s = = requestedName ) return true ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
for ( std : : string & s : m_LastNames ) {
if ( s = = requestedName ) return true ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
return false ;
}
void UserManager : : RequestCharacterList ( const SystemAddress & sysAddr ) {
User * u = GetUser ( sysAddr ) ;
if ( ! u ) return ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
sql : : PreparedStatement * stmt = Database : : CreatePreppedStmt ( " SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 4; " ) ;
stmt - > setUInt ( 1 , u - > GetAccountID ( ) ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
sql : : ResultSet * res = stmt - > executeQuery ( ) ;
2023-09-24 07:55:54 +00:00
std : : vector < Character * > & chars = u - > GetCharacters ( ) ;
2022-07-25 02:26:51 +00:00
2023-09-24 07:55:54 +00:00
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 ] ;
2021-12-05 17:54:36 +00:00
2023-09-24 07:55:54 +00:00
continue ;
}
2021-12-05 17:54:36 +00:00
2023-09-24 07:55:54 +00:00
auto * skillComponent = chars [ i ] - > GetEntity ( ) - > GetComponent < SkillComponent > ( ) ;
2021-12-05 17:54:36 +00:00
2023-09-24 07:55:54 +00:00
if ( skillComponent ! = nullptr ) {
skillComponent - > Reset ( ) ;
}
2021-12-05 17:54:36 +00:00
2023-09-24 07:55:54 +00:00
Game : : entityManager - > DestroyEntity ( chars [ i ] - > GetEntity ( ) ) ;
2021-12-05 17:54:36 +00:00
2023-09-24 07:55:54 +00:00
chars [ i ] - > SaveXMLToDatabase ( ) ;
2021-12-05 17:54:36 +00:00
2023-09-24 07:55:54 +00:00
chars [ i ] - > GetEntity ( ) - > SetCharacter ( nullptr ) ;
delete chars [ i ] ;
}
2022-07-25 02:26:51 +00:00
2023-09-24 07:55:54 +00:00
chars . clear ( ) ;
2022-07-25 02:26:51 +00:00
2023-09-24 07:55:54 +00:00
while ( res - > next ( ) ) {
LWOOBJID objID = res - > getUInt64 ( 1 ) ;
Character * character = new Character ( uint32_t ( objID ) , u ) ;
character - > SetIsNewLogin ( ) ;
chars . push_back ( character ) ;
2021-12-05 17:54:36 +00:00
}
delete res ;
delete stmt ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
WorldPackets : : SendCharacterList ( sysAddr , u ) ;
}
void UserManager : : CreateCharacter ( const SystemAddress & sysAddr , Packet * packet ) {
User * u = GetUser ( sysAddr ) ;
if ( ! u ) return ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
std : : string name = PacketUtils : : ReadString ( 8 , packet , true ) ;
2022-07-28 13:39:57 +00:00
2023-09-21 01:06:28 +00:00
uint32_t firstNameIndex = PacketUtils : : ReadU32 ( 74 , packet ) ;
uint32_t middleNameIndex = PacketUtils : : ReadU32 ( 78 , packet ) ;
uint32_t lastNameIndex = PacketUtils : : ReadU32 ( 82 , packet ) ;
2021-12-05 17:54:36 +00:00
std : : string predefinedName = GetPredefinedName ( firstNameIndex , middleNameIndex , lastNameIndex ) ;
2022-07-28 13:39:57 +00:00
2023-09-21 01:06:28 +00:00
uint32_t shirtColor = PacketUtils : : ReadU32 ( 95 , packet ) ;
uint32_t shirtStyle = PacketUtils : : ReadU32 ( 99 , packet ) ;
uint32_t pantsColor = PacketUtils : : ReadU32 ( 103 , packet ) ;
uint32_t hairStyle = PacketUtils : : ReadU32 ( 107 , packet ) ;
uint32_t hairColor = PacketUtils : : ReadU32 ( 111 , packet ) ;
uint32_t lh = PacketUtils : : ReadU32 ( 115 , packet ) ;
uint32_t rh = PacketUtils : : ReadU32 ( 119 , packet ) ;
uint32_t eyebrows = PacketUtils : : ReadU32 ( 123 , packet ) ;
uint32_t eyes = PacketUtils : : ReadU32 ( 127 , packet ) ;
uint32_t mouth = PacketUtils : : ReadU32 ( 131 , packet ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
LOT shirtLOT = FindCharShirtID ( shirtColor , shirtStyle ) ;
LOT pantsLOT = FindCharPantsID ( pantsColor ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
if ( name ! = " " & & ! UserManager : : IsNameAvailable ( name ) ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " AccountID: %i chose unavailable name: %s " , u - > GetAccountID ( ) , name . c_str ( ) ) ;
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterCreationResponse ( sysAddr , eCharacterCreationResponse : : CUSTOM_NAME_IN_USE ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
if ( ! IsNameAvailable ( predefinedName ) ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " AccountID: %i chose unavailable predefined name: %s " , u - > GetAccountID ( ) , predefinedName . c_str ( ) ) ;
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterCreationResponse ( sysAddr , eCharacterCreationResponse : : PREDEFINED_NAME_IN_USE ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-28 13:39:57 +00:00
2022-02-03 22:03:54 +00:00
if ( name = = " " ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " AccountID: %i is creating a character with predefined name: %s " , u - > GetAccountID ( ) , predefinedName . c_str ( ) ) ;
2022-02-03 22:03:54 +00:00
} else {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " AccountID: %i is creating a character with name: %s (temporary: %s) " , u - > GetAccountID ( ) , name . c_str ( ) , predefinedName . c_str ( ) ) ;
2022-02-03 22:03:54 +00:00
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
//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 ( ) ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Character object id unavailable, check objectidtracker! " ) ;
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterCreationResponse ( sysAddr , eCharacterCreationResponse : : OBJECT_ID_UNAVAILABLE ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
std : : stringstream xml ;
xml < < " <obj v= \" 1 \" ><mf hc= \" " < < hairColor < < " \" hs= \" " < < hairStyle < < " \" hd= \" 0 \" t= \" " < < shirtColor < < " \" l= \" " < < pantsColor ;
2022-01-01 17:23:14 +00:00
xml < < " \" hdc= \" 0 \" cd= \" " < < shirtStyle < < " \" lh= \" " < < lh < < " \" rh= \" " < < rh < < " \" es= \" " < < eyebrows < < " \" " ;
xml < < " ess= \" " < < eyes < < " \" ms= \" " < < mouth < < " \" /> " ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
xml < < " <char acct= \" " < < u - > GetAccountID ( ) < < " \" cc= \" 0 \" gm= \" 0 \" ft= \" 0 \" llog= \" " < < time ( NULL ) < < " \" " ;
2022-01-01 17:23:14 +00:00
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> " ;
2021-12-05 17:54:36 +00:00
xml < < " <dest hm= \" 4 \" hc= \" 4 \" im= \" 0 \" ic= \" 0 \" am= \" 0 \" ac= \" 0 \" d= \" 0 \" /> " ;
2022-04-13 08:49:55 +00:00
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 \" > " ;
2021-12-05 17:54:36 +00:00
std : : string xmlSave1 = xml . str ( ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
ObjectIDManager : : Instance ( ) - > RequestPersistentID ( [ = ] ( uint32_t idforshirt ) {
std : : stringstream xml2 ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
LWOOBJID lwoidforshirt = idforshirt ;
2023-04-25 18:17:40 +00:00
GeneralUtils : : SetBit ( lwoidforshirt , eObjectBits : : CHARACTER ) ;
GeneralUtils : : SetBit ( lwoidforshirt , eObjectBits : : PERSISTENT ) ;
2021-12-05 17:54:36 +00:00
xml2 < < xmlSave1 < < " <i l= \" " < < shirtLOT < < " \" id= \" " < < lwoidforshirt < < " \" s= \" 0 \" c= \" 1 \" eq= \" 1 \" b= \" 1 \" /> " ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
std : : string xmlSave2 = xml2 . str ( ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
ObjectIDManager : : Instance ( ) - > RequestPersistentID ( [ = ] ( uint32_t idforpants ) {
LWOOBJID lwoidforpants = idforpants ;
2023-04-25 18:17:40 +00:00
GeneralUtils : : SetBit ( lwoidforpants , eObjectBits : : CHARACTER ) ;
GeneralUtils : : SetBit ( lwoidforpants , eObjectBits : : PERSISTENT ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
std : : stringstream xml3 ;
xml3 < < xmlSave2 < < " <i l= \" " < < pantsLOT < < " \" id= \" " < < lwoidforpants < < " \" s= \" 1 \" c= \" 1 \" eq= \" 1 \" b= \" 1 \" /> " ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
xml3 < < " </in></items></inv><lvl l= \" 1 \" cv= \" 1 \" sb= \" 500 \" /><flag></flag></obj> " ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
//Check to see if our name was pre-approved:
bool nameOk = IsNamePreapproved ( name ) ;
2023-03-24 23:16:45 +00:00
if ( ! nameOk & & u - > GetMaxGMLevel ( ) > eGameMasterLevel : : FORUM_MODERATOR ) nameOk = true ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
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 ) ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
if ( nameOk ) {
stmt - > setString ( 3 , name . c_str ( ) ) ;
stmt - > setString ( 4 , " " ) ;
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
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 ) ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
stmt - > execute ( ) ;
delete stmt ;
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
//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 ;
2022-07-28 13:39:57 +00:00
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterCreationResponse ( sysAddr , eCharacterCreationResponse : : SUCCESS ) ;
2021-12-05 17:54:36 +00:00
UserManager : : RequestCharacterList ( sysAddr ) ;
} ) ;
} ) ;
} ) ;
}
void UserManager : : DeleteCharacter ( const SystemAddress & sysAddr , Packet * packet ) {
User * u = GetUser ( sysAddr ) ;
if ( ! u ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Couldn't get user to delete character " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-25 02:26:51 +00:00
2023-09-21 01:06:28 +00:00
LWOOBJID objectID = PacketUtils : : ReadS64 ( 8 , packet ) ;
2022-07-13 03:36:06 +00:00
uint32_t charID = static_cast < uint32_t > ( objectID ) ;
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Received char delete req for ID: %llu (%u) " , objectID , charID ) ;
2022-07-28 13:39:57 +00:00
2023-09-28 17:16:11 +00:00
bool hasCharacter = CheatDetection : : VerifyLwoobjidIsSender (
objectID ,
sysAddr ,
CheckType : : User ,
" User %i tried to delete a character that it does not own! " ,
u - > GetAccountID ( ) ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
if ( ! hasCharacter ) {
WorldPackets : : SendCharacterDeleteResponse ( sysAddr , false ) ;
} else {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Deleting character %i " , charID ) ;
2021-12-05 17:54:36 +00:00
{
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=?; " ) ;
2022-07-13 03:36:06 +00:00
stmt - > setUInt ( 1 , charID ) ;
stmt - > setUInt ( 2 , charID ) ;
2021-12-05 17:54:36 +00:00
stmt - > execute ( ) ;
delete stmt ;
2022-07-13 03:36:06 +00:00
CBITSTREAM ;
2023-09-21 01:06:28 +00:00
BitStreamUtils : : WriteHeader ( bitStream , eConnectionType : : CHAT_INTERNAL , eChatInternalMessageType : : PLAYER_REMOVED_NOTIFICATION ) ;
2022-07-13 03:36:06 +00:00
bitStream . Write ( objectID ) ;
Game : : chatServer - > Send ( & bitStream , SYSTEM_PRIORITY , RELIABLE , 0 , Game : : chatSysAddr , false ) ;
2021-12-05 17:54:36 +00:00
}
{
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 ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
WorldPackets : : SendCharacterDeleteResponse ( sysAddr , true ) ;
}
}
void UserManager : : RenameCharacter ( const SystemAddress & sysAddr , Packet * packet ) {
User * u = GetUser ( sysAddr ) ;
if ( ! u ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Couldn't get user to delete character " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-28 13:39:57 +00:00
2023-09-21 01:06:28 +00:00
LWOOBJID objectID = PacketUtils : : ReadS64 ( 8 , packet ) ;
2023-04-25 18:17:40 +00:00
GeneralUtils : : ClearBit ( objectID , eObjectBits : : CHARACTER ) ;
GeneralUtils : : ClearBit ( objectID , eObjectBits : : PERSISTENT ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
uint32_t charID = static_cast < uint32_t > ( objectID ) ;
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Received char rename request for ID: %llu (%u) " , objectID , charID ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
std : : string newName = PacketUtils : : ReadString ( 16 , packet , true ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
Character * character = nullptr ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
//Check if this user has this character:
2023-09-28 17:16:11 +00:00
bool ownsCharacter = CheatDetection : : VerifyLwoobjidIsSender (
objectID ,
sysAddr ,
CheckType : : User ,
" User %i tried to rename a character that it does not own! " ,
u - > GetAccountID ( ) ) ;
std : : find_if ( u - > GetCharacters ( ) . begin ( ) , u - > GetCharacters ( ) . end ( ) , [ & ] ( Character * c ) {
if ( c - > GetID ( ) = = charID ) {
character = c ;
return true ;
}
return false ;
} ) ;
2022-07-28 13:39:57 +00:00
2023-09-28 17:16:11 +00:00
if ( ! ownsCharacter | | ! character ) {
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterRenameResponse ( sysAddr , eRenameResponse : : UNKNOWN_ERROR ) ;
2023-09-28 17:16:11 +00:00
} else if ( ownsCharacter & & character ) {
2021-12-05 17:54:36 +00:00
if ( newName = = character - > GetName ( ) ) {
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterRenameResponse ( sysAddr , eRenameResponse : : NAME_UNAVAILABLE ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
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 ;
2022-07-28 13:39:57 +00:00
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Character %s now known as %s " , character - > GetName ( ) . c_str ( ) , newName . c_str ( ) ) ;
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterRenameResponse ( sysAddr , eRenameResponse : : SUCCESS ) ;
2021-12-05 17:54:36 +00:00
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 ;
2022-07-28 13:39:57 +00:00
2022-07-25 02:26:51 +00:00
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 ( ) ) ;
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterRenameResponse ( sysAddr , eRenameResponse : : SUCCESS ) ;
2021-12-05 17:54:36 +00:00
UserManager : : RequestCharacterList ( sysAddr ) ;
}
} else {
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterRenameResponse ( sysAddr , eRenameResponse : : NAME_IN_USE ) ;
2021-12-05 17:54:36 +00:00
}
} else {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Unknown error occurred when renaming character, either hasCharacter or character variable != true. " ) ;
2023-05-02 22:39:21 +00:00
WorldPackets : : SendCharacterRenameResponse ( sysAddr , eRenameResponse : : UNKNOWN_ERROR ) ;
2021-12-05 17:54:36 +00:00
}
}
void UserManager : : LoginCharacter ( const SystemAddress & sysAddr , uint32_t playerID ) {
User * u = GetUser ( sysAddr ) ;
if ( ! u ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Couldn't get user to log in character " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
Character * character = nullptr ;
bool hasCharacter = false ;
std : : vector < Character * > & characters = u - > GetCharacters ( ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
for ( size_t i = 0 ; i < characters . size ( ) ; + + i ) {
if ( characters [ i ] - > GetID ( ) = = playerID ) { hasCharacter = true ; character = characters [ i ] ; }
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
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 ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
uint32_t zoneID = character - > GetZoneID ( ) ;
if ( zoneID = = LWOZONEID_INVALID ) zoneID = 1000 ; //Send char to VE
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
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 ) {
2022-07-25 02:26:51 +00:00
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 ) ;
2021-12-05 17:54:36 +00:00
if ( character ) {
character - > SetZoneID ( zoneID ) ;
character - > SetZoneInstance ( zoneInstance ) ;
character - > SetZoneClone ( zoneClone ) ;
}
WorldPackets : : SendTransferToWorld ( sysAddr , serverIP , serverPort , mythranShift ) ;
return ;
} ) ;
} else {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " UserManager " , " Unknown error occurred when logging in a character, either hasCharacter or character variable != true. " ) ;
2021-12-05 17:54:36 +00:00
}
}
2021-12-26 13:45:21 +00:00
uint32_t FindCharShirtID ( uint32_t shirtColor , uint32_t shirtStyle ) {
2022-03-24 07:09:18 +00:00
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 & ) {
2022-03-24 07:33:05 +00:00
Game : : logger - > Log ( " Character Create " , " Failed to execute query! Using backup... " ) ;
2022-03-24 07:09:18 +00:00
// in case of no shirt found in CDServer, return problematic red vest.
return 4069 ;
2021-12-05 17:54:36 +00:00
}
}
uint32_t FindCharPantsID ( uint32_t pantsColor ) {
2022-03-24 07:30:52 +00:00
try {
2022-03-25 00:51:21 +00:00
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 ) ;
2022-03-24 07:30:52 +00:00
auto pantsLOT = tableData . getIntField ( 0 , - 1 ) ;
tableData . finalize ( ) ;
return pantsLOT ;
} catch ( const std : : exception & ) {
2022-03-24 07:33:05 +00:00
Game : : logger - > Log ( " Character Create " , " Failed to execute query! Using backup... " ) ;
2022-03-24 07:30:52 +00:00
// in case of no pants color found in CDServer, return red pants.
return 2508 ;
2021-12-05 17:54:36 +00:00
}
}
void UserManager : : SaveAllActiveCharacters ( ) {
for ( auto user : m_Users ) {
if ( user . second ) {
auto character = user . second - > GetLastUsedChar ( ) ;
if ( character ) character - > SaveXMLToDatabase ( ) ;
}
}
}