diff --git a/issue_template.md b/issue_template.md
new file mode 100644
index 00000000000..87f2e21af41
--- /dev/null
+++ b/issue_template.md
@@ -0,0 +1,23 @@
+**Description**:
+
+**Current behaviour**: Tell us what happens
+
+**Expected behaviour**: Tell us what should happen instead
+
+**Steps to reproduce the problem**:
+
+1.
+2.
+3.
+
+**Branch(es)**: 335/6x
+
+**TC hash/commit**:
+
+**TDB version**:
+
+**Operating system**:
+
+
+[//]: # (This template is for problem reports, for other type of reports edit it accordingly)
+[//]: # (If this is a crash report, include the crashlog with https://gist.github.com/)
diff --git a/pull_request_template.md b/pull_request_template.md
new file mode 100644
index 00000000000..7438ed561cf
--- /dev/null
+++ b/pull_request_template.md
@@ -0,0 +1,13 @@
+**Changes proposed**:
+
+-
+-
+-
+
+**Target branch(es)**: 335/6x
+
+**Issues addressed**: Fixes #
+
+**Tests performed**: (Does it build? Tested in-game?)
+
+**Known issues and TODO list**:
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql
index 99dd6378ab7..afac4485173 100644
--- a/sql/base/characters_database.sql
+++ b/sql/base/characters_database.sql
@@ -847,6 +847,7 @@ CREATE TABLE `character_instance` (
`guid` int(10) unsigned NOT NULL DEFAULT '0',
`instance` int(10) unsigned NOT NULL DEFAULT '0',
`permanent` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `extendState` TINYINT(2) UNSIGNED NOT NULL DEFAULT '1',
PRIMARY KEY (`guid`,`instance`),
KEY `instance` (`instance`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@@ -2786,7 +2787,7 @@ CREATE TABLE `updates` (
LOCK TABLES `updates` WRITE;
/*!40000 ALTER TABLE `updates` DISABLE KEYS */;
-INSERT INTO `updates` VALUES ('2015_03_20_00_characters.sql','B761760804EA73BD297F296C5C1919687DF7191C','ARCHIVED','2015-03-21 21:44:15',0),('2015_03_20_01_characters.sql','894F08B70449A5481FFAF394EE5571D7FC4D8A3A','ARCHIVED','2015-03-21 21:44:15',0),('2015_03_20_02_characters.sql','97D7BE0CAADC79F3F11B9FD296B8C6CD40FE593B','ARCHIVED','2015-03-21 21:44:51',0),('2015_06_26_00_characters_335.sql','f8230a59a9e878a6f54f421d6621f1595bd93861','ARCHIVED','2015-03-21 21:44:51',0),('2015_09_28_00_characters_335.sql','F8682A431D50E54BDC4AC0E7DBED21AE8AAB6AD4','ARCHIVED','2015-09-28 21:00:00',0),('2016_01_11_00_characters_from_335.sql','AAECE4BA6FDCF0C34A57FADD11DB68DAFD8AE9CD','ARCHIVED','2016-01-10 21:57:56',0),('2016_01_11_01_characters_from_335.sql','F8682A431D50E54BDC4AC0E7DBED21AE8AAB6AD4','ARCHIVED','2015-09-29 22:23:32',0),('2016_01_11_02_characters_from_335.sql','4555A7F35C107E54C13D74D20F141039ED42943E','ARCHIVED','2015-10-29 20:32:37',0),('2016_01_14_from_335_2015_10_06_00_characters.sql','16842FDD7E8547F2260D3312F53EFF8761EFAB35','ARCHIVED','2015-10-11 19:47:41',0),('2016_01_14_from_335_2015_10_07_00_characters.sql','E15AB463CEBE321001D7BFDEA4B662FF618728FD','ARCHIVED','2015-10-11 19:47:41',0),('2016_01_14_from_335_2015_10_12_00_characters.sql','D6F9927BDED72AD0A81D6EC2C6500CBC34A39FA2','ARCHIVED','2015-10-13 01:06:25',0),('2016_01_14_from_335_2015_10_28_00_characters.sql','622A9CA8FCE690429EBE23BA071A37C7A007BF8B','ARCHIVED','2015-10-28 21:49:52',0),('2016_01_14_from_335_2015_11_03_00_characters.sql','CC045717B8FDD9733351E52A5302560CD08AAD57','ARCHIVED','2015-11-03 22:56:17',0);
+INSERT INTO `updates` VALUES ('2015_03_20_00_characters.sql','B761760804EA73BD297F296C5C1919687DF7191C','ARCHIVED','2015-03-21 21:44:15',0),('2015_03_20_01_characters.sql','894F08B70449A5481FFAF394EE5571D7FC4D8A3A','ARCHIVED','2015-03-21 21:44:15',0),('2015_03_20_02_characters.sql','97D7BE0CAADC79F3F11B9FD296B8C6CD40FE593B','ARCHIVED','2015-03-21 21:44:51',0),('2015_06_26_00_characters_335.sql','f8230a59a9e878a6f54f421d6621f1595bd93861','ARCHIVED','2015-03-21 21:44:51',0),('2015_09_28_00_characters_335.sql','F8682A431D50E54BDC4AC0E7DBED21AE8AAB6AD4','ARCHIVED','2015-09-28 21:00:00',0),('2016_01_11_00_characters_from_335.sql','AAECE4BA6FDCF0C34A57FADD11DB68DAFD8AE9CD','ARCHIVED','2016-01-10 21:57:56',0),('2016_01_11_01_characters_from_335.sql','F8682A431D50E54BDC4AC0E7DBED21AE8AAB6AD4','ARCHIVED','2015-09-29 22:23:32',0),('2016_01_11_02_characters_from_335.sql','4555A7F35C107E54C13D74D20F141039ED42943E','ARCHIVED','2015-10-29 20:32:37',0),('2016_01_14_from_335_2015_10_06_00_characters.sql','16842FDD7E8547F2260D3312F53EFF8761EFAB35','ARCHIVED','2015-10-11 19:47:41',0),('2016_01_14_from_335_2015_10_07_00_characters.sql','E15AB463CEBE321001D7BFDEA4B662FF618728FD','ARCHIVED','2015-10-11 19:47:41',0),('2016_01_14_from_335_2015_10_12_00_characters.sql','D6F9927BDED72AD0A81D6EC2C6500CBC34A39FA2','ARCHIVED','2015-10-13 01:06:25',0),('2016_01_14_from_335_2015_10_28_00_characters.sql','622A9CA8FCE690429EBE23BA071A37C7A007BF8B','ARCHIVED','2015-10-28 21:49:52',0),('2016_01_14_from_335_2015_11_03_00_characters.sql','CC045717B8FDD9733351E52A5302560CD08AAD57','ARCHIVED','2015-11-03 22:56:17',0),('2016_02_21_from_335_2016_02_10_00_characters.sql','F1B4DA202819CABC7319A4470A2D224A34609E97','RELEASED','2016-02-10 00:00:00',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;
diff --git a/sql/updates/characters/2016_02_21_from_335_2016_02_10_00_characters.sql b/sql/updates/characters/2016_02_21_from_335_2016_02_10_00_characters.sql
new file mode 100644
index 00000000000..d49ed155bc8
--- /dev/null
+++ b/sql/updates/characters/2016_02_21_from_335_2016_02_10_00_characters.sql
@@ -0,0 +1,2 @@
+-- extend=0 is expired, extend=1 is normal, extend=2 is extended
+ALTER TABLE `character_instance` ADD COLUMN `extendState` TINYINT(2) UNSIGNED NOT NULL DEFAULT '1';
diff --git a/src/common/Define.h b/src/common/Define.h
index dd10298f65c..106d07f61a5 100644
--- a/src/common/Define.h
+++ b/src/common/Define.h
@@ -83,10 +83,16 @@
# define ATTR_NORETURN __attribute__((__noreturn__))
# define ATTR_PRINTF(F, V) __attribute__ ((__format__ (__printf__, F, V)))
# define ATTR_DEPRECATED __attribute__((__deprecated__))
+# define TRINITY_CONSTEXPR constexpr
#else //COMPILER != COMPILER_GNU
# define ATTR_NORETURN
# define ATTR_PRINTF(F, V)
# define ATTR_DEPRECATED
+#if _MSC_VER >= 1900
+# define TRINITY_CONSTEXPR constexpr
+#else
+# define TRINITY_CONSTEXPR
+#endif
#endif //COMPILER == COMPILER_GNU
#define UI64FMTD "%" PRIu64
diff --git a/src/common/Threading/MPSCQueue.h b/src/common/Threading/MPSCQueue.h
new file mode 100644
index 00000000000..09648b844be
--- /dev/null
+++ b/src/common/Threading/MPSCQueue.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore
+ *
+ * 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 .
+ */
+
+#ifndef MPSCQueue_h__
+#define MPSCQueue_h__
+
+#include
+#include
+
+// C++ implementation of Dmitry Vyukov's lock free MPSC queue
+// http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue
+template
+class MPSCQueue
+{
+public:
+ MPSCQueue() : _head(new Node()), _tail(_head.load(std::memory_order_relaxed))
+ {
+ Node* front = _head.load(std::memory_order_relaxed);
+ front->Next.store(nullptr, std::memory_order_relaxed);
+ }
+
+ ~MPSCQueue()
+ {
+ T* output;
+ while (this->Dequeue(output))
+ ;
+
+ Node* front = _head.load(std::memory_order_relaxed);
+ delete front;
+ }
+
+ void Enqueue(T* input)
+ {
+ Node* node = new Node(input);
+ Node* prevHead = _head.exchange(node, std::memory_order_acq_rel);
+ prevHead->Next.store(node, std::memory_order_release);
+ }
+
+ bool Dequeue(T*& result)
+ {
+ Node* tail = _tail.load(std::memory_order_relaxed);
+ Node* next = tail->Next.load(std::memory_order_acquire);
+ if (!next)
+ return false;
+
+ result = next->Data;
+ _tail.store(next, std::memory_order_release);
+ delete tail;
+ return true;
+ }
+
+private:
+ struct Node
+ {
+ Node() = default;
+ explicit Node(T* data) : Data(data) { Next.store(nullptr, std::memory_order_relaxed); }
+
+ T* Data;
+ std::atomic Next;
+ };
+
+ std::atomic _head;
+ std::atomic _tail;
+
+ MPSCQueue(MPSCQueue const&) = delete;
+ MPSCQueue& operator=(MPSCQueue const&) = delete;
+};
+
+#endif // MPSCQueue_h__
diff --git a/src/server/shared/Containers.h b/src/common/Utilities/Containers.h
similarity index 60%
rename from src/server/shared/Containers.h
rename to src/common/Utilities/Containers.h
index 76708845442..7177582cf62 100644
--- a/src/server/shared/Containers.h
+++ b/src/common/Utilities/Containers.h
@@ -19,10 +19,11 @@
#define TRINITY_CONTAINERS_H
#include "Define.h"
+#include "Random.h"
+#include
+#include
#include
-
-//! Because circular includes are bad
-extern uint32 urand(uint32 min, uint32 max);
+#include
namespace Trinity
{
@@ -57,14 +58,64 @@ namespace Trinity
list = listCopy;
}
- /* Select a random element from a container. Note: make sure you explicitly empty check the container */
- template typename C::value_type const& SelectRandomContainerElement(C const& container)
+ /*
+ * Select a random element from a container.
+ *
+ * Note: container cannot be empty
+ */
+ template
+ typename C::value_type const& SelectRandomContainerElement(C const& container)
{
typename C::const_iterator it = container.begin();
std::advance(it, urand(0, container.size() - 1));
return *it;
}
+ /*
+ * Select a random element from a container where each element has a different chance to be selected.
+ *
+ * @param container Container to select an element from
+ * @param weights Chances of each element to be selected, must be in the same order as elements in container.
+ * Caller is responsible for checking that sum of all weights is greater than 0.
+ *
+ * Note: container cannot be empty
+ */
+ template
+ typename C::const_iterator SelectRandomWeightedContainerElement(C const& container, std::vector weights)
+ {
+ Trinity::discrete_distribution_param ddParam(weights.begin(), weights.end());
+ std::discrete_distribution dd(ddParam);
+ typename C::const_iterator it = container.begin();
+ std::advance(it, dd(SFMTEngine::Instance()));
+ return it;
+ }
+
+ /*
+ * Select a random element from a container where each element has a different chance to be selected.
+ *
+ * @param container Container to select an element from
+ * @param weightExtractor Function retrieving chance of each element in container, expected to take an element of the container and returning a double
+ *
+ * Note: container cannot be empty
+ */
+ template
+ typename C::const_iterator SelectRandomWeightedContainerElement(C const& container, Fn weightExtractor)
+ {
+ std::vector weights;
+ weights.reserve(container.size());
+ double weightSum = 0.0;
+ for (auto itr = container.begin(); itr != container.end(); ++itr)
+ {
+ double weight = weightExtractor(*itr);
+ weights.push_back(weight);
+ weightSum += weight;
+ }
+ if (weightSum <= 0.0)
+ weights.assign(container.size(), 1.0);
+
+ return SelectRandomWeightedContainerElement(container, weights);
+ }
+
/**
* @fn bool Trinity::Containers::Intersects(Iterator first1, Iterator last1, Iterator first2, Iterator last2)
*
diff --git a/src/common/Utilities/Random.cpp b/src/common/Utilities/Random.cpp
new file mode 100644
index 00000000000..cc013110b01
--- /dev/null
+++ b/src/common/Utilities/Random.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore
+ *
+ * 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 .
+ */
+
+#include "Random.h"
+#include "Common.h"
+#include "Errors.h"
+#include "SFMT.h"
+#include
+
+static boost::thread_specific_ptr sfmtRand;
+
+static SFMTRand* GetRng()
+{
+ SFMTRand* rand = sfmtRand.get();
+
+ if (!rand)
+ {
+ rand = new SFMTRand();
+ sfmtRand.reset(rand);
+ }
+
+ return rand;
+}
+
+int32 irand(int32 min, int32 max)
+{
+ ASSERT(max >= min);
+ return int32(GetRng()->IRandom(min, max));
+}
+
+uint32 urand(uint32 min, uint32 max)
+{
+ ASSERT(max >= min);
+ return GetRng()->URandom(min, max);
+}
+
+uint32 urandms(uint32 min, uint32 max)
+{
+ ASSERT(max >= min);
+ ASSERT(INT_MAX / IN_MILLISECONDS >= max);
+ return GetRng()->URandom(min * IN_MILLISECONDS, max * IN_MILLISECONDS);
+}
+
+float frand(float min, float max)
+{
+ ASSERT(max >= min);
+ return float(GetRng()->Random() * (max - min) + min);
+}
+
+uint32 rand32()
+{
+ return GetRng()->BRandom();
+}
+
+double rand_norm()
+{
+ return GetRng()->Random();
+}
+
+double rand_chance()
+{
+ return GetRng()->Random() * 100.0;
+}
+
+SFMTEngine& SFMTEngine::Instance()
+{
+ static SFMTEngine engine;
+ return engine;
+}
diff --git a/src/common/Utilities/Random.h b/src/common/Utilities/Random.h
new file mode 100644
index 00000000000..5610651a83b
--- /dev/null
+++ b/src/common/Utilities/Random.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore
+ *
+ * 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 .
+ */
+
+#ifndef Random_h__
+#define Random_h__
+
+#include "Define.h"
+#include
+#include
+
+/* Return a random number in the range min..max. */
+int32 irand(int32 min, int32 max);
+
+/* Return a random number in the range min..max (inclusive). */
+uint32 urand(uint32 min, uint32 max);
+
+/* Return a random millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */
+uint32 urandms(uint32 min, uint32 max);
+
+/* Return a random number in the range 0 .. UINT32_MAX. */
+uint32 rand32();
+
+/* Return a random number in the range min..max */
+float frand(float min, float max);
+
+/* Return a random double from 0.0 to 1.0 (exclusive). */
+double rand_norm();
+
+/* Return a random double from 0.0 to 100.0 (exclusive). */
+double rand_chance();
+
+/* Return true if a random roll fits in the specified chance (range 0-100). */
+inline bool roll_chance_f(float chance)
+{
+ return chance > rand_chance();
+}
+
+/* Return true if a random roll fits in the specified chance (range 0-100). */
+inline bool roll_chance_i(int chance)
+{
+ return chance > irand(0, 99);
+}
+
+/*
+* SFMT wrapper satisfying UniformRandomNumberGenerator concept for use in algorithms
+*/
+class SFMTEngine
+{
+public:
+ typedef uint32 result_type;
+
+ static TRINITY_CONSTEXPR result_type min() { return std::numeric_limits::min(); }
+ static TRINITY_CONSTEXPR result_type max() { return std::numeric_limits::max(); }
+ result_type operator()() const { return rand32(); }
+
+ static SFMTEngine& Instance();
+};
+
+// Ugly, horrible, i don't even..., hack for VS2013 to work around missing discrete_distribution(iterator, iterator) constructor
+namespace Trinity
+{
+#if COMPILER == COMPILER_MICROSOFT && _MSC_VER <= 1800
+ template
+ struct discrete_distribution_param : public std::discrete_distribution::param_type
+ {
+ typedef typename std::discrete_distribution::param_type base;
+
+ template
+ discrete_distribution_param(InIt begin, InIt end) : base(_Noinit())
+ {
+ this->_Pvec.assign(begin, end);
+ this->_Init();
+ }
+ };
+#else
+ template
+ using discrete_distribution_param = typename std::discrete_distribution::param_type;
+#endif
+}
+
+#endif // Random_h__
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp
index 388e05a9bbb..f36dcbadbd0 100644
--- a/src/common/Utilities/Util.cpp
+++ b/src/common/Utilities/Util.cpp
@@ -20,10 +20,8 @@
#include "Common.h"
#include "CompilerDefs.h"
#include "utf8.h"
-#include "SFMT.h"
#include "Errors.h" // for ASSERT
#include
-#include
#if COMPILER == COMPILER_GNU
#include
@@ -31,61 +29,6 @@
#include
#endif
-static boost::thread_specific_ptr sfmtRand;
-
-static SFMTRand* GetRng()
-{
- SFMTRand* rand = sfmtRand.get();
-
- if (!rand)
- {
- rand = new SFMTRand();
- sfmtRand.reset(rand);
- }
-
- return rand;
-}
-
-int32 irand(int32 min, int32 max)
-{
- ASSERT(max >= min);
- return int32(GetRng()->IRandom(min, max));
-}
-
-uint32 urand(uint32 min, uint32 max)
-{
- ASSERT(max >= min);
- return GetRng()->URandom(min, max);
-}
-
-uint32 urandms(uint32 min, uint32 max)
-{
- ASSERT(max >= min);
- ASSERT(INT_MAX/IN_MILLISECONDS >= max);
- return GetRng()->URandom(min * IN_MILLISECONDS, max * IN_MILLISECONDS);
-}
-
-float frand(float min, float max)
-{
- ASSERT(max >= min);
- return float(GetRng()->Random() * (max - min) + min);
-}
-
-uint32 rand32()
-{
- return GetRng()->BRandom();
-}
-
-double rand_norm()
-{
- return GetRng()->Random();
-}
-
-double rand_chance()
-{
- return GetRng()->Random() * 100.0;
-}
-
Tokenizer::Tokenizer(const std::string &src, const char sep, uint32 vectorReserve)
{
m_str = new char[src.length() + 1];
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h
index 60f5a00e59d..b28938e55ea 100644
--- a/src/common/Utilities/Util.h
+++ b/src/common/Utilities/Util.h
@@ -21,6 +21,7 @@
#include "Define.h"
#include "Errors.h"
+#include "Random.h"
#include
#include
@@ -77,39 +78,6 @@ std::string secsToTimeString(uint64 timeInSecs, bool shortText = false, bool hou
uint32 TimeStringToSecs(const std::string& timestring);
std::string TimeToTimestampStr(time_t t);
-/* Return a random number in the range min..max. */
-int32 irand(int32 min, int32 max);
-
-/* Return a random number in the range min..max (inclusive). */
-uint32 urand(uint32 min, uint32 max);
-
-/* Return a random millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */
-uint32 urandms(uint32 min, uint32 max);
-
-/* Return a random number in the range 0 .. UINT32_MAX. */
-uint32 rand32();
-
-/* Return a random number in the range min..max */
-float frand(float min, float max);
-
-/* Return a random double from 0.0 to 1.0 (exclusive). */
-double rand_norm();
-
-/* Return a random double from 0.0 to 100.0 (exclusive). */
-double rand_chance();
-
-/* Return true if a random roll fits in the specified chance (range 0-100). */
-inline bool roll_chance_f(float chance)
-{
- return chance > rand_chance();
-}
-
-/* Return true if a random roll fits in the specified chance (range 0-100). */
-inline bool roll_chance_i(int chance)
-{
- return chance > irand(0, 99);
-}
-
inline void ApplyPercentModFloatVar(float& var, float val, bool apply)
{
if (val == -100.0f) // prevent set var to zero
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp
index 0c812ebd494..0618ec437b6 100644
--- a/src/server/authserver/Main.cpp
+++ b/src/server/authserver/Main.cpp
@@ -68,11 +68,14 @@ bool StartDB();
void StopDB();
void SignalHandler(const boost::system::error_code& error, int signalNumber);
void KeepDatabaseAliveHandler(const boost::system::error_code& error);
+void BanExpiryHandler(boost::system::error_code const& error);
variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService);
boost::asio::io_service* _ioService;
boost::asio::deadline_timer* _dbPingTimer;
uint32 _dbPingInterval;
+boost::asio::deadline_timer* _banExpiryCheckTimer;
+uint32 _banExpiryCheckInterval;
LoginDatabaseWorkerPool LoginDatabase;
int main(int argc, char** argv)
@@ -169,6 +172,11 @@ int main(int argc, char** argv)
_dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval));
_dbPingTimer->async_wait(KeepDatabaseAliveHandler);
+ _banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60);
+ _banExpiryCheckTimer = new boost::asio::deadline_timer(*_ioService);
+ _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval));
+ _banExpiryCheckTimer->async_wait(BanExpiryHandler);
+
#if PLATFORM == PLATFORM_WINDOWS
if (m_ServiceStatus != -1)
{
@@ -181,6 +189,7 @@ int main(int argc, char** argv)
// Start the io service worker loop
_ioService->run();
+ _banExpiryCheckTimer->cancel();
_dbPingTimer->cancel();
sAuthSocketMgr.StopNetwork();
@@ -192,6 +201,7 @@ int main(int argc, char** argv)
signals.cancel();
+ delete _banExpiryCheckTimer;
delete _dbPingTimer;
delete _ioService;
return 0;
@@ -242,6 +252,18 @@ void KeepDatabaseAliveHandler(const boost::system::error_code& error)
}
}
+void BanExpiryHandler(boost::system::error_code const& error)
+{
+ if (!error)
+ {
+ LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
+ LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
+
+ _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval));
+ _banExpiryCheckTimer->async_wait(BanExpiryHandler);
+ }
+}
+
#if PLATFORM == PLATFORM_WINDOWS
void ServiceStatusWatcher(boost::system::error_code const& error)
{
diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp
index f3725383065..b909694c878 100644
--- a/src/server/authserver/Server/AuthSession.cpp
+++ b/src/server/authserver/Server/AuthSession.cpp
@@ -43,12 +43,6 @@ enum eAuthCmd
XFER_CANCEL = 0x34
};
-enum eStatus
-{
- STATUS_CONNECTED = 0,
- STATUS_AUTHED
-};
-
#pragma pack(push, 1)
typedef struct AUTH_LOGON_CHALLENGE_C
@@ -115,11 +109,10 @@ enum class BufferSizes : uint32
SRP_6_S = 0x20,
};
+#define MAX_ACCEPTED_CHALLENGE_SIZE (sizeof(AUTH_LOGON_CHALLENGE_C) + 16)
+
#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4
#define REALM_LIST_PACKET_SIZE 5
-#define XFER_ACCEPT_SIZE 1
-#define XFER_RESUME_SIZE 9
-#define XFER_CANCEL_SIZE 1
std::unordered_map AuthSession::InitHandlers()
{
@@ -130,15 +123,98 @@ std::unordered_map AuthSession::InitHandlers()
handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CONNECTED, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge };
handlers[AUTH_RECONNECT_PROOF] = { STATUS_CONNECTED, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof };
handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList };
- handlers[XFER_ACCEPT] = { STATUS_AUTHED, XFER_ACCEPT_SIZE, &AuthSession::HandleXferAccept };
- handlers[XFER_RESUME] = { STATUS_AUTHED, XFER_RESUME_SIZE, &AuthSession::HandleXferResume };
- handlers[XFER_CANCEL] = { STATUS_AUTHED, XFER_CANCEL_SIZE, &AuthSession::HandleXferCancel };
return handlers;
}
std::unordered_map const Handlers = AuthSession::InitHandlers();
+void AccountInfo::LoadResult(Field* fields)
+{
+ // 0 1 2 3 4 5 6
+ //SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate,
+ // 7 8 9 10 11 12
+ // ab.unbandate = ab.bandate, aa.gmlevel, a.token_key, a.sha_pass_hash, a.v, a.s
+ //FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?
+
+ Id = fields[0].GetUInt32();
+ Login = fields[1].GetString();
+ IsLockedToIP = fields[2].GetBool();
+ LockCountry = fields[3].GetString();
+ LastIP = fields[4].GetString();
+ FailedLogins = fields[5].GetUInt32();
+ IsBanned = fields[6].GetUInt64() != 0;
+ IsPermanenetlyBanned = fields[7].GetUInt64() != 0;
+ SecurityLevel = AccountTypes(fields[8].GetUInt8());
+}
+
+AuthSession::AuthSession(tcp::socket&& socket) : Socket(std::move(socket)),
+_sentChallenge(false), _sentProof(false),
+_status(STATUS_CONNECTED), _build(0), _expversion(0)
+{
+ N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
+ g.SetDword(7);
+}
+
+void AuthSession::Start()
+{
+ std::string ip_address = GetRemoteIpAddress().to_string();
+ TC_LOG_TRACE("session", "Accepted connection from %s", ip_address.c_str());
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO);
+ stmt->setString(0, ip_address);
+ stmt->setUInt32(1, inet_addr(ip_address.c_str()));
+
+ _queryCallback = std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1);
+ _queryFuture = LoginDatabase.AsyncQuery(stmt);
+}
+
+bool AuthSession::Update()
+{
+ if (!AuthSocket::Update())
+ return false;
+
+ if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
+ {
+ auto callback = _queryCallback;
+ _queryCallback = nullptr;
+ callback(_queryFuture.get());
+ }
+
+ return true;
+}
+
+void AuthSession::CheckIpCallback(PreparedQueryResult result)
+{
+ if (result)
+ {
+ bool banned = false;
+ do
+ {
+ Field* fields = result->Fetch();
+ if (fields[0].GetUInt64() != 0)
+ banned = true;
+
+ if (!fields[1].GetString().empty())
+ _ipCountry = fields[1].GetString();
+
+ } while (result->NextRow());
+
+ if (banned)
+ {
+ ByteBuffer pkt;
+ pkt << uint8(AUTH_LOGON_CHALLENGE);
+ pkt << uint8(0x00);
+ pkt << uint8(WOW_FAIL_BANNED);
+ SendPacket(pkt);
+ TC_LOG_DEBUG("session", "[AuthSession::CheckIpCallback] Banned ip '%s:%d' tries to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort());
+ return;
+ }
+ }
+
+ AsyncRead();
+}
+
void AuthSession::ReadHandler()
{
MessageBuffer& packet = GetReadBuffer();
@@ -153,6 +229,12 @@ void AuthSession::ReadHandler()
break;
}
+ if (_status != itr->second.status)
+ {
+ CloseSocket();
+ return;
+ }
+
uint16 size = uint16(itr->second.packetSize);
if (packet.GetActiveSize() < size)
break;
@@ -161,12 +243,17 @@ void AuthSession::ReadHandler()
{
sAuthLogonChallenge_C* challenge = reinterpret_cast(packet.GetReadPointer());
size += challenge->size;
+ if (size > MAX_ACCEPTED_CHALLENGE_SIZE)
+ {
+ CloseSocket();
+ return;
+ }
}
if (packet.GetActiveSize() < size)
break;
- if (!(*this.*Handlers.at(cmd).handler)())
+ if (!(*this.*itr->second.handler)())
{
CloseSocket();
return;
@@ -187,235 +274,211 @@ void AuthSession::SendPacket(ByteBuffer& packet)
{
MessageBuffer buffer;
buffer.Write(packet.contents(), packet.size());
-
- std::unique_lock guard(_writeLock);
-
- QueuePacket(std::move(buffer), guard);
+ QueuePacket(std::move(buffer));
}
}
bool AuthSession::HandleLogonChallenge()
{
+ if (_sentChallenge)
+ return false;
+
+ _sentChallenge = true;
+
sAuthLogonChallenge_C* challenge = reinterpret_cast(GetReadBuffer().GetReadPointer());
+ if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len)
+ return false;
- //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size);
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I);
+ std::string login((const char*)challenge->I, challenge->I_len);
+ TC_LOG_DEBUG("server.authserver", "[AuthChallenge] '%s'", login.c_str());
- ByteBuffer pkt;
+ if (_queryCallback)
+ {
+ ByteBuffer pkt;
+ pkt << uint8(AUTH_LOGON_CHALLENGE);
+ pkt << uint8(0x00);
+ pkt << uint8(WOW_FAIL_DB_BUSY);
+ SendPacket(pkt);
+
+ TC_LOG_DEBUG("server.authserver", "[AuthChallenge] %s attempted to log too quick after previous attempt!", login.c_str());
+ return true;
+ }
- _login.assign((const char*)challenge->I, challenge->I_len);
_build = challenge->build;
_expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG));
- _os = (const char*)challenge->os;
-
- if (_os.size() > 4)
- return false;
+ std::array os;
+ os.fill('\0');
+ memcpy(os.data(), challenge->os, sizeof(challenge->os));
+ _os = os.data();
// Restore string order as its byte order is reversed
std::reverse(_os.begin(), _os.end());
+ _localizationName.resize(4);
+ for (int i = 0; i < 4; ++i)
+ _localizationName[i] = challenge->country[4 - i - 1];
+
+ // Get the account details from the account table
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE);
+ stmt->setString(0, login);
+
+ _queryCallback = std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1);
+ _queryFuture = LoginDatabase.AsyncQuery(stmt);
+ return true;
+}
+
+void AuthSession::LogonChallengeCallback(PreparedQueryResult result)
+{
+ ByteBuffer pkt;
pkt << uint8(AUTH_LOGON_CHALLENGE);
pkt << uint8(0x00);
- // Verify that this IP is not in the ip_banned table
- LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
+ if (!result)
+ {
+ pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
+ SendPacket(pkt);
+ return;
+ }
+
+ Field* fields = result->Fetch();
+
+ _accountInfo.LoadResult(fields);
std::string ipAddress = GetRemoteIpAddress().to_string();
uint16 port = GetRemotePort();
- PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED);
- stmt->setString(0, ipAddress);
- PreparedQueryResult result = LoginDatabase.Query(stmt);
- if (result)
+ // If the IP is 'locked', check that the player comes indeed from the correct IP address
+ if (_accountInfo.IsLockedToIP)
{
- pkt << uint8(WOW_FAIL_BANNED);
- TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned ip tries to login!", ipAddress.c_str(), port);
+ TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountInfo.Login.c_str(), _accountInfo.LastIP.c_str(), ipAddress.c_str());
+ if (_accountInfo.LastIP != ipAddress)
+ {
+ pkt << uint8(WOW_FAIL_LOCKED_ENFORCED);
+ SendPacket(pkt);
+ return;
+ }
}
else
{
- // Get the account details from the account table
- // No SQL injection (prepared statement)
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE);
- stmt->setString(0, _login);
-
- PreparedQueryResult res2 = LoginDatabase.Query(stmt);
- if (res2)
+ TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _accountInfo.Login.c_str());
+ if (_accountInfo.LockCountry.empty() || _accountInfo.LockCountry == "00")
+ TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _accountInfo.Login.c_str());
+ else if (!_accountInfo.LockCountry.empty() && !_ipCountry.empty())
{
- Field* fields = res2->Fetch();
-
- // If the IP is 'locked', check that the player comes indeed from the correct IP address
- bool locked = false;
- if (fields[2].GetUInt8() == 1) // if ip is locked
+ TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _accountInfo.Login.c_str(), _accountInfo.LockCountry.c_str(), _ipCountry.c_str());
+ if (_ipCountry != _accountInfo.LockCountry)
{
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), fields[4].GetCString());
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Player address is '%s'", ipAddress.c_str());
-
- if (strcmp(fields[4].GetCString(), ipAddress.c_str()) != 0)
- {
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP differs");
- pkt << uint8(WOW_FAIL_LOCKED_ENFORCED);
- locked = true;
- }
- else
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP matches");
- }
- else
- {
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _login.c_str());
- std::string accountCountry = fields[3].GetString();
- if (accountCountry.empty() || accountCountry == "00")
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _login.c_str());
- else if (!accountCountry.empty())
- {
- uint32 ip = inet_addr(ipAddress.c_str());
- EndianConvertReverse(ip);
-
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY);
- stmt->setUInt32(0, ip);
- if (PreparedQueryResult sessionCountryQuery = LoginDatabase.Query(stmt))
- {
- std::string loginCountry = (*sessionCountryQuery)[0].GetString();
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _login.c_str(),
- accountCountry.c_str(), loginCountry.c_str());
-
- if (loginCountry != accountCountry)
- {
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country differs.");
- pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK);
- locked = true;
- }
- else
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country matches");
- }
- else
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] IP2NATION Table empty");
- }
- }
-
- if (!locked)
- {
- //set expired bans to inactive
- LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
-
- // If the account is banned, reject the logon attempt
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED);
- stmt->setUInt32(0, fields[1].GetUInt32());
- PreparedQueryResult banresult = LoginDatabase.Query(stmt);
- if (banresult)
- {
- if ((*banresult)[0].GetUInt32() == (*banresult)[1].GetUInt32())
- {
- pkt << uint8(WOW_FAIL_BANNED);
- TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", ipAddress.c_str(),
- port, _login.c_str());
- }
- else
- {
- pkt << uint8(WOW_FAIL_SUSPENDED);
- TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!",
- ipAddress.c_str(), port, _login.c_str());
- }
- }
- else
- {
- // Get the password from the account table, upper it, and make the SRP6 calculation
- std::string rI = fields[0].GetString();
-
- // Don't calculate (v, s) if there are already some in the database
- std::string databaseV = fields[6].GetString();
- std::string databaseS = fields[7].GetString();
-
- TC_LOG_DEBUG("network", "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str());
-
- // multiply with 2 since bytes are stored as hexstring
- if (databaseV.size() != size_t(BufferSizes::SRP_6_V) * 2 || databaseS.size() != size_t(BufferSizes::SRP_6_S) * 2)
- SetVSFields(rI);
- else
- {
- s.SetHexStr(databaseS.c_str());
- v.SetHexStr(databaseV.c_str());
- }
-
- b.SetRand(19 * 8);
- BigNumber gmod = g.ModExp(b, N);
- B = ((v * 3) + gmod) % N;
-
- ASSERT(gmod.GetNumBytes() <= 32);
-
- BigNumber unk3;
- unk3.SetRand(16 * 8);
-
- // Fill the response packet with the result
- if (fields[9].GetUInt32() && AuthHelper::IsBuildSupportingBattlenet(_build))
- pkt << uint8(WOW_FAIL_USE_BATTLENET);
- else if (AuthHelper::IsAcceptedClientBuild(_build))
- pkt << uint8(WOW_SUCCESS);
- else
- pkt << uint8(WOW_FAIL_VERSION_INVALID);
-
- // B may be calculated < 32B so we force minimal length to 32B
- pkt.append(B.AsByteArray(32).get(), 32); // 32 bytes
- pkt << uint8(1);
- pkt.append(g.AsByteArray(1).get(), 1);
- pkt << uint8(32);
- pkt.append(N.AsByteArray(32).get(), 32);
- pkt.append(s.AsByteArray(int32(BufferSizes::SRP_6_S)).get(), size_t(BufferSizes::SRP_6_S)); // 32 bytes
- pkt.append(unk3.AsByteArray(16).get(), 16);
- uint8 securityFlags = 0;
-
- // Check if token is used
- _tokenKey = fields[8].GetString();
- if (!_tokenKey.empty())
- securityFlags = 4;
-
- pkt << uint8(securityFlags); // security flags (0x0...0x04)
-
- if (securityFlags & 0x01) // PIN input
- {
- pkt << uint32(0);
- pkt << uint64(0) << uint64(0); // 16 bytes hash?
- }
-
- if (securityFlags & 0x02) // Matrix input
- {
- pkt << uint8(0);
- pkt << uint8(0);
- pkt << uint8(0);
- pkt << uint8(0);
- pkt << uint64(0);
- }
-
- if (securityFlags & 0x04) // Security token input
- pkt << uint8(1);
-
- uint8 secLevel = fields[5].GetUInt8();
- _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR;
-
- _localizationName.resize(4);
- for (int i = 0; i < 4; ++i)
- _localizationName[i] = challenge->country[4 - i - 1];
-
- TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%c%c%c%c' locale (%u)",
- ipAddress.c_str(), port, _login.c_str(),
- challenge->country[3], challenge->country[2], challenge->country[1], challenge->country[0],
- GetLocaleByName(_localizationName)
- );
- }
+ pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK);
+ SendPacket(pkt);
+ return;
}
}
- else //no account
- pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
}
+ // If the account is banned, reject the logon attempt
+ if (_accountInfo.IsBanned)
+ {
+ if (_accountInfo.IsPermanenetlyBanned)
+ {
+ pkt << uint8(WOW_FAIL_BANNED);
+ SendPacket(pkt);
+ TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", ipAddress.c_str(), port, _accountInfo.Login.c_str());
+ return;
+ }
+ else
+ {
+ pkt << uint8(WOW_FAIL_SUSPENDED);
+ SendPacket(pkt);
+ TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", ipAddress.c_str(), port, _accountInfo.Login.c_str());
+ return;
+ }
+ }
+
+ // Get the password from the account table, upper it, and make the SRP6 calculation
+ std::string rI = fields[10].GetString();
+
+ // Don't calculate (v, s) if there are already some in the database
+ std::string databaseV = fields[11].GetString();
+ std::string databaseS = fields[12].GetString();
+
+ TC_LOG_DEBUG("network", "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str());
+
+ // multiply with 2 since bytes are stored as hexstring
+ if (databaseV.size() != size_t(BufferSizes::SRP_6_V) * 2 || databaseS.size() != size_t(BufferSizes::SRP_6_S) * 2)
+ SetVSFields(rI);
+ else
+ {
+ s.SetHexStr(databaseS.c_str());
+ v.SetHexStr(databaseV.c_str());
+ }
+
+ b.SetRand(19 * 8);
+ BigNumber gmod = g.ModExp(b, N);
+ B = ((v * 3) + gmod) % N;
+
+ ASSERT(gmod.GetNumBytes() <= 32);
+
+ BigNumber unk3;
+ unk3.SetRand(16 * 8);
+
+ // Fill the response packet with the result
+ if (fields[13].GetUInt32() && AuthHelper::IsBuildSupportingBattlenet(_build))
+ pkt << uint8(WOW_FAIL_USE_BATTLENET);
+ else if (AuthHelper::IsAcceptedClientBuild(_build))
+ pkt << uint8(WOW_SUCCESS);
+ else
+ pkt << uint8(WOW_FAIL_VERSION_INVALID);
+
+ // B may be calculated < 32B so we force minimal length to 32B
+ pkt.append(B.AsByteArray(32).get(), 32); // 32 bytes
+ pkt << uint8(1);
+ pkt.append(g.AsByteArray(1).get(), 1);
+ pkt << uint8(32);
+ pkt.append(N.AsByteArray(32).get(), 32);
+ pkt.append(s.AsByteArray(int32(BufferSizes::SRP_6_S)).get(), size_t(BufferSizes::SRP_6_S)); // 32 bytes
+ pkt.append(unk3.AsByteArray(16).get(), 16);
+ uint8 securityFlags = 0;
+
+ // Check if token is used
+ _tokenKey = fields[9].GetString();
+ if (!_tokenKey.empty())
+ securityFlags = 4;
+
+ pkt << uint8(securityFlags); // security flags (0x0...0x04)
+
+ if (securityFlags & 0x01) // PIN input
+ {
+ pkt << uint32(0);
+ pkt << uint64(0) << uint64(0); // 16 bytes hash?
+ }
+
+ if (securityFlags & 0x02) // Matrix input
+ {
+ pkt << uint8(0);
+ pkt << uint8(0);
+ pkt << uint8(0);
+ pkt << uint8(0);
+ pkt << uint64(0);
+ }
+
+ if (securityFlags & 0x04) // Security token input
+ pkt << uint8(1);
+
+ TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%s' locale (%u)",
+ ipAddress.c_str(), port, _accountInfo.Login.c_str(), _localizationName.c_str(), GetLocaleByName(_localizationName));
+
SendPacket(pkt);
- return true;
}
// Logon Proof command handler
bool AuthSession::HandleLogonProof()
{
-
TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof");
+ if (_sentProof)
+ return false;
+
+ _sentProof = true;
+
// Read the packet
sAuthLogonProof_C *logonProof = reinterpret_cast(GetReadBuffer().GetReadPointer());
@@ -489,7 +552,7 @@ bool AuthSession::HandleLogonProof()
t3.SetBinary(hash, 20);
sha.Initialize();
- sha.UpdateData(_login);
+ sha.UpdateData(_accountInfo.Login);
sha.Finalize();
uint8 t4[SHA_DIGEST_LENGTH];
memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH);
@@ -505,7 +568,7 @@ bool AuthSession::HandleLogonProof()
// Check if SRP6 results match (password is correct), else send an error
if (!memcmp(M.AsByteArray(sha.GetLength()).get(), logonProof->M1, 20))
{
- TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str());
+ TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str());
// Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF);
@@ -513,7 +576,7 @@ bool AuthSession::HandleLogonProof()
stmt->setString(1, GetRemoteIpAddress().to_string().c_str());
stmt->setUInt32(2, GetLocaleByName(_localizationName));
stmt->setString(3, _os);
- stmt->setString(4, _login);
+ stmt->setString(4, _accountInfo.Login);
LoginDatabase.DirectExecute(stmt);
// Finish SRP6 and send the final result to the client
@@ -528,6 +591,7 @@ bool AuthSession::HandleLogonProof()
std::string token(reinterpret_cast(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size);
GetReadBuffer().ReadCompleted(sizeof(size) + size);
uint32 validToken = TOTP::GenerateToken(_tokenKey.c_str());
+ _tokenKey.clear();
uint32 incomingToken = atoi(token.c_str());
if (validToken != incomingToken)
{
@@ -568,7 +632,7 @@ bool AuthSession::HandleLogonProof()
}
SendPacket(packet);
- _isAuthenticated = true;
+ _status = STATUS_AUTHED;
}
else
{
@@ -580,7 +644,7 @@ bool AuthSession::HandleLogonProof()
SendPacket(packet);
TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!",
- GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str());
+ GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str());
uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0);
@@ -588,7 +652,7 @@ bool AuthSession::HandleLogonProof()
if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false))
{
PreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING);
- logstmt->setString(0, _login);
+ logstmt->setString(0, _accountInfo.Login);
logstmt->setString(1, GetRemoteIpAddress().to_string());
logstmt->setString(2, "Logged on failed AccountLogin due wrong password");
@@ -599,42 +663,33 @@ bool AuthSession::HandleLogonProof()
{
//Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS);
- stmt->setString(0, _login);
+ stmt->setString(0, _accountInfo.Login);
LoginDatabase.Execute(stmt);
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_FAILEDLOGINS);
- stmt->setString(0, _login);
-
- if (PreparedQueryResult loginfail = LoginDatabase.Query(stmt))
+ if (_accountInfo.FailedLogins >= MaxWrongPassCount)
{
- uint32 failed_logins = (*loginfail)[1].GetUInt32();
+ uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
+ bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false);
- if (failed_logins >= MaxWrongPassCount)
+ if (WrongPassBanType)
{
- uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
- bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false);
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED);
+ stmt->setUInt32(0, _accountInfo.Id);
+ stmt->setUInt32(1, WrongPassBanTime);
+ LoginDatabase.Execute(stmt);
- if (WrongPassBanType)
- {
- uint32 acc_id = (*loginfail)[0].GetUInt32();
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED);
- stmt->setUInt32(0, acc_id);
- stmt->setUInt32(1, WrongPassBanTime);
- LoginDatabase.Execute(stmt);
+ TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
+ GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str(), WrongPassBanTime, _accountInfo.FailedLogins);
+ }
+ else
+ {
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
+ stmt->setString(0, GetRemoteIpAddress().to_string());
+ stmt->setUInt32(1, WrongPassBanTime);
+ LoginDatabase.Execute(stmt);
- TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
- GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str(), WrongPassBanTime, failed_logins);
- }
- else
- {
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
- stmt->setString(0, GetRemoteIpAddress().to_string());
- stmt->setUInt32(1, WrongPassBanTime);
- LoginDatabase.Execute(stmt);
-
- TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] IP got banned for '%u' seconds because account %s failed to authenticate '%u' times",
- GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), WrongPassBanTime, _login.c_str(), failed_logins);
- }
+ TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] IP got banned for '%u' seconds because account %s failed to authenticate '%u' times",
+ GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), WrongPassBanTime, _accountInfo.Login.c_str(), _accountInfo.FailedLogins);
}
}
}
@@ -645,61 +700,88 @@ bool AuthSession::HandleLogonProof()
bool AuthSession::HandleReconnectChallenge()
{
- TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectChallenge");
- sAuthLogonChallenge_C* challenge = reinterpret_cast(GetReadBuffer().GetReadPointer());
-
- //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size);
- TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I);
-
- _login.assign((const char*)challenge->I, challenge->I_len);
-
- PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SESSIONKEY);
- stmt->setString(0, _login);
- PreparedQueryResult result = LoginDatabase.Query(stmt);
-
- // Stop if the account is not found
- if (!result)
- {
- TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login and we cannot find his session key in the database.",
- GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str());
+ if (_sentChallenge)
return false;
+
+ _sentChallenge = true;
+
+ sAuthLogonChallenge_C* challenge = reinterpret_cast(GetReadBuffer().GetReadPointer());
+ if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len)
+ return false;
+
+ std::string login((const char*)challenge->I, challenge->I_len);
+ TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] '%s'", login.c_str());
+
+ if (_queryCallback)
+ {
+ ByteBuffer pkt;
+ pkt << uint8(AUTH_RECONNECT_CHALLENGE);
+ pkt << uint8(WOW_FAIL_DB_BUSY);
+ SendPacket(pkt);
+
+ TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] %s attempted to log too quick after previous attempt!", login.c_str());
+ return true;
}
- // Reinitialize build, expansion and the account securitylevel
_build = challenge->build;
_expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG));
- _os = (const char*)challenge->os;
-
- if (_os.size() > 4)
- return false;
+ std::array os;
+ os.fill('\0');
+ memcpy(os.data(), challenge->os, sizeof(challenge->os));
+ _os = os.data();
// Restore string order as its byte order is reversed
std::reverse(_os.begin(), _os.end());
- Field* fields = result->Fetch();
- uint8 secLevel = fields[2].GetUInt8();
- _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR;
+ _localizationName.resize(4);
+ for (int i = 0; i < 4; ++i)
+ _localizationName[i] = challenge->country[4 - i - 1];
- K.SetHexStr((*result)[0].GetCString());
+ // Get the account details from the account table
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE);
+ stmt->setString(0, login);
- // Sending response
+ _queryCallback = std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1);
+ _queryFuture = LoginDatabase.AsyncQuery(stmt);
+ return true;
+}
+
+void AuthSession::ReconnectChallengeCallback(PreparedQueryResult result)
+{
ByteBuffer pkt;
pkt << uint8(AUTH_RECONNECT_CHALLENGE);
- pkt << uint8(0x00);
+
+ if (!result)
+ {
+ pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
+ SendPacket(pkt);
+ return;
+ }
+
+ Field* fields = result->Fetch();
+
+ _accountInfo.LoadResult(fields);
+ K.SetHexStr(fields[9].GetCString());
_reconnectProof.SetRand(16 * 8);
- pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random
+
+ pkt << uint8(WOW_SUCCESS);
+ pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random
pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros
SendPacket(pkt);
-
- return true;
}
+
bool AuthSession::HandleReconnectProof()
{
TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof");
+ if (_sentProof)
+ return false;
+
+ _sentProof = true;
+
sAuthReconnectProof_C *reconnectProof = reinterpret_cast(GetReadBuffer().GetReadPointer());
- if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes())
+ if (_accountInfo.Login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes())
return false;
BigNumber t1;
@@ -707,7 +789,7 @@ bool AuthSession::HandleReconnectProof()
SHA1Hash sha;
sha.Initialize();
- sha.UpdateData(_login);
+ sha.UpdateData(_accountInfo.Login);
sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, NULL);
sha.Finalize();
@@ -719,13 +801,13 @@ bool AuthSession::HandleReconnectProof()
pkt << uint8(0x00);
pkt << uint16(0x00); // 2 bytes zeros
SendPacket(pkt);
- _isAuthenticated = true;
+ _status = STATUS_AUTHED;
return true;
}
else
{
TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", GetRemoteIpAddress().to_string().c_str(),
- GetRemotePort(), _login.c_str());
+ GetRemotePort(), _accountInfo.Login.c_str());
return false;
}
}
@@ -734,20 +816,31 @@ bool AuthSession::HandleRealmList()
{
TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList");
- // Get the user id (else close the connection)
- // No SQL injection (prepared statement)
- PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME);
- stmt->setString(0, _login);
- PreparedQueryResult result = LoginDatabase.Query(stmt);
- if (!result)
+ if (_queryCallback)
{
- TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login but we cannot find him in the database.", GetRemoteIpAddress().to_string().c_str(),
- GetRemotePort(), _login.c_str());
+ TC_LOG_DEBUG("server.authserver", "[RealmList] %s attempted to get realmlist too quick after previous attempt!", _accountInfo.Login.c_str());
return false;
}
- Field* fields = result->Fetch();
- uint32 id = fields[0].GetUInt32();
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS);
+ stmt->setUInt32(0, _accountInfo.Id);
+
+ _queryCallback = std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1);
+ _queryFuture = LoginDatabase.AsyncQuery(stmt);
+ return true;
+}
+
+void AuthSession::RealmListCallback(PreparedQueryResult result)
+{
+ std::map characterCounts;
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ characterCounts[fields[0].GetUInt32()] = fields[1].GetUInt8();
+ } while (result->NextRow());
+ }
// Update realm list if need
sRealmList->UpdateIfNeed();
@@ -784,25 +877,17 @@ bool AuthSession::HandleRealmList()
name = ss.str();
}
- uint8 lock = (realm.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0;
+ uint8 lock = (realm.allowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0;
- uint8 AmountOfCharacters = 0;
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_NUM_CHARS_ON_REALM);
- stmt->setUInt32(0, realm.m_ID);
- stmt->setUInt32(1, id);
- result = LoginDatabase.Query(stmt);
- if (result)
- AmountOfCharacters = (*result)[0].GetUInt8();
-
- pkt << realm.icon; // realm type
+ pkt << uint8(realm.icon); // realm type
if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
- pkt << lock; // if 1, then realm locked
+ pkt << uint8(lock); // if 1, then realm locked
pkt << uint8(flag); // RealmFlags
pkt << name;
pkt << boost::lexical_cast(realm.GetAddressForClient(GetRemoteIpAddress()));
- pkt << realm.populationLevel;
- pkt << AmountOfCharacters;
- pkt << realm.timezone; // realm category
+ pkt << float(realm.populationLevel);
+ pkt << uint8(characterCounts[realm.m_ID]);
+ pkt << uint8(realm.timezone); // realm category
if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
pkt << uint8(realm.m_ID);
else
@@ -844,32 +929,6 @@ bool AuthSession::HandleRealmList()
hdr.append(RealmListSizeBuffer); // append RealmList's size buffer
hdr.append(pkt); // append realms in the realmlist
SendPacket(hdr);
- return true;
-}
-
-// Resume patch transfer
-bool AuthSession::HandleXferResume()
-{
- TC_LOG_DEBUG("server.authserver", "Entering _HandleXferResume");
- //uint8
- //uint64
- return true;
-}
-
-// Cancel patch transfer
-bool AuthSession::HandleXferCancel()
-{
- TC_LOG_DEBUG("server.authserver", "Entering _HandleXferCancel");
- //uint8
- return false;
-}
-
-// Accept patch transfer
-bool AuthSession::HandleXferAccept()
-{
- TC_LOG_DEBUG("server.authserver", "Entering _HandleXferAccept");
- //uint8
- return true;
}
// Make the SRP6 calculation from hash in dB
@@ -897,6 +956,6 @@ void AuthSession::SetVSFields(const std::string& rI)
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS);
stmt->setString(0, v.AsHexStr());
stmt->setString(1, s.AsHexStr());
- stmt->setString(2, _login);
+ stmt->setString(2, _accountInfo.Login);
LoginDatabase.Execute(stmt);
}
diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h
index d40e0852b57..1babb7407a9 100644
--- a/src/server/authserver/Server/AuthSession.h
+++ b/src/server/authserver/Server/AuthSession.h
@@ -23,29 +23,48 @@
#include "ByteBuffer.h"
#include "Socket.h"
#include "BigNumber.h"
+#include "Callback.h"
#include
#include
using boost::asio::ip::tcp;
+class Field;
struct AuthHandler;
+enum AuthStatus
+{
+ STATUS_CONNECTED = 0,
+ STATUS_AUTHED
+};
+
+struct AccountInfo
+{
+ void LoadResult(Field* fields);
+
+ uint32 Id = 0;
+ std::string Login;
+ bool IsLockedToIP = false;
+ std::string LockCountry;
+ std::string LastIP;
+ uint32 FailedLogins = 0;
+ bool IsBanned = false;
+ bool IsPermanenetlyBanned = false;
+ AccountTypes SecurityLevel = SEC_PLAYER;
+ std::string TokenKey;
+};
+
class AuthSession : public Socket
{
+ typedef Socket AuthSocket;
+
public:
static std::unordered_map InitHandlers();
- AuthSession(tcp::socket&& socket) : Socket(std::move(socket)),
- _isAuthenticated(false), _build(0), _expversion(0), _accountSecurityLevel(SEC_PLAYER)
- {
- N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
- g.SetDword(7);
- }
+ AuthSession(tcp::socket&& socket);
- void Start() override
- {
- AsyncRead();
- }
+ void Start() override;
+ bool Update() override;
void SendPacket(ByteBuffer& packet);
@@ -59,10 +78,10 @@ private:
bool HandleReconnectProof();
bool HandleRealmList();
- //data transfer handle for patch
- bool HandleXferResume();
- bool HandleXferCancel();
- bool HandleXferAccept();
+ void CheckIpCallback(PreparedQueryResult result);
+ void LogonChallengeCallback(PreparedQueryResult result);
+ void ReconnectChallengeCallback(PreparedQueryResult result);
+ void RealmListCallback(PreparedQueryResult result);
void SetVSFields(const std::string& rI);
@@ -71,22 +90,27 @@ private:
BigNumber K;
BigNumber _reconnectProof;
- bool _isAuthenticated;
+ bool _sentChallenge;
+ bool _sentProof;
+
+ AuthStatus _status;
+ AccountInfo _accountInfo;
std::string _tokenKey;
- std::string _login;
std::string _localizationName;
std::string _os;
+ std::string _ipCountry;
uint16 _build;
uint8 _expversion;
- AccountTypes _accountSecurityLevel;
+ PreparedQueryResultFuture _queryFuture;
+ std::function _queryCallback;
};
#pragma pack(push, 1)
struct AuthHandler
{
- uint32 status;
+ AuthStatus status;
size_t packetSize;
bool (AuthSession::*handler)();
};
diff --git a/src/server/authserver/Server/AuthSocketMgr.h b/src/server/authserver/Server/AuthSocketMgr.h
index fa96502663f..a16b7d405b9 100644
--- a/src/server/authserver/Server/AuthSocketMgr.h
+++ b/src/server/authserver/Server/AuthSocketMgr.h
@@ -21,8 +21,6 @@
#include "SocketMgr.h"
#include "AuthSession.h"
-void OnSocketAccept(tcp::socket&& sock);
-
class AuthSocketMgr : public SocketMgr
{
typedef SocketMgr BaseSocketMgr;
@@ -39,7 +37,7 @@ public:
if (!BaseSocketMgr::StartNetwork(service, bindIp, port))
return false;
- _acceptor->AsyncAcceptManaged(&OnSocketAccept);
+ _acceptor->AsyncAcceptWithCallback<&AuthSocketMgr::OnSocketAccept>();
return true;
}
@@ -48,14 +46,13 @@ protected:
{
return new NetworkThread[1];
}
+
+ static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
+ {
+ Instance().OnSocketOpen(std::forward(sock), threadIndex);
+ }
};
#define sAuthSocketMgr AuthSocketMgr::Instance()
-void OnSocketAccept(tcp::socket&& sock)
-{
- sAuthSocketMgr.OnSocketOpen(std::forward(sock));
-}
-
-
#endif // AuthSocketMgr_h__
diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist
index 604988d62e5..82c3cd47148 100644
--- a/src/server/authserver/authserver.conf.dist
+++ b/src/server/authserver/authserver.conf.dist
@@ -130,6 +130,13 @@ WrongPass.BanType = 0
WrongPass.Logging = 0
+#
+# BanExpiryCheckInterval
+# Description: Time (in seconds) between checks for expired bans
+# Default: 60
+
+BanExpiryCheckInterval = 60
+
#
###################################################################################################
diff --git a/src/server/bnetserver/Packets/BitStream.h b/src/server/bnetserver/Packets/BitStream.h
index 4594cccc74f..7ad996607e2 100644
--- a/src/server/bnetserver/Packets/BitStream.h
+++ b/src/server/bnetserver/Packets/BitStream.h
@@ -217,6 +217,7 @@ namespace Battlenet
uint8* GetBuffer() { return _buffer.data(); }
uint8 const* GetBuffer() const { return _buffer.data(); }
+ uint32 GetReadPos() const { return _readPos; }
size_t GetSize() const { return ((_writePos + 7) & ~7) / 8; }
// These methods are meant to only be used when their corresponding actions in the client ignore the value completely
diff --git a/src/server/bnetserver/Packets/ConnectionPackets.h b/src/server/bnetserver/Packets/ConnectionPackets.h
index bc6193f53de..c07063d5293 100644
--- a/src/server/bnetserver/Packets/ConnectionPackets.h
+++ b/src/server/bnetserver/Packets/ConnectionPackets.h
@@ -63,6 +63,8 @@ namespace Battlenet
void Read() override { }
std::string ToString() const override;
void CallHandler(Session* session) override;
+ uint8* GetRemainingData() { return _stream.GetBuffer() + (((_stream.GetReadPos() + 7) & ~7) / 8); }
+ size_t GetRemainingSize() const { return _stream.GetSize() - (((_stream.GetReadPos() + 7) & ~7) / 8); }
};
class LogoutRequest final : public ClientPacket
diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp
index 2fed7fc3572..5506634f38b 100644
--- a/src/server/bnetserver/Server/Session.cpp
+++ b/src/server/bnetserver/Server/Session.cpp
@@ -141,6 +141,7 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const&
Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
logonResponse->SetAuthResult(AUTH_INVALID_PROGRAM);
AsyncWrite(logonResponse);
+ TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s attempted to log in with game other than WoW (using %s)!", GetClientInfo().c_str(), logonRequest.Program.c_str());
return;
}
@@ -149,6 +150,7 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const&
Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
logonResponse->SetAuthResult(AUTH_INVALID_OS);
AsyncWrite(logonResponse);
+ TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s attempted to log in from an unsupported platform (using %s)!", GetClientInfo().c_str(), logonRequest.Platform.c_str());
return;
}
@@ -157,6 +159,7 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const&
Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
logonResponse->SetAuthResult(AUTH_UNSUPPORTED_LANGUAGE);
AsyncWrite(logonResponse);
+ TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s attempted to log in with unsupported locale (using %s)!", GetClientInfo().c_str(), logonRequest.Locale.c_str());
return;
}
@@ -166,15 +169,23 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const&
{
Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
if (!sComponentMgr->HasProgram(component.Program))
+ {
logonResponse->SetAuthResult(AUTH_INVALID_PROGRAM);
+ TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s is using unsupported component program %s!", GetClientInfo().c_str(), component.Program.c_str());
+ }
else if (!sComponentMgr->HasPlatform(component.Platform))
+ {
logonResponse->SetAuthResult(AUTH_INVALID_OS);
+ TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s is using unsupported component platform %s!", GetClientInfo().c_str(), component.Platform.c_str());
+ }
else
{
if (component.Program != "WoW" || AuthHelper::IsBuildSupportingBattlenet(component.Build))
logonResponse->SetAuthResult(AUTH_REGION_BAD_VERSION);
else
logonResponse->SetAuthResult(AUTH_USE_GRUNT_LOGON);
+
+ TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s is using unsupported component version %u!", GetClientInfo().c_str(), component.Build);
}
AsyncWrite(logonResponse);
@@ -204,6 +215,7 @@ void Battlenet::Session::HandleLogonRequestCallback(PreparedQueryResult result)
Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
logonResponse->SetAuthResult(AUTH_UNKNOWN_ACCOUNT);
AsyncWrite(logonResponse);
+ TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s is trying to log in from unknown account!", GetClientInfo().c_str());
return;
}
@@ -224,7 +236,7 @@ void Battlenet::Session::HandleLogonRequestCallback(PreparedQueryResult result)
std::string ip_address = GetRemoteIpAddress().to_string();
// If the IP is 'locked', check that the player comes indeed from the correct IP address
- if (_accountInfo->IsLockedToIP) // if ip is locked
+ if (_accountInfo->IsLockedToIP)
{
TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountInfo->Login.c_str(), _accountInfo->LastIP.c_str(), ip_address.c_str());
@@ -417,9 +429,10 @@ void Battlenet::Session::HandlePing(Connection::Ping const& /*ping*/)
AsyncWrite(new Connection::Pong());
}
-void Battlenet::Session::HandleEnableEncryption(Connection::EnableEncryption const& /*enableEncryption*/)
+void Battlenet::Session::HandleEnableEncryption(Connection::EnableEncryption& enableEncryption)
{
_crypt.Init(&K);
+ _crypt.DecryptRecv(enableEncryption.GetRemainingData(), enableEncryption.GetRemainingSize());
}
void Battlenet::Session::HandleLogoutRequest(Connection::LogoutRequest const& /*logoutRequest*/)
@@ -461,12 +474,13 @@ void Battlenet::Session::HandleListSubscribeRequestCallback(PreparedQueryResult
} while (result->NextRow());
}
- for (RealmList::RealmMap::value_type const& i : sRealmList->GetRealms())
- listSubscribeResponse->RealmData.push_back(BuildListUpdate(&i.second));
-
- listSubscribeResponse->RealmData.push_back(new WoWRealm::ListComplete());
-
AsyncWrite(listSubscribeResponse);
+
+ for (RealmList::RealmMap::value_type const& i : sRealmList->GetRealms())
+ AsyncWrite(BuildListUpdate(&i.second));
+
+ AsyncWrite(new WoWRealm::ListComplete());
+
_subscribedToRealmListUpdates = true;
}
@@ -486,7 +500,7 @@ void Battlenet::Session::HandleJoinRequestV2(WoWRealm::JoinRequestV2 const& join
return;
}
- joinResponse->ServerSeed = uint32(rand32());
+ joinResponse->ServerSeed = rand32();
uint8 sessionKey[40];
HmacSha1 hmac(K.GetNumBytes(), K.AsByteArray().get());
@@ -563,7 +577,7 @@ void Battlenet::Session::ReadHandler()
if (stream.Read(1))
header.Channel = stream.Read(4);
- if (header.Channel != AUTHENTICATION && !_authed)
+ if (header.Channel != AUTHENTICATION && (header.Channel != CONNECTION || header.Opcode != Connection::CMSG_PING) && !_authed)
{
TC_LOG_DEBUG("session.packets", "%s Received not allowed %s. Client has not authed yet.", GetClientInfo().c_str(), header.ToString().c_str());
CloseSocket();
@@ -608,18 +622,6 @@ void Battlenet::Session::Start()
std::string ip_address = GetRemoteIpAddress().to_string();
TC_LOG_TRACE("session", "Accepted connection from %s", ip_address.c_str());
- if (_queryCallback)
- {
- Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
- logonResponse->SetAuthResult(AUTH_LOGON_TOO_FAST);
- AsyncWrite(logonResponse);
- TC_LOG_DEBUG("session", "[Session::Start] %s attempted to log too quick after previous attempt!", GetClientInfo().c_str());
- return;
- }
-
- // Verify that this IP is not in the ip_banned table
- LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
-
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO);
stmt->setString(0, ip_address);
stmt->setUInt32(1, inet_addr(ip_address.c_str()));
@@ -659,12 +661,37 @@ void Battlenet::Session::CheckIpCallback(PreparedQueryResult result)
bool Battlenet::Session::Update()
{
+ EncryptableBuffer* queued;
+ MessageBuffer buffer((std::size_t(BufferSizes::Read)));
+ while (_bufferQueue.Dequeue(queued))
+ {
+ std::size_t packetSize = queued->Buffer.GetActiveSize();
+ if (queued->Encrypt)
+ _crypt.EncryptSend(queued->Buffer.GetReadPointer(), packetSize);
+
+ if (buffer.GetRemainingSpace() < packetSize)
+ {
+ QueuePacket(std::move(buffer));
+ buffer.Resize(std::size_t(BufferSizes::Read));
+ }
+
+ if (buffer.GetRemainingSpace() >= packetSize)
+ buffer.Write(queued->Buffer.GetReadPointer(), packetSize);
+ else // single packet larger than 16384 bytes - client will reject.
+ QueuePacket(std::move(queued->Buffer));
+
+ delete queued;
+ }
+
+ if (buffer.GetActiveSize() > 0)
+ QueuePacket(std::move(buffer));
+
if (!BattlenetSocket::Update())
return false;
if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
- auto callback = std::move(_queryCallback);
+ auto callback = _queryCallback;
_queryCallback = nullptr;
callback(_queryFuture.get());
}
@@ -684,15 +711,12 @@ void Battlenet::Session::AsyncWrite(ServerPacket* packet)
packet->Write();
- MessageBuffer buffer;
- buffer.Write(packet->GetData(), packet->GetSize());
+ EncryptableBuffer* buffer = new EncryptableBuffer();
+ buffer->Buffer.Write(packet->GetData(), packet->GetSize());
+ buffer->Encrypt = _crypt.IsInitialized();
delete packet;
- std::unique_lock guard(_writeLock);
-
- _crypt.EncryptSend(buffer.GetReadPointer(), buffer.GetActiveSize());
-
- QueuePacket(std::move(buffer), guard);
+ _bufferQueue.Enqueue(buffer);
}
inline void ReplaceResponse(Battlenet::ServerPacket** oldResponse, Battlenet::ServerPacket* newResponse)
@@ -807,6 +831,7 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke
Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
logonResponse->SetAuthResult(AUTH_UNKNOWN_ACCOUNT);
ReplaceResponse(response, logonResponse);
+ TC_LOG_DEBUG("session", "[Battlenet::Password] %s attempted to log in with invalid password!", GetClientInfo().c_str());
return false;
}
@@ -815,12 +840,10 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke
Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
logonResponse->SetAuthResult(LOGIN_NO_GAME_ACCOUNT);
ReplaceResponse(response, logonResponse);
+ TC_LOG_DEBUG("session", "[Battlenet::Password] %s does not have any linked game accounts!", GetClientInfo().c_str());
return false;
}
- //set expired game account bans to inactive
- LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
-
BigNumber M;
sha.Initialize();
sha.UpdateBigNumbers(&A, &M1, &K, NULL);
@@ -929,6 +952,7 @@ bool Battlenet::Session::HandleSelectGameAccountModule(BitStream* dataStream, Se
Authentication::LogonResponse* complete = new Authentication::LogonResponse();
complete->SetAuthResult(LOGIN_NO_GAME_ACCOUNT);
ReplaceResponse(response, complete);
+ TC_LOG_DEBUG("session", "[Battlenet::SelectGameAccount] %s attempted to log in with invalid game account name %s!", GetClientInfo().c_str(), account.c_str());
return false;
}
@@ -961,7 +985,7 @@ bool Battlenet::Session::HandleSelectGameAccountModule(BitStream* dataStream, Se
bool Battlenet::Session::HandleRiskFingerprintModule(BitStream* dataStream, ServerPacket** response)
{
Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse();
- if (dataStream->Read(8) == 1 && _accountInfo && _gameAccountInfo)
+ if (dataStream->Read(8) == 1 && _accountInfo && _gameAccountInfo)
{
logonResponse->AccountId = _accountInfo->Id;
logonResponse->GameAccountName = _gameAccountInfo->Name;
@@ -1043,7 +1067,7 @@ bool Battlenet::Session::HandleResumeModule(BitStream* dataStream, ServerPacket*
stmt->setString(0, _accountInfo->Login);
LoginDatabase.Execute(stmt);
- TC_LOG_DEBUG("session", "[Battlenet::Resume] Invalid proof!");
+ TC_LOG_DEBUG("session", "[Battlenet::Resume] %s attempted to reconnect with invalid password!", GetClientInfo().c_str());
Authentication::ResumeResponse* resumeResponse = new Authentication::ResumeResponse();
resumeResponse->SetAuthResult(AUTH_UNKNOWN_ACCOUNT);
ReplaceResponse(response, resumeResponse);
diff --git a/src/server/bnetserver/Server/Session.h b/src/server/bnetserver/Server/Session.h
index d909c68dc24..e747e2b7407 100644
--- a/src/server/bnetserver/Server/Session.h
+++ b/src/server/bnetserver/Server/Session.h
@@ -22,7 +22,8 @@
#include "BattlenetPacketCrypt.h"
#include "Socket.h"
#include "BigNumber.h"
-#include "Database/DatabaseEnv.h"
+#include "Callback.h"
+#include "MPSCQueue.h"
#include
#include
@@ -96,7 +97,7 @@ namespace Battlenet
// Connection
void HandlePing(Connection::Ping const& ping);
- void HandleEnableEncryption(Connection::EnableEncryption const& enableEncryption);
+ void HandleEnableEncryption(Connection::EnableEncryption& enableEncryption);
void HandleLogoutRequest(Connection::LogoutRequest const& logoutRequest);
void HandleConnectionClosing(Connection::ConnectionClosing const& connectionClosing);
@@ -172,10 +173,18 @@ namespace Battlenet
std::queue _modulesWaitingForData;
+ struct EncryptableBuffer
+ {
+ MessageBuffer Buffer;
+ bool Encrypt;
+ };
+
+ MPSCQueue _bufferQueue;
+
PacketCrypt _crypt;
bool _authed;
bool _subscribedToRealmListUpdates;
-
+
PreparedQueryResultFuture _queryFuture;
std::function _queryCallback;
};
diff --git a/src/server/bnetserver/Server/SessionManager.cpp b/src/server/bnetserver/Server/SessionManager.cpp
index b0e2ceca508..c53214495d4 100644
--- a/src/server/bnetserver/Server/SessionManager.cpp
+++ b/src/server/bnetserver/Server/SessionManager.cpp
@@ -22,7 +22,8 @@ bool Battlenet::SessionManager::StartNetwork(boost::asio::io_service& service, s
if (!BaseSocketMgr::StartNetwork(service, bindIp, port))
return false;
- _acceptor->AsyncAcceptManaged(&OnSocketAccept);
+ _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this));
+ _acceptor->AsyncAcceptWithCallback<&OnSocketAccept>();
return true;
}
@@ -31,9 +32,9 @@ NetworkThread* Battlenet::SessionManager::CreateThreads() co
return new NetworkThread[GetNetworkThreadCount()];
}
-void Battlenet::SessionManager::OnSocketAccept(tcp::socket&& sock)
+void Battlenet::SessionManager::OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
{
- sSessionMgr.OnSocketOpen(std::forward(sock));
+ sSessionMgr.OnSocketOpen(std::forward(sock), threadIndex);
}
void Battlenet::SessionManager::AddSession(Session* session)
@@ -46,7 +47,11 @@ void Battlenet::SessionManager::AddSession(Session* session)
void Battlenet::SessionManager::RemoveSession(Session* session)
{
std::unique_lock lock(_sessionMutex);
- _sessions.erase({ session->GetAccountId(), session->GetGameAccountId() });
+ auto itr = _sessions.find({ session->GetAccountId(), session->GetGameAccountId() });
+ // Remove old session only if it was not overwritten by reconnecting
+ if (itr != _sessions.end() && itr->second == session)
+ _sessions.erase(itr);
+
_sessionsByAccountId[session->GetAccountId()].remove(session);
}
diff --git a/src/server/bnetserver/Server/SessionManager.h b/src/server/bnetserver/Server/SessionManager.h
index 014d5a4bd98..5cf0b199f15 100644
--- a/src/server/bnetserver/Server/SessionManager.h
+++ b/src/server/bnetserver/Server/SessionManager.h
@@ -40,7 +40,7 @@ namespace Battlenet
#pragma pack(pop)
- class SessionManager : SocketMgr
+ class SessionManager : public SocketMgr
{
typedef SocketMgr BaseSocketMgr;
typedef std::map SessionMap;
@@ -75,7 +75,7 @@ namespace Battlenet
NetworkThread* CreateThreads() const override;
private:
- static void OnSocketAccept(tcp::socket&& sock);
+ static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex);
SessionMap _sessions;
SessionByAccountMap _sessionsByAccountId;
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index 3885aa098b7..7367f6ea02b 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -72,7 +72,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH);
- PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, extendState, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
"base_amount0, base_amount1, base_amount2, maxduration, remaintime, remaincharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_SPELL, "SELECT spell, active, disabled FROM character_spell WHERE guid = ?", CONNECTION_ASYNC);
@@ -426,8 +426,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_WORLDSTATE, "UPDATE worldstates SET value = ? WHERE entry = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_WORLDSTATE, "INSERT INTO worldstates (entry, value) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID, "DELETE FROM character_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_UPD_CHAR_INSTANCE, "UPDATE character_instance SET instance = ?, permanent = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_INSTANCE, "UPDATE character_instance SET instance = ?, permanent = ?, extendState = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent, extendState) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_GENDER_PLAYERBYTES, "UPDATE characters SET gender = ?, playerBytes = ?, playerBytes2 = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHARACTER_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS, "UPDATE character_social SET flags = flags | ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC);
@@ -455,6 +455,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, "SELECT class, level, at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES, "SELECT at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PERM_BIND_BY_INSTANCE, "SELECT guid FROM character_instance WHERE instance = ? and permanent = 1", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_OLD_CHARS, "SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < ?", CONNECTION_SYNCH);
@@ -490,9 +491,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_CHAR_GIFT, "INSERT INTO character_gifts (guid, item_guid, entry, flags) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_INSTANCE_BY_INSTANCE, "DELETE FROM instance WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, "DELETE FROM character_instance WHERE instance = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF, "DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = ? and difficulty = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF, "DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE (extendState = 0 or permanent = 0) and map = ? and difficulty = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF, "DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = ? and difficulty = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_DEL_INSTANCE_BY_MAP_DIFF, "DELETE FROM instance WHERE map = ? and difficulty = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF, "DELETE FROM instance WHERE map = ? and difficulty = ? and (SELECT guid FROM character_instance WHERE extendState != 0 AND instance = id LIMIT 1) IS NULL", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF, "UPDATE character_instance LEFT JOIN instance ON character_instance.instance = id SET extendState = extendState-1 WHERE map = ? and difficulty = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_MAIL_ITEM_BY_ID, "DELETE FROM mail_items WHERE mail_id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PETITION_BY_GUID, "DELETE FROM petition WHERE petitionguid = ?", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index c6e9f01b897..bb169628df0 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -384,6 +384,7 @@ enum CharacterDatabaseStatements
CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN,
CHAR_SEL_CHAR_AT_LOGIN_TITLES,
CHAR_SEL_INSTANCE,
+ CHAR_SEL_PERM_BIND_BY_INSTANCE,
CHAR_SEL_CHAR_COD_ITEM_MAIL,
CHAR_SEL_CHAR_SOCIAL,
CHAR_SEL_CHAR_OLD_CHARS,
@@ -414,9 +415,10 @@ enum CharacterDatabaseStatements
CHAR_INS_CHAR_GIFT,
CHAR_DEL_INSTANCE_BY_INSTANCE,
CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE,
- CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF,
+ CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF,
CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF,
- CHAR_DEL_INSTANCE_BY_MAP_DIFF,
+ CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF,
+ CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF,
CHAR_DEL_MAIL_ITEM_BY_ID,
CHAR_INS_PETITION,
CHAR_DEL_PETITION_BY_GUID,
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
index 270d68693e3..bcf551f4737 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -24,26 +24,27 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_IP_BANNED, "SELECT * FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_IP_INFO, "(SELECT unbandate > UNIX_TIMESTAMP() OR unbandate = bandate AS banned, NULL as country FROM ip_banned WHERE ip = ?) "
"UNION "
"(SELECT NULL AS banned, country FROM ip2nation WHERE INET_NTOA(ip) = ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_IP_AUTO_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban')", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_IP_BANNED_ALL, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) ORDER BY unbandate", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_IP_BANNED_BY_IP, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) AND ip LIKE CONCAT('%%', ?, '%%') ORDER BY unbandate", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED, "SELECT bandate, unbandate FROM account_banned WHERE id = ? AND active = 1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_ALL, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 GROUP BY account.id", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 AND username LIKE CONCAT('%%', ?, '%%') GROUP BY account.id", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED, "INSERT INTO account_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban', 1)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_SEL_SESSIONKEY, "SELECT a.sessionkey, a.id, aa.gmlevel FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.sha_pass_hash, a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.v, a.s, a.token_key, a.battlenet_account FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.id, UPPER(a.username), a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, "
+ "ab.unbandate = ab.bandate, aa.gmlevel, a.token_key, a.sha_pass_hash, a.v, a.s, a.battlenet_account "
+ "FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_RECONNECTCHALLENGE, "SELECT a.id, UPPER(a.username), a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, "
+ "ab.unbandate = ab.bandate, aa.gmlevel, a.sessionKey "
+ "FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_FAILEDLOGINS, "UPDATE account SET failed_logins = failed_logins + 1 WHERE username = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, "
@@ -53,7 +54,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
"WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS, "SELECT realmid, numchars FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_ID, "SELECT 1 FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_IP_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?)", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
index e966b7a3ac6..34efaf86fd0 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.h
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -32,26 +32,23 @@ enum LoginDatabaseStatements
LOGIN_SEL_REALMLIST,
LOGIN_DEL_EXPIRED_IP_BANS,
LOGIN_UPD_EXPIRED_ACCOUNT_BANS,
- LOGIN_SEL_IP_BANNED,
LOGIN_SEL_IP_INFO,
LOGIN_INS_IP_AUTO_BANNED,
- LOGIN_SEL_ACCOUNT_BANNED,
LOGIN_SEL_ACCOUNT_BANNED_ALL,
LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME,
LOGIN_INS_ACCOUNT_AUTO_BANNED,
LOGIN_DEL_ACCOUNT_BANNED,
- LOGIN_SEL_SESSIONKEY,
LOGIN_UPD_VS,
LOGIN_UPD_LOGONPROOF,
LOGIN_SEL_LOGONCHALLENGE,
+ LOGIN_SEL_RECONNECTCHALLENGE,
LOGIN_SEL_LOGON_COUNTRY,
LOGIN_UPD_FAILEDLOGINS,
- LOGIN_SEL_FAILEDLOGINS,
LOGIN_SEL_ACCOUNT_ID_BY_NAME,
LOGIN_SEL_ACCOUNT_LIST_BY_NAME,
LOGIN_SEL_ACCOUNT_INFO_BY_NAME,
LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL,
- LOGIN_SEL_NUM_CHARS_ON_REALM,
+ LOGIN_SEL_REALM_CHARACTER_COUNTS,
LOGIN_SEL_ACCOUNT_BY_IP,
LOGIN_INS_IP_BANNED,
LOGIN_DEL_IP_NOT_BANNED,
diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp
index fd0dbdd4b5a..001fdf20610 100644
--- a/src/server/database/Updater/UpdateFetcher.cpp
+++ b/src/server/database/Updater/UpdateFetcher.cpp
@@ -142,14 +142,12 @@ UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path co
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::max());
- auto const char_count = in.gcount();
- in.seekg(start_pos);
+ auto update = [&in] {
+ std::ostringstream ss;
+ ss << in.rdbuf();
+ return Trinity::make_unique(ss.str());
+ }();
- SQLUpdate const update(new std::string(char_count, char{}));
-
- in.read(&(*update)[0], update->size());
in.close();
return update;
}
diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h
index 22a0d08c7f8..32f8516413d 100644
--- a/src/server/database/Updater/UpdateFetcher.h
+++ b/src/server/database/Updater/UpdateFetcher.h
@@ -103,7 +103,7 @@ private:
typedef std::unordered_map HashToFileNameStorage;
typedef std::unordered_map AppliedFileStorage;
typedef std::vector DirectoryStorage;
- typedef std::shared_ptr SQLUpdate;
+ typedef std::unique_ptr SQLUpdate;
LocaleFileStorage GetFileList() const;
void FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const;
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp
index c254a9124c1..352ae635878 100644
--- a/src/server/game/AI/CreatureAI.cpp
+++ b/src/server/game/AI/CreatureAI.cpp
@@ -133,7 +133,7 @@ void CreatureAI::MoveInLineOfSight(Unit* who)
if (me->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) // non-combat pets should just stand there and look good;)
return;
- if (me->CanStartAttack(who, false))
+ if (me->HasReactState(REACT_AGGRESSIVE) && me->CanStartAttack(who, false))
AttackStart(who);
//else if (who->GetVictim() && me->IsFriendlyTo(who)
// && me->IsWithinDistInMap(who, sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS))
@@ -350,7 +350,7 @@ int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill) con
return boundsWarning ? LANG_CREATURE_MOVEMENT_MAYBE_UNBOUNDED : 0;
}
-bool CreatureAI::CheckBoundary(Position* who) const
+bool CreatureAI::CheckBoundary(Position const* who) const
{
if (!who)
who = me;
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index f250a79ea25..239fda577a7 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -79,7 +79,7 @@ class CreatureAI : public UnitAI
Creature* DoSummon(uint32 entry, WorldObject* obj, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
- bool CheckBoundary(Position* who = nullptr) const;
+ bool CheckBoundary(Position const* who = nullptr) const;
void SetBoundary(CreatureBoundary const* boundary) { _boundary = boundary; me->DoImmediateBoundaryCheck(); }
public:
enum EvadeReason
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index fe957abcc75..608534f634e 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -547,18 +547,27 @@ void BossAI::UpdateAI(uint32 diff)
DoMeleeAttackIfReady();
}
-void BossAI::_DespawnAtEvade()
+void BossAI::_DespawnAtEvade(uint32 delayToRespawn)
{
+ if (delayToRespawn < 2)
+ {
+ TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %u seconds, defaulting to 2.", delayToRespawn);
+ delayToRespawn = 2;
+ }
+
uint32 corpseDelay = me->GetCorpseDelay();
uint32 respawnDelay = me->GetRespawnDelay();
me->SetCorpseDelay(1);
- me->SetRespawnDelay(29);
+ me->SetRespawnDelay(delayToRespawn - 1);
me->DespawnOrUnsummon();
me->SetCorpseDelay(corpseDelay);
me->SetRespawnDelay(respawnDelay);
+
+ if(instance)
+ instance->SetBossState(_bossId, FAIL);
}
// WorldBossAI - for non-instanced bosses
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index ff0e7d3d7f9..5452a033a17 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -360,12 +360,14 @@ class BossAI : public ScriptedAI
void JustDied(Unit* /*killer*/) override { _JustDied(); }
void JustReachedHome() override { _JustReachedHome(); }
+ bool CanAIAttack(Unit const* target) const override { return CheckBoundary(target); }
+
protected:
void _Reset();
void _EnterCombat();
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
- void _DespawnAtEvade();
+ void _DespawnAtEvade(uint32 delayToRespawn = 30);
void TeleportCheaters();
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index 68cb8d346d0..fa80634e0f0 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -105,7 +105,7 @@ bool npc_escortAI::AssistPlayerInCombat(Unit* who)
void npc_escortAI::MoveInLineOfSight(Unit* who)
{
- if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
+ if (me->HasReactState(REACT_AGGRESSIVE) && !me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
{
if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombat(who))
return;
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
index dc9f6d2681e..778edf8cab5 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -102,7 +102,7 @@ bool FollowerAI::AssistPlayerInCombat(Unit* who)
void FollowerAI::MoveInLineOfSight(Unit* who)
{
- if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
+ if (me->HasReactState(REACT_AGGRESSIVE) && !me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
{
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(who))
return;
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index eca327e770e..361bb1a5b1d 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -466,6 +466,9 @@ bool SmartAI::CanAIAttack(const Unit* /*who*/) const
bool SmartAI::AssistPlayerInCombat(Unit* who)
{
+ if (me->HasReactState(REACT_PASSIVE))
+ return false;
+
if (!who || !who->GetVictim())
return false;
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 206f8d121da..0d5ed677b9c 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -2008,7 +2008,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (Creature* creature = (*itr)->ToCreature())
{
creature->GetMotionMaster()->Clear();
- creature->GetMotionMaster()->MoveJump(e.target.x, e.target.y, e.target.z, (float)e.action.jump.speedxy, (float)e.action.jump.speedz);
+ creature->GetMotionMaster()->MoveJump(e.target.x, e.target.y, e.target.z, 0.0f, (float)e.action.jump.speedxy, (float)e.action.jump.speedz); // @todo add optional jump orientation support?
}
}
/// @todo Resume path when reached jump location
@@ -2315,6 +2315,31 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
break;
}
+ case SMART_ACTION_RANDOM_SOUND:
+ {
+ std::vector sounds;
+ std::copy_if(e.action.randomSound.sounds.begin(), e.action.randomSound.sounds.end(),
+ std::back_inserter(sounds), [](uint32 sound) { return sound != 0; });
+
+ bool onlySelf = e.action.randomSound.onlySelf != 0;
+
+ if (ObjectList* targets = GetTargets(e, unit))
+ {
+ for (WorldObject* const obj : *targets)
+ {
+ if (IsUnit(obj))
+ {
+ uint32 sound = Trinity::Containers::SelectRandomContainerElement(sounds);
+ obj->PlayDirectSound(sound, onlySelf ? obj->ToPlayer() : nullptr);
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_RANDOM_SOUND: target: %s (%s), sound: %u, onlyself: %s",
+ obj->GetName().c_str(), obj->GetGUID().ToString().c_str(), sound, onlySelf ? "true" : "false");
+ }
+ }
+
+ delete targets;
+ break;
+ }
+ }
default:
TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u, Unhandled Action type %u", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
break;
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index e7782f46020..feede2deb59 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -468,7 +468,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.map);
return false;
}
- if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !GetAreaEntryByAreaID(e.event.respawn.area))
+ if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !sAreaTableStore.LookupEntry(e.event.respawn.area))
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Area entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.area);
return false;
@@ -827,6 +827,21 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
if (e.action.randomEmote.emote6 && !IsEmoteValid(e, e.action.randomEmote.emote6))
return false;
break;
+ case SMART_ACTION_RANDOM_SOUND:
+ {
+ if (std::all_of(e.action.randomSound.sounds.begin(), e.action.randomSound.sounds.end(), [](uint32 sound) { return sound == 0; }))
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u does not have any non-zero sound",
+ e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+
+ for (uint32 sound : e.action.randomSound.sounds)
+ if (sound && !IsSoundValid(e, sound))
+ return false;
+
+ break;
+ }
case SMART_ACTION_CAST:
{
if (!IsSpellValid(e, e.action.cast.spell))
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index 324c04d87ce..6e1fb5d81bb 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -44,6 +44,16 @@ struct WayPoint
float z;
};
+enum eSmartAI
+{
+ SMART_EVENT_PARAM_COUNT = 4,
+ SMART_ACTION_PARAM_COUNT = 6,
+ SMART_SUMMON_COUNTER = 0xFFFFFF,
+ SMART_ESCORT_LAST_OOC_POINT = 0xFFFFFF,
+ SMART_RANDOM_POINT = 0xFFFFFE,
+ SMART_ESCORT_TARGETS = 0xFFFFFF
+};
+
enum SMART_EVENT_PHASE
{
SMART_EVENT_PHASE_ALWAYS = 0,
@@ -541,8 +551,9 @@ enum SMART_ACTION
SMART_ACTION_GAME_EVENT_START = 112, // GameEventId
SMART_ACTION_START_CLOSEST_WAYPOINT = 113, // wp1, wp2, wp3, wp4, wp5, wp6, wp7
SMART_ACTION_RISE_UP = 114, // distance
+ SMART_ACTION_RANDOM_SOUND = 115, // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf
- SMART_ACTION_END = 115
+ SMART_ACTION_END = 116
};
struct SmartAction
@@ -1025,6 +1036,12 @@ struct SmartAction
uint32 wp6;
} closestWaypointFromList;
+ struct
+ {
+ std::array sounds;
+ uint32 onlySelf;
+ } randomSound;
+
//! Note for any new future actions
//! All parameters must have type uint32
@@ -1188,16 +1205,6 @@ struct SmartTarget
};
};
-enum eSmartAI
-{
- SMART_EVENT_PARAM_COUNT = 4,
- SMART_ACTION_PARAM_COUNT = 6,
- SMART_SUMMON_COUNTER = 0xFFFFFF,
- SMART_ESCORT_LAST_OOC_POINT = 0xFFFFFF,
- SMART_RANDOM_POINT = 0xFFFFFE,
- SMART_ESCORT_TARGETS = 0xFFFFFF
-};
-
enum SmartScriptType
{
SMART_SCRIPT_TYPE_CREATURE = 0, //done
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index 961b99b71a8..c8ca970d193 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -47,7 +47,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
{
if (dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` for criteria (Entry: %u) has wrong data type (%u), ignored.", criteria->ID, dataType);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` for criteria (Entry: %u) contains a wrong data type (%u), ignored.", criteria->ID, dataType);
return false;
}
@@ -82,7 +82,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
default:
if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has data for non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->type);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains data for a non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->type);
return false;
}
break;
@@ -96,7 +96,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
if (!creature.id || !sObjectMgr->GetCreatureTemplate(creature.id))
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) has non-existing creature id in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) contains a non-existing creature id in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, creature.id);
return false;
}
@@ -110,13 +110,13 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
}
if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) has non-existing class in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) contains a non-existing class in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, classRace.class_id);
return false;
}
if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) has non-existing race in value2 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) contains a non-existing race in value2 (%u), ignored.",
criteria->ID, criteria->type, dataType, classRace.race_id);
return false;
}
@@ -124,7 +124,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
if (health.percent < 1 || health.percent > 100)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) has wrong percent value in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) contains a wrong percent value in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, health.percent);
return false;
}
@@ -135,19 +135,19 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(aura.spell_id);
if (!spellEntry)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell id in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a wrong spell id in value1 (%u), ignored.",
criteria->ID, criteria->type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.spell_id);
return false;
}
if (aura.effect_idx >= 3)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell effect index in value2 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a wrong spell effect index in value2 (%u), ignored.",
criteria->ID, criteria->type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.effect_idx);
return false;
}
if (!spellEntry->Effects[aura.effect_idx].ApplyAuraName)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has non-aura spell effect (ID: %u Effect: %u), ignores.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a non-aura spell effect (ID: %u Effect: %u), ignored.",
criteria->ID, criteria->type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.spell_id, aura.effect_idx);
return false;
}
@@ -156,7 +156,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE:
if (value.compType >= COMP_TYPE_MAX)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE (%u) has wrong ComparisionType in value2 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE (%u) contains a wrong ComparisionType in value2 (%u), ignored.",
criteria->ID, criteria->type, dataType, value.compType);
return false;
}
@@ -164,7 +164,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL:
if (level.minlevel > STRONG_MAX_LEVEL)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL (%u) has wrong minlevel in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL (%u) contains a wrong minlevel in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, level.minlevel);
return false;
}
@@ -172,7 +172,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER:
if (gender.gender > GENDER_NONE)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER (%u) has wrong gender in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER (%u) contains a wrong gender value in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, gender.gender);
return false;
}
@@ -180,7 +180,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT:
if (!ScriptId)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT (%u) does not have ScriptName set, ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT (%u) does not have a ScriptName set, ignored.",
criteria->ID, criteria->type, dataType);
return false;
}
@@ -188,7 +188,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT:
if (map_players.maxcount <= 0)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT (%u) has wrong max players count in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT (%u) contains a wrong max players count in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, map_players.maxcount);
return false;
}
@@ -196,7 +196,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM:
if (team.team != ALLIANCE && team.team != HORDE)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM (%u) has unknown team in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM (%u) contains an unknown team value in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, team.team);
return false;
}
@@ -204,7 +204,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK:
if (drunk.state >= MAX_DRUNKEN)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK (%u) has unknown drunken state in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK (%u) contains an unknown drunken state value in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, drunk.state);
return false;
}
@@ -212,7 +212,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY:
if (!sHolidaysStore.LookupEntry(holiday.id))
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) has unknown holiday in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) contains an unknown holiday entry in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, holiday.id);
return false;
}
@@ -233,7 +233,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM:
if (equipped_item.item_quality >= MAX_ITEM_QUALITY)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM (%u) has unknown quality state in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM (%u) contains an unknown quality state value in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, equipped_item.item_quality);
return false;
}
@@ -241,19 +241,19 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE:
if (!classRace.class_id && !classRace.race_id)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) must not have 0 in either value field, ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) should not have 0 in either value field. Ignored.",
criteria->ID, criteria->type, dataType);
return false;
}
if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) has non-existing class in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) contains a non-existing class entry in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, classRace.class_id);
return false;
}
if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) has non-existing race in value2 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) contains a non-existing race entry in value2 (%u), ignored.",
criteria->ID, criteria->type, dataType, classRace.race_id);
return false;
}
@@ -261,13 +261,13 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE:
if (!sCharTitlesStore.LookupEntry(known_title.title_id))
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE (%u) have unknown title_id in value1 (%u), ignore.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE (%u) contains an unknown title_id in value1 (%u), ignore.",
criteria->ID, criteria->type, dataType, known_title.title_id);
return false;
}
return true;
default:
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) has data for non-supported data type (%u), ignored.", criteria->ID, criteria->type, dataType);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) contains data of a non-supported data type (%u), ignored.", criteria->ID, criteria->type, dataType);
return false;
}
}
@@ -346,14 +346,14 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un
Map* map = source->GetMap();
if (!map->IsDungeon())
{
- TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-dungeon/non-raid map %u",
+ TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u in a non-dungeon/non-raid map %u",
dataType, criteria_id, map->GetId());
return false;
}
InstanceScript* instance = map->ToInstanceMap()->GetInstanceScript();
if (!instance)
{
- TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map does not have a instance script",
+ TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u in map %u, but the map does not have an instance script.",
dataType, criteria_id, map->GetId());
return false;
}
@@ -695,8 +695,8 @@ void AchievementMgr::LoadFromDB(PreparedQueryResult achievementResult, P
AchievementCriteriaEntry const* criteria = sAchievementMgr->GetAchievementCriteria(id);
if (!criteria)
{
- // we will remove not existed criteria for all characters
- TC_LOG_ERROR("achievement", "Non-existing achievement criteria %u data removed from table `character_achievement_progress`.", id);
+ // Removing non-existing criteria data for all characters
+ TC_LOG_ERROR("achievement", "Non-existing achievement criteria %u data has been removed from the table `character_achievement_progress`.", id);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA);
stmt->setUInt16(0, uint16(id));
@@ -2636,17 +2636,15 @@ bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* ac
bool matchFound = false;
for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
{
- uint32 area_id = worldOverlayEntry->areatableID[j];
- if (!area_id) // array have 0 only in empty tail
+ AreaTableEntry const* area = sAreaTableStore.LookupEntry(worldOverlayEntry->areatableID[j]);
+ if (!area)
break;
- int32 exploreFlag = GetAreaFlagByAreaID(area_id);
- if (exploreFlag < 0)
+ uint32 playerIndexOffset = uint32(area->exploreFlag) / 32;
+ if (playerIndexOffset >= PLAYER_EXPLORED_ZONES_SIZE)
continue;
- uint32 playerIndexOffset = uint32(exploreFlag) / 32;
- uint32 mask = 1 << (uint32(exploreFlag) % 32);
-
+ uint32 mask = 1 << (uint32(area->exploreFlag) % 32);
if (referencePlayer->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
{
matchFound = true;
@@ -3165,7 +3163,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
m_AchievementCriteriasByTimedType[criteria->timedCriteriaStartType].push_back(criteria);
}
- TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria and %u guild achievement crieteria in %u ms", criterias, guildCriterias, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria and %u guild achievement crieteria in %u ms.", criterias, guildCriterias, GetMSTimeDiffToNow(oldMSTime));
}
void AchievementGlobalMgr::LoadAchievementReferenceList()
@@ -3194,7 +3192,7 @@ void AchievementGlobalMgr::LoadAchievementReferenceList()
if (AchievementEntry const* achievement = sAchievementMgr->GetAchievement(4539))
const_cast(achievement)->mapID = 631; // Correct map requirement (currently has Ulduar)
- TC_LOG_INFO("server.loading", ">> Loaded %u achievement references in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u achievement references in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
void AchievementGlobalMgr::LoadAchievementCriteriaData()
@@ -3222,7 +3220,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
if (!criteria)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has data for non-existing criteria (Entry: %u), ignore.", criteria_id);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains data for non-existing criteria (Entry: %u). Ignored.", criteria_id);
continue;
}
@@ -3232,7 +3230,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
if (scriptName.length()) // not empty
{
if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT)
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has ScriptName set for non-scripted data type (Entry: %u, type %u), useless data.", criteria_id, dataType);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains a ScriptName for non-scripted data type (Entry: %u, type %u), useless data.", criteria_id, dataType);
else
scriptId = sObjectMgr->GetScriptId(scriptName);
}
@@ -3278,8 +3276,8 @@ void AchievementGlobalMgr::LoadCompletedAchievements()
const AchievementEntry* achievement = sAchievementMgr->GetAchievement(achievementId);
if (!achievement)
{
- // Remove non existent achievements from all characters
- TC_LOG_ERROR("achievement", "Non-existing achievement %u data removed from table `character_achievement`.", achievementId);
+ // Remove non-existing achievements from all characters
+ TC_LOG_ERROR("achievement", "Non-existing achievement %u data has been removed from the table `character_achievement`.", achievementId);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEVMENT);
stmt->setUInt16(0, uint16(achievementId));
@@ -3292,7 +3290,7 @@ void AchievementGlobalMgr::LoadCompletedAchievements()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %lu realm first completed achievements in %u ms", (unsigned long)m_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %lu realm first completed achievements in %u ms.", (unsigned long)m_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime));
}
void AchievementGlobalMgr::LoadRewards()
@@ -3319,7 +3317,7 @@ void AchievementGlobalMgr::LoadRewards()
AchievementEntry const* achievement = GetAchievement(entry);
if (!achievement)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` has wrong achievement (Entry: %u), ignored.", entry);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` contains a wrong achievement entry (Entry: %u), ignored.", entry);
continue;
}
@@ -3335,19 +3333,19 @@ void AchievementGlobalMgr::LoadRewards()
// must be title or mail at least
if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have title or item reward data, ignored.", entry);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not contain title or item reward data. Ignored.", entry);
continue;
}
if (achievement->requiredFaction == ACHIEVEMENT_FACTION_ANY && (!reward.titleId[0] ^ !reward.titleId[1]))
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) for only one team.", entry, reward.titleId[0], reward.titleId[1]);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains the title (A: %u H: %u) for only one team.", entry, reward.titleId[0], reward.titleId[1]);
if (reward.titleId[0])
{
CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
if (!titleEntry)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
reward.titleId[0] = 0;
}
}
@@ -3357,7 +3355,7 @@ void AchievementGlobalMgr::LoadRewards()
CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
if (!titleEntry)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_H`, set to 0", entry, reward.titleId[1]);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_H`, set to 0", entry, reward.titleId[1]);
reward.titleId[1] = 0;
}
}
@@ -3367,41 +3365,41 @@ void AchievementGlobalMgr::LoadRewards()
{
if (!sObjectMgr->GetCreatureTemplate(reward.sender))
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
reward.sender = 0;
}
}
else
{
if (reward.itemId)
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has item reward, item will not be rewarded.", entry);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains an item reward. Item will not be rewarded.", entry);
if (!reward.subject.empty())
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has mail subject.", entry);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains a mail subject.", entry);
if (!reward.text.empty())
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has mail text.", entry);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains mail text.", entry);
if (reward.mailTemplate)
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has mailTemplate.", entry);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but has a mailTemplate.", entry);
}
if (reward.mailTemplate)
{
if (!sMailTemplateStore.LookupEntry(reward.mailTemplate))
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid mailTemplate (%u).", entry, reward.mailTemplate);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using an invalid mailTemplate (%u).", entry, reward.mailTemplate);
reward.mailTemplate = 0;
}
else if (!reward.subject.empty() || !reward.text.empty())
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has mailTemplate (%u) and mail subject/text.", entry, reward.mailTemplate);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using mailTemplate (%u) and mail subject/text.", entry, reward.mailTemplate);
}
if (reward.itemId)
{
if (!sObjectMgr->GetItemTemplate(reward.itemId))
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will not contain item.", entry, reward.itemId);
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid item id %u, reward mail will not contain the rewarded item.", entry, reward.itemId);
reward.itemId = 0;
}
}
@@ -3411,7 +3409,7 @@ void AchievementGlobalMgr::LoadRewards()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u achievement rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u achievement rewards in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
void AchievementGlobalMgr::LoadRewardLocales()
@@ -3426,7 +3424,7 @@ void AchievementGlobalMgr::LoadRewardLocales()
if (!result)
{
- TC_LOG_INFO("server.loading", ">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
return;
}
@@ -3438,7 +3436,7 @@ void AchievementGlobalMgr::LoadRewardLocales()
if (m_achievementRewards.find(entry) == m_achievementRewards.end())
{
- TC_LOG_ERROR("sql.sql", "Table `locales_achievement_reward` (Entry: %u) has locale strings for non-existing achievement reward.", entry);
+ TC_LOG_ERROR("sql.sql", "Table `locales_achievement_reward` (Entry: %u) contains locale strings for a non-existing achievement reward.", entry);
continue;
}
@@ -3453,7 +3451,7 @@ void AchievementGlobalMgr::LoadRewardLocales()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u achievement reward locale strings in %u ms", uint32(m_achievementRewardLocales.size()), GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u achievement reward locale strings in %u ms.", uint32(m_achievementRewardLocales.size()), GetMSTimeDiffToNow(oldMSTime));
}
AchievementEntry const* AchievementGlobalMgr::GetAchievement(uint32 achievementId) const
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
index 04cbf41d810..16c65df4e7c 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
@@ -576,6 +576,14 @@ void AuctionHouseObject::Update()
if (AuctionsMap.empty())
return;
+ // Clear expired throttled players
+ for (PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.begin(); itr != GetAllThrottleMap.end();)
+ {
+ if (itr->second <= curTime)
+ itr = GetAllThrottleMap.erase(itr);
+ else
+ ++itr;
+ }
SQLTransaction trans = CharacterDatabase.BeginTransaction();
@@ -651,13 +659,40 @@ void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player,
void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
- uint32& count, uint32& totalcount)
+ uint32& count, uint32& totalcount, bool getall)
{
int loc_idx = player->GetSession()->GetSessionDbLocaleIndex();
int locdbc_idx = player->GetSession()->GetSessionDbcLocale();
time_t curTime = sWorld->GetGameTime();
+ PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.find(player->GetGUID());
+ time_t throttleTime = itr != GetAllThrottleMap.end() ? itr->second : curTime;
+
+ if (getall && throttleTime <= curTime)
+ {
+ for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
+ {
+ AuctionEntry* Aentry = itr->second;
+ // Skip expired auctions
+ if (Aentry->expire_time < curTime)
+ continue;
+
+ Item* item = sAuctionMgr->GetAItem(Aentry->itemGUIDLow);
+ if (!item)
+ continue;
+
+ ++count;
+ ++totalcount;
+ Aentry->BuildAuctionInfo(data, item);
+
+ if (count >= MAX_GETALL_RETURN)
+ break;
+ }
+ GetAllThrottleMap[player->GetGUID()] = curTime + sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY);
+ return;
+ }
+
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
{
AuctionEntry* Aentry = itr->second;
@@ -747,16 +782,16 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
if (count < 50 && totalcount >= listfrom)
{
++count;
- Aentry->BuildAuctionInfo(data);
+ Aentry->BuildAuctionInfo(data, item);
}
++totalcount;
}
}
//this function inserts to WorldPacket auction's data
-bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const
+bool AuctionEntry::BuildAuctionInfo(WorldPacket& data, Item* sourceItem) const
{
- Item* item = sAuctionMgr->GetAItem(itemGUIDLow);
+ Item* item = (sourceItem) ? sourceItem : sAuctionMgr->GetAItem(itemGUIDLow);
if (!item)
{
TC_LOG_ERROR("misc", "AuctionEntry::BuildAuctionInfo: Auction %u has a non-existent item: %u", Id, itemGUIDLow);
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h
index 874cef189f6..7e31db01a22 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.h
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h
@@ -30,6 +30,7 @@ class WorldPacket;
#define MIN_AUCTION_TIME (12*HOUR)
#define MAX_AUCTION_ITEMS 160
+#define MAX_GETALL_RETURN 55000
enum AuctionError
{
@@ -90,7 +91,7 @@ struct AuctionEntry
uint8 GetHouseId() const { return houseId; }
uint32 GetAuctionCut() const;
uint32 GetAuctionOutBid() const;
- bool BuildAuctionInfo(WorldPacket & data) const;
+ bool BuildAuctionInfo(WorldPacket & data, Item* sourceItem = nullptr) const;
void DeleteFromDB(SQLTransaction& trans) const;
void SaveToDB(SQLTransaction& trans) const;
bool LoadFromDB(Field* fields);
@@ -110,6 +111,7 @@ class AuctionHouseObject
}
typedef std::map AuctionEntryMap;
+ typedef std::unordered_map PlayerGetAllThrottleMap;
uint32 Getcount() const { return AuctionsMap.size(); }
@@ -133,10 +135,15 @@ class AuctionHouseObject
void BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
- uint32& count, uint32& totalcount);
+ uint32& count, uint32& totalcount, bool getall = false);
private:
AuctionEntryMap AuctionsMap;
+
+ // Map of throttled players for GetAll, and throttle expiry time
+ // Stored here, rather than player object to maintain persistence after logout
+ PlayerGetAllThrottleMap GetAllThrottleMap;
+
};
class AuctionHouseMgr
diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp
index 2aae8f0138d..b9d39abfbf7 100644
--- a/src/server/game/Battlefield/Battlefield.cpp
+++ b/src/server/game/Battlefield/Battlefield.cpp
@@ -285,7 +285,7 @@ void Battlefield::InitStalker(uint32 entry, Position const& pos)
if (Creature* creature = SpawnCreature(entry, pos, TEAM_NEUTRAL))
StalkerGuid = creature->GetGUID();
else
- TC_LOG_ERROR("bg.battlefield", "Battlefield::InitStalker: could not spawn Stalker (Creature entry %u), zone messeges will be un-available", entry);
+ TC_LOG_ERROR("bg.battlefield", "Battlefield::InitStalker: Could not spawn Stalker (Creature entry %u), zone messages will be unavailable!", entry);
}
void Battlefield::KickAfkPlayers()
@@ -543,10 +543,10 @@ BfGraveyard* Battlefield::GetGraveyardById(uint32 id) const
if (BfGraveyard* graveyard = m_GraveyardList.at(id))
return graveyard;
else
- TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u not existed", id);
+ TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u does not exist.", id);
}
else
- TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u cant be found", id);
+ TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u could not be found.", id);
return NULL;
}
@@ -764,7 +764,7 @@ Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, fl
Map* map = sMapMgr->CreateBaseMap(m_MapId);
if (!map)
{
- TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u map not found", entry);
+ TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u, map not found.", entry);
return nullptr;
}
@@ -780,7 +780,7 @@ Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, fl
CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
if (!cinfo)
{
- TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: entry %u does not exist.", entry);
+ TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Entry %u does not exist.", entry);
return nullptr;
}
@@ -803,8 +803,8 @@ GameObject* Battlefield::SpawnGameObject(uint32 entry, float x, float y, float z
GameObject* go = new GameObject;
if (!go->Create(map->GenerateLowGuid(), entry, map, PHASEMASK_NORMAL, x, y, z, o, 0, 0, 0, 0, 100, GO_STATE_READY))
{
- TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Gameobject template %u not found in database! Battlefield not created!", entry);
- TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Cannot create gameobject template %u! Battlefield not created!", entry);
+ TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Gameobject template %u could not be found in the database! Battlefield has not been created!", entry);
+ TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Could not create gameobject template %u! Battlefield has not been created!", entry);
delete go;
return NULL;
}
@@ -905,7 +905,7 @@ bool BfCapturePoint::SetCapturePointData(GameObject* capturePoint)
GameObjectTemplate const* goinfo = capturePoint->GetGOInfo();
if (goinfo->type != GAMEOBJECT_TYPE_CAPTURE_POINT)
{
- TC_LOG_ERROR("misc", "OutdoorPvP: GO %u is not capture point!", capturePoint->GetEntry());
+ TC_LOG_ERROR("misc", "OutdoorPvP: GO %u is not a capture point!", capturePoint->GetEntry());
return false;
}
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
index 067083b64eb..d8697c62d88 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
@@ -790,7 +790,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates()
BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId);
if (!bl)
{
- TC_LOG_ERROR("bg.battleground", "Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeId);
+ TC_LOG_ERROR("bg.battleground", "Battleground ID %u could not be found in BattlemasterList.dbc. The battleground was not created.", bgTypeId);
continue;
}
@@ -808,14 +808,14 @@ void BattlegroundMgr::LoadBattlegroundTemplates()
if (bgTemplate.MaxPlayersPerTeam == 0 || bgTemplate.MinPlayersPerTeam > bgTemplate.MaxPlayersPerTeam)
{
- TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u has bad values for MinPlayersPerTeam (%u) and MaxPlayersPerTeam(%u)",
+ TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains bad values for MinPlayersPerTeam (%u) and MaxPlayersPerTeam(%u).",
bgTemplate.Id, bgTemplate.MinPlayersPerTeam, bgTemplate.MaxPlayersPerTeam);
continue;
}
if (bgTemplate.MinLevel == 0 || bgTemplate.MaxLevel == 0 || bgTemplate.MinLevel > bgTemplate.MaxLevel)
{
- TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u has bad values for MinLevel (%u) and MaxLevel (%u)",
+ TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains bad values for MinLevel (%u) and MaxLevel (%u).",
bgTemplate.Id, bgTemplate.MinLevel, bgTemplate.MaxLevel);
continue;
}
@@ -829,7 +829,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates()
}
else
{
- TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u has non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTemplate.Id, startId);
+ TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains a non-existing WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTemplate.Id, startId);
continue;
}
@@ -840,7 +840,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates()
}
else
{
- TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u has non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTemplate.Id, startId);
+ TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains a non-existing WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTemplate.Id, startId);
continue;
}
}
@@ -1036,7 +1036,7 @@ BattlegroundTypeId BattlegroundMgr::BGTemplateId(BattlegroundQueueTypeId bgQueue
case BATTLEGROUND_QUEUE_5v5:
return BATTLEGROUND_AA;
default:
- return BattlegroundTypeId(0); // used for unknown template (it existed and do nothing)
+ return BattlegroundTypeId(0); // used for unknown template (it exists and does nothing)
}
}
@@ -1140,7 +1140,7 @@ void BattlegroundMgr::LoadBattleMastersEntry()
uint32 bgTypeId = fields[1].GetUInt32();
if (!sBattlemasterListStore.LookupEntry(bgTypeId))
{
- TC_LOG_ERROR("sql.sql", "Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.", entry, bgTypeId);
+ TC_LOG_ERROR("sql.sql", "Table `battlemaster_entry` contains entry %u for a non-existing battleground type %u, ignored.", entry, bgTypeId);
continue;
}
@@ -1160,7 +1160,7 @@ void BattlegroundMgr::CheckBattleMasters()
{
if ((itr->second.npcflag & UNIT_NPC_FLAG_BATTLEMASTER) && mBattleMastersMap.find(itr->second.Entry) == mBattleMastersMap.end())
{
- TC_LOG_ERROR("sql.sql", "CreatureTemplate (Entry: %u) has UNIT_NPC_FLAG_BATTLEMASTER but no data in `battlemaster_entry` table. Removing flag!", itr->second.Entry);
+ TC_LOG_ERROR("sql.sql", "Creature_Template Entry: %u has UNIT_NPC_FLAG_BATTLEMASTER, but no data in the `battlemaster_entry` table. Removing flag.", itr->second.Entry);
const_cast(&itr->second)->npcflag &= ~UNIT_NPC_FLAG_BATTLEMASTER;
}
}
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
index c6efdfcd1fd..af8c4f439f6 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
@@ -171,7 +171,7 @@ void BattlegroundEY::CheckSomeoneJoinedPoint()
Player* player = ObjectAccessor::FindPlayer(m_PlayersNearPoint[EY_POINTS_MAX][j]);
if (!player)
{
- TC_LOG_ERROR("bg.battleground", "BattlegroundEY:CheckSomeoneJoinedPoint: Player (%s) not found!", m_PlayersNearPoint[EY_POINTS_MAX][j].ToString().c_str());
+ TC_LOG_ERROR("bg.battleground", "BattlegroundEY:CheckSomeoneJoinedPoint: Player (%s) could not be found!", m_PlayersNearPoint[EY_POINTS_MAX][j].ToString().c_str());
++j;
continue;
}
@@ -212,8 +212,8 @@ void BattlegroundEY::CheckSomeoneLeftPoint()
Player* player = ObjectAccessor::FindPlayer(m_PlayersNearPoint[i][j]);
if (!player)
{
- TC_LOG_ERROR("bg.battleground", "BattlegroundEY:CheckSomeoneLeftPoint Player (%s) not found!", m_PlayersNearPoint[i][j].ToString().c_str());
- //move not existed player to "free space" - this will cause many error showing in log, but it is a very important bug
+ TC_LOG_ERROR("bg.battleground", "BattlegroundEY:CheckSomeoneLeftPoint Player (%s) could not be found!", m_PlayersNearPoint[i][j].ToString().c_str());
+ //move non-existing players to "free space" - this will cause many errors showing in log, but it is a very important bug
m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]);
m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j);
continue;
@@ -498,7 +498,7 @@ bool BattlegroundEY::SetupBattleground()
|| !AddObject(BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER, BG_OBJECT_HU_TOWER_CAP_EY_ENTRY, 2282.121582f, 1760.006958f, 1189.707153f, 1.919862f, 0, 0, 0.819152f, 0.573576f, RESPAWN_ONE_DAY)
)
{
- TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn some object Battleground not created!");
+ TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn some objects. The battleground was not created.");
return false;
}
@@ -515,21 +515,21 @@ bool BattlegroundEY::SetupBattleground()
|| !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REAVER + i * 3 + 1, Buff_Entries[1], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY)
|| !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REAVER + i * 3 + 2, Buff_Entries[2], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY)
)
- TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Cannot spawn buff");
+ TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Could not spawn Speedbuff Fel Reaver.");
}
WorldSafeLocsEntry const* sg = NULL;
sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE);
if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, TEAM_ALLIANCE))
{
- TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide! Battleground not created!");
+ TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide. The battleground was not created.");
return false;
}
sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_HORDE);
if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, TEAM_HORDE))
{
- TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide! Battleground not created!");
+ TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide. The battleground was not created.");
return false;
}
@@ -595,7 +595,7 @@ void BattlegroundEY::RespawnFlagAfterDrop()
if (obj)
obj->Delete();
else
- TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Unknown dropped flag (%s)", GetDroppedFlagGUID().ToString().c_str());
+ TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Unknown dropped flag (%s).", GetDroppedFlagGUID().ToString().c_str());
SetDroppedFlagGUID(ObjectGuid::Empty);
}
@@ -767,7 +767,7 @@ void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point)
WorldSafeLocsEntry const* sg = NULL;
sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveYardId);
if (!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, GetTeamIndexByTeamId(Team)))
- TC_LOG_ERROR("bg.battleground", "BatteGroundEY: Failed to spawn spirit guide! point: %u, team: %u, graveyard_id: %u",
+ TC_LOG_ERROR("bg.battleground", "BatteGroundEY: Failed to spawn spirit guide. point: %u, team: %u, graveyard_id: %u",
Point, Team, m_CapturingPointTypes[Point].GraveYardId);
// SpawnBGCreature(Point, RESPAWN_IMMEDIATELY);
@@ -917,7 +917,7 @@ WorldSafeLocsEntry const* BattlegroundEY::GetClosestGraveYard(Player* player)
if (!entry)
{
- TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Not found the main team graveyard. Graveyard system isn't working!");
+ TC_LOG_ERROR("bg.battleground", "BattlegroundEY: The main team graveyard could not be found. The graveyard system will not be operational!");
return NULL;
}
@@ -934,7 +934,7 @@ WorldSafeLocsEntry const* BattlegroundEY::GetClosestGraveYard(Player* player)
{
entry = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[i].GraveYardId);
if (!entry)
- TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Not found graveyard: %u", m_CapturingPointTypes[i].GraveYardId);
+ TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Graveyard %u could not be found.", m_CapturingPointTypes[i].GraveYardId);
else
{
distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z);
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
index 8176ddc8012..2cdbf3fb3ae 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
@@ -389,6 +389,8 @@ void BattlegroundSA::PostUpdateImpl(uint32 diff)
{
if (TotalTime >= BG_SA_ROUNDLENGTH)
{
+ CastSpellOnTeam(SPELL_END_OF_ROUND, ALLIANCE);
+ CastSpellOnTeam(SPELL_END_OF_ROUND, HORDE);
RoundScores[0].winner = Attackers;
RoundScores[0].time = BG_SA_ROUNDLENGTH;
TotalTime = 0;
@@ -401,8 +403,6 @@ void BattlegroundSA::PostUpdateImpl(uint32 diff)
ToggleTimer();
ResetObjs();
GetBgMap()->UpdateAreaDependentAuras();
- CastSpellOnTeam(SPELL_END_OF_ROUND, ALLIANCE);
- CastSpellOnTeam(SPELL_END_OF_ROUND, HORDE);
return;
}
}
@@ -410,6 +410,8 @@ void BattlegroundSA::PostUpdateImpl(uint32 diff)
{
if (TotalTime >= EndRoundTimer)
{
+ CastSpellOnTeam(SPELL_END_OF_ROUND, ALLIANCE);
+ CastSpellOnTeam(SPELL_END_OF_ROUND, HORDE);
RoundScores[1].time = BG_SA_ROUNDLENGTH;
RoundScores[1].winner = (Attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE;
if (RoundScores[0].time == RoundScores[1].time)
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index 4675fda810e..7c1e371f933 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -297,10 +297,10 @@ bool ChatHandler::ExecuteCommandInTable(std::vector const& table, c
uint32 areaId = player->GetAreaId();
std::string areaName = "Unknown";
std::string zoneName = "Unknown";
- if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId))
+ if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId))
{
areaName = area->area_name;
- if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone))
+ if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone))
zoneName = zone->area_name;
}
@@ -360,7 +360,7 @@ bool ChatHandler::SetDataForCommandInTable(std::vector& table, char
// expected subcommand by full name DB content
else if (*text)
{
- TC_LOG_ERROR("sql.sql", "Table `command` have unexpected subcommand '%s' in command '%s', skip.", text, fullcommand.c_str());
+ TC_LOG_ERROR("sql.sql", "Table `command` contains an unexpected subcommand '%s' in command '%s', skipped.", text, fullcommand.c_str());
return false;
}
@@ -376,9 +376,9 @@ bool ChatHandler::SetDataForCommandInTable(std::vector& table, char
if (!cmd.empty())
{
if (&table == &getCommandTable())
- TC_LOG_ERROR("sql.sql", "Table `command` have not existed command '%s', skip.", cmd.c_str());
+ TC_LOG_ERROR("sql.sql", "Table `command` contains a non-existing command '%s', skipped.", cmd.c_str());
else
- TC_LOG_ERROR("sql.sql", "Table `command` have not existed subcommand '%s' in command '%s', skip.", cmd.c_str(), fullcommand.c_str());
+ TC_LOG_ERROR("sql.sql", "Table `command` contains a non-existing subcommand '%s' in command '%s', skipped.", cmd.c_str(), fullcommand.c_str());
}
return false;
@@ -486,7 +486,7 @@ bool ChatHandler::ShowHelpForSubCommands(std::vector const& table,
std::string list;
for (uint32 i = 0; i < table.size(); ++i)
{
- // must be available (ignore handler existence for show command with possible available subcommands)
+ // must be available (ignore handler existence to show command with possible available subcommands)
if (!isAvailable(table[i]))
continue;
@@ -525,7 +525,7 @@ bool ChatHandler::ShowHelpForCommand(std::vector const& table, cons
{
for (uint32 i = 0; i < table.size(); ++i)
{
- // must be available (ignore handler existence for show command with possible available subcommands)
+ // must be available (ignore handler existence to show command with possible available subcommands)
if (!isAvailable(table[i]))
continue;
@@ -555,7 +555,7 @@ bool ChatHandler::ShowHelpForCommand(std::vector const& table, cons
{
for (uint32 i = 0; i < table.size(); ++i)
{
- // must be available (ignore handler existence for show command with possible available subcommands)
+ // must be available (ignore handler existence to show command with possible available subcommands)
if (!isAvailable(table[i]))
continue;
@@ -1116,7 +1116,7 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* p
*player_name = pl ? pl->GetName() : "";
}
- // some from req. data must be provided (note: name is empty if player not exist)
+ // some from req. data must be provided (note: name is empty if player does not exist)
if ((!player || !*player) && (!player_guid || !*player_guid) && (!player_name || player_name->empty()))
{
SendSysMessage(LANG_PLAYER_NOT_FOUND);
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index e2fad683aad..b56f9f1c70d 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -1749,7 +1749,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const
}
case CONDITION_SOURCE_TYPE_PHASE:
{
- if (cond->SourceEntry && !GetAreaEntryByAreaID(cond->SourceEntry))
+ if (cond->SourceEntry && !sAreaTableStore.LookupEntry(cond->SourceEntry))
{
TC_LOG_ERROR("sql.sql", "%s SourceEntry in `condition` table, does not exist in AreaTable.dbc, ignoring.", cond->ToString().c_str());
return false;
@@ -1826,7 +1826,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const
}
case CONDITION_ZONEID:
{
- AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(cond->ConditionValue1);
+ AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(cond->ConditionValue1);
if (!areaEntry)
{
TC_LOG_ERROR("sql.sql", "%s Area (%u) does not exist, skipped.", cond->ToString(true).c_str(), cond->ConditionValue1);
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 4891b17d71b..7df533d4c59 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -52,11 +52,9 @@ struct WMOAreaTableTripple
typedef std::map WMOAreaInfoByTripple;
typedef std::multimap CharSectionsMap;
-DBCStorage sAreaStore(AreaTableEntryfmt);
+DBCStorage sAreaTableStore(AreaTableEntryfmt);
DBCStorage sAreaGroupStore(AreaGroupEntryfmt);
DBCStorage sAreaPOIStore(AreaPOIEntryfmt);
-static AreaFlagByAreaID sAreaFlagByAreaID;
-static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files
static WMOAreaInfoByTripple sWMOAreaInfoByTripple;
@@ -95,6 +93,9 @@ DBCStorage sDurabilityCostsStore(DurabilityCostsfmt);
DBCStorage sEmotesStore(EmotesEntryfmt);
DBCStorage sEmotesTextStore(EmotesTextEntryfmt);
+typedef std::tuple EmotesTextSoundKey;
+static std::map sEmotesTextSoundMap;
+DBCStorage sEmotesTextSoundStore(EmotesTextSoundEntryfmt);
typedef std::map FactionTeamMap;
static FactionTeamMap sFactionTeamMap;
@@ -337,21 +338,7 @@ void LoadDBCStores(const std::string& dataPath)
StoreProblemList bad_dbc_files;
uint32 availableDbcLocales = 0xFFFFFFFF;
- LoadDBC(availableDbcLocales, bad_dbc_files, sAreaStore, dbcPath, "AreaTable.dbc");
-
- // must be after sAreaStore loading
- for (uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) // areaflag numbered from 0
- {
- if (AreaTableEntry const* area = sAreaStore.LookupEntry(i))
- {
- // fill AreaId->DBC records
- sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID), area->exploreFlag));
-
- // fill MapId->DBC records (skip sub zones and continents)
- if (area->zone == 0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 && area->mapid != 571)
- sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid, area->exploreFlag));
- }
- }
+ LoadDBC(availableDbcLocales, bad_dbc_files, sAreaTableStore, dbcPath, "AreaTable.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementStore, dbcPath, "Achievement.dbc", &CustomAchievementfmt, &CustomAchievementIndex);//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementCriteriaStore, dbcPath, "Achievement_Criteria.dbc");//15595
@@ -397,6 +384,10 @@ void LoadDBCStores(const std::string& dataPath)
}
}
+ LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesTextSoundStore, dbcPath, "EmotesTextSound.dbc");
+ for (uint32 i = 0; i < sEmotesTextSoundStore.GetNumRows(); ++i)
+ if (EmotesTextSoundEntry const* entry = sEmotesTextSoundStore.LookupEntry(i))
+ sEmotesTextSoundMap[EmotesTextSoundKey(entry->EmotesTextId, entry->RaceId, entry->SexId)] = entry;
LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoExtraStore, dbcPath, "CreatureDisplayInfoExtra.dbc");//15595
@@ -854,7 +845,7 @@ void LoadDBCStores(const std::string& dataPath)
}
// Check loaded DBC files proper version
- if (!sAreaStore.LookupEntry(4713) || // last area (areaflag) added in 4.3.4 (15595)
+ if (!sAreaTableStore.LookupEntry(5995) || // last area (areaflag) added in 4.3.4 (15595)
!sCharTitlesStore.LookupEntry(287) || // last char title added in 4.3.4 (15595)
!sGemPropertiesStore.LookupEntry(2250) || // last gem property added in 4.3.4 (15595)
!sMapStore.LookupEntry(980) || // last map added in 4.3.4 (15595)
@@ -911,41 +902,13 @@ uint32 GetTalentSpellCost(uint32 spellId)
return 0;
}
-int32 GetAreaFlagByAreaID(uint32 area_id)
-{
- AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id);
- if (i == sAreaFlagByAreaID.end())
- return -1;
-
- return i->second;
-}
WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid)
{
WMOAreaInfoByTripple::iterator i = sWMOAreaInfoByTripple.find(WMOAreaTableTripple(rootid, adtid, groupid));
- if (i == sWMOAreaInfoByTripple.end())
- return NULL;
- return i->second;
-}
-
-AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id)
-{
- int32 areaflag = GetAreaFlagByAreaID(area_id);
- if (areaflag < 0)
+ if (i == sWMOAreaInfoByTripple.end())
return NULL;
-
- return sAreaStore.LookupEntry(areaflag);
-}
-
-AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id)
-{
- if (area_flag)
- return sAreaStore.LookupEntry(area_flag);
-
- if (MapEntry const* mapEntry = sMapStore.LookupEntry(map_id))
- return GetAreaEntryByAreaID(mapEntry->linked_zone);
-
- return NULL;
+ return i->second;
}
char const* GetRaceName(uint8 race, uint8 /*locale*/)
@@ -960,15 +923,6 @@ char const* GetClassName(uint8 class_, uint8 /*locale*/)
return classEntry ? classEntry->name : NULL;
}
-uint32 GetAreaFlagByMapId(uint32 mapid)
-{
- AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid);
- if (i == sAreaFlagByMapID.end())
- return 0;
- else
- return i->second;
-}
-
uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId)
{
if (mapid != 530 && mapid != 571 && mapid != 732) // speed for most cases
@@ -1404,3 +1358,8 @@ ResponseCodes ValidateName(std::string const& name, LocaleConstant locale)
return CHAR_NAME_SUCCESS;
}
+
+EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender)
+{
+ return sEmotesTextSoundMap[EmotesTextSoundKey(emote, race, gender)];
+}
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index 647a659e8ae..a1f259b2cc8 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -33,11 +33,6 @@ char const* GetPetName(uint32 petfamily, uint32 dbclang);
uint32 GetTalentSpellCost(uint32 spellId);
TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
-int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found
-AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id);
-AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id);
-uint32 GetAreaFlagByMapId(uint32 mapid);
-
char const* GetRaceName(uint8 race, uint8 locale);
char const* GetClassName(uint8 class_, uint8 locale);
@@ -93,9 +88,11 @@ SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, u
ResponseCodes ValidateName(std::string const& name, LocaleConstant locale);
+EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender);
+
extern DBCStorage sAchievementStore;
extern DBCStorage sAchievementCriteriaStore;
-extern DBCStorage sAreaStore;// recommend access using functions
+extern DBCStorage sAreaTableStore;
extern DBCStorage sAreaGroupStore;
extern DBCStorage sAreaPOIStore;
extern DBCStorage sAreaTriggerStore;
@@ -126,6 +123,7 @@ extern DBCStorage sDurabilityCostsStore;
extern DBCStorage sDurabilityQualityStore;
extern DBCStorage sEmotesStore;
extern DBCStorage sEmotesTextStore;
+extern DBCStorage sEmotesTextSoundStore;
extern DBCStorage sFactionStore;
extern DBCStorage sFactionTemplateStore;
extern DBCStorage sGameObjectDisplayInfoStore;
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index 3cc660e0175..e01ca3268c2 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -525,7 +525,7 @@ struct AreaTableEntry
uint32 ID; // 0
uint32 mapid; // 1
uint32 zone; // 2 if 0 then it's zone, else it's zone id of this area
- uint32 exploreFlag; // 3, main index
+ uint32 exploreFlag; // 3
uint32 flags; // 4,
//uint32 unk5; // 5,
//uint32 unk6; // 6,
@@ -980,6 +980,15 @@ struct EmotesTextEntry
uint32 textid;
};
+struct EmotesTextSoundEntry
+{
+ uint32 Id; // 0
+ uint32 EmotesTextId; // 1
+ uint32 RaceId; // 2
+ uint32 SexId; // 3, 0 male / 1 female
+ uint32 SoundId; // 4
+};
+
struct FactionEntry
{
uint32 ID; // 0 m_ID
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index bcc6734bbe1..d36cfa0e3e7 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -26,7 +26,7 @@ char const Achievementfmt[] = "niixsxiixixxii";
const std::string CustomAchievementfmt = "pppaaaapapaapp";
const std::string CustomAchievementIndex = "ID";
char const AchievementCriteriafmt[] = "niiiliiiisiiiiixxiiiiii";
-char const AreaTableEntryfmt[] = "iiinixxxxxisiiiiiffixxxxxx";
+char const AreaTableEntryfmt[] = "niiiixxxxxisiiiiiffixxxxxx";
char const AreaGroupEntryfmt[] = "niiiiiii";
char const AreaPOIEntryfmt[] = "niiiiiiiiiiiffixixxixx";
char const AreaTriggerEntryfmt[] = "nifffxxxfffff";
@@ -57,6 +57,7 @@ char const DurabilityCostsfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiii";
char const DurabilityQualityfmt[] = "nf";
char const EmotesEntryfmt[] = "nxxiiixx";
char const EmotesTextEntryfmt[] = "nxixxxxxxxxxxxxxxxx";
+char const EmotesTextSoundEntryfmt[] = "niiii";
char const FactionEntryfmt[] = "niiiiiiiiiiiiiiiiiiffixsxi";
char const FactionTemplateEntryfmt[] = "niiiiiiiiiiiii";
char const GameObjectDisplayInfofmt[] = "nsxxxxxxxxxxffffffxxx";
diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp
index d1e6f1153ea..5abe223e2f6 100644
--- a/src/server/game/Entities/Corpse/Corpse.cpp
+++ b/src/server/game/Entities/Corpse/Corpse.cpp
@@ -158,7 +158,7 @@ bool Corpse::LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields)
SetObjectScale(1.0f);
SetUInt32Value(CORPSE_FIELD_DISPLAY_ID, fields[5].GetUInt32());
- _LoadIntoDataField(fields[6].GetCString(), CORPSE_FIELD_ITEM, EQUIPMENT_SLOT_END);
+ _LoadIntoDataField(fields[6].GetString(), CORPSE_FIELD_ITEM, EQUIPMENT_SLOT_END);
SetUInt32Value(CORPSE_FIELD_BYTES_1, fields[7].GetUInt32());
SetUInt32Value(CORPSE_FIELD_BYTES_2, fields[8].GetUInt32());
SetUInt32Value(CORPSE_FIELD_FLAGS, fields[9].GetUInt8());
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index d8991dc9dca..4c3f55ec5fc 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -369,13 +369,13 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
// Load creature equipment
- if (!data || data->equipmentId == 0)
- LoadEquipment(); // use default equipment (if available)
- else if (data && data->equipmentId != 0) // override, 0 means no equipment
+ if (data && data->equipmentId != 0)
{
m_originalEquipmentId = data->equipmentId;
LoadEquipment(data->equipmentId);
}
+ else
+ LoadEquipment(0, true);
SetName(normalInfo->Name); // at normal entry always
@@ -911,6 +911,12 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 /*phaseMask*
if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING)
AddUnitState(UNIT_STATE_IGNORE_PATHFINDING);
+ if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
+ {
+ ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true);
+ ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, true);
+ }
+
return true;
}
@@ -1312,8 +1318,38 @@ bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, Creatu
return true;
}
-bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap)
+bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate)
{
+ if (!allowDuplicate)
+ {
+ // If an alive instance of this spawnId is already found, skip creation
+ // If only dead instance(s) exist, despawn them and spawn a new (maybe also dead) version
+ const auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(spawnId);
+ std::vector despawnList;
+
+ if (creatureBounds.first != creatureBounds.second)
+ {
+ for (auto itr = creatureBounds.first; itr != creatureBounds.second; ++itr)
+ {
+ if (itr->second->IsAlive())
+ {
+ TC_LOG_DEBUG("maps", "Would have spawned %u but %s already exists", spawnId, creatureBounds.first->second->GetGUID().ToString().c_str());
+ return false;
+ }
+ else
+ {
+ despawnList.push_back(itr->second);
+ TC_LOG_DEBUG("maps", "Despawned dead instance of spawn %u (%s)", spawnId, itr->second->GetGUID().ToString().c_str());
+ }
+ }
+
+ for (Creature* despawnCreature : despawnList)
+ {
+ despawnCreature->AddObjectToRemoveList();
+ }
+ }
+ }
+
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
if (!data)
@@ -1393,6 +1429,7 @@ void Creature::LoadEquipment(int8 id, bool force /*= true*/)
SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, 0);
m_equipmentId = 0;
}
+
return;
}
@@ -1401,7 +1438,7 @@ void Creature::LoadEquipment(int8 id, bool force /*= true*/)
return;
m_equipmentId = id;
- for (uint8 i = 0; i < 3; ++i)
+ for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, einfo->ItemEntry[i]);
}
@@ -1624,10 +1661,28 @@ void Creature::setDeathState(DeathState s)
UpdateMovementFlags();
- CreatureTemplate const* cinfo = GetCreatureTemplate();
- SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag);
ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_IGNORE_PATHFINDING));
- SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
+
+ if (!IsPet())
+ {
+ CreatureData const* creatureData = GetCreatureData();
+ CreatureTemplate const* cinfo = GetCreatureTemplate();
+
+ uint32 npcflag, unit_flags, dynamicflags;
+ ObjectMgr::ChooseCreatureFlags(cinfo, npcflag, unit_flags, dynamicflags, creatureData);
+
+ SetUInt32Value(UNIT_NPC_FLAGS, npcflag);
+ SetUInt32Value(UNIT_FIELD_FLAGS, unit_flags);
+ SetUInt32Value(UNIT_DYNAMIC_FLAGS, dynamicflags);
+
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+
+ SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
+
+ if (creatureData && GetPhaseMask() != creatureData->phaseMask)
+ SetPhaseMask(creatureData->phaseMask, false);
+ }
+
Motion_Initialize();
Unit::setDeathState(ALIVE);
LoadCreaturesAddon();
@@ -2114,6 +2169,11 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool /*force*/) const
if (GetMap()->IsDungeon())
return true;
+ // if the mob is actively being damaged, do not reset due to distance unless it's a world boss
+ if (!isWorldBoss())
+ if (time(NULL) - GetLastDamagedTime() <= MAX_AGGRO_RESET_TIME)
+ return true;
+
//Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and out of combat every update tick.
float dist = std::max(GetAttackDistance(victim), sWorld->getFloatConfig(CONFIG_THREAT_RADIUS)) + m_CombatDistance;
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 9022444caca..fbf68e69152 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -39,23 +39,25 @@ class WorldSession;
enum CreatureFlagsExtra
{
- CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
- CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility)
- CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry
- CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry
- CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block
- CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks
- CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP
- CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature
- CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me
- CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging)
- CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard
- CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes
- CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills
- CREATURE_FLAG_EXTRA_TAUNT_DIMINISH = 0x00080000, // Taunt is a subject to diminishing returns on this creautre
- CREATURE_FLAG_EXTRA_ALL_DIMINISH = 0x00100000, // Creature is subject to all diminishing returns as player are
- CREATURE_FLAG_EXTRA_DUNGEON_BOSS = 0x10000000, // creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB)
- CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING = 0x20000000 // creature ignore pathfinding
+ CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
+ CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility)
+ CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry
+ CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry
+ CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block
+ CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks
+ CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP
+ CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature
+ CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me
+ CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging)
+ CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard
+ CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes
+ CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills
+ CREATURE_FLAG_EXTRA_TAUNT_DIMINISH = 0x00080000, // Taunt is a subject to diminishing returns on this creautre
+ CREATURE_FLAG_EXTRA_ALL_DIMINISH = 0x00100000, // creature is subject to all diminishing returns as player are
+ CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ = 0x00200000, // creature does not need to take player damage for kill credit
+ CREATURE_FLAG_EXTRA_DUNGEON_BOSS = 0x10000000, // creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB)
+ CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING = 0x20000000, // creature ignore pathfinding
+ CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK = 0x40000000 // creature is immune to knockback effects
};
#define CREATURE_FLAG_EXTRA_DB_ALLOWED (CREATURE_FLAG_EXTRA_INSTANCE_BIND | CREATURE_FLAG_EXTRA_CIVILIAN | \
@@ -63,7 +65,7 @@ enum CreatureFlagsExtra
CREATURE_FLAG_EXTRA_NO_CRUSH | CREATURE_FLAG_EXTRA_NO_XP_AT_KILL | CREATURE_FLAG_EXTRA_TRIGGER | \
CREATURE_FLAG_EXTRA_NO_TAUNT | CREATURE_FLAG_EXTRA_WORLDEVENT | CREATURE_FLAG_EXTRA_NO_CRIT | \
CREATURE_FLAG_EXTRA_NO_SKILLGAIN | CREATURE_FLAG_EXTRA_TAUNT_DIMINISH | CREATURE_FLAG_EXTRA_ALL_DIMINISH | \
- CREATURE_FLAG_EXTRA_GUARD | CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING)
+ CREATURE_FLAG_EXTRA_GUARD | CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING | CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ | CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
#define CREATURE_REGEN_INTERVAL 2 * IN_MILLISECONDS
@@ -532,7 +534,7 @@ class Creature : public Unit, public GridObject, public MapObject
void setDeathState(DeathState s) override; // override virtual Unit::setDeathState
bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadCreatureFromDB(spawnId, map, false); }
- bool LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true);
+ bool LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true, bool allowDuplicate = false);
void SaveToDB();
// overriden in Pet
virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
@@ -654,7 +656,7 @@ class Creature : public Unit, public GridObject, public MapObject
void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; }
bool IsReputationGainDisabled() const { return DisableReputationGain; }
- bool IsDamageEnoughForLootingAndReward() const { return m_PlayerDamageReq == 0; }
+ bool IsDamageEnoughForLootingAndReward() const { return (m_creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ) || (m_PlayerDamageReq == 0); }
void LowerPlayerDamageReq(uint32 unDamage);
void ResetPlayerDamageReq() { m_PlayerDamageReq = GetHealth() / 2; }
uint32 m_PlayerDamageReq;
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index c3d793a7553..fd2302ef4c5 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -1392,7 +1392,15 @@ void GameObject::Use(Unit* user)
break;
}
- player->KillCreditGO(info->entry, GetGUID());
+ if (Group* group = player->GetGroup())
+ {
+ for (GroupReference const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
+ if (Player* member = itr->GetSource())
+ if (member->IsAtGroupRewardDistance(this))
+ member->KillCreditGO(info->entry, GetGUID());
+ }
+ else
+ player->KillCreditGO(info->entry, GetGUID());
}
if (uint32 trapEntry = info->goober.linkedTrapId)
@@ -1430,7 +1438,10 @@ void GameObject::Use(Unit* user)
player->SendCinematicStart(info->camera.cinematicId);
if (info->camera.eventID)
+ {
GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this);
+ EventInform(info->camera.eventID, user);
+ }
return;
}
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index 78b99d692db..effd616f847 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -452,8 +452,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi
need_save = true;
}
- std::string enchants = fields[6].GetString();
- _LoadIntoDataField(enchants.c_str(), ITEM_FIELD_ENCHANTMENT_1_1, MAX_ENCHANTMENT_SLOT * MAX_ENCHANTMENT_OFFSET);
+ _LoadIntoDataField(fields[6].GetString(), ITEM_FIELD_ENCHANTMENT_1_1, MAX_ENCHANTMENT_SLOT * MAX_ENCHANTMENT_OFFSET);
SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID, fields[7].GetInt16());
// recalculate suffix factor
if (GetItemRandomPropertyId() < 0)
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 946fe37eaf8..b2dcd7fbd8a 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -222,7 +222,7 @@ void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all)
if (all)
{
for (uint8 i = 0; i < TaxiMaskSize; ++i)
- data << uint8(sTaxiNodesMask[i]); // all existed nodes
+ data << uint8(sTaxiNodesMask[i]); // all existing nodes
}
else
{
@@ -607,7 +607,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(createInfo->Race, createInfo->Class);
if (!info)
{
- TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid race/class pair (%u/%u) - refusing to do so.",
+ TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid race/class pair (%u/%u) - refusing to do so.",
GetSession()->GetAccountId(), m_name.c_str(), createInfo->Race, createInfo->Class);
return false;
}
@@ -620,7 +620,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(createInfo->Class);
if (!cEntry)
{
- TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid character class (%u) - refusing to do so (wrong DBC-files?)",
+ TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid character class (%u) - refusing to do so (wrong DBC-files?)",
GetSession()->GetAccountId(), m_name.c_str(), createInfo->Class);
return false;
}
@@ -635,14 +635,14 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
if (!IsValidGender(createInfo->Gender))
{
- TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid gender (%u) - refusing to do so",
+ TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid gender (%u) - refusing to do so",
GetSession()->GetAccountId(), m_name.c_str(), createInfo->Gender);
return false;
}
if (!ValidateAppearance(createInfo->Race, createInfo->Class, createInfo->Gender, createInfo->HairStyle, createInfo->HairColor, createInfo->Face, createInfo->FacialHair, createInfo->Skin, true))
{
- TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with invalid appearance attributes - refusing to do so",
+ TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with invalid appearance attributes - refusing to do so",
GetSession()->GetAccountId(), m_name.c_str());
return false;
}
@@ -959,9 +959,9 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
if (!IsAlive())
{
- if (type == DAMAGE_FALL) // DealDamage not apply item durability loss at self damage
+ if (type == DAMAGE_FALL) // DealDamage does not apply item durability loss from self-induced damage.
{
- TC_LOG_DEBUG("entities.player", "We are fall to death, loosing 10 percents durability");
+ TC_LOG_DEBUG("entities.player", "You have died from falling, losing 10 percent total armor durability.");
DurabilityLossAll(0.10f, false);
// durability lost message
SendDurabilityLoss(this, 10);
@@ -1544,7 +1544,7 @@ void Player::setDeathState(DeathState s)
{
if (!cur)
{
- TC_LOG_ERROR("entities.player", "setDeathState: attempt to kill a dead player %s(%d)", GetName().c_str(), GetGUID().GetCounter());
+ TC_LOG_ERROR("entities.player", "setDeathState: Attempted to kill a dead player %s(%d)", GetName().c_str(), GetGUID().GetCounter());
return;
}
@@ -1831,7 +1831,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
// client without expansion support
if (GetSession()->Expansion() < mEntry->Expansion())
{
- TC_LOG_DEBUG("maps", "Player %s using client without required expansion tried teleport to non accessible map %u", GetName().c_str(), mapid);
+ TC_LOG_DEBUG("maps", "Player %s using client without required expansion tried teleporting to non accessible map %u", GetName().c_str(), mapid);
if (Transport* transport = GetTransport())
{
@@ -2474,7 +2474,7 @@ bool Player::CanInteractWithQuestGiver(Object* questGiver)
return false;
}
-Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask)
+Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflagmask)
{
// unit checks
if (!guid)
@@ -2518,7 +2518,21 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask)
return creature;
}
-GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const
+GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const
+{
+ if (GameObject* go = GetMap()->GetGameObject(guid))
+ {
+ if (go->IsWithinDistInMap(this, go->GetInteractionDistance()))
+ return go;
+
+ TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal %f is allowed)", go->GetGOInfo()->name.c_str(),
+ go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this), go->GetInteractionDistance());
+ }
+
+ return nullptr;
+}
+
+GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const
{
if (GameObject* go = GetMap()->GetGameObject(guid))
{
@@ -2527,12 +2541,12 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTy
if (go->IsWithinDistInMap(this, go->GetInteractionDistance()))
return go;
- TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name.c_str(),
- go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this));
+ TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal %f is allowed)", go->GetGOInfo()->name.c_str(),
+ go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this), go->GetInteractionDistance());
}
}
- return NULL;
+ return nullptr;
}
bool Player::IsUnderWater() const
@@ -3213,12 +3227,12 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning)
// do character spell book cleanup (all characters)
if (!IsInWorld() && !learning) // spell load case
{
- TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId);
+ TC_LOG_ERROR("spells", "Player::addSpell: Non-existing spell #%u requested from SpellStore. Deleting spell for all characters in `character_spell`.", spellId);
DeleteSpellFromAllPlayers(spellId);
}
else
- TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId);
+ TC_LOG_ERROR("spells", "Player::addSpell: Non-existing spell #%u requested from SpellStore.", spellId);
return false;
}
@@ -3228,12 +3242,12 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning)
// do character spell book cleanup (all characters)
if (!IsInWorld() && !learning) // spell load case
{
- TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", spellId);
+ TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u, learning this spell is not allowed. Deleting this spell for all characters in `character_talent`.", spellId);
DeleteSpellFromAllPlayers(spellId);
}
else
- TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed.", spellId);
+ TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u, learning this spell is not allowed.", spellId);
return false;
}
@@ -3278,12 +3292,12 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
// do character spell book cleanup (all characters)
if (!IsInWorld() && !learning) // spell load case
{
- TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId);
+ TC_LOG_ERROR("spells", "Player::addSpell: Non-existing spell #%u requested from SpellStore. Deleting for all characters in `character_spell`.", spellId);
DeleteSpellFromAllPlayers(spellId);
}
else
- TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId);
+ TC_LOG_ERROR("spells", "Player::addSpell: Non-existing spell #%u requested from SpellStore.", spellId);
return false;
}
@@ -3293,12 +3307,12 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
// do character spell book cleanup (all characters)
if (!IsInWorld() && !learning) // spell load case
{
- TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.", spellId);
+ TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u, learning this spell is not allowed. Deleting this spell for all characters in `character_spell`.", spellId);
DeleteSpellFromAllPlayers(spellId);
}
else
- TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u learning not allowed.", spellId);
+ TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u, learning this spell is not allowed.", spellId);
return false;
}
@@ -3968,7 +3982,7 @@ bool Player::ResetTalents(bool no_cost)
for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
{
- // skip non-existant talent ranks
+ // skip non-existing talent ranks
if (talentInfo->RankID[rank] == 0)
continue;
const SpellInfo* _spellEntry = sSpellMgr->GetSpellInfo(talentInfo->RankID[rank]);
@@ -4662,6 +4676,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
// remove death flag + set aura
SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00);
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS);
// This must be called always even on Players with race != RACE_NIGHTELF in case of faction change
RemoveAurasDueToSpell(20584); // RACE_NIGHTELF speed bonuses
@@ -4773,7 +4788,7 @@ void Player::OfflineResurrect(ObjectGuid const& guid, SQLTransaction& trans)
Corpse* Player::CreateCorpse()
{
- // prevent existence 2 corpse for player
+ // prevent the existence of 2 corpses for one player
SpawnCorpseBones();
uint32 _pb, _pb2, _cfb1, _cfb2;
@@ -4996,7 +5011,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g
DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
if (!dcost)
{
- TC_LOG_ERROR("entities.player.items", "RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel);
+ TC_LOG_ERROR("entities.player.items", "RepairDurability: Wrong item level %u", ditemProto->ItemLevel);
return TotalCost;
}
@@ -5020,7 +5035,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g
{
if (GetGuildId() == 0)
{
- TC_LOG_DEBUG("entities.player.items", "You are not member of a guild");
+ TC_LOG_DEBUG("entities.player.items", "You are not member of a guild.");
return TotalCost;
}
@@ -5035,7 +5050,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g
}
else if (!HasEnoughMoney(uint64(costs)))
{
- TC_LOG_DEBUG("entities.player.items", "You do not have enough money");
+ TC_LOG_DEBUG("entities.player.items", "You do not have enough money.");
return TotalCost;
}
else
@@ -5057,7 +5072,7 @@ void Player::RepopAtGraveyard()
// note: this can be called also when the player is alive
// for example from WorldSession::HandleMovementOpcodes
- AreaTableEntry const* zone = GetAreaEntryByAreaID(GetAreaId());
+ AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetAreaId());
// Such zones are considered unreachable as a ghost and the player must be automatically revived
if ((!IsAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) || GetTransport() || GetPositionZ() < (zone ? zone->MaxDepth : -500.0f))
@@ -5100,6 +5115,8 @@ void Player::RepopAtGraveyard()
}
else if (GetPositionZ() < zone->MaxDepth)
TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
+
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS);
}
bool Player::CanJoinConstantChannelInZone(ChatChannelsEntry const* channel, AreaTableEntry const* zone)
@@ -5144,7 +5161,7 @@ void Player::UpdateLocalChannels(uint32 newZone)
if (GetSession()->PlayerLoading() && !IsBeingTeleportedFar())
return; // The client handles it automatically after loading, but not after teleporting
- AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone);
+ AreaTableEntry const* current_zone = sAreaTableStore.LookupEntry(newZone);
if (!current_zone)
return;
@@ -5238,7 +5255,7 @@ void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, floa
{
if (modGroup >= BASEMOD_END || modType >= MOD_END)
{
- TC_LOG_ERROR("spells", "ERROR in HandleBaseModValue(): non existed BaseModGroup of wrong BaseModType!");
+ TC_LOG_ERROR("spells", "ERROR in HandleBaseModValue(): Non-existing BaseModGroup or wrong BaseModType!");
return;
}
@@ -5263,7 +5280,7 @@ float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
{
if (modGroup >= BASEMOD_END || modType >= MOD_END)
{
- TC_LOG_ERROR("spells", "trial to access non existed BaseModGroup or wrong BaseModType!");
+ TC_LOG_ERROR("spells", "Attempt to access non-existing BaseModGroup or wrong BaseModType!");
return 0.0f;
}
@@ -5277,7 +5294,7 @@ float Player::GetTotalBaseModValue(BaseModGroup modGroup) const
{
if (modGroup >= BASEMOD_END)
{
- TC_LOG_ERROR("spells", "wrong BaseModGroup in GetTotalBaseModValue()!");
+ TC_LOG_ERROR("spells", "Wrong BaseModGroup in GetTotalBaseModValue()!");
return 0.0f;
}
@@ -6155,20 +6172,20 @@ bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type)
case ACTION_BUTTON_SPELL:
if (!sSpellMgr->GetSpellInfo(action))
{
- TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): spell not exist", action, button, GetName().c_str(), GetGUID().GetCounter());
+ TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): spell does not exist", action, button, GetName().c_str(), GetGUID().GetCounter());
return false;
}
if (!HasSpell(action))
{
- TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): player don't known this spell", action, button, GetName().c_str(), GetGUID().GetCounter());
+ TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): player does not know this spell", action, button, GetName().c_str(), GetGUID().GetCounter());
return false;
}
break;
case ACTION_BUTTON_ITEM:
if (!sObjectMgr->GetItemTemplate(action))
{
- TC_LOG_DEBUG("entities.player", "Item action %u not added into button %u for player %s (GUID: %u): item not exist", action, button, GetName().c_str(), GetGUID().GetCounter());
+ TC_LOG_DEBUG("entities.player", "Item action %u not added into button %u for player %s (GUID: %u): item does not exist", action, button, GetName().c_str(), GetGUID().GetCounter());
return false;
}
break;
@@ -6191,7 +6208,7 @@ ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type)
if (!IsActionButtonDataValid(button, action, type))
return NULL;
- // it create new button (NEW state) if need or return existed
+ // it create new button (NEW state) if need or return existing
ActionButton& ab = m_actionButtons[button];
// set data and update to CHANGED if not NEW
@@ -6319,22 +6336,32 @@ void Player::CheckAreaExploreAndOutdoor()
return;
bool isOutdoor;
- uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor);
+ uint32 areaId = GetBaseMap()->GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor);
+ AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId);
if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor)
RemoveAurasWithAttribute(SPELL_ATTR0_OUTDOORS_ONLY);
- if (areaFlag == 0xffff)
+ if (!areaId)
return;
- int offset = areaFlag / 32;
- if (offset >= PLAYER_EXPLORED_ZONES_SIZE)
+ if (!areaEntry)
{
- TC_LOG_ERROR("entities.player", "Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", areaFlag, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE);
+ TC_LOG_ERROR("entities.player", "Player '%s' (%s) discovered unknown area (x: %f y: %f z: %f map: %u)",
+ GetName().c_str(), GetGUID().ToString().c_str(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId());
return;
}
- uint32 val = (uint32)(1 << (areaFlag % 32));
+ uint32 offset = areaEntry->exploreFlag / 32;
+
+ if (offset >= PLAYER_EXPLORED_ZONES_SIZE)
+ {
+ TC_LOG_ERROR("entities.player", "Player::CheckAreaExploreAndOutdoor: Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).",
+ areaEntry->exploreFlag, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE);
+ return;
+ }
+
+ uint32 val = (uint32)(1 << (areaEntry->exploreFlag % 32));
uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
if (!(currFields & val))
@@ -6343,19 +6370,11 @@ void Player::CheckAreaExploreAndOutdoor()
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA);
- AreaTableEntry const* areaEntry = GetAreaEntryByAreaFlagAndMap(areaFlag, GetMapId());
- if (!areaEntry)
- {
- TC_LOG_ERROR("entities.player", "Player %u discovered unknown area (x: %f y: %f z: %f map: %u", GetGUID().GetCounter(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId());
- return;
- }
-
if (areaEntry->area_level > 0)
{
- uint32 area = areaEntry->ID;
if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
- SendExplorationExperience(area, 0);
+ SendExplorationExperience(areaId, 0);
}
else
{
@@ -6379,9 +6398,9 @@ void Player::CheckAreaExploreAndOutdoor()
}
GiveXP(XP, NULL);
- SendExplorationExperience(area, XP);
+ SendExplorationExperience(areaId, XP);
}
- TC_LOG_DEBUG("entities.player", "Player %u discovered a new area: %u", GetGUID().GetCounter(), area);
+ TC_LOG_DEBUG("entities.player", "Player '%s' (%s) discovered a new area: %u", GetName().c_str(),GetGUID().ToString().c_str(), areaId);
}
}
}
@@ -7300,7 +7319,7 @@ void Player::UpdateArea(uint32 newArea)
// so apply them accordingly
m_areaUpdateId = newArea;
- AreaTableEntry const* area = GetAreaEntryByAreaID(newArea);
+ AreaTableEntry const* area = sAreaTableStore.LookupEntry(newArea);
pvpInfo.IsInFFAPvPArea = area && (area->flags & AREA_FLAG_ARENA);
UpdatePvPState(true);
@@ -7348,7 +7367,7 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
// zone changed, so area changed as well, update it
UpdateArea(newArea);
- AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone);
+ AreaTableEntry const* zone = sAreaTableStore.LookupEntry(newZone);
if (!zone)
return;
@@ -8146,7 +8165,7 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellData.SpellId);
if (!spellInfo)
{
- TC_LOG_ERROR("entities.player.items", "WORLD: unknown Item spellid %i", spellData.SpellId);
+ TC_LOG_ERROR("entities.player.items", "WORLD: Unknown item spellid %i", spellData.SpellId);
continue;
}
@@ -8249,7 +8268,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learn_spell_id);
if (!spellInfo)
{
- TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ", proto->ItemId, learn_spell_id);
+ TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) has wrong spell id %u, ignoring.", proto->ItemId, learn_spell_id);
SendEquipError(EQUIP_ERR_INTERNAL_BAG_ERROR, item, NULL);
return;
}
@@ -8278,7 +8297,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellData.SpellId);
if (!spellInfo)
{
- TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring", proto->ItemId, spellData.SpellId);
+ TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) has wrong spell id %u, ignoring.", proto->ItemId, spellData.SpellId);
continue;
}
@@ -8308,7 +8327,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pEnchant->spellid[s]);
if (!spellInfo)
{
- TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]);
+ TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell Enchant %i casts unknown spell %i.", pEnchant->ID, pEnchant->spellid[s]);
continue;
}
@@ -9493,7 +9512,7 @@ uint32 Player::GetXPRestBonus(uint32 xp)
SetRestBonus(GetRestBonus() - rested_bonus);
- TC_LOG_DEBUG("entities.player", "GetXPRestBonus: Player %s (%u) gain %u xp (+%u Rested Bonus). Rested points=%f", GetName().c_str(), GetGUID().GetCounter(), xp+rested_bonus, rested_bonus, GetRestBonus());
+ TC_LOG_DEBUG("entities.player", "GetXPRestBonus: Player %s (%u) gains %u xp (+%u Rested Bonus). Rested points=%f", GetName().c_str(), GetGUID().GetCounter(), xp+rested_bonus, rested_bonus, GetRestBonus());
return rested_bonus;
}
@@ -9525,7 +9544,7 @@ void Player::ResetPetTalents()
CharmInfo* charmInfo = pet->GetCharmInfo();
if (!charmInfo)
{
- TC_LOG_ERROR("entities.player", "Object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUID().GetCounter(), pet->GetTypeId());
+ TC_LOG_ERROR("entities.player", "Object (GUID: %u TypeId: %u) is considered pet-like, but doesn't have charm info!", pet->GetGUID().GetCounter(), pet->GetTypeId());
return;
}
pet->resetTalents();
@@ -10372,7 +10391,7 @@ InventoryResult Player::CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest, Ite
if (bag == skip_bag)
return EQUIP_ERR_WRONG_BAG_TYPE;
- // skip not existed bag or self targeted bag
+ // skip non-existing bag or self targeted bag
Bag* pBag = GetBagByPos(bag);
if (!pBag || pBag == pSrcItem)
return EQUIP_ERR_WRONG_BAG_TYPE;
@@ -11141,7 +11160,7 @@ InventoryResult Player::CanUnequipItem(uint16 pos, bool swap) const
Item* pItem = GetItemByPos(pos);
- // Applied only to existed equipped item
+ // Applied only to existing equipped item
if (!pItem)
return EQUIP_ERR_OK;
@@ -11751,7 +11770,7 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update)
SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(cooldownSpell);
if (!spellProto)
- TC_LOG_ERROR("entities.player", "Weapon switch cooldown spell %u couldn't be found in Spell.dbc", cooldownSpell);
+ TC_LOG_ERROR("entities.player", "Weapon switch cooldown spell %u could not be found in Spell.dbc", cooldownSpell);
else
{
m_weaponChangeTimer = spellProto->StartRecoveryTime;
@@ -11999,7 +12018,7 @@ void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool
// store item
Item* pLastItem = StoreItem(dest, pItem, update);
- // only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way)
+ // only set if not merged to existing stack (pItem can be deleted already but we can compare pointers any way)
if (pLastItem == pItem)
{
// update owner for last item (this can be original item with wrong owner
@@ -12407,7 +12426,7 @@ void Player::SplitItem(uint16 src, uint16 dst, uint32 count)
return;
}
- // not let split more existed items (can be only at cheating)
+ // not let split more existing items (can be only at cheating)
if (pSrcItem->GetCount() < count)
{
SendEquipError(EQUIP_ERR_TOO_FEW_TO_SPLIT, pSrcItem, NULL);
@@ -13957,7 +13976,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool
VendorItemData const* vendorItems = creature->GetVendorItems();
if (!vendorItems || vendorItems->Empty())
{
- TC_LOG_ERROR("sql.sql", "Creature %s (Entry: %u GUID: %u DB GUID: %u) has UNIT_NPC_FLAG_VENDOR set but has an empty trading item list.", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUID().GetCounter(), creature->GetSpawnId());
+ TC_LOG_ERROR("sql.sql", "Creature %s (Entry: %u GUID: %u DB GUID: %u) has UNIT_NPC_FLAG_VENDOR set, but has an empty trading item list.", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUID().GetCounter(), creature->GetSpawnId());
canTalk = false;
}
break;
@@ -13991,7 +14010,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool
break;
case GOSSIP_OPTION_TRAINER:
if (getClass() != creature->GetCreatureTemplate()->trainer_class && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS)
- TC_LOG_ERROR("sql.sql", "GOSSIP_OPTION_TRAINER:: Player %s (GUID: %u) request wrong gossip menu: %u with wrong class: %u at Creature: %s (Entry: %u, Trainer Class: %u)",
+ TC_LOG_ERROR("sql.sql", "GOSSIP_OPTION_TRAINER:: Player %s (GUID: %u) requested wrong gossip menu: %u with wrong class: %u at Creature: %s (Entry: %u, Trainer Class: %u)",
GetName().c_str(), GetGUID().GetCounter(), menu->GetGossipMenu().GetMenuId(), getClass(), creature->GetName().c_str(), creature->GetEntry(), creature->GetCreatureTemplate()->trainer_class);
// no break;
case GOSSIP_OPTION_GOSSIP:
@@ -14007,7 +14026,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool
canTalk = false;
break;
default:
- TC_LOG_ERROR("sql.sql", "Creature entry %u has unknown gossip option %u for menu %u", creature->GetEntry(), itr->second.OptionType, itr->second.MenuId);
+ TC_LOG_ERROR("sql.sql", "Creature entry %u has unknown gossip option %u for menu %u.", creature->GetEntry(), itr->second.OptionType, itr->second.MenuId);
canTalk = false;
break;
}
@@ -14120,7 +14139,7 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men
{
if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER)
{
- TC_LOG_ERROR("entities.player", "Player guid %u request invalid gossip option for GameObject entry %u", GetGUID().GetCounter(), source->GetEntry());
+ TC_LOG_ERROR("entities.player", "Player guid %u requested invalid gossip option for GameObject entry %u.", GetGUID().GetCounter(), source->GetEntry());
return;
}
}
@@ -14224,7 +14243,7 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men
if (bgTypeId == BATTLEGROUND_TYPE_NONE)
{
- TC_LOG_ERROR("entities.player", "a user (guid %u) requested battlegroundlist from a npc who is no battlemaster", GetGUID().GetCounter());
+ TC_LOG_ERROR("entities.player", "User (guid %u) requested battlegroundlist from an NPC who is not a battlemaster.", GetGUID().GetCounter());
return;
}
@@ -15214,7 +15233,7 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (1).", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required quest(s) (1).", qInfo->GetQuestId());
}
return false;
}
@@ -15247,7 +15266,7 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (2).", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required quest(s) (2).", qInfo->GetQuestId());
}
return false;
@@ -15263,7 +15282,7 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (3).", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required quest(s) (3).", qInfo->GetQuestId());
}
return false;
@@ -15281,7 +15300,7 @@ bool Player::SatisfyQuestClass(Quest const* qInfo, bool msg) const
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestClass: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required class.", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestClass: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player is not (one of) the required class(es).", qInfo->GetQuestId());
}
return false;
@@ -15300,7 +15319,7 @@ bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE);
- TC_LOG_DEBUG("misc", "SatisfyQuestRace: Sent INVALIDREASON_QUEST_FAILED_WRONG_RACE (questId: %u) because player does not have required race.", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestRace: Sent INVALIDREASON_QUEST_FAILED_WRONG_RACE (questId: %u) because player is not (one of) the required race(s).", qInfo->GetQuestId());
}
return false;
@@ -15316,7 +15335,7 @@ bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (min).", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required reputation (min).", qInfo->GetQuestId());
}
return false;
}
@@ -15327,7 +15346,7 @@ bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (max).", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required reputation (max).", qInfo->GetQuestId());
}
return false;
}
@@ -15340,7 +15359,7 @@ bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (ReputationObjective2).", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required reputation (ReputationObjective2).", qInfo->GetQuestId());
}
return false;
}
@@ -15369,7 +15388,7 @@ bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestConditions: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not meet conditions.", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestConditions: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not meet the conditions.", qInfo->GetQuestId());
}
TC_LOG_DEBUG("condition", "Player::SatisfyQuestConditions: conditions not met for quest %u", qInfo->GetQuestId());
return false;
@@ -15415,7 +15434,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did daily quests in exclusive group.", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did all daily quests in exclusive group.", qInfo->GetQuestId());
}
return false;
@@ -15427,7 +15446,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg)
if (msg)
{
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
- TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did quest in exclusive group.", qInfo->GetQuestId());
+ TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player has already taken one or more quests in the exclusive group.", qInfo->GetQuestId());
}
return false;
}
@@ -15496,7 +15515,7 @@ bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg)
if (qInfo->IsDFQuest())
{
- if (!m_DFQuests.empty())
+ if (m_DFQuests.find(qInfo->GetQuestId()) != m_DFQuests.end())
return false;
return true;
@@ -16818,7 +16837,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
{
std::string name = "";
sObjectMgr->GetPlayerNameByGUID(guid, name);
- TC_LOG_ERROR("entities.player", "Player %s %s not found in table `characters`, can't load. ", name.c_str(), guid.ToString().c_str());
+ TC_LOG_ERROR("entities.player", "Player %s %s not found in table `characters`, can't load.", name.c_str(), guid.ToString().c_str());
return false;
}
@@ -16830,7 +16849,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
// player should be able to load/delete character only with correct account!
if (dbAccountId != GetSession()->GetAccountId())
{
- TC_LOG_ERROR("entities.player", "Player %s loading from wrong account (is: %u, should be: %u)", guid.ToString().c_str(), GetSession()->GetAccountId(), dbAccountId);
+ TC_LOG_ERROR("entities.player", "Player %s attempts to load from wrong account (current: %u, should be: %u)", guid.ToString().c_str(), GetSession()->GetAccountId(), dbAccountId);
return false;
}
@@ -16862,7 +16881,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
uint8 gender = fields[5].GetUInt8();
if (!IsValidGender(gender))
{
- TC_LOG_ERROR("entities.player", "Player %s has wrong gender (%u), can't be loaded.", guid.ToString().c_str(), gender);
+ TC_LOG_ERROR("entities.player", "Player %s is the wrong gender (%u) and can't be loaded.", guid.ToString().c_str(), gender);
return false;
}
@@ -16877,15 +16896,15 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
if (!info)
{
- TC_LOG_ERROR("entities.player", "Player %s has wrong race/class (%u/%u), can't be loaded.", guid.ToString().c_str(), getRace(), getClass());
+ TC_LOG_ERROR("entities.player", "Player %s has an invalid race/class combination (%u/%u) and can't be loaded.", guid.ToString().c_str(), getRace(), getClass());
return false;
}
SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8());
SetUInt32Value(PLAYER_XP, fields[7].GetUInt32());
- _LoadIntoDataField(fields[55].GetCString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE);
- _LoadIntoDataField(fields[57].GetCString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2);
+ _LoadIntoDataField(fields[55].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE);
+ _LoadIntoDataField(fields[57].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2);
SetObjectScale(1.0f);
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f);
@@ -16912,7 +16931,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
GetByteValue(PLAYER_BYTES_2, 0), // facial hair
GetByteValue(PLAYER_BYTES, 0))) // skin color
{
- TC_LOG_ERROR("entities.player", "Player %s has wrong Appearance values (Hair/Skin/Color), can't be loaded.", guid.ToString().c_str());
+ TC_LOG_ERROR("entities.player", "Player %s has wrong Appearance values (Hair/Skin/Color) and can't be loaded.", guid.ToString().c_str());
return false;
}
@@ -16924,7 +16943,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
InitDisplayIds();
- // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
+ // cleanup inventory related item value fields (it will be filled correctly in _LoadInventory)
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
{
SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid::Empty);
@@ -16934,7 +16953,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
m_items[slot] = NULL;
}
- TC_LOG_DEBUG("entities.player.loading", "Load Basic value of player %s is: ", m_name.c_str());
+ TC_LOG_DEBUG("entities.player.loading", "Load Basic values of player %s: ", m_name.c_str());
outDebugValues();
//Need to call it to initialize m_team (m_team can be calculated from race)
@@ -17001,7 +17020,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
if (!mapEntry || !IsPositionValid())
{
- TC_LOG_ERROR("entities.player", "Player %s have invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
+ TC_LOG_ERROR("entities.player", "Player %s has invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleporting player to default race/class location.",
guid.ToString().c_str(), mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
RelocateToHomebind();
}
@@ -17042,7 +17061,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
//if (mapId == MAPID_INVALID) -- code kept for reference
if (int16(mapId) == int16(-1)) // Battleground Entry Point not found (???)
{
- TC_LOG_ERROR("entities.player", "Player %s was in BG in database, but BG was not found, and entry point was invalid! Teleport to default race/class locations.",
+ TC_LOG_ERROR("entities.player", "Player %s was in BG in database, but BG was not found and entry point was invalid! Teleporting to default race/class locations.",
guid.ToString().c_str());
RelocateToHomebind();
}
@@ -17074,7 +17093,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
std::fabs(m_movementInfo.transport.pos.GetPositionY()) > 250.0f ||
std::fabs(m_movementInfo.transport.pos.GetPositionZ()) > 250.0f)
{
- TC_LOG_ERROR("entities.player", "Player %s have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.",
+ TC_LOG_ERROR("entities.player", "Player %s has invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleporting player to bind location.",
guid.ToString().c_str(), x, y, z, o);
m_movementInfo.transport.Reset();
@@ -17091,7 +17110,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
}
else
{
- TC_LOG_ERROR("entities.player", "Player (%s) have problems with transport guid (%u). Teleport to bind location.",
+ TC_LOG_ERROR("entities.player", "Player %s has problems with transport guid (%u). Teleporting to bind location.",
guid.ToString().c_str(), transLowGUID);
RelocateToHomebind();
@@ -17115,14 +17134,14 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
if (uint32 node_id = m_taxi.GetTaxiSource())
nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
- if (!nodeEntry) // don't know taxi start node, to homebind
+ if (!nodeEntry) // don't know taxi start node, teleport to homebind
{
- TC_LOG_ERROR("entities.player", "Character %u have wrong data in taxi destination list, teleport to homebind.", GetGUID().GetCounter());
+ TC_LOG_ERROR("entities.player", "Character %u has wrong data in taxi destination list. Teleporting player to homebind.", GetGUID().GetCounter());
RelocateToHomebind();
}
- else // have start node, to it
+ else // has start node, teleport to it
{
- TC_LOG_ERROR("entities.player", "Character %u have too short taxi destination list, teleport to original node.", GetGUID().GetCounter());
+ TC_LOG_ERROR("entities.player", "Character %u has too short taxi destination list. Teleporting player to original node.", GetGUID().GetCounter());
mapId = nodeEntry->map_id;
Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f);
}
@@ -17151,7 +17170,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
{
if (GetSession()->Expansion() < mapEntry->Expansion())
{
- TC_LOG_DEBUG("entities.player.loading", "Player %s using client without required expansion tried login at non accessible map %u", GetName().c_str(), mapId);
+ TC_LOG_DEBUG("entities.player.loading", "Player %s is using client without required expansion, tried to log in to inaccessible map %u.", GetName().c_str(), mapId);
RelocateToHomebind();
}
@@ -17281,7 +17300,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
m_stableSlots = fields[33].GetUInt8();
if (m_stableSlots > MAX_PET_STABLES)
{
- TC_LOG_ERROR("entities.player", "Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES, uint32(m_stableSlots));
+ TC_LOG_ERROR("entities.player", "Player can not have more than %u stable slots, but has %u in DB.", MAX_PET_STABLES, uint32(m_stableSlots));
m_stableSlots = MAX_PET_STABLES;
}
@@ -17289,7 +17308,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
if (HasAtLoginFlag(AT_LOGIN_RENAME))
{
- TC_LOG_ERROR("entities.player", "Player (GUID: %u) tried to login while forced to rename, can't load.'", GetGUID().GetCounter());
+ TC_LOG_ERROR("entities.player", "Player (GUID: %u) tried to login while forced to rename, could not load player.", GetGUID().GetCounter());
return false;
}
@@ -17651,9 +17670,9 @@ void Player::_LoadActions(PreparedQueryResult result)
ab->uState = ACTIONBUTTON_UNCHANGED;
else
{
- TC_LOG_ERROR("entities.player", " ...at loading, and will deleted in DB also");
+ TC_LOG_ERROR("entities.player", " ...at loading, and will also be deleted in DB.");
- // Will deleted in DB at next save (it can create data until save but marked as deleted)
+ // Will be deleted in DB at next save (it can create data until save but marked as deleted).
m_actionButtons[button].uState = ACTIONBUTTON_DELETED;
}
} while (result->NextRow());
@@ -17662,7 +17681,7 @@ void Player::_LoadActions(PreparedQueryResult result)
void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff)
{
- TC_LOG_DEBUG("entities.player.loading", "Loading auras for player %u", GetGUID().GetCounter());
+ TC_LOG_DEBUG("entities.player.loading", "Loading auras for player %u.", GetGUID().GetCounter());
/* 0 1 2 3 4 5 6 7 8 9 10
QueryResult* result = CharacterDatabase.PQuery("SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2,
@@ -17695,7 +17714,7 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff)
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
if (!spellInfo)
{
- TC_LOG_ERROR("entities.player", "Unknown aura (spellid %u), ignore.", spellid);
+ TC_LOG_ERROR("entities.player", "Unknown aura (spellid %u), ignored.", spellid);
continue;
}
@@ -17729,7 +17748,7 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff)
aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, &damage[0]);
aura->ApplyForTargets();
- TC_LOG_DEBUG("entities.player", "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask);
+ TC_LOG_DEBUG("entities.player", "Added aura spellid %u, effectmask %u.", spellInfo->Id, effmask);
}
}
while (result->NextRow());
@@ -17755,10 +17774,10 @@ void Player::_LoadGlyphAuras()
TC_LOG_ERROR("entities.player", "Player %s has glyph with typeflags %u in slot with typeflags %u, removing.", m_name.c_str(), gp->TypeFlags, gs->TypeFlags);
}
else
- TC_LOG_ERROR("entities.player", "Player %s has not existing glyph slot entry %u on index %u", m_name.c_str(), GetGlyphSlot(i), i);
+ TC_LOG_ERROR("entities.player", "Player %s has non-existing glyph slot entry %u on index %u.", m_name.c_str(), GetGlyphSlot(i), i);
}
else
- TC_LOG_ERROR("entities.player", "Player %s has not existing glyph entry %u on index %u", m_name.c_str(), glyph, i);
+ TC_LOG_ERROR("entities.player", "Player %s has non-existing glyph entry %u on index %u.", m_name.c_str(), glyph, i);
// On any error remove glyph
SetGlyph(i, 0);
@@ -17888,7 +17907,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
item->SetState(ITEM_UNCHANGED, this);
else
{
- TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) which can't be loaded into inventory (Bag GUID: %u, slot: %u) by reason %u. Item will be sent by mail.",
+ TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) which can't be loaded into inventory (Bag GUID: %u, slot: %u) for reason %u. Item will be sent by mail.",
GetGUID().GetCounter(), GetName().c_str(), item->GetGUID().GetCounter(), item->GetEntry(), bagGuid, slot, err);
item->DeleteFromInventoryDB(trans);
problematicItems.push_back(item);
@@ -18068,7 +18087,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F
}
else
{
- TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has broken item (GUID: %u, entry: %u) in inventory. Deleting item.",
+ TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has a broken item (GUID: %u, entry: %u) in inventory. Deleting item.",
GetGUID().GetCounter(), GetName().c_str(), itemGuid, itemEntry);
remove = true;
}
@@ -18083,7 +18102,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F
}
else
{
- TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has unknown item (entry: %u) in inventory. Deleting item.",
+ TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has an unknown item (entry: %u) in inventory. Deleting item.",
GetGUID().GetCounter(), GetName().c_str(), itemEntry);
Item::DeleteFromInventoryDB(trans, itemGuid);
Item::DeleteFromDB(trans, itemGuid);
@@ -18114,7 +18133,7 @@ void Player::_LoadMailedItems(Mail* mail)
if (!proto)
{
- TC_LOG_ERROR("entities.player", "Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUID().GetCounter(), itemGuid, itemTemplate, mail->messageID);
+ TC_LOG_ERROR("entities.player", "Player %u has an unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUID().GetCounter(), itemGuid, itemTemplate, mail->messageID);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM);
stmt->setUInt32(0, itemGuid);
@@ -18130,7 +18149,7 @@ void Player::_LoadMailedItems(Mail* mail)
if (!item->LoadFromDB(itemGuid, ObjectGuid(HighGuid::Player, fields[13].GetUInt32()), fields, itemTemplate))
{
- TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, itemGuid);
+ TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems - Item in mail (%u) doesn't exist!!! - item guid: %u, deleted from mail.", mail->messageID, itemGuid);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
stmt->setUInt32(0, itemGuid);
@@ -18193,7 +18212,7 @@ void Player::_LoadMail()
if (m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId))
{
- TC_LOG_ERROR("entities.player", "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
+ TC_LOG_ERROR("entities.player", "Player::_LoadMail - Mail (%u) contains a non-existing MailTemplateId (%u), removing at load.", m->messageID, m->mailTemplateId);
m->mailTemplateId = 0;
}
@@ -18374,7 +18393,7 @@ void Player::_LoadDailyQuestStatus(PreparedQueryResult result)
if (quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query
{
- TC_LOG_ERROR("entities.player", "Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`", GetGUID().GetCounter());
+ TC_LOG_ERROR("entities.player", "Player (GUID: %u) has more than 25 daily quest records in `charcter_queststatus_daily`", GetGUID().GetCounter());
break;
}
@@ -18523,8 +18542,9 @@ void Player::_LoadBoundInstances(PreparedQueryResult result)
uint32 mapId = fields[2].GetUInt16();
uint32 instanceId = fields[0].GetUInt32();
uint8 difficulty = fields[3].GetUInt8();
+ BindExtensionState extendState = BindExtensionState(fields[4].GetUInt8());
- time_t resetTime = time_t(fields[4].GetUInt32());
+ time_t resetTime = time_t(fields[5].GetUInt32());
// the resettime for normal instances is only saved when the InstanceSave is unloaded
// so the value read from the DB may be wrong here but only if the InstanceSave is loaded
// and in that case it is not used
@@ -18536,12 +18556,12 @@ void Player::_LoadBoundInstances(PreparedQueryResult result)
if (!mapEntry || !mapEntry->IsDungeon())
{
- TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed or not dungeon map %d (%s)", GetName().c_str(), GetGUID().GetCounter(), mapId, mapname.c_str());
+ TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to a non-existing or non-dungeon map %d (%s).", GetName().c_str(), GetGUID().GetCounter(), mapId, mapname.c_str());
deleteInstance = true;
}
else if (difficulty >= MAX_DIFFICULTY)
{
- TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u (%s)", GetName().c_str(), GetGUID().GetCounter(), difficulty, mapId, mapname.c_str());
+ TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has a bind to a non-existing difficulty %d instance for map %u (%s)", GetName().c_str(), GetGUID().GetCounter(), difficulty, mapId, mapname.c_str());
deleteInstance = true;
}
else
@@ -18549,12 +18569,12 @@ void Player::_LoadBoundInstances(PreparedQueryResult result)
MapDifficulty const* mapDiff = GetMapDifficultyData(mapId, Difficulty(difficulty));
if (!mapDiff)
{
- TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u (%s)", GetName().c_str(), GetGUID().GetCounter(), difficulty, mapId, mapname.c_str());
+ TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has a bind to a non-existing difficulty %d instance for map %u (%s).", GetName().c_str(), GetGUID().GetCounter(), difficulty, mapId, mapname.c_str());
deleteInstance = true;
}
else if (!perm && group)
{
- TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d (%s), %d, %d", GetName().c_str(), GetGUID().GetCounter(), group->GetLowGUID(), mapId, mapname.c_str(), instanceId, difficulty);
+ TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) is in group %d, but has a non-permanent character bind to map %d (%s), %d, %d.", GetName().c_str(), GetGUID().GetCounter(), group->GetLowGUID(), mapId, mapname.c_str(), instanceId, difficulty);
deleteInstance = true;
}
}
@@ -18573,13 +18593,13 @@ void Player::_LoadBoundInstances(PreparedQueryResult result)
// since non permanent binds are always solo bind, they can always be reset
if (InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapId, instanceId, Difficulty(difficulty), resetTime, !perm, true))
- BindToInstance(save, perm, true);
+ BindToInstance(save, perm, extendState, true);
}
while (result->NextRow());
}
}
-InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty)
+InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty, bool withExpired)
{
// some instances only have one difficulty
MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(mapid, difficulty);
@@ -18588,9 +18608,9 @@ InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty
BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
if (itr != m_boundInstances[difficulty].end())
- return &itr->second;
- else
- return NULL;
+ if (itr->second.extendState || withExpired)
+ return &itr->second;
+ return nullptr;
}
InstanceSave* Player::GetInstanceSave(uint32 mapid, bool raid)
@@ -18633,24 +18653,32 @@ void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficu
}
}
-InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, bool load)
+InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, BindExtensionState extendState, bool load)
{
if (save)
{
InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
+ if (extendState == EXTEND_STATE_KEEP) // special flag, keep the player's current extend state when updating for new boss down
+ {
+ if (save == bind.save)
+ extendState = bind.extendState;
+ else
+ extendState = EXTEND_STATE_NORMAL;
+ }
if (!load)
{
if (bind.save)
{
// update the save when the group kills a boss
- if (permanent != bind.perm || save != bind.save)
+ if (permanent != bind.perm || save != bind.save || extendState != bind.extendState)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE);
stmt->setUInt32(0, save->GetInstanceId());
stmt->setBool(1, permanent);
- stmt->setUInt32(2, GetGUID().GetCounter());
- stmt->setUInt32(3, bind.save->GetInstanceId());
+ stmt->setUInt8(2, extendState);
+ stmt->setUInt32(3, GetGUID().GetCounter());
+ stmt->setUInt32(4, bind.save->GetInstanceId());
CharacterDatabase.Execute(stmt);
}
@@ -18662,6 +18690,7 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, b
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt32(1, save->GetInstanceId());
stmt->setBool(2, permanent);
+ stmt->setUInt8(3, extendState);
CharacterDatabase.Execute(stmt);
}
@@ -18679,9 +18708,10 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, b
bind.save = save;
bind.perm = permanent;
+ bind.extendState = extendState;
if (!load)
TC_LOG_DEBUG("maps", "Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName().c_str(), GetGUID().GetCounter(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
- sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficulty(), save->GetMapId(), permanent);
+ sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficulty(), save->GetMapId(), permanent, uint8(extendState));
return &bind;
}
@@ -18699,7 +18729,7 @@ void Player::BindToInstance()
GetSession()->SendPacket(&data);
if (!IsGameMaster())
{
- BindToInstance(mapSave, true);
+ BindToInstance(mapSave, true, EXTEND_STATE_KEEP);
GetSession()->SendCalendarRaidLockout(mapSave, true);
}
}
@@ -18725,23 +18755,27 @@ void Player::SendRaidInfo()
{
for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
{
- if (itr->second.perm)
+ InstancePlayerBind const& bind = itr->second;
+ if (bind.perm)
{
- InstanceSave* save = itr->second.save;
+ InstanceSave* save = bind.save;
bool isHeroic = save->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || save->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC;
uint32 completedEncounters = 0;
if (Map* map = sMapMgr->FindMap(save->GetMapId(), save->GetInstanceId()))
if (InstanceScript* instanceScript = ((InstanceMap*)map)->GetInstanceScript())
completedEncounters = instanceScript->GetCompletedEncounterMask();
- data << uint32(save->GetMapId()); // map id
- data << uint32(save->GetDifficulty()); // difficulty
+ data << uint32(save->GetMapId()); // map id
+ data << uint32(save->GetDifficulty()); // difficulty
data << uint32(isHeroic); // heroic
- data << uint64(save->GetInstanceId()); // instance id
- data << uint8(1); // expired = 0
- data << uint8(0); // extended = 1
- data << uint32(save->GetResetTime() - now); // reset time
- data << uint32(completedEncounters); // completed encounters mask
+ data << uint64(save->GetInstanceId()); // instance id
+ data << uint8(bind.extendState != EXTEND_STATE_EXPIRED); // expired = 0
+ data << uint8(bind.extendState == EXTEND_STATE_EXTENDED); // extended = 1
+ time_t nextReset = save->GetResetTime();
+ if (bind.extendState == EXTEND_STATE_EXTENDED)
+ nextReset = sInstanceSaveMgr->GetSubsequentResetTime(save->GetMapId(), save->GetDifficulty(), save->GetResetTime());
+ data << uint32(nextReset - now); // reset time
+ data << uint32(completedEncounters); // completed encounters mask
++counter;
}
}
@@ -19499,7 +19533,7 @@ void Player::_SaveInventory(SQLTransaction& trans)
}
else
{
- TC_LOG_ERROR("entities.player", "Can't find %s but is in refundable storage for player %u ! Removing.", itr->ToString().c_str(), GetGUID().GetCounter());
+ TC_LOG_ERROR("entities.player", "Can't find %s, but is in refundable storage for player %u ! Removing.", itr->ToString().c_str(), GetGUID().GetCounter());
m_refundableItems.erase(itr);
}
}
@@ -19548,7 +19582,7 @@ void Player::_SaveInventory(SQLTransaction& trans)
{
TC_LOG_ERROR("entities.player", "Player(GUID: %u Name: %s)::_SaveInventory - the bag(%u) and slot(%u) values for the item with guid %u are incorrect, the item with guid %u is there instead!", lowGuid, GetName().c_str(), item->GetBagSlot(), item->GetSlot(), item->GetGUID().GetCounter(), test->GetGUID().GetCounter());
// save all changes to the item...
- if (item->GetState() != ITEM_NEW) // only for existing items, no dupes
+ if (item->GetState() != ITEM_NEW) // only for existing items, no duplicates
item->SaveToDB(trans);
// ...but do not save position in inventory
continue;
@@ -20376,8 +20410,8 @@ Pet* Player::GetPet() const
if (IsInWorld() && pet)
return pet;
- //there may be a guardian in slot
- //TC_LOG_ERROR("entities.player", "Player::GetPet: Pet %u not exist.", GUID_LOPART(pet_guid));
+ // there may be a guardian in this slot
+ //TC_LOG_ERROR("entities.player", "Player::GetPet: Pet %u does not exist.", GUID_LOPART(pet_guid));
//const_cast(this)->SetPetGUID(0);
}
@@ -20479,7 +20513,7 @@ void Player::StopCastingCharm()
if (GetCharmGUID())
{
- TC_LOG_FATAL("entities.player", "Player %s (%s) is not able to uncharm unit (%s)", GetName().c_str(), GetGUID().ToString().c_str(), GetCharmGUID().ToString().c_str());
+ TC_LOG_FATAL("entities.player", "Player %s (%s) is not able to uncharm unit (%s)!", GetName().c_str(), GetGUID().ToString().c_str(), GetCharmGUID().ToString().c_str());
if (charm->GetCharmerGUID())
{
TC_LOG_FATAL("entities.player", "Charmed unit has charmer %s", charm->GetCharmerGUID().ToString().c_str());
@@ -21771,7 +21805,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
if (!iece)
{
- TC_LOG_ERROR("entities.player", "Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost);
+ TC_LOG_ERROR("entities.player", "Item %u has wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost);
return false;
}
@@ -21847,7 +21881,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
uint32 maxCount = MAX_MONEY_AMOUNT / pProto->BuyPrice;
if ((uint32)count > maxCount)
{
- TC_LOG_ERROR("entities.player", "Player %s tried to buy %u item id %u, causing overflow", GetName().c_str(), (uint32)count, pProto->ItemId);
+ TC_LOG_ERROR("entities.player", "Player %s tried to buy %u item id %u, causing overflow.", GetName().c_str(), (uint32)count, pProto->ItemId);
count = (uint8)maxCount;
}
price = pProto->BuyPrice * count; //it should not exceed MAX_MONEY_AMOUNT
@@ -21952,7 +21986,7 @@ void Player::UpdateHomebindTime(uint32 time)
data << uint32(m_HomebindTimer);
data << uint32(1);
GetSession()->SendPacket(&data);
- TC_LOG_DEBUG("maps", "PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName().c_str(), GetGUID().GetCounter());
+ TC_LOG_DEBUG("maps", "PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds.", GetName().c_str(), GetGUID().GetCounter());
}
}
@@ -22022,7 +22056,7 @@ void Player::UpdatePotionCooldown(Spell* spell)
// Call not from spell cast, send cooldown event for item spells if no in combat
if (!spell)
{
- // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions)
+ // spell/item pair let set proper cooldown (except non-existing charged spell cooldown spellmods for potions)
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(m_lastPotionId))
for (uint8 idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx)
if (proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
@@ -23714,7 +23748,7 @@ bool Player::HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item cons
break;
}
default:
- TC_LOG_ERROR("entities.player", "HasItemFitToSpellRequirements: Not handled spell requirement for item class %u", spellInfo->EquippedItemClass);
+ TC_LOG_ERROR("entities.player", "HasItemFitToSpellRequirements: Spell requirement not handled for item class %u", spellInfo->EquippedItemClass);
break;
}
@@ -24380,7 +24414,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
{
if (apply)
{
- TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName().c_str(), target->GetEntry(), target->GetTypeId());
+ TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s created seer %u (TypeId: %u).", GetName().c_str(), target->GetEntry(), target->GetTypeId());
if (!AddGuidValue(PLAYER_FARSIGHT, target->GetGUID()))
{
@@ -24396,7 +24430,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
}
else
{
- TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s remove seer", GetName().c_str());
+ TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s removed seer", GetName().c_str());
if (!RemoveGuidValue(PLAYER_FARSIGHT, target->GetGUID()))
{
@@ -24975,7 +25009,7 @@ void Player::_LoadSkills(PreparedQueryResult result)
if (value == 0)
{
- TC_LOG_ERROR("entities.player", "Character %u has skill %u with value 0. Will be deleted.", GetGUID().GetCounter(), skill);
+ TC_LOG_ERROR("entities.player", "Character %u has skill %u with value 0. Skill will be deleted.", GetGUID().GetCounter(), skill);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL);
@@ -25326,7 +25360,7 @@ bool Player::LearnTalent(uint32 talentId, uint32 talentRank)
uint32 spellid = talentInfo->RankID[talentRank];
if (spellid == 0)
{
- TC_LOG_ERROR("entities.player", "Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
+ TC_LOG_ERROR("entities.player", "Talent.dbc contains talent: %u Rank: %u spell id = 0", talentId, talentRank);
return false;
}
@@ -25480,7 +25514,7 @@ void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRa
uint32 spellid = talentInfo->RankID[talentRank];
if (spellid == 0)
{
- TC_LOG_ERROR("entities.player", "Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
+ TC_LOG_ERROR("entities.player", "Talent.dbc contains talent: %u Rank: %u spell id = 0", talentId, talentRank);
return;
}
@@ -25808,7 +25842,7 @@ void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset)
if (!found) // something wrong...
{
- TC_LOG_ERROR("entities.player", "Player %s tried to save equipment set " UI64FMTD " (index %u), but that equipment set not found!", GetName().c_str(), eqset.Guid, index);
+ TC_LOG_ERROR("entities.player", "Player %s tried to save equipment set " UI64FMTD " (index %u), but that equipment set was not found!", GetName().c_str(), eqset.Guid, index);
return;
}
}
@@ -26146,7 +26180,7 @@ void Player::ActivateSpec(uint8 spec)
for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
{
- // skip non-existant talent ranks
+ // skip non-existing talent ranks
if (talentInfo->RankID[rank] == 0)
continue;
RemoveSpell(talentInfo->RankID[rank], true); // removes the talent, and all dependant, learned, and chained spells..
@@ -26205,7 +26239,7 @@ void Player::ActivateSpec(uint8 spec)
// learn highest talent rank that exists in newly activated spec
for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
{
- // skip non-existant talent ranks
+ // skip non-existing talent ranks
if (talentInfo->RankID[rank] == 0)
continue;
// if the talent can be found in the newly activated PlayerTalentMap
@@ -26823,11 +26857,11 @@ std::string Player::GetMapAreaAndZoneString()
uint32 areaId = GetAreaId();
std::string areaName = "Unknown";
std::string zoneName = "Unknown";
- if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId))
+ if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId))
{
int locale = GetSession()->GetSessionDbcLocale();
areaName = area->area_name[locale];
- if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone))
+ if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone))
zoneName = zone->area_name[locale];
}
@@ -26884,7 +26918,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
pet->Relocate(x, y, z, ang);
if (!pet->IsPositionValid())
{
- TC_LOG_ERROR("misc", "Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)", pet->GetGUID().GetCounter(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY());
+ TC_LOG_ERROR("misc", "Pet (guidlow %d, entry %d) not summoned. Suggested coordinates are not valid (X: %f Y: %f)", pet->GetGUID().GetCounter(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY());
delete pet;
return NULL;
}
@@ -26893,7 +26927,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
uint32 pet_number = sObjectMgr->GeneratePetNumber();
if (!pet->Create(map->GenerateLowGuid(), map, entry, pet_number))
{
- TC_LOG_ERROR("misc", "no such creature entry %u", entry);
+ TC_LOG_ERROR("misc", "No such creature entry %u", entry);
delete pet;
return NULL;
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 6d6f1408a73..12c3c033fe8 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -909,14 +909,27 @@ enum PlayerDelayedOperations
// Maximum money amount : 2^31 - 1
extern uint64 const MAX_MONEY_AMOUNT;
+enum BindExtensionState
+{
+ EXTEND_STATE_EXPIRED = 0,
+ EXTEND_STATE_NORMAL = 1,
+ EXTEND_STATE_EXTENDED = 2,
+ EXTEND_STATE_KEEP = 255 // special state: keep current save type
+};
struct InstancePlayerBind
{
InstanceSave* save;
- bool perm;
/* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players
- that aren't already permanently bound when they are inside when a boss is killed
- or when they enter an instance that the group leader is permanently bound to. */
- InstancePlayerBind() : save(NULL), perm(false) { }
+ that aren't already permanently bound when they are inside when a boss is killed
+ or when they enter an instance that the group leader is permanently bound to. */
+ bool perm;
+ /* extend state listing:
+ EXPIRED - doesn't affect anything unless manually re-extended by player
+ NORMAL - standard state
+ EXTENDED - won't be promoted to EXPIRED at next reset period, will instead be promoted to NORMAL */
+ BindExtensionState extendState;
+
+ InstancePlayerBind() : save(NULL), perm(false), extendState(EXTEND_STATE_NORMAL) { }
};
struct AccessRequirement
@@ -1198,8 +1211,9 @@ class Player : public Unit, public GridObject
void SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time, bool welcome);
bool CanInteractWithQuestGiver(Object* questGiver);
- Creature* GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask);
- GameObject* GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const;
+ Creature* GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflagmask);
+ GameObject* GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const;
+ GameObject* GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const;
void ToggleAFK();
void ToggleDND();
@@ -2316,12 +2330,12 @@ class Player : public Unit, public GridObject
bool m_InstanceValid;
// permanent binds and solo binds by difficulty
BoundInstancesMap m_boundInstances[MAX_DIFFICULTY];
- InstancePlayerBind* GetBoundInstance(uint32 mapid, Difficulty difficulty);
+ InstancePlayerBind* GetBoundInstance(uint32 mapid, Difficulty difficulty, bool withExpired = false);
BoundInstancesMap& GetBoundInstances(Difficulty difficulty) { return m_boundInstances[difficulty]; }
InstanceSave* GetInstanceSave(uint32 mapid, bool raid);
void UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload = false);
void UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficulty, bool unload = false);
- InstancePlayerBind* BindToInstance(InstanceSave* save, bool permanent, bool load = false);
+ InstancePlayerBind* BindToInstance(InstanceSave* save, bool permanent, BindExtensionState extendState = EXTEND_STATE_NORMAL, bool load = false);
void BindToInstance();
void SetPendingBind(uint32 instanceId, uint32 bindTimer);
bool HasPendingBind() const { return _pendingBindId > 0; }
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index db084fdd606..f9f91147e6a 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -1633,6 +1633,9 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe
RoundToInterval(auraAbsorbMod, 0.0f, 100.0f);
+ int32 absorbIgnoringDamage = CalculatePct(dmgInfo.GetDamage(), auraAbsorbMod);
+ dmgInfo.ModifyDamage(-absorbIgnoringDamage);
+
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vSchoolAbsorbCopy(victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB));
@@ -1665,9 +1668,6 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe
if (defaultPrevented)
continue;
- // Apply absorb mod auras
- AddPct(currentAbsorb, -auraAbsorbMod);
-
// absorb must be smaller than the damage itself
currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(dmgInfo.GetDamage()));
@@ -1716,8 +1716,6 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe
if (defaultPrevented)
continue;
- AddPct(currentAbsorb, -auraAbsorbMod);
-
// absorb must be smaller than the damage itself
currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(dmgInfo.GetDamage()));
@@ -1746,6 +1744,8 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe
}
}
+ dmgInfo.ModifyDamage(absorbIgnoringDamage);
+
// split damage auras - only when not damaging self
if (victim != this)
{
@@ -4130,13 +4130,10 @@ void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelCharges
if (aura->GetSpellInfo()->GetDispelMask() & dispelMask)
{
- if (aura->GetSpellInfo()->Dispel == DISPEL_MAGIC)
- {
- // do not remove positive auras if friendly target
- // negative auras if non-friendly target
- if (aurApp->IsPositive() == IsFriendlyTo(caster))
- continue;
- }
+ // do not remove positive auras if friendly target
+ // negative auras if non-friendly target
+ if (aurApp->IsPositive() == IsFriendlyTo(caster))
+ continue;
// The charges / stack amounts don't count towards the total number of auras that can be dispelled.
// Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell
@@ -11140,7 +11137,7 @@ Unit* Creature::SelectVictim()
}
}
else
- return NULL;
+ return nullptr;
if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target))
{
@@ -11149,14 +11146,6 @@ Unit* Creature::SelectVictim()
return target;
}
- // Case where mob is being kited.
- // Mob may not be in range to attack or may have dropped target. In any case,
- // don't evade if damage received within the last 10 seconds
- // Does not apply to world bosses to prevent kiting to cities
- if (!isWorldBoss() && !GetInstanceId())
- if (time(NULL) - GetLastDamagedTime() <= MAX_AGGRO_RESET_TIME)
- return target;
-
// last case when creature must not go to evade mode:
// it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list
// for example at owner command to pet attack some far away creature
@@ -11165,12 +11154,12 @@ Unit* Creature::SelectVictim()
{
if ((*itr) && !CanCreatureAttack(*itr) && (*itr)->GetTypeId() != TYPEID_PLAYER
&& !(*itr)->ToCreature()->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
- return NULL;
+ return nullptr;
}
/// @todo a vehicle may eat some mob, so mob should not evade
if (GetVehicle())
- return NULL;
+ return nullptr;
// search nearby enemy before enter evade mode
if (HasReactState(REACT_AGGRESSIVE))
@@ -11188,17 +11177,17 @@ Unit* Creature::SelectVictim()
{
if ((*itr)->GetBase()->IsPermanent())
{
- AI()->EnterEvadeMode();
+ AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_OTHER);
break;
}
}
- return NULL;
+ return nullptr;
}
// enter in evade mode in other case
AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES);
- return NULL;
+ return nullptr;
}
//======================================================================
@@ -15458,12 +15447,12 @@ void Unit::JumpTo(float speedXY, float speedZ, bool forward)
}
}
-void Unit::JumpTo(WorldObject* obj, float speedZ)
+void Unit::JumpTo(WorldObject* obj, float speedZ, bool withOrientation)
{
float x, y, z;
obj->GetContactPoint(this, x, y, z);
float speedXY = GetExactDist2d(x, y) * 10.0f / speedZ;
- GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ);
+ GetMotionMaster()->MoveJump(x, y, z, GetAngle(obj), speedXY, speedZ, EVENT_JUMP, withOrientation);
}
bool Unit::HandleSpellClick(Unit* clicker, int8 seatId)
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 8171243ce96..4700a483a74 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1583,7 +1583,7 @@ class Unit : public WorldObject
void SendMoveKnockBack(Player* player, float speedXY, float speedZ, float vcos, float vsin);
void KnockbackFrom(float x, float y, float speedXY, float speedZ);
void JumpTo(float speedXY, float speedZ, bool forward = true);
- void JumpTo(WorldObject* obj, float speedZ);
+ void JumpTo(WorldObject* obj, float speedZ, bool withOrientation = false);
void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false);
diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp
index d9afebae12e..26b3da2a0e4 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -221,7 +221,7 @@ void GameEventMgr::LoadFromDB()
uint8 event_id = fields[0].GetUInt8();
if (event_id == 0)
{
- TC_LOG_ERROR("sql.sql", "`game_event` game event entry 0 is reserved and can't be used.");
+ TC_LOG_ERROR("sql.sql", "`game_event`: game event entry 0 is reserved and can't be used.");
continue;
}
@@ -240,7 +240,7 @@ void GameEventMgr::LoadFromDB()
if (pGameEvent.length == 0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check
{
- TC_LOG_ERROR("sql.sql", "`game_event` game event id (%i) isn't a world event and has length = 0, thus it can't be used.", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event`: game event id (%i) is not a world event and has length = 0, thus cannot be used.", event_id);
continue;
}
@@ -248,7 +248,7 @@ void GameEventMgr::LoadFromDB()
{
if (!sHolidaysStore.LookupEntry(pGameEvent.holiday_id))
{
- TC_LOG_ERROR("sql.sql", "`game_event` game event id (%i) have not existed holiday id %u.", event_id, pGameEvent.holiday_id);
+ TC_LOG_ERROR("sql.sql", "`game_event`: game event id (%i) contains nonexisting holiday id %u.", event_id, pGameEvent.holiday_id);
pGameEvent.holiday_id = HOLIDAY_NONE;
}
}
@@ -259,7 +259,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
@@ -283,7 +283,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_save` game event entry (%i) is out of range compared to max event entry in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_save`: game event entry (%i) is out of range compared to max event entry in `game_event`.", event_id);
continue;
}
@@ -294,7 +294,7 @@ void GameEventMgr::LoadFromDB()
}
else
{
- TC_LOG_ERROR("sql.sql", "game_event_save includes event save for non-worldevent id %u", event_id);
+ TC_LOG_ERROR("sql.sql", "game_event_save includes event save for non-worldevent id %u.", event_id);
continue;
}
@@ -302,7 +302,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u game event saves in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u game event saves in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -326,7 +326,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_prerequisite` game event id (%i) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_prerequisite`: game event id (%i) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -335,14 +335,14 @@ void GameEventMgr::LoadFromDB()
uint16 prerequisite_event = fields[1].GetUInt32();
if (prerequisite_event >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_prerequisite` game event prerequisite id (%i) is out of range compared to max event id in `game_event`", prerequisite_event);
+ TC_LOG_ERROR("sql.sql", "`game_event_prerequisite`: game event prerequisite id (%i) is out of range compared to max event id in `game_event`.", prerequisite_event);
continue;
}
mGameEvent[event_id].prerequisite_events.insert(prerequisite_event);
}
else
{
- TC_LOG_ERROR("sql.sql", "game_event_prerequisiste includes event entry for non-worldevent id %u", event_id);
+ TC_LOG_ERROR("sql.sql", "game_event_prerequisiste includes event entry for non-worldevent id %u.", event_id);
continue;
}
@@ -350,7 +350,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u game event prerequisites in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u game event prerequisites in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -363,7 +363,7 @@ void GameEventMgr::LoadFromDB()
QueryResult result = WorldDatabase.Query("SELECT guid, eventEntry FROM game_event_creature");
if (!result)
- TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in game events. DB table `game_event_creature` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in game events. DB table `game_event_creature` is empty.");
else
{
uint32 count = 0;
@@ -385,7 +385,7 @@ void GameEventMgr::LoadFromDB()
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size()))
{
- TC_LOG_ERROR("sql.sql", "`game_event_creature` game event id (%i) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_creature`: game event id (%i) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -396,7 +396,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u creatures in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u creatures in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -431,7 +431,7 @@ void GameEventMgr::LoadFromDB()
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventGameobjectGuids.size()))
{
- TC_LOG_ERROR("sql.sql", "`game_event_gameobject` game event id (%i) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_gameobject`: game event id (%i) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -442,7 +442,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u gameobjects in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u gameobjects in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -469,7 +469,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEventModelEquip.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_model_equip` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_model_equip`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -485,7 +485,7 @@ void GameEventMgr::LoadFromDB()
int8 equipId = static_cast(newModelEquipSet.equipment_id);
if (!sObjectMgr->GetEquipmentInfo(entry, equipId))
{
- TC_LOG_ERROR("sql.sql", "Table `game_event_model_equip` have creature (Guid: %u, entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.",
+ TC_LOG_ERROR("sql.sql", "Table `game_event_model_equip` contains creature (Guid: %u, entry: %u) with equipment_id %u not found in table `creature_equip_template`. Setting entry to no equipment.",
guid, entry, newModelEquipSet.equipment_id);
continue;
}
@@ -497,7 +497,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u model/equipment changes in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u model/equipment changes in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -523,7 +523,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEventCreatureQuests.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_creature_quest` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_creature_quest`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -534,7 +534,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -560,7 +560,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEventGameObjectQuests.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_gameobject_quest` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_gameobject_quest`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -571,7 +571,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -598,7 +598,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_quest_condition` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_quest_condition`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -610,7 +610,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u quest event conditions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u quest event conditions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -635,7 +635,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_condition` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_condition`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -648,7 +648,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u conditions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u conditions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -673,7 +673,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_condition_save` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_condition_save`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -684,7 +684,7 @@ void GameEventMgr::LoadFromDB()
}
else
{
- TC_LOG_ERROR("sql.sql", "game_event_condition_save contains not present condition evt id %u cond id %u", event_id, condition);
+ TC_LOG_ERROR("sql.sql", "game_event_condition_save contains not present condition event id %u condition id %u.", event_id, condition);
continue;
}
@@ -692,7 +692,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u condition saves in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u condition saves in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -718,7 +718,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_npcflag` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_npcflag`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -728,7 +728,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u npcflags in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u npcflags in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -753,13 +753,13 @@ void GameEventMgr::LoadFromDB()
if (!sObjectMgr->GetQuestTemplate(questId))
{
- TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation` quest id (%u) does not exist in `quest_template`", questId);
+ TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation`: quest id (%u) does not exist in `quest_template`.", questId);
continue;
}
if (eventEntry >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation` event id (%u) is out of range compared to max event in `game_event`", eventEntry);
+ TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation`: event id (%u) is out of range compared to max event in `game_event`.", eventEntry);
continue;
}
@@ -768,7 +768,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -792,7 +792,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEventVendors.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_npc_vendor` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_npc_vendor`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -831,7 +831,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u vendor additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u vendor additions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -855,7 +855,7 @@ void GameEventMgr::LoadFromDB()
if (event_id >= mGameEvent.size())
{
- TC_LOG_ERROR("sql.sql", "`game_event_battleground_holiday` game event id (%u) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_battleground_holiday`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
@@ -865,7 +865,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u battleground holidays in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u battleground holidays in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@@ -893,13 +893,13 @@ void GameEventMgr::LoadFromDB()
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventPoolIds.size()))
{
- TC_LOG_ERROR("sql.sql", "`game_event_pool` game event id (%i) is out of range compared to max event id in `game_event`", event_id);
+ TC_LOG_ERROR("sql.sql", "`game_event_pool`: game event id (%i) is out of range compared to max event id in `game_event`.", event_id);
continue;
}
if (!sPoolMgr->CheckPool(entry))
{
- TC_LOG_ERROR("sql.sql", "Pool Id (%u) has all creatures or gameobjects with explicit chance sum <>100 and no equal chance defined. The pool system cannot pick one to spawn.", entry);
+ TC_LOG_ERROR("sql.sql", "Pool Id (%u) has all creatures or gameobjects with explicit chance sum <> 100 and no equal chance defined. The pool system cannot pick one to spawn.", entry);
continue;
}
@@ -910,7 +910,7 @@ void GameEventMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u pools for game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u pools for game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
}
}
}
@@ -972,7 +972,7 @@ void GameEventMgr::StartArenaSeason()
if (!result)
{
- TC_LOG_ERROR("gameevent", "ArenaSeason (%u) must be an existant Arena Season", season);
+ TC_LOG_ERROR("gameevent", "ArenaSeason (%u) must be an existing Arena Season.", season);
return;
}
@@ -981,7 +981,7 @@ void GameEventMgr::StartArenaSeason()
if (eventId >= mGameEvent.size())
{
- TC_LOG_ERROR("gameevent", "EventEntry %u for ArenaSeason (%u) does not exists", eventId, season);
+ TC_LOG_ERROR("gameevent", "EventEntry %u for ArenaSeason (%u) does not exist.", eventId, season);
return;
}
@@ -1172,7 +1172,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size()))
{
- TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %zu)",
+ TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventCreatureGuids element %i (size: %zu).",
internal_event_id, mGameEventCreatureGuids.size());
return;
}
@@ -1199,7 +1199,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventGameobjectGuids.size()))
{
- TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %zu)",
+ TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventGameobjectGuids element %i (size: %zu).",
internal_event_id, mGameEventGameobjectGuids.size());
return;
}
@@ -1232,7 +1232,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventPoolIds.size()))
{
- TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempt access to out of range mGameEventPoolIds element %u (size: %zu)",
+ TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventPoolIds element %u (size: %zu).",
internal_event_id, mGameEventPoolIds.size());
return;
}
@@ -1247,7 +1247,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size()))
{
- TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %zu)",
+ TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventCreatureGuids element %i (size: %zu).",
internal_event_id, mGameEventCreatureGuids.size());
return;
}
@@ -1277,7 +1277,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventGameobjectGuids.size()))
{
- TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %zu)",
+ TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventGameobjectGuids element %i (size: %zu).",
internal_event_id, mGameEventGameobjectGuids.size());
return;
}
@@ -1306,7 +1306,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
}
if (internal_event_id < 0 || internal_event_id >= int32(mGameEventPoolIds.size()))
{
- TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventPoolIds element %u (size: %zu)", internal_event_id, mGameEventPoolIds.size());
+ TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventPoolIds element %u (size: %zu).", internal_event_id, mGameEventPoolIds.size());
return;
}
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 2f4c510d44d..0490e21eb9d 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -1786,7 +1786,7 @@ void ObjectMgr::LoadCreatures()
if (!ok)
continue;
- // -1 random, 0 no equipment,
+ // -1 random, 0 no equipment
if (data.equipmentId != 0)
{
if (!GetEquipmentInfo(data.id, data.equipmentId))
@@ -4069,7 +4069,7 @@ void ObjectMgr::LoadQuests()
// client quest log visual (area case)
if (qinfo->ZoneOrSort > 0)
{
- if (!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
+ if (!sAreaTableStore.LookupEntry(qinfo->ZoneOrSort))
{
TC_LOG_ERROR("sql.sql", "Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
qinfo->GetQuestId(), qinfo->ZoneOrSort);
@@ -6025,7 +6025,7 @@ void ObjectMgr::LoadGraveyardZones()
continue;
}
- AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId);
+ AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
if (!areaEntry)
{
TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing zone id (%u), skipped.", zoneId);
@@ -7885,7 +7885,7 @@ void ObjectMgr::LoadFishingBaseSkillLevel()
uint32 entry = fields[0].GetUInt32();
int32 skill = fields[1].GetInt16();
- AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
+ AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry);
if (!fArea)
{
TC_LOG_ERROR("sql.sql", "AreaId %u defined in `skill_fishing_base_level` does not exist", entry);
diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h
index 8ad9fdce954..a13aa38f228 100644
--- a/src/server/game/Grids/GridDefines.h
+++ b/src/server/game/Grids/GridDefines.h
@@ -229,7 +229,7 @@ namespace Trinity
inline bool IsValidMapCoord(float x, float y, float z)
{
- return IsValidMapCoord(x, y) && std::isfinite(z);
+ return IsValidMapCoord(x, y) && IsValidMapCoord(z);
}
inline bool IsValidMapCoord(float x, float y, float z, float o)
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp
index a5713ed8bfd..a7201ea3744 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp
@@ -131,7 +131,7 @@ inline void CreatureUnitRelocationWorker(Creature* c, Unit* u)
if (!u->IsAlive() || !c->IsAlive() || c == u || u->IsInFlight())
return;
- if (c->HasReactState(REACT_AGGRESSIVE) && !c->HasUnitState(UNIT_STATE_SIGHTLESS))
+ if (!c->HasUnitState(UNIT_STATE_SIGHTLESS))
{
if (c->IsAIEnabled && c->CanSeeOrDetect(u, false, true))
c->AI()->MoveInLineOfSight_Safe(u);
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index 04ed24db847..aa3672815d6 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -718,7 +718,8 @@ void Group::ConvertLeaderInstancesToGroup(Player* player, Group* group, bool swi
for (Player::BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
{
if (!switchLeader || !group->GetBoundInstance(itr->second.save->GetDifficulty(), itr->first))
- group->BindToInstance(itr->second.save, itr->second.perm, false);
+ if (itr->second.extendState) // not expired
+ group->BindToInstance(itr->second.save, itr->second.perm, false);
// permanent binds are not removed
if (switchLeader && !itr->second.perm)
@@ -2322,6 +2323,8 @@ LootMethod Group::GetLootMethod() const
ObjectGuid Group::GetLooterGuid() const
{
+ if (GetLootMethod() == FREE_FOR_ALL)
+ return ObjectGuid::Empty;
return m_looterGuid;
}
diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp
index 529a77a929d..77f2c462dad 100644
--- a/src/server/game/Handlers/AuctionHouseHandler.cpp
+++ b/src/server/game/Handlers/AuctionHouseHandler.cpp
@@ -731,7 +731,7 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData)
TC_LOG_DEBUG("network", "WORLD: Received CMSG_AUCTION_LIST_ITEMS");
std::string searchedname;
- uint8 levelmin, levelmax, usable;
+ uint8 levelmin, levelmax, usable, getAll;
uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality;
ObjectGuid guid;
@@ -743,7 +743,7 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData)
recvData >> auctionSlotID >> auctionMainCategory >> auctionSubCategory;
recvData >> quality >> usable;
- recvData.read_skip(); // unk
+ recvData >> getAll;
recvData.read_skip(); // unk
// this block looks like it uses some lame byte packing or similar...
@@ -781,11 +781,11 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData)
auctionHouse->BuildListAuctionItems(data, _player,
wsearchedname, listfrom, levelmin, levelmax, usable,
auctionSlotID, auctionMainCategory, auctionSubCategory, quality,
- count, totalcount);
+ count, totalcount, (getAll != 0 && sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY) != 0));
data.put(0, count);
data << uint32(totalcount);
- data << uint32(300); //unk 2.3.0
+ data << (uint32) sWorld->getIntConfig(CONFIG_AUCTION_SEARCH_DELAY);
SendPacket(&data);
}
diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp
index 5acafc006fe..8e895c0ba2d 100644
--- a/src/server/game/Handlers/CalendarHandler.cpp
+++ b/src/server/game/Handlers/CalendarHandler.cpp
@@ -700,6 +700,21 @@ void WorldSession::HandleSetSavedInstanceExtend(WorldPacket& recvData)
recvData >> mapId >> difficulty>> toggleExtend;
TC_LOG_DEBUG("network", "CMSG_SET_SAVED_INSTANCE_EXTEND - MapId: %u, Difficulty: %u, ToggleExtend: %s", mapId, difficulty, toggleExtend ? "On" : "Off");
+ if (Player* player = GetPlayer())
+ {
+ InstancePlayerBind* instanceBind = player->GetBoundInstance(mapId, Difficulty(difficulty), toggleExtend == 1); // include expired instances if we are toggling extend on
+ if (!instanceBind || !instanceBind->save || !instanceBind->perm)
+ return;
+
+ BindExtensionState newState;
+ if (!toggleExtend || instanceBind->extendState == EXTEND_STATE_EXPIRED)
+ newState = EXTEND_STATE_NORMAL;
+ else
+ newState = EXTEND_STATE_EXTENDED;
+
+ player->BindToInstance(instanceBind->save, true, newState, false);
+ }
+
/*
InstancePlayerBind* instanceBind = _player->GetBoundInstance(mapId, Difficulty(difficulty));
if (!instanceBind || !instanceBind->save)
diff --git a/src/server/game/Handlers/ChannelHandler.cpp b/src/server/game/Handlers/ChannelHandler.cpp
index 8b01cf235ae..a5a5b0ba7e3 100644
--- a/src/server/game/Handlers/ChannelHandler.cpp
+++ b/src/server/game/Handlers/ChannelHandler.cpp
@@ -46,7 +46,7 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
if (!channel)
return;
- AreaTableEntry const* zone = GetAreaEntryByAreaID(GetPlayer()->GetZoneId());
+ AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetPlayer()->GetZoneId());
if (!zone || !GetPlayer()->CanJoinConstantChannelInZone(channel, zone))
return;
}
diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp
index 8b3c39418bb..3c747619e75 100644
--- a/src/server/game/Handlers/MailHandler.cpp
+++ b/src/server/game/Handlers/MailHandler.cpp
@@ -37,7 +37,7 @@ bool WorldSession::CanOpenMailBox(ObjectGuid guid)
{
if (!HasPermission(rbac::RBAC_PERM_COMMAND_MAILBOX))
{
- TC_LOG_WARN("cheat", "%s attempt open mailbox in cheating way.", _player->GetName().c_str());
+ TC_LOG_WARN("cheat", "%s attempted to open mailbox by using a cheat.", _player->GetName().c_str());
return false;
}
}
@@ -158,7 +158,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
if (!receiverGuid)
{
- TC_LOG_INFO("network", "Player %u is sending mail to %s (GUID: not existed!) with subject %s "
+ TC_LOG_INFO("network", "Player %u is sending mail to %s (GUID: non-existing!) with subject %s "
"and body %s includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u",
player->GetGUID().GetCounter(), receiverName.c_str(), subject.c_str(), body.c_str(),
items_count, money, COD, stationery, package);
@@ -167,7 +167,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
}
TC_LOG_INFO("network", "Player %u is sending mail to %s (%s) with subject %s and body %s "
- "includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u",
+ "including %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u",
player->GetGUID().GetCounter(), receiverName.c_str(), receiverGuid.ToString().c_str(), subject.c_str(),
body.c_str(), items_count, money, COD, stationery, package);
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 1f9fb1e1a41..13216c6d489 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -105,7 +105,7 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData)
GameObject* go = NULL;
if (guid.IsCreatureOrVehicle())
{
- unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
+ unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_GOSSIP);
if (!unit)
{
TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with him.", guid.ToString().c_str());
@@ -114,10 +114,10 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData)
}
else if (guid.IsGameObject())
{
- go = _player->GetMap()->GetGameObject(guid);
+ go = _player->GetGameObjectIfCanInteractWith(guid);
if (!go)
{
- TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found.", guid.ToString().c_str());
+ TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with it.", guid.ToString().c_str());
return;
}
}
@@ -319,7 +319,7 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData)
continue;
std::string aname;
- if (AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(pzoneid))
+ if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(pzoneid))
aname = areaEntry->area_name[GetSessionDbcLocale()];
bool s_show = true;
@@ -1255,7 +1255,7 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recvData)
WorldPacket data(SMSG_INSPECT_TALENT, 8 + 4 + 1 + 1 + talent_points + 8 + 4 + 8 + 4);
data << player->GetGUID();
- if (sWorld->getBoolConfig(CONFIG_TALENTS_INSPECTING) || _player->IsGameMaster())
+ if (GetPlayer()->CanBeGameMaster() || sWorld->getIntConfig(CONFIG_TALENTS_INSPECTING) + (GetPlayer()->GetTeamId() == player->GetTeamId()) > 1)
player->BuildPlayerTalentsInfoData(&data);
else
{
@@ -1886,7 +1886,7 @@ void WorldSession::HandleHearthAndResurrect(WorldPacket& /*recvData*/)
return;
}
- AreaTableEntry const* atEntry = GetAreaEntryByAreaID(_player->GetAreaId());
+ AreaTableEntry const* atEntry = sAreaTableStore.LookupEntry(_player->GetAreaId());
if (!atEntry || !(atEntry->flags & AREA_FLAG_WINTERGRASP_2))
return;
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index ba061d0330e..989723f8382 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -402,7 +402,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket)
plrMover->UpdateFallInformationIfNeed(movementInfo, opcode);
- AreaTableEntry const* zone = GetAreaEntryByAreaID(plrMover->GetAreaId());
+ AreaTableEntry const* zone = sAreaTableStore.LookupEntry(plrMover->GetAreaId());
float depth = zone ? zone->MaxDepth : -500.0f;
if (movementInfo.pos.GetPositionZ() < depth)
{
@@ -413,6 +413,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket)
/// @todo discard movement packets after the player is rooted
if (plrMover->IsAlive())
{
+ plrMover->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS);
plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
// player can be alive if GM/etc
// change the death state to CORPSE to prevent the death timer from
diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp
index ea57f156aa0..7e45de8a6d6 100644
--- a/src/server/game/Handlers/NPCHandler.cpp
+++ b/src/server/game/Handlers/NPCHandler.cpp
@@ -317,7 +317,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData)
ObjectGuid guid;
recvData >> guid;
- Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
+ Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_GOSSIP);
if (!unit)
{
TC_LOG_DEBUG("network", "WORLD: HandleGossipHelloOpcode - %s not found or you can not interact with him.", guid.ToString().c_str());
@@ -357,47 +357,6 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData)
unit->AI()->sGossipHello(_player);
}
-/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData)
-{
- TC_LOG_DEBUG("network", "WORLD: CMSG_GOSSIP_SELECT_OPTION");
-
- uint32 option;
- uint32 unk;
- uint64 guid;
- std::string code = "";
-
- recvData >> guid >> unk >> option;
-
- if (_player->PlayerTalkClass->GossipOptionCoded(option))
- {
- TC_LOG_DEBUG("network", "reading string");
- recvData >> code;
- TC_LOG_DEBUG("network", "string read: %s", code.c_str());
- }
-
- Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
- if (!unit)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
- return;
- }
-
- // remove fake death
- if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
- GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
-
- if (!code.empty())
- {
- if (!Script->GossipSelectWithCode(_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str()))
- unit->OnGossipSelect (_player, option);
- }
- else
- {
- if (!Script->OnGossipSelect (_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction (option)))
- unit->OnGossipSelect (_player, option);
- }
-}*/
-
void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket& recvData)
{
TC_LOG_DEBUG("network", "WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp
index f103ab24951..2aa2d4e200e 100644
--- a/src/server/game/Handlers/QuestHandler.cpp
+++ b/src/server/game/Handlers/QuestHandler.cpp
@@ -75,7 +75,7 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData)
TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_HELLO %s", guid.ToString().c_str());
- Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
+ Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_QUESTGIVER);
if (!creature)
{
TC_LOG_DEBUG("network", "WORLD: HandleQuestgiverHelloOpcode - %s not found or you can't interact with him.",
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index f8914d3c8d7..9541cacc91c 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -287,11 +287,8 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recvData)
TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_GAMEOBJ_USE Message [%s]", guid.ToString().c_str());
- if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(guid))
+ if (GameObject* obj = GetPlayer()->GetGameObjectIfCanInteractWith(guid))
{
- if (!obj->IsWithinDistInMap(GetPlayer(), obj->GetInteractionDistance()))
- return;
-
// ignore for remote control state
if (GetPlayer()->m_mover != GetPlayer())
if (!(GetPlayer()->IsOnVehicle(GetPlayer()->m_mover) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted())
@@ -312,17 +309,13 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
if (_player->m_mover != _player)
return;
- GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
- if (!go)
- return;
+ if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(guid))
+ {
+ if (go->AI()->GossipHello(_player))
+ return;
- if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
- return;
-
- if (go->AI()->GossipHello(_player))
- return;
-
- _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
+ _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
+ }
}
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp
index 51a41176f31..432889e1e22 100644
--- a/src/server/game/Instances/InstanceSaveMgr.cpp
+++ b/src/server/game/Instances/InstanceSaveMgr.cpp
@@ -435,6 +435,23 @@ void InstanceSaveManager::LoadResetTimes()
}
}
+time_t InstanceSaveManager::GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const
+{
+ MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty);
+ if (!mapDiff || !mapDiff->resetTime)
+ {
+ TC_LOG_ERROR("misc", "InstanceSaveManager::GetSubsequentResetTime: not valid difficulty or no reset delay for map %u", mapid);
+ return 0;
+ }
+
+ time_t diff = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR;
+ time_t period = uint32(((mapDiff->resetTime * sWorld->getRate(RATE_INSTANCE_RESET_TIME)) / DAY) * DAY);
+ if (period < DAY)
+ period = DAY;
+
+ return ((resetTime + MINUTE) / DAY * DAY) + period + diff;
+}
+
void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event)
{
if (!add)
@@ -472,6 +489,17 @@ void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent ev
m_resetTimeQueue.insert(std::pair(time, event));
}
+void InstanceSaveManager::ForceGlobalReset(uint32 mapId, Difficulty difficulty)
+{
+ if (!GetDownscaledMapDifficultyData(mapId, difficulty))
+ return;
+ // remove currently scheduled reset times
+ ScheduleReset(false, 0, InstResetEvent(1, mapId, difficulty, 0));
+ ScheduleReset(false, 0, InstResetEvent(4, mapId, difficulty, 0));
+ // force global reset on the instance
+ _ResetOrWarnAll(mapId, difficulty, false, time(nullptr));
+}
+
void InstanceSaveManager::Update()
{
time_t now = time(NULL);
@@ -512,10 +540,26 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr)
// do not allow UnbindInstance to automatically unload the InstanceSaves
lock_instLists = true;
+ bool shouldDelete = true;
InstanceSave::PlayerListType &pList = itr->second->m_playerList;
- while (!pList.empty())
+ std::vector temp; // list of expired binds that should be unbound
+ for (Player* player : pList)
+ {
+ if (InstancePlayerBind* bind = player->GetBoundInstance(itr->second->GetMapId(), itr->second->GetDifficulty()))
+ {
+ ASSERT(bind->save == itr->second);
+ if (bind->perm && bind->extendState) // permanent and not already expired
+ {
+ // actual promotion in DB already happened in caller
+ bind->extendState = bind->extendState == EXTEND_STATE_EXTENDED ? EXTEND_STATE_NORMAL : EXTEND_STATE_EXPIRED;
+ shouldDelete = false;
+ continue;
+ }
+ }
+ temp.push_back(player);
+ }
+ for (Player* player : temp)
{
- Player* player = *(pList.begin());
player->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true);
}
@@ -526,8 +570,13 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr)
group->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true);
}
- delete itr->second;
- m_instanceSaveById.erase(itr++);
+ if (shouldDelete)
+ {
+ delete itr->second;
+ itr = m_instanceSaveById.erase(itr);
+ }
+ else
+ ++itr;
lock_instLists = false;
}
@@ -568,31 +617,21 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b
MapEntry const* mapEntry = sMapStore.LookupEntry(mapid);
if (!mapEntry->Instanceable())
return;
+ TC_LOG_DEBUG("misc", "InstanceSaveManager::ResetOrWarnAll: Processing map %s (%u) on difficulty %u (warn? %u)", mapEntry->name[0], mapid, uint8(difficulty), warn);
time_t now = time(NULL);
if (!warn)
{
- MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty);
- if (!mapDiff || !mapDiff->resetTime)
- {
- TC_LOG_ERROR("misc", "InstanceSaveManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid);
+ // calculate the next reset time
+ time_t next_reset = GetSubsequentResetTime(mapid, difficulty, resetTime);
+ if (!next_reset)
return;
- }
- // remove all binds to instances of the given map
- for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();)
- {
- if (itr->second->GetMapId() == mapid && itr->second->GetDifficulty() == difficulty)
- _ResetSave(itr);
- else
- ++itr;
- }
-
- // delete them from the DB, even if not loaded
+ // delete/promote instance binds from the DB, even if not loaded
SQLTransaction trans = CharacterDatabase.BeginTransaction();
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF);
stmt->setUInt16(0, uint16(mapid));
stmt->setUInt8(1, uint8(difficulty));
trans->Append(stmt);
@@ -602,21 +641,26 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b
stmt->setUInt8(1, uint8(difficulty));
trans->Append(stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INSTANCE_BY_MAP_DIFF);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF);
+ stmt->setUInt16(0, uint16(mapid));
+ stmt->setUInt8(1, uint8(difficulty));
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF);
stmt->setUInt16(0, uint16(mapid));
stmt->setUInt8(1, uint8(difficulty));
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
- // calculate the next reset time
- uint32 diff = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR;
-
- uint32 period = uint32(((mapDiff->resetTime * sWorld->getRate(RATE_INSTANCE_RESET_TIME))/DAY) * DAY);
- if (period < DAY)
- period = DAY;
-
- uint32 next_reset = uint32(((resetTime + MINUTE) / DAY * DAY) + period + diff);
+ // promote loaded binds to instances of the given map
+ for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();)
+ {
+ if (itr->second->GetMapId() == mapid && itr->second->GetDifficulty() == difficulty)
+ _ResetSave(itr);
+ else
+ ++itr;
+ }
SetResetTimeFor(mapid, difficulty, next_reset);
ScheduleReset(true, time_t(next_reset-3600), InstResetEvent(1, mapid, difficulty, 0));
diff --git a/src/server/game/Instances/InstanceSaveMgr.h b/src/server/game/Instances/InstanceSaveMgr.h
index 956513e9010..6c75fb7e5b2 100644
--- a/src/server/game/Instances/InstanceSaveMgr.h
+++ b/src/server/game/Instances/InstanceSaveMgr.h
@@ -190,6 +190,7 @@ class InstanceSaveManager
ResetTimeByMapDifficultyMap::const_iterator itr = m_resetTimeByMapDifficulty.find(MAKE_PAIR32(mapid, d));
return itr != m_resetTimeByMapDifficulty.end() ? itr->second : 0;
}
+ time_t GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const;
// Use this on startup when initializing reset times
void InitializeResetTimeFor(uint32 mapid, Difficulty d, time_t t)
@@ -210,6 +211,7 @@ class InstanceSaveManager
return m_resetTimeByMapDifficulty;
}
void ScheduleReset(bool add, time_t time, InstResetEvent event);
+ void ForceGlobalReset(uint32 mapId, Difficulty difficulty);
void Update();
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index 95d78689c82..8dc15c25b3a 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -1591,8 +1591,8 @@ void LoadLootTemplates_Fishing()
uint32 count = LootTemplates_Fishing.LoadAndCollectLootIds(lootIdSet);
// remove real entries and check existence loot
- for (uint32 i = 1; i < sAreaStore.GetNumRows(); ++i)
- if (AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(i))
+ for (uint32 i = 1; i < sAreaTableStore.GetNumRows(); ++i)
+ if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(i))
if (lootIdSet.find(areaEntry->ID) != lootIdSet.end())
lootIdSet.erase(areaEntry->ID);
diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp
index b0e8a1ebe02..bc31a3c8c06 100644
--- a/src/server/game/Mails/Mail.cpp
+++ b/src/server/game/Mails/Mail.cpp
@@ -49,8 +49,8 @@ MailSender::MailSender(Object* sender, MailStationery stationery) : m_stationery
break;
default:
m_messageType = MAIL_NORMAL;
- m_senderId = 0; // will show mail from not existed player
- TC_LOG_ERROR("misc", "MailSender::MailSender - Mail have unexpected sender typeid (%u)", sender->GetTypeId());
+ m_senderId = 0; // will show mail from non-existing player
+ TC_LOG_ERROR("misc", "MailSender::MailSender - Mail message contains unexpected sender typeid (%u).", sender->GetTypeId());
break;
}
}
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 728fdc59f6f..9f044a47cab 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -1640,13 +1640,13 @@ GridMap::GridMap()
_flags = 0;
// Area data
_gridArea = 0;
- _areaMap = NULL;
+ _areaMap = nullptr;
// Height level data
_gridHeight = INVALID_HEIGHT;
_gridGetHeight = &GridMap::getHeightFromFlat;
_gridIntHeightMultiplier = 0;
- m_V9 = NULL;
- m_V8 = NULL;
+ m_V9 = nullptr;
+ m_V8 = nullptr;
// Liquid data
_liquidType = 0;
_liquidOffX = 0;
@@ -1654,9 +1654,9 @@ GridMap::GridMap()
_liquidWidth = 0;
_liquidHeight = 0;
_liquidLevel = INVALID_HEIGHT;
- _liquidEntry = NULL;
- _liquidFlags = NULL;
- _liquidMap = NULL;
+ _liquidEntry = nullptr;
+ _liquidFlags = nullptr;
+ _liquidMap = nullptr;
}
GridMap::~GridMap()
@@ -1722,12 +1722,12 @@ void GridMap::unloadData()
delete[] _liquidEntry;
delete[] _liquidFlags;
delete[] _liquidMap;
- _areaMap = NULL;
- m_V9 = NULL;
- m_V8 = NULL;
- _liquidEntry = NULL;
- _liquidFlags = NULL;
- _liquidMap = NULL;
+ _areaMap = nullptr;
+ m_V9 = nullptr;
+ m_V8 = nullptr;
+ _liquidEntry = nullptr;
+ _liquidFlags = nullptr;
+ _liquidMap = nullptr;
_gridGetHeight = &GridMap::getHeightFromFlat;
}
@@ -1742,7 +1742,7 @@ bool GridMap::loadAreaData(FILE* in, uint32 offset, uint32 /*size*/)
_gridArea = header.gridArea;
if (!(header.flags & MAP_AREA_NO_AREA))
{
- _areaMap = new uint16 [16*16];
+ _areaMap = new uint16[16 * 16];
if (fread(_areaMap, sizeof(uint16), 16*16, in) != 16*16)
return false;
}
@@ -1792,6 +1792,7 @@ bool GridMap::loadHeightData(FILE* in, uint32 offset, uint32 /*size*/)
}
else
_gridGetHeight = &GridMap::getHeightFromFlat;
+
return true;
}
@@ -2121,12 +2122,12 @@ inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 R
uint32 liqTypeIdx = liquidEntry->Type;
if (entry < 21)
{
- if (AreaTableEntry const* area = GetAreaEntryByAreaFlagAndMap(getArea(x, y), MAPID_INVALID))
+ if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y)))
{
uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
if (!overrideLiquid && area->zone)
{
- area = GetAreaEntryByAreaID(area->zone);
+ area = sAreaTableStore.LookupEntry(area->zone);
if (area)
overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
}
@@ -2300,7 +2301,7 @@ bool Map::IsOutdoors(float x, float y, float z) const
if (wmoEntry)
{
TC_LOG_DEBUG("maps", "Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId);
- atEntry = GetAreaEntryByAreaID(wmoEntry->areaId);
+ atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId);
}
return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry);
}
@@ -2324,7 +2325,7 @@ bool Map::GetAreaInfo(float x, float y, float z, uint32 &flags, int32 &adtId, in
return false;
}
-uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const
+uint32 Map::GetAreaId(float x, float y, float z, bool *isOutdoors) const
{
uint32 mogpFlags;
int32 adtId, rootId, groupId;
@@ -2337,20 +2338,21 @@ uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const
haveAreaInfo = true;
wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId);
if (wmoEntry)
- atEntry = GetAreaEntryByAreaID(wmoEntry->areaId);
+ atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId);
}
- uint16 areaflag;
+ uint32 areaId = 0;
if (atEntry)
- areaflag = atEntry->exploreFlag;
+ areaId = atEntry->ID;
else
{
if (GridMap* gmap = const_cast