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"
2023-10-21 23:31:55 +00:00
# include "Logger.h"
2021-12-05 17:54:36 +00:00
# include "User.h"
2024-01-05 12:33:52 +00:00
# include "WorldPackets.h"
2021-12-05 17:54:36 +00:00
# include "Character.h"
2024-01-05 12:33:52 +00:00
# include "BitStream.h"
2024-01-05 12:31:22 +00:00
# include "ObjectIDManager.h"
2023-10-21 23:31:55 +00:00
# include "Logger.h"
2021-12-05 17:54:36 +00:00
# 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"
2024-04-01 03:27:50 +00:00
# include "eChatMessageType.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
2023-12-23 17:24:16 +00:00
auto fnStream = Game : : assetManager - > GetFile ( " names/minifigname_first.txt " ) ;
if ( ! fnStream ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to load %s " , ( Game : : assetManager - > GetResPath ( ) / " names/minifigname_first.txt " ) . string ( ) . c_str ( ) ) ;
2022-11-26 10:29:53 +00:00
throw std : : runtime_error ( " Aborting initialization due to missing minifigure name file. " ) ;
}
2023-12-23 17:24:16 +00:00
2022-11-26 10:29:53 +00:00
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 ) ;
}
2021-12-05 17:54:36 +00:00
2023-12-23 17:24:16 +00:00
auto mnStream = Game : : assetManager - > GetFile ( " names/minifigname_middle.txt " ) ;
if ( ! mnStream ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to load %s " , ( Game : : assetManager - > GetResPath ( ) / " names/minifigname_middle.txt " ) . string ( ) . c_str ( ) ) ;
2022-11-26 10:29:53 +00:00
throw std : : runtime_error ( " Aborting initialization due to missing minifigure name file. " ) ;
}
2023-12-23 17:24:16 +00:00
2022-11-26 10:29:53 +00:00
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 ) ;
}
2021-12-05 17:54:36 +00:00
2023-12-23 17:24:16 +00:00
auto lnStream = Game : : assetManager - > GetFile ( " names/minifigname_last.txt " ) ;
if ( ! lnStream ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to load %s " , ( Game : : assetManager - > GetResPath ( ) / " names/minifigname_last.txt " ) . string ( ) . c_str ( ) ) ;
2022-11-26 10:29:53 +00:00
throw std : : runtime_error ( " Aborting initialization due to missing minifigure name file. " ) ;
}
2023-12-23 17:24:16 +00:00
2022-11-26 10:29:53 +00:00
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-07-25 02:26:51 +00:00
2023-12-23 17:24:16 +00:00
// Load our pre-approved names:
auto chatListStream = Game : : assetManager - > GetFile ( " chatplus_en_us.txt " ) ;
if ( ! chatListStream ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to load %s " , ( Game : : assetManager - > GetResPath ( ) / " chatplus_en_us.txt " ) . string ( ) . c_str ( ) ) ;
2024-04-05 05:51:40 +00:00
throw std : : runtime_error ( " Aborting initialization due to missing chat allowlist file. " ) ;
2022-11-26 10:29:53 +00:00
}
2023-12-23 17:24:16 +00:00
2022-11-26 10:29:53 +00:00
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 ) ;
}
}
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 ) {
2023-11-16 01:29:00 +00:00
if ( GeneralUtils : : CaseInsensitiveStringCompare ( p . second - > GetUsername ( ) , username ) ) return p . second ;
2021-12-05 17:54:36 +00:00
}
}
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 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Deleted user %i " , user - > GetAccountID ( ) ) ;
2021-12-05 17:54:36 +00:00
delete user ;
}
m_UsersToDelete . clear ( ) ;
}
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 ;
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 ) ;
2023-11-22 02:14:30 +00:00
2023-09-24 07:55:54 +00:00
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-11-18 00:47:18 +00:00
for ( const auto & characterId : Database : : Get ( ) - > GetAccountCharacterIds ( u - > GetAccountID ( ) ) ) {
Character * character = new Character ( characterId , u ) ;
character - > UpdateFromDatabase ( ) ;
2023-09-24 07:55:54 +00:00
character - > SetIsNewLogin ( ) ;
chars . push_back ( character ) ;
2021-12-05 17:54:36 +00:00
}
2024-01-07 07:05:57 +00:00
RakNet : : BitStream bitStream ;
BitStreamUtils : : WriteHeader ( bitStream , eConnectionType : : CLIENT , eClientMessageType : : CHARACTER_LIST_RESPONSE ) ;
std : : vector < Character * > characters = u - > GetCharacters ( ) ;
bitStream . Write < uint8_t > ( characters . size ( ) ) ;
bitStream . Write < uint8_t > ( 0 ) ; //TODO: Pick the most recent played index. character index in front, just picking 0
for ( uint32_t i = 0 ; i < characters . size ( ) ; + + i ) {
bitStream . Write ( characters [ i ] - > GetObjectID ( ) ) ;
bitStream . Write < uint32_t > ( 0 ) ;
bitStream . Write ( LUWString ( characters [ i ] - > GetName ( ) ) ) ;
bitStream . Write ( LUWString ( characters [ i ] - > GetUnapprovedName ( ) ) ) ;
bitStream . Write < uint8_t > ( characters [ i ] - > GetNameRejected ( ) ) ;
bitStream . Write < uint8_t > ( false ) ;
bitStream . Write ( LUString ( " " , 10 ) ) ;
bitStream . Write ( characters [ i ] - > GetShirtColor ( ) ) ;
bitStream . Write ( characters [ i ] - > GetShirtStyle ( ) ) ;
bitStream . Write ( characters [ i ] - > GetPantsColor ( ) ) ;
bitStream . Write ( characters [ i ] - > GetHairStyle ( ) ) ;
bitStream . Write ( characters [ i ] - > GetHairColor ( ) ) ;
bitStream . Write ( characters [ i ] - > GetLeftHand ( ) ) ;
bitStream . Write ( characters [ i ] - > GetRightHand ( ) ) ;
bitStream . Write ( characters [ i ] - > GetEyebrows ( ) ) ;
bitStream . Write ( characters [ i ] - > GetEyes ( ) ) ;
bitStream . Write ( characters [ i ] - > GetMouth ( ) ) ;
bitStream . Write < uint32_t > ( 0 ) ;
bitStream . Write < uint16_t > ( characters [ i ] - > GetZoneID ( ) ) ;
bitStream . Write < uint16_t > ( characters [ i ] - > GetZoneInstance ( ) ) ;
bitStream . Write ( characters [ i ] - > GetZoneClone ( ) ) ;
bitStream . Write ( characters [ i ] - > GetLastLogin ( ) ) ;
const auto & equippedItems = characters [ i ] - > GetEquippedItems ( ) ;
bitStream . Write < uint16_t > ( equippedItems . size ( ) ) ;
for ( uint32_t j = 0 ; j < equippedItems . size ( ) ; + + j ) {
bitStream . Write ( equippedItems [ j ] ) ;
}
}
SEND_PACKET ;
2021-12-05 17:54:36 +00:00
}
void UserManager : : CreateCharacter ( const SystemAddress & sysAddr , Packet * packet ) {
User * u = GetUser ( sysAddr ) ;
if ( ! u ) return ;
2024-01-07 08:02:27 +00:00
2024-01-12 20:23:44 +00:00
LUWString LUWStringName ;
2024-01-07 08:02:27 +00:00
uint32_t firstNameIndex ;
uint32_t middleNameIndex ;
uint32_t lastNameIndex ;
uint32_t shirtColor ;
uint32_t shirtStyle ;
uint32_t pantsColor ;
uint32_t hairStyle ;
uint32_t hairColor ;
uint32_t lh ;
uint32_t rh ;
uint32_t eyebrows ;
uint32_t eyes ;
uint32_t mouth ;
CINSTREAM_SKIP_HEADER ;
inStream . Read ( LUWStringName ) ;
inStream . Read ( firstNameIndex ) ;
inStream . Read ( middleNameIndex ) ;
inStream . Read ( lastNameIndex ) ;
inStream . IgnoreBytes ( 9 ) ;
inStream . Read ( shirtColor ) ;
inStream . Read ( shirtStyle ) ;
inStream . Read ( pantsColor ) ;
inStream . Read ( hairStyle ) ;
inStream . Read ( hairColor ) ;
inStream . Read ( lh ) ;
inStream . Read ( rh ) ;
inStream . Read ( eyebrows ) ;
inStream . Read ( eyes ) ;
inStream . Read ( mouth ) ;
const auto name = LUWStringName . GetAsString ( ) ;
2021-12-05 17:54:36 +00:00
std : : string predefinedName = GetPredefinedName ( firstNameIndex , middleNameIndex , lastNameIndex ) ;
2024-02-11 06:08:22 +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
2023-11-18 00:47:18 +00:00
if ( ! name . empty ( ) & & Database : : Get ( ) - > GetCharacterInfo ( name ) ) {
2023-10-21 23:31:55 +00:00
LOG ( " 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
2023-11-18 00:47:18 +00:00
if ( Database : : Get ( ) - > GetCharacterInfo ( predefinedName ) ) {
2023-10-21 23:31:55 +00:00
LOG ( " 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
2023-11-18 00:47:18 +00:00
if ( name . empty ( ) ) {
2023-10-21 23:31:55 +00:00
LOG ( " AccountID: %i is creating a character with predefined name: %s " , u - > GetAccountID ( ) , predefinedName . c_str ( ) ) ;
2022-02-03 22:03:54 +00:00
} else {
2023-10-21 23:31:55 +00:00
LOG ( " 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:
2024-02-11 06:08:22 +00:00
ObjectIDManager : : RequestPersistentID ( [ = , this ] ( uint32_t objectID ) mutable {
2023-11-18 00:47:18 +00:00
if ( Database : : Get ( ) - > GetCharacterInfo ( objectID ) ) {
LOG ( " Character object id unavailable, check object_id_tracker! " ) ;
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 ;
2023-11-22 02:14:30 +00:00
xml < < " <obj v= \" 1 \" > " ;
xml < < " <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> " ;
2023-11-22 02:14:30 +00:00
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-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +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 \" > " ;
2022-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +00:00
LWOOBJID lwoidforshirt = ObjectIDManager : : GenerateRandomObjectID ( ) ;
LWOOBJID lwoidforpants ;
2022-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +00:00
do {
lwoidforpants = ObjectIDManager : : GenerateRandomObjectID ( ) ;
} while ( lwoidforpants = = lwoidforshirt ) ; //Make sure we don't have the same ID for both shirt and pants
2022-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +00:00
GeneralUtils : : SetBit ( lwoidforshirt , eObjectBits : : CHARACTER ) ;
GeneralUtils : : SetBit ( lwoidforshirt , eObjectBits : : PERSISTENT ) ;
GeneralUtils : : SetBit ( lwoidforpants , eObjectBits : : CHARACTER ) ;
GeneralUtils : : SetBit ( lwoidforpants , eObjectBits : : PERSISTENT ) ;
2022-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +00:00
xml < < " <i l= \" " < < shirtLOT < < " \" id= \" " < < lwoidforshirt < < " \" s= \" 0 \" c= \" 1 \" eq= \" 1 \" b= \" 1 \" /> " ;
xml < < " <i l= \" " < < pantsLOT < < " \" id= \" " < < lwoidforpants < < " \" s= \" 1 \" c= \" 1 \" eq= \" 1 \" b= \" 1 \" /> " ;
2022-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +00:00
xml < < " </in></items></inv><lvl l= \" 1 \" cv= \" 1 \" sb= \" 500 \" /><flag></flag></obj> " ;
2022-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +00:00
//Check to see if our name was pre-approved:
bool nameOk = IsNamePreapproved ( name ) ;
if ( ! nameOk & & u - > GetMaxGMLevel ( ) > eGameMasterLevel : : FORUM_MODERATOR ) nameOk = true ;
2022-07-28 13:39:57 +00:00
2024-02-11 06:08:22 +00:00
// If predefined name is invalid, change it to be their object id
// that way more than one player can create characters if the predefined name files are not provided
if ( predefinedName = = " INVALID " ) {
std : : stringstream nameObjID ;
nameObjID < < " minifig " < < objectID ;
predefinedName = nameObjID . str ( ) ;
}
2023-11-22 02:14:30 +00:00
std : : string_view nameToAssign = ! name . empty ( ) & & nameOk ? name : predefinedName ;
std : : string pendingName = ! name . empty ( ) & & ! nameOk ? name : " " ;
2023-11-18 00:47:18 +00:00
2023-11-22 02:14:30 +00:00
ICharInfo : : Info info ;
info . name = nameToAssign ;
info . pendingName = pendingName ;
info . id = objectID ;
info . accountId = u - > GetAccountID ( ) ;
2023-11-18 00:47:18 +00:00
2023-11-22 02:14:30 +00:00
Database : : Get ( ) - > InsertNewCharacter ( info ) ;
2022-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +00:00
//Now finally insert our character xml:
Database : : Get ( ) - > InsertCharacterXml ( objectID , xml . str ( ) ) ;
2022-07-28 13:39:57 +00:00
2023-11-22 02:14:30 +00:00
WorldPackets : : SendCharacterCreationResponse ( sysAddr , eCharacterCreationResponse : : SUCCESS ) ;
UserManager : : RequestCharacterList ( sysAddr ) ;
2024-01-07 07:05:57 +00:00
} ) ;
2021-12-05 17:54:36 +00:00
}
void UserManager : : DeleteCharacter ( const SystemAddress & sysAddr , Packet * packet ) {
User * u = GetUser ( sysAddr ) ;
if ( ! u ) {
2023-10-21 23:31:55 +00:00
LOG ( " Couldn't get user to delete character " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-25 02:26:51 +00:00
2024-01-07 08:02:27 +00:00
CINSTREAM_SKIP_HEADER ;
LWOOBJID objectID ;
inStream . Read ( objectID ) ;
2022-07-13 03:36:06 +00:00
uint32_t charID = static_cast < uint32_t > ( objectID ) ;
2023-10-21 23:31:55 +00:00
LOG ( " 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 {
2023-10-21 23:31:55 +00:00
LOG ( " Deleting character %i " , charID ) ;
2023-11-18 00:47:18 +00:00
Database : : Get ( ) - > DeleteCharacter ( charID ) ;
CBITSTREAM ;
2024-04-01 03:27:50 +00:00
BitStreamUtils : : WriteHeader ( bitStream , eConnectionType : : CHAT , eChatMessageType : : UNEXPECTED_DISCONNECT ) ;
2023-11-18 00:47:18 +00:00
bitStream . Write ( objectID ) ;
Game : : chatServer - > Send ( & bitStream , SYSTEM_PRIORITY , RELIABLE , 0 , Game : : chatSysAddr , false ) ;
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 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Couldn't get user to delete character " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-28 13:39:57 +00:00
2024-01-07 08:02:27 +00:00
CINSTREAM_SKIP_HEADER ;
LWOOBJID objectID ;
inStream . Read ( objectID ) ;
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 ) ;
2023-10-21 23:31:55 +00:00
LOG ( " Received char rename request for ID: %llu (%u) " , objectID , charID ) ;
2022-07-28 13:39:57 +00:00
2024-01-12 20:23:44 +00:00
LUWString LUWStringName ;
2024-01-07 08:02:27 +00:00
inStream . Read ( LUWStringName ) ;
const auto newName = LUWStringName . GetAsString ( ) ;
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 ( ) ) ;
2024-01-30 03:45:50 +00:00
auto unusedItr = std : : find_if ( u - > GetCharacters ( ) . begin ( ) , u - > GetCharacters ( ) . end ( ) , [ & ] ( Character * c ) {
2023-09-28 17:16:11 +00:00
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
2023-11-29 08:25:06 +00:00
if ( ! Database : : Get ( ) - > GetCharacterInfo ( newName ) ) {
2021-12-05 17:54:36 +00:00
if ( IsNamePreapproved ( newName ) ) {
2023-11-18 00:47:18 +00:00
Database : : Get ( ) - > SetCharacterName ( charID , newName ) ;
2023-10-21 23:31:55 +00:00
LOG ( " 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 {
2023-11-18 00:47:18 +00:00
Database : : Get ( ) - > SetPendingCharacterName ( charID , newName ) ;
2023-10-21 23:31:55 +00:00
LOG ( " 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 {
2023-10-21 23:31:55 +00:00
LOG ( " 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 ) {
2023-10-21 23:31:55 +00:00
LOG ( " 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 ) {
2023-11-18 00:47:18 +00:00
Database : : Get ( ) - > UpdateLastLoggedInCharacter ( playerID ) ;
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 ) {
2023-10-21 23:31:55 +00:00
LOG ( " 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 {
2023-10-21 23:31:55 +00:00
LOG ( " 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 {
2023-11-22 02:14:30 +00:00
auto stmt = CDClientDatabase : : CreatePreppedStmt (
2024-04-14 04:41:51 +00:00
" select obj.id as objectId 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) == ? AND icc.color1 == ? AND icc.decal == ? "
2023-11-22 02:14:30 +00:00
) ;
stmt . bind ( 1 , " character create shirt " ) ;
stmt . bind ( 2 , static_cast < int > ( shirtColor ) ) ;
stmt . bind ( 3 , static_cast < int > ( shirtStyle ) ) ;
auto tableData = stmt . execQuery ( ) ;
2024-04-14 04:41:51 +00:00
auto shirtLOT = tableData . getIntField ( " objectId " , 4069 ) ;
2022-03-24 07:09:18 +00:00
tableData . finalize ( ) ;
return shirtLOT ;
2023-11-22 02:14:30 +00:00
} catch ( const std : : exception & ex ) {
LOG ( " Could not look up shirt %i %i: %s " , shirtColor , shirtStyle , ex . what ( ) ) ;
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 {
2023-11-22 02:14:30 +00:00
auto stmt = CDClientDatabase : : CreatePreppedStmt (
2024-04-14 04:41:51 +00:00
" select obj.id as objectId 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) == ? AND icc.color1 == ? "
2023-11-22 02:14:30 +00:00
) ;
stmt . bind ( 1 , " cc pants " ) ;
stmt . bind ( 2 , static_cast < int > ( pantsColor ) ) ;
auto tableData = stmt . execQuery ( ) ;
2024-04-14 04:41:51 +00:00
auto pantsLOT = tableData . getIntField ( " objectId " , 2508 ) ;
2022-03-24 07:30:52 +00:00
tableData . finalize ( ) ;
return pantsLOT ;
2023-11-22 02:14:30 +00:00
} catch ( const std : : exception & ex ) {
LOG ( " Could not look up pants %i: %s " , pantsColor , ex . what ( ) ) ;
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 ( ) ;
}
}
}