Files
CypherCore/Source/Game/Handlers/CharacterHandler.cs
T

2872 lines
130 KiB
C#

/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Collections;
using Framework.Constants;
using Framework.Database;
using Game.Cache;
using Game.DataStorage;
using Game.Entities;
using Game.Groups;
using Game.Guilds;
using Game.Maps;
using Game.Networking;
using Game.Networking.Packets;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game
{
public partial class WorldSession
{
[WorldPacketHandler(ClientOpcodes.EnumCharacters, Status = SessionStatus.Authed)]
void HandleCharEnum(EnumCharacters charEnum)
{
// remove expired bans
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_EXPIRED_BANS);
DB.Characters.Execute(stmt);
// get all the data necessary for loading all characters (along with their pets) on the account
EnumCharactersQueryHolder holder = new EnumCharactersQueryHolder();
if (!holder.Initialize(GetAccountId(), WorldConfig.GetBoolValue(WorldCfg.DeclinedNamesUsed), false))
{
HandleCharEnum(holder);
return;
}
_charEnumCallback = DB.Characters.DelayQueryHolder(holder);
}
void HandleCharEnum(EnumCharactersQueryHolder holder)
{
EnumCharactersResult charResult = new EnumCharactersResult();
charResult.Success = true;
charResult.IsDeletedCharacters = holder.IsDeletedCharacters();
charResult.DisabledClassesMask.Set(WorldConfig.GetUIntValue(WorldCfg.CharacterCreatingDisabledClassmask));
if (!charResult.IsDeletedCharacters)
_legitCharacters.Clear();
MultiMap<ulong, ChrCustomizationChoice> customizations = new MultiMap<ulong, ChrCustomizationChoice>();
SQLResult customizationsResult = holder.GetResult(EnumCharacterQueryLoad.Customizations);
if (!customizationsResult.IsEmpty())
{
do
{
ChrCustomizationChoice choice = new ChrCustomizationChoice();
choice.ChrCustomizationOptionID = customizationsResult.Read<uint>(1);
choice.ChrCustomizationChoiceID = customizationsResult.Read<uint>(2);
customizations.Add(customizationsResult.Read<ulong>(0), choice);
} while (customizationsResult.NextRow());
}
SQLResult result = holder.GetResult(EnumCharacterQueryLoad.Characters);
if (!result.IsEmpty())
{
do
{
EnumCharactersResult.CharacterInfo charInfo = new EnumCharactersResult.CharacterInfo(result.GetFields());
var customizationsForChar = customizations.LookupByKey(charInfo.Guid.GetCounter());
if (!customizationsForChar.Empty())
charInfo.Customizations = new Array<ChrCustomizationChoice>(customizationsForChar.ToArray());
Log.outInfo(LogFilter.Network, "Loading Character {0} from account {1}.", charInfo.Guid.ToString(), GetAccountId());
if (!charResult.IsDeletedCharacters)
{
if (!ValidateAppearance((Race)charInfo.RaceId, charInfo.ClassId, (Gender)charInfo.SexId, charInfo.Customizations))
{
Log.outError(LogFilter.Player, "Player {0} has wrong Appearance values (Hair/Skin/Color), forcing recustomize", charInfo.Guid.ToString());
charInfo.Customizations.Clear();
if (charInfo.Flags2 != CharacterCustomizeFlags.Customize)
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_ADD_AT_LOGIN_FLAG);
stmt.AddValue(0, (ushort)AtLoginFlags.Customize);
stmt.AddValue(1, charInfo.Guid.GetCounter());
DB.Characters.Execute(stmt);
charInfo.Flags2 = CharacterCustomizeFlags.Customize;
}
}
// Do not allow locked characters to login
if (!charInfo.Flags.HasAnyFlag(CharacterFlags.CharacterLockedForTransfer | CharacterFlags.LockedByBilling))
_legitCharacters.Add(charInfo.Guid);
}
if (!Global.CharacterCacheStorage.HasCharacterCacheEntry(charInfo.Guid)) // This can happen if characters are inserted into the database manually. Core hasn't loaded name data yet.
Global.CharacterCacheStorage.AddCharacterCacheEntry(charInfo.Guid, GetAccountId(), charInfo.Name, charInfo.SexId, charInfo.RaceId, (byte)charInfo.ClassId, charInfo.ExperienceLevel, false);
charResult.MaxCharacterLevel = Math.Max(charResult.MaxCharacterLevel, charInfo.ExperienceLevel);
charResult.Characters.Add(charInfo);
}
while (result.NextRow());
}
charResult.IsAlliedRacesCreationAllowed = GetAccountExpansion() >= Expansion.BattleForAzeroth;
foreach (var requirement in Global.ObjectMgr.GetRaceUnlockRequirements())
{
EnumCharactersResult.RaceUnlock raceUnlock = new EnumCharactersResult.RaceUnlock();
raceUnlock.RaceID = requirement.Key;
raceUnlock.HasExpansion = (byte)GetAccountExpansion() >= requirement.Value.Expansion;
charResult.RaceUnlockData.Add(raceUnlock);
}
SendPacket(charResult);
}
[WorldPacketHandler(ClientOpcodes.EnumCharactersDeletedByClient, Status = SessionStatus.Authed)]
void HandleCharUndeleteEnum(EnumCharacters enumCharacters)
{
// get all the data necessary for loading all undeleted characters (along with their pets) on the account
EnumCharactersQueryHolder holder = new EnumCharactersQueryHolder();
if (!holder.Initialize(GetAccountId(), WorldConfig.GetBoolValue(WorldCfg.DeclinedNamesUsed), true))
{
HandleCharEnum(holder);
return;
}
_charEnumCallback = DB.Characters.DelayQueryHolder(holder);
}
void HandleCharUndeleteEnumCallback(SQLResult result)
{
EnumCharactersResult charEnum = new EnumCharactersResult();
charEnum.Success = true;
charEnum.IsDeletedCharacters = true;
charEnum.DisabledClassesMask.Set(WorldConfig.GetUIntValue(WorldCfg.CharacterCreatingDisabledClassmask));
if (!result.IsEmpty())
{
do
{
EnumCharactersResult.CharacterInfo charInfo = new EnumCharactersResult.CharacterInfo(result.GetFields());
Log.outInfo(LogFilter.Network, "Loading undeleted char guid {0} from account {1}.", charInfo.Guid.ToString(), GetAccountId());
if (!Global.CharacterCacheStorage.HasCharacterCacheEntry(charInfo.Guid)) // This can happen if characters are inserted into the database manually. Core hasn't loaded name data yet.
Global.CharacterCacheStorage.AddCharacterCacheEntry(charInfo.Guid, GetAccountId(), charInfo.Name, charInfo.SexId, charInfo.RaceId, (byte)charInfo.ClassId, charInfo.ExperienceLevel, true);
charEnum.Characters.Add(charInfo);
}
while (result.NextRow());
}
SendPacket(charEnum);
}
public bool MeetsChrCustomizationReq(ChrCustomizationReqRecord req, Class playerClass, bool checkRequiredDependentChoices, List<ChrCustomizationChoice> selectedChoices)
{
if (!req.GetFlags().HasFlag(ChrCustomizationReqFlag.HasRequirements))
return true;
if (req.ClassMask != 0 && (req.ClassMask & (1 << ((int)playerClass - 1))) == 0)
return false;
if (req.AchievementID != 0 /*&& !HasAchieved(req->AchievementID)*/)
return false;
if (req.ItemModifiedAppearanceID != 0 && !GetCollectionMgr().HasItemAppearance(req.ItemModifiedAppearanceID).PermAppearance)
return false;
if (checkRequiredDependentChoices)
{
var requiredChoices = Global.DB2Mgr.GetRequiredCustomizationChoices(req.Id);
if (requiredChoices != null)
{
foreach (var key in requiredChoices.Keys)
{
bool hasRequiredChoiceForOption = false;
foreach (uint requiredChoice in requiredChoices[key])
{
if (selectedChoices.Any(choice => choice.ChrCustomizationChoiceID == requiredChoice))
{
hasRequiredChoiceForOption = true;
break;
}
}
if (!hasRequiredChoiceForOption)
return false;
}
}
}
return true;
}
public bool ValidateAppearance(Race race, Class playerClass, Gender gender, List<ChrCustomizationChoice> customizations)
{
var options = Global.DB2Mgr.GetCustomiztionOptions(race, gender);
if (options.Empty())
return false;
uint previousOption = 0;
foreach (var playerChoice in customizations)
{
// check uniqueness of options
if (playerChoice.ChrCustomizationOptionID == previousOption)
return false;
previousOption = playerChoice.ChrCustomizationOptionID;
// check if we can use this option
var customizationOptionData = options.Find(option => { return option.Id == playerChoice.ChrCustomizationOptionID; });
// option not found for race/gender combination
if (customizationOptionData == null)
return false;
ChrCustomizationReqRecord req = CliDB.ChrCustomizationReqStorage.LookupByKey(customizationOptionData.ChrCustomizationReqID);
if (req != null)
if (!MeetsChrCustomizationReq(req, playerClass, false, customizations))
return false;
var choicesForOption = Global.DB2Mgr.GetCustomiztionChoices(playerChoice.ChrCustomizationOptionID);
if (choicesForOption.Empty())
return false;
var customizationChoiceData = choicesForOption.Find(choice => { return choice.Id == playerChoice.ChrCustomizationChoiceID; });
// choice not found for option
if (customizationChoiceData == null)
return false;
ChrCustomizationReqRecord reqEntry = CliDB.ChrCustomizationReqStorage.LookupByKey(customizationChoiceData.ChrCustomizationReqID);
if (reqEntry != null)
if (!MeetsChrCustomizationReq(reqEntry, playerClass, true, customizations))
return false;
}
return true;
}
[WorldPacketHandler(ClientOpcodes.CreateCharacter, Status = SessionStatus.Authed)]
void HandleCharCreate(CreateCharacter charCreate)
{
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationTeammask))
{
int mask = WorldConfig.GetIntValue(WorldCfg.CharacterCreatingDisabled);
if (mask != 0)
{
bool disabled = false;
uint team = Player.TeamIdForRace(charCreate.CreateInfo.RaceId);
switch (team)
{
case TeamId.Alliance:
disabled = Convert.ToBoolean(mask & (1 << 0));
break;
case TeamId.Horde:
disabled = Convert.ToBoolean(mask & (1 << 1));
break;
case TeamId.Neutral:
disabled = Convert.ToBoolean(mask & (1 << 2));
break;
}
if (disabled)
{
SendCharCreate(ResponseCodes.CharCreateDisabled);
return;
}
}
}
ChrClassesRecord classEntry = CliDB.ChrClassesStorage.LookupByKey(charCreate.CreateInfo.ClassId);
if (classEntry == null)
{
Log.outError(LogFilter.Network, "Class ({0}) not found in DBC while creating new char for account (ID: {1}): wrong DBC files or cheater?", charCreate.CreateInfo.ClassId, GetAccountId());
SendCharCreate(ResponseCodes.CharCreateFailed);
return;
}
ChrRacesRecord raceEntry = CliDB.ChrRacesStorage.LookupByKey(charCreate.CreateInfo.RaceId);
if (raceEntry == null)
{
Log.outError(LogFilter.Network, "Race ({0}) not found in DBC while creating new char for account (ID: {1}): wrong DBC files or cheater?", charCreate.CreateInfo.RaceId, GetAccountId());
SendCharCreate(ResponseCodes.CharCreateFailed);
return;
}
// prevent character creating Expansion race without Expansion account
RaceUnlockRequirement raceExpansionRequirement = Global.ObjectMgr.GetRaceUnlockRequirement(charCreate.CreateInfo.RaceId);
if (raceExpansionRequirement == null)
{
Log.outError(LogFilter.Player, $"Account {GetAccountId()} tried to create character with unavailable race {charCreate.CreateInfo.RaceId}");
SendCharCreate(ResponseCodes.AccountCreateFailed);
return;
}
if (raceExpansionRequirement.Expansion > (byte)GetAccountExpansion())
{
Log.outError(LogFilter.Player, $"Expansion {GetAccountExpansion()} account:[{GetAccountId()}] tried to Create character with expansion {raceExpansionRequirement.Expansion} race ({charCreate.CreateInfo.RaceId})");
SendCharCreate(ResponseCodes.CharCreateExpansion);
return;
}
//if (raceExpansionRequirement.AchievementId && !)
//{
// TC_LOG_ERROR("entities.player.cheat", "Expansion %u account:[%d] tried to Create character without achievement %u race (%u)",
// GetAccountExpansion(), GetAccountId(), raceExpansionRequirement.AchievementId, charCreate.CreateInfo.Race);
// SendCharCreate(CHAR_CREATE_ALLIED_RACE_ACHIEVEMENT);
// return;
//}
// prevent character creating Expansion class without Expansion account
ClassAvailability classExpansionRequirement = Global.ObjectMgr.GetClassExpansionRequirement(charCreate.CreateInfo.RaceId, charCreate.CreateInfo.ClassId);
if (classExpansionRequirement == null)
{
Log.outError(LogFilter.Player, $"Expansion {GetAccountExpansion()} account:[{GetAccountId()}] tried to Create character for race/class combination that is missing requirements in db ({charCreate.CreateInfo.RaceId}/{charCreate.CreateInfo.ClassId})");
SendCharCreate(ResponseCodes.CharCreateExpansionClass);
return;
}
if (classExpansionRequirement.ActiveExpansionLevel > (int)GetExpansion() || classExpansionRequirement.AccountExpansionLevel > (int)GetAccountExpansion())
{
Log.outError(LogFilter.Player, $"Account:[{GetAccountId()}] tried to create character with race/class {charCreate.CreateInfo.RaceId}/{charCreate.CreateInfo.ClassId} without required expansion(had {GetExpansion()}/{GetAccountExpansion()}, required {classExpansionRequirement.ActiveExpansionLevel}/{classExpansionRequirement.AccountExpansionLevel})");
SendCharCreate(ResponseCodes.CharCreateExpansionClass);
return;
}
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationRacemask))
{
ulong raceMaskDisabled = WorldConfig.GetUInt64Value(WorldCfg.CharacterCreatingDisabledRacemask);
if (Convert.ToBoolean((1ul << ((int)charCreate.CreateInfo.RaceId - 1)) & raceMaskDisabled))
{
SendCharCreate(ResponseCodes.CharCreateDisabled);
return;
}
}
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationClassmask))
{
int classMaskDisabled = WorldConfig.GetIntValue(WorldCfg.CharacterCreatingDisabledClassmask);
if (Convert.ToBoolean((1 << ((int)charCreate.CreateInfo.ClassId - 1)) & classMaskDisabled))
{
SendCharCreate(ResponseCodes.CharCreateDisabled);
return;
}
}
// prevent character creating with invalid name
if (!ObjectManager.NormalizePlayerName(ref charCreate.CreateInfo.Name))
{
Log.outError(LogFilter.Network, "Account:[{0}] but tried to Create character with empty [name] ", GetAccountId());
SendCharCreate(ResponseCodes.CharNameNoName);
return;
}
// check name limitations
ResponseCodes res = ObjectManager.CheckPlayerName(charCreate.CreateInfo.Name, GetSessionDbcLocale(), true);
if (res != ResponseCodes.CharNameSuccess)
{
SendCharCreate(res);
return;
}
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationReservedname) && Global.ObjectMgr.IsReservedName(charCreate.CreateInfo.Name))
{
SendCharCreate(ResponseCodes.CharNameReserved);
return;
}
CharacterCreateInfo createInfo = charCreate.CreateInfo;
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHECK_NAME);
stmt.AddValue(0, charCreate.CreateInfo.Name);
_queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt).WithChainingCallback((queryCallback, result) =>
{
if (!result.IsEmpty())
{
SendCharCreate(ResponseCodes.CharCreateNameInUse);
return;
}
stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_SUM_REALM_CHARACTERS);
stmt.AddValue(0, GetAccountId());
queryCallback.SetNextQuery(DB.Login.AsyncQuery(stmt));
}).WithChainingCallback((queryCallback, result) =>
{
ulong acctCharCount = 0;
if (!result.IsEmpty())
acctCharCount = result.Read<ulong>(0);
if (acctCharCount >= WorldConfig.GetUIntValue(WorldCfg.CharactersPerAccount))
{
SendCharCreate(ResponseCodes.CharCreateAccountLimit);
return;
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_SUM_CHARS);
stmt.AddValue(0, GetAccountId());
queryCallback.SetNextQuery(DB.Characters.AsyncQuery(stmt));
}).WithChainingCallback((queryCallback, result) =>
{
if (!result.IsEmpty())
{
createInfo.CharCount = (byte)result.Read<ulong>(0); // SQL's COUNT() returns uint64 but it will always be less than uint8.Max
if (createInfo.CharCount >= WorldConfig.GetIntValue(WorldCfg.CharactersPerRealm))
{
SendCharCreate(ResponseCodes.CharCreateServerLimit);
return;
}
}
int demonHunterReqLevel = WorldConfig.GetIntValue(WorldCfg.CharacterCreatingMinLevelForDemonHunter);
bool hasDemonHunterReqLevel = demonHunterReqLevel == 0;
bool allowTwoSideAccounts = !Global.WorldMgr.IsPvPRealm() || HasPermission(RBACPermissions.TwoSideCharacterCreation);
int skipCinematics = WorldConfig.GetIntValue(WorldCfg.SkipCinematics);
bool checkDemonHunterReqs = createInfo.ClassId == Class.DemonHunter && !HasPermission(RBACPermissions.SkipCheckCharacterCreationDemonHunter);
void finalizeCharacterCreation(SQLResult result1)
{
bool haveSameRace = false;
if (result1 != null && !result1.IsEmpty())
{
Team team = Player.TeamForRace(createInfo.RaceId);
byte accRace = result1.Read<byte>(1);
if (checkDemonHunterReqs)
{
if (!hasDemonHunterReqLevel)
{
byte accLevel = result1.Read<byte>(0);
if (accLevel >= demonHunterReqLevel)
hasDemonHunterReqLevel = true;
}
}
// need to check team only for first character
// @todo what to if account already has characters of both races?
if (!allowTwoSideAccounts)
{
Team accTeam = 0;
if (accRace > 0)
accTeam = Player.TeamForRace((Race)accRace);
if (accTeam != team)
{
SendCharCreate(ResponseCodes.CharCreatePvpTeamsViolation);
return;
}
}
// search same race for cinematic or same class if need
// @todo check if cinematic already shown? (already logged in?; cinematic field)
while ((skipCinematics == 1 && !haveSameRace) || createInfo.ClassId == Class.DemonHunter)
{
if (!result1.NextRow())
break;
accRace = result1.Read<byte>(1);
if (!haveSameRace)
haveSameRace = createInfo.RaceId == (Race)accRace;
if (checkDemonHunterReqs)
{
if (!hasDemonHunterReqLevel)
{
byte acc_level = result1.Read<byte>(0);
if (acc_level >= demonHunterReqLevel)
hasDemonHunterReqLevel = true;
}
}
}
}
if (checkDemonHunterReqs && !hasDemonHunterReqLevel)
{
SendCharCreate(ResponseCodes.CharCreateNewPlayer);
return;
}
Player newChar = new Player(this);
newChar.GetMotionMaster().Initialize();
if (!newChar.Create(Global.ObjectMgr.GetGenerator(HighGuid.Player).Generate(), createInfo))
{
// Player not create (race/class/etc problem?)
newChar.CleanupsBeforeDelete();
SendCharCreate(ResponseCodes.CharCreateError);
return;
}
if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2)
newChar.SetCinematic(1); // not show intro
newChar.atLoginFlags = AtLoginFlags.FirstLogin; // First login
SQLTransaction characterTransaction = new SQLTransaction();
SQLTransaction loginTransaction = new SQLTransaction();
// Player created, save it now
newChar.SaveToDB(loginTransaction, characterTransaction, true);
createInfo.CharCount += 1;
stmt = DB.Login.GetPreparedStatement(LoginStatements.DEL_REALM_CHARACTERS_BY_REALM);
stmt.AddValue(0, GetAccountId());
stmt.AddValue(1, Global.WorldMgr.GetRealm().Id.Index);
loginTransaction.Append(stmt);
stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_REALM_CHARACTERS);
stmt.AddValue(0, createInfo.CharCount);
stmt.AddValue(1, GetAccountId());
stmt.AddValue(2, Global.WorldMgr.GetRealm().Id.Index);
loginTransaction.Append(stmt);
DB.Login.CommitTransaction(loginTransaction);
AddTransactionCallback(DB.Characters.AsyncCommitTransaction(characterTransaction)).AfterComplete(success =>
{
if (success)
{
Log.outInfo(LogFilter.Player, "Account: {0} (IP: {1}) Create Character: {2} {3}", GetAccountId(), GetRemoteAddress(), createInfo.Name, newChar.GetGUID().ToString());
Global.ScriptMgr.OnPlayerCreate(newChar);
Global.CharacterCacheStorage.AddCharacterCacheEntry(newChar.GetGUID(), GetAccountId(), newChar.GetName(), (byte)newChar.GetNativeSex(), (byte)newChar.GetRace(), (byte)newChar.GetClass(), (byte)newChar.GetLevel(), false);
SendCharCreate(ResponseCodes.CharCreateSuccess, newChar.GetGUID());
}
else
SendCharCreate(ResponseCodes.CharCreateError);
});
}
if (!allowTwoSideAccounts || skipCinematics == 1 || createInfo.ClassId == Class.DemonHunter)
{
finalizeCharacterCreation(new SQLResult());
return;
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_CREATE_INFO);
stmt.AddValue(0, GetAccountId());
stmt.AddValue(1, (skipCinematics == 1 || createInfo.ClassId == Class.DemonHunter) ? 1200 : 1); // 200 (max chars per realm) + 1000 (max deleted chars per realm)
queryCallback.WithCallback(finalizeCharacterCreation).SetNextQuery(DB.Characters.AsyncQuery(stmt));
}));
}
[WorldPacketHandler(ClientOpcodes.CharDelete, Status = SessionStatus.Authed)]
void HandleCharDelete(CharDelete charDelete)
{
// Initiating
uint initAccountId = GetAccountId();
// can't delete loaded character
if (Global.ObjAccessor.FindPlayer(charDelete.Guid))
{
Global.ScriptMgr.OnPlayerFailedDelete(charDelete.Guid, initAccountId);
return;
}
// is guild leader
if (Global.GuildMgr.GetGuildByLeader(charDelete.Guid))
{
Global.ScriptMgr.OnPlayerFailedDelete(charDelete.Guid, initAccountId);
SendCharDelete(ResponseCodes.CharDeleteFailedGuildLeader);
return;
}
// is arena team captain
if (Global.ArenaTeamMgr.GetArenaTeamByCaptain(charDelete.Guid) != null)
{
Global.ScriptMgr.OnPlayerFailedDelete(charDelete.Guid, initAccountId);
SendCharDelete(ResponseCodes.CharDeleteFailedArenaCaptain);
return;
}
CharacterCacheEntry characterInfo = Global.CharacterCacheStorage.GetCharacterCacheByGuid(charDelete.Guid);
if (characterInfo == null)
{
Global.ScriptMgr.OnPlayerFailedDelete(charDelete.Guid, initAccountId);
return;
}
uint accountId = characterInfo.AccountId;
string name = characterInfo.Name;
byte level = characterInfo.Level;
// prevent deleting other players' characters using cheating tools
if (accountId != GetAccountId())
{
Global.ScriptMgr.OnPlayerFailedDelete(charDelete.Guid, initAccountId);
return;
}
string IP_str = GetRemoteAddress();
Log.outInfo(LogFilter.Player, "Account: {0}, IP: {1} deleted character: {2}, {3}, Level: {4}", accountId, IP_str, name, charDelete.Guid.ToString(), level);
// To prevent hook failure, place hook before removing reference from DB
Global.ScriptMgr.OnPlayerDelete(charDelete.Guid, initAccountId); // To prevent race conditioning, but as it also makes sense, we hand the accountId over for successful delete.
// Shouldn't interfere with character deletion though
Global.GuildFinderMgr.RemoveAllMembershipRequestsFromPlayer(charDelete.Guid);
Global.CalendarMgr.RemoveAllPlayerEventsAndInvites(charDelete.Guid);
Player.DeleteFromDB(charDelete.Guid, accountId);
SendCharDelete(ResponseCodes.CharDeleteSuccess);
}
[WorldPacketHandler(ClientOpcodes.GenerateRandomCharacterName, Status = SessionStatus.Authed)]
void HandleRandomizeCharName(GenerateRandomCharacterName packet)
{
if (!Player.IsValidRace((Race)packet.Race))
{
Log.outError(LogFilter.Network, "Invalid race ({0}) sent by accountId: {1}", packet.Race, GetAccountId());
return;
}
if (!Player.IsValidGender((Gender)packet.Sex))
{
Log.outError(LogFilter.Network, "Invalid gender ({0}) sent by accountId: {1}", packet.Sex, GetAccountId());
return;
}
GenerateRandomCharacterNameResult result = new GenerateRandomCharacterNameResult();
result.Success = true;
result.Name = Global.DB2Mgr.GetNameGenEntry(packet.Race, packet.Sex);
SendPacket(result);
}
[WorldPacketHandler(ClientOpcodes.ReorderCharacters, Status = SessionStatus.Authed)]
void HandleReorderCharacters(ReorderCharacters reorderChars)
{
SQLTransaction trans = new SQLTransaction();
foreach (var reorderInfo in reorderChars.Entries)
{
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_LIST_SLOT);
stmt.AddValue(0, reorderInfo.NewPosition);
stmt.AddValue(1, reorderInfo.PlayerGUID.GetCounter());
stmt.AddValue(2, GetAccountId());
trans.Append(stmt);
}
DB.Characters.CommitTransaction(trans);
}
[WorldPacketHandler(ClientOpcodes.PlayerLogin, Status = SessionStatus.Authed)]
void HandlePlayerLogin(PlayerLogin playerLogin)
{
if (PlayerLoading() || GetPlayer() != null)
{
Log.outError(LogFilter.Network, "Player tries to login again, AccountId = {0}", GetAccountId());
return;
}
m_playerLoading = playerLogin.Guid;
Log.outDebug(LogFilter.Network, "Character {0} logging in", playerLogin.Guid.ToString());
if (!_legitCharacters.Contains(playerLogin.Guid))
{
Log.outError(LogFilter.Network, "Account ({0}) can't login with that character ({1}).", GetAccountId(), playerLogin.Guid.ToString());
KickPlayer();
return;
}
SendConnectToInstance(ConnectToSerial.WorldAttempt1);
}
public void HandleContinuePlayerLogin()
{
if (!PlayerLoading() || GetPlayer())
{
KickPlayer();
return;
}
LoginQueryHolder holder = new LoginQueryHolder(GetAccountId(), m_playerLoading);
holder.Initialize();
SendPacket(new ResumeComms(ConnectionType.Instance));
_charLoginCallback = DB.Characters.DelayQueryHolder(holder);
}
public void HandlePlayerLogin(LoginQueryHolder holder)
{
ObjectGuid playerGuid = holder.GetGuid();
Player pCurrChar = new Player(this);
if (!pCurrChar.LoadFromDB(playerGuid, holder))
{
SetPlayer(null);
KickPlayer();
m_playerLoading.Clear();
return;
}
pCurrChar.SetVirtualPlayerRealm(Global.WorldMgr.GetVirtualRealmAddress());
SendTutorialsData();
pCurrChar.GetMotionMaster().Initialize();
pCurrChar.SendDungeonDifficulty();
LoginVerifyWorld loginVerifyWorld = new LoginVerifyWorld();
loginVerifyWorld.MapID = (int)pCurrChar.GetMapId();
loginVerifyWorld.Pos = pCurrChar.GetPosition();
SendPacket(loginVerifyWorld);
LoadAccountData(holder.GetResult(PlayerLoginQueryLoad.AccountData), AccountDataTypes.PerCharacterCacheMask);
AccountDataTimes accountDataTimes = new AccountDataTimes();
accountDataTimes.PlayerGuid = playerGuid;
accountDataTimes.ServerTime = (uint)GameTime.GetGameTime();
for (AccountDataTypes i = 0; i < AccountDataTypes.Max; ++i)
accountDataTimes.AccountTimes[(int)i] = (uint)GetAccountData(i).Time;
SendPacket(accountDataTimes);
SendFeatureSystemStatus();
MOTD motd = new MOTD();
motd.Text = Global.WorldMgr.GetMotd();
SendPacket(motd);
SendSetTimeZoneInformation();
// Send PVPSeason
{
SeasonInfo seasonInfo = new SeasonInfo();
seasonInfo.PreviousSeason = (WorldConfig.GetIntValue(WorldCfg.ArenaSeasonId) - (WorldConfig.GetBoolValue(WorldCfg.ArenaSeasonInProgress) ? 1 : 0));
if (WorldConfig.GetBoolValue(WorldCfg.ArenaSeasonInProgress))
seasonInfo.CurrentSeason = WorldConfig.GetIntValue(WorldCfg.ArenaSeasonId);
SendPacket(seasonInfo);
}
SQLResult resultGuild = holder.GetResult(PlayerLoginQueryLoad.Guild);
if (!resultGuild.IsEmpty())
{
pCurrChar.SetInGuild(resultGuild.Read<uint>(0));
pCurrChar.SetGuildRank(resultGuild.Read<byte>(1));
Guild guild = Global.GuildMgr.GetGuildById(pCurrChar.GetGuildId());
if (guild)
pCurrChar.SetGuildLevel(guild.GetLevel());
}
else if (pCurrChar.GetGuildId() != 0)
{
pCurrChar.SetInGuild(0);
pCurrChar.SetGuildRank(0);
pCurrChar.SetGuildLevel(0);
}
// TODO: Move this to BattlePetMgr::SendJournalLock() just to have all packets in one file
SendPacket(new BattlePetJournalLockAcquired());
pCurrChar.SendInitialPacketsBeforeAddToMap();
//Show cinematic at the first time that player login
if (pCurrChar.GetCinematic() == 0)
{
pCurrChar.SetCinematic(1);
ChrClassesRecord cEntry = CliDB.ChrClassesStorage.LookupByKey(pCurrChar.GetClass());
if (cEntry != null)
{
ChrRacesRecord rEntry = CliDB.ChrRacesStorage.LookupByKey(pCurrChar.GetRace());
if (pCurrChar.GetClass() == Class.DemonHunter) // @todo: find a more generic solution
pCurrChar.SendMovieStart(469);
else if (cEntry.CinematicSequenceID != 0)
pCurrChar.SendCinematicStart(cEntry.CinematicSequenceID);
else if (rEntry != null)
pCurrChar.SendCinematicStart(rEntry.CinematicSequenceID);
}
}
if (!pCurrChar.GetMap().AddPlayerToMap(pCurrChar))
{
var at = Global.ObjectMgr.GetGoBackTrigger(pCurrChar.GetMapId());
if (at != null)
pCurrChar.TeleportTo(at.target_mapId, at.target_X, at.target_Y, at.target_Z, pCurrChar.Orientation);
else
pCurrChar.TeleportTo(pCurrChar.GetHomebind());
}
Global.ObjAccessor.AddObject(pCurrChar);
if (pCurrChar.GetGuildId() != 0)
{
Guild guild = Global.GuildMgr.GetGuildById(pCurrChar.GetGuildId());
if (guild)
guild.SendLoginInfo(this);
else
{
// remove wrong guild data
Log.outError(LogFilter.Server, "Player {0} ({1}) marked as member of not existing guild (id: {2}), removing guild membership for player.", pCurrChar.GetName(), pCurrChar.GetGUID().ToString(),
pCurrChar.GetGuildId());
pCurrChar.SetInGuild(0);
}
}
pCurrChar.SendInitialPacketsAfterAddToMap();
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_ONLINE);
stmt.AddValue(0, pCurrChar.GetGUID().GetCounter());
DB.Characters.Execute(stmt);
stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_ACCOUNT_ONLINE);
stmt.AddValue(0, GetAccountId());
DB.Login.Execute(stmt);
pCurrChar.SetInGameTime(GameTime.GetGameTimeMS());
// announce group about member online (must be after add to player list to receive announce to self)
Group group = pCurrChar.GetGroup();
if (group)
{
group.SendUpdate();
group.ResetMaxEnchantingLevel();
}
// friend status
Global.SocialMgr.SendFriendStatus(pCurrChar, FriendsResult.Online, pCurrChar.GetGUID(), true);
// Place character in world (and load zone) before some object loading
pCurrChar.LoadCorpse(holder.GetResult(PlayerLoginQueryLoad.CorpseLocation));
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_FAVORITE_AUCTIONS);
stmt.AddValue(0, pCurrChar.GetGUID().GetCounter());
GetQueryProcessor().AddCallback(DB.Characters.AsyncQuery(stmt)).WithCallback(favoriteAuctionResult =>
{
AuctionFavoriteList favoriteItems = new AuctionFavoriteList();
if (!favoriteAuctionResult.IsEmpty())
{
do
{
AuctionFavoriteInfo item = new AuctionFavoriteInfo();
item.Order = favoriteAuctionResult.Read<uint>(0);
item.ItemID = favoriteAuctionResult.Read<uint>(1);
item.ItemLevel = favoriteAuctionResult.Read<uint>(2);
item.BattlePetSpeciesID = favoriteAuctionResult.Read<uint>(3);
item.SuffixItemNameDescriptionID = favoriteAuctionResult.Read<uint>(4);
favoriteItems.Items.Add(item);
} while (favoriteAuctionResult.NextRow());
}
SendPacket(favoriteItems);
});
// setting Ghost+speed if dead
if (pCurrChar.GetDeathState() != DeathState.Alive)
{
// not blizz like, we must correctly save and load player instead...
if (pCurrChar.GetRace() == Race.NightElf && !pCurrChar.HasAura(20584))
pCurrChar.CastSpell(pCurrChar, 20584, true);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
if (!pCurrChar.HasAura(8326))
pCurrChar.CastSpell(pCurrChar, 8326, true, null); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
pCurrChar.SetWaterWalking(true);
}
pCurrChar.ContinueTaxiFlight();
// reset for all pets before pet loading
if (pCurrChar.HasAtLoginFlag(AtLoginFlags.ResetPetTalents))
{
// Delete all of the player's pet spells
PreparedStatement stmtSpells = DB.Characters.GetPreparedStatement(CharStatements.DEL_ALL_PET_SPELLS_BY_OWNER);
stmtSpells.AddValue(0, pCurrChar.GetGUID().GetCounter());
DB.Characters.Execute(stmtSpells);
// Then reset all of the player's pet specualizations
PreparedStatement stmtSpec = DB.Characters.GetPreparedStatement(CharStatements.UPD_PET_SPECS_BY_OWNER);
stmtSpec.AddValue(0, pCurrChar.GetGUID().GetCounter());
DB.Characters.Execute(stmtSpec);
}
// Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned)
pCurrChar.LoadPet();
// Set FFA PvP for non GM in non-rest mode
if (Global.WorldMgr.IsFFAPvPRealm() && !pCurrChar.IsGameMaster() && !pCurrChar.HasPlayerFlag(PlayerFlags.Resting))
pCurrChar.AddPvpFlag(UnitPVPStateFlags.FFAPvp);
if (pCurrChar.HasPlayerFlag(PlayerFlags.ContestedPVP))
pCurrChar.SetContestedPvP();
// Apply at_login requests
if (pCurrChar.HasAtLoginFlag(AtLoginFlags.ResetSpells))
{
pCurrChar.ResetSpells();
SendNotification(CypherStrings.ResetSpells);
}
if (pCurrChar.HasAtLoginFlag(AtLoginFlags.ResetTalents))
{
pCurrChar.ResetTalents(true);
pCurrChar.ResetTalentSpecialization();
pCurrChar.SendTalentsInfoData(); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state
SendNotification(CypherStrings.ResetTalents);
}
if (pCurrChar.HasAtLoginFlag(AtLoginFlags.FirstLogin))
{
pCurrChar.RemoveAtLoginFlag(AtLoginFlags.FirstLogin);
PlayerInfo info = Global.ObjectMgr.GetPlayerInfo(pCurrChar.GetRace(), pCurrChar.GetClass());
foreach (var spellId in info.castSpells)
pCurrChar.CastSpell(pCurrChar, spellId, true);
// start with every map explored
if (WorldConfig.GetBoolValue(WorldCfg.StartAllExplored))
{
for (ushort i = 0; i < PlayerConst.ExploredZonesSize; i++)
pCurrChar.SetUpdateFieldValue(ref pCurrChar.m_values.ModifyValue(pCurrChar.m_activePlayerData).ModifyValue(pCurrChar.m_activePlayerData.ExploredZones, i), 0xFFFFFFFFFFFFFFFF);
}
//Reputations if "StartAllReputation" is enabled
if (WorldConfig.GetBoolValue(WorldCfg.StartAllRep))
{
ReputationMgr repMgr = pCurrChar.GetReputationMgr();
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(942), 42999, false); // Cenarion Expedition
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(935), 42999, false); // The Sha'tar
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(936), 42999, false); // Shattrath City
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1011), 42999, false); // Lower City
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(970), 42999, false); // Sporeggar
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(967), 42999, false); // The Violet Eye
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(989), 42999, false); // Keepers of Time
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(932), 42999, false); // The Aldor
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(934), 42999, false); // The Scryers
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1038), 42999, false); // Ogri'la
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1077), 42999, false); // Shattered Sun Offensive
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1106), 42999, false); // Argent Crusade
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1104), 42999, false); // Frenzyheart Tribe
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1090), 42999, false); // Kirin Tor
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1098), 42999, false); // Knights of the Ebon Blade
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1156), 42999, false); // The Ashen Verdict
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1073), 42999, false); // The Kalu'ak
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1105), 42999, false); // The Oracles
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1119), 42999, false); // The Sons of Hodir
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1091), 42999, false); // The Wyrmrest Accord
// Factions depending on team, like cities and some more stuff
switch (pCurrChar.GetTeam())
{
case Team.Alliance:
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(72), 42999, false); // Stormwind
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(47), 42999, false); // Ironforge
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(69), 42999, false); // Darnassus
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(930), 42999, false); // Exodar
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(730), 42999, false); // Stormpike Guard
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(978), 42999, false); // Kurenai
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(54), 42999, false); // Gnomeregan Exiles
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(946), 42999, false); // Honor Hold
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1037), 42999, false); // Alliance Vanguard
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1068), 42999, false); // Explorers' League
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1126), 42999, false); // The Frostborn
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1094), 42999, false); // The Silver Covenant
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1050), 42999, false); // Valiance Expedition
break;
case Team.Horde:
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(76), 42999, false); // Orgrimmar
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(68), 42999, false); // Undercity
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(81), 42999, false); // Thunder Bluff
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(911), 42999, false); // Silvermoon City
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(729), 42999, false); // Frostwolf Clan
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(941), 42999, false); // The Mag'har
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(530), 42999, false); // Darkspear Trolls
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(947), 42999, false); // Thrallmar
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1052), 42999, false); // Horde Expedition
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1067), 42999, false); // The Hand of Vengeance
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1124), 42999, false); // The Sunreavers
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1064), 42999, false); // The Taunka
repMgr.SetOneFactionReputation(CliDB.FactionStorage.LookupByKey(1085), 42999, false); // Warsong Offensive
break;
default:
break;
}
repMgr.SendState(null);
}
}
// show time before shutdown if shutdown planned.
if (Global.WorldMgr.IsShuttingDown())
Global.WorldMgr.ShutdownMsg(true, pCurrChar);
if (WorldConfig.GetBoolValue(WorldCfg.AllTaxiPaths))
pCurrChar.SetTaxiCheater(true);
if (pCurrChar.IsGameMaster())
SendNotification(CypherStrings.GmOn);
string IP_str = GetRemoteAddress();
Log.outDebug(LogFilter.Network, "Account: {0} (IP: {1}) Login Character:[{2}] ({3}) Level: {4}",
GetAccountId(), IP_str, pCurrChar.GetName(), pCurrChar.GetGUID().ToString(), pCurrChar.GetLevel());
if (!pCurrChar.IsStandState() && !pCurrChar.HasUnitState(UnitState.Stunned))
pCurrChar.SetStandState(UnitStandStateType.Stand);
m_playerLoading.Clear();
Global.ScriptMgr.OnPlayerLogin(pCurrChar);
}
public void AbortLogin(LoginFailureReason reason)
{
if (!PlayerLoading() || GetPlayer())
{
KickPlayer();
return;
}
m_playerLoading.Clear();
SendPacket(new CharacterLoginFailed(reason));
}
[WorldPacketHandler(ClientOpcodes.LoadingScreenNotify, Status = SessionStatus.Authed)]
void HandleLoadScreen(LoadingScreenNotify loadingScreenNotify)
{
// TODO: Do something with this packet
}
public void SendFeatureSystemStatus()
{
FeatureSystemStatus features = new FeatureSystemStatus();
// START OF DUMMY VALUES
features.ComplaintStatus = 2;
features.ScrollOfResurrectionRequestsRemaining = 1;
features.ScrollOfResurrectionMaxRequestsPerDay = 1;
features.TwitterPostThrottleLimit = 60;
features.TwitterPostThrottleCooldown = 20;
features.CfgRealmID = 2;
features.CfgRealmRecID = 0;
features.TokenPollTimeSeconds = 300;
features.VoiceEnabled = false;
features.BrowserEnabled = false; // Has to be false, otherwise client will crash if "Customer Support" is opened
features.EuropaTicketSystemStatus.HasValue = true;
features.EuropaTicketSystemStatus.Value.ThrottleState.MaxTries = 10;
features.EuropaTicketSystemStatus.Value.ThrottleState.PerMilliseconds = 60000;
features.EuropaTicketSystemStatus.Value.ThrottleState.TryCount = 1;
features.EuropaTicketSystemStatus.Value.ThrottleState.LastResetTimeBeforeNow = 111111;
features.ComplaintStatus = 0;
features.TutorialsEnabled = true;
features.NPETutorialsEnabled = true;
// END OF DUMMY VALUES
features.EuropaTicketSystemStatus.Value.TicketsEnabled = WorldConfig.GetBoolValue(WorldCfg.SupportTicketsEnabled);
features.EuropaTicketSystemStatus.Value.BugsEnabled = WorldConfig.GetBoolValue(WorldCfg.SupportBugsEnabled);
features.EuropaTicketSystemStatus.Value.ComplaintsEnabled = WorldConfig.GetBoolValue(WorldCfg.SupportComplaintsEnabled);
features.EuropaTicketSystemStatus.Value.SuggestionsEnabled = WorldConfig.GetBoolValue(WorldCfg.SupportSuggestionsEnabled);
features.CharUndeleteEnabled = WorldConfig.GetBoolValue(WorldCfg.FeatureSystemCharacterUndeleteEnabled);
features.BpayStoreEnabled = WorldConfig.GetBoolValue(WorldCfg.FeatureSystemBpayStoreEnabled);
features.IsMuted = !CanSpeak();
SendPacket(features);
}
[WorldPacketHandler(ClientOpcodes.SetFactionAtWar)]
void HandleSetFactionAtWar(SetFactionAtWar packet)
{
GetPlayer().GetReputationMgr().SetAtWar(packet.FactionIndex, true);
}
[WorldPacketHandler(ClientOpcodes.SetFactionNotAtWar)]
void HandleSetFactionNotAtWar(SetFactionNotAtWar packet)
{
GetPlayer().GetReputationMgr().SetAtWar(packet.FactionIndex, false);
}
[WorldPacketHandler(ClientOpcodes.Tutorial)]
void HandleTutorialFlag(TutorialSetFlag packet)
{
switch (packet.Action)
{
case TutorialAction.Update:
{
byte index = (byte)(packet.TutorialBit >> 5);
if (index >= SharedConst.MaxAccountTutorialValues)
{
Log.outError(LogFilter.Network, "CMSG_TUTORIAL_FLAG received bad TutorialBit {0}.", packet.TutorialBit);
return;
}
uint flag = GetTutorialInt(index);
flag |= (uint)(1 << (int)(packet.TutorialBit & 0x1F));
SetTutorialInt(index, flag);
break;
}
case TutorialAction.Clear:
for (byte i = 0; i < SharedConst.MaxAccountTutorialValues; ++i)
SetTutorialInt(i, 0xFFFFFFFF);
break;
case TutorialAction.Reset:
for (byte i = 0; i < SharedConst.MaxAccountTutorialValues; ++i)
SetTutorialInt(i, 0x00000000);
break;
default:
Log.outError(LogFilter.Network, "CMSG_TUTORIAL_FLAG received unknown TutorialAction {0}.", packet.Action);
return;
}
}
[WorldPacketHandler(ClientOpcodes.SetWatchedFaction)]
void HandleSetWatchedFaction(SetWatchedFaction packet)
{
GetPlayer().SetWatchedFactionIndex(packet.FactionIndex);
}
[WorldPacketHandler(ClientOpcodes.SetFactionInactive)]
void HandleSetFactionInactive(SetFactionInactive packet)
{
GetPlayer().GetReputationMgr().SetInactive(packet.Index, packet.State);
}
[WorldPacketHandler(ClientOpcodes.CheckCharacterNameAvailability)]
void HandleCheckCharacterNameAvailability(CheckCharacterNameAvailability checkCharacterNameAvailability)
{
// prevent character rename to invalid name
if (!ObjectManager.NormalizePlayerName(ref checkCharacterNameAvailability.Name))
{
SendPacket(new CheckCharacterNameAvailabilityResult(checkCharacterNameAvailability.SequenceIndex, ResponseCodes.CharNameNoName));
return;
}
ResponseCodes res = ObjectManager.CheckPlayerName(checkCharacterNameAvailability.Name, GetSessionDbcLocale(), true);
if (res != ResponseCodes.CharNameSuccess)
{
SendPacket(new CheckCharacterNameAvailabilityResult(checkCharacterNameAvailability.SequenceIndex, res));
return;
}
// check name limitations
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationReservedname) && Global.ObjectMgr.IsReservedName(checkCharacterNameAvailability.Name))
{
SendPacket(new CheckCharacterNameAvailabilityResult(checkCharacterNameAvailability.SequenceIndex, ResponseCodes.CharNameReserved));
return;
}
// Ensure that there is no character with the desired new name
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHECK_NAME);
stmt.AddValue(0, checkCharacterNameAvailability.Name);
_queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt).WithCallback(result =>
{
SendPacket(new CheckCharacterNameAvailabilityResult(checkCharacterNameAvailability.SequenceIndex, !result.IsEmpty() ? ResponseCodes.CharCreateNameInUse : ResponseCodes.Success));
}));
}
[WorldPacketHandler(ClientOpcodes.RequestForcedReactions)]
void HandleRequestForcedReactions(RequestForcedReactions requestForcedReactions)
{
GetPlayer().GetReputationMgr().SendForceReactions();
}
[WorldPacketHandler(ClientOpcodes.CharacterRenameRequest, Status = SessionStatus.Authed)]
void HandleCharRename(CharacterRenameRequest request)
{
if (!_legitCharacters.Contains(request.RenameInfo.Guid))
{
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to rename character {2}, but it does not belong to their account!",
GetAccountId(), GetRemoteAddress(), request.RenameInfo.Guid.ToString());
KickPlayer();
return;
}
// prevent character rename to invalid name
if (!ObjectManager.NormalizePlayerName(ref request.RenameInfo.NewName))
{
SendCharRename(ResponseCodes.CharNameNoName, request.RenameInfo);
return;
}
ResponseCodes res = ObjectManager.CheckPlayerName(request.RenameInfo.NewName, GetSessionDbcLocale(), true);
if (res != ResponseCodes.CharNameSuccess)
{
SendCharRename(res, request.RenameInfo);
return;
}
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationReservedname) && Global.ObjectMgr.IsReservedName(request.RenameInfo.NewName))
{
SendCharRename(ResponseCodes.CharNameReserved, request.RenameInfo);
return;
}
// Ensure that there is no character with the desired new name
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_FREE_NAME);
stmt.AddValue(0, request.RenameInfo.Guid.GetCounter());
stmt.AddValue(1, request.RenameInfo.NewName);
_queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt).WithCallback(HandleCharRenameCallBack, request.RenameInfo));
}
void HandleCharRenameCallBack(CharacterRenameInfo renameInfo, SQLResult result)
{
if (result.IsEmpty())
{
SendCharRename(ResponseCodes.CharNameFailure, renameInfo);
return;
}
string oldName = result.Read<string>(0);
// check name limitations
AtLoginFlags atLoginFlags = (AtLoginFlags)result.Read<uint>(1);
if (!atLoginFlags.HasAnyFlag(AtLoginFlags.Rename))
{
SendCharRename(ResponseCodes.CharCreateError, renameInfo);
return;
}
atLoginFlags &= ~AtLoginFlags.Rename;
SQLTransaction trans = new SQLTransaction();
ulong lowGuid = renameInfo.Guid.GetCounter();
// Update name and at_login flag in the db
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_NAME_AT_LOGIN);
stmt.AddValue(0, renameInfo.NewName);
stmt.AddValue(1, atLoginFlags);
stmt.AddValue(2, lowGuid);
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_DECLINED_NAME);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
DB.Characters.CommitTransaction(trans);
Log.outInfo(LogFilter.Player, "Account: {0} (IP: {1}) Character:[{2}] ({3}) Changed name to: {4}",
GetAccountId(), GetRemoteAddress(), oldName, renameInfo.Guid.ToString(), renameInfo.NewName);
SendCharRename(ResponseCodes.Success, renameInfo);
Global.CharacterCacheStorage.UpdateCharacterData(renameInfo.Guid, renameInfo.NewName);
}
[WorldPacketHandler(ClientOpcodes.SetPlayerDeclinedNames, Status = SessionStatus.Authed)]
void HandleSetPlayerDeclinedNames(SetPlayerDeclinedNames packet)
{
// not accept declined names for unsupported languages
string name;
if (!Global.CharacterCacheStorage.GetCharacterNameByGuid(packet.Player, out name))
{
SendSetPlayerDeclinedNamesResult(DeclinedNameResult.Error, packet.Player);
return;
}
if (!char.IsLetter(name[0])) // name already stored as only single alphabet using
{
SendSetPlayerDeclinedNamesResult(DeclinedNameResult.Error, packet.Player);
return;
}
for (int i = 0; i < SharedConst.MaxDeclinedNameCases; ++i)
{
string declinedName = packet.DeclinedNames.name[i];
if (!ObjectManager.NormalizePlayerName(ref declinedName))
{
SendSetPlayerDeclinedNamesResult(DeclinedNameResult.Error, packet.Player);
return;
}
packet.DeclinedNames.name[i] = declinedName;
}
for (int i = 0; i < SharedConst.MaxDeclinedNameCases; ++i)
{
string declinedName = packet.DeclinedNames.name[i];
DB.Characters.EscapeString(ref declinedName);
packet.DeclinedNames.name[i] = declinedName;
}
SQLTransaction trans = new SQLTransaction();
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_DECLINED_NAME);
stmt.AddValue(0, packet.Player.GetCounter());
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_DECLINED_NAME);
stmt.AddValue(0, packet.Player.GetCounter());
for (byte i = 0; i < SharedConst.MaxDeclinedNameCases; i++)
stmt.AddValue(i + 1, packet.DeclinedNames.name[i]);
trans.Append(stmt);
DB.Characters.CommitTransaction(trans);
SendSetPlayerDeclinedNamesResult(DeclinedNameResult.Success, packet.Player);
}
[WorldPacketHandler(ClientOpcodes.AlterAppearance)]
void HandleAlterAppearance(AlterApperance packet)
{
if (!ValidateAppearance(_player.GetRace(), _player.GetClass(), (Gender)packet.NewSex, packet.Customizations))
return;
GameObject go = GetPlayer().FindNearestGameObjectOfType(GameObjectTypes.BarberChair, 5.0f);
if (!go)
{
SendPacket(new BarberShopResult(BarberShopResult.ResultEnum.NotOnChair));
return;
}
if (GetPlayer().GetStandState() != (UnitStandStateType)((int)UnitStandStateType.SitLowChair + go.GetGoInfo().BarberChair.chairheight))
{
SendPacket(new BarberShopResult(BarberShopResult.ResultEnum.NotOnChair));
return;
}
long cost = GetPlayer().GetBarberShopCost(packet.Customizations);
if (!GetPlayer().HasEnoughMoney(cost))
{
SendPacket(new BarberShopResult(BarberShopResult.ResultEnum.NoMoney));
return;
}
SendPacket(new BarberShopResult(BarberShopResult.ResultEnum.Success));
_player.ModifyMoney(-cost);
_player.UpdateCriteria(CriteriaTypes.GoldSpentAtBarber, (ulong)cost);
if (_player.GetNativeSex() != (Gender)packet.NewSex)
{
_player.SetNativeSex((Gender)packet.NewSex);
_player.InitDisplayIds();
_player.RestoreDisplayId(false);
}
_player.SetCustomizations(packet.Customizations);
_player.UpdateCriteria(CriteriaTypes.VisitBarberShop, 1);
_player.SetStandState(UnitStandStateType.Stand);
Global.CharacterCacheStorage.UpdateCharacterGender(_player.GetGUID(), packet.NewSex);
}
[WorldPacketHandler(ClientOpcodes.CharCustomize, Status = SessionStatus.Authed)]
void HandleCharCustomize(CharCustomize packet)
{
if (!_legitCharacters.Contains(packet.CustomizeInfo.CharGUID))
{
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to customise {2}, but it does not belong to their account!",
GetAccountId(), GetRemoteAddress(), packet.CustomizeInfo.CharGUID.ToString());
KickPlayer();
return;
}
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_CUSTOMIZE_INFO);
stmt.AddValue(0, packet.CustomizeInfo.CharGUID.GetCounter());
_queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt).WithCallback(HandleCharCustomizeCallback, packet.CustomizeInfo));
}
void HandleCharCustomizeCallback(CharCustomizeInfo customizeInfo, SQLResult result)
{
if (result.IsEmpty())
{
SendCharCustomize(ResponseCodes.CharCreateError, customizeInfo);
return;
}
string oldName = result.Read<string>(0);
Race plrRace = (Race)result.Read<byte>(1);
Class plrClass = (Class)result.Read<byte>(2);
Gender plrGender = (Gender)result.Read<byte>(3);
AtLoginFlags atLoginFlags = (AtLoginFlags)result.Read<ushort>(4);
if (!ValidateAppearance(plrRace, plrClass, plrGender, customizeInfo.Customizations))
{
SendCharCustomize(ResponseCodes.CharCreateError, customizeInfo);
return;
}
if (!atLoginFlags.HasAnyFlag(AtLoginFlags.Customize))
{
SendCharCustomize(ResponseCodes.CharCreateError, customizeInfo);
return;
}
// prevent character rename
if (WorldConfig.GetBoolValue(WorldCfg.PreventRenameCustomization) && (customizeInfo.CharName != oldName))
{
SendCharCustomize(ResponseCodes.CharNameFailure, customizeInfo);
return;
}
atLoginFlags &= ~AtLoginFlags.Customize;
// prevent character rename to invalid name
if (!ObjectManager.NormalizePlayerName(ref customizeInfo.CharName))
{
SendCharCustomize(ResponseCodes.CharNameNoName, customizeInfo);
return;
}
ResponseCodes res = ObjectManager.CheckPlayerName(customizeInfo.CharName, GetSessionDbcLocale(), true);
if (res != ResponseCodes.CharNameSuccess)
{
SendCharCustomize(res, customizeInfo);
return;
}
// check name limitations
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationReservedname) && Global.ObjectMgr.IsReservedName(customizeInfo.CharName))
{
SendCharCustomize(ResponseCodes.CharNameReserved, customizeInfo);
return;
}
// character with this name already exist
// @todo: make async
ObjectGuid newGuid = Global.CharacterCacheStorage.GetCharacterGuidByName(customizeInfo.CharName);
if (!newGuid.IsEmpty())
{
if (newGuid != customizeInfo.CharGUID)
{
SendCharCustomize(ResponseCodes.CharCreateNameInUse, customizeInfo);
return;
}
}
PreparedStatement stmt;
SQLTransaction trans = new SQLTransaction();
ulong lowGuid = customizeInfo.CharGUID.GetCounter();
// Customize
Player.SaveCustomizations(trans, lowGuid, customizeInfo.Customizations);
// Name Change and update atLogin flags
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_NAME_AT_LOGIN);
stmt.AddValue(0, customizeInfo.CharName);
stmt.AddValue(1, atLoginFlags);
stmt.AddValue(2, lowGuid);
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_DECLINED_NAME);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
}
DB.Characters.CommitTransaction(trans);
Global.CharacterCacheStorage.UpdateCharacterData(customizeInfo.CharGUID, customizeInfo.CharName, (byte)customizeInfo.SexID);
SendCharCustomize(ResponseCodes.Success, customizeInfo);
Log.outInfo(LogFilter.Player, "Account: {0} (IP: {1}), Character[{2}] ({3}) Customized to: {4}",
GetAccountId(), GetRemoteAddress(), oldName, customizeInfo.CharGUID.ToString(), customizeInfo.CharName);
}
[WorldPacketHandler(ClientOpcodes.SaveEquipmentSet)]
void HandleEquipmentSetSave(SaveEquipmentSet saveEquipmentSet)
{
if (saveEquipmentSet.Set.SetID >= ItemConst.MaxEquipmentSetIndex) // client set slots amount
return;
if (saveEquipmentSet.Set.Type > EquipmentSetInfo.EquipmentSetType.Transmog)
return;
for (byte i = 0; i < EquipmentSlot.End; ++i)
{
if (!Convert.ToBoolean(saveEquipmentSet.Set.IgnoreMask & (1 << i)))
{
if (saveEquipmentSet.Set.Type == EquipmentSetInfo.EquipmentSetType.Equipment)
{
saveEquipmentSet.Set.Appearances[i] = 0;
ObjectGuid itemGuid = saveEquipmentSet.Set.Pieces[i];
if (!itemGuid.IsEmpty())
{
Item item = _player.GetItemByPos(InventorySlots.Bag0, i);
// cheating check 1 (item equipped but sent empty guid)
if (!item)
return;
// cheating check 2 (sent guid does not match equipped item)
if (item.GetGUID() != itemGuid)
return;
}
else
saveEquipmentSet.Set.IgnoreMask |= 1u << i;
}
else
{
saveEquipmentSet.Set.Pieces[i].Clear();
if (saveEquipmentSet.Set.Appearances[i] != 0)
{
if (!CliDB.ItemModifiedAppearanceStorage.ContainsKey(saveEquipmentSet.Set.Appearances[i]))
return;
(bool hasAppearance, _) = GetCollectionMgr().HasItemAppearance((uint)saveEquipmentSet.Set.Appearances[i]);
if (!hasAppearance)
return;
}
else
saveEquipmentSet.Set.IgnoreMask |= 1u << i;
}
}
else
{
saveEquipmentSet.Set.Pieces[i].Clear();
saveEquipmentSet.Set.Appearances[i] = 0;
}
}
saveEquipmentSet.Set.IgnoreMask &= 0x7FFFF; // clear invalid bits (i > EQUIPMENT_SLOT_END)
if (saveEquipmentSet.Set.Type == EquipmentSetInfo.EquipmentSetType.Equipment)
{
saveEquipmentSet.Set.Enchants[0] = 0;
saveEquipmentSet.Set.Enchants[1] = 0;
}
else
{
var validateIllusion = new Func<uint, bool>(enchantId =>
{
SpellItemEnchantmentRecord illusion = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchantId);
if (illusion == null)
return false;
if (illusion.ItemVisual == 0 || !illusion.Flags.HasAnyFlag(EnchantmentSlotMask.Collectable))
return false;
PlayerConditionRecord condition = CliDB.PlayerConditionStorage.LookupByKey(illusion.TransmogUseConditionID);
if (condition != null)
if (!ConditionManager.IsPlayerMeetingCondition(_player, condition))
return false;
if (illusion.ScalingClassRestricted > 0 && illusion.ScalingClassRestricted != (byte)_player.GetClass())
return false;
return true;
});
if (saveEquipmentSet.Set.Enchants[0] != 0 && !validateIllusion((uint)saveEquipmentSet.Set.Enchants[0]))
return;
if (saveEquipmentSet.Set.Enchants[1] != 0 && !validateIllusion((uint)saveEquipmentSet.Set.Enchants[1]))
return;
}
GetPlayer().SetEquipmentSet(saveEquipmentSet.Set);
}
[WorldPacketHandler(ClientOpcodes.DeleteEquipmentSet)]
void HandleDeleteEquipmentSet(DeleteEquipmentSet packet)
{
GetPlayer().DeleteEquipmentSet(packet.ID);
}
[WorldPacketHandler(ClientOpcodes.UseEquipmentSet)]
void HandleUseEquipmentSet(UseEquipmentSet useEquipmentSet)
{
ObjectGuid ignoredItemGuid = new ObjectGuid(0x0C00040000000000, 0xFFFFFFFFFFFFFFFF);
for (byte i = 0; i < EquipmentSlot.End; ++i)
{
Log.outDebug(LogFilter.Player, "{0}: ContainerSlot: {1}, Slot: {2}", useEquipmentSet.Items[i].Item.ToString(), useEquipmentSet.Items[i].ContainerSlot, useEquipmentSet.Items[i].Slot);
// check if item slot is set to "ignored" (raw value == 1), must not be unequipped then
if (useEquipmentSet.Items[i].Item == ignoredItemGuid)
continue;
// Only equip weapons in combat
if (GetPlayer().IsInCombat() && i != EquipmentSlot.MainHand && i != EquipmentSlot.OffHand)
continue;
Item item = GetPlayer().GetItemByGuid(useEquipmentSet.Items[i].Item);
ushort dstPos = (ushort)(i | (InventorySlots.Bag0 << 8));
if (!item)
{
Item uItem = GetPlayer().GetItemByPos(InventorySlots.Bag0, i);
if (!uItem)
continue;
List<ItemPosCount> itemPosCount = new List<ItemPosCount>();
InventoryResult inventoryResult = GetPlayer().CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, itemPosCount, uItem, false);
if (inventoryResult == InventoryResult.Ok)
{
if (_player.CanEquipItem(ItemConst.NullSlot, out dstPos, uItem, false) != InventoryResult.Ok)
continue;
GetPlayer().RemoveItem(InventorySlots.Bag0, i, true);
GetPlayer().StoreItem(itemPosCount, uItem, true);
}
else
GetPlayer().SendEquipError(inventoryResult, uItem);
continue;
}
if (item.GetPos() == dstPos)
continue;
if (_player.CanUnequipItem(dstPos, true) != InventoryResult.Ok)
continue;
GetPlayer().SwapItem(item.GetPos(), dstPos);
}
UseEquipmentSetResult result = new UseEquipmentSetResult();
result.GUID = useEquipmentSet.GUID;
result.Reason = 0; // 4 - equipment swap failed - inventory is full
SendPacket(result);
}
[WorldPacketHandler(ClientOpcodes.CharRaceOrFactionChange, Status = SessionStatus.Authed)]
void HandleCharRaceOrFactionChange(CharRaceOrFactionChange packet)
{
if (!_legitCharacters.Contains(packet.RaceOrFactionChangeInfo.Guid))
{
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to factionchange character {2}, but it does not belong to their account!",
GetAccountId(), GetRemoteAddress(), packet.RaceOrFactionChangeInfo.Guid.ToString());
KickPlayer();
return;
}
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS);
stmt.AddValue(0, packet.RaceOrFactionChangeInfo.Guid.GetCounter());
_queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt).WithCallback(HandleCharRaceOrFactionChangeCallback, packet.RaceOrFactionChangeInfo));
}
void HandleCharRaceOrFactionChangeCallback(CharRaceOrFactionChangeInfo factionChangeInfo, SQLResult result)
{
if (result.IsEmpty())
{
SendCharFactionChange(ResponseCodes.CharCreateError, factionChangeInfo);
return;
}
// get the players old (at this moment current) race
CharacterCacheEntry characterInfo = Global.CharacterCacheStorage.GetCharacterCacheByGuid(factionChangeInfo.Guid);
if (characterInfo == null)
{
SendCharFactionChange(ResponseCodes.CharCreateError, factionChangeInfo);
return;
}
string oldName = characterInfo.Name;
Race oldRace = characterInfo.RaceId;
Class playerClass = characterInfo.ClassId;
byte level = characterInfo.Level;
if (Global.ObjectMgr.GetPlayerInfo(factionChangeInfo.RaceID, playerClass) == null)
{
SendCharFactionChange(ResponseCodes.CharCreateError, factionChangeInfo);
return;
}
AtLoginFlags atLoginFlags = (AtLoginFlags)result.Read<ushort>(0);
string knownTitlesStr = result.Read<string>(1);
AtLoginFlags usedLoginFlag = (factionChangeInfo.FactionChange ? AtLoginFlags.ChangeFaction : AtLoginFlags.ChangeRace);
if (!atLoginFlags.HasAnyFlag(usedLoginFlag))
{
SendCharFactionChange(ResponseCodes.CharCreateError, factionChangeInfo);
return;
}
uint newTeamId = Player.TeamIdForRace(factionChangeInfo.RaceID);
if (newTeamId == TeamId.Neutral)
{
SendCharFactionChange(ResponseCodes.CharCreateRestrictedRaceclass, factionChangeInfo);
return;
}
if (factionChangeInfo.FactionChange == (Player.TeamIdForRace(oldRace) == newTeamId))
{
SendCharFactionChange(factionChangeInfo.FactionChange ? ResponseCodes.CharCreateCharacterSwapFaction : ResponseCodes.CharCreateCharacterRaceOnly, factionChangeInfo);
return;
}
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationRacemask))
{
ulong raceMaskDisabled = WorldConfig.GetUInt64Value(WorldCfg.CharacterCreatingDisabledRacemask);
if (Convert.ToBoolean(1ul << ((int)factionChangeInfo.RaceID - 1) & raceMaskDisabled))
{
SendCharFactionChange(ResponseCodes.CharCreateError, factionChangeInfo);
return;
}
}
// prevent character rename
if (WorldConfig.GetBoolValue(WorldCfg.PreventRenameCustomization) && (factionChangeInfo.Name != oldName))
{
SendCharFactionChange(ResponseCodes.CharNameFailure, factionChangeInfo);
return;
}
// prevent character rename to invalid name
if (!ObjectManager.NormalizePlayerName(ref factionChangeInfo.Name))
{
SendCharFactionChange(ResponseCodes.CharNameNoName, factionChangeInfo);
return;
}
ResponseCodes res = ObjectManager.CheckPlayerName(factionChangeInfo.Name, GetSessionDbcLocale(), true);
if (res != ResponseCodes.CharNameSuccess)
{
SendCharFactionChange(res, factionChangeInfo);
return;
}
// check name limitations
if (!HasPermission(RBACPermissions.SkipCheckCharacterCreationReservedname) && Global.ObjectMgr.IsReservedName(factionChangeInfo.Name))
{
SendCharFactionChange(ResponseCodes.CharNameReserved, factionChangeInfo);
return;
}
// character with this name already exist
ObjectGuid newGuid = Global.CharacterCacheStorage.GetCharacterGuidByName(factionChangeInfo.Name);
if (!newGuid.IsEmpty())
{
if (newGuid != factionChangeInfo.Guid)
{
SendCharFactionChange(ResponseCodes.CharCreateNameInUse, factionChangeInfo);
return;
}
}
if (Global.ArenaTeamMgr.GetArenaTeamByCaptain(factionChangeInfo.Guid) != null)
{
SendCharFactionChange(ResponseCodes.CharCreateCharacterArenaLeader, factionChangeInfo);
return;
}
// All checks are fine, deal with race change now
ulong lowGuid = factionChangeInfo.Guid.GetCounter();
PreparedStatement stmt;
SQLTransaction trans = new SQLTransaction();
// resurrect the character in case he's dead
Player.OfflineResurrect(factionChangeInfo.Guid, trans);
// Name Change and update atLogin flags
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_NAME_AT_LOGIN);
stmt.AddValue(0, factionChangeInfo.Name);
stmt.AddValue(1, (ushort)((atLoginFlags | AtLoginFlags.Resurrect) & ~usedLoginFlag));
stmt.AddValue(2, lowGuid);
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_DECLINED_NAME);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
}
// Customize
Player.SaveCustomizations(trans, lowGuid, factionChangeInfo.Customizations);
// Race Change
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_RACE);
stmt.AddValue(0, factionChangeInfo.RaceID);
stmt.AddValue(1, lowGuid);
trans.Append(stmt);
}
Global.CharacterCacheStorage.UpdateCharacterData(factionChangeInfo.Guid, factionChangeInfo.Name, (byte)factionChangeInfo.SexID, (byte)factionChangeInfo.RaceID);
if (oldRace != factionChangeInfo.RaceID)
{
// Switch Languages
// delete all languages first
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_SKILL_LANGUAGES);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
// Now add them back
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_SKILL_LANGUAGE);
stmt.AddValue(0, lowGuid);
// Faction specific languages
if (newTeamId == TeamId.Horde)
stmt.AddValue(1, 109);
else
stmt.AddValue(1, 98);
trans.Append(stmt);
// Race specific languages
if (factionChangeInfo.RaceID != Race.Orc && factionChangeInfo.RaceID != Race.Human)
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_SKILL_LANGUAGE);
stmt.AddValue(0, lowGuid);
switch (factionChangeInfo.RaceID)
{
case Race.Dwarf:
stmt.AddValue(1, 111);
break;
case Race.Draenei:
case Race.LightforgedDraenei:
stmt.AddValue(1, 759);
break;
case Race.Gnome:
stmt.AddValue(1, 313);
break;
case Race.NightElf:
stmt.AddValue(1, 113);
break;
case Race.Worgen:
stmt.AddValue(1, 791);
break;
case Race.Undead:
stmt.AddValue(1, 673);
break;
case Race.Tauren:
case Race.HighmountainTauren:
stmt.AddValue(1, 115);
break;
case Race.Troll:
stmt.AddValue(1, 315);
break;
case Race.BloodElf:
case Race.VoidElf:
stmt.AddValue(1, 137);
break;
case Race.Goblin:
stmt.AddValue(1, 792);
break;
case Race.Nightborne:
stmt.AddValue(1, 2464);
break;
default:
Log.outError(LogFilter.Player, $"Could not find language data for race ({factionChangeInfo.RaceID}).");
SendCharFactionChange(ResponseCodes.CharCreateError, factionChangeInfo);
return;
}
trans.Append(stmt);
}
// Team Conversation
if (factionChangeInfo.FactionChange)
{
// Delete all Flypaths
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_TAXI_PATH);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
if (level > 7)
{
// Update Taxi path
// this doesn't seem to be 100% blizzlike... but it can't really be helped.
string taximaskstream = "";
var factionMask = newTeamId == TeamId.Horde ? CliDB.HordeTaxiNodesMask : CliDB.AllianceTaxiNodesMask;
for (int i = 0; i < PlayerConst.TaxiMaskSize; ++i)
{
// i = (315 - 1) / 8 = 39
// m = 1 << ((315 - 1) % 8) = 4
int deathKnightExtraNode = playerClass != Class.Deathknight || i != 39 ? 0 : 4;
taximaskstream += (uint)(factionMask[i] | deathKnightExtraNode) + ' ';
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_TAXIMASK);
stmt.AddValue(0, taximaskstream);
stmt.AddValue(1, lowGuid);
trans.Append(stmt);
}
if (!WorldConfig.GetBoolValue(WorldCfg.AllowTwoSideInteractionGuild))
{
// Reset guild
Guild guild = Global.GuildMgr.GetGuildById(characterInfo.GuildId);
if (guild != null)
guild.DeleteMember(trans, factionChangeInfo.Guid, false, false, true);
Player.LeaveAllArenaTeams(factionChangeInfo.Guid);
}
if (!HasPermission(RBACPermissions.TwoSideAddFriend))
{
// Delete Friend List
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_SOCIAL_BY_GUID);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_SOCIAL_BY_FRIEND);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
}
// Reset homebind and position
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_PLAYER_HOMEBIND);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_PLAYER_HOMEBIND);
stmt.AddValue(0, lowGuid);
WorldLocation loc;
ushort zoneId = 0;
if (newTeamId == TeamId.Alliance)
{
loc = new WorldLocation(0, -8867.68f, 673.373f, 97.9034f, 0.0f);
zoneId = 1519;
}
else
{
loc = new WorldLocation(1, 1633.33f, -4439.11f, 15.7588f, 0.0f);
zoneId = 1637;
}
stmt.AddValue(1, loc.GetMapId());
stmt.AddValue(2, zoneId);
stmt.AddValue(3, loc.GetPositionX());
stmt.AddValue(4, loc.GetPositionY());
stmt.AddValue(5, loc.GetPositionZ());
trans.Append(stmt);
Player.SavePositionInDB(loc, zoneId, factionChangeInfo.Guid, trans);
// Achievement conversion
foreach (var it in Global.ObjectMgr.FactionChangeAchievements)
{
uint achiev_alliance = it.Key;
uint achiev_horde = it.Value;
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT);
stmt.AddValue(0, (ushort)(newTeamId == TeamId.Alliance ? achiev_alliance : achiev_horde));
stmt.AddValue(1, lowGuid);
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_ACHIEVEMENT);
stmt.AddValue(0, (ushort)(newTeamId == TeamId.Alliance ? achiev_alliance : achiev_horde));
stmt.AddValue(1, (ushort)(newTeamId == TeamId.Alliance ? achiev_horde : achiev_alliance));
stmt.AddValue(2, lowGuid);
trans.Append(stmt);
}
// Item conversion
foreach (var it in Global.ObjectMgr.FactionChangeItems)
{
uint item_alliance = it.Key;
uint item_horde = it.Value;
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_INVENTORY_FACTION_CHANGE);
stmt.AddValue(0, (newTeamId == TeamId.Alliance ? item_alliance : item_horde));
stmt.AddValue(1, (newTeamId == TeamId.Alliance ? item_horde : item_alliance));
stmt.AddValue(2, lowGuid);
trans.Append(stmt);
}
// Delete all current quests
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
// Quest conversion
foreach (var it in Global.ObjectMgr.FactionChangeQuests)
{
uint quest_alliance = it.Key;
uint quest_horde = it.Value;
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST);
stmt.AddValue(0, lowGuid);
stmt.AddValue(1, (newTeamId == TeamId.Alliance ? quest_alliance : quest_horde));
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_QUESTSTATUS_REWARDED_FACTION_CHANGE);
stmt.AddValue(0, (newTeamId == TeamId.Alliance ? quest_alliance : quest_horde));
stmt.AddValue(1, (newTeamId == TeamId.Alliance ? quest_horde : quest_alliance));
stmt.AddValue(2, lowGuid);
trans.Append(stmt);
}
// Mark all rewarded quests as "active" (will count for completed quests achievements)
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
// Disable all old-faction specific quests
{
var questTemplates = Global.ObjectMgr.GetQuestTemplates();
foreach (Quest quest in questTemplates.Values)
{
long newRaceMask = (long)(newTeamId == TeamId.Alliance ? Race.RaceMaskAlliance : Race.RaceMaskHorde);
if (quest.AllowableRaces != -1 && !Convert.ToBoolean(quest.AllowableRaces & newRaceMask))
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST);
stmt.AddValue(0, lowGuid);
stmt.AddValue(1, quest.Id);
trans.Append(stmt);
}
}
}
// Spell conversion
foreach (var it in Global.ObjectMgr.FactionChangeSpells)
{
uint spell_alliance = it.Key;
uint spell_horde = it.Value;
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_SPELL_BY_SPELL);
stmt.AddValue(0, (newTeamId == TeamId.Alliance ? spell_alliance : spell_horde));
stmt.AddValue(1, lowGuid);
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_SPELL_FACTION_CHANGE);
stmt.AddValue(0, (newTeamId == TeamId.Alliance ? spell_alliance : spell_horde));
stmt.AddValue(1, (newTeamId == TeamId.Alliance ? spell_horde : spell_alliance));
stmt.AddValue(2, lowGuid);
trans.Append(stmt);
}
// Reputation conversion
foreach (var it in Global.ObjectMgr.FactionChangeReputation)
{
uint reputation_alliance = it.Key;
uint reputation_horde = it.Value;
uint newReputation = (newTeamId == TeamId.Alliance) ? reputation_alliance : reputation_horde;
uint oldReputation = (newTeamId == TeamId.Alliance) ? reputation_horde : reputation_alliance;
// select old standing set in db
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_REP_BY_FACTION);
stmt.AddValue(0, oldReputation);
stmt.AddValue(1, lowGuid);
result = DB.Characters.Query(stmt);
if (!result.IsEmpty())
{
int oldDBRep = result.Read<int>(0);
FactionRecord factionEntry = CliDB.FactionStorage.LookupByKey(oldReputation);
// old base reputation
int oldBaseRep = ReputationMgr.GetBaseReputationOf(factionEntry, oldRace, playerClass);
// new base reputation
int newBaseRep = ReputationMgr.GetBaseReputationOf(CliDB.FactionStorage.LookupByKey(newReputation), factionChangeInfo.RaceID, playerClass);
// final reputation shouldnt change
int FinalRep = oldDBRep + oldBaseRep;
int newDBRep = FinalRep - newBaseRep;
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_REP_BY_FACTION);
stmt.AddValue(0, newReputation);
stmt.AddValue(1, lowGuid);
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_REP_FACTION_CHANGE);
stmt.AddValue(0, (ushort)newReputation);
stmt.AddValue(1, newDBRep);
stmt.AddValue(2, (ushort)oldReputation);
stmt.AddValue(3, lowGuid);
trans.Append(stmt);
}
}
// Title conversion
if (!string.IsNullOrEmpty(knownTitlesStr))
{
List<uint> knownTitles = new List<uint>();
var tokens = new StringArray(knownTitlesStr, ' ');
for (int index = 0; index < tokens.Length; ++index)
{
if (uint.TryParse(tokens[index], out uint id))
knownTitles.Add(id);
}
foreach (var it in Global.ObjectMgr.FactionChangeTitles)
{
uint title_alliance = it.Key;
uint title_horde = it.Value;
CharTitlesRecord atitleInfo = CliDB.CharTitlesStorage.LookupByKey(title_alliance);
CharTitlesRecord htitleInfo = CliDB.CharTitlesStorage.LookupByKey(title_horde);
// new team
if (newTeamId == TeamId.Alliance)
{
uint maskID = htitleInfo.MaskID;
int index = (int)maskID / 32;
if (index >= knownTitles.Count)
continue;
uint old_flag = (uint)(1 << (int)(maskID % 32));
uint new_flag = (uint)(1 << (atitleInfo.MaskID % 32));
if (Convert.ToBoolean(knownTitles[index] & old_flag))
{
knownTitles[index] &= ~old_flag;
// use index of the new title
knownTitles[atitleInfo.MaskID / 32] |= new_flag;
}
}
else
{
uint maskID = atitleInfo.MaskID;
int index = (int)maskID / 32;
if (index >= knownTitles.Count)
continue;
uint old_flag = (uint)(1 << (int)(maskID % 32));
uint new_flag = (uint)(1 << (htitleInfo.MaskID % 32));
if (Convert.ToBoolean(knownTitles[index] & old_flag))
{
knownTitles[index] &= ~old_flag;
// use index of the new title
knownTitles[htitleInfo.MaskID / 32] |= new_flag;
}
}
string ss = "";
for (int index = 0; index < knownTitles.Count; ++index)
ss += knownTitles[index] + ' ';
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_TITLES_FACTION_CHANGE);
stmt.AddValue(0, ss);
stmt.AddValue(1, lowGuid);
trans.Append(stmt);
// unset any currently chosen title
stmt = DB.Characters.GetPreparedStatement(CharStatements.RES_CHAR_TITLES_FACTION_CHANGE);
stmt.AddValue(0, lowGuid);
trans.Append(stmt);
}
}
}
}
DB.Characters.CommitTransaction(trans);
Log.outDebug(LogFilter.Player, "{0} (IP: {1}) changed race from {2} to {3}", GetPlayerInfo(), GetRemoteAddress(), oldRace, factionChangeInfo.RaceID);
SendCharFactionChange(ResponseCodes.Success, factionChangeInfo);
}
[WorldPacketHandler(ClientOpcodes.OpeningCinematic)]
void HandleOpeningCinematic(OpeningCinematic packet)
{
// Only players that has not yet gained any experience can use this
if (GetPlayer().m_activePlayerData.XP != 0)
return;
ChrClassesRecord classEntry = CliDB.ChrClassesStorage.LookupByKey(GetPlayer().GetClass());
if (classEntry != null)
{
ChrRacesRecord raceEntry = CliDB.ChrRacesStorage.LookupByKey(GetPlayer().GetRace());
if (classEntry.CinematicSequenceID != 0)
GetPlayer().SendCinematicStart(classEntry.CinematicSequenceID);
else if (raceEntry != null)
GetPlayer().SendCinematicStart(raceEntry.CinematicSequenceID);
}
}
[WorldPacketHandler(ClientOpcodes.GetUndeleteCharacterCooldownStatus, Status = SessionStatus.Authed)]
void HandleGetUndeleteCooldownStatus(GetUndeleteCharacterCooldownStatus getCooldown)
{
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_LAST_CHAR_UNDELETE);
stmt.AddValue(0, GetBattlenetAccountId());
_queryProcessor.AddCallback(DB.Login.AsyncQuery(stmt).WithCallback(HandleUndeleteCooldownStatusCallback));
}
void HandleUndeleteCooldownStatusCallback(SQLResult result)
{
uint cooldown = 0;
uint maxCooldown = WorldConfig.GetUIntValue(WorldCfg.FeatureSystemCharacterUndeleteCooldown);
if (!result.IsEmpty())
{
uint lastUndelete = result.Read<uint>(0);
uint now = (uint)Time.UnixTime;
if (lastUndelete + maxCooldown > now)
cooldown = Math.Max(0, lastUndelete + maxCooldown - now);
}
SendUndeleteCooldownStatusResponse(cooldown, maxCooldown);
}
[WorldPacketHandler(ClientOpcodes.UndeleteCharacter, Status = SessionStatus.Authed)]
void HandleCharUndelete(UndeleteCharacter undeleteCharacter)
{
if (!WorldConfig.GetBoolValue(WorldCfg.FeatureSystemCharacterUndeleteEnabled))
{
SendUndeleteCharacterResponse(CharacterUndeleteResult.Disabled, undeleteCharacter.UndeleteInfo);
return;
}
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_LAST_CHAR_UNDELETE);
stmt.AddValue(0, GetBattlenetAccountId());
CharacterUndeleteInfo undeleteInfo = undeleteCharacter.UndeleteInfo;
_queryProcessor.AddCallback(DB.Login.AsyncQuery(stmt).WithChainingCallback((queryCallback, result) =>
{
if (!result.IsEmpty())
{
uint lastUndelete = result.Read<uint>(0);
uint maxCooldown = WorldConfig.GetUIntValue(WorldCfg.FeatureSystemCharacterUndeleteCooldown);
if (lastUndelete != 0 && (lastUndelete + maxCooldown > Time.UnixTime))
{
SendUndeleteCharacterResponse(CharacterUndeleteResult.Cooldown, undeleteInfo);
return;
}
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_DEL_INFO_BY_GUID);
stmt.AddValue(0, undeleteInfo.CharacterGuid.GetCounter());
queryCallback.SetNextQuery(DB.Characters.AsyncQuery(stmt));
}).WithChainingCallback((queryCallback, result) =>
{
if (result.IsEmpty())
{
SendUndeleteCharacterResponse(CharacterUndeleteResult.CharCreate, undeleteInfo);
return;
}
undeleteInfo.Name = result.Read<string>(1);
uint account = result.Read<uint>(2);
if (account != GetAccountId())
{
SendUndeleteCharacterResponse(CharacterUndeleteResult.Unknown, undeleteInfo);
return;
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHECK_NAME);
stmt.AddValue(0, undeleteInfo.Name);
queryCallback.SetNextQuery(DB.Characters.AsyncQuery(stmt));
}).WithChainingCallback((queryCallback, result) =>
{
if (!result.IsEmpty())
{
SendUndeleteCharacterResponse(CharacterUndeleteResult.NameTakenByThisAccount, undeleteInfo);
return;
}
// @todo: add more safety checks
// * max char count per account
// * max death knight count
// * max demon hunter count
// * team violation
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_SUM_CHARS);
stmt.AddValue(0, GetAccountId());
queryCallback.SetNextQuery(DB.Characters.AsyncQuery(stmt));
}).WithCallback(result =>
{
if (!result.IsEmpty())
{
if (result.Read<ulong>(0) >= WorldConfig.GetUIntValue(WorldCfg.CharactersPerRealm)) // SQL's COUNT() returns uint64 but it will always be less than uint8.Max
{
SendUndeleteCharacterResponse(CharacterUndeleteResult.CharCreate, undeleteInfo);
return;
}
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_RESTORE_DELETE_INFO);
stmt.AddValue(0, undeleteInfo.Name);
stmt.AddValue(1, GetAccountId());
stmt.AddValue(2, undeleteInfo.CharacterGuid.GetCounter());
DB.Characters.Execute(stmt);
stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_LAST_CHAR_UNDELETE);
stmt.AddValue(0, GetBattlenetAccountId());
DB.Login.Execute(stmt);
Global.CharacterCacheStorage.UpdateCharacterInfoDeleted(undeleteInfo.CharacterGuid, false, undeleteInfo.Name);
SendUndeleteCharacterResponse(CharacterUndeleteResult.Ok, undeleteInfo);
}));
}
[WorldPacketHandler(ClientOpcodes.RepopRequest)]
void HandleRepopRequest(RepopRequest packet)
{
if (GetPlayer().IsAlive() || GetPlayer().HasPlayerFlag(PlayerFlags.Ghost))
return;
if (GetPlayer().HasAuraType(AuraType.PreventResurrection))
return; // silently return, client should display the error by itself
// the world update order is sessions, players, creatures
// the netcode runs in parallel with all of these
// creatures can kill players
// so if the server is lagging enough the player can
// release spirit after he's killed but before he is updated
if (GetPlayer().GetDeathState() == DeathState.JustDied)
{
Log.outDebug(LogFilter.Network, "HandleRepopRequestOpcode: got request after player {0} ({1}) was killed and before he was updated",
GetPlayer().GetName(), GetPlayer().GetGUID().ToString());
GetPlayer().KillPlayer();
}
//this is spirit release confirm?
GetPlayer().RemovePet(null, PetSaveMode.NotInSlot, true);
GetPlayer().BuildPlayerRepop();
GetPlayer().RepopAtGraveyard();
}
[WorldPacketHandler(ClientOpcodes.ClientPortGraveyard)]
void HandlePortGraveyard(PortGraveyard packet)
{
if (GetPlayer().IsAlive() || !GetPlayer().HasPlayerFlag(PlayerFlags.Ghost))
return;
GetPlayer().RepopAtGraveyard();
}
[WorldPacketHandler(ClientOpcodes.RequestCemeteryList, Processing = PacketProcessing.Inplace)]
void HandleRequestCemeteryList(RequestCemeteryList requestCemeteryList)
{
uint zoneId = GetPlayer().GetZoneId();
uint team = (uint)GetPlayer().GetTeam();
List<uint> graveyardIds = new List<uint>();
var range = Global.ObjectMgr.GraveYardStorage.LookupByKey(zoneId);
for (uint i = 0; i < range.Count && graveyardIds.Count < 16; ++i) // client max
{
var gYard = range[(int)i];
if (gYard.team == 0 || gYard.team == team)
graveyardIds.Add(i);
}
if (graveyardIds.Empty())
{
Log.outDebug(LogFilter.Network, "No graveyards found for zone {0} for player {1} (team {2}) in CMSG_REQUEST_CEMETERY_LIST",
zoneId, m_GUIDLow, team);
return;
}
RequestCemeteryListResponse packet = new RequestCemeteryListResponse();
packet.IsGossipTriggered = false;
foreach (uint id in graveyardIds)
packet.CemeteryID.Add(id);
SendPacket(packet);
}
[WorldPacketHandler(ClientOpcodes.ReclaimCorpse)]
void HandleReclaimCorpse(ReclaimCorpse packet)
{
if (GetPlayer().IsAlive())
return;
// do not allow corpse reclaim in arena
if (GetPlayer().InArena())
return;
// body not released yet
if (!GetPlayer().HasPlayerFlag(PlayerFlags.Ghost))
return;
Corpse corpse = GetPlayer().GetCorpse();
if (!corpse)
return;
// prevent resurrect before 30-sec delay after body release not finished
if ((corpse.GetGhostTime() + GetPlayer().GetCorpseReclaimDelay(corpse.GetCorpseType() == CorpseType.ResurrectablePVP)) > Time.UnixTime)
return;
if (!corpse.IsWithinDistInMap(GetPlayer(), 39, true))
return;
// resurrect
GetPlayer().ResurrectPlayer(GetPlayer().InBattleground() ? 1.0f : 0.5f);
// spawn bones
GetPlayer().SpawnCorpseBones();
}
[WorldPacketHandler(ClientOpcodes.ResurrectResponse)]
void HandleResurrectResponse(ResurrectResponse packet)
{
if (GetPlayer().IsAlive())
return;
if (packet.Response != 0) // Accept = 0 Decline = 1 Timeout = 2
{
GetPlayer().ClearResurrectRequestData(); // reject
return;
}
if (!GetPlayer().IsRessurectRequestedBy(packet.Resurrecter))
return;
Player ressPlayer = Global.ObjAccessor.GetPlayer(GetPlayer(), packet.Resurrecter);
if (ressPlayer)
{
InstanceScript instance = ressPlayer.GetInstanceScript();
if (instance != null)
{
if (instance.IsEncounterInProgress())
{
if (instance.GetCombatResurrectionCharges() == 0)
return;
else
instance.UseCombatResurrection();
}
}
}
GetPlayer().ResurrectUsingRequestData();
}
[WorldPacketHandler(ClientOpcodes.StandStateChange)]
void HandleStandStateChange(StandStateChange packet)
{
GetPlayer().SetStandState(packet.StandState);
}
void SendCharCreate(ResponseCodes result, ObjectGuid guid = default)
{
CreateChar response = new CreateChar();
response.Code = result;
response.Guid = guid;
SendPacket(response);
}
void SendCharDelete(ResponseCodes result)
{
DeleteChar response = new DeleteChar();
response.Code = result;
SendPacket(response);
}
void SendCharRename(ResponseCodes result, CharacterRenameInfo renameInfo)
{
CharacterRenameResult packet = new CharacterRenameResult();
packet.Result = result;
packet.Name = renameInfo.NewName;
if (result == ResponseCodes.Success)
packet.Guid.Set(renameInfo.Guid);
SendPacket(packet);
}
void SendCharCustomize(ResponseCodes result, CharCustomizeInfo customizeInfo)
{
if (result == ResponseCodes.Success)
{
CharCustomizeSuccess response = new CharCustomizeSuccess(customizeInfo);
SendPacket(response);
}
else
{
CharCustomizeFailure failed = new CharCustomizeFailure();
failed.Result = (byte)result;
failed.CharGUID = customizeInfo.CharGUID;
SendPacket(failed);
}
}
void SendCharFactionChange(ResponseCodes result, CharRaceOrFactionChangeInfo factionChangeInfo)
{
CharFactionChangeResult packet = new CharFactionChangeResult();
packet.Result = result;
packet.Guid = factionChangeInfo.Guid;
if (result == ResponseCodes.Success)
{
packet.Display.HasValue = true;
packet.Display.Value.Name = factionChangeInfo.Name;
packet.Display.Value.SexID = (byte)factionChangeInfo.SexID;
packet.Display.Value.Customizations = factionChangeInfo.Customizations;
packet.Display.Value.RaceID = (byte)factionChangeInfo.RaceID;
}
SendPacket(packet);
}
void SendSetPlayerDeclinedNamesResult(DeclinedNameResult result, ObjectGuid guid)
{
SetPlayerDeclinedNamesResult packet = new SetPlayerDeclinedNamesResult();
packet.ResultCode = result;
packet.Player = guid;
SendPacket(packet);
}
void SendUndeleteCooldownStatusResponse(uint currentCooldown, uint maxCooldown)
{
UndeleteCooldownStatusResponse response = new UndeleteCooldownStatusResponse();
response.OnCooldown = (currentCooldown > 0);
response.MaxCooldown = maxCooldown;
response.CurrentCooldown = currentCooldown;
SendPacket(response);
}
void SendUndeleteCharacterResponse(CharacterUndeleteResult result, CharacterUndeleteInfo undeleteInfo)
{
UndeleteCharacterResponse response = new UndeleteCharacterResponse();
response.UndeleteInfo = undeleteInfo;
response.Result = result;
SendPacket(response);
}
}
public class LoginQueryHolder : SQLQueryHolder<PlayerLoginQueryLoad>
{
public LoginQueryHolder(uint accountId, ObjectGuid guid)
{
m_accountId = accountId;
m_guid = guid;
}
public void Initialize()
{
ulong lowGuid = m_guid.GetCounter();
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.From, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_CUSTOMIZATIONS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Customizations, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_GROUP_MEMBER);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Group, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_INSTANCE);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.BoundInstances, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_AURAS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Auras, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_AURA_EFFECTS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.AuraEffects, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_SPELL);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Spells, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.QuestStatus, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.QuestStatusObjectives, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.QuestStatusObjectivesCriteria, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.QuestStatusObjectivesCriteriaProgress, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_DAILY);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.DailyQuestStatus, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_WEEKLY);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.WeeklyQuestStatus, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_MONTHLY);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.MonthlyQuestStatus, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_SEASONAL);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.SeasonalQuestStatus, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_REPUTATION);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Reputation, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_INVENTORY);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Inventory, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ITEM_INSTANCE_ARTIFACT);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Artifacts, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ITEM_INSTANCE_AZERITE);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Azerite, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.AzeriteMilestonePowers, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.AzeriteUnlockedEssences, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ITEM_INSTANCE_AZERITE_EMPOWERED);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.AzeriteEmpowered, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_VOID_STORAGE);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.VoidStorage, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_ACTIONS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Actions, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_MAILCOUNT);
stmt.AddValue(0, lowGuid);
stmt.AddValue(1, Time.UnixTime);
SetQuery(PlayerLoginQueryLoad.MailCount, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_MAILDATE);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.MailDate, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_SOCIALLIST);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.SocialList, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_HOMEBIND);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.HomeBind, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_SPELLCOOLDOWNS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.SpellCooldowns, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_SPELL_CHARGES);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.SpellCharges, stmt);
if (WorldConfig.GetBoolValue(WorldCfg.DeclinedNamesUsed))
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_DECLINEDNAMES);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.DeclinedNames, stmt);
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_GUILD_MEMBER);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Guild, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_ARENAINFO);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.ArenaInfo, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_ACHIEVEMENTS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Achievements, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_CRITERIAPROGRESS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.CriteriaProgress, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_EQUIPMENTSETS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.EquipmentSets, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_TRANSMOG_OUTFITS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.TransmogOutfits, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_CUF_PROFILES);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.CufProfiles, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_BGDATA);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.BgData, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GLYPHS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Glyphs, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_TALENTS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Talents, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_PVP_TALENTS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.PvpTalents, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_PLAYER_ACCOUNT_DATA);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.AccountData, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_SKILLS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Skills, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_RANDOMBG);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.RandomBg, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_BANNED);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Banned, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUSREW);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.QuestStatusRew, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ACCOUNT_INSTANCELOCKTIMES);
stmt.AddValue(0, m_accountId);
SetQuery(PlayerLoginQueryLoad.InstanceLockTimes, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_PLAYER_CURRENCY);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Currency, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CORPSE_LOCATION);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.CorpseLocation, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GARRISON);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Garrison, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GARRISON_BLUEPRINTS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.GarrisonBlueprints, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GARRISON_BUILDINGS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.GarrisonBuildings, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GARRISON_FOLLOWERS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.GarrisonFollowers, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.GarrisonFollowerAbilities, stmt);
}
public ObjectGuid GetGuid() { return m_guid; }
uint GetAccountId() { return m_accountId; }
uint m_accountId;
ObjectGuid m_guid;
}
class EnumCharactersQueryHolder : SQLQueryHolder<EnumCharacterQueryLoad>
{
public bool Initialize(uint accountId, bool withDeclinedNames, bool isDeletedCharacters)
{
_isDeletedCharacters = isDeletedCharacters;
CharStatements[][] statements =
{
new [] { CharStatements.SEL_ENUM, CharStatements.SEL_ENUM_DECLINED_NAME, CharStatements.SEL_ENUM_CUSTOMIZATIONS },
new [] { CharStatements.SEL_UNDELETE_ENUM, CharStatements.SEL_UNDELETE_ENUM_DECLINED_NAME, CharStatements.SEL_UNDELETE_ENUM_CUSTOMIZATIONS }
};
bool result = true;
PreparedStatement stmt = DB.Characters.GetPreparedStatement(statements[isDeletedCharacters ? 1 : 0][withDeclinedNames ? 1 : 0]);
stmt.AddValue(0, (byte)PetSaveMode.AsCurrent);
stmt.AddValue(1, accountId);
SetQuery(EnumCharacterQueryLoad.Characters, stmt);
stmt = DB.Characters.GetPreparedStatement(statements[isDeletedCharacters ? 1 : 0][2]);
stmt.AddValue(0, accountId);
SetQuery(EnumCharacterQueryLoad.Customizations, stmt);
return result;
}
public bool IsDeletedCharacters() { return _isDeletedCharacters; }
bool _isDeletedCharacters = false;
}
// used at player loading query list preparing, and later result selection
public enum PlayerLoginQueryLoad
{
From,
Customizations,
Group,
BoundInstances,
Auras,
AuraEffects,
Spells,
QuestStatus,
QuestStatusObjectives,
QuestStatusObjectivesCriteria,
QuestStatusObjectivesCriteriaProgress,
DailyQuestStatus,
Reputation,
Inventory,
Artifacts,
Azerite,
AzeriteMilestonePowers,
AzeriteUnlockedEssences,
AzeriteEmpowered,
Actions,
MailCount,
MailDate,
SocialList,
HomeBind,
SpellCooldowns,
SpellCharges,
DeclinedNames,
Guild,
ArenaInfo,
Achievements,
CriteriaProgress,
EquipmentSets,
TransmogOutfits,
BgData,
Glyphs,
Talents,
PvpTalents,
AccountData,
Skills,
WeeklyQuestStatus,
RandomBg,
Banned,
QuestStatusRew,
InstanceLockTimes,
SeasonalQuestStatus,
MonthlyQuestStatus,
VoidStorage,
Currency,
CufProfiles,
CorpseLocation,
Garrison,
GarrisonBlueprints,
GarrisonBuildings,
GarrisonFollowers,
GarrisonFollowerAbilities,
Max
}
enum EnumCharacterQueryLoad
{
Characters,
Customizations
}
}