[8072] First step to get rid of data blob.

Adds new fields gender, level, xp, money, playerBytes, playerBytes2 and playerFlags to characters table.

The update will not work if your database contains characters with an old data field (not fitting to the actual client version).

It's recommended to backup your character database before applying this patch.

Signed-off-by: hunuza <hunuza@gmail.com>
This commit is contained in:
hunuza 2009-06-22 19:10:20 +02:00
parent f34ce077c0
commit 8a32a19bad
13 changed files with 229 additions and 171 deletions

View file

@ -21,7 +21,7 @@
DROP TABLE IF EXISTS `character_db_version`; DROP TABLE IF EXISTS `character_db_version`;
CREATE TABLE `character_db_version` ( CREATE TABLE `character_db_version` (
`required_8030_02_characters_character_action` bit(1) default NULL `required_8072_02_characters_characters` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB';
-- --
@ -197,6 +197,13 @@ CREATE TABLE `characters` (
`name` varchar(12) NOT NULL default '', `name` varchar(12) NOT NULL default '',
`race` tinyint(3) unsigned NOT NULL default '0', `race` tinyint(3) unsigned NOT NULL default '0',
`class` tinyint(3) unsigned NOT NULL default '0', `class` tinyint(3) unsigned NOT NULL default '0',
`gender` TINYINT UNSIGNED NOT NULL default '0',
`level` TINYINT UNSIGNED NOT NULL default '0',
`xp` INT UNSIGNED NOT NULL default '0',
`money` INT UNSIGNED NOT NULL default '0',
`playerBytes` INT UNSIGNED NOT NULL default '0',
`playerBytes2` INT UNSIGNED NOT NULL default '0',
`playerFlags` INT UNSIGNED NOT NULL default '0',
`position_x` float NOT NULL default '0', `position_x` float NOT NULL default '0',
`position_y` float NOT NULL default '0', `position_y` float NOT NULL default '0',
`position_z` float NOT NULL default '0', `position_z` float NOT NULL default '0',

View file

@ -0,0 +1,10 @@
ALTER TABLE character_db_version CHANGE COLUMN required_8030_02_characters_character_action required_8072_01_characters_characters bit;
ALTER TABLE characters
ADD gender TINYINT UNSIGNED NOT NULL default '0' AFTER class,
ADD level TINYINT UNSIGNED NOT NULL default '0' AFTER gender,
ADD xp INT UNSIGNED NOT NULL default '0' AFTER level,
ADD money INT UNSIGNED NOT NULL default '0' AFTER xp,
ADD playerBytes INT UNSIGNED NOT NULL default '0' AFTER money,
ADD playerBytes2 INT UNSIGNED NOT NULL default '0' AFTER playerBytes,
ADD playerFlags INT UNSIGNED NOT NULL default '0' AFTER playerBytes2;

View file

@ -0,0 +1,11 @@
ALTER TABLE character_db_version CHANGE COLUMN required_8072_01_characters_characters required_8072_02_characters_characters bit;
UPDATE characters SET
gender = (CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(data, ' ', 23), ' ', -1) AS UNSIGNED) & 0xFF0000) >> 16,
level = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(data, ' ', 54), ' ', -1) AS UNSIGNED),
xp = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(data, ' ', 609), ' ', -1) AS UNSIGNED),
money = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(data, ' ', 1145), ' ', -1) AS UNSIGNED),
playerBytes = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(data, ' ', 154), ' ', -1) AS UNSIGNED),
playerBytes2 = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(data, ' ', 155), ' ', -1) AS UNSIGNED),
playerFlags = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(data, ' ', 151), ' ', -1) AS UNSIGNED)
WHERE LENGTH(SUBSTRING_INDEX(data, ' ', 1294)) < LENGTH(data) && LENGTH(data) <= LENGTH(SUBSTRING_INDEX(data, ' ', 1295));

View file

@ -233,6 +233,8 @@ pkgdata_DATA = \
8064_01_mangos_spell_chain.sql \ 8064_01_mangos_spell_chain.sql \
8065_01_mangos_spell_proc_event.sql \ 8065_01_mangos_spell_proc_event.sql \
8071_01_mangos_command.sql \ 8071_01_mangos_command.sql \
8072_01_characters_characters.sql \
8072_02_characters_characters.sql \
README README
## Additional files to include when running 'make dist' ## Additional files to include when running 'make dist'
@ -446,4 +448,6 @@ EXTRA_DIST = \
8064_01_mangos_spell_chain.sql \ 8064_01_mangos_spell_chain.sql \
8065_01_mangos_spell_proc_event.sql \ 8065_01_mangos_spell_proc_event.sql \
8071_01_mangos_command.sql \ 8071_01_mangos_command.sql \
8072_01_characters_characters.sql \
8072_02_characters_characters.sql \
README README

View file

@ -59,7 +59,7 @@ bool LoginQueryHolder::Initialize()
// NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure. // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure.
// !!! NOTE: including unused `zone`,`online` // !!! NOTE: including unused `zone`,`online`
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points,bgid,bgteam,bgmap,bgx,bgy,bgz,bgo FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points,bgid,bgteam,bgmap,bgx,bgy,bgz,bgo FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid));
@ -128,21 +128,15 @@ void WorldSession::HandleCharEnum(QueryResult * result)
if( result ) if( result )
{ {
Player *plr = new Player(this);
do do
{ {
uint32 guidlow = (*result)[0].GetUInt32(); uint32 guidlow = (*result)[0].GetUInt32();
sLog.outDetail("Loading char guid %u from account %u.",guidlow,GetAccountId()); sLog.outDetail("Loading char guid %u from account %u.",guidlow,GetAccountId());
if(Player::BuildEnumData(result, &data))
if(plr->MinimalLoadFromDB( result, guidlow ))
{
plr->BuildEnumData( result, &data );
++num; ++num;
} }
}
while( result->NextRow() ); while( result->NextRow() );
delete plr;
delete result; delete result;
} }
@ -157,19 +151,23 @@ void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ )
CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(), CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(),
!sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ? !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ?
// ------- Query Without Declined Names -------- // ------- Query Without Declined Names --------
// 0 1 2 3 4 5 6 7 8 // 0 1 2 3 4 5 6 7
"SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, " "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
// 9 10 11 12 13 14 // 8 9 10 11 12 13 14
"characters.at_login, characters.zone, character_pet.entry, character_pet.modelid, character_pet.level, guild_member.guildid " "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
// 15 16 17 18 19
"characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data "
"FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='%u' " "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='%u' "
"LEFT JOIN guild_member ON characters.guid = guild_member.guid " "LEFT JOIN guild_member ON characters.guid = guild_member.guid "
"WHERE characters.account = '%u' ORDER BY characters.guid" "WHERE characters.account = '%u' ORDER BY characters.guid"
: :
// --------- Query With Declined Names --------- // --------- Query With Declined Names ---------
// 0 1 2 3 4 5 6 7 8 // 0 1 2 3 4 5 6 7
"SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, " "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
// 9 10 11 12 13 14 15 // 8 9 10 11 12 13 14
"characters.at_login, characters.zone, character_pet.entry, character_pet.modelid, character_pet.level, guild_member.guildid, character_declinedname.genitive " "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
// 15 16 17 18 19 20
"characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, character_declinedname.genitive "
"FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='%u' " "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='%u' "
"LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid " "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid "
"LEFT JOIN guild_member ON characters.guid = guild_member.guid " "LEFT JOIN guild_member ON characters.guid = guild_member.guid "
@ -335,7 +333,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
if(!AllowTwoSideAccounts || skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) if(!AllowTwoSideAccounts || skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT)
{ {
QueryResult *result2 = CharacterDatabase.PQuery("SELECT guid,race,class FROM characters WHERE account = '%u' %s", QueryResult *result2 = CharacterDatabase.PQuery("SELECT level,race,class FROM characters WHERE account = '%u' %s",
GetAccountId(), (skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) ? "" : "LIMIT 1"); GetAccountId(), (skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) ? "" : "LIMIT 1");
if(result2) if(result2)
{ {
@ -362,8 +360,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
if(!have_req_level_for_heroic) if(!have_req_level_for_heroic)
{ {
uint32 acc_guid = field[0].GetUInt32(); uint32 acc_level = field[0].GetUInt32();
uint32 acc_level = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,acc_guid);
if(acc_level >= req_level_for_heroic) if(acc_level >= req_level_for_heroic)
have_req_level_for_heroic = true; have_req_level_for_heroic = true;
} }
@ -417,8 +414,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
if(!have_req_level_for_heroic) if(!have_req_level_for_heroic)
{ {
uint32 acc_guid = field[0].GetUInt32(); uint32 acc_level = field[0].GetUInt32();
uint32 acc_level = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,acc_guid);
if(acc_level >= req_level_for_heroic) if(acc_level >= req_level_for_heroic)
have_req_level_for_heroic = true; have_req_level_for_heroic = true;
} }

View file

@ -352,17 +352,14 @@ bool Guild::FillPlayerData(uint64 guid, MemberSlot* memslot)
} }
else else
{ {
QueryResult *result = CharacterDatabase.PQuery("SELECT name,data,zone,class FROM characters WHERE guid = '%u'", GUID_LOPART(guid)); QueryResult *result = CharacterDatabase.PQuery("SELECT name,level,zone,class FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
if(!result) if(!result)
return false; // player doesn't exist return false; // player doesn't exist
Field *fields = result->Fetch(); Field *fields = result->Fetch();
plName = fields[0].GetCppString(); plName = fields[0].GetCppString();
plLevel = fields[1].GetUInt32();
Tokens data = StrSplit(fields[1].GetCppString(), " ");
plLevel = Player::GetUInt32ValueFromArray(data,UNIT_FIELD_LEVEL);
plZone = fields[2].GetUInt32(); plZone = fields[2].GetUInt32();
plClass = fields[3].GetUInt32(); plClass = fields[3].GetUInt32();
delete result; delete result;

View file

@ -976,7 +976,7 @@ void WorldSession::HandleGuildBankDepositMoney( WorldPacket & recv_data )
pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money); pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money);
GetPlayer()->ModifyMoney(-int(money)); GetPlayer()->ModifyMoney(-int(money));
GetPlayer()->SaveDataFieldToDB(); //contains money GetPlayer()->SaveGoldToDB();
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
@ -1032,7 +1032,7 @@ void WorldSession::HandleGuildBankWithdrawMoney( WorldPacket & recv_data )
} }
GetPlayer()->ModifyMoney(money); GetPlayer()->ModifyMoney(money);
GetPlayer()->SaveDataFieldToDB(); // contains money GetPlayer()->SaveGoldToDB();
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();

View file

@ -2133,22 +2133,17 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
if (HasLowerSecurity(NULL, target_guid)) if (HasLowerSecurity(NULL, target_guid))
return false; return false;
// 0 // 0 1 2 3
QueryResult *result = CharacterDatabase.PQuery("SELECT totaltime FROM characters WHERE guid = '%u'", GUID_LOPART(target_guid)); QueryResult *result = CharacterDatabase.PQuery("SELECT totaltime, level, money, account FROM characters WHERE guid = '%u'", GUID_LOPART(target_guid));
if (!result) if (!result)
return false; return false;
Field *fields = result->Fetch(); Field *fields = result->Fetch();
total_player_time = fields[0].GetUInt32(); total_player_time = fields[0].GetUInt32();
level = fields[1].GetUInt32();
money = fields[2].GetUInt32();
accId = fields[3].GetUInt32();
delete result; delete result;
Tokens data;
if (!Player::LoadValuesArrayFromDB(data,target_guid))
return false;
money = Player::GetUInt32ValueFromArray(data, PLAYER_FIELD_COINAGE);
level = Player::GetUInt32ValueFromArray(data, UNIT_FIELD_LEVEL);
accId = objmgr.GetPlayerAccountIdByGUID(target_guid);
} }
std::string username = GetMangosString(LANG_ERROR); std::string username = GetMangosString(LANG_ERROR);

View file

@ -3931,11 +3931,7 @@ void ChatHandler::HandleCharacterLevel(Player* player, uint64 player_guid, uint3
else else
{ {
// update level and XP at level, all other will be updated at loading // update level and XP at level, all other will be updated at loading
Tokens values; CharacterDatabase.PExecute("UPDATE characters SET level = '%u', xp = 0 WHERE guid = '%u'", newlevel, GUID_LOPART(player_guid));
Player::LoadValuesArrayFromDB(values,player_guid);
Player::SetUInt32ValueInArray(values,UNIT_FIELD_LEVEL,newlevel);
Player::SetUInt32ValueInArray(values,PLAYER_XP,0);
Player::SaveValuesArrayInDB(values,player_guid);
} }
} }
@ -3960,7 +3956,7 @@ bool ChatHandler::HandleCharacterLevelCommand(const char* args)
if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name))
return false; return false;
int32 oldlevel = target ? target->getLevel() : Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,target_guid); int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid);
int32 newlevel = levelStr ? atoi(levelStr) : oldlevel; int32 newlevel = levelStr ? atoi(levelStr) : oldlevel;
if(newlevel < 1) if(newlevel < 1)
@ -3999,7 +3995,7 @@ bool ChatHandler::HandleLevelUpCommand(const char* args)
if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name))
return false; return false;
int32 oldlevel = target ? target->getLevel() : Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,target_guid); int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid);
int32 addlevel = levelStr ? atoi(levelStr) : 1; int32 addlevel = levelStr ? atoi(levelStr) : 1;
int32 newlevel = oldlevel + addlevel; int32 newlevel = oldlevel + addlevel;

View file

@ -557,7 +557,7 @@ void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data )
// save money and mail to prevent cheating // save money and mail to prevent cheating
CharacterDatabase.BeginTransaction(); CharacterDatabase.BeginTransaction();
pl->SaveDataFieldToDB(); // contains money pl->SaveGoldToDB();
pl->_SaveMail(); pl->_SaveMail();
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
} }

View file

@ -538,9 +538,6 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
for (int i = 0; i < PLAYER_SLOTS_COUNT; ++i) for (int i = 0; i < PLAYER_SLOTS_COUNT; ++i)
m_items[i] = NULL; m_items[i] = NULL;
m_race = race;
m_class = class_;
SetMapId(info->mapId); SetMapId(info->mapId);
Relocate(info->positionX,info->positionY,info->positionZ); Relocate(info->positionX,info->positionY,info->positionZ);
@ -572,7 +569,7 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
break; break;
} }
setFactionForRace(m_race); setFactionForRace(race);
uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 ); uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 );
@ -1400,53 +1397,67 @@ void Player::setDeathState(DeathState s)
} }
} }
void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
{ {
// 0 1 2 3 4 5 6 7
// "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
// 8 9 10 11 12 13 14
// "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
// 15 16 17 18 19 20
// "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, character_declinedname.genitive "
Field *fields = result->Fetch(); Field *fields = result->Fetch();
*p_data << uint64(GetGUID()); uint32 guid = fields[0].GetUInt32();
*p_data << m_name; uint8 pRace = fields[2].GetUInt8();
uint8 pClass = fields[3].GetUInt8();
*p_data << uint8(getRace()); PlayerInfo const *info = objmgr.GetPlayerInfo(pRace, pClass);
uint8 pClass = getClass(); if(!info)
*p_data << uint8(pClass); {
*p_data << uint8(getGender()); sLog.outError("Player %u have incorrect race/class pair. Don't build enum.", guid);
return false;
}
uint32 bytes = GetUInt32Value(PLAYER_BYTES); *p_data << uint64(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
*p_data << uint8(bytes); *p_data << fields[1].GetString(); // name
*p_data << uint8(bytes >> 8); *p_data << uint8(pRace); // race
*p_data << uint8(bytes >> 16); *p_data << uint8(pClass); // class
*p_data << uint8(bytes >> 24); *p_data << uint8(fields[4].GetUInt8()); // gender
bytes = GetUInt32Value(PLAYER_BYTES_2); uint32 playerBytes = fields[5].GetUInt32();
*p_data << uint8(bytes); *p_data << uint8(playerBytes); // skin
*p_data << uint8(playerBytes >> 8); // face
*p_data << uint8(playerBytes >> 16); // hair style
*p_data << uint8(playerBytes >> 24); // hair color
*p_data << uint8(getLevel()); // player level uint32 playerBytes2 = fields[6].GetUInt32();
// do not use GetMap! it will spawn a new instance since the bound instances are not loaded *p_data << uint8(playerBytes2 & 0xFF); // facial hair
uint32 zoneId = fields[10].GetUInt32();
sLog.outDebug("Player::BuildEnumData: map:%u, x:%f, y:%f, z:%f zone:%u", GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), zoneId);
*p_data << uint32(zoneId);
*p_data << uint32(GetMapId());
*p_data << GetPositionX(); *p_data << uint8(fields[7].GetUInt8()); // level
*p_data << GetPositionY(); *p_data << uint32(fields[8].GetUInt32()); // zone
*p_data << GetPositionZ(); *p_data << uint32(fields[9].GetUInt32()); // map
// guild id *p_data << fields[10].GetFloat(); // x
*p_data << uint32(fields[14].GetUInt32()); *p_data << fields[11].GetFloat(); // y
*p_data << fields[12].GetFloat(); // z
*p_data << uint32(fields[13].GetUInt32()); // guild id
uint32 char_flags = 0; uint32 char_flags = 0;
if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM)) uint32 playerFlags = fields[14].GetUInt32();
uint32 atLoginFlags = fields[15].GetUInt32();
if(playerFlags & PLAYER_FLAGS_HIDE_HELM)
char_flags |= CHARACTER_FLAG_HIDE_HELM; char_flags |= CHARACTER_FLAG_HIDE_HELM;
if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK)) if(playerFlags & PLAYER_FLAGS_HIDE_CLOAK)
char_flags |= CHARACTER_FLAG_HIDE_CLOAK; char_flags |= CHARACTER_FLAG_HIDE_CLOAK;
if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) if(playerFlags & PLAYER_FLAGS_GHOST)
char_flags |= CHARACTER_FLAG_GHOST; char_flags |= CHARACTER_FLAG_GHOST;
if(HasAtLoginFlag(AT_LOGIN_RENAME)) if(atLoginFlags & AT_LOGIN_RENAME)
char_flags |= CHARACTER_FLAG_RENAME; char_flags |= CHARACTER_FLAG_RENAME;
if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED)) if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED))
{ {
if(!fields[15].GetCppString().empty()) if(!fields[20].GetCppString().empty())
char_flags |= CHARACTER_FLAG_DECLINED; char_flags |= CHARACTER_FLAG_DECLINED;
} }
else else
@ -1454,7 +1465,7 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
*p_data << uint32(char_flags); // character flags *p_data << uint32(char_flags); // character flags
// character customize (flags?) // character customize (flags?)
*p_data << uint32(HasAtLoginFlag(AT_LOGIN_CUSTOMIZE) ? 1 : 0); *p_data << uint32(atLoginFlags & AT_LOGIN_CUSTOMIZE ? 1 : 0);
*p_data << uint8(1); // unknown *p_data << uint8(1); // unknown
// Pets info // Pets info
@ -1464,14 +1475,14 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
uint32 petFamily = 0; uint32 petFamily = 0;
// show pet at selection character in character list only for non-ghost character // show pet at selection character in character list only for non-ghost character
if (result && isAlive() && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER || pClass == CLASS_DEATH_KNIGHT)) if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER || pClass == CLASS_DEATH_KNIGHT))
{ {
uint32 entry = fields[11].GetUInt32(); uint32 entry = fields[16].GetUInt32();
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry); CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry);
if(cInfo) if(cInfo)
{ {
petDisplayId = fields[12].GetUInt32(); petDisplayId = fields[17].GetUInt32();
petLevel = fields[13].GetUInt32(); petLevel = fields[18].GetUInt32();
petFamily = cInfo->family; petFamily = cInfo->family;
} }
} }
@ -1481,36 +1492,40 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
*p_data << uint32(petFamily); *p_data << uint32(petFamily);
} }
// TODO: do not access data field here
Tokens data = StrSplit(fields[19].GetCppString(), " ");
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++) for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++)
{ {
uint32 visualbase = PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2); uint32 visualbase = PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2);
uint32 item_id = GetUInt32Value(visualbase); uint32 item_id = GetUInt32ValueFromArray(data, visualbase);
const ItemPrototype * proto = objmgr.GetItemPrototype(item_id); const ItemPrototype * proto = objmgr.GetItemPrototype(item_id);
if(!proto)
{
*p_data << uint32(0);
*p_data << uint8(0);
*p_data << uint32(0);
continue;
}
SpellItemEnchantmentEntry const *enchant = NULL; SpellItemEnchantmentEntry const *enchant = NULL;
uint32 enchants = GetUInt32ValueFromArray(data, visualbase + 1);
for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot) for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot)
{ {
uint32 enchantId = GetUInt16Value(visualbase + 1, enchantSlot); if(enchant = sSpellItemEnchantmentStore.LookupEntry(enchantSlot >> enchantSlot*16))
if(enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId))
break; break;
} }
if (proto != NULL)
{
*p_data << uint32(proto->DisplayInfoID); *p_data << uint32(proto->DisplayInfoID);
*p_data << uint8(proto->InventoryType); *p_data << uint8(proto->InventoryType);
*p_data << uint32(enchant ? enchant->aura_id : 0); *p_data << uint32(enchant ? enchant->aura_id : 0);
} }
else
{
*p_data << uint32(0);
*p_data << uint8(0);
*p_data << uint32(0); // enchant?
}
}
*p_data << uint32(0); // first bag display id *p_data << uint32(0); // first bag display id
*p_data << uint8(0); // first bag inventory type *p_data << uint8(0); // first bag inventory type
*p_data << uint32(0); // enchant? *p_data << uint32(0); // enchant?
return true;
} }
bool Player::ToggleAFK() bool Player::ToggleAFK()
@ -6061,6 +6076,19 @@ uint32 Player::GetZoneIdFromDB(uint64 guid)
return zone; return zone;
} }
uint32 Player::GetLevelFromDB(uint64 guid)
{
QueryResult *result = CharacterDatabase.PQuery( "SELECT level FROM characters WHERE guid='%u'", GUID_LOPART(guid) );
if (!result)
return 0;
Field* fields = result->Fetch();
uint32 level = fields[0].GetUInt32();
delete result;
return level;
}
void Player::UpdateArea(uint32 newArea) void Player::UpdateArea(uint32 newArea)
{ {
// FFA_PVP flags are area and not zone id dependent // FFA_PVP flags are area and not zone id dependent
@ -13667,8 +13695,8 @@ bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid )
bool delete_result = true; bool delete_result = true;
if (!result) if (!result)
{ {
// 0 1 2 3 4 5 6 7 8 9 10 // 0 1 2 3 4 5 6 7 8 9 10 11
result = CharacterDatabase.PQuery("SELECT guid, data, name, position_x, position_y, position_z, map, totaltime, leveltime, at_login, zone FROM characters WHERE guid = '%u'",guid); result = CharacterDatabase.PQuery("SELECT guid, data, name, position_x, position_y, position_z, map, totaltime, leveltime, at_login, zone, level FROM characters WHERE guid = '%u'",guid);
if (!result) if (!result)
return false; return false;
} }
@ -13870,8 +13898,8 @@ float Player::GetFloatValueFromDB(uint16 index, uint64 guid)
bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
{ {
//// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [28] [29] 30 31 32 33 34 35 36 37 38 39 40 //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
//QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points,bgid,bgteam,bgmap,bgx,bgy,bgz,bgo FROM characters WHERE guid = '%u'", guid); //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points,bgid,bgteam,bgmap,bgx,bgy,bgz,bgo FROM characters WHERE guid = '%u'", guid);
QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM); QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
if(!result) if(!result)
@ -13915,6 +13943,19 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
// overwrite possible wrong/corrupted guid // overwrite possible wrong/corrupted guid
SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER)); SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
// overwrite some data fields
uint32 bytes0 = GetUInt32Value(UNIT_FIELD_BYTES_0) & 0xFF000000;
bytes0 |= fields[4].GetUInt8();
bytes0 |= fields[5].GetUInt8() << 8;
bytes0 |= fields[6].GetUInt8() << 16;
SetUInt32Value(UNIT_FIELD_LEVEL, fields[7].GetUInt8());
SetUInt32Value(PLAYER_XP, fields[8].GetUInt32());
SetUInt32Value(PLAYER_FIELD_COINAGE, fields[9].GetUInt32());
SetUInt32Value(PLAYER_BYTES, fields[10].GetUInt32());
SetUInt32Value(PLAYER_BYTES_2, fields[11].GetUInt32());
SetUInt32Value(PLAYER_BYTES_3, (GetUInt32Value(PLAYER_BYTES_3) & ~1) | fields[6].GetUInt8());
SetUInt32Value(PLAYER_FLAGS, fields[12].GetUInt32());
// cleanup inventory related item value fields (its will be filled correctly in _LoadInventory) // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
for(uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) for(uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
{ {
@ -13935,14 +13976,11 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
sLog.outDebug("Load Basic value of player %s is: ", m_name.c_str()); sLog.outDebug("Load Basic value of player %s is: ", m_name.c_str());
outDebugValues(); outDebugValues();
m_race = fields[4].GetUInt8(); //Need to call it to initialize m_team (m_team can be calculated from race)
//Need to call it to initialize m_team (m_team can be calculated from m_race)
//Other way is to saves m_team into characters table. //Other way is to saves m_team into characters table.
setFactionForRace(m_race); setFactionForRace(getRace());
SetCharm(NULL); SetCharm(NULL);
m_class = fields[5].GetUInt8();
// load home bind and check in same time class/race pair, it used later for restore broken positions // load home bind and check in same time class/race pair, it used later for restore broken positions
if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND))) if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND)))
{ {
@ -13953,16 +13991,16 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
InitPrimaryProfessions(); // to max set before any spell loaded InitPrimaryProfessions(); // to max set before any spell loaded
// init saved position, and fix it later if problematic // init saved position, and fix it later if problematic
uint32 transGUID = fields[24].GetUInt32(); uint32 transGUID = fields[31].GetUInt32();
Relocate(fields[6].GetFloat(),fields[7].GetFloat(),fields[8].GetFloat(),fields[10].GetFloat()); Relocate(fields[13].GetFloat(),fields[14].GetFloat(),fields[15].GetFloat(),fields[17].GetFloat());
SetMapId(fields[9].GetUInt32()); SetMapId(fields[16].GetUInt32());
SetDifficulty(fields[32].GetUInt32()); // may be changed in _LoadGroup SetDifficulty(fields[39].GetUInt32()); // may be changed in _LoadGroup
_LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP)); _LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP));
_LoadArenaTeamInfo(holder->GetResult(PLAYER_LOGIN_QUERY_LOADARENAINFO)); _LoadArenaTeamInfo(holder->GetResult(PLAYER_LOGIN_QUERY_LOADARENAINFO));
uint32 arena_currency = GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY) + fields[33].GetUInt32(); uint32 arena_currency = GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY) + fields[40].GetUInt32();
if (arena_currency > sWorld.getConfig(CONFIG_MAX_ARENA_POINTS)) if (arena_currency > sWorld.getConfig(CONFIG_MAX_ARENA_POINTS))
arena_currency = sWorld.getConfig(CONFIG_MAX_ARENA_POINTS); arena_currency = sWorld.getConfig(CONFIG_MAX_ARENA_POINTS);
@ -13999,12 +14037,12 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
m_movementInfo.t_o = 0.0f; m_movementInfo.t_o = 0.0f;
} }
uint32 bgid = fields[34].GetUInt32(); uint32 bgid = fields[41].GetUInt32();
uint32 bgteam = fields[35].GetUInt32(); uint32 bgteam = fields[42].GetUInt32();
if(bgid) //saved in BattleGround if(bgid) //saved in BattleGround
{ {
SetBattleGroundEntryPoint(fields[36].GetUInt32(),fields[37].GetFloat(),fields[38].GetFloat(),fields[39].GetFloat(),fields[40].GetFloat()); SetBattleGroundEntryPoint(fields[43].GetUInt32(),fields[44].GetFloat(),fields[45].GetFloat(),fields[46].GetFloat(),fields[47].GetFloat());
// check entry point and fix to homebind if need // check entry point and fix to homebind if need
MapEntry const* mapEntry = sMapStore.LookupEntry(m_bgEntryPoint.mapid); MapEntry const* mapEntry = sMapStore.LookupEntry(m_bgEntryPoint.mapid);
@ -14043,8 +14081,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
if(!mapEntry || mapEntry->IsBattleGroundOrArena()) if(!mapEntry || mapEntry->IsBattleGroundOrArena())
{ {
// return to BG master // return to BG master
SetMapId(fields[36].GetUInt32()); SetMapId(fields[43].GetUInt32());
Relocate(fields[37].GetFloat(),fields[38].GetFloat(),fields[39].GetFloat(),fields[40].GetFloat()); Relocate(fields[44].GetFloat(),fields[45].GetFloat(),fields[46].GetFloat(),fields[47].GetFloat());
// check entry point and fix to homebind if need // check entry point and fix to homebind if need
mapEntry = sMapStore.LookupEntry(GetMapId()); mapEntry = sMapStore.LookupEntry(GetMapId());
@ -14055,10 +14093,10 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
if (transGUID != 0) if (transGUID != 0)
{ {
m_movementInfo.t_x = fields[20].GetFloat(); m_movementInfo.t_x = fields[27].GetFloat();
m_movementInfo.t_y = fields[21].GetFloat(); m_movementInfo.t_y = fields[28].GetFloat();
m_movementInfo.t_z = fields[22].GetFloat(); m_movementInfo.t_z = fields[29].GetFloat();
m_movementInfo.t_o = fields[23].GetFloat(); m_movementInfo.t_o = fields[30].GetFloat();
if( !MaNGOS::IsValidMapCoord( if( !MaNGOS::IsValidMapCoord(
GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y, GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
@ -14149,7 +14187,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
SaveRecallPosition(); SaveRecallPosition();
time_t now = time(NULL); time_t now = time(NULL);
time_t logoutTime = time_t(fields[16].GetUInt64()); time_t logoutTime = time_t(fields[23].GetUInt64());
// since last logout (in seconds) // since last logout (in seconds)
uint64 time_diff = uint64(now - logoutTime); uint64 time_diff = uint64(now - logoutTime);
@ -14164,27 +14202,27 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
uint16 newDrunkenValue = uint16(soberFactor*(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE)); uint16 newDrunkenValue = uint16(soberFactor*(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE));
SetDrunkValue(newDrunkenValue); SetDrunkValue(newDrunkenValue);
m_rest_bonus = fields[15].GetFloat(); m_rest_bonus = fields[22].GetFloat();
//speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour) //speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
float bubble0 = 0.031; float bubble0 = 0.031;
//speed collect rest bonus in offline, in logout, in tavern, city (section/in hour) //speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
float bubble1 = 0.125; float bubble1 = 0.125;
if((int32)fields[16].GetUInt32() > 0) if((int32)fields[23].GetUInt32() > 0)
{ {
float bubble = fields[17].GetUInt32() > 0 float bubble = fields[24].GetUInt32() > 0
? bubble1*sWorld.getRate(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY) ? bubble1*sWorld.getRate(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY)
: bubble0*sWorld.getRate(RATE_REST_OFFLINE_IN_WILDERNESS); : bubble0*sWorld.getRate(RATE_REST_OFFLINE_IN_WILDERNESS);
SetRestBonus(GetRestBonus()+ time_diff*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble); SetRestBonus(GetRestBonus()+ time_diff*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble);
} }
m_cinematic = fields[12].GetUInt32(); m_cinematic = fields[19].GetUInt32();
m_Played_time[0]= fields[13].GetUInt32(); m_Played_time[0]= fields[20].GetUInt32();
m_Played_time[1]= fields[14].GetUInt32(); m_Played_time[1]= fields[21].GetUInt32();
m_resetTalentsCost = fields[18].GetUInt32(); m_resetTalentsCost = fields[25].GetUInt32();
m_resetTalentsTime = time_t(fields[19].GetUInt64()); m_resetTalentsTime = time_t(fields[26].GetUInt64());
// reserve some flags // reserve some flags
uint32 old_safe_flags = GetUInt32Value(PLAYER_FLAGS) & ( PLAYER_FLAGS_HIDE_CLOAK | PLAYER_FLAGS_HIDE_HELM ); uint32 old_safe_flags = GetUInt32Value(PLAYER_FLAGS) & ( PLAYER_FLAGS_HIDE_CLOAK | PLAYER_FLAGS_HIDE_HELM );
@ -14192,29 +14230,29 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM) ) if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM) )
SetUInt32Value(PLAYER_FLAGS, 0 | old_safe_flags); SetUInt32Value(PLAYER_FLAGS, 0 | old_safe_flags);
m_taxi.LoadTaxiMask( fields[11].GetString() ); // must be before InitTaxiNodesForLevel m_taxi.LoadTaxiMask( fields[18].GetString() ); // must be before InitTaxiNodesForLevel
uint32 extraflags = fields[25].GetUInt32(); uint32 extraflags = fields[25].GetUInt32();
m_stableSlots = fields[26].GetUInt32(); m_stableSlots = fields[33].GetUInt32();
if(m_stableSlots > MAX_PET_STABLES) if(m_stableSlots > MAX_PET_STABLES)
{ {
sLog.outError("Player can have not more %u stable slots, but have in DB %u",MAX_PET_STABLES,uint32(m_stableSlots)); sLog.outError("Player can have not more %u stable slots, but have in DB %u",MAX_PET_STABLES,uint32(m_stableSlots));
m_stableSlots = MAX_PET_STABLES; m_stableSlots = MAX_PET_STABLES;
} }
m_atLoginFlags = fields[27].GetUInt32(); m_atLoginFlags = fields[34].GetUInt32();
// Honor system // Honor system
// Update Honor kills data // Update Honor kills data
m_lastHonorUpdateTime = logoutTime; m_lastHonorUpdateTime = logoutTime;
UpdateHonorFields(); UpdateHonorFields();
m_deathExpireTime = (time_t)fields[30].GetUInt64(); m_deathExpireTime = (time_t)fields[37].GetUInt64();
if(m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP) if(m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP)
m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1; m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1;
std::string taxi_nodes = fields[31].GetCppString(); std::string taxi_nodes = fields[38].GetCppString();
delete result; delete result;
@ -14551,7 +14589,7 @@ void Player::_LoadAuras(QueryResult *result, uint32 timediff)
delete result; delete result;
} }
if(m_class == CLASS_WARRIOR) if(getClass() == CLASS_WARRIOR)
CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true); CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
} }
@ -15373,7 +15411,7 @@ void Player::SaveToDB()
CharacterDatabase.escape_string(sql_name); CharacterDatabase.escape_string(sql_name);
std::ostringstream ss; std::ostringstream ss;
ss << "INSERT INTO characters (guid,account,name,race,class," ss << "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags,"
"map, dungeon_difficulty, position_x, position_y, position_z, orientation, data, " "map, dungeon_difficulty, position_x, position_y, position_z, orientation, data, "
"taximask, online, cinematic, " "taximask, online, cinematic, "
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, " "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
@ -15382,8 +15420,15 @@ void Player::SaveToDB()
<< GetGUIDLow() << ", " << GetGUIDLow() << ", "
<< GetSession()->GetAccountId() << ", '" << GetSession()->GetAccountId() << ", '"
<< sql_name << "', " << sql_name << "', "
<< m_race << ", " << (uint32)getRace() << ", "
<< m_class << ", "; << (uint32)getClass() << ", "
<< (uint32)getGender() << ", "
<< getLevel() << ", "
<< GetUInt32Value(PLAYER_XP) << ", "
<< GetMoney() << ", "
<< GetUInt32Value(PLAYER_BYTES) << ", "
<< GetUInt32Value(PLAYER_BYTES_2) << ", "
<< GetUInt32Value(PLAYER_FLAGS) << ", ";
if(!IsBeingTeleported()) if(!IsBeingTeleported())
{ {
@ -15514,8 +15559,12 @@ void Player::SaveToDB()
void Player::SaveInventoryAndGoldToDB() void Player::SaveInventoryAndGoldToDB()
{ {
_SaveInventory(); _SaveInventory();
//money is in data field SaveGoldToDB();
SaveDataFieldToDB(); }
void Player::SaveGoldToDB()
{
CharacterDatabase.PExecute("UPDATE money = '%u' WHERE guid = '%u'", GetMoney(), GetGUIDLow());
} }
void Player::_SaveActions() void Player::_SaveActions()
@ -15939,38 +15988,31 @@ void Player::SetFloatValueInDB(uint16 index, float value, uint64 guid)
void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair) void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair)
{ {
Tokens tokens; // 0 1 2 3 4
if(!LoadValuesArrayFromDB(tokens, guid)) QueryResult* result = CharacterDatabase.PQuery("SELECT data, race, class, playerBytes, playerBytes2 FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
if(!result)
return; return;
uint32 unit_bytes0 = GetUInt32ValueFromArray(tokens, UNIT_FIELD_BYTES_0); Field* fields = result->Fetch();
uint8 race = unit_bytes0 & 0xFF;
uint8 class_ = (unit_bytes0 >> 8) & 0xFF;
PlayerInfo const* info = objmgr.GetPlayerInfo(race, class_); Tokens tokens = StrSplit(fields[0].GetString(), " ");
PlayerInfo const* info = objmgr.GetPlayerInfo(fields[1].GetUInt8(), fields[2].GetUInt8());
if(!info) if(!info)
return; return;
unit_bytes0 &= ~(0xFF << 16); // TODO: do not access data field here
unit_bytes0 |= (gender << 16);
SetUInt32ValueInArray(tokens, UNIT_FIELD_BYTES_0, unit_bytes0);
SetUInt32ValueInArray(tokens, UNIT_FIELD_DISPLAYID, gender ? info->displayId_f : info->displayId_m); SetUInt32ValueInArray(tokens, UNIT_FIELD_DISPLAYID, gender ? info->displayId_f : info->displayId_m);
SetUInt32ValueInArray(tokens, UNIT_FIELD_NATIVEDISPLAYID, gender ? info->displayId_f : info->displayId_m); SetUInt32ValueInArray(tokens, UNIT_FIELD_NATIVEDISPLAYID, gender ? info->displayId_f : info->displayId_m);
SetUInt32ValueInArray(tokens, PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24))); uint32 player_bytes2 = fields[4].GetUInt32();
uint32 player_bytes2 = GetUInt32ValueFromArray(tokens, PLAYER_BYTES_2);
player_bytes2 &= ~0xFF; player_bytes2 &= ~0xFF;
player_bytes2 |= facialHair; player_bytes2 |= facialHair;
SetUInt32ValueInArray(tokens, PLAYER_BYTES_2, player_bytes2);
uint32 player_bytes3 = GetUInt32ValueFromArray(tokens, PLAYER_BYTES_3);
player_bytes3 &= ~0xFF;
player_bytes3 |= gender;
SetUInt32ValueInArray(tokens, PLAYER_BYTES_3, player_bytes3);
SaveValuesArrayInDB(tokens, guid); SaveValuesArrayInDB(tokens, guid);
CharacterDatabase.PExecute("UPDATE characters SET gender = '%u', playerBytes = '%u', playerBytes2 = '%u' WHERE guid = '%u'", gender, skin | (face << 8) | (hairStyle << 16) | (hairColor << 24), player_bytes2, GUID_LOPART(guid));
delete result;
} }
void Player::SendAttackSwingDeadTarget() void Player::SendAttackSwingDeadTarget()

View file

@ -902,7 +902,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void Update( uint32 time ); void Update( uint32 time );
void BuildEnumData( QueryResult * result, WorldPacket * p_data ); static bool BuildEnumData( QueryResult * result, WorldPacket * p_data );
void SetInWater(bool apply); void SetInWater(bool apply);
@ -1261,6 +1261,7 @@ class MANGOS_DLL_SPEC Player : public Unit
static uint32 GetUInt32ValueFromDB(uint16 index, uint64 guid); static uint32 GetUInt32ValueFromDB(uint16 index, uint64 guid);
static float GetFloatValueFromDB(uint16 index, uint64 guid); static float GetFloatValueFromDB(uint16 index, uint64 guid);
static uint32 GetZoneIdFromDB(uint64 guid); static uint32 GetZoneIdFromDB(uint64 guid);
static uint32 GetLevelFromDB(uint64 guid);
static bool LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid); static bool LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid);
/*********************************************************/ /*********************************************************/
@ -1269,6 +1270,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void SaveToDB(); void SaveToDB();
void SaveInventoryAndGoldToDB(); // fast save function for item/money cheating preventing void SaveInventoryAndGoldToDB(); // fast save function for item/money cheating preventing
void SaveGoldToDB();
void SaveDataFieldToDB(); void SaveDataFieldToDB();
static bool SaveValuesArrayInDB(Tokens const& data,uint64 guid); static bool SaveValuesArrayInDB(Tokens const& data,uint64 guid);
static void SetUInt32ValueInArray(Tokens& data,uint16 index, uint32 value); static void SetUInt32ValueInArray(Tokens& data,uint16 index, uint32 value);
@ -2187,8 +2189,6 @@ class MANGOS_DLL_SPEC Player : public Unit
void outDebugValues() const; void outDebugValues() const;
uint64 m_lootGuid; uint64 m_lootGuid;
uint32 m_race;
uint32 m_class;
uint32 m_team; uint32 m_team;
uint32 m_nextSave; uint32 m_nextSave;
time_t m_speakTime; time_t m_speakTime;

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "8071" #define REVISION_NR "8072"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__