aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/bnetserver/CMakeLists.txt2
-rw-r--r--src/server/bnetserver/Main.cpp30
-rw-r--r--src/server/bnetserver/bnetserver.conf.dist82
-rw-r--r--src/server/game/CMakeLists.txt1
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h3
-rw-r--r--src/server/game/Entities/Player/Player.cpp25
-rw-r--r--src/server/game/Handlers/BankHandler.cpp172
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp134
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp26
-rw-r--r--src/server/game/Handlers/NPCHandler.cpp25
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h28
-rw-r--r--src/server/game/Scripting/ScriptLoader.cpp2
-rw-r--r--src/server/game/Server/Packets/BankPackets.cpp38
-rw-r--r--src/server/game/Server/Packets/BankPackets.h65
-rw-r--r--src/server/game/Server/Packets/MailPackets.cpp4
-rw-r--r--src/server/game/Server/Packets/MailPackets.h1
-rw-r--r--src/server/game/Server/Packets/MovementPackets.cpp12
-rw-r--r--src/server/game/Server/Packets/MovementPackets.h20
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp24
-rw-r--r--src/server/game/Server/Protocol/Opcodes.h61
-rw-r--r--src/server/game/Server/WorldSession.h18
-rw-r--r--src/server/game/Spells/Auras/SpellAuraDefines.h7
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp5
-rw-r--r--src/server/game/Spells/SpellEffects.cpp6
-rw-r--r--src/server/scripts/CMakeLists.txt1
-rw-r--r--src/server/scripts/Kalimdor/CMakeLists.txt1
-rw-r--r--src/server/scripts/Kalimdor/zone_mulgore.cpp178
-rw-r--r--src/server/scripts/Kalimdor/zone_the_barrens.cpp1
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp6
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp18
-rw-r--r--src/server/scripts/Northrend/zone_borean_tundra.cpp44
-rw-r--r--src/server/scripts/Spells/spell_quest.cpp31
-rw-r--r--src/server/shared/CMakeLists.txt4
-rw-r--r--src/server/shared/Database/DatabaseLoader.cpp193
-rw-r--r--src/server/shared/Database/DatabaseLoader.h72
-rw-r--r--src/server/shared/Database/DatabaseWorkerPool.h91
-rw-r--r--src/server/shared/Database/MySQLConnection.cpp8
-rw-r--r--src/server/shared/Database/MySQLConnection.h5
-rw-r--r--src/server/shared/Updater/DBUpdater.cpp414
-rw-r--r--src/server/shared/Updater/DBUpdater.h79
-rw-r--r--src/server/shared/Updater/UpdateFetcher.cpp387
-rw-r--r--src/server/shared/Updater/UpdateFetcher.h128
-rw-r--r--src/server/worldserver/CMakeLists.txt2
-rw-r--r--src/server/worldserver/Main.cpp107
-rw-r--r--src/server/worldserver/worldserver.conf.dist86
45 files changed, 1992 insertions, 655 deletions
diff --git a/src/server/bnetserver/CMakeLists.txt b/src/server/bnetserver/CMakeLists.txt
index c9f83cc528d..4ea4f697cf7 100644
--- a/src/server/bnetserver/CMakeLists.txt
+++ b/src/server/bnetserver/CMakeLists.txt
@@ -47,6 +47,7 @@ include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/cppformat
${CMAKE_SOURCE_DIR}/dep/zmqpp
+ ${CMAKE_SOURCE_DIR}/dep/process
${CMAKE_SOURCE_DIR}/src/server/shared
${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
${CMAKE_SOURCE_DIR}/src/server/shared/Database
@@ -58,6 +59,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/src/server/shared/Networking
${CMAKE_SOURCE_DIR}/src/server/shared/Realm
${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Updater
${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
${CMAKE_SOURCE_DIR}/src/server/ipc
${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/src/server/bnetserver/Main.cpp b/src/server/bnetserver/Main.cpp
index 5f4f63287e5..678e24c4682 100644
--- a/src/server/bnetserver/Main.cpp
+++ b/src/server/bnetserver/Main.cpp
@@ -36,6 +36,7 @@
#include "SystemConfig.h"
#include "Util.h"
#include "ZmqContext.h"
+#include "DatabaseLoader.h"
#include <cstdlib>
#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -162,32 +163,13 @@ bool StartDB()
{
MySQL::Library_Init();
- std::string dbstring = sConfigMgr->GetStringDefault("LoginDatabaseInfo", "");
- if (dbstring.empty())
- {
- TC_LOG_ERROR("server.bnetserver", "Database not specified");
- return false;
- }
-
- int32 worker_threads = sConfigMgr->GetIntDefault("LoginDatabase.WorkerThreads", 1);
- if (worker_threads < 1 || worker_threads > 32)
- {
- TC_LOG_ERROR("server.bnetserver", "Improper value specified for LoginDatabase.WorkerThreads, defaulting to 1.");
- worker_threads = 1;
- }
+ // Load databases
+ DatabaseLoader loader("server.bnetserver", DatabaseLoader::DATABASE_NONE);
+ loader
+ .AddDatabase(LoginDatabase, "Login");
- int32 synch_threads = sConfigMgr->GetIntDefault("LoginDatabase.SynchThreads", 1);
- if (synch_threads < 1 || synch_threads > 32)
- {
- TC_LOG_ERROR("server.bnetserver", "Improper value specified for LoginDatabase.SynchThreads, defaulting to 1.");
- synch_threads = 1;
- }
-
- if (!LoginDatabase.Open(dbstring, uint8(worker_threads), uint8(synch_threads)))
- {
- TC_LOG_ERROR("server.bnetserver", "Cannot connect to database");
+ if (!loader.Load())
return false;
- }
TC_LOG_INFO("server.bnetserver", "Started auth database connection pool.");
sLog->SetRealmId(0); // Enables DB appenders when realm is set.
diff --git a/src/server/bnetserver/bnetserver.conf.dist b/src/server/bnetserver/bnetserver.conf.dist
index 462bb0cd075..804ce51ef2a 100644
--- a/src/server/bnetserver/bnetserver.conf.dist
+++ b/src/server/bnetserver/bnetserver.conf.dist
@@ -9,6 +9,7 @@
# EXAMPLE CONFIG
# AUTH SERVER SETTINGS
# MYSQL SETTINGS
+# UPDATE SETTINGS
# LOGGING SYSTEM SETTINGS
#
###################################################################################################
@@ -165,6 +166,86 @@ Wrong.Password.Login.Logging = 0
###################################################################################################
###################################################################################################
+# UPDATE SETTINGS
+#
+# Updates.EnableDatabases
+# Description: A mask that describes which databases shall be updated.
+#
+# Following flags are available
+# DATABASE_LOGIN = 1, // Auth database
+#
+# Default: 0 - (All Disabled)
+# 1 - (All Enabled)
+
+Updates.EnableDatabases = 0
+
+#
+# Updates.SourcePath
+# Description: The path to your TrinityCore source directory.
+# If the path is left empty, built-in CMAKE_SOURCE_DIR is used.
+# Example: "../TrinityCore"
+# Default: ""
+
+Updates.SourcePath = ""
+
+#
+# Updates.SourcePath
+# Description: The path to your mysql cli binary.
+# If the path is left empty, built-in path from cmake is used.
+# Example: "C:/Program Files/MySQL/MySQL Server 5.6/bin/mysql.exe"
+# "mysql.exe"
+# "/usr/bin/mysql"
+# Default: ""
+
+Updates.MySqlCLIPath = ""
+
+#
+# Updates.AutoSetup
+# Description: Auto populate empty databases.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Updates.AutoSetup = 1
+
+#
+# Updates.Redundancy
+# Description: Perform data redundancy checks through hashing
+# to detect changes on sql updates and reapply it.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Updates.Redundancy = 1
+
+#
+# Updates.ArchivedRedundancy
+# Description: Check hashes of archived updates (slows down startup).
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Updates.ArchivedRedundancy = 0
+
+#
+# Updates.AllowRehash
+# Description: Inserts the current file hash in the database if it is left empty.
+# Useful if you want to mark a file as applied but you don't know its hash.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Updates.AllowRehash = 1
+
+#
+# Updates.CleanDeadRef
+# Description: Cleans dead/ orphaned references that occure if a update was deleted or renamed and edited.
+# Disable this if you want to know if the database is in a possible "dirty state".
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Updates.CleanDeadRef = 1
+
+#
+###################################################################################################
+
+###################################################################################################
#
# LOGGING SYSTEM SETTINGS
#
@@ -255,6 +336,7 @@ Logger.root=3,Console Bnet
Logger.realmlist=3,Console Bnet
Logger.session=3,Console Bnet
Logger.session.packets=3,Console Bnet
+Logger.sql.updates=3,Console Bnet
#
###################################################################################################
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index ab54efab1fd..55792856eb4 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -131,6 +131,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/src/server/shared/Packets
${CMAKE_SOURCE_DIR}/src/server/shared/Realm
${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Updater
${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
${CMAKE_SOURCE_DIR}/src/server/ipc
${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index d5d53df48c1..42b19771387 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -470,6 +470,7 @@ struct GameObjectTemplate
{
uint32 chairheight; // 0 chairheight, int, Min value: 0, Max value: 2, Default value: 1
int32 HeightOffset; // 1 Height Offset (inches), int, Min value: -100, Max value: 100, Default value: 0
+ uint32 SitAnimKit; // 2 Sit Anim Kit, References: AnimKit, NoValue = 0
} barberChair;
// 33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
struct
@@ -587,6 +588,8 @@ struct GameObjectTemplate
{
int32 mapID; // 0 mapID, References: Map, NoValue = -1
int32 namedset; // 1 named set, int, Min value: -2147483648, Max value: 2147483647, Default value: 0
+ uint32 Primarydoodadset; // 2 Primary doodad set, int, Min value: -2147483648, Max value: 2147483647, Default value: 0
+ uint32 Secondarydoodadset; // 3 Secondary doodad set, int, Min value: -2147483648, Max value: 2147483647, Default value: 0
} phaseableMO;
// 44 GAMEOBJECT_TYPE_GARRISON_MONUMENT
struct
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 6ea9bc523b9..0f5a36e14b4 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -23691,28 +23691,9 @@ void Player::SetMover(Unit* target)
m_mover = target;
m_mover->m_movedPlayer = this;
- ObjectGuid guid = target->GetGUID();
-
- WorldPacket data(SMSG_MOVE_SET_ACTIVE_MOVER, 9);
- data.WriteBit(guid[5]);
- data.WriteBit(guid[7]);
- data.WriteBit(guid[3]);
- data.WriteBit(guid[6]);
- data.WriteBit(guid[0]);
- data.WriteBit(guid[4]);
- data.WriteBit(guid[1]);
- data.WriteBit(guid[2]);
-
- data.WriteByteSeq(guid[6]);
- data.WriteByteSeq(guid[2]);
- data.WriteByteSeq(guid[3]);
- data.WriteByteSeq(guid[0]);
- data.WriteByteSeq(guid[5]);
- data.WriteByteSeq(guid[7]);
- data.WriteByteSeq(guid[1]);
- data.WriteByteSeq(guid[4]);
-
- SendDirectMessage(&data);
+ WorldPackets::Movement::MoveSetActiveMover packet;
+ packet.MoverGUID = target->GetGUID();
+ SendDirectMessage(packet.Write());
}
void Player::UpdateZoneDependentAuras(uint32 newZone)
diff --git a/src/server/game/Handlers/BankHandler.cpp b/src/server/game/Handlers/BankHandler.cpp
new file mode 100644
index 00000000000..8879b4e2532
--- /dev/null
+++ b/src/server/game/Handlers/BankHandler.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#include "BankPackets.h"
+#include "NPCPackets.h"
+#include "ObjectMgr.h"
+#include "Opcodes.h"
+#include "Player.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+
+void WorldSession::HandleAutoBankItemOpcode(WorldPackets::Bank::AutoBankItem& packet)
+{
+ TC_LOG_DEBUG("network", "STORAGE: receive bag = %u, slot = %u", packet.Bag, packet.Slot);
+
+ if (!CanUseBank())
+ {
+ TC_LOG_ERROR("network", "WORLD: HandleAutoBankItemOpcode - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
+ return;
+ }
+
+ Item* item = _player->GetItemByPos(packet.Bag, packet.Slot);
+ if (!item)
+ return;
+
+ ItemPosCountVec dest;
+ InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, item, false);
+ if (msg != EQUIP_ERR_OK)
+ {
+ _player->SendEquipError(msg, item, NULL);
+ return;
+ }
+
+ if (dest.size() == 1 && dest[0].pos == item->GetPos())
+ {
+ _player->SendEquipError(EQUIP_ERR_CANT_SWAP, item, NULL);
+ return;
+ }
+
+ _player->RemoveItem(packet.Bag, packet.Slot, true);
+ _player->ItemRemovedQuestCheck(item->GetEntry(), item->GetCount());
+ _player->BankItem(dest, item, true);
+}
+
+void WorldSession::HandleBankerActivateOpcode(WorldPackets::NPC::Hello& packet)
+{
+ Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_BANKER);
+ if (!unit)
+ {
+ TC_LOG_ERROR("network", "WORLD: HandleBankerActivateOpcode - %s not found or you can not interact with him.", packet.Unit.ToString().c_str());
+ return;
+ }
+
+ // remove fake death
+ if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
+ GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
+ // set currentBankerGUID for other bank action
+
+ SendShowBank(packet.Unit);
+}
+
+void WorldSession::HandleAutoStoreBankItemOpcode(WorldPackets::Bank::AutoStoreBankItem& packet)
+{
+ TC_LOG_DEBUG("network", "STORAGE: receive bag = %u, slot = %u", packet.Bag, packet.Slot);
+
+ if (!CanUseBank())
+ {
+ TC_LOG_ERROR("network", "WORLD: HandleAutoStoreBankItemOpcode - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
+ return;
+ }
+
+ Item* item = _player->GetItemByPos(packet.Bag, packet.Slot);
+ if (!item)
+ return;
+
+ if (_player->IsBankPos(packet.Bag, packet.Slot)) // moving from bank to inventory
+ {
+ ItemPosCountVec dest;
+ InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, item, false);
+ if (msg != EQUIP_ERR_OK)
+ {
+ _player->SendEquipError(msg, item, NULL);
+ return;
+ }
+
+ _player->RemoveItem(packet.Bag, packet.Slot, true);
+ if (Item const* storedItem = _player->StoreItem(dest, item, true))
+ _player->ItemAddedQuestCheck(storedItem->GetEntry(), storedItem->GetCount());
+
+ }
+ else // moving from inventory to bank
+ {
+ ItemPosCountVec dest;
+ InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, item, false);
+ if (msg != EQUIP_ERR_OK)
+ {
+ _player->SendEquipError(msg, item, NULL);
+ return;
+ }
+
+ _player->RemoveItem(packet.Bag, packet.Slot, true);
+ _player->BankItem(dest, item, true);
+ }
+}
+
+void WorldSession::HandleBuyBankSlotOpcode(WorldPackets::Bank::BuyBankSlot& packet)
+{
+ WorldPacket data(SMSG_BUY_BANK_SLOT_RESULT, 4);
+ if (!CanUseBank(packet.Guid))
+ {
+ data << uint32(ERR_BANKSLOT_NOTBANKER);
+ SendPacket(&data);
+ TC_LOG_ERROR("network", "WORLD: HandleBuyBankSlotOpcode - %s not found or you can't interact with him.", packet.Guid.ToString().c_str());
+ return;
+ }
+
+ uint32 slot = _player->GetBankBagSlotCount();
+
+ // next slot
+ ++slot;
+
+ TC_LOG_INFO("network", "PLAYER: Buy bank bag slot, slot number = %u", slot);
+
+ BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
+
+ if (!slotEntry)
+ {
+ data << uint32(ERR_BANKSLOT_FAILED_TOO_MANY);
+ SendPacket(&data);
+ return;
+ }
+
+ uint32 price = slotEntry->Cost;
+
+ if (!_player->HasEnoughMoney(uint64(price)))
+ {
+ data << uint32(ERR_BANKSLOT_INSUFFICIENT_FUNDS);
+ SendPacket(&data);
+ return;
+ }
+
+ _player->SetBankBagSlotCount(slot);
+ _player->ModifyMoney(-int64(price));
+
+ data << uint32(ERR_BANKSLOT_OK);
+ SendPacket(&data);
+
+ _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT);
+}
+
+void WorldSession::SendShowBank(ObjectGuid guid)
+{
+ m_currentBankerGUID = guid;
+ WorldPackets::NPC::ShowBank packet;
+ packet.Guid = guid;
+ SendPacket(packet.Write());
+}
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index e6803832a56..db7b49521c3 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -741,140 +741,6 @@ void WorldSession::HandleAutoStoreBagItemOpcode(WorldPackets::Item::AutoStoreBag
_player->StoreItem(dest, item, true);
}
-void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket)
-{
- TC_LOG_DEBUG("network", "WORLD: CMSG_BUY_BANK_SLOT");
-
- ObjectGuid guid;
- recvPacket >> guid;
-
- WorldPacket data(SMSG_BUY_BANK_SLOT_RESULT, 4);
- if (!CanUseBank(guid))
- {
- data << uint32(ERR_BANKSLOT_NOTBANKER);
- SendPacket(&data);
- TC_LOG_DEBUG("network", "WORLD: HandleBuyBankSlotOpcode - %s not found or you can't interact with him.", guid.ToString().c_str());
- return;
- }
-
- uint32 slot = _player->GetBankBagSlotCount();
-
- // next slot
- ++slot;
-
- TC_LOG_INFO("network", "PLAYER: Buy bank bag slot, slot number = %u", slot);
-
- BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
-
- if (!slotEntry)
- {
- data << uint32(ERR_BANKSLOT_FAILED_TOO_MANY);
- SendPacket(&data);
- return;
- }
-
- uint32 price = slotEntry->Cost;
-
- if (!_player->HasEnoughMoney(uint64(price)))
- {
- data << uint32(ERR_BANKSLOT_INSUFFICIENT_FUNDS);
- SendPacket(&data);
- return;
- }
-
- _player->SetBankBagSlotCount(slot);
- _player->ModifyMoney(-int64(price));
-
- data << uint32(ERR_BANKSLOT_OK);
- SendPacket(&data);
-
- _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT);
-}
-
-void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
-{
- TC_LOG_DEBUG("network", "WORLD: CMSG_AUTOBANK_ITEM");
- uint8 srcbag, srcslot;
-
- recvPacket >> srcbag >> srcslot;
- TC_LOG_DEBUG("network", "STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
-
- if (!CanUseBank())
- {
- TC_LOG_DEBUG("network", "WORLD: HandleAutoBankItemOpcode - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
- return;
- }
-
- Item* pItem = _player->GetItemByPos(srcbag, srcslot);
- if (!pItem)
- return;
-
- ItemPosCountVec dest;
- InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
- if (msg != EQUIP_ERR_OK)
- {
- _player->SendEquipError(msg, pItem, NULL);
- return;
- }
-
- if (dest.size() == 1 && dest[0].pos == pItem->GetPos())
- {
- _player->SendEquipError(EQUIP_ERR_CANT_SWAP, pItem, NULL);
- return;
- }
-
- _player->RemoveItem(srcbag, srcslot, true);
- _player->ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
- _player->BankItem(dest, pItem, true);
-}
-
-void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
-{
- TC_LOG_DEBUG("network", "WORLD: CMSG_AUTOSTORE_BANK_ITEM");
- uint8 srcbag, srcslot;
-
- recvPacket >> srcbag >> srcslot;
- TC_LOG_DEBUG("network", "STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
-
- if (!CanUseBank())
- {
- TC_LOG_DEBUG("network", "WORLD: HandleAutoStoreBankItemOpcode - Unit (%s) not found or you can't interact with him.", m_currentBankerGUID.ToString().c_str());
- return;
- }
-
- Item* pItem = _player->GetItemByPos(srcbag, srcslot);
- if (!pItem)
- return;
-
- if (_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory
- {
- ItemPosCountVec dest;
- InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
- if (msg != EQUIP_ERR_OK)
- {
- _player->SendEquipError(msg, pItem, NULL);
- return;
- }
-
- _player->RemoveItem(srcbag, srcslot, true);
- if (Item const* storedItem = _player->StoreItem(dest, pItem, true))
- _player->ItemAddedQuestCheck(storedItem->GetEntry(), storedItem->GetCount());
- }
- else // moving from inventory to bank
- {
- ItemPosCountVec dest;
- InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
- if (msg != EQUIP_ERR_OK)
- {
- _player->SendEquipError(msg, pItem, NULL);
- return;
- }
-
- _player->RemoveItem(srcbag, srcslot, true);
- _player->BankItem(dest, pItem, true);
- }
-}
-
void WorldSession::SendEnchantmentLog(ObjectGuid target, ObjectGuid caster, uint32 itemId, uint32 enchantId)
{
WorldPacket data(SMSG_ENCHANTMENT_LOG, (8+8+4+4));
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index d1f4c862b0c..f40527b66d7 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -465,33 +465,13 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPackets::Movement::MovementSpe
}
}
-void WorldSession::HandleSetActiveMoverOpcode(WorldPacket& recvPacket)
+void WorldSession::HandleSetActiveMoverOpcode(WorldPackets::Movement::SetActiveMover& packet)
{
TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
- ObjectGuid guid;
-
- guid[7] = recvPacket.ReadBit();
- guid[2] = recvPacket.ReadBit();
- guid[1] = recvPacket.ReadBit();
- guid[0] = recvPacket.ReadBit();
- guid[4] = recvPacket.ReadBit();
- guid[5] = recvPacket.ReadBit();
- guid[6] = recvPacket.ReadBit();
- guid[3] = recvPacket.ReadBit();
-
- recvPacket.ReadByteSeq(guid[3]);
- recvPacket.ReadByteSeq(guid[2]);
- recvPacket.ReadByteSeq(guid[4]);
- recvPacket.ReadByteSeq(guid[0]);
- recvPacket.ReadByteSeq(guid[5]);
- recvPacket.ReadByteSeq(guid[1]);
- recvPacket.ReadByteSeq(guid[6]);
- recvPacket.ReadByteSeq(guid[7]);
-
if (GetPlayer()->IsInWorld())
- if (_player->m_mover->GetGUID() != guid)
- TC_LOG_DEBUG("network", "HandleSetActiveMoverOpcode: incorrect mover guid: mover is %s and should be %s" , guid.ToString().c_str(), _player->m_mover->GetGUID().ToString().c_str());
+ if (_player->m_mover->GetGUID() != packet.ActiveMover)
+ TC_LOG_DEBUG("network", "HandleSetActiveMoverOpcode: incorrect mover guid: mover is %s and should be %s" , packet.ActiveMover.ToString().c_str(), _player->m_mover->GetGUID().ToString().c_str());
}
void WorldSession::HandleMoveNotActiveMover(WorldPacket &recvData)
diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp
index 80a2284aa7d..48e752b5d0b 100644
--- a/src/server/game/Handlers/NPCHandler.cpp
+++ b/src/server/game/Handlers/NPCHandler.cpp
@@ -73,31 +73,6 @@ void WorldSession::SendTabardVendorActivate(ObjectGuid guid)
SendPacket(packet.Write());
}
-void WorldSession::HandleBankerActivateOpcode(WorldPackets::NPC::Hello& packet)
-{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_BANKER_ACTIVATE");
-
- Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_BANKER);
- if (!unit)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleBankerActivateOpcode - %s not found or you can not interact with him.", packet.Unit.ToString().c_str());
- return;
- }
-
- // remove fake death
- if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
- GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
-
- SendShowBank(packet.Unit);
-}
-
-void WorldSession::SendShowBank(ObjectGuid guid)
-{
- WorldPackets::NPC::ShowBank packet;
- packet.Guid = guid;
- SendPacket(packet.Write());
-}
-
void WorldSession::SendShowMailBox(ObjectGuid guid)
{
WorldPacket data(SMSG_SHOW_MAILBOX, 8);
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 5142eebbaa2..0f01ba2dfae 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -1252,7 +1252,13 @@ enum SpellEffectName
SPELL_EFFECT_242 = 242,
SPELL_EFFECT_243 = 243,
SPELL_EFFECT_244 = 244,
- TOTAL_SPELL_EFFECTS = 245,
+ SPELL_EFFECT_245 = 245,
+ SPELL_EFFECT_246 = 246,
+ SPELL_EFFECT_247 = 247,
+ SPELL_EFFECT_248 = 248,
+ SPELL_EFFECT_249 = 249,
+ SPELL_EFFECT_250 = 250,
+ TOTAL_SPELL_EFFECTS = 251,
};
enum SpellCastResult // 19702
@@ -1984,6 +1990,26 @@ enum Targets
TARGET_UNK_125 = 125,
TARGET_UNK_126 = 126,
TARGET_UNK_127 = 127,
+ TARGET_UNK_128 = 128,
+ TARGET_UNK_129 = 129,
+ TARGET_UNK_130 = 130,
+ TARGET_UNK_131 = 131,
+ TARGET_UNK_132 = 132,
+ TARGET_UNK_133 = 133,
+ TARGET_UNK_134 = 134,
+ TARGET_UNK_135 = 135,
+ TARGET_UNK_136 = 136,
+ TARGET_UNK_137 = 137,
+ TARGET_UNK_138 = 138,
+ TARGET_UNK_139 = 139,
+ TARGET_UNK_140 = 140,
+ TARGET_UNK_141 = 141,
+ TARGET_UNK_142 = 142,
+ TARGET_UNK_143 = 143,
+ TARGET_UNK_144 = 144,
+ TARGET_UNK_145 = 145,
+ TARGET_UNK_146 = 146,
+ TARGET_UNK_147 = 147,
TOTAL_SPELL_TARGETS
};
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 603089b0a6b..553c752ccce 100644
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -368,7 +368,6 @@ void AddSC_dustwallow_marsh();
void AddSC_felwood();
void AddSC_feralas();
void AddSC_moonglade();
-void AddSC_mulgore();
void AddSC_orgrimmar();
void AddSC_silithus();
void AddSC_stonetalon_mountains();
@@ -1090,7 +1089,6 @@ void AddKalimdorScripts()
AddSC_felwood();
AddSC_feralas();
AddSC_moonglade();
- AddSC_mulgore();
AddSC_orgrimmar();
AddSC_silithus();
AddSC_stonetalon_mountains();
diff --git a/src/server/game/Server/Packets/BankPackets.cpp b/src/server/game/Server/Packets/BankPackets.cpp
new file mode 100644
index 00000000000..0f6af30bed0
--- /dev/null
+++ b/src/server/game/Server/Packets/BankPackets.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#include "BankPackets.h"
+#include "ItemPackets.h"
+
+void WorldPackets::Bank::AutoBankItem::Read()
+{
+ _worldPacket >> Inv
+ >> Bag
+ >> Slot;
+}
+
+void WorldPackets::Bank::AutoStoreBankItem::Read()
+{
+ _worldPacket >> Inv
+ >> Bag
+ >> Slot;
+}
+
+void WorldPackets::Bank::BuyBankSlot::Read()
+{
+ _worldPacket >> Guid;
+}
diff --git a/src/server/game/Server/Packets/BankPackets.h b/src/server/game/Server/Packets/BankPackets.h
new file mode 100644
index 00000000000..dc7883c3643
--- /dev/null
+++ b/src/server/game/Server/Packets/BankPackets.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#ifndef BankPackets_h__
+#define BankPackets_h__
+
+#include "ItemPackets.h"
+#include "Packet.h"
+#include "ObjectGuid.h"
+#include "WorldSession.h"
+
+namespace WorldPackets
+{
+ namespace Bank
+ {
+ class AutoBankItem final : public ClientPacket
+ {
+ public:
+ AutoBankItem(WorldPacket&& packet) : ClientPacket(CMSG_AUTOBANK_ITEM, std::move(packet)) { }
+
+ void Read() override;
+
+ WorldPackets::Item::InvUpdate Inv;
+ uint8 Bag = 0;
+ uint8 Slot = 0;
+ };
+
+ class AutoStoreBankItem final : public ClientPacket
+ {
+ public:
+ AutoStoreBankItem(WorldPacket&& packet) : ClientPacket(CMSG_AUTOSTORE_BANK_ITEM, std::move(packet)) { }
+
+ void Read() override;
+
+ WorldPackets::Item::InvUpdate Inv;
+ uint8 Bag = 0;
+ uint8 Slot = 0;
+ };
+
+ class BuyBankSlot final : public ClientPacket
+ {
+ public:
+ BuyBankSlot(WorldPacket&& packet) : ClientPacket(CMSG_BUY_BANK_SLOT, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid Guid;
+ };
+ }
+}
+#endif // BankPackets_h__
diff --git a/src/server/game/Server/Packets/MailPackets.cpp b/src/server/game/Server/Packets/MailPackets.cpp
index d558b4682c1..425feac5d9f 100644
--- a/src/server/game/Server/Packets/MailPackets.cpp
+++ b/src/server/game/Server/Packets/MailPackets.cpp
@@ -240,8 +240,6 @@ WorldPackets::Mail::MailQueryNextTimeResult::MailNextTimeEntry::MailNextTimeEntr
{
case MAIL_NORMAL:
SenderGuid = ObjectGuid::Create<HighGuid::Player>(mail->sender);
- SenderHint.NativeRealmAddress.Set(GetVirtualRealmAddress());
- SenderHint.VirtualRealmAddress.Set(GetVirtualRealmAddress());
break;
case MAIL_AUCTION:
case MAIL_CREATURE:
@@ -264,8 +262,6 @@ WorldPacket const* WorldPackets::Mail::MailQueryNextTimeResult::Write()
for (auto const& entry : Next)
{
_worldPacket << entry.SenderGuid;
- _worldPacket << entry.SenderHint;
-
_worldPacket << float(entry.TimeLeft);
_worldPacket << int32(entry.AltSenderID);
_worldPacket << int8(entry.AltSenderType);
diff --git a/src/server/game/Server/Packets/MailPackets.h b/src/server/game/Server/Packets/MailPackets.h
index 281a48b7248..e79469a76d8 100644
--- a/src/server/game/Server/Packets/MailPackets.h
+++ b/src/server/game/Server/Packets/MailPackets.h
@@ -223,7 +223,6 @@ namespace WorldPackets
MailNextTimeEntry(::Mail const* mail);
ObjectGuid SenderGuid;
- Query::PlayerGuidLookupHint SenderHint;
float TimeLeft = 0.0f;
int32 AltSenderID = 0;
int8 AltSenderType = 0;
diff --git a/src/server/game/Server/Packets/MovementPackets.cpp b/src/server/game/Server/Packets/MovementPackets.cpp
index d0a3e0038e9..836c00b70ca 100644
--- a/src/server/game/Server/Packets/MovementPackets.cpp
+++ b/src/server/game/Server/Packets/MovementPackets.cpp
@@ -608,3 +608,15 @@ void WorldPackets::Movement::MovementSpeedAck::Read()
_worldPacket >> AckIndex;
_worldPacket >> Speed;
}
+
+void WorldPackets::Movement::SetActiveMover::Read()
+{
+ _worldPacket >> ActiveMover;
+}
+
+WorldPacket const* WorldPackets::Movement::MoveSetActiveMover::Write()
+{
+ _worldPacket << MoverGUID;
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/MovementPackets.h b/src/server/game/Server/Packets/MovementPackets.h
index 0e5449c7947..0b57cdaddb0 100644
--- a/src/server/game/Server/Packets/MovementPackets.h
+++ b/src/server/game/Server/Packets/MovementPackets.h
@@ -308,6 +308,26 @@ namespace WorldPackets
int32 AckIndex = 0;
float Speed = 0.0f;
};
+
+ class SetActiveMover final : public ClientPacket
+ {
+ public:
+ SetActiveMover(WorldPacket&& packet) : ClientPacket(CMSG_SET_ACTIVE_MOVER, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid ActiveMover;
+ };
+
+ class MoveSetActiveMover final : public ServerPacket
+ {
+ public:
+ MoveSetActiveMover() : ServerPacket(SMSG_MOVE_SET_ACTIVE_MOVER, 8) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid MoverGUID;
+ };
}
ByteBuffer& operator<<(ByteBuffer& data, Movement::MonsterSplineFilterKey const& monsterSplineFilterKey);
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index bb6d69260bc..467e56fb1f1 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -20,6 +20,7 @@
#include "WorldSession.h"
#include "Packets/AchievementPackets.h"
#include "Packets/AuctionHousePackets.h"
+#include "Packets/BankPackets.h"
#include "Packets/BlackMarketPackets.h"
#include "Packets/CharacterPackets.h"
#include "Packets/ChannelPackets.h"
@@ -178,12 +179,12 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_AUCTION_SELL_ITEM, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionSellItem );
DEFINE_HANDLER(CMSG_AUTH_CONTINUED_SESSION, STATUS_NEVER, PROCESS_INPLACE, WorldPacket, &WorldSession::Handle_EarlyProccess);
DEFINE_HANDLER(CMSG_AUTH_SESSION, STATUS_NEVER, PROCESS_INPLACE, WorldPacket, &WorldSession::Handle_EarlyProccess);
- DEFINE_OPCODE_HANDLER_OLD(CMSG_AUTOBANK_ITEM, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoBankItemOpcode );
+ DEFINE_HANDLER(CMSG_AUTOBANK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Bank::AutoBankItem, &WorldSession::HandleAutoBankItemOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_AUTOEQUIP_GROUND_ITEM, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
- DEFINE_HANDLER(CMSG_AUTOEQUIP_ITEM, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Item::AutoEquipItem, &WorldSession::HandleAutoEquipItemOpcode);
+ DEFINE_HANDLER(CMSG_AUTOEQUIP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::AutoEquipItem, &WorldSession::HandleAutoEquipItemOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_AUTOEQUIP_ITEM_SLOT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoEquipItemSlotOpcode );
DEFINE_HANDLER(CMSG_AUTOSTORE_BAG_ITEM, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Item::AutoStoreBagItem, &WorldSession::HandleAutoStoreBagItemOpcode);
- DEFINE_OPCODE_HANDLER_OLD(CMSG_AUTOSTORE_BANK_ITEM, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoStoreBankItemOpcode );
+ DEFINE_HANDLER(CMSG_AUTOSTORE_BANK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Bank::AutoStoreBankItem, &WorldSession::HandleAutoStoreBankItemOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_AUTOSTORE_GROUND_ITEM, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_HANDLER(CMSG_AUTOSTORE_LOOT_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Loot::AutoStoreLootItem, &WorldSession::HandleAutostoreLootItemOpcode);
DEFINE_HANDLER(CMSG_BANKER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleBankerActivateOpcode);
@@ -228,7 +229,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_BUG_REPORT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleBugReportOpcode );
DEFINE_OPCODE_HANDLER_OLD(CMSG_BUSY_TRADE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleBusyTradeOpcode );
DEFINE_HANDLER(CMSG_BUY_BACK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::BuyBackItem, &WorldSession::HandleBuybackItem);
- DEFINE_OPCODE_HANDLER_OLD(CMSG_BUY_BANK_SLOT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleBuyBankSlotOpcode );
+ DEFINE_HANDLER(CMSG_BUY_BANK_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Bank::BuyBankSlot, &WorldSession::HandleBuyBankSlotOpcode);
DEFINE_HANDLER(CMSG_BUY_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::BuyItem, &WorldSession::HandleBuyItemOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_CAGE_BATTLE_PET, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_CALENDAR_ADD_EVENT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarAddEvent );
@@ -488,7 +489,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_LF_GUILD_SET_GUILD_POST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildFinderSetGuildPost );
DEFINE_HANDLER(CMSG_LIST_INVENTORY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleListInventoryOpcode);
DEFINE_HANDLER(CMSG_LOAD_SCREEN, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::Character::LoadingScreenNotify, &WorldSession::HandleLoadScreenOpcode);
- DEFINE_HANDLER(CMSG_LOGOUT_CANCEL, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Character::LogoutCancel, &WorldSession::HandleLogoutCancelOpcode);
+ DEFINE_HANDLER(CMSG_LOGOUT_CANCEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::LogoutCancel, &WorldSession::HandleLogoutCancelOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_LOGOUT_INSTANT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_HANDLER(CMSG_LOGOUT_REQUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::LogoutRequest, &WorldSession::HandleLogoutRequestOpcode);
DEFINE_HANDLER(CMSG_LOG_DISCONNECT, STATUS_NEVER, PROCESS_INPLACE, WorldPacket, &WorldSession::Handle_EarlyProccess);
@@ -600,7 +601,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_MOVE_TOGGLE_COLLISION_ACK, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_MOVE_TOGGLE_COLLISION_CHEAT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_MOVE_WATER_WALK_ACK, STATUS_UNHANDLED, PROCESS_THREADSAFE, &WorldSession::HandleMoveWaterWalkAck );
- DEFINE_HANDLER(CMSG_MOVE_WORLDPORT_ACK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Movement::WorldPortAck, &WorldSession::HandleMoveWorldportAckOpcode);
+ DEFINE_HANDLER(CMSG_MOVE_WORLDPORT_ACK, STATUS_TRANSFER, PROCESS_THREADUNSAFE, WorldPackets::Movement::WorldPortAck, &WorldSession::HandleMoveWorldportAckOpcode);
DEFINE_HANDLER(CMSG_NAME_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Query::QueryPlayerName, &WorldSession::HandleNameQueryOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_NEUTRAL_PLAYER_SELECT_FACTION, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_NEW_SPELL_SLOT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
@@ -667,7 +668,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_QUESTGIVER_HELLO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Quest::QuestGiverHello, &WorldSession::HandleQuestgiverHelloOpcode);
DEFINE_HANDLER(CMSG_QUESTGIVER_QUERY_QUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Quest::QuestGiverQueryQuest, &WorldSession::HandleQuestgiverQueryQuestOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_QUESTGIVER_QUEST_AUTOLAUNCH, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
- DEFINE_HANDLER(CMSG_QUESTGIVER_REQUEST_REWARD, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Quest::QuestGiverRequestReward, &WorldSession::HandleQuestgiverRequestRewardOpcode);
+ DEFINE_HANDLER(CMSG_QUESTGIVER_REQUEST_REWARD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Quest::QuestGiverRequestReward, &WorldSession::HandleQuestgiverRequestRewardOpcode);
DEFINE_HANDLER(CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Quest::QuestGiverStatusMultipleQuery, &WorldSession::HandleQuestgiverStatusMultipleQuery);
DEFINE_HANDLER(CMSG_QUESTGIVER_STATUS_QUERY, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Quest::QuestGiverStatusQuery, &WorldSession::HandleQuestgiverStatusQueryOpcode);
DEFINE_HANDLER(CMSG_QUESTLOG_REMOVE_QUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Quest::QuestLogRemoveQuest, &WorldSession::HandleQuestLogRemoveQuest);
@@ -691,7 +692,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_REPAIR_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::RepairItem, &WorldSession::HandleRepairItemOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_REPLACE_ACCOUNT_DATA, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_REPLACE_TROPHY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
- DEFINE_HANDLER(CMSG_REPOP_REQUEST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Misc::RepopRequest, &WorldSession::HandleRepopRequest);
+ DEFINE_HANDLER(CMSG_REPOP_REQUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::RepopRequest, &WorldSession::HandleRepopRequest);
DEFINE_OPCODE_HANDLER_OLD(CMSG_REPORT_FILTERED, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_REPORT_IGNORED, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleChatIgnoredOpcode );
DEFINE_OPCODE_HANDLER_OLD(CMSG_REPORT_PVP_AFK, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleReportPvPAFK );
@@ -742,7 +743,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_ACHIEVEMENTS_HIDDEN, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_HANDLER(CMSG_SET_ACTIONBAR_TOGGLES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::SetActionBarToggles, &WorldSession::HandleSetActionBarToggles);
DEFINE_HANDLER(CMSG_SET_ACTION_BUTTON, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::SetActionButton, &WorldSession::HandleSetActionButtonOpcode);
- DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_ACTIVE_MOVER, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetActiveMoverOpcode );
+ DEFINE_HANDLER(CMSG_SET_ACTIVE_MOVER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Movement::SetActiveMover, &WorldSession::HandleSetActiveMoverOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_ACTIVE_VOICE_CHANNEL, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetActiveVoiceChannel );
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_ADVANCED_COMBAT_LOGGING, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_ASSISTANT_LEADER, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::HandleGroupAssistantLeaderOpcode);
@@ -808,7 +809,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_SUSPEND_COMMS_ACK, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_SUSPEND_TOKEN_RESPONSE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_HANDLER(CMSG_SWAP_INV_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::SwapInvItem, &WorldSession::HandleSwapInvItemOpcode);
- DEFINE_HANDLER(CMSG_SWAP_ITEM, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Item::SwapItem, &WorldSession::HandleSwapItem);
+ DEFINE_HANDLER(CMSG_SWAP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::SwapItem, &WorldSession::HandleSwapItem);
DEFINE_OPCODE_HANDLER_OLD(CMSG_SWAP_SUB_GROUPS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::HandleGroupSwapSubGroupOpcode );
DEFINE_OPCODE_HANDLER_OLD(CMSG_SYNC_DANCE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_HANDLER(CMSG_TABARD_VENDOR_ACTIVATE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleTabardVendorActivateOpcode);
@@ -835,6 +836,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_TURN_IN_PETITION, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Petition::TurnInPetition, &WorldSession::HandleTurnInPetition);
DEFINE_HANDLER(CMSG_TUTORIAL_FLAG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::TutorialSetFlag, &WorldSession::HandleTutorialFlag);
DEFINE_HANDLER(CMSG_UI_TIME_REQUEST, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Misc::UITimeRequest, &WorldSession::HandleUITimeRequest);
+ DEFINE_OPCODE_HANDLER_OLD(CMSG_TWITTER_GET_STATUS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_OPCODE_HANDLER_OLD(CMSG_UNACCEPT_TRADE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleUnacceptTradeOpcode );
DEFINE_HANDLER(CMSG_UNDELETE_CHARACTER, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Character::UndeleteCharacter, &WorldSession::HandleCharUndeleteOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_UNLEARN_SKILL, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleUnlearnSkillOpcode );
@@ -1445,7 +1447,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_KNOCK_BACK, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_REMOVE_MOVEMENT_FORCE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_ROOT, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_ACTIVE_MOVER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_ACTIVE_MOVER, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_ANIM_KIT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_CAN_FLY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_CAN_TURN_WHILE_FALLING, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h
index e1b459c2599..819fe08ead7 100644
--- a/src/server/game/Server/Protocol/Opcodes.h
+++ b/src/server/game/Server/Protocol/Opcodes.h
@@ -73,21 +73,21 @@ enum OpcodeClient : uint32
CMSG_ATTACKSWING = 0x048B,
CMSG_AUCTION_HELLO_REQUEST = 0x06E3,
CMSG_AUCTION_LIST_BIDDER_ITEMS = 0x0C81,
- CMSG_AUCTION_LIST_ITEMS = 0xBADD,
+ CMSG_AUCTION_LIST_ITEMS = 0x05C3,
CMSG_AUCTION_LIST_OWNER_ITEMS = 0x082A,
CMSG_AUCTION_LIST_PENDING_SALES = 0x0D82,
CMSG_AUCTION_PLACE_BID = 0x09E1,
- CMSG_AUCTION_REMOVE_ITEM = 0xBADD,
+ CMSG_AUCTION_REMOVE_ITEM = 0x1309,
CMSG_AUCTION_REPLICATE_ITEMS = 0xBADD,
- CMSG_AUCTION_SELL_ITEM = 0xBADD,
+ CMSG_AUCTION_SELL_ITEM = 0x09A3,
CMSG_AUTH_CONTINUED_SESSION = 0x1A72,
CMSG_AUTH_SESSION = 0x1872,
- CMSG_AUTOBANK_ITEM = 0xBADD,
+ CMSG_AUTOBANK_ITEM = 0x00C6,
CMSG_AUTOEQUIP_GROUND_ITEM = 0xBADD,
- CMSG_AUTOEQUIP_ITEM = 0xBADD,
- CMSG_AUTOEQUIP_ITEM_SLOT = 0xBADD,
+ CMSG_AUTOEQUIP_ITEM = 0x0235,
+ CMSG_AUTOEQUIP_ITEM_SLOT = 0x00E5,
CMSG_AUTOSTORE_BAG_ITEM = 0xBADD,
- CMSG_AUTOSTORE_BANK_ITEM = 0xBADD,
+ CMSG_AUTOSTORE_BANK_ITEM = 0x00D5,
CMSG_AUTOSTORE_GROUND_ITEM = 0xBADD,
CMSG_AUTOSTORE_LOOT_ITEM = 0x0843,
CMSG_BANKER_ACTIVATE = 0x0931,
@@ -115,7 +115,7 @@ enum OpcodeClient : uint32
CMSG_BATTLE_PET_DELETE_PET = 0xBADD,
CMSG_BATTLE_PET_DELETE_PET_CHEAT = 0xBADD,
CMSG_BATTLE_PET_MODIFY_NAME = 0x1131,
- CMSG_BATTLE_PET_NAME_QUERY = 0xBADD,
+ CMSG_BATTLE_PET_NAME_QUERY = 0x1184,
CMSG_BATTLE_PET_REQUEST_JOURNAL = 0x1773,
CMSG_BATTLE_PET_REQUEST_JOURNAL_LOCK = 0x1B24,
CMSG_BATTLE_PET_REQUEST_UPDATE = 0xBADD,
@@ -279,7 +279,7 @@ enum OpcodeClient : uint32
CMSG_GET_GARRISON_INFO = 0x0352,
CMSG_GET_ITEM_PURCHASE_DATA = 0x0CE4,
CMSG_GET_MAIL_LIST = 0x0979,
- CMSG_GET_MIRROR_IMAGE_DATA = 0xBADD,
+ CMSG_GET_MIRROR_IMAGE_DATA = 0x1952,
CMSG_GET_SHIPMENT_INFO = 0xBADD,
CMSG_GET_TROPHY_LIST = 0xBADD,
CMSG_GET_UNDELETE_COOLDOWN_STATUS = 0x196A,
@@ -288,7 +288,7 @@ enum OpcodeClient : uint32
CMSG_GM_LAG_REPORT = 0xBADD,
CMSG_GM_NUKE = 0xBADD,
CMSG_GM_SET_SECURITY_GROUP = 0xBADD,
- CMSG_GM_SURVEY_SUBMIT = 0xBADD,
+ CMSG_GM_SURVEY_SUBMIT = 0x1BAB,
CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY = 0xBADD,
CMSG_GM_TICKET_CREATE = 0x19A4,
CMSG_GM_TICKET_DELETE_TICKET = 0x1B39,
@@ -397,7 +397,7 @@ enum OpcodeClient : uint32
CMSG_LF_GUILD_SET_GUILD_POST = 0xBADD,
CMSG_LIST_INVENTORY = 0x06C4,
CMSG_LOAD_SCREEN = 0x13E4,
- CMSG_LOGOUT_CANCEL = 0xBADD,
+ CMSG_LOGOUT_CANCEL = 0x0DC1,
CMSG_LOGOUT_INSTANT = 0xBADD,
CMSG_LOGOUT_REQUEST = 0x0824,
CMSG_LOG_DISCONNECT = 0x1432,
@@ -452,7 +452,7 @@ enum OpcodeClient : uint32
CMSG_MOVE_ENABLE_SWIM_TO_FLY_TRANS_ACK = 0x038B,
CMSG_MOVE_FALL_LAND = 0x0DEA,
CMSG_MOVE_FALL_RESET = 0x0F89,
- CMSG_MOVE_FEATHER_FALL_ACK = 0x0F89,
+ CMSG_MOVE_FEATHER_FALL_ACK = 0x02B1,
CMSG_MOVE_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK = 0xBADD,
CMSG_MOVE_FORCE_FLIGHT_SPEED_CHANGE_ACK = 0x09CB,
CMSG_MOVE_FORCE_PITCH_RATE_CHANGE_ACK = 0xBADD,
@@ -548,7 +548,7 @@ enum OpcodeClient : uint32
CMSG_PET_CAST_SPELL = 0xBADD,
CMSG_PET_LEARN_TALENT = 0xBADD,
CMSG_PET_NAME_CACHE = 0xBADD,
- CMSG_PET_NAME_QUERY = 0xBADD,
+ CMSG_PET_NAME_QUERY = 0x0CCB,
CMSG_PET_RENAME = 0x1333,
CMSG_PET_SET_ACTION = 0xBADD,
CMSG_PET_SET_SPECIALIZATION = 0xBADD,
@@ -577,7 +577,7 @@ enum OpcodeClient : uint32
CMSG_QUESTGIVER_HELLO = 0x0B2A,
CMSG_QUESTGIVER_QUERY_QUEST = 0x131A,
CMSG_QUESTGIVER_QUEST_AUTOLAUNCH = 0xBADD,
- CMSG_QUESTGIVER_REQUEST_REWARD = 0xBADD,
+ CMSG_QUESTGIVER_REQUEST_REWARD = 0x1509,
CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY = 0x0A39,
CMSG_QUESTGIVER_STATUS_QUERY = 0x0A7B,
CMSG_QUESTLOG_REMOVE_QUEST = 0x04C3,
@@ -595,12 +595,12 @@ enum OpcodeClient : uint32
CMSG_REALM_NAME_QUERY = 0x17BC,
CMSG_RECLAIM_CORPSE = 0x093B,
CMSG_RECRUIT_A_FRIEND = 0xBADD,
- CMSG_REGISTER_ADDON_PREFIXES = 0xBADD,
+ CMSG_REGISTER_ADDON_PREFIXES = 0x063B,
CMSG_REORDER_CHARACTERS = 0x1729,
CMSG_REPAIR_ITEM = 0x05E2,
CMSG_REPLACE_ACCOUNT_DATA = 0xBADD,
CMSG_REPLACE_TROPHY = 0xBADD,
- CMSG_REPOP_REQUEST = 0xBADD,
+ CMSG_REPOP_REQUEST = 0x0B3B,
CMSG_REPORT_FILTERED = 0xBADD,
CMSG_REPORT_IGNORED = 0xBADD,
CMSG_REPORT_PVP_AFK = 0xBADD,
@@ -614,8 +614,8 @@ enum OpcodeClient : uint32
CMSG_REQUEST_GUILD_REWARDS_LIST = 0xBADD,
CMSG_REQUEST_HONOR_STATS = 0xBADD,
CMSG_REQUEST_LFG_LIST_BLACKLIST = 0xBADD,
- CMSG_REQUEST_PARTY_JOIN_UPDATES = 0xBADD,
- CMSG_REQUEST_PARTY_MEMBER_STATS = 0xBADD,
+ CMSG_REQUEST_PARTY_JOIN_UPDATES = 0x1339,
+ CMSG_REQUEST_PARTY_MEMBER_STATS = 0x1972,
CMSG_REQUEST_PET_INFO = 0xBADD,
CMSG_REQUEST_PVP_OPTIONS_ENABLED = 0xBADD,
CMSG_REQUEST_PVP_REWARDS = 0xBADD,
@@ -743,6 +743,7 @@ enum OpcodeClient : uint32
CMSG_TROPHY_MONUMENT_LOAD_SELECTED_TROPHY_ID = 0xBADD,
CMSG_TURN_IN_PETITION = 0xBADD,
CMSG_TUTORIAL_FLAG = 0x1132,
+ CMSG_TWITTER_GET_STATUS = 0x1128,
CMSG_UI_TIME_REQUEST = 0x1B7B,
CMSG_UNACCEPT_TRADE = 0xBADD,
CMSG_UNDELETE_CHARACTER = 0xBADD,
@@ -767,7 +768,7 @@ enum OpcodeClient : uint32
CMSG_VOICE_DEL_IGNORE = 0xBADD,
CMSG_VOICE_SESSION_ENABLE = 0x153A,
CMSG_VOID_STORAGE_QUERY = 0x17C3,
- CMSG_VOID_STORAGE_TRANSFER = 0xBADD,
+ CMSG_VOID_STORAGE_TRANSFER = 0x0674,
CMSG_VOID_STORAGE_UNLOCK = 0xBADD,
CMSG_VOID_SWAP_ITEM = 0xBADD,
CMSG_WARDEN_DATA = 0x11E3,
@@ -892,8 +893,8 @@ enum OpcodeServer : uint32
SMSG_BATTLEGROUND_INIT = 0xBADD,
SMSG_BATTLEGROUND_PLAYER_JOINED = 0xBADD,
SMSG_BATTLEGROUND_PLAYER_LEFT = 0xBADD,
- SMSG_BATTLEGROUND_PLAYER_POSITIONS = 0xBADD,
- SMSG_BATTLEGROUND_POINTS = 0xBADD,
+ SMSG_BATTLEGROUND_PLAYER_POSITIONS = 0x1962,
+ SMSG_BATTLEGROUND_POINTS = 0x15AB,
SMSG_BATTLENET_CHALLENGE_ABORT = 0xBADD,
SMSG_BATTLENET_CHALLENGE_START = 0xBADD,
SMSG_BATTLE_PAY_ACK_FAILED = 0xBADD,
@@ -1306,7 +1307,7 @@ enum OpcodeServer : uint32
SMSG_LFG_PLAYER_INFO = 0x03FA,
SMSG_LFG_PLAYER_REWARD = 0xBADD,
SMSG_LFG_PROPOSAL_UPDATE = 0xBADD,
- SMSG_LFG_QUEUE_STATUS = 0xBADD,
+ SMSG_LFG_QUEUE_STATUS = 0x0292,
SMSG_LFG_ROLE_CHECK_UPDATE = 0xBADD,
SMSG_LFG_ROLE_CHOSEN = 0xBADD,
SMSG_LFG_SEARCH_RESULTS = 0xBADD,
@@ -1314,7 +1315,7 @@ enum OpcodeServer : uint32
SMSG_LFG_TELEPORT_DENIED = 0xBADD,
SMSG_LFG_UPDATE_LIST = 0xBADD,
SMSG_LFG_UPDATE_SEARCH = 0xBADD,
- SMSG_LFG_UPDATE_STATUS = 0xBADD,
+ SMSG_LFG_UPDATE_STATUS = 0x02B1,
SMSG_LF_GUILD_APPLICANT_LIST_UPDATED = 0xBADD,
SMSG_LF_GUILD_APPLICATIONS = 0xBADD,
SMSG_LF_GUILD_APPLICATIONS_LIST_CHANGED = 0xBADD,
@@ -1485,10 +1486,10 @@ enum OpcodeServer : uint32
SMSG_PARTY_COMMAND_RESULT = 0xBADD,
SMSG_PARTY_INVITE = 0x1F9C,
SMSG_PARTY_KILL_LOG = 0x0CA4,
- SMSG_PARTY_MEMBER_STATE = 0xBADD,
+ SMSG_PARTY_MEMBER_STATE = 0x1564,
SMSG_PARTY_MEMBER_STATS = 0xBADD,
SMSG_PARTY_MEMBER_STATS_FULL = 0xBADD,
- SMSG_PARTY_UPDATE = 0xBADD,
+ SMSG_PARTY_UPDATE = 0x0981,
SMSG_PAUSE_MIRROR_TIMER = 0xBADD,
SMSG_PENDING_RAID_LOCK = 0xBADD,
SMSG_PERIODICAURALOG = 0x184B,
@@ -1612,7 +1613,7 @@ enum OpcodeServer : uint32
SMSG_REFER_A_FRIEND_EXPIRED = 0xBADD,
SMSG_REFER_A_FRIEND_FAILURE = 0xBADD,
SMSG_REFRESH_COMPONENT = 0xBADD,
- SMSG_REFRESH_SPELL_HISTORY = 0xBADD,
+ SMSG_REFRESH_SPELL_HISTORY = 0x072B,
SMSG_REMOVE_ITEM_PASSIVE = 0xBADD,
SMSG_REMOVE_LOSS_OF_CONTROL = 0xBADD,
SMSG_REPLACE_TROPHY_RESPONSE = 0xBADD,
@@ -1686,7 +1687,7 @@ enum OpcodeServer : uint32
SMSG_SET_FACTION_STANDING = 0x1129,
SMSG_SET_FACTION_VISIBLE = 0x0A72,
SMSG_SET_FLAT_SPELL_MODIFIER = 0x120B,
- SMSG_SET_FORCED_REACTIONS = 0xBADD,
+ SMSG_SET_FORCED_REACTIONS = 0x0EC2,
SMSG_SET_ITEM_PURCHASE_DATA = 0xBADD,
SMSG_SET_LFG_TIME_WALKER = 0xBADD,
SMSG_SET_LOOT_METHOD_FAILED = 0xBADD,
@@ -1815,10 +1816,10 @@ enum OpcodeServer : uint32
SMSG_VOICE_SESSION_ROSTER_UPDATE = 0xBADD,
SMSG_VOICE_SET_TALKER_MUTED = 0xBADD,
SMSG_VOID_ITEM_SWAP_RESPONSE = 0xBADD,
- SMSG_VOID_STORAGE_CONTENTS = 0xBADD,
+ SMSG_VOID_STORAGE_CONTENTS = 0x1122,
SMSG_VOID_STORAGE_FAILED = 0xBADD,
- SMSG_VOID_STORAGE_TRANSFER_CHANGES = 0xBADD,
- SMSG_VOID_TRANSFER_RESULT = 0xBADD,
+ SMSG_VOID_STORAGE_TRANSFER_CHANGES = 0x1BAB,
+ SMSG_VOID_TRANSFER_RESULT = 0x192C,
SMSG_WAIT_QUEUE_FINISH = 0xBADD,
SMSG_WAIT_QUEUE_UPDATE = 0xBADD,
SMSG_WARDEN_DATA = 0x110A,
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 48c79fb9df2..61c27ff57d8 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -90,6 +90,13 @@ namespace WorldPackets
enum class ConnectToSerial : uint32;
}
+ namespace Bank
+ {
+ class AutoBankItem;
+ class AutoStoreBankItem;
+ class BuyBankSlot;
+ }
+
namespace BlackMarket
{
class BlackMarketOpen;
@@ -289,6 +296,7 @@ namespace WorldPackets
class MoveTeleportAck;
class MovementAck;
class MovementSpeedAck;
+ class SetActiveMover;
}
namespace NPC
@@ -913,7 +921,7 @@ class WorldSession
void HandleMoveWorldportAckOpcode(); // for server-side calls
void HandleMovementOpcodes(WorldPackets::Movement::ClientPlayerMovement& packet);
- void HandleSetActiveMoverOpcode(WorldPacket& recvData);
+ void HandleSetActiveMoverOpcode(WorldPackets::Movement::SetActiveMover& packet);
void HandleMoveNotActiveMover(WorldPacket& recvData);
void HandleDismissControlledVehicle(WorldPacket& recvData);
void HandleRequestVehicleExit(WorldPacket& recvData);
@@ -1002,7 +1010,6 @@ class WorldSession
void HandleTabardVendorActivateOpcode(WorldPackets::NPC::Hello& packet);
void HandleBankerActivateOpcode(WorldPackets::NPC::Hello& packet);
- void HandleBuyBankSlotOpcode(WorldPacket& recvPacket);
void HandleTrainerListOpcode(WorldPackets::NPC::Hello& packet);
void HandleTrainerBuySpellOpcode(WorldPacket& recvPacket);
void HandlePetitionShowList(WorldPackets::Petition::PetitionShowList& packet);
@@ -1046,6 +1053,11 @@ class WorldSession
void HandleAuctionPlaceBid(WorldPacket& recvData);
void HandleAuctionListPendingSales(WorldPacket& recvData);
+ // Bank
+ void HandleAutoBankItemOpcode(WorldPackets::Bank::AutoBankItem& packet);
+ void HandleAutoStoreBankItemOpcode(WorldPackets::Bank::AutoStoreBankItem& packet);
+ void HandleBuyBankSlotOpcode(WorldPackets::Bank::BuyBankSlot& packet);
+
// Black Market
void HandleBlackMarketOpen(WorldPackets::BlackMarket::BlackMarketOpen& packet);
@@ -1075,8 +1087,6 @@ class WorldSession
void HandleAutoEquipItemSlotOpcode(WorldPacket& recvPacket);
void HandleSwapItem(WorldPackets::Item::SwapItem& swapItem);
void HandleBuybackItem(WorldPackets::Item::BuyBackItem& packet);
- void HandleAutoBankItemOpcode(WorldPacket& recvPacket);
- void HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket);
void HandleWrapItemOpcode(WorldPacket& recvPacket);
void HandleAttackSwingOpcode(WorldPackets::Combat::AttackSwing& packet);
diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h
index 112893fbc75..7e60ac9b5da 100644
--- a/src/server/game/Spells/Auras/SpellAuraDefines.h
+++ b/src/server/game/Spells/Auras/SpellAuraDefines.h
@@ -535,7 +535,12 @@ enum AuraType
SPELL_AURA_476 = 476,
SPELL_AURA_477 = 477,
SPELL_AURA_478 = 478,
- TOTAL_AURAS = 479 // 6.0.3
+ SPELL_AURA_479 = 479,
+ SPELL_AURA_480 = 480,
+ SPELL_AURA_481 = 481,
+ SPELL_AURA_482 = 482,
+ SPELL_AURA_483 = 483,
+ TOTAL_AURAS = 484
};
enum AuraObjectType
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index dd90e10f7bb..e35b1a0a6b1 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -539,6 +539,11 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //476
&AuraEffect::HandleNULL, //477
&AuraEffect::HandleNULL, //478
+ &AuraEffect::HandleNULL, //479
+ &AuraEffect::HandleNULL, //480
+ &AuraEffect::HandleNULL, //481
+ &AuraEffect::HandleNULL, //482
+ &AuraEffect::HandleNULL, //483
};
AuraEffect::AuraEffect(Aura* base, uint32 effIndex, int32 *baseAmount, Unit* caster) :
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index a5afe74728f..d464e305462 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -317,6 +317,12 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //242 SPELL_EFFECT_242
&Spell::EffectNULL, //243 SPELL_EFFECT_243
&Spell::EffectNULL, //244 SPELL_EFFECT_244
+ &Spell::EffectNULL, //244 SPELL_EFFECT_245
+ &Spell::EffectNULL, //244 SPELL_EFFECT_246
+ &Spell::EffectNULL, //244 SPELL_EFFECT_247
+ &Spell::EffectNULL, //244 SPELL_EFFECT_248
+ &Spell::EffectNULL, //244 SPELL_EFFECT_249
+ &Spell::EffectNULL, //244 SPELL_EFFECT_250
};
void Spell::EffectNULL(SpellEffIndex /*effIndex*/)
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index 13026b56fc2..79d3cea0868 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -64,6 +64,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/src/server/shared/Logging
${CMAKE_SOURCE_DIR}/src/server/shared/Packets
${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Updater
${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
${CMAKE_SOURCE_DIR}/src/server/ipc
${CMAKE_SOURCE_DIR}/src/server/collision
diff --git a/src/server/scripts/Kalimdor/CMakeLists.txt b/src/server/scripts/Kalimdor/CMakeLists.txt
index d5e445230bd..8c3f3216e91 100644
--- a/src/server/scripts/Kalimdor/CMakeLists.txt
+++ b/src/server/scripts/Kalimdor/CMakeLists.txt
@@ -92,7 +92,6 @@ set(scripts_STAT_SRCS
Kalimdor/RuinsOfAhnQiraj/boss_moam.cpp
Kalimdor/RuinsOfAhnQiraj/ruins_of_ahnqiraj.h
Kalimdor/RuinsOfAhnQiraj/boss_kurinnaxx.cpp
- Kalimdor/zone_mulgore.cpp
Kalimdor/zone_bloodmyst_isle.cpp
Kalimdor/zone_thunder_bluff.cpp
Kalimdor/zone_azshara.cpp
diff --git a/src/server/scripts/Kalimdor/zone_mulgore.cpp b/src/server/scripts/Kalimdor/zone_mulgore.cpp
deleted file mode 100644
index b6db431d0b2..00000000000
--- a/src/server/scripts/Kalimdor/zone_mulgore.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
- *
- * 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 2 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/>.
- */
-
-/* ScriptData
-SDName: Mulgore
-SD%Complete: 100
-SDComment: Support for quest: 11129, 861
-SDCategory: Mulgore
-EndScriptData */
-
-/* ContentData
-npc_kyle_frenzied
-EndContentData */
-
-#include "ScriptMgr.h"
-#include "ScriptedCreature.h"
-#include "ScriptedGossip.h"
-#include "Player.h"
-#include "SpellInfo.h"
-
-/*#####
-# npc_kyle_frenzied
-######*/
-
-enum KyleFrenzied
-{
- EMOTE_SEE_LUNCH = 0,
- EMOTE_EAT_LUNCH = 1,
- EMOTE_DANCE = 2,
-
- SPELL_LUNCH = 42222,
- NPC_KYLE_FRENZIED = 23616,
- NPC_KYLE_FRIENDLY = 23622,
- POINT_ID = 1
-};
-
-class npc_kyle_frenzied : public CreatureScript
-{
-public:
- npc_kyle_frenzied() : CreatureScript("npc_kyle_frenzied") { }
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new npc_kyle_frenziedAI (creature);
- }
-
- struct npc_kyle_frenziedAI : public ScriptedAI
- {
- npc_kyle_frenziedAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- }
-
- void Initialize()
- {
- EventActive = false;
- IsMovingToLunch = false;
- PlayerGUID.Clear();
- EventTimer = 5000;
- EventPhase = 0;
- }
-
- bool EventActive;
- bool IsMovingToLunch;
- ObjectGuid PlayerGUID;
- uint32 EventTimer;
- uint8 EventPhase;
-
- void Reset() override
- {
- Initialize();
-
- if (me->GetEntry() == NPC_KYLE_FRIENDLY)
- me->UpdateEntry(NPC_KYLE_FRENZIED);
- }
-
- void SpellHit(Unit* Caster, SpellInfo const* Spell)
- {
- if (!me->GetVictim() && !EventActive && Spell->Id == SPELL_LUNCH)
- {
- if (Caster->GetTypeId() == TYPEID_PLAYER)
- PlayerGUID = Caster->GetGUID();
-
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
- {
- me->GetMotionMaster()->MovementExpired();
- me->GetMotionMaster()->MoveIdle();
- me->StopMoving();
- }
-
- EventActive = true;
- Talk(EMOTE_SEE_LUNCH);
- me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_CREATURE_SPECIAL);
- }
- }
-
- void MovementInform(uint32 type, uint32 pointId) override
- {
- if (type != POINT_MOTION_TYPE || !EventActive)
- return;
-
- if (pointId == POINT_ID)
- IsMovingToLunch = false;
- }
-
- void UpdateAI(uint32 diff) override
- {
- if (EventActive)
- {
- if (IsMovingToLunch)
- return;
-
- if (EventTimer <= diff)
- {
- EventTimer = 5000;
- ++EventPhase;
-
- switch (EventPhase)
- {
- case 1:
- if (Unit* unit = ObjectAccessor::GetUnit(*me, PlayerGUID))
- {
- if (GameObject* go = unit->GetGameObject(SPELL_LUNCH))
- {
- IsMovingToLunch = true;
- me->GetMotionMaster()->MovePoint(POINT_ID, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
- }
- }
- break;
- case 2:
- Talk(EMOTE_EAT_LUNCH);
- me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_USE_STANDING);
- break;
- case 3:
- if (Player* unit = ObjectAccessor::GetPlayer(*me, PlayerGUID))
- unit->TalkedToCreature(me->GetEntry(), me->GetGUID());
-
- me->UpdateEntry(NPC_KYLE_FRIENDLY);
- break;
- case 4:
- EventTimer = 30000;
- Talk(EMOTE_DANCE);
- me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_DANCESPECIAL);
- break;
- case 5:
- me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
- Reset();
- me->GetMotionMaster()->Clear();
- break;
- }
- }
- else
- EventTimer -= diff;
- }
- }
- };
-
-};
-
-void AddSC_mulgore()
-{
- new npc_kyle_frenzied();
-}
diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
index 232261df951..1c9d5cf2c62 100644
--- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp
+++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
@@ -654,6 +654,7 @@ public:
if (quest->GetQuestId() == QUEST_ESCAPE)
{
creature->setFaction(FACTION_RATCHET);
+ creature->AI()->Talk(SAY_START);
if (npc_escortAI* pEscortAI = CAST_AI(npc_wizzlecrank_shredder::npc_wizzlecrank_shredderAI, creature->AI()))
pEscortAI->Start(true, false, player->GetGUID());
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp
index 861956d435e..b751c1db10d 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp
@@ -255,12 +255,6 @@ class instance_trial_of_the_crusader : public InstanceMapScript
if (GetBossState(BOSS_VALKIRIES) == SPECIAL)
state = DONE;
break;
- case DONE:
- if (instance->GetPlayers().getFirst()->GetSource()->GetTeam() == ALLIANCE)
- EventStage = 4020;
- else
- EventStage = 4030;
- break;
default:
break;
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp
index 16526b694e9..974bd4672f1 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp
@@ -133,7 +133,7 @@ class npc_announcer_toc10 : public CreatureScript
char const* _message = "We are ready!";
- if (player->IsInCombat() || instance->IsEncounterInProgress() || instance->GetData(TYPE_EVENT))
+ if (player->IsInCombat() || instance->IsEncounterInProgress())
return true;
uint8 i = 0;
@@ -199,17 +199,11 @@ class npc_announcer_toc10 : public CreatureScript
}
else if (instance->GetBossState(BOSS_LICH_KING) != DONE)
{
- if (GameObject* floor = ObjectAccessor::GetGameObject(*player, instance->GetGuidData(GO_ARGENT_COLISEUM_FLOOR)))
- floor->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED);
-
- creature->CastSpell(creature, SPELL_CORPSE_TELEPORT, false);
- creature->CastSpell(creature, SPELL_DESTROY_FLOOR_KNOCKUP, false);
-
- if (!ObjectAccessor::GetCreature(*creature, instance->GetGuidData(NPC_ANUBARAK)))
- creature->SummonCreature(NPC_ANUBARAK, AnubarakLoc[0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME);
-
- if (creature->IsVisible())
- creature->SetVisible(false);
+ if (creature->GetMap()->GetPlayers().getFirst()->GetSource()->GetTeam() == ALLIANCE)
+ instance->SetData(TYPE_EVENT, 4020);
+ else
+ instance->SetData(TYPE_EVENT, 4030);
+ instance->SetBossState(BOSS_LICH_KING, NOT_STARTED);
}
creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
return true;
diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp
index f6b9f9fb6be..b58d9419e95 100644
--- a/src/server/scripts/Northrend/zone_borean_tundra.cpp
+++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp
@@ -29,7 +29,6 @@ npc_corastrasza
npc_sinkhole_kill_credit
npc_khunok_the_behemoth
npc_nerubar_victim
-npc_keristrasza
npc_nesingwary_trapper
npc_lurgglbr
npc_nexus_drake_hatchling
@@ -223,48 +222,6 @@ public:
};
/*######
-## npc_keristrasza
-######*/
-
-enum Keristrasza
-{
- SPELL_TELEPORT_TO_SARAGOSA = 46772
-};
-
-#define GOSSIP_HELLO_KERI "I am prepared to face Saragosa!"
-
-class npc_keristrasza : public CreatureScript
-{
-public:
- npc_keristrasza() : CreatureScript("npc_keristrasza") { }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- if (player->GetQuestStatus(11957) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_KERI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
-
- player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
-
- return true;
- }
-
- bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF + 1)
- {
- player->CLOSE_GOSSIP_MENU();
- player->CastSpell(player, SPELL_TELEPORT_TO_SARAGOSA, true);
- }
-
- return true;
- }
-};
-
-/*######
## npc_corastrasza
######*/
@@ -2488,7 +2445,6 @@ void AddSC_borean_tundra()
{
new npc_sinkhole_kill_credit();
new npc_khunok_the_behemoth();
- new npc_keristrasza();
new npc_corastrasza();
new npc_iruk();
new npc_nerubar_victim();
diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp
index 287e3591f5d..f3c8396832c 100644
--- a/src/server/scripts/Spells/spell_quest.cpp
+++ b/src/server/scripts/Spells/spell_quest.cpp
@@ -2495,6 +2495,36 @@ public:
}
};
+class spell_q12414_hand_over_reins : public SpellScriptLoader
+{
+ public:
+ spell_q12414_hand_over_reins() : SpellScriptLoader("spell_q12414_hand_over_reins") { }
+
+ class spell_q12414_hand_over_reins_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_q12414_hand_over_reins_SpellScript);
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ Creature* caster = GetCaster()->ToCreature();
+ GetHitUnit()->ExitVehicle();
+
+ if (caster)
+ caster->DespawnOrUnsummon();
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_q12414_hand_over_reins_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_q12414_hand_over_reins_SpellScript();
+ }
+};
+
void AddSC_quest_spell_scripts()
{
new spell_q55_sacred_cleansing();
@@ -2555,4 +2585,5 @@ void AddSC_quest_spell_scripts()
new spell_q10929_fumping();
new spell_q28813_get_our_boys_back_dummy();
new spell_q28813_set_health_random();
+ new spell_q12414_hand_over_reins();
}
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt
index b6ac8b03099..657b3fde349 100644
--- a/src/server/shared/CMakeLists.txt
+++ b/src/server/shared/CMakeLists.txt
@@ -22,6 +22,7 @@ file(GLOB_RECURSE sources_Networking Networking/*.cpp Networking/*.h)
file(GLOB_RECURSE sources_Packets Packets/*.cpp Packets/*.h)
file(GLOB_RECURSE sources_Realm Realm/*.cpp Realm/*.h)
file(GLOB_RECURSE sources_Threading Threading/*.cpp Threading/*.h)
+file(GLOB_RECURSE sources_Updater Updater/*.cpp Updater/*.h)
file(GLOB_RECURSE sources_Utilities Utilities/*.cpp Utilities/*.h)
file(GLOB sources_localdir *.cpp *.h)
@@ -53,6 +54,7 @@ set(shared_STAT_SRCS
${sources_Packets}
${sources_Realm}
${sources_Threading}
+ ${sources_Updater}
${sources_Utilities}
${sources_localdir}
)
@@ -63,6 +65,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/cppformat
${CMAKE_SOURCE_DIR}/dep/utf8cpp
+ ${CMAKE_SOURCE_DIR}/dep/process
${CMAKE_SOURCE_DIR}/src/server
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/Configuration
@@ -77,6 +80,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/Realm
${CMAKE_CURRENT_SOURCE_DIR}/Threading
${CMAKE_CURRENT_SOURCE_DIR}/Utilities
+ ${CMAKE_CURRENT_SOURCE_DIR}/Updater
${CMAKE_SOURCE_DIR}/src/server/game/Entities/Object
${MYSQL_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR}
diff --git a/src/server/shared/Database/DatabaseLoader.cpp b/src/server/shared/Database/DatabaseLoader.cpp
new file mode 100644
index 00000000000..25c400fdfa8
--- /dev/null
+++ b/src/server/shared/Database/DatabaseLoader.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#include "DatabaseLoader.h"
+#include "DBUpdater.h"
+#include "Config.h"
+
+#include <mysqld_error.h>
+
+DatabaseLoader::DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask)
+ : _logger(logger), _autoSetup(sConfigMgr->GetBoolDefault("Updates.AutoSetup", true)),
+ _updateFlags(sConfigMgr->GetIntDefault("Updates.EnableDatabases", defaultUpdateMask))
+{
+}
+
+template <class T>
+DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name)
+{
+ bool const updatesEnabledForThis = DBUpdater<T>::IsEnabled(_updateFlags);
+
+ _open.push(std::make_pair([this, name, updatesEnabledForThis, &pool]() -> bool
+ {
+ std::string const dbString = sConfigMgr->GetStringDefault(name + "DatabaseInfo", "");
+ if (dbString.empty())
+ {
+ TC_LOG_ERROR(_logger.c_str(), "Database %s not specified in configuration file!", name.c_str());
+ return false;
+ }
+
+ uint8 const asyncThreads = uint8(sConfigMgr->GetIntDefault(name + "Database.WorkerThreads", 1));
+ if (asyncThreads < 1 || asyncThreads > 32)
+ {
+ TC_LOG_ERROR(_logger.c_str(), "%s database: invalid number of worker threads specified. "
+ "Please pick a value between 1 and 32.", name.c_str());
+ return false;
+ }
+
+ uint8 const synchThreads = uint8(sConfigMgr->GetIntDefault(name + "Database.SynchThreads", 1));
+
+ pool.SetConnectionInfo(dbString, asyncThreads, synchThreads);
+ if (uint32 error = pool.Open())
+ {
+ // Database does not exist
+ if ((error == ER_BAD_DB_ERROR) && updatesEnabledForThis && _autoSetup)
+ {
+ // Try to create the database and connect again if auto setup is enabled
+ if (DBUpdater<T>::Create(pool) && (!pool.Open()))
+ error = 0;
+ }
+
+ // If the error wasn't handled quit
+ if (error)
+ {
+ TC_LOG_ERROR("sql.driver", "\nDatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile "
+ "for specific errors. Read wiki at http://collab.kpsn.org/display/tc/TrinityCore+Home", name.c_str());
+
+ return false;
+ }
+ }
+ return true;
+ },
+ [&pool]()
+ {
+ pool.Close();
+ }));
+
+ // Populate and update only if updates are enabled for this pool
+ if (updatesEnabledForThis)
+ {
+ _populate.push([this, name, &pool]() -> bool
+ {
+ if (!DBUpdater<T>::Populate(pool))
+ {
+ TC_LOG_ERROR(_logger.c_str(), "Could not populate the %s database, see log for details.", name.c_str());
+ return false;
+ }
+ return true;
+ });
+
+ _update.push([this, name, &pool]() -> bool
+ {
+ if (!DBUpdater<T>::Update(pool))
+ {
+ TC_LOG_ERROR(_logger.c_str(), "Could not update the %s database, see log for details.", name.c_str());
+ return false;
+ }
+ return true;
+ });
+ }
+
+ _prepare.push([this, name, &pool]() -> bool
+ {
+ if (!pool.PrepareStatements())
+ {
+ TC_LOG_ERROR(_logger.c_str(), "Could not prepare statements of the %s database, see log for details.", name.c_str());
+ return false;
+ }
+ return true;
+ });
+
+ return *this;
+}
+
+bool DatabaseLoader::Load()
+{
+ if (!OpenDatabases())
+ return false;
+
+ if (!PopulateDatabases())
+ return false;
+
+ if (!UpdateDatabases())
+ return false;
+
+ if (!PrepareStatements())
+ return false;
+
+ return true;
+}
+
+bool DatabaseLoader::OpenDatabases()
+{
+ while (!_open.empty())
+ {
+ std::pair<Predicate, std::function<void()>> const load = _open.top();
+ if (load.first())
+ _close.push(load.second);
+ else
+ {
+ // Close all loaded databases
+ while (!_close.empty())
+ {
+ _close.top()();
+ _close.pop();
+ }
+ return false;
+ }
+
+ _open.pop();
+ }
+ return true;
+}
+
+// Processes the elements of the given stack until a predicate returned false.
+bool DatabaseLoader::Process(std::stack<Predicate>& stack)
+{
+ while (!stack.empty())
+ {
+ if (!stack.top()())
+ return false;
+
+ stack.pop();
+ }
+ return true;
+}
+
+bool DatabaseLoader::PopulateDatabases()
+{
+ return Process(_populate);
+}
+
+bool DatabaseLoader::UpdateDatabases()
+{
+ return Process(_update);
+}
+
+bool DatabaseLoader::PrepareStatements()
+{
+ return Process(_prepare);
+}
+
+template
+DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>& pool, std::string const& name);
+template
+DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>& pool, std::string const& name);
+template
+DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>& pool, std::string const& name);
+template
+DatabaseLoader& DatabaseLoader::AddDatabase<HotfixDatabaseConnection>(DatabaseWorkerPool<HotfixDatabaseConnection>& pool, std::string const& name);
diff --git a/src/server/shared/Database/DatabaseLoader.h b/src/server/shared/Database/DatabaseLoader.h
new file mode 100644
index 00000000000..3bbf7e75771
--- /dev/null
+++ b/src/server/shared/Database/DatabaseLoader.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#ifndef DatabaseLoader_h__
+#define DatabaseLoader_h__
+
+#include "DatabaseWorkerPool.h"
+#include "DatabaseEnv.h"
+
+#include <stack>
+#include <functional>
+
+// A helper class to initiate all database worker pools,
+// handles updating, delays preparing of statements and cleans up on failure.
+class DatabaseLoader
+{
+public:
+ DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask);
+
+ // Register a database to the loader (lazy implemented)
+ template <class T>
+ DatabaseLoader& AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name);
+
+ // Load all databases
+ bool Load();
+
+ enum DatabaseTypeFlags
+ {
+ DATABASE_NONE = 0,
+
+ DATABASE_LOGIN = 1,
+ DATABASE_CHARACTER = 2,
+ DATABASE_WORLD = 4,
+ DATABASE_HOTFIX = 8,
+
+ DATABASE_MASK_ALL = DATABASE_LOGIN | DATABASE_CHARACTER | DATABASE_WORLD | DATABASE_HOTFIX
+ };
+
+private:
+ bool OpenDatabases();
+ bool PopulateDatabases();
+ bool UpdateDatabases();
+ bool PrepareStatements();
+
+ using Predicate = std::function<bool()>;
+
+ static bool Process(std::stack<Predicate>& stack);
+
+ std::string const _logger;
+ bool const _autoSetup;
+ uint32 const _updateFlags;
+
+ std::stack<std::pair<Predicate, std::function<void()>>> _open;
+ std::stack<std::function<void()>> _close;
+ std::stack<Predicate> _populate, _update, _prepare;
+};
+
+#endif // DatabaseLoader_h__
diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h
index f1c6a7acbf5..6210986ff8b 100644
--- a/src/server/shared/Database/DatabaseWorkerPool.h
+++ b/src/server/shared/Database/DatabaseWorkerPool.h
@@ -30,6 +30,7 @@
#include "AdhocStatement.h"
#include <mysqld_error.h>
+#include <memory>
#define MIN_MYSQL_SERVER_VERSION 50100u
#define MIN_MYSQL_CLIENT_VERSION 50100u
@@ -57,9 +58,9 @@ class DatabaseWorkerPool
public:
/* Activity state */
- DatabaseWorkerPool() : _connectionInfo(NULL)
+ DatabaseWorkerPool() : _queue(new ProducerConsumerQueue<SQLOperation*>()),
+ _async_threads(0), _synch_threads(0)
{
- _queue = new ProducerConsumerQueue<SQLOperation*>();
memset(_connectionCount, 0, sizeof(_connectionCount));
_connections.resize(IDX_SIZE);
@@ -70,31 +71,37 @@ class DatabaseWorkerPool
~DatabaseWorkerPool()
{
_queue->Cancel();
+ }
- delete _queue;
+ void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads)
+ {
+ _connectionInfo.reset(new MySQLConnectionInfo(infoString));
- delete _connectionInfo;
+ _async_threads = asyncThreads;
+ _synch_threads = synchThreads;
}
- bool Open(const std::string& infoString, uint8 async_threads, uint8 synch_threads)
+ uint32 Open()
{
- _connectionInfo = new MySQLConnectionInfo(infoString);
+ WPFatal(_connectionInfo.get(), "Connection info was not set!");
TC_LOG_INFO("sql.driver", "Opening DatabasePool '%s'. Asynchronous connections: %u, synchronous connections: %u.",
- GetDatabaseName(), async_threads, synch_threads);
+ GetDatabaseName(), _async_threads, _synch_threads);
- bool res = OpenConnections(IDX_ASYNC, async_threads);
+ uint32 error = OpenConnections(IDX_ASYNC, _async_threads);
- if (!res)
- return res;
+ if (error)
+ return error;
- res = OpenConnections(IDX_SYNCH, synch_threads);
+ error = OpenConnections(IDX_SYNCH, _synch_threads);
- if (res)
+ if (!error)
+ {
TC_LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. %u total connections running.", GetDatabaseName(),
(_connectionCount[IDX_SYNCH] + _connectionCount[IDX_ASYNC]));
+ }
- return res;
+ return error;
}
void Close()
@@ -120,6 +127,32 @@ class DatabaseWorkerPool
TC_LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
}
+ //! Prepares all prepared statements
+ bool PrepareStatements()
+ {
+ for (uint8 i = 0; i < IDX_SIZE; ++i)
+ for (uint32 c = 0; c < _connectionCount[i]; ++c)
+ {
+ T* t = _connections[i][c];
+ t->LockIfReady();
+ if (!t->PrepareStatements())
+ {
+ t->Unlock();
+ Close();
+ return false;
+ }
+ else
+ t->Unlock();
+ }
+
+ return true;
+ }
+
+ inline MySQLConnectionInfo const* GetConnectionInfo() const
+ {
+ return _connectionInfo.get();
+ }
+
/**
Delayed one-way statement methods.
*/
@@ -461,7 +494,7 @@ class DatabaseWorkerPool
}
private:
- bool OpenConnections(InternalIndex type, uint8 numConnections)
+ uint32 OpenConnections(InternalIndex type, uint8 numConnections)
{
_connections[type].resize(numConnections);
for (uint8 i = 0; i < numConnections; ++i)
@@ -469,7 +502,7 @@ class DatabaseWorkerPool
T* t;
if (type == IDX_ASYNC)
- t = new T(_queue, *_connectionInfo);
+ t = new T(_queue.get(), *_connectionInfo);
else if (type == IDX_SYNCH)
t = new T(*_connectionInfo);
else
@@ -478,35 +511,32 @@ class DatabaseWorkerPool
_connections[type][i] = t;
++_connectionCount[type];
- bool res = t->Open();
+ uint32 error = t->Open();
- if (res)
+ if (!error)
{
if (mysql_get_server_version(t->GetHandle()) < MIN_MYSQL_SERVER_VERSION)
{
TC_LOG_ERROR("sql.driver", "TrinityCore does not support MySQL versions below 5.1");
- res = false;
+ error = 1;
}
}
// Failed to open a connection or invalid version, abort and cleanup
- if (!res)
+ if (error)
{
- TC_LOG_ERROR("sql.driver", "DatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile "
- "for specific errors. Read wiki at http://collab.kpsn.org/display/tc/TrinityCore+Home", GetDatabaseName());
-
while (_connectionCount[type] != 0)
{
T* t = _connections[type][i--];
delete t;
--_connectionCount[type];
}
-
- return false;
+ return error;
}
}
- return true;
+ // Everything is fine
+ return 0;
}
unsigned long EscapeString(char *to, const char *from, unsigned long length)
@@ -546,10 +576,13 @@ class DatabaseWorkerPool
return _connectionInfo->database.c_str();
}
- ProducerConsumerQueue<SQLOperation*>* _queue; //! Queue shared by async worker threads.
- std::vector< std::vector<T*> > _connections;
- uint32 _connectionCount[2]; //! Counter of MySQL connections;
- MySQLConnectionInfo* _connectionInfo;
+ //! Queue shared by async worker threads.
+ std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue;
+ std::vector<std::vector<T*>> _connections;
+ //! Counter of MySQL connections;
+ uint32 _connectionCount[IDX_SIZE];
+ std::unique_ptr<MySQLConnectionInfo> _connectionInfo;
+ uint8 _async_threads, _synch_threads;
};
#endif
diff --git a/src/server/shared/Database/MySQLConnection.cpp b/src/server/shared/Database/MySQLConnection.cpp
index 1a9f973d47b..1fa3f01a5e1 100644
--- a/src/server/shared/Database/MySQLConnection.cpp
+++ b/src/server/shared/Database/MySQLConnection.cpp
@@ -72,7 +72,7 @@ void MySQLConnection::Close()
delete this;
}
-bool MySQLConnection::Open()
+uint32 MySQLConnection::Open()
{
MYSQL *mysqlInit;
mysqlInit = mysql_init(NULL);
@@ -137,13 +137,13 @@ bool MySQLConnection::Open()
// set connection properties to UTF8 to properly handle locales for different
// server configs - core sends data in UTF8, so MySQL must expect UTF8 too
mysql_set_character_set(m_Mysql, "utf8");
- return PrepareStatements();
+ return 0;
}
else
{
- TC_LOG_ERROR("sql.sql", "Could not connect to MySQL database at %s: %s\n", m_connectionInfo.host.c_str(), mysql_error(mysqlInit));
+ TC_LOG_ERROR("sql.sql", "Could not connect to MySQL database at %s: %s", m_connectionInfo.host.c_str(), mysql_error(mysqlInit));
mysql_close(mysqlInit);
- return false;
+ return mysql_errno(mysqlInit);
}
}
diff --git a/src/server/shared/Database/MySQLConnection.h b/src/server/shared/Database/MySQLConnection.h
index d486f5b4679..78d8d2fb5dd 100644
--- a/src/server/shared/Database/MySQLConnection.h
+++ b/src/server/shared/Database/MySQLConnection.h
@@ -72,9 +72,11 @@ class MySQLConnection
MySQLConnection(ProducerConsumerQueue<SQLOperation*>* queue, MySQLConnectionInfo& connInfo); //! Constructor for asynchronous connections.
virtual ~MySQLConnection();
- virtual bool Open();
+ virtual uint32 Open();
void Close();
+ bool PrepareStatements();
+
public:
bool Execute(const char* sql);
bool Execute(PreparedStatement* stmt);
@@ -111,7 +113,6 @@ class MySQLConnection
MySQLPreparedStatement* GetPreparedStatement(uint32 index);
void PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags);
- bool PrepareStatements();
virtual void DoPrepareStatements() = 0;
protected:
diff --git a/src/server/shared/Updater/DBUpdater.cpp b/src/server/shared/Updater/DBUpdater.cpp
new file mode 100644
index 00000000000..156c2a8fdd4
--- /dev/null
+++ b/src/server/shared/Updater/DBUpdater.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#include "DBUpdater.h"
+#include "Log.h"
+#include "revision.h"
+#include "UpdateFetcher.h"
+#include "DatabaseLoader.h"
+#include "Config.h"
+
+#include <fstream>
+#include <iostream>
+#include <unordered_map>
+#include <boost/process.hpp>
+#include <boost/process/mitigate.hpp>
+#include <boost/iostreams/device/file_descriptor.hpp>
+#include <boost/system/system_error.hpp>
+
+using namespace boost::process;
+using namespace boost::process::initializers;
+using namespace boost::iostreams;
+
+template<class T>
+std::string DBUpdater<T>::GetSourceDirectory()
+{
+ std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", "");
+ if (!entry.empty())
+ return entry;
+ else
+ return _SOURCE_DIRECTORY;
+}
+
+template<class T>
+std::string DBUpdater<T>::GetMySqlCli()
+{
+ std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", "");
+ if (!entry.empty())
+ return entry;
+ else
+ return _MYSQL_EXECUTABLE;
+}
+
+// Auth Database
+template<>
+std::string DBUpdater<LoginDatabaseConnection>::GetConfigEntry()
+{
+ return "Updates.Auth";
+}
+
+template<>
+std::string DBUpdater<LoginDatabaseConnection>::GetTableName()
+{
+ return "Auth";
+}
+
+template<>
+std::string DBUpdater<LoginDatabaseConnection>::GetBaseFile()
+{
+ return DBUpdater<LoginDatabaseConnection>::GetSourceDirectory() + "/sql/base/auth_database.sql";
+}
+
+template<>
+bool DBUpdater<LoginDatabaseConnection>::IsEnabled(uint32 const updateMask)
+{
+ // This way silences warnings under msvc
+ return (updateMask & DatabaseLoader::DATABASE_LOGIN) ? true : false;
+}
+
+// World Database
+template<>
+std::string DBUpdater<WorldDatabaseConnection>::GetConfigEntry()
+{
+ return "Updates.World";
+}
+
+template<>
+std::string DBUpdater<WorldDatabaseConnection>::GetTableName()
+{
+ return "World";
+}
+
+template<>
+std::string DBUpdater<WorldDatabaseConnection>::GetBaseFile()
+{
+ return _FULL_DATABASE;
+}
+
+template<>
+bool DBUpdater<WorldDatabaseConnection>::IsEnabled(uint32 const updateMask)
+{
+ // This way silences warnings under msvc
+ return (updateMask & DatabaseLoader::DATABASE_WORLD) ? true : false;
+}
+
+template<>
+BaseLocation DBUpdater<WorldDatabaseConnection>::GetBaseLocationType()
+{
+ return LOCATION_DOWNLOAD;
+}
+
+// Character Database
+template<>
+std::string DBUpdater<CharacterDatabaseConnection>::GetConfigEntry()
+{
+ return "Updates.Character";
+}
+
+template<>
+std::string DBUpdater<CharacterDatabaseConnection>::GetTableName()
+{
+ return "Character";
+}
+
+template<>
+std::string DBUpdater<CharacterDatabaseConnection>::GetBaseFile()
+{
+ return DBUpdater<CharacterDatabaseConnection>::GetSourceDirectory() + "/sql/base/characters_database.sql";
+}
+
+template<>
+bool DBUpdater<CharacterDatabaseConnection>::IsEnabled(uint32 const updateMask)
+{
+ // This way silences warnings under msvc
+ return (updateMask & DatabaseLoader::DATABASE_CHARACTER) ? true : false;
+}
+
+// Hotfix Database
+template<>
+std::string DBUpdater<HotfixDatabaseConnection>::GetConfigEntry()
+{
+ return "Updates.Hotfix";
+}
+
+template<>
+std::string DBUpdater<HotfixDatabaseConnection>::GetTableName()
+{
+ return "Hotfixes";
+}
+
+template<>
+std::string DBUpdater<HotfixDatabaseConnection>::GetBaseFile()
+{
+ return _HOTFIXES_DATABASE;
+}
+
+template<>
+bool DBUpdater<HotfixDatabaseConnection>::IsEnabled(uint32 const updateMask)
+{
+ // This way silences warnings under msvc
+ return (updateMask & DatabaseLoader::DATABASE_HOTFIX) ? true : false;
+}
+
+template<>
+BaseLocation DBUpdater<HotfixDatabaseConnection>::GetBaseLocationType()
+{
+ return LOCATION_DOWNLOAD;
+}
+
+// All
+template<class T>
+BaseLocation DBUpdater<T>::GetBaseLocationType()
+{
+ return LOCATION_REPOSITORY;
+}
+
+template<class T>
+bool DBUpdater<T>::CheckExecutable()
+{
+ DBUpdater<T>::Path const exe(DBUpdater<T>::GetMySqlCli());
+ if (!exists(exe))
+ {
+ // Check for mysql in path
+ std::vector<std::string> args = {"--version"};
+ uint32 ret;
+ try
+ {
+ child c = execute(run_exe("mysql"), set_args(args), throw_on_error(), close_stdout());
+ ret = wait_for_exit(c);
+ }
+ catch (boost::system::system_error&)
+ {
+ ret = EXIT_FAILURE;
+ }
+
+ if (ret == EXIT_FAILURE)
+ {
+ TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\', correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
+ absolute(exe).generic_string().c_str());
+
+ return false;
+ }
+ }
+ return true;
+}
+
+template<class T>
+bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
+{
+ TC_LOG_INFO("sql.updates", "Database \"%s\" does not exist, do you want to create it? [yes (default) / no]: ",
+ pool.GetConnectionInfo()->database.c_str());
+
+ std::string answer;
+ std::getline(std::cin, answer);
+ if (!answer.empty() && !(answer.substr(0, 1) == "y"))
+ return false;
+
+ TC_LOG_INFO("sql.updates", "Creating database \"%s\"...", pool.GetConnectionInfo()->database.c_str());
+
+ // Path of temp file
+ static Path const temp("create_table.sql");
+
+ // Create temporary query to use external mysql cli
+ std::ofstream file(temp.generic_string());
+ if (!file.is_open())
+ {
+ TC_LOG_FATAL("sql.updates", "Failed to create temporary query file \"%s\"!", temp.generic_string().c_str());
+ return false;
+ }
+
+ file << "CREATE DATABASE `" << pool.GetConnectionInfo()->database << "` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci\n\n";
+
+ file.close();
+
+ try
+ {
+ DBUpdater<T>::ApplyFile(pool, pool.GetConnectionInfo()->host, pool.GetConnectionInfo()->user, pool.GetConnectionInfo()->password,
+ pool.GetConnectionInfo()->port_or_socket, "", temp);
+ }
+ catch (UpdateException&)
+ {
+ TC_LOG_FATAL("sql.updates", "Failed to create database %s! Has the user `CREATE` priviliges?", pool.GetConnectionInfo()->database.c_str());
+ boost::filesystem::remove(temp);
+ return false;
+ }
+
+ TC_LOG_INFO("sql.updates", "Done.");
+ boost::filesystem::remove(temp);
+ return true;
+}
+
+template<class T>
+bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
+{
+ if (!DBUpdater<T>::CheckExecutable())
+ return false;
+
+ TC_LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str());
+
+ Path const sourceDirectory(GetSourceDirectory());
+
+ if (!is_directory(sourceDirectory))
+ {
+ TC_LOG_ERROR("sql.updates", "DBUpdater: Given source directory %s does not exist, skipped!", sourceDirectory.generic_string().c_str());
+ return false;
+ }
+
+ UpdateFetcher updateFetcher(sourceDirectory, [&](std::string const& query) { DBUpdater<T>::Apply(pool, query); },
+ [&](Path const& file) { DBUpdater<T>::ApplyFile(pool, file); },
+ [&](std::string const& query) -> QueryResult { return DBUpdater<T>::Retrieve(pool, query); });
+
+ uint32 const count = updateFetcher.Update(
+ sConfigMgr->GetBoolDefault("Updates.Redundancy", true),
+ sConfigMgr->GetBoolDefault("Updates.AllowRehash", true),
+ sConfigMgr->GetBoolDefault("Updates.ArchivedRedundancy", false),
+ sConfigMgr->GetBoolDefault("Updates.CleanDeadRef", true));
+
+ if (!count)
+ TC_LOG_INFO("sql.updates", ">> %s database is up-to-date!", DBUpdater<T>::GetTableName().c_str());
+ else
+ TC_LOG_INFO("sql.updates", ">> Applied %d %s.", count, count == 1 ? "query" : "queries");
+
+ return true;
+}
+
+template<class T>
+bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
+{
+ {
+ QueryResult const result = Retrieve(pool, "SHOW TABLES");
+ if (result && (result->GetRowCount() > 0))
+ return true;
+ }
+
+ if (!DBUpdater<T>::CheckExecutable())
+ return false;
+
+ TC_LOG_INFO("sql.updates", "Database %s is empty, auto populating it...", DBUpdater<T>::GetTableName().c_str());
+
+ std::string const p = DBUpdater<T>::GetBaseFile();
+ if (p.empty())
+ {
+ TC_LOG_INFO("sql.updates", ">> No base file provided, skipped!");
+ return true;
+ }
+
+ Path const base(p);
+ if (!exists(base))
+ {
+ switch (DBUpdater<T>::GetBaseLocationType())
+ {
+ case LOCATION_REPOSITORY:
+ {
+ TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing, try to clone the source again.",
+ base.generic_string().c_str(), base.filename().generic_string().c_str());
+
+ break;
+ }
+ case LOCATION_DOWNLOAD:
+ {
+ TC_LOG_ERROR("sql.updates", ">> File \"%s\" is missing, download it from \"http://www.trinitycore.org/f/files/category/1-database/\"" \
+ " and place it in your server directory.", base.filename().generic_string().c_str());
+ break;
+ }
+ }
+ return false;
+ }
+
+ // Update database
+ TC_LOG_INFO("sql.updates", ">> Applying \'%s\'...", base.generic_string().c_str());
+ ApplyFile(pool, base);
+
+ TC_LOG_INFO("sql.updates", ">> Done!");
+ return true;
+}
+
+template<class T>
+QueryResult DBUpdater<T>::Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query)
+{
+ return pool.PQuery(query.c_str());
+}
+
+template<class T>
+void DBUpdater<T>::Apply(DatabaseWorkerPool<T>& pool, std::string const& query)
+{
+ pool.DirectExecute(query.c_str());
+}
+
+template<class T>
+void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path)
+{
+ DBUpdater<T>::ApplyFile(pool, pool.GetConnectionInfo()->host, pool.GetConnectionInfo()->user, pool.GetConnectionInfo()->password,
+ pool.GetConnectionInfo()->port_or_socket, pool.GetConnectionInfo()->database, path);
+}
+
+template<class T>
+void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& host, std::string const& user,
+ std::string const& password, std::string const& port_or_socket, std::string const& database, Path const& path)
+{
+ std::vector<std::string> args;
+ args.reserve(7);
+
+ // CLI Client connection info
+ args.push_back("-h" + host);
+ args.push_back("-u" + user);
+ args.push_back("-p" + password);
+ args.push_back("-P" + port_or_socket);
+
+ // Set the default charset to utf8
+ args.push_back("--default-character-set=utf8");
+
+ // Set max allowed packet to 1 GB
+ args.push_back("--max-allowed-packet=1GB");
+
+ // Database
+ if (!database.empty())
+ args.push_back(database);
+
+ // ToDo: use the existing query in memory as virtual file if possible
+ file_descriptor_source source(path);
+
+ uint32 ret;
+ try
+ {
+ child c = execute(run_exe(DBUpdater<T>::GetMySqlCli().empty() ? "mysql" :
+ boost::filesystem::absolute(DBUpdater<T>::GetMySqlCli()).generic_string()),
+ set_args(args), bind_stdin(source), throw_on_error());
+
+ ret = wait_for_exit(c);
+ }
+ catch (boost::system::system_error&)
+ {
+ ret = EXIT_FAILURE;
+ }
+
+ source.close();
+
+ if (ret != EXIT_SUCCESS)
+ {
+ TC_LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \
+ " If you are an user pull the latest revision from the repository. If you are a developer fix your sql query.",
+ path.generic_string().c_str(), pool.GetConnectionInfo()->database.c_str());
+
+ throw UpdateException("update failed");
+ }
+}
+
+template class DBUpdater<LoginDatabaseConnection>;
+template class DBUpdater<WorldDatabaseConnection>;
+template class DBUpdater<CharacterDatabaseConnection>;
+template class DBUpdater<HotfixDatabaseConnection>;
diff --git a/src/server/shared/Updater/DBUpdater.h b/src/server/shared/Updater/DBUpdater.h
new file mode 100644
index 00000000000..0caf8a438fb
--- /dev/null
+++ b/src/server/shared/Updater/DBUpdater.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#ifndef DBUpdater_h__
+#define DBUpdater_h__
+
+#include "DatabaseEnv.h"
+
+#include <string>
+#include <boost/filesystem.hpp>
+
+class UpdateException : public std::exception
+{
+public:
+ UpdateException(std::string const& msg) : _msg(msg) { }
+ ~UpdateException() throw() { }
+
+ char const* what() const throw() override { return _msg.c_str(); }
+
+private:
+ std::string const _msg;
+};
+
+enum BaseLocation
+{
+ LOCATION_REPOSITORY,
+ LOCATION_DOWNLOAD
+};
+
+template <class T>
+class DBUpdater
+{
+public:
+ using Path = boost::filesystem::path;
+
+ static std::string GetSourceDirectory();
+
+ static inline std::string GetConfigEntry();
+
+ static inline std::string GetTableName();
+
+ static std::string GetBaseFile();
+
+ static bool IsEnabled(uint32 const updateMask);
+
+ static BaseLocation GetBaseLocationType();
+
+ static bool Create(DatabaseWorkerPool<T>& pool);
+
+ static bool Update(DatabaseWorkerPool<T>& pool);
+
+ static bool Populate(DatabaseWorkerPool<T>& pool);
+
+private:
+ static std::string GetMySqlCli();
+ static bool CheckExecutable();
+
+ static QueryResult Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query);
+ static void Apply(DatabaseWorkerPool<T>& pool, std::string const& query);
+ static void ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path);
+ static void ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& host, std::string const& user,
+ std::string const& password, std::string const& port_or_socket, std::string const& database, Path const& path);
+};
+
+#endif // DBUpdater_h__
diff --git a/src/server/shared/Updater/UpdateFetcher.cpp b/src/server/shared/Updater/UpdateFetcher.cpp
new file mode 100644
index 00000000000..8084c6ba37f
--- /dev/null
+++ b/src/server/shared/Updater/UpdateFetcher.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#include "UpdateFetcher.h"
+#include "Log.h"
+#include "Util.h"
+
+#include <fstream>
+#include <chrono>
+#include <vector>
+#include <sstream>
+#include <exception>
+#include <unordered_map>
+#include <openssl/sha.h>
+
+using namespace boost::filesystem;
+
+UpdateFetcher::UpdateFetcher(Path const& sourceDirectory,
+ std::function<void(std::string const&)> const& apply,
+ std::function<void(Path const& path)> const& applyFile,
+ std::function<QueryResult(std::string const&)> const& retrieve) :
+ _sourceDirectory(sourceDirectory), _apply(apply), _applyFile(applyFile),
+ _retrieve(retrieve)
+{
+}
+
+UpdateFetcher::LocaleFileStorage UpdateFetcher::GetFileList() const
+{
+ LocaleFileStorage files;
+ DirectoryStorage directories = ReceiveIncludedDirectories();
+ for (auto const& entry : directories)
+ FillFileListRecursively(entry.path, files, entry.state, 1);
+
+ return files;
+}
+
+void UpdateFetcher::FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const
+{
+ static uint32 const MAX_DEPTH = 10;
+ static directory_iterator const end;
+
+ for (directory_iterator itr(path); itr != end; ++itr)
+ {
+ if (is_directory(itr->path()))
+ {
+ if (depth < MAX_DEPTH)
+ FillFileListRecursively(itr->path(), storage, state, depth + 1);
+ }
+ else if (itr->path().extension() == ".sql")
+ {
+ TC_LOG_TRACE("sql.updates", "Added locale file \"%s\".", itr->path().filename().generic_string().c_str());
+
+ LocaleFileEntry const entry = { itr->path(), state };
+
+ // Check for doubled filenames
+ // Since elements are only compared through their filenames this is ok
+ if (storage.find(entry) != storage.end())
+ {
+ TC_LOG_FATAL("sql.updates", "Duplicated filename occurred \"%s\", since updates are ordered " \
+ "through its filename every name needs to be unique!", itr->path().generic_string().c_str());
+
+ throw UpdateException("Updating failed, see the log for details.");
+ }
+
+ storage.insert(entry);
+ }
+ }
+}
+
+UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() const
+{
+ DirectoryStorage directories;
+
+ QueryResult const result = _retrieve("SELECT `path`, `state` FROM `updates_include`");
+ if (!result)
+ return directories;
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ std::string path = fields[0].GetString();
+ if (path.substr(0, 1) == "$")
+ path = _sourceDirectory.generic_string() + path.substr(1);
+
+ Path const p(path);
+
+ if (!is_directory(p))
+ {
+ TC_LOG_ERROR("sql.updates", "DBUpdater: Given update include directory \"%s\" isn't existing, skipped!", p.generic_string().c_str());
+ continue;
+ }
+
+ DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert(fields[1].GetString()) };
+ directories.push_back(entry);
+
+ TC_LOG_TRACE("sql.updates", "Added applied file \"%s\" from remote.", p.filename().generic_string().c_str());
+
+ } while (result->NextRow());
+
+ return directories;
+}
+
+UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles() const
+{
+ AppliedFileStorage map;
+
+ QueryResult result = _retrieve("SELECT `name`, `hash`, `state`, `timestamp` FROM `updates` ORDER BY `name` ASC");
+ if (!result)
+ return map;
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ AppliedFileEntry const entry = { fields[0].GetString(), fields[1].GetString(),
+ AppliedFileEntry::StateConvert(fields[2].GetString()), fields[3].GetUInt32() };
+
+ map.insert(std::make_pair(entry.name, entry));
+ }
+ while (result->NextRow());
+
+ return map;
+}
+
+UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path const& file) const
+{
+ std::ifstream in(file.c_str());
+ WPFatal(in.is_open(), "Could not read an update file.");
+
+ auto const start_pos = in.tellg();
+ in.ignore(std::numeric_limits<std::streamsize>::max());
+ auto const char_count = in.gcount();
+ in.seekg(start_pos);
+
+ SQLUpdate const update(new std::string(char_count, char{}));
+
+ in.read(&(*update)[0], update->size());
+ in.close();
+ return update;
+}
+
+uint32 UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, bool const cleanDeadReferences) const
+{
+ LocaleFileStorage const available = GetFileList();
+ AppliedFileStorage applied = ReceiveAppliedFiles();
+
+ // Fill hash to name cache
+ HashToFileNameStorage hashToName;
+ for (auto entry : applied)
+ hashToName.insert(std::make_pair(entry.second.hash, entry.first));
+
+ uint32 importedUpdates = 0;
+
+ for (auto const& availableQuery : available)
+ {
+ TC_LOG_DEBUG("sql.updates", "Checking update \"%s\"...", availableQuery.first.filename().generic_string().c_str());
+
+ AppliedFileStorage::const_iterator iter = applied.find(availableQuery.first.filename().string());
+ if (iter != applied.end())
+ {
+ // If redundancy is disabled skip it since the update is already applied.
+ if (!redundancyChecks)
+ {
+ TC_LOG_DEBUG("sql.updates", ">> Update is already applied, skipping redundancy checks.");
+ applied.erase(iter);
+ continue;
+ }
+
+ // If the update is in an archived directory and is marked as archived in our database skip redundancy checks (archived updates never change).
+ if (!archivedRedundancy && (iter->second.state == ARCHIVED) && (availableQuery.second == ARCHIVED))
+ {
+ TC_LOG_DEBUG("sql.updates", ">> Update is archived and marked as archived in database, skipping redundancy checks.");
+ applied.erase(iter);
+ continue;
+ }
+ }
+
+ // Read update from file
+ SQLUpdate const update = ReadSQLUpdate(availableQuery.first);
+
+ // Calculate hash
+ std::string const hash = CalculateHash(update);
+
+ UpdateMode mode = MODE_APPLY;
+
+ // Update is not in our applied list
+ if (iter == applied.end())
+ {
+ // Catch renames (different filename but same hash)
+ HashToFileNameStorage::const_iterator const hashIter = hashToName.find(hash);
+ if (hashIter != hashToName.end())
+ {
+ // Check if the original file was removed if not we've got a problem.
+ LocaleFileStorage::const_iterator localeIter;
+ // Push localeIter forward
+ for (localeIter = available.begin(); (localeIter != available.end()) &&
+ (localeIter->first.filename().string() != hashIter->second); ++localeIter);
+
+ // Conflict!
+ if (localeIter != available.end())
+ {
+ TC_LOG_WARN("sql.updates", ">> Seems like update \"%s\" \'%s\' was renamed, but the old file is still there! " \
+ "Trade it as a new file! (Probably its an unmodified copy of file \"%s\")",
+ availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str(),
+ localeIter->first.filename().string().c_str());
+ }
+ // Its save to trade the file as renamed here
+ else
+ {
+ TC_LOG_INFO("sql.updates", ">> Renaming update \"%s\" to \"%s\" \'%s\'.",
+ hashIter->second.c_str(), availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
+
+ RenameEntry(hashIter->second, availableQuery.first.filename().string());
+ applied.erase(hashIter->second);
+ continue;
+ }
+ }
+ // Apply the update if it was never seen before.
+ else
+ {
+ TC_LOG_INFO("sql.updates", ">> Applying update \"%s\" \'%s\'...",
+ availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
+ }
+ }
+ // Rehash the update entry if it is contained in our database but with an empty hash.
+ else if (allowRehash && iter->second.hash.empty())
+ {
+ mode = MODE_REHASH;
+
+ TC_LOG_INFO("sql.updates", ">> Re-hashing update \"%s\" \'%s\'...", availableQuery.first.filename().string().c_str(),
+ hash.substr(0, 7).c_str());
+ }
+ else
+ {
+ // If the hash of the files differs from the one stored in our database reapply the update (because it was changed).
+ if (iter->second.hash != hash)
+ {
+ TC_LOG_INFO("sql.updates", ">> Reapplying update \"%s\" \'%s\' -> \'%s\' (it changed)...", availableQuery.first.filename().string().c_str(),
+ iter->second.hash.substr(0, 7).c_str(), hash.substr(0, 7).c_str());
+ }
+ else
+ {
+ // If the file wasn't changed and just moved update its state if necessary.
+ if (iter->second.state != availableQuery.second)
+ {
+ TC_LOG_DEBUG("sql.updates", ">> Updating state of \"%s\" to \'%s\'...",
+ availableQuery.first.filename().string().c_str(), AppliedFileEntry::StateConvert(availableQuery.second).c_str());
+
+ UpdateState(availableQuery.first.filename().string(), availableQuery.second);
+ }
+
+ TC_LOG_DEBUG("sql.updates", ">> Update is already applied and is matching hash \'%s\'.", hash.substr(0, 7).c_str());
+
+ applied.erase(iter);
+ continue;
+ }
+ }
+
+ uint32 speed = 0;
+ AppliedFileEntry const file = { availableQuery.first.filename().string(), hash, availableQuery.second, 0 };
+
+ switch (mode)
+ {
+ case MODE_APPLY:
+ speed = Apply(availableQuery.first);
+ /*no break*/
+ case MODE_REHASH:
+ UpdateEntry(file, speed);
+ break;
+ }
+
+ if (iter != applied.end())
+ applied.erase(iter);
+
+ if (mode == MODE_APPLY)
+ ++importedUpdates;
+ }
+
+ for (auto const& entry : applied)
+ {
+ TC_LOG_WARN("sql.updates", ">> File \'%s\' was applied to the database but is missing in" \
+ " your update directory now!%s", entry.first.c_str(), cleanDeadReferences ? " Deleting orphaned entry..." : "");
+ }
+
+ if (cleanDeadReferences)
+ CleanUp(applied);
+
+ return importedUpdates;
+}
+
+std::string UpdateFetcher::CalculateHash(SQLUpdate const& query) const
+{
+ // Calculate a Sha1 hash based on query content.
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ SHA1((unsigned char*)query->c_str(), query->length(), (unsigned char*)&digest);
+
+ return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH);
+}
+
+uint32 UpdateFetcher::Apply(Path const& path) const
+{
+ using Time = std::chrono::high_resolution_clock;
+ using ms = std::chrono::milliseconds;
+
+ // Benchmark query speed
+ auto const begin = Time::now();
+
+ // Update database
+ _applyFile(path);
+
+ // Return time the query took to apply
+ return std::chrono::duration_cast<ms>(Time::now() - begin).count();
+}
+
+void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const
+{
+ std::string const update = "REPLACE INTO `updates` (`name`, `hash`, `state`, `speed`) VALUES (\"" +
+ entry.name + "\", \"" + entry.hash + "\", \'" + entry.GetStateAsString() + "\', " + std::to_string(speed) + ")";
+
+ // Update database
+ _apply(update);
+}
+
+void UpdateFetcher::RenameEntry(std::string const& from, std::string const& to) const
+{
+ // Delete target if it exists
+ {
+ std::string const update = "DELETE FROM `updates` WHERE `name`=\"" + to + "\"";
+
+ // Update database
+ _apply(update);
+ }
+
+ // Rename
+ {
+ std::string const update = "UPDATE `updates` SET `name`=\"" + to + "\" WHERE `name`=\"" + from + "\"";
+
+ // Update database
+ _apply(update);
+ }
+}
+
+void UpdateFetcher::CleanUp(AppliedFileStorage const& storage) const
+{
+ if (storage.empty())
+ return;
+
+ std::stringstream update;
+ size_t remaining = storage.size();
+
+ update << "DELETE FROM `updates` WHERE `name` IN(";
+
+ for (auto const& entry : storage)
+ {
+ update << "\"" << entry.first << "\"";
+ if ((--remaining) > 0)
+ update << ", ";
+ }
+
+ update << ")";
+
+ // Update database
+ _apply(update.str());
+}
+
+void UpdateFetcher::UpdateState(std::string const& name, State const state) const
+{
+ std::string const update = "UPDATE `updates` SET `state`=\'" + AppliedFileEntry::StateConvert(state) + "\' WHERE `name`=\"" + name + "\"";
+
+ // Update database
+ _apply(update);
+}
diff --git a/src/server/shared/Updater/UpdateFetcher.h b/src/server/shared/Updater/UpdateFetcher.h
new file mode 100644
index 00000000000..b348eecf4e1
--- /dev/null
+++ b/src/server/shared/Updater/UpdateFetcher.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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/>.
+ */
+
+#ifndef UpdateFetcher_h__
+#define UpdateFetcher_h__
+
+#include <DBUpdater.h>
+
+#include <functional>
+#include <string>
+#include <memory>
+#include <vector>
+#include <boost/container/flat_set.hpp>
+
+class UpdateFetcher
+{
+ using Path = boost::filesystem::path;
+
+public:
+ UpdateFetcher(Path const& updateDirectory,
+ std::function<void(std::string const&)> const& apply,
+ std::function<void(Path const& path)> const& applyFile,
+ std::function<QueryResult(std::string const&)> const& retrieve);
+
+ uint32 Update(bool const redundancyChecks, bool const allowRehash,
+ bool const archivedRedundancy, bool const cleanDeadReferences) const;
+
+private:
+ enum UpdateMode
+ {
+ MODE_APPLY,
+ MODE_REHASH
+ };
+
+ enum State
+ {
+ RELEASED,
+ ARCHIVED
+ };
+
+ struct AppliedFileEntry
+ {
+ std::string const name;
+
+ std::string const hash;
+
+ State const state;
+
+ uint32 const timestamp;
+
+ static inline State StateConvert(std::string const& state)
+ {
+ return (state == "RELEASED") ? RELEASED : ARCHIVED;
+ }
+
+ static inline std::string StateConvert(State const state)
+ {
+ return (state == RELEASED) ? "RELEASED" : "ARCHIVED";
+ }
+
+ std::string GetStateAsString() const
+ {
+ return StateConvert(state);
+ }
+ };
+
+ struct DirectoryEntry
+ {
+ Path const path;
+
+ State const state;
+ };
+
+ using LocaleFileEntry = std::pair<Path, State>;
+
+ struct PathCompare
+ {
+ inline bool operator() (LocaleFileEntry const& left, LocaleFileEntry const& right) const
+ {
+ return left.first.filename().string() < right.first.filename().string();
+ }
+ };
+
+ using LocaleFileStorage = boost::container::flat_set<LocaleFileEntry, PathCompare>;
+ using HashToFileNameStorage = std::unordered_map<std::string, std::string>;
+ using AppliedFileStorage = std::unordered_map<std::string, AppliedFileEntry>;
+ using DirectoryStorage = std::vector<UpdateFetcher::DirectoryEntry>;
+ using SQLUpdate = std::shared_ptr<std::string>;
+
+ LocaleFileStorage GetFileList() const;
+ void FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const;
+
+ DirectoryStorage ReceiveIncludedDirectories() const;
+ AppliedFileStorage ReceiveAppliedFiles() const;
+
+ SQLUpdate ReadSQLUpdate(Path const& file) const;
+ std::string CalculateHash(SQLUpdate const& query) const;
+
+ uint32 Apply(Path const& path) const;
+
+ void UpdateEntry(AppliedFileEntry const& entry, uint32 const speed = 0) const;
+ void RenameEntry(std::string const& from, std::string const& to) const;
+ void CleanUp(AppliedFileStorage const& storage) const;
+
+ void UpdateState(std::string const& name, State const state) const;
+
+ Path const _sourceDirectory;
+
+ std::function<void(std::string const&)> const _apply;
+ std::function<void(Path const& path)> const _applyFile;
+ std::function<QueryResult(std::string const&)> const _retrieve;
+};
+
+#endif // UpdateFetcher_h__
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index 683c4de8eb8..63da20df2c7 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -49,6 +49,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/cppformat
${CMAKE_SOURCE_DIR}/dep/zmqpp
+ ${CMAKE_SOURCE_DIR}/dep/process
${CMAKE_SOURCE_DIR}/src/server/collision
${CMAKE_SOURCE_DIR}/src/server/collision/Management
${CMAKE_SOURCE_DIR}/src/server/collision/Models
@@ -65,6 +66,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/src/server/shared/Networking
${CMAKE_SOURCE_DIR}/src/server/shared/Packets
${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Updater
${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
${CMAKE_SOURCE_DIR}/src/server/ipc
${CMAKE_SOURCE_DIR}/src/server/game
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp
index 15d08a903b9..50bb8d7646d 100644
--- a/src/server/worldserver/Main.cpp
+++ b/src/server/worldserver/Main.cpp
@@ -44,6 +44,7 @@
#include "WorldSocketMgr.h"
#include "BattlenetServerManager.h"
#include "Realm/Realm.h"
+#include "DatabaseLoader.h"
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
#include <boost/asio/io_service.hpp>
@@ -520,105 +521,16 @@ bool StartDB()
{
MySQL::Library_Init();
- std::string dbString;
- uint8 asyncThreads, synchThreads;
+ // Load databases
+ DatabaseLoader loader("server.worldserver", DatabaseLoader::DATABASE_NONE);
+ loader
+ .AddDatabase(HotfixDatabase, "Hotfix")
+ .AddDatabase(WorldDatabase, "World")
+ .AddDatabase(CharacterDatabase, "Character")
+ .AddDatabase(LoginDatabase, "Login");
- dbString = sConfigMgr->GetStringDefault("WorldDatabaseInfo", "");
- if (dbString.empty())
- {
- TC_LOG_ERROR("server.worldserver", "World database not specified in configuration file");
- return false;
- }
-
- asyncThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.WorkerThreads", 1));
- if (asyncThreads < 1 || asyncThreads > 32)
- {
- TC_LOG_ERROR("server.worldserver", "World database: invalid number of worker threads specified. "
- "Please pick a value between 1 and 32.");
- return false;
- }
-
- synchThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.SynchThreads", 1));
- ///- Initialize the world database
- if (!WorldDatabase.Open(dbString, asyncThreads, synchThreads))
- {
- TC_LOG_ERROR("server.worldserver", "Cannot connect to world database %s", dbString.c_str());
- return false;
- }
-
- ///- Get character database info from configuration file
- dbString = sConfigMgr->GetStringDefault("CharacterDatabaseInfo", "");
- if (dbString.empty())
- {
- TC_LOG_ERROR("server.worldserver", "Character database not specified in configuration file");
- return false;
- }
-
- asyncThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.WorkerThreads", 1));
- if (asyncThreads < 1 || asyncThreads > 32)
- {
- TC_LOG_ERROR("server.worldserver", "Character database: invalid number of worker threads specified. "
- "Please pick a value between 1 and 32.");
- return false;
- }
-
- synchThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.SynchThreads", 2));
-
- ///- Initialize the Character database
- if (!CharacterDatabase.Open(dbString, asyncThreads, synchThreads))
- {
- TC_LOG_ERROR("server.worldserver", "Cannot connect to Character database %s", dbString.c_str());
- return false;
- }
-
- ///- Get hotfixes database info from configuration file
- dbString = sConfigMgr->GetStringDefault("HotfixDatabaseInfo", "");
- if (dbString.empty())
- {
- TC_LOG_ERROR("server.worldserver", "Hotfixes database not specified in configuration file");
- return false;
- }
-
- asyncThreads = uint8(sConfigMgr->GetIntDefault("HotfixDatabase.WorkerThreads", 1));
- if (asyncThreads < 1 || asyncThreads > 32)
- {
- TC_LOG_ERROR("server.worldserver", "Hotfixes database: invalid number of worker threads specified. "
- "Please pick a value between 1 and 32.");
- return false;
- }
-
- synchThreads = uint8(sConfigMgr->GetIntDefault("HotfixDatabase.SynchThreads", 2));
-
- ///- Initialize the hotfixes database
- if (!HotfixDatabase.Open(dbString, asyncThreads, synchThreads))
- {
- TC_LOG_ERROR("server.worldserver", "Cannot connect to the hotfix database %s", dbString.c_str());
- return false;
- }
-
- ///- Get login database info from configuration file
- dbString = sConfigMgr->GetStringDefault("LoginDatabaseInfo", "");
- if (dbString.empty())
- {
- TC_LOG_ERROR("server.worldserver", "Login database not specified in configuration file");
- return false;
- }
-
- asyncThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.WorkerThreads", 1));
- if (asyncThreads < 1 || asyncThreads > 32)
- {
- TC_LOG_ERROR("server.worldserver", "Login database: invalid number of worker threads specified. "
- "Please pick a value between 1 and 32.");
- return false;
- }
-
- synchThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.SynchThreads", 1));
- ///- Initialise the login database
- if (!LoginDatabase.Open(dbString, asyncThreads, synchThreads))
- {
- TC_LOG_ERROR("server.worldserver", "Cannot connect to login database %s", dbString.c_str());
+ if (!loader.Load())
return false;
- }
///- Get the realm Id from the configuration file
realmHandle.Index = sConfigMgr->GetIntDefault("RealmID", 0);
@@ -628,6 +540,7 @@ bool StartDB()
return false;
}
+ // Realm Handles
QueryResult realmIdQuery = LoginDatabase.PQuery("SELECT `Region`,`Battlegroup` FROM `realmlist` WHERE `id`=%u", realmHandle.Index);
if (!realmIdQuery)
{
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 86d9dac2946..c1cdc4a7517 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -11,6 +11,7 @@
# PERFORMANCE SETTINGS
# SERVER LOGGING
# SERVER SETTINGS
+# UPDATE SETTINGS
# WARDEN SETTINGS
# PLAYER INTERACTION
# CREATURE SETTINGS
@@ -1130,6 +1131,90 @@ FeatureSystem.CharacterUndelete.Cooldown = 2592000
###################################################################################################
###################################################################################################
+# UPDATE SETTINGS
+#
+# Updates.EnableDatabases
+# Description: A mask that describes which databases shall be updated.
+#
+# Following flags are available
+# DATABASE_LOGIN = 1, // Auth database
+# DATABASE_CHARACTER = 2, // Character database
+# DATABASE_WORLD = 4, // World database
+# DATABASE_HOTFIX = 8, // Hotfixes database
+#
+# Default: 0 - (All Disabled)
+# 4 - (Enable world only)
+# 15 - (All enabled)
+
+Updates.EnableDatabases = 0
+
+#
+# Updates.SourcePath
+# Description: The path to your TrinityCore source directory.
+# If the path is left empty, built-in CMAKE_SOURCE_DIR is used.
+# Example: "../TrinityCore"
+# Default: ""
+
+Updates.SourcePath = ""
+
+#
+# Updates.SourcePath
+# Description: The path to your mysql cli binary.
+# If the path is left empty, built-in path from cmake is used.
+# Example: "C:/Program Files/MySQL/MySQL Server 5.6/bin/mysql.exe"
+# "mysql.exe"
+# "/usr/bin/mysql"
+# Default: ""
+
+Updates.MySqlCLIPath = ""
+
+#
+# Updates.AutoSetup
+# Description: Auto populate empty databases.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Updates.AutoSetup = 1
+
+#
+# Updates.Redundancy
+# Description: Perform data redundancy checks through hashing
+# to detect changes on sql updates and reapply it.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Updates.Redundancy = 1
+
+#
+# Updates.ArchivedRedundancy
+# Description: Check hashes of archived updates (slows down startup).
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Updates.ArchivedRedundancy = 0
+
+#
+# Updates.AllowRehash
+# Description: Inserts the current file hash in the database if it is left empty.
+# Useful if you want to mark a file as applied but you don't know its hash.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Updates.AllowRehash = 1
+
+#
+# Updates.CleanDeadRef
+# Description: Cleans dead/ orphaned references that occure if a update was deleted or renamed and edited.
+# Disable this if you want to know if the database is in a possible "dirty state".
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Updates.CleanDeadRef = 1
+
+#
+###################################################################################################
+
+###################################################################################################
# WARDEN SETTINGS
#
# Warden.Enabled
@@ -3224,6 +3309,7 @@ Logger.root=5,Console Server
Logger.server=3,Console Server
Logger.commands.gm=3,Console GM
Logger.sql.sql=5,Console DBErrors
+Logger.sql.updates=3,Console Server
#Logger.achievement=3,Console Server
#Logger.ahbot=3,Console Server