aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/Collision/Management/MMapManager.h2
-rw-r--r--src/common/Common.h39
-rw-r--r--src/common/Logging/Appender.h2
-rw-r--r--src/common/Utilities/EventMap.h12
-rw-r--r--src/common/Utilities/MessageBuffer.h (renamed from src/server/shared/Networking/MessageBuffer.h)1
-rw-r--r--src/common/Utilities/StartProcess.cpp267
-rw-r--r--src/common/Utilities/StartProcess.h67
-rw-r--r--src/common/Utilities/TaskScheduler.h2
-rw-r--r--src/common/Utilities/Util.cpp46
-rw-r--r--src/common/Utilities/Util.h3
-rw-r--r--src/server/authserver/CMakeLists.txt2
-rw-r--r--src/server/authserver/Server/AuthSession.cpp2
-rw-r--r--src/server/authserver/authserver.conf.dist7
-rw-r--r--src/server/database/CMakeLists.txt3
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.cpp2
-rw-r--r--src/server/database/Database/Field.h2
-rw-r--r--src/server/database/Database/PreparedStatement.cpp2
-rw-r--r--src/server/database/Database/QueryResult.cpp2
-rw-r--r--src/server/database/Updater/DBUpdater.cpp70
-rw-r--r--src/server/database/Updater/UpdateFetcher.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp2
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp2
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp45
-rw-r--r--src/server/game/CMakeLists.txt27
-rw-r--r--src/server/game/Chat/Chat.cpp26
-rw-r--r--src/server/game/Chat/Chat.h4
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp4
-rw-r--r--src/server/game/DataStores/DBCStores.cpp250
-rw-r--r--src/server/game/DataStores/DBCStores.h5
-rw-r--r--src/server/game/DataStores/DBCStructure.h12
-rw-r--r--src/server/game/DataStores/DBCfmt.h3
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp4
-rw-r--r--src/server/game/DungeonFinding/LFGQueue.cpp12
-rw-r--r--src/server/game/DungeonFinding/LFGQueue.h3
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.cpp6
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp32
-rw-r--r--src/server/game/Entities/Creature/Creature.h1
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp12
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h4
-rw-r--r--src/server/game/Entities/Object/Object.cpp23
-rw-r--r--src/server/game/Entities/Object/Object.h1
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp11
-rw-r--r--src/server/game/Entities/Player/Player.cpp322
-rw-r--r--src/server/game/Entities/Player/Player.h127
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp4
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp20
-rw-r--r--src/server/game/Entities/Unit/Unit.h38
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp100
-rw-r--r--src/server/game/Globals/ObjectMgr.h3
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp25
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp12
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp10
-rw-r--r--src/server/game/Instances/InstanceScript.cpp14
-rw-r--r--src/server/game/Instances/InstanceScript.h8
-rw-r--r--src/server/game/Loot/LootMgr.h4
-rw-r--r--src/server/game/Maps/AreaBoundary.h2
-rw-r--r--src/server/game/Maps/Map.cpp16
-rw-r--r--src/server/game/Maps/MapManager.cpp2
-rw-r--r--src/server/game/Maps/MapManager.h8
-rw-r--r--src/server/game/Maps/MapScripts.cpp (renamed from src/server/game/Scripting/MapScripts.cpp)7
-rw-r--r--src/server/game/Miscellaneous/Language.h4
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h2
-rw-r--r--src/server/game/Movement/MotionMaster.cpp21
-rw-r--r--src/server/game/Movement/MotionMaster.h6
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp6
-rw-r--r--src/server/game/Quests/QuestDef.h2
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp1124
-rw-r--r--src/server/game/Scripting/ScriptMgr.h72
-rw-r--r--src/server/game/Scripting/ScriptReloadMgr.cpp1520
-rw-r--r--src/server/game/Scripting/ScriptReloadMgr.h81
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp28
-rw-r--r--src/server/game/Spells/Spell.cpp4
-rw-r--r--src/server/game/Spells/SpellEffects.cpp7
-rw-r--r--src/server/game/Spells/SpellHistory.cpp8
-rw-r--r--src/server/game/Spells/SpellMgr.h2
-rw-r--r--src/server/game/Spells/SpellScript.cpp7
-rw-r--r--src/server/game/Spells/SpellScript.h11
-rw-r--r--src/server/game/World/World.cpp18
-rw-r--r--src/server/game/World/World.h7
-rw-r--r--src/server/scripts/CMakeLists.txt253
-rw-r--r--src/server/scripts/Commands/cs_account.cpp10
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp17
-rw-r--r--src/server/scripts/Commands/cs_modify.cpp4
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp10
-rw-r--r--src/server/scripts/Commands/cs_reset.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp2
-rw-r--r--src/server/scripts/Kalimdor/kalimdor_script_loader.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_the_barrens.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp6
-rw-r--r--src/server/scripts/ScriptLoader.cpp51
-rw-r--r--src/server/scripts/ScriptLoader.cpp.in.cmake64
-rw-r--r--src/server/scripts/ScriptPCH.cpp (renamed from src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp)0
-rw-r--r--src/server/scripts/ScriptPCH.h (renamed from src/server/scripts/PrecompiledHeaders/ScriptPCH.h)0
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp4
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp4
-rw-r--r--src/server/scripts/Spells/spell_holiday.cpp4
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp2
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp4
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp2
-rw-r--r--src/server/scripts/Spells/spell_pet.cpp6
-rw-r--r--src/server/scripts/Spells/spell_priest.cpp2
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp2
-rw-r--r--src/server/scripts/Spells/spell_warlock.cpp4
-rw-r--r--src/server/scripts/Spells/spell_warrior.cpp2
-rw-r--r--src/server/scripts/World/duel_reset.cpp4
-rw-r--r--src/server/shared/CMakeLists.txt2
-rw-r--r--src/server/shared/DataStores/DBCStore.h115
-rw-r--r--src/server/shared/Packets/ByteBuffer.h9
-rw-r--r--src/server/shared/Realm/Realm.cpp35
-rw-r--r--src/server/shared/Realm/Realm.h24
-rw-r--r--src/server/shared/Realm/RealmList.cpp (renamed from src/server/authserver/Realms/RealmList.cpp)52
-rw-r--r--src/server/shared/Realm/RealmList.h (renamed from src/server/authserver/Realms/RealmList.h)12
-rw-r--r--src/server/worldserver/CMakeLists.txt8
-rw-r--r--src/server/worldserver/Main.cpp2
-rw-r--r--src/server/worldserver/worldserver.conf.dist75
-rw-r--r--src/tools/map_extractor/System.cpp74
122 files changed, 4691 insertions, 872 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 4250df6d1f1..0428738f2dd 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -33,8 +33,8 @@ GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_definitions(-DTRINITY_API_EXPORT_COMMON)
add_library(common
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
# Do NOT add any extra include directory here, as we don't want the common
@@ -71,7 +71,9 @@ target_link_libraries(common
openssl
valgrind
threads
- jemalloc)
+ jemalloc
+ PRIVATE
+ process)
add_dependencies(common revision_data.h)
diff --git a/src/common/Collision/Management/MMapManager.h b/src/common/Collision/Management/MMapManager.h
index afb693553a4..a8d792296bb 100644
--- a/src/common/Collision/Management/MMapManager.h
+++ b/src/common/Collision/Management/MMapManager.h
@@ -75,7 +75,7 @@ namespace MMAP
dtNavMesh const* GetNavMesh(uint32 mapId);
uint32 getLoadedTilesCount() const { return loadedTiles; }
- uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
+ uint32 getLoadedMapsCount() const { return uint32(loadedMMaps.size()); }
private:
bool loadMapData(uint32 mapId);
uint32 packTileID(int32 x, int32 y);
diff --git a/src/common/Common.h b/src/common/Common.h
index 4ab5ae867b9..aa04abacd30 100644
--- a/src/common/Common.h
+++ b/src/common/Common.h
@@ -21,28 +21,31 @@
#include "Define.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <cmath>
-#include <errno.h>
-#include <signal.h>
-#include <assert.h>
-
-#include <set>
-#include <unordered_set>
+#include <algorithm>
+#include <array>
+#include <exception>
#include <list>
-#include <string>
#include <map>
-#include <unordered_map>
+#include <memory>
#include <queue>
+#include <set>
#include <sstream>
-#include <algorithm>
-#include <memory>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
#include <vector>
-#include <array>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <cerrno>
+#include <csignal>
+
+#include <boost/optional.hpp>
+#include <boost/utility/in_place_factory.hpp>
#include <boost/functional/hash.hpp>
#include "Debugging/Errors.h"
@@ -151,6 +154,10 @@ typedef std::vector<std::string> StringVector;
#define MAX_QUERY_LEN 32*1024
+//! Optional helper class to wrap optional values within.
+template <typename T>
+using Optional = boost::optional<T>;
+
namespace Trinity
{
//! std::make_unique implementation (TODO: remove this once C++14 is supported)
diff --git a/src/common/Logging/Appender.h b/src/common/Logging/Appender.h
index 22fe1112239..d24daa2b60d 100644
--- a/src/common/Logging/Appender.h
+++ b/src/common/Logging/Appender.h
@@ -41,7 +41,7 @@ enum LogLevel
const uint8 MaxLogLevels = 6;
-enum AppenderType
+enum AppenderType : uint8
{
APPENDER_NONE,
APPENDER_CONSOLE,
diff --git a/src/common/Utilities/EventMap.h b/src/common/Utilities/EventMap.h
index bb40980181d..6a314a9e633 100644
--- a/src/common/Utilities/EventMap.h
+++ b/src/common/Utilities/EventMap.h
@@ -122,7 +122,7 @@ public:
*/
void ScheduleEvent(uint32 eventId, Milliseconds const& time, uint32 group = 0, uint8 phase = 0)
{
- ScheduleEvent(eventId, time.count(), group, phase);
+ ScheduleEvent(eventId, uint32(time.count()), group, phase);
}
/**
@@ -145,7 +145,7 @@ public:
*/
void RescheduleEvent(uint32 eventId, Milliseconds const& time, uint32 group = 0, uint8 phase = 0)
{
- RescheduleEvent(eventId, time.count(), group, phase);
+ RescheduleEvent(eventId, uint32(time.count()), group, phase);
}
/**
@@ -169,7 +169,7 @@ public:
*/
void Repeat(Milliseconds const& time)
{
- Repeat(time.count());
+ Repeat(uint32(time.count()));
}
/**
@@ -190,7 +190,7 @@ public:
*/
void Repeat(Milliseconds const& minTime, Milliseconds const& maxTime)
{
- Repeat(minTime.count(), maxTime.count());
+ Repeat(uint32(minTime.count()), uint32(maxTime.count()));
}
/**
@@ -218,7 +218,7 @@ public:
*/
void DelayEvents(Milliseconds const& delay)
{
- DelayEvents(delay.count());
+ DelayEvents(uint32(delay.count()));
}
/**
@@ -239,7 +239,7 @@ public:
*/
void DelayEvents(Milliseconds const& delay, uint32 group)
{
- DelayEvents(delay.count(), group);
+ DelayEvents(uint32(delay.count()), group);
}
/**
diff --git a/src/server/shared/Networking/MessageBuffer.h b/src/common/Utilities/MessageBuffer.h
index d68bee181b1..d08c4b25bab 100644
--- a/src/server/shared/Networking/MessageBuffer.h
+++ b/src/common/Utilities/MessageBuffer.h
@@ -20,6 +20,7 @@
#include "Define.h"
#include <vector>
+#include <cstring>
class MessageBuffer
{
diff --git a/src/common/Utilities/StartProcess.cpp b/src/common/Utilities/StartProcess.cpp
new file mode 100644
index 00000000000..c47c02bbe87
--- /dev/null
+++ b/src/common/Utilities/StartProcess.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "StartProcess.h"
+
+#include <atomic>
+#include <thread>
+#include <functional>
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/iostreams/stream.hpp>
+#include <boost/iostreams/copy.hpp>
+#include <boost/iostreams/concepts.hpp>
+#include <boost/iostreams/device/file_descriptor.hpp>
+#include <boost/process.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "Common.h"
+#include "Log.h"
+
+using namespace boost::process;
+using namespace boost::process::initializers;
+using namespace boost::iostreams;
+
+namespace Trinity {
+
+template<typename T>
+class TCLogSink
+{
+ T callback_;
+
+public:
+ typedef char char_type;
+ typedef sink_tag category;
+
+ // Requires a callback type which has a void(std::string) signature
+ TCLogSink(T callback)
+ : callback_(std::move(callback)) { }
+
+ std::streamsize write(const char* str, std::streamsize size)
+ {
+ callback_(std::string(str, size));
+ return size;
+ }
+};
+
+template<typename T>
+auto MakeTCLogSink(T&& callback)
+ -> TCLogSink<typename std::decay<T>::type>
+{
+ return { std::forward<T>(callback) };
+}
+
+template<typename T>
+static int CreateChildProcess(T waiter, std::string const& executable,
+ std::vector<std::string> const& args,
+ std::string const& logger, std::string const& input,
+ bool secure)
+{
+ auto outPipe = create_pipe();
+ auto errPipe = create_pipe();
+
+ Optional<file_descriptor_source> inputSource;
+
+ if (!secure)
+ {
+ TC_LOG_TRACE(logger.c_str(), "Starting process \"%s\" with arguments: \"%s\".",
+ executable.c_str(), boost::algorithm::join(args, " ").c_str());
+ }
+
+ // Start the child process
+ child c = [&]
+ {
+ if (!input.empty())
+ {
+ inputSource = file_descriptor_source(input);
+
+ // With binding stdin
+ return execute(run_exe(boost::filesystem::absolute(executable)),
+ set_args(args),
+ bind_stdin(*inputSource),
+ bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
+ bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
+ }
+ else
+ {
+ // Without binding stdin
+ return execute(run_exe(boost::filesystem::absolute(executable)),
+ set_args(args),
+ bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
+ bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
+ }
+ }();
+
+ file_descriptor_source outFd(outPipe.source, close_handle);
+ file_descriptor_source errFd(errPipe.source, close_handle);
+
+ auto outInfo = MakeTCLogSink([&](std::string msg)
+ {
+ TC_LOG_INFO(logger.c_str(), "%s", msg.c_str());
+ });
+
+ auto outError = MakeTCLogSink([&](std::string msg)
+ {
+ TC_LOG_ERROR(logger.c_str(), "%s", msg.c_str());
+ });
+
+ copy(outFd, outInfo);
+ copy(errFd, outError);
+
+ // Call the waiter in the current scope to prevent
+ // the streams from closing too early on leaving the scope.
+ int const result = waiter(c);
+
+ if (!secure)
+ {
+ TC_LOG_TRACE(logger.c_str(), ">> Process \"%s\" finished with return value %i.",
+ executable.c_str(), result);
+ }
+
+ if (inputSource)
+ inputSource->close();
+
+ return result;
+}
+
+int StartProcess(std::string const& executable, std::vector<std::string> const& args,
+ std::string const& logger, std::string input_file, bool secure)
+{
+ return CreateChildProcess([](child& c) -> int
+ {
+ try
+ {
+ return wait_for_exit(c);
+ }
+ catch (...)
+ {
+ return EXIT_FAILURE;
+ }
+ }, executable, args, logger, input_file, secure);
+}
+
+class AsyncProcessResultImplementation
+ : public AsyncProcessResult
+{
+ std::string const executable;
+ std::vector<std::string> const args;
+ std::string const logger;
+ std::string const input_file;
+ bool const is_secure;
+
+ std::atomic<bool> was_terminated;
+
+ // Workaround for missing move support in boost < 1.57
+ Optional<std::shared_ptr<std::future<int>>> result;
+ Optional<std::reference_wrapper<child>> my_child;
+
+public:
+ explicit AsyncProcessResultImplementation(std::string executable_, std::vector<std::string> args_,
+ std::string logger_, std::string input_file_,
+ bool secure)
+ : executable(std::move(executable_)), args(std::move(args_)),
+ logger(std::move(logger_)), input_file(input_file_),
+ is_secure(secure), was_terminated(false) { }
+
+ AsyncProcessResultImplementation(AsyncProcessResultImplementation const&) = delete;
+ AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation const&) = delete;
+ AsyncProcessResultImplementation(AsyncProcessResultImplementation&&) = delete;
+ AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation&&) = delete;
+
+ int StartProcess()
+ {
+ ASSERT(!my_child, "Process started already!");
+
+ return CreateChildProcess([&](child& c) -> int
+ {
+ int result;
+ my_child = std::reference_wrapper<child>(c);
+
+ try
+ {
+ result = wait_for_exit(c);
+ }
+ catch (...)
+ {
+ result = EXIT_FAILURE;
+ }
+
+ my_child.reset();
+ return was_terminated ? EXIT_FAILURE : result;
+
+ }, executable, args, logger, input_file, is_secure);
+ }
+
+ void SetFuture(std::future<int> result_)
+ {
+ result = std::make_shared<std::future<int>>(std::move(result_));
+ }
+
+ /// Returns the future which contains the result of the process
+ /// as soon it is finished.
+ std::future<int>& GetFutureResult() override
+ {
+ ASSERT(*result, "The process wasn't started!");
+ return **result;
+ }
+
+ /// Tries to terminate the process
+ void Terminate() override
+ {
+ if (!my_child)
+ {
+ was_terminated = true;
+ try
+ {
+ terminate(my_child->get());
+ }
+ catch(...)
+ {
+ // Do nothing
+ }
+ }
+ }
+};
+
+TC_COMMON_API std::shared_ptr<AsyncProcessResult>
+ StartAsyncProcess(std::string executable, std::vector<std::string> args,
+ std::string logger, std::string input_file, bool secure)
+{
+ auto handle = std::make_shared<AsyncProcessResultImplementation>(
+ std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure);
+
+ handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); }));
+ return handle;
+}
+
+Optional<std::string> SearchExecutableInPath(std::string const& filename)
+{
+ try
+ {
+ auto result = search_path(filename);
+ if (result.empty())
+ return boost::none;
+ else
+ return result;
+ }
+ catch (...)
+ {
+ return boost::none;
+ }
+}
+
+} // namespace Trinity
diff --git a/src/common/Utilities/StartProcess.h b/src/common/Utilities/StartProcess.h
new file mode 100644
index 00000000000..3b380bd4f4e
--- /dev/null
+++ b/src/common/Utilities/StartProcess.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef Process_h__
+#define Process_h__
+
+#include <future>
+#include <memory>
+#include "Common.h"
+
+namespace Trinity {
+
+/// Starts a process with the given arguments and parameters and will block
+/// until the process is finished.
+/// When an input path is given, the file will be routed to the processes stdin.
+/// When the process is marked as secure no arguments are leaked to logs.
+/// Note that most executables expect it's name as the first argument.
+TC_COMMON_API int StartProcess(std::string const& executable, std::vector<std::string> const& args,
+ std::string const& logger, std::string input_file = "",
+ bool secure = false);
+
+/// Platform and library independent representation
+/// of asynchronous process results
+class AsyncProcessResult
+{
+public:
+ virtual ~AsyncProcessResult() { }
+
+ /// Returns the future which contains the result of the process
+ /// as soon it is finished.
+ virtual std::future<int>& GetFutureResult() = 0;
+
+ /// Tries to terminate the process
+ virtual void Terminate() = 0;
+};
+
+/// Starts a process asynchronously with the given arguments and parameters and
+/// returns an AsyncProcessResult immediately which is set, when the process exits.
+/// When an input path is given, the file will be routed to the processes stdin.
+/// When the process is marked as secure no arguments are leaked to logs.
+/// Note that most executables expect it's name as the first argument.
+TC_COMMON_API std::shared_ptr<AsyncProcessResult>
+ StartAsyncProcess(std::string executable, std::vector<std::string> args,
+ std::string logger, std::string input_file = "",
+ bool secure = false);
+
+/// Searches for the given executable in the PATH variable
+/// and returns a present optional when it was found.
+TC_COMMON_API Optional<std::string> SearchExecutableInPath(std::string const& filename);
+
+} // namespace Trinity
+
+#endif // Process_h__
diff --git a/src/common/Utilities/TaskScheduler.h b/src/common/Utilities/TaskScheduler.h
index c5163ef33d5..6784c968683 100644
--- a/src/common/Utilities/TaskScheduler.h
+++ b/src/common/Utilities/TaskScheduler.h
@@ -401,7 +401,7 @@ private:
auto const milli_max = std::chrono::duration_cast<std::chrono::milliseconds>(max);
// TC specific: use SFMT URandom
- return std::chrono::milliseconds(urand(milli_min.count(), milli_max.count()));
+ return std::chrono::milliseconds(urand(uint32(milli_min.count()), uint32(milli_max.count())));
}
/// Dispatch remaining tasks
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp
index 6572280d00b..3d8cda66d48 100644
--- a/src/common/Utilities/Util.cpp
+++ b/src/common/Utilities/Util.cpp
@@ -22,6 +22,7 @@
#include "utf8.h"
#include "Errors.h" // for ASSERT
#include <stdarg.h>
+#include <boost/algorithm/string/case_conv.hpp>
#if COMPILER == COMPILER_GNU
#include <sys/socket.h>
@@ -425,7 +426,7 @@ bool utf8ToConsole(const std::string& utf8str, std::string& conStr)
return false;
conStr.resize(wstr.size());
- CharToOemBuffW(&wstr[0], &conStr[0], wstr.size());
+ CharToOemBuffW(&wstr[0], &conStr[0], uint32(wstr.size()));
#else
// not implemented yet
conStr = utf8str;
@@ -439,7 +440,7 @@ bool consoleToUtf8(const std::string& conStr, std::string& utf8str)
#if PLATFORM == PLATFORM_WINDOWS
std::wstring wstr;
wstr.resize(conStr.size());
- OemToCharBuffW(&conStr[0], &wstr[0], conStr.size());
+ OemToCharBuffW(&conStr[0], &wstr[0], uint32(conStr.size()));
return WStrToUtf8(wstr, utf8str);
#else
@@ -457,7 +458,7 @@ bool Utf8FitTo(const std::string& str, std::wstring const& search)
return false;
// converting to lower case
- wstrToLower( temp );
+ wstrToLower(temp);
if (temp.find(search) == std::wstring::npos)
return false;
@@ -476,10 +477,10 @@ void utf8printf(FILE* out, const char *str, ...)
void vutf8printf(FILE* out, const char *str, va_list* ap)
{
#if PLATFORM == PLATFORM_WINDOWS
- char temp_buf[32*1024];
- wchar_t wtemp_buf[32*1024];
+ char temp_buf[32 * 1024];
+ wchar_t wtemp_buf[32 * 1024];
- size_t temp_len = vsnprintf(temp_buf, 32*1024, str, *ap);
+ size_t temp_len = vsnprintf(temp_buf, 32 * 1024, str, *ap);
//vsnprintf returns -1 if the buffer is too small
if (temp_len == size_t(-1))
temp_len = 32*1024-1;
@@ -487,7 +488,7 @@ void vutf8printf(FILE* out, const char *str, va_list* ap)
size_t wtemp_len = 32*1024-1;
Utf8toWStr(temp_buf, temp_len, wtemp_buf, wtemp_len);
- CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len+1);
+ CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], uint32(wtemp_len + 1));
fprintf(out, "%s", temp_buf);
#else
vfprintf(out, str, *ap);
@@ -528,3 +529,34 @@ std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse
return ss.str();
}
+
+void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= false*/)
+{
+ // string must have even number of characters
+ if (str.length() & 1)
+ return;
+
+ int32 init = 0;
+ int32 end = int32(str.length());
+ int8 op = 1;
+
+ if (reverse)
+ {
+ init = int32(str.length() - 2);
+ end = -2;
+ op = -1;
+ }
+
+ uint32 j = 0;
+ for (int32 i = init; i != end; i += 2 * op)
+ {
+ char buffer[3] = { str[i], str[i + 1], '\0' };
+ out[j++] = uint8(strtoul(buffer, NULL, 16));
+ }
+}
+
+bool StringToBool(std::string const& str)
+{
+ std::string lowerStr = boost::algorithm::to_lower_copy(str);
+ return lowerStr == "1" || lowerStr == "true" || lowerStr == "yes";
+}
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h
index cd0a8bae823..cc68f3b2237 100644
--- a/src/common/Utilities/Util.h
+++ b/src/common/Utilities/Util.h
@@ -318,6 +318,9 @@ TC_COMMON_API uint32 CreatePIDFile(std::string const& filename);
TC_COMMON_API uint32 GetPID();
TC_COMMON_API std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false);
+TC_COMMON_API void HexStrToByteArray(std::string const& str, uint8* out, bool reverse = false);
+
+TC_COMMON_API bool StringToBool(std::string const& str);
// simple class for not-modifyable list
template <typename T>
diff --git a/src/server/authserver/CMakeLists.txt b/src/server/authserver/CMakeLists.txt
index 1d40129ec1b..d1f0e4460e9 100644
--- a/src/server/authserver/CMakeLists.txt
+++ b/src/server/authserver/CMakeLists.txt
@@ -31,8 +31,8 @@ endif()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(authserver
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
if( NOT WIN32 )
diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp
index 982aca58eee..f044e0cea94 100644
--- a/src/server/authserver/Server/AuthSession.cpp
+++ b/src/server/authserver/Server/AuthSession.cpp
@@ -924,7 +924,7 @@ void AuthSession::RealmListCallback(PreparedQueryResult result)
pkt << uint8(lock); // if 1, then realm locked
pkt << uint8(flag); // RealmFlags
pkt << name;
- pkt << boost::lexical_cast<std::string>(GetAddressForClient(realm, GetRemoteIpAddress()));
+ pkt << boost::lexical_cast<std::string>(realm.GetAddressForClient(GetRemoteIpAddress()));
pkt << float(realm.PopulationLevel);
pkt << uint8(characterCounts[realm.Id.Realm]);
pkt << uint8(realm.Timezone); // realm category
diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist
index a97d75b3f57..db60e3b3ece 100644
--- a/src/server/authserver/authserver.conf.dist
+++ b/src/server/authserver/authserver.conf.dist
@@ -184,6 +184,13 @@ LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth"
LoginDatabase.WorkerThreads = 1
#
+# LoginDatabase.SynchThreads
+# Description: The amount of MySQL connections spawned to handle.
+# Default: 1 - (LoginDatabase.WorkerThreads)
+
+LoginDatabase.SynchThreads = 1
+
+#
###################################################################################################
###################################################################################################
diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt
index 06772cd5309..bd2fa280ad6 100644
--- a/src/server/database/CMakeLists.txt
+++ b/src/server/database/CMakeLists.txt
@@ -22,8 +22,8 @@ endif()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_library(database
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
# Do NOT add any extra include directory unless it does not create unneeded extra dependencies,
@@ -52,7 +52,6 @@ add_definitions(-DTRINITY_API_EXPORT_DATABASE)
target_link_libraries(database
PUBLIC
common
- process
mysql)
set_target_properties(database
diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp
index 087a35682aa..ba2a4256919 100644
--- a/src/server/database/Database/DatabaseWorkerPool.cpp
+++ b/src/server/database/Database/DatabaseWorkerPool.cpp
@@ -234,7 +234,7 @@ void DatabaseWorkerPool<T>::EscapeString(std::string& str)
return;
char* buf = new char[str.size() * 2 + 1];
- EscapeString(buf, str.c_str(), str.size());
+ EscapeString(buf, str.c_str(), uint32(str.size()));
str = buf;
delete[] buf;
}
diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h
index fd4ddc79450..123e25dbbf3 100644
--- a/src/server/database/Database/Field.h
+++ b/src/server/database/Database/Field.h
@@ -323,7 +323,7 @@ class TC_DATABASE_API Field
data.value = NULL;
}
- static size_t SizeForType(MYSQL_FIELD* field)
+ static uint32 SizeForType(MYSQL_FIELD* field)
{
switch (field->type)
{
diff --git a/src/server/database/Database/PreparedStatement.cpp b/src/server/database/Database/PreparedStatement.cpp
index 848a923c75d..119f1d4c93b 100644
--- a/src/server/database/Database/PreparedStatement.cpp
+++ b/src/server/database/Database/PreparedStatement.cpp
@@ -344,7 +344,7 @@ void MySQLPreparedStatement::setString(const uint8 index, const char* value)
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
- size_t len = strlen(value) + 1;
+ uint32 len = uint32(strlen(value) + 1);
param->buffer_type = MYSQL_TYPE_VAR_STRING;
delete [] static_cast<char *>(param->buffer);
param->buffer = new char[len];
diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp
index f02457f67ca..db9e737830c 100644
--- a/src/server/database/Database/QueryResult.cpp
+++ b/src/server/database/Database/QueryResult.cpp
@@ -76,7 +76,7 @@ m_length(NULL)
std::size_t rowSize = 0;
for (uint32 i = 0; i < m_fieldCount; ++i)
{
- size_t size = Field::SizeForType(&field[i]);
+ uint32 size = Field::SizeForType(&field[i]);
rowSize += size;
m_rBind[i].buffer_type = field[i].type;
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index 5f5b519d2b3..3be81e85715 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -22,19 +22,11 @@
#include "DatabaseLoader.h"
#include "Config.h"
#include "BuiltInConfig.h"
+#include "StartProcess.h"
#include <fstream>
#include <iostream>
#include <unordered_map>
-#include <boost/process.hpp>
-#include <boost/iostreams/stream.hpp>
-#include <boost/iostreams/copy.hpp>
-#include <boost/iostreams/device/file_descriptor.hpp>
-#include <boost/system/system_error.hpp>
-
-using namespace boost::process;
-using namespace boost::process::initializers;
-using namespace boost::iostreams;
std::string DBUpdaterUtil::GetCorrectedMySQLExecutable()
{
@@ -51,19 +43,16 @@ bool DBUpdaterUtil::CheckExecutable()
{
exe.clear();
- try
- {
- exe = search_path("mysql");
- }
- catch (std::runtime_error&)
+ if (auto path = Trinity::SearchExecutableInPath("mysql"))
{
- }
+ exe = std::move(*path);
- if (!exe.empty() && exists(exe))
- {
- // Correct the path to the cli
- corrected_path() = absolute(exe).generic_string();
- return true;
+ if (!exe.empty() && exists(exe))
+ {
+ // Correct the path to the cli
+ corrected_path() = absolute(exe).generic_string();
+ return true;
+ }
}
TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
@@ -387,44 +376,9 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (!database.empty())
args.push_back(database);
- // ToDo: use the existing query in memory as virtual file if possible
- file_descriptor_source source(path);
-
- uint32 ret;
- try
- {
- boost::process::pipe outPipe = create_pipe();
- boost::process::pipe errPipe = create_pipe();
-
- child c = execute(run_exe(
- boost::filesystem::absolute(DBUpdaterUtil::GetCorrectedMySQLExecutable()).generic_string()),
- set_args(args), bind_stdin(source), throw_on_error(),
- bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
- bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
-
- file_descriptor_source mysqlOutfd(outPipe.source, close_handle);
- file_descriptor_source mysqlErrfd(errPipe.source, close_handle);
-
- stream<file_descriptor_source> mysqlOutStream(mysqlOutfd);
- stream<file_descriptor_source> mysqlErrStream(mysqlErrfd);
-
- std::stringstream out;
- std::stringstream err;
-
- copy(mysqlOutStream, out);
- copy(mysqlErrStream, err);
-
- TC_LOG_INFO("sql.updates", "%s", out.str().c_str());
- TC_LOG_ERROR("sql.updates", "%s", err.str().c_str());
-
- ret = wait_for_exit(c);
- }
- catch (boost::system::system_error&)
- {
- ret = EXIT_FAILURE;
- }
-
- source.close();
+ // Invokes a mysql process which doesn't leak credentials to logs
+ int const ret = Trinity::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args,
+ "sql.updates", path.generic_string(), true);
if (ret != EXIT_SUCCESS)
{
diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp
index 2d60cdb92ef..6f67867c52b 100644
--- a/src/server/database/Updater/UpdateFetcher.cpp
+++ b/src/server/database/Updater/UpdateFetcher.cpp
@@ -354,7 +354,7 @@ uint32 UpdateFetcher::Apply(Path const& path) const
_applyFile(path);
// Return time the query took to apply
- return std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count();
+ return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count());
}
void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 859282891fb..9db36c76bb4 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -489,7 +489,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
// unless target is outside spell range, out of mana, or LOS.
bool _allowMove = false;
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(e.action.cast.spell);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(e.action.cast.spell);
int32 mana = me->GetPower(POWER_MANA);
if (me->GetDistance(*itr) > spellInfo->GetMaxRange(true) ||
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index ba1738a523b..86aaf45af88 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -859,7 +859,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
if (!IsSpellValid(e, e.action.cast.spell))
return false;
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(e.action.cast.spell);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(e.action.cast.spell);
for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
if (spellInfo->Effects[j].IsEffect(SPELL_EFFECT_KILL_CREDIT) || spellInfo->Effects[j].IsEffect(SPELL_EFFECT_KILL_CREDIT2))
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index ebc814405bf..87f5ff6ce5c 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -1533,7 +1533,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
//! Since no common attributes were found, (not even in titleRewardFlags field)
//! we explicitly check by ID. Maybe in the future we could move the achievement_reward
//! condition fields to the condition system.
- if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->GetByteValue(PLAYER_BYTES_3, 0) : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)])
+ if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)])
if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
GetPlayer()->SetTitle(titleEntry);
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
index 5acb56b5173..fd105c5b25f 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
@@ -582,13 +582,13 @@ void AuctionBotSeller::LoadSellerValues(SellerConfiguration& config)
break;
}
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_GRAY, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GRAY_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_WHITE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_WHITE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_GREEN, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GREEN_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_BLUE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_BLUE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_PURPLE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_PURPLE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_ORANGE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_ORANGE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_YELLOW, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_YELLOW_PRICE_RATIO) / 100);
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_GRAY, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GRAY_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_WHITE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_WHITE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_GREEN, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GREEN_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_BLUE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_BLUE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_PURPLE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_PURPLE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_ORANGE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_ORANGE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_YELLOW, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_YELLOW_PRICE_RATIO));
config.SetPriceRatioPerClass(ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_PRICE_RATIO));
config.SetPriceRatioPerClass(ITEM_CLASS_CONTAINER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_PRICE_RATIO));
@@ -701,10 +701,10 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
{
uint32 classRatio = config.GetPriceRatioPerClass(ItemClass(itemProto->Class));
uint32 qualityRatio = config.GetPriceRatioPerQuality(AuctionQuality(itemProto->Quality));
- uint32 priceRatio = (classRatio * qualityRatio) / 100;
+ float priceRatio = (classRatio * qualityRatio) / 10000.0f;
- uint32 buyPrice = itemProto->BuyPrice;
- uint32 sellPrice = itemProto->SellPrice;
+ float buyPrice = itemProto->BuyPrice;
+ float sellPrice = itemProto->SellPrice;
if (buyPrice == 0)
{
@@ -712,11 +712,11 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
buyPrice = sellPrice * GetSellModifier(itemProto);
else
{
- uint32 divisor = ((itemProto->Class == 2 || itemProto->Class == 4) ? 284 : 80);
- uint32 tempLevel = (itemProto->ItemLevel == 0 ? 1 : itemProto->ItemLevel);
- uint32 tempQuality = (itemProto->Quality == 0 ? 1 : itemProto->Quality);
+ float divisor = ((itemProto->Class == ITEM_CLASS_WEAPON || itemProto->Class == ITEM_CLASS_ARMOR) ? 284.0f : 80.0f);
+ float tempLevel = (itemProto->ItemLevel == 0 ? 1.0f : itemProto->ItemLevel);
+ float tempQuality = (itemProto->Quality == 0 ? 1.0f : itemProto->Quality);
- buyPrice = tempLevel * tempQuality * GetBuyModifier(itemProto)* tempLevel / divisor;
+ buyPrice = tempLevel * tempQuality * static_cast<float>(GetBuyModifier(itemProto))* tempLevel / divisor;
}
}
@@ -725,15 +725,16 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
if (sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYPRICE_SELLER))
buyPrice = sellPrice;
-
- uint32 basePrice = (buyPrice * stackCount * priceRatio) / (itemProto->Class == 6 ? 200 : itemProto->BuyCount) / 100;
- uint32 range = basePrice * 0.04;
-
- buyp = urand(basePrice - range, basePrice + range) + 1;
-
- basePrice = buyp * .5;
+
+ float basePriceFloat = (buyPrice * stackCount * priceRatio) / (itemProto->Class == 6 ? 200.0f : static_cast<float>(itemProto->BuyCount)) / 100.0f;
+ float range = basePriceFloat * 0.04f;
+
+ buyp = static_cast<uint32>(frand(basePriceFloat - range, basePriceFloat + range) + 0.5f);
+ if (buyp == 0)
+ buyp = 1;
+ uint32 basePrice = buyp * .5;
range = buyp * .4;
- bidp = urand(basePrice - range, basePrice + range) + 1;
+ bidp = urand(static_cast<uint32>(basePrice - range + 0.5f), static_cast<uint32>(basePrice + range + 0.5f)) + 1;
}
// Determines the stack size to use for the item
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index df0d2b7cc2a..239e59d6dbb 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -23,27 +23,36 @@ GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_definitions(-DTRINITY_API_EXPORT_GAME)
-add_library(game
- ${PRIVATE_SOURCES}
- ${PRIVATE_PCH_SOURCE}
-)
-
CollectIncludeDirectories(
${CMAKE_CURRENT_SOURCE_DIR}
PUBLIC_INCLUDES
# Exclude
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
+# Provide an interface target for the game project to allow
+# dependent projects to build meanwhile.
+add_library(game-interface INTERFACE)
+
+target_include_directories(game-interface
+ INTERFACE
+ ${PUBLIC_INCLUDES})
+
+target_link_libraries(game-interface
+ INTERFACE
+ shared
+ Detour)
+
+add_library(game
+ ${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES})
+
target_include_directories(game
- PUBLIC
- ${PUBLIC_INCLUDES}
PRIVATE
${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(game
PUBLIC
- shared
- Detour
+ game-interface
PRIVATE
efsw)
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index f0e98ee8c1f..ec90a5f7efb 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -32,18 +32,19 @@
#include "ScriptMgr.h"
#include "ChatLink.h"
-bool ChatHandler::load_command_table = true;
+// Lazy loading of the command table cache from commands and the
+// ScriptMgr should be thread safe since the player commands,
+// cli commands and ScriptMgr updates are all dispatched one after
+// one inside the world update loop.
+static Optional<std::vector<ChatCommand>> commandTableCache;
std::vector<ChatCommand> const& ChatHandler::getCommandTable()
{
- static std::vector<ChatCommand> commandTableCache;
-
- if (LoadCommandTable())
+ if (!commandTableCache)
{
- SetLoadCommandTable(false);
-
- std::vector<ChatCommand> cmds = sScriptMgr->GetChatCommands();
- commandTableCache.swap(cmds);
+ // We need to initialize this at top since SetDataForCommandInTable
+ // calls getCommandTable() recursively.
+ commandTableCache = sScriptMgr->GetChatCommands();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS);
PreparedQueryResult result = WorldDatabase.Query(stmt);
@@ -54,13 +55,18 @@ std::vector<ChatCommand> const& ChatHandler::getCommandTable()
Field* fields = result->Fetch();
std::string name = fields[0].GetString();
- SetDataForCommandInTable(commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
+ SetDataForCommandInTable(*commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
}
while (result->NextRow());
}
}
- return commandTableCache;
+ return *commandTableCache;
+}
+
+void ChatHandler::invalidateCommandTable()
+{
+ commandTableCache.reset();
}
char const* ChatHandler::GetTrinityString(uint32 entry) const
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index a061ff63b00..1c9368275ad 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -96,6 +96,7 @@ class TC_GAME_API ChatHandler
bool ParseCommands(const char* text);
static std::vector<ChatCommand> const& getCommandTable();
+ static void invalidateCommandTable();
bool isValidChatMessage(const char* msg);
void SendGlobalSysMessage(const char *str);
@@ -143,8 +144,6 @@ class TC_GAME_API ChatHandler
GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(ObjectGuid::LowType lowguid, uint32 entry);
bool HasSentErrorMessage() const { return sentErrorMessage; }
void SetSentErrorMessage(bool val){ sentErrorMessage = val; }
- static bool LoadCommandTable() { return load_command_table; }
- static void SetLoadCommandTable(bool val) { load_command_table = val; }
bool ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd);
protected:
@@ -157,7 +156,6 @@ class TC_GAME_API ChatHandler
WorldSession* m_session; // != NULL for chat command call and NULL for CLI command
// common global flag
- static bool load_command_table;
bool sentErrorMessage;
};
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index 50e3e64ea2c..afb6255079b 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -440,7 +440,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
if (Unit* unit = object->ToUnit())
{
if (ConditionValue1 == 0)
- condMeets = (unit->getStandState() == ConditionValue2);
+ condMeets = (unit->GetStandState() == ConditionValue2);
else if (ConditionValue2 == 0)
condMeets = unit->IsStandState();
else if (ConditionValue2 == 1)
@@ -1242,7 +1242,7 @@ bool ConditionMgr::addToGossipMenuItems(Condition* cond) const
bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond) const
{
uint32 conditionEffMask = cond->SourceGroup;
- SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->EnsureSpellInfo(cond->SourceEntry));
+ SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->AssertSpellInfo(cond->SourceEntry));
std::list<uint32> sharedMasks;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 6c51c71fe7b..5ede70da2a3 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -27,6 +27,9 @@
#include <boost/regex.hpp>
#include <map>
+#include <fstream>
+#include <iostream>
+#include <iomanip>
typedef std::map<uint16, uint32> AreaFlagByAreaID;
typedef std::map<uint32, uint32> AreaFlagByMapID;
@@ -73,6 +76,7 @@ DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt);
DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt);
DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt);
DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt);
+DBCStorage <CinematicCameraEntry> sCinematicCameraStore(CinematicCameraEntryfmt);
DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore(CinematicSequencesEntryfmt);
DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt);
DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore(CreatureDisplayInfoExtrafmt);
@@ -217,6 +221,8 @@ DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt);
DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore(WorldMapOverlayEntryfmt);
DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt);
+std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore;
+
typedef std::list<std::string> StoreProblemList;
uint32 DBCFileCount = 0;
@@ -311,6 +317,7 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sChatChannelsStore, dbcPath, "ChatChannels.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sChrClassesStore, dbcPath, "ChrClasses.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sChrRacesStore, dbcPath, "ChrRaces.dbc");
+ LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicCameraStore, dbcPath, "CinematicCamera.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoExtraStore, dbcPath, "CreatureDisplayInfoExtra.dbc");
@@ -717,10 +724,250 @@ void LoadDBCStores(const std::string& dataPath)
exit(1);
}
+ LoadM2Cameras(dataPath);
+
TC_LOG_INFO("server.loading", ">> Initialized %d data stores in %u ms", DBCFileCount, GetMSTimeDiffToNow(oldMSTime));
}
+// Convert the geomoetry from a spline value, to an actual WoW XYZ
+G3D::Vector3 TranslateLocation(G3D::Vector4 const* DBCPosition, G3D::Vector3 const* basePosition, G3D::Vector3 const* splineVector)
+{
+ G3D::Vector3 work;
+ float x = basePosition->x + splineVector->x;
+ float y = basePosition->y + splineVector->y;
+ float z = basePosition->z + splineVector->z;
+ float const distance = sqrt((x * x) + (y * y));
+ float angle = std::atan2(x, y) - DBCPosition->w;
+
+ if (angle < 0)
+ angle += 2 * float(M_PI);
+
+ work.x = DBCPosition->x + (distance * sin(angle));
+ work.y = DBCPosition->y + (distance * cos(angle));
+ work.z = DBCPosition->z + z;
+ return work;
+}
+
+// Number of cameras not used. Multiple cameras never used in 3.3.5
+bool readCamera(M2Camera const* cam, uint32 buffSize, M2Header const* header, CinematicCameraEntry const* dbcentry)
+{
+ char const* buffer = reinterpret_cast<char const*>(header);
+
+ FlyByCameraCollection cameras;
+ FlyByCameraCollection targetcam;
+
+ G3D::Vector4 DBCData;
+ DBCData.x = dbcentry->base_x;
+ DBCData.y = dbcentry->base_y;
+ DBCData.z = dbcentry->base_z;
+ DBCData.w = dbcentry->base_o;
+
+ // Read target locations, only so that we can calculate orientation
+ for (uint32 k = 0; k < cam->target_positions.timestamps.number; ++k)
+ {
+ // Extract Target positions
+ if (cam->target_positions.timestamps.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ M2Array const* targTsArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.timestamps.offset_elements);
+ if (targTsArray->offset_elements + sizeof(uint32) > buffSize || cam->target_positions.values.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ uint32 const* targTimestamps = reinterpret_cast<uint32 const*>(buffer + targTsArray->offset_elements);
+ M2Array const* targArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.values.offset_elements);
+
+ if (targArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ M2SplineKey<G3D::Vector3> const* targPositions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + targArray->offset_elements);
+
+ // Read the data for this set
+ uint32 currPos = targArray->offset_elements;
+ for (uint32 i = 0; i < targTsArray->number; ++i)
+ {
+ if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ // Translate co-ordinates
+ G3D::Vector3 newPos = TranslateLocation(&DBCData, &cam->target_position_base, &targPositions->p0);
+
+ // Add to vector
+ FlyByCamera thisCam;
+ thisCam.timeStamp = targTimestamps[i];
+ thisCam.locations.x = newPos.x;
+ thisCam.locations.y = newPos.y;
+ thisCam.locations.z = newPos.z;
+ thisCam.locations.w = 0.0f;
+ targetcam.push_back(thisCam);
+ targPositions++;
+ currPos += sizeof(M2SplineKey<G3D::Vector3>);
+ }
+ }
+
+ // Read camera positions and timestamps (translating first position of 3 only, we don't need to translate the whole spline)
+ for (uint32 k = 0; k < cam->positions.timestamps.number; ++k)
+ {
+ // Extract Camera positions for this set
+ if (cam->positions.timestamps.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ M2Array const* posTsArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.timestamps.offset_elements);
+ if (posTsArray->offset_elements + sizeof(uint32) > buffSize || cam->positions.values.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ uint32 const* posTimestamps = reinterpret_cast<uint32 const*>(buffer + posTsArray->offset_elements);
+ M2Array const* posArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.values.offset_elements);
+ if (posArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ M2SplineKey<G3D::Vector3> const* positions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + posArray->offset_elements);
+
+ // Read the data for this set
+ uint32 currPos = posArray->offset_elements;
+ for (uint32 i = 0; i < posTsArray->number; ++i)
+ {
+ if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ // Translate co-ordinates
+ G3D::Vector3 newPos = TranslateLocation(&DBCData, &cam->position_base, &positions->p0);
+
+ // Add to vector
+ FlyByCamera thisCam;
+ thisCam.timeStamp = posTimestamps[i];
+ thisCam.locations.x = newPos.x;
+ thisCam.locations.y = newPos.y;
+ thisCam.locations.z = newPos.z;
+
+ if (targetcam.size() > 0)
+ {
+ // Find the target camera before and after this camera
+ FlyByCamera lastTarget;
+ FlyByCamera nextTarget;
+
+ // Pre-load first item
+ lastTarget = targetcam[0];
+ nextTarget = targetcam[0];
+ for (uint32 j = 0; j < targetcam.size(); ++j)
+ {
+ nextTarget = targetcam[j];
+ if (targetcam[j].timeStamp > posTimestamps[i])
+ break;
+
+ lastTarget = targetcam[j];
+ }
+
+ float x = lastTarget.locations.x;
+ float y = lastTarget.locations.y;
+ float z = lastTarget.locations.z;
+
+ // Now, the timestamps for target cam and position can be different. So, if they differ we interpolate
+ if (lastTarget.timeStamp != posTimestamps[i])
+ {
+ uint32 timeDiffTarget = nextTarget.timeStamp - lastTarget.timeStamp;
+ uint32 timeDiffThis = posTimestamps[i] - lastTarget.timeStamp;
+ float xDiff = nextTarget.locations.x - lastTarget.locations.x;
+ float yDiff = nextTarget.locations.y - lastTarget.locations.y;
+ float zDiff = nextTarget.locations.z - lastTarget.locations.z;
+ x = lastTarget.locations.x + (xDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ y = lastTarget.locations.y + (yDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ z = lastTarget.locations.z + (zDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ }
+ float xDiff = x - thisCam.locations.x;
+ float yDiff = y - thisCam.locations.y;
+ thisCam.locations.w = std::atan2(yDiff, xDiff);
+
+ if (thisCam.locations.w < 0)
+ thisCam.locations.w += 2 * float(M_PI);
+ }
+
+ cameras.push_back(thisCam);
+ positions++;
+ currPos += sizeof(M2SplineKey<G3D::Vector3>);
+ }
+ }
+
+ sFlyByCameraStore[dbcentry->id] = cameras;
+ return true;
+}
+
+void LoadM2Cameras(const std::string& dataPath)
+{
+ sFlyByCameraStore.clear();
+ TC_LOG_INFO("server.loading", ">> Loading Cinematic Camera files");
+
+ uint32 oldMSTime = getMSTime();
+ for (uint32 i = 0; i < sCinematicCameraStore.GetNumRows(); ++i)
+ {
+ if (CinematicCameraEntry const* dbcentry = sCinematicCameraStore.LookupEntry(i))
+ {
+ std::string filename = dataPath.c_str();
+ filename.append(dbcentry->filename);
+
+ // Replace slashes
+ size_t loc = filename.find("\\");
+ while (loc != std::string::npos)
+ {
+ filename.replace(loc, 1, "/");
+ loc = filename.find("\\");
+ }
+
+ // Replace mdx to .m2
+ loc = filename.find(".mdx");
+ if (loc != std::string::npos)
+ filename.replace(loc, 4, ".m2");
+
+ std::ifstream m2file(filename.c_str(), std::ios::in | std::ios::binary);
+ if (!m2file.is_open())
+ continue;
+
+ // Get file size
+ m2file.seekg(0, std::ios::end);
+ std::streamoff const fileSize = m2file.tellg();
+
+ // Reject if not at least the size of the header
+ if (static_cast<uint32 const>(fileSize) < sizeof(M2Header))
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File is smaller than header size", filename.c_str());
+ m2file.close();
+ continue;
+ }
+
+ // Read 4 bytes (signature)
+ m2file.seekg(0, std::ios::beg);
+ char fileCheck[5];
+ m2file.read(fileCheck, 4);
+ fileCheck[4] = 0;
+
+ // Check file has correct magic (MD20)
+ if (strcmp(fileCheck, "MD20"))
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File identifier not found", filename.c_str());
+ m2file.close();
+ continue;
+ }
+
+ // Now we have a good file, read it all into a vector of char's, then close the file.
+ std::vector<char> buffer(fileSize);
+ m2file.seekg(0, std::ios::beg);
+ if (!m2file.read(buffer.data(), fileSize))
+ {
+ m2file.close();
+ continue;
+ }
+ m2file.close();
+
+ // Read header
+ M2Header const* header = reinterpret_cast<M2Header const*>(buffer.data());
+
+ if (header->ofsCameras + sizeof(M2Camera) > static_cast<uint32 const>(fileSize))
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.c_str());
+ continue;
+ }
+
+ // Get camera(s) - Main header, then dump them.
+ M2Camera const* cam = reinterpret_cast<M2Camera const*>(buffer.data() + header->ofsCameras);
+ if (!readCamera(cam, fileSize, header, dbcentry))
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.c_str());
+ }
+ }
+ TC_LOG_INFO("server.loading", ">> Loaded %u cinematic waypoint sets in %u ms", (uint32)sFlyByCameraStore.size(), GetMSTimeDiffToNow(oldMSTime));
+}
+
SimpleFactionsList const* GetFactionTeamList(uint32 faction)
{
FactionTeamMap::const_iterator itr = sFactionTeamMap.find(faction);
@@ -1017,5 +1264,6 @@ ResponseCodes ValidateName(std::string const& name, LocaleConstant locale)
EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender)
{
- return sEmotesTextSoundMap[EmotesTextSoundKey(emote, race, gender)];
+ auto itr = sEmotesTextSoundMap.find(EmotesTextSoundKey(emote, race, gender));
+ return itr != sEmotesTextSoundMap.end() ? itr->second : nullptr;
}
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index beb0bb514cc..6cad13fdf9a 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -27,6 +27,8 @@
#include <list>
typedef std::list<uint32> SimpleFactionsList;
+typedef std::vector<FlyByCamera> FlyByCameraCollection;
+
TC_GAME_API SimpleFactionsList const* GetFactionTeamList(uint32 faction);
TC_GAME_API char* GetPetName(uint32 petfamily, uint32 dbclang);
@@ -96,6 +98,7 @@ TC_GAME_API extern DBCStorage <CharSectionsEntry> sCharSectionsStore;
TC_GAME_API extern DBCStorage <CharTitlesEntry> sCharTitlesStore;
TC_GAME_API extern DBCStorage <ChrClassesEntry> sChrClassesStore;
TC_GAME_API extern DBCStorage <ChrRacesEntry> sChrRacesStore;
+TC_GAME_API extern DBCStorage <CinematicCameraEntry> sCinematicCameraStore;
TC_GAME_API extern DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore;
TC_GAME_API extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore;
TC_GAME_API extern DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore;
@@ -193,7 +196,9 @@ TC_GAME_API extern DBCStorage <WMOAreaTableEntry> sWMOAreaTableStore;
//TC_GAME_API extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates
TC_GAME_API extern DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore;
TC_GAME_API extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore;
+TC_GAME_API extern std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore;
TC_GAME_API void LoadDBCStores(const std::string& dataPath);
+TC_GAME_API void LoadM2Cameras(const std::string& dataPath);
#endif
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index b5dc4489148..51b3dcbd38b 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -725,24 +725,22 @@ struct ChrRacesEntry
uint32 expansion; // 68 (0 - original race, 1 - tbc addon, ...)
};
-/* not used
struct CinematicCameraEntry
{
uint32 id; // 0 index
char* filename; // 1
uint32 soundid; // 2 in SoundEntries.dbc or 0
- float start_x; // 3
- float start_y; // 4
- float start_z; // 5
- float unk6; // 6 speed?
+ float base_x; // 3
+ float base_y; // 4
+ float base_z; // 5
+ float base_o; // 6
};
-*/
struct CinematicSequencesEntry
{
uint32 Id; // 0 index
//uint32 unk1; // 1 always 0
- //uint32 cinematicCamera; // 2 id in CinematicCamera.dbc
+ uint32 cinematicCamera; // 2 id in CinematicCamera.dbc
// 3-9 always 0
};
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index c61ec997bc2..1accc81714b 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -38,7 +38,8 @@ char const CharTitlesEntryfmt[] = "nxssssssssssssssssxssssssssssssssssxi";
char const ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx";
char const ChrClassesEntryfmt[] = "nxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii";
char const ChrRacesEntryfmt[] = "niixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
-char const CinematicSequencesEntryfmt[] = "nxxxxxxxxx";
+char const CinematicCameraEntryfmt[] = "nsiffff";
+char const CinematicSequencesEntryfmt[] = "nxixxxxxxx";
char const CreatureDisplayInfofmt[] = "nixifxxxxxxxxxxx";
char const CreatureDisplayInfoExtrafmt[] = "diixxxxxxxxxxxxxxxxxx";
char const CreatureFamilyfmt[] = "nfifiiiiixssssssssssssssssxx";
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index fe997bc0343..e72859c23e7 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -40,8 +40,6 @@ namespace lfg
LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1),
m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK))
{
- new LFGPlayerScript();
- new LFGGroupScript();
}
LFGMgr::~LFGMgr()
@@ -1148,7 +1146,7 @@ void LFGMgr::RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdate
for (GuidList::const_iterator it = proposal.queues.begin(); it != proposal.queues.end(); ++it)
{
ObjectGuid guid = *it;
- queue.AddToQueue(guid);
+ queue.AddToQueue(guid, true);
}
ProposalsStore.erase(itProposal);
diff --git a/src/server/game/DungeonFinding/LFGQueue.cpp b/src/server/game/DungeonFinding/LFGQueue.cpp
index 314803d1602..d156158dee6 100644
--- a/src/server/game/DungeonFinding/LFGQueue.cpp
+++ b/src/server/game/DungeonFinding/LFGQueue.cpp
@@ -117,7 +117,7 @@ std::string LFGQueue::GetDetailedMatchRoles(GuidList const& check) const
return o.str();
}
-void LFGQueue::AddToQueue(ObjectGuid guid)
+void LFGQueue::AddToQueue(ObjectGuid guid, bool reAdd)
{
LfgQueueDataContainer::iterator itQueue = QueueDataStore.find(guid);
if (itQueue == QueueDataStore.end())
@@ -126,7 +126,10 @@ void LFGQueue::AddToQueue(ObjectGuid guid)
return;
}
- AddToNewQueue(guid);
+ if (reAdd)
+ AddToFrontCurrentQueue(guid);
+ else
+ AddToNewQueue(guid);
}
void LFGQueue::RemoveFromQueue(ObjectGuid guid)
@@ -171,6 +174,11 @@ void LFGQueue::AddToCurrentQueue(ObjectGuid guid)
currentQueueStore.push_back(guid);
}
+void LFGQueue::AddToFrontCurrentQueue(ObjectGuid guid)
+{
+ currentQueueStore.push_front(guid);
+}
+
void LFGQueue::RemoveFromCurrentQueue(ObjectGuid guid)
{
currentQueueStore.remove(guid);
diff --git a/src/server/game/DungeonFinding/LFGQueue.h b/src/server/game/DungeonFinding/LFGQueue.h
index 7c35c4d04e3..32711e81468 100644
--- a/src/server/game/DungeonFinding/LFGQueue.h
+++ b/src/server/game/DungeonFinding/LFGQueue.h
@@ -89,7 +89,7 @@ class TC_GAME_API LFGQueue
// Add/Remove from queue
std::string GetDetailedMatchRoles(GuidList const& check) const;
- void AddToQueue(ObjectGuid guid);
+ void AddToQueue(ObjectGuid guid, bool reAdd = false);
void RemoveFromQueue(ObjectGuid guid);
void AddQueueData(ObjectGuid guid, time_t joinTime, LfgDungeonSet const& dungeons, LfgRolesMap const& rolesMap);
void RemoveQueueData(ObjectGuid guid);
@@ -116,6 +116,7 @@ class TC_GAME_API LFGQueue
void AddToNewQueue(ObjectGuid guid);
void AddToCurrentQueue(ObjectGuid guid);
+ void AddToFrontCurrentQueue(ObjectGuid guid);
void RemoveFromNewQueue(ObjectGuid guid);
void RemoveFromCurrentQueue(ObjectGuid guid);
diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp
index 79d36055870..1d2b963b254 100644
--- a/src/server/game/DungeonFinding/LFGScripts.cpp
+++ b/src/server/game/DungeonFinding/LFGScripts.cpp
@@ -241,4 +241,10 @@ void LFGGroupScript::OnInviteMember(Group* group, ObjectGuid guid)
sLFGMgr->LeaveLfg(leader);
}
+void AddSC_LFGScripts()
+{
+ new LFGPlayerScript();
+ new LFGGroupScript();
+}
+
} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGScripts.h b/src/server/game/DungeonFinding/LFGScripts.h
index ec64604a282..9f52668ea61 100644
--- a/src/server/game/DungeonFinding/LFGScripts.h
+++ b/src/server/game/DungeonFinding/LFGScripts.h
@@ -53,4 +53,6 @@ class TC_GAME_API LFGGroupScript : public GroupScript
void OnInviteMember(Group* group, ObjectGuid guid) override;
};
+/*keep private*/ void AddSC_LFGScripts();
+
} // namespace lfg
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 61d5d7d9459..d5e1e0956b1 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -342,10 +342,10 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
m_creatureInfo = cinfo; // map mode related always
// equal to player Race field, but creature does not have race
- SetByteValue(UNIT_FIELD_BYTES_0, 0, 0);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, 0);
// known valid are: CLASS_WARRIOR, CLASS_PALADIN, CLASS_ROGUE, CLASS_MAGE
- SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(cinfo->unit_class));
// Cancel load if no model defined
if (!(cinfo->GetFirstValidModelId()))
@@ -364,7 +364,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
// Load creature equipment
if (data && data->equipmentId != 0)
@@ -791,6 +791,24 @@ void Creature::DoFleeToGetAssistance()
}
}
+bool Creature::AIM_Destroy()
+{
+ if (m_AI_locked)
+ {
+ TC_LOG_DEBUG("scripts", "AIM_Destroy: failed to destroy, locked.");
+ return false;
+ }
+
+ ASSERT(!i_disabledAI,
+ "The disabled AI wasn't cleared!");
+
+ delete i_AI;
+ i_AI = nullptr;
+
+ IsAIEnabled = false;
+ return true;
+}
+
bool Creature::AIM_Initialize(CreatureAI* ai)
{
// make sure nothing can change the AI during AI update
@@ -800,12 +818,12 @@ bool Creature::AIM_Initialize(CreatureAI* ai)
return false;
}
- UnitAI* oldAI = i_AI;
+ AIM_Destroy();
Motion_Initialize();
i_AI = ai ? ai : FactorySelector::selectAI(this);
- delete oldAI;
+
IsAIEnabled = true;
i_AI->InitializeAI();
// Initialize vehicle
@@ -889,7 +907,7 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
LastUsedScriptID = GetCreatureTemplate()->ScriptID;
@@ -1705,7 +1723,7 @@ void Creature::Respawn(bool force)
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
GetMotionMaster()->InitDefault();
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index c6b19dbb939..8466dad9e95 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -476,6 +476,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool IsInEvadeMode() const { return HasUnitState(UNIT_STATE_EVADE); }
+ bool AIM_Destroy();
bool AIM_Initialize(CreatureAI* ai = NULL);
void Motion_Initialize();
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 1acfeacbf83..7f922f89572 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -71,9 +71,15 @@ GameObject::~GameObject()
// CleanupsBeforeDelete();
}
-bool GameObject::AIM_Initialize()
+void GameObject::AIM_Destroy()
{
delete m_AI;
+ m_AI = nullptr;
+}
+
+bool GameObject::AIM_Initialize()
+{
+ AIM_Destroy();
m_AI = FactorySelector::SelectGameObjectAI(this);
@@ -1282,8 +1288,8 @@ void GameObject::Use(Unit* user)
{
if (Player* ChairUser = ObjectAccessor::FindPlayer(itr->second))
{
- if (ChairUser->IsSitState() && ChairUser->getStandState() != UNIT_STAND_STATE_SIT && ChairUser->GetExactDist2d(x_i, y_i) < 0.1f)
- continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->getStandState() != UNIT_STAND_STATE_SIT check is required.
+ if (ChairUser->IsSitState() && ChairUser->GetStandState() != UNIT_STAND_STATE_SIT && ChairUser->GetExactDist2d(x_i, y_i) < 0.1f)
+ continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->GetStandState() != UNIT_STAND_STATE_SIT check is required.
else
itr->second.Clear(); // This seat is unoccupied.
}
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 49f94013ac2..2b092427178 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -849,8 +849,10 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void UpdateModelPosition();
- protected:
+ void AIM_Destroy();
bool AIM_Initialize();
+
+ protected:
GameObjectModel* CreateModel();
void UpdateModel(); // updates model in case displayId were changed
uint32 m_spellId;
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 824f42ad671..45952ba51ac 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -928,6 +928,11 @@ bool Object::HasByteFlag(uint16 index, uint8 offset, uint8 flag) const
return (((uint8*)&m_uint32Values[index])[offset] & flag) != 0;
}
+void Object::ApplyModByteFlag(uint16 index, uint8 offset, uint8 flag, bool apply)
+{
+ if (apply) SetByteFlag(index, offset, flag); else RemoveByteFlag(index, offset, flag);
+}
+
void Object::SetFlag64(uint16 index, uint64 newFlag)
{
uint64 oldval = GetUInt64Value(index);
@@ -1465,9 +1470,20 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
float WorldObject::GetGridActivationRange() const
{
if (ToPlayer())
+ {
+ if (ToPlayer()->IsOnCinematic())
+ return DEFAULT_VISIBILITY_INSTANCE;
return GetMap()->GetVisibilityRange();
+ }
else if (ToCreature())
return ToCreature()->m_SightDistance;
+ else if (ToDynObject())
+ {
+ if (isActiveObject())
+ return GetMap()->GetVisibilityRange();
+ else
+ return 0.0f;
+ }
else
return 0.0f;
}
@@ -1488,6 +1504,8 @@ float WorldObject::GetSightRange(const WorldObject* target) const
{
if (target && target->isActiveObject() && !target->ToPlayer())
return MAX_VISIBILITY_DISTANCE;
+ else if (ToPlayer()->IsOnCinematic())
+ return DEFAULT_VISIBILITY_INSTANCE;
else
return GetMap()->GetVisibilityRange();
}
@@ -1497,6 +1515,11 @@ float WorldObject::GetSightRange(const WorldObject* target) const
return SIGHT_RANGE_UNIT;
}
+ if (ToDynObject() && isActiveObject())
+ {
+ return GetMap()->GetVisibilityRange();
+ }
+
return 0.0f;
}
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 029a8c08fd7..d4c8fc35451 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -160,6 +160,7 @@ class TC_GAME_API Object
void RemoveByteFlag(uint16 index, uint8 offset, uint8 newFlag);
void ToggleByteFlag(uint16 index, uint8 offset, uint8 flag);
bool HasByteFlag(uint16 index, uint8 offset, uint8 flag) const;
+ void ApplyModByteFlag(uint16 index, uint8 offset, uint8 flag, bool apply);
void SetFlag64(uint16 index, uint64 newFlag);
void RemoveFlag64(uint16 index, uint64 oldFlag);
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 0da63c5e500..37fb23b38f5 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -213,12 +213,14 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
case SUMMON_PET:
petlevel = owner->getLevel();
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x800); // class = mage
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(CLASS_MAGE));
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
// this enables popup window (pet dismiss, cancel)
break;
case HUNTER_PET:
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); // class = warrior, gender = none, power = focus
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_WARRIOR);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, GENDER_NONE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, POWER_FOCUS);
SetSheath(SHEATH_STATE_MELEE);
SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
@@ -226,7 +228,6 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
// this enables popup window (pet abandon, cancel)
SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS));
SetPower(POWER_HAPPINESS, fields[12].GetUInt32());
- setPowerType(POWER_FOCUS);
break;
default:
if (!IsPetGhoul())
@@ -822,7 +823,9 @@ bool Pet::CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map, uint32 phas
if (cinfo->type == CREATURE_TYPE_BEAST)
{
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_WARRIOR);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, GENDER_NONE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, POWER_FOCUS);
SetSheath(SHEATH_STATE_MELEE);
SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
}
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index a3602166edf..c58f2c940fc 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -96,6 +96,9 @@
#define SKILL_PERM_BONUS(x) int16(PAIR32_HIPART(x))
#define MAKE_SKILL_BONUS(t, p) MAKE_PAIR32(t, p)
+#define CINEMATIC_LOOKAHEAD (2 * IN_MILLISECONDS)
+#define CINEMATIC_UPDATEDIFF 500
+
enum CharacterFlags
{
CHARACTER_FLAG_NONE = 0x00000000,
@@ -535,6 +538,14 @@ Player::Player(WorldSession* session): Unit(true)
_activeCheats = CHEAT_NONE;
healthBeforeDuel = 0;
manaBeforeDuel = 0;
+
+ m_cinematicDiff = 0;
+ m_lastCinematicCheck = 0;
+ m_activeCinematicCameraId = 0;
+ m_cinematicCamera = nullptr;
+ m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f);
+ m_CinematicObject = nullptr;
+
m_achievementMgr = new AchievementMgr(this);
m_reputationMgr = new ReputationMgr(this);
}
@@ -643,9 +654,10 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
return false;
}
- uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16);
-
- SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, createInfo->Race);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, createInfo->Class);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, createInfo->Gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, powertype);
InitDisplayIds();
if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
{
@@ -658,13 +670,14 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); // -1 is default value
- SetUInt32Value(PLAYER_BYTES, (createInfo->Skin | (createInfo->Face << 8) | (createInfo->HairStyle << 16) | (createInfo->HairColor << 24)));
- SetUInt32Value(PLAYER_BYTES_2, (createInfo->FacialHair |
- (0x00 << 8) |
- (0x00 << 16) |
- (((GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED) << 24)));
- SetByteValue(PLAYER_BYTES_3, 0, createInfo->Gender);
- SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1)
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, createInfo->Skin);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID, createInfo->Face);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, createInfo->HairStyle);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, createInfo->HairColor);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, createInfo->FacialHair);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, (GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, createInfo->Gender);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_ARENA_FACTION, 0);
SetUInt32Value(PLAYER_GUILDID, 0);
SetUInt32Value(PLAYER_GUILDRANK, 0);
@@ -1179,7 +1192,7 @@ void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/)
m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK);
uint32 newDrunkenState = Player::GetDrunkenstateByValue(newDrunkValue);
- SetByteValue(PLAYER_BYTES_3, 1, newDrunkValue);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, newDrunkValue);
UpdateObjectVisibility();
if (!isSobering)
@@ -1222,7 +1235,15 @@ void Player::Update(uint32 p_time)
m_spellModTakingSpell = nullptr;
}
- //used to implement delayed far teleport
+ // Update cinematic location, if 500ms have passed and we're doing a cinematic now.
+ m_cinematicDiff += p_time;
+ if (m_cinematicCamera && m_activeCinematicCameraId && GetMSTimeDiffToNow(m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF)
+ {
+ m_lastCinematicCheck = getMSTime();
+ UpdateCinematicLocation(p_time);
+ }
+
+ //used to implement delayed far teleports
SetCanDelayTeleport(true);
Unit::Update(p_time);
SetCanDelayTeleport(false);
@@ -2822,8 +2843,8 @@ void Player::GiveLevel(uint8 level)
{
++m_grantableLevels;
- if (!HasByteFlag(PLAYER_FIELD_BYTES, 1, 0x01))
- SetByteFlag(PLAYER_FIELD_BYTES, 1, 0x01);
+ if (!HasByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01))
+ SetByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01);
}
sScriptMgr->OnPlayerLevelChanged(this, oldLevel);
@@ -3841,7 +3862,7 @@ bool Player::Has310Flyer(bool checkAllSpells, uint32 excludeSpellId)
if (_spell_idx->second->skillId != SKILL_MOUNTS)
break; // We can break because mount spells belong only to one skillline (at least 310 flyers do)
- spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED &&
spellInfo->Effects[i].CalcValue() == 310)
@@ -3861,7 +3882,7 @@ void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns)
// remove cooldowns on spells that have < 10 min CD
GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS;
}, true);
@@ -4789,7 +4810,7 @@ Corpse* Player::CreateCorpse()
// prevent the existence of 2 corpses for one player
SpawnCorpseBones();
- uint32 _pb, _pb2, _cfb1, _cfb2;
+ uint32 _cfb1, _cfb2;
Corpse* corpse = new Corpse((m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE);
SetPvPDeath(false);
@@ -4802,16 +4823,13 @@ Corpse* Player::CreateCorpse()
_corpseLocation.WorldRelocate(*this);
- _pb = GetUInt32Value(PLAYER_BYTES);
- _pb2 = GetUInt32Value(PLAYER_BYTES_2);
-
- uint8 skin = (uint8)(_pb);
- uint8 face = (uint8)(_pb >> 8);
- uint8 hairstyle = (uint8)(_pb >> 16);
- uint8 haircolor = (uint8)(_pb >> 24);
- uint8 facialhair = (uint8)(_pb2);
+ uint8 skin = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
+ uint8 face = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID);
+ uint8 hairstyle = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID);
+ uint8 haircolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
+ uint8 facialhair = GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE);
- _cfb1 = ((0x00) | (getRace() << 8) | (GetByteValue(PLAYER_BYTES_3, 0) << 16) | (skin << 24));
+ _cfb1 = ((0x00) | (getRace() << 8) | (GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) << 16) | (skin << 24));
_cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
corpse->SetUInt32Value(CORPSE_FIELD_BYTES_1, _cfb1);
@@ -6399,11 +6417,13 @@ void Player::SendDirectMessage(WorldPacket const* data) const
m_session->SendPacket(data);
}
-void Player::SendCinematicStart(uint32 CinematicSequenceId) const
+void Player::SendCinematicStart(uint32 CinematicSequenceId)
{
WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
data << uint32(CinematicSequenceId);
SendDirectMessage(&data);
+ if (const CinematicSequencesEntry* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId))
+ SetActiveCinematicCamera(sequence->cinematicCamera);
}
void Player::SendMovieStart(uint32 MovieId) const
@@ -10344,8 +10364,7 @@ InventoryResult Player::CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest, Ite
if (pItem2)
{
// can be merged at least partly
- uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
- if (res != EQUIP_ERR_OK)
+ if (pItem2->CanBeMergedPartlyWith(pProto) != EQUIP_ERR_OK)
continue;
// descrease at current stacksize
@@ -10395,8 +10414,7 @@ InventoryResult Player::CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 sl
if (pItem2)
{
// can be merged at least partly
- uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
- if (res != EQUIP_ERR_OK)
+ if (pItem2->CanBeMergedPartlyWith(pProto) != EQUIP_ERR_OK)
continue;
// descrease at current stacksize
@@ -11074,7 +11092,7 @@ InventoryResult Player::CanStoreItems(Item** items, int count, uint32* itemLimit
continue;
// search free slot in bags
- for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
+ for (uint8 t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
{
if (Bag* bag = GetBagByPos(t))
{
@@ -12599,7 +12617,7 @@ void Player::DestroyConjuredItems(bool update)
Item* Player::GetItemByEntry(uint32 entry) const
{
// in inventory
- for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
+ for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem->GetEntry() == entry)
return pItem;
@@ -12609,14 +12627,14 @@ Item* Player::GetItemByEntry(uint32 entry) const
if (pItem->GetEntry() == entry)
return pItem;
- for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
if (Bag* pBag = GetBagByPos(i))
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
if (Item* pItem = pBag->GetItemByPos(j))
if (pItem->GetEntry() == entry)
return pItem;
- for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem->GetEntry() == entry)
return pItem;
@@ -13809,8 +13827,8 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
TC_LOG_ERROR("entities.player", "Player::ApplyEnchantment: Unknown item enchantment (ID: %u, DisplayType: %u) for player '%s' (%s)",
enchant_id, enchant_display_type, GetName().c_str(), GetGUID().ToString().c_str());
break;
- } /*switch (enchant_display_type)*/
- } /*for*/
+ }
+ }
}
// visualize enchantment at player and equipped items
@@ -16861,12 +16879,9 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
return false;
}
- // overwrite some data fields
- uint32 bytes0 = 0;
- bytes0 |= fields[3].GetUInt8(); // race
- bytes0 |= fields[4].GetUInt8() << 8; // class
- bytes0 |= gender << 16; // gender
- SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, fields[3].GetUInt8());
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, fields[4].GetUInt8());
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, gender);
// check if race/class combination is valid
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
@@ -16893,25 +16908,25 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
money = MAX_MONEY_AMOUNT;
SetMoney(money);
- SetByteValue(PLAYER_BYTES, 0, fields[9].GetUInt8());
- SetByteValue(PLAYER_BYTES, 1, fields[10].GetUInt8());
- SetByteValue(PLAYER_BYTES, 2, fields[11].GetUInt8());
- SetByteValue(PLAYER_BYTES, 3, fields[12].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 0, fields[13].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 2, fields[14].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 3, fields[15].GetUInt8());
- SetByteValue(PLAYER_BYTES_3, 0, fields[5].GetUInt8());
- SetByteValue(PLAYER_BYTES_3, 1, fields[54].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, fields[9].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID, fields[10].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, fields[11].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, fields[12].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, fields[13].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS, fields[14].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, fields[15].GetUInt8());
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, fields[5].GetUInt8());
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, fields[54].GetUInt8());
if (!ValidateAppearance(
fields[3].GetUInt8(), // race
fields[4].GetUInt8(), // class
gender,
- GetByteValue(PLAYER_BYTES, 2),
- GetByteValue(PLAYER_BYTES, 3),
- GetByteValue(PLAYER_BYTES, 1),
- GetByteValue(PLAYER_BYTES_2, 0),
- GetByteValue(PLAYER_BYTES, 0)))
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID),
+ GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID)))
{
TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong Appearance values (Hair/Skin/Color), can't load.", guid.ToString().c_str());
return false;
@@ -16925,7 +16940,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
SetUInt32Value(PLAYER_AMMO_ID, fields[68].GetUInt32());
// set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise)
- SetByteValue(PLAYER_FIELD_BYTES, 2, fields[70].GetUInt8());
+ SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, fields[70].GetUInt8());
InitDisplayIds();
@@ -17508,7 +17523,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_REFER_A_FRIEND);
if (m_grantableLevels > 0)
- SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01);
+ SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01);
_LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES));
@@ -17715,7 +17730,7 @@ void Player::LoadCorpse(PreparedQueryResult result)
{
Field* fields = result->Fetch();
_corpseLocation.WorldRelocate(fields[0].GetUInt16(), fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
- ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(_corpseLocation.GetMapId())->Instanceable());
+ ApplyModByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(_corpseLocation.GetMapId())->Instanceable());
}
else
ResurrectPlayer(0.5f);
@@ -17751,7 +17766,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
ObjectGuid::LowType bagGuid = fields[11].GetUInt32();
uint8 slot = fields[12].GetUInt8();
- uint8 err = EQUIP_ERR_OK;
+ InventoryResult err = EQUIP_ERR_OK;
// Item is not in bag
if (!bagGuid)
{
@@ -17827,7 +17842,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
else
{
TC_LOG_ERROR("entities.player", "Player::_LoadInventory: Player '%s' (%s) has item (%s, entry: %u) which can't be loaded into inventory (Bag %u, slot: %u) by reason %u. Item will be sent by mail.",
- GetName().c_str(), GetGUID().ToString().c_str(), item->GetGUID().ToString().c_str(), item->GetEntry(), bagGuid, slot, err);
+ GetName().c_str(), GetGUID().ToString().c_str(), item->GetGUID().ToString().c_str(), item->GetEntry(), bagGuid, slot, uint32(err));
item->DeleteFromInventoryDB(trans);
problematicItems.push_back(item);
}
@@ -18949,17 +18964,17 @@ void Player::SaveToDB(bool create /*=false*/)
stmt->setString(index++, GetName());
stmt->setUInt8(index++, getRace());
stmt->setUInt8(index++, getClass());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, 0)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
stmt->setUInt8(index++, getLevel());
stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
stmt->setUInt32(index++, GetMoney());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 1));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 3));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 3));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE));
stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
stmt->setUInt16(index++, (uint16)GetMapId());
stmt->setUInt32(index++, (uint32)GetInstanceId());
@@ -19049,7 +19064,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
stmt->setString(index++, ss.str());
- stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES));
stmt->setUInt32(index, m_grantableLevels);
}
else
@@ -19059,17 +19074,17 @@ void Player::SaveToDB(bool create /*=false*/)
stmt->setString(index++, GetName());
stmt->setUInt8(index++, getRace());
stmt->setUInt8(index++, getClass());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, 0)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
stmt->setUInt8(index++, getLevel());
stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
stmt->setUInt32(index++, GetMoney());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 1));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 3));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 3));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE));
stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
if (!IsBeingTeleported())
@@ -19174,7 +19189,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
stmt->setString(index++, ss.str());
- stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES));
stmt->setUInt32(index++, m_grantableLevels);
stmt->setUInt8(index++, IsInWorld() && !GetSession()->PlayerLogout() ? 1 : 0);
@@ -20894,13 +20909,13 @@ void Player::SetRestBonus(float rest_bonus_new)
// update data for client
if ((GetsRecruitAFriendBonus(true) && (GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0)))
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_RAF_LINKED);
else
{
if (m_rest_bonus > 10)
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RESTED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_RESTED);
else if (m_rest_bonus <= 1)
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NOT_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_NOT_RAF_LINKED);
}
//RestTickUpdate
@@ -21234,7 +21249,7 @@ void Player::InitDisplayIds()
return;
}
- uint8 gender = GetByteValue(PLAYER_BYTES_3, 0);
+ uint8 gender = GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER);
switch (gender)
{
case GENDER_FEMALE:
@@ -21842,7 +21857,7 @@ void Player::SetBattlegroundEntryPoint()
void Player::SetBGTeam(uint32 team)
{
m_bgData.bgTeam = team;
- SetByteValue(PLAYER_BYTES_3, 3, uint8(team == ALLIANCE ? 1 : 0));
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_ARENA_FACTION, uint8(team == ALLIANCE ? 1 : 0));
}
uint32 Player::GetBGTeam() const
@@ -22543,7 +22558,7 @@ void Player::ApplyEquipCooldown(Item* pItem)
continue;
// Don't replace longer cooldowns by equip cooldown if we have any.
- if (GetSpellHistory()->GetRemainingCooldown(sSpellMgr->EnsureSpellInfo(spellData.SpellId)) > 30 * IN_MILLISECONDS)
+ if (GetSpellHistory()->GetRemainingCooldown(sSpellMgr->AssertSpellInfo(spellData.SpellId)) > 30 * IN_MILLISECONDS)
continue;
GetSpellHistory()->AddCooldown(spellData.SpellId, pItem->GetEntry(), std::chrono::seconds(30));
@@ -23237,8 +23252,7 @@ void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/)
return;
ItemPosCountVec off_dest;
- uint8 off_msg = CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false);
- if (off_msg == EQUIP_ERR_OK)
+ if (CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false) == EQUIP_ERR_OK)
{
RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
StoreItem(off_dest, offItem, true);
@@ -24064,10 +24078,10 @@ uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 n
if (level > GT_MAX_LEVEL)
level = GT_MAX_LEVEL; // max level in this dbc
- uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2);
- uint8 haircolor = GetByteValue(PLAYER_BYTES, 3);
- uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0);
- uint8 skincolor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 hairstyle = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID);
+ uint8 haircolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
+ uint8 facialhair = GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE);
+ uint8 skincolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
if ((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair) && (!newSkin || (newSkin->hair_id == skincolor)))
return 0;
@@ -26097,6 +26111,9 @@ bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/)
if (!packetOnly && !Unit::SetCanFly(apply))
return false;
+ if (!apply)
+ SetFallInformation(0, GetPositionZ());
+
WorldPacket data(apply ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
@@ -26193,6 +26210,125 @@ float Player::GetCollisionHeight(bool mounted) const
}
}
+void Player::BeginCinematic()
+{
+ // Sanity check for active camera set
+ if (m_activeCinematicCameraId == 0)
+ return;
+
+ auto itr = sFlyByCameraStore.find(m_activeCinematicCameraId);
+ if (itr != sFlyByCameraStore.end())
+ {
+ // Initialize diff, and set camera
+ m_cinematicDiff = 0;
+ m_cinematicCamera = &itr->second;
+
+ auto camitr = m_cinematicCamera->begin();
+ if (camitr != m_cinematicCamera->end())
+ {
+ Position pos(camitr->locations.x, camitr->locations.y, camitr->locations.z, camitr->locations.w);
+ if (!pos.IsPositionValid())
+ return;
+
+ m_mapRef->LoadGrid(camitr->locations.x, camitr->locations.y);
+ m_CinematicObject = SummonCreature(VISUAL_WAYPOINT, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 120000);
+ if (m_CinematicObject)
+ {
+ m_CinematicObject->setActive(true);
+ SetViewpoint(m_CinematicObject, true);
+ }
+ }
+ }
+}
+
+void Player::EndCinematic()
+{
+ m_cinematicDiff = 0;
+ m_cinematicCamera = nullptr;
+ m_activeCinematicCameraId = 0;
+ if (m_CinematicObject)
+ {
+ if (m_seer && m_seer == m_CinematicObject)
+ SetViewpoint(m_CinematicObject, false);
+ m_CinematicObject->AddObjectToRemoveList();
+ }
+}
+
+void Player::UpdateCinematicLocation(uint32 /*diff*/)
+{
+ Position lastPosition;
+ uint32 lastTimestamp = 0;
+ Position nextPosition;
+ uint32 nextTimestamp = 0;
+
+ if (m_cinematicCamera->size() == 0)
+ return;
+
+ // Obtain direction of travel
+ for (FlyByCamera cam : *m_cinematicCamera)
+ {
+ if (cam.timeStamp > m_cinematicDiff)
+ {
+ nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ nextTimestamp = cam.timeStamp;
+ break;
+ }
+ lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ lastTimestamp = cam.timeStamp;
+ }
+ float angle = lastPosition.GetAngle(&nextPosition);
+ angle -= lastPosition.GetOrientation();
+ if (angle < 0)
+ angle += 2 * float(M_PI);
+
+ // Look for position around 2 second ahead of us.
+ int32 workDiff = m_cinematicDiff;
+
+ // Modify result based on camera direction (Humans for example, have the camera point behind)
+ workDiff += static_cast<int32>(float(CINEMATIC_LOOKAHEAD) * cos(angle));
+
+ // Get an iterator to the last entry in the cameras, to make sure we don't go beyond the end
+ FlyByCameraCollection::const_reverse_iterator endItr = m_cinematicCamera->rbegin();
+ if (endItr != m_cinematicCamera->rend() && workDiff > static_cast<int32>(endItr->timeStamp))
+ workDiff = endItr->timeStamp;
+
+ // Never try to go back in time before the start of cinematic!
+ if (workDiff < 0)
+ workDiff = m_cinematicDiff;
+
+ // Obtain the previous and next waypoint based on timestamp
+ for (FlyByCamera cam : *m_cinematicCamera)
+ {
+ if (static_cast<int32>(cam.timeStamp) >= workDiff)
+ {
+ nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ nextTimestamp = cam.timeStamp;
+ break;
+ }
+ lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ lastTimestamp = cam.timeStamp;
+ }
+
+ // Never try to go beyond the end of the cinematic
+ if (workDiff > static_cast<int32>(nextTimestamp))
+ workDiff = static_cast<int32>(nextTimestamp);
+
+ // Interpolate the position for this moment in time (or the adjusted moment in time)
+ uint32 timeDiff = nextTimestamp - lastTimestamp;
+ uint32 interDiff = workDiff - lastTimestamp;
+ float xDiff = nextPosition.m_positionX - lastPosition.m_positionX;
+ float yDiff = nextPosition.m_positionY - lastPosition.m_positionY;
+ float zDiff = nextPosition.m_positionZ - lastPosition.m_positionZ;
+ Position interPosition(lastPosition.m_positionX + (xDiff * (float(interDiff)/float(timeDiff))), lastPosition.m_positionY +
+ (yDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionZ + (zDiff * (float(interDiff) / float(timeDiff))));
+
+ // Advance (at speed) to this position. The remote sight object is used
+ // to send update information to player in cinematic
+ if (m_CinematicObject && interPosition.IsPositionValid())
+ m_CinematicObject->MonsterMoveWithSpeed(interPosition.m_positionX, interPosition.m_positionY, interPosition.m_positionZ, 200.0f, false, true);
+}
+
+
std::string Player::GetMapAreaAndZoneString() const
{
uint32 areaId = GetAreaId();
@@ -26288,7 +26424,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
case SUMMON_PET:
// this enables pet details window (Shift+P)
pet->GetCharmInfo()->SetPetNumber(pet_number, true);
- pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 2048);
+ pet->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_MAGE);
pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
pet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
pet->SetFullHealth();
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 91a4a403fab..fd0fac69674 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -315,7 +315,7 @@ enum RuneCooldowns
RUNE_MISS_COOLDOWN = 1500 // cooldown applied on runes when the spell misses
};
-enum RuneType
+enum RuneType : uint8
{
RUNE_BLOOD = 0,
RUNE_UNHOLY = 1,
@@ -415,50 +415,48 @@ enum PlayerFlags
PLAYER_FLAGS_UNK31 = 0x80000000
};
-// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1)
-// can't use enum for uint64 values
-#define PLAYER_TITLE_DISABLED UI64LIT(0x0000000000000000)
-#define PLAYER_TITLE_NONE UI64LIT(0x0000000000000001)
-#define PLAYER_TITLE_PRIVATE UI64LIT(0x0000000000000002) // 1
-#define PLAYER_TITLE_CORPORAL UI64LIT(0x0000000000000004) // 2
-#define PLAYER_TITLE_SERGEANT_A UI64LIT(0x0000000000000008) // 3
-#define PLAYER_TITLE_MASTER_SERGEANT UI64LIT(0x0000000000000010) // 4
-#define PLAYER_TITLE_SERGEANT_MAJOR UI64LIT(0x0000000000000020) // 5
-#define PLAYER_TITLE_KNIGHT UI64LIT(0x0000000000000040) // 6
-#define PLAYER_TITLE_KNIGHT_LIEUTENANT UI64LIT(0x0000000000000080) // 7
-#define PLAYER_TITLE_KNIGHT_CAPTAIN UI64LIT(0x0000000000000100) // 8
-#define PLAYER_TITLE_KNIGHT_CHAMPION UI64LIT(0x0000000000000200) // 9
-#define PLAYER_TITLE_LIEUTENANT_COMMANDER UI64LIT(0x0000000000000400) // 10
-#define PLAYER_TITLE_COMMANDER UI64LIT(0x0000000000000800) // 11
-#define PLAYER_TITLE_MARSHAL UI64LIT(0x0000000000001000) // 12
-#define PLAYER_TITLE_FIELD_MARSHAL UI64LIT(0x0000000000002000) // 13
-#define PLAYER_TITLE_GRAND_MARSHAL UI64LIT(0x0000000000004000) // 14
-#define PLAYER_TITLE_SCOUT UI64LIT(0x0000000000008000) // 15
-#define PLAYER_TITLE_GRUNT UI64LIT(0x0000000000010000) // 16
-#define PLAYER_TITLE_SERGEANT_H UI64LIT(0x0000000000020000) // 17
-#define PLAYER_TITLE_SENIOR_SERGEANT UI64LIT(0x0000000000040000) // 18
-#define PLAYER_TITLE_FIRST_SERGEANT UI64LIT(0x0000000000080000) // 19
-#define PLAYER_TITLE_STONE_GUARD UI64LIT(0x0000000000100000) // 20
-#define PLAYER_TITLE_BLOOD_GUARD UI64LIT(0x0000000000200000) // 21
-#define PLAYER_TITLE_LEGIONNAIRE UI64LIT(0x0000000000400000) // 22
-#define PLAYER_TITLE_CENTURION UI64LIT(0x0000000000800000) // 23
-#define PLAYER_TITLE_CHAMPION UI64LIT(0x0000000001000000) // 24
-#define PLAYER_TITLE_LIEUTENANT_GENERAL UI64LIT(0x0000000002000000) // 25
-#define PLAYER_TITLE_GENERAL UI64LIT(0x0000000004000000) // 26
-#define PLAYER_TITLE_WARLORD UI64LIT(0x0000000008000000) // 27
-#define PLAYER_TITLE_HIGH_WARLORD UI64LIT(0x0000000010000000) // 28
-#define PLAYER_TITLE_GLADIATOR UI64LIT(0x0000000020000000) // 29
-#define PLAYER_TITLE_DUELIST UI64LIT(0x0000000040000000) // 30
-#define PLAYER_TITLE_RIVAL UI64LIT(0x0000000080000000) // 31
-#define PLAYER_TITLE_CHALLENGER UI64LIT(0x0000000100000000) // 32
-#define PLAYER_TITLE_SCARAB_LORD UI64LIT(0x0000000200000000) // 33
-#define PLAYER_TITLE_CONQUEROR UI64LIT(0x0000000400000000) // 34
-#define PLAYER_TITLE_JUSTICAR UI64LIT(0x0000000800000000) // 35
-#define PLAYER_TITLE_CHAMPION_OF_THE_NAARU UI64LIT(0x0000001000000000) // 36
-#define PLAYER_TITLE_MERCILESS_GLADIATOR UI64LIT(0x0000002000000000) // 37
-#define PLAYER_TITLE_OF_THE_SHATTERED_SUN UI64LIT(0x0000004000000000) // 38
-#define PLAYER_TITLE_HAND_OF_ADAL UI64LIT(0x0000008000000000) // 39
-#define PLAYER_TITLE_VENGEFUL_GLADIATOR UI64LIT(0x0000010000000000) // 40
+enum PlayerBytesOffsets
+{
+ PLAYER_BYTES_OFFSET_SKIN_ID = 0,
+ PLAYER_BYTES_OFFSET_FACE_ID = 1,
+ PLAYER_BYTES_OFFSET_HAIR_STYLE_ID = 2,
+ PLAYER_BYTES_OFFSET_HAIR_COLOR_ID = 3
+};
+
+enum PlayerBytes2Offsets
+{
+ PLAYER_BYTES_2_OFFSET_FACIAL_STYLE = 0,
+ PLAYER_BYTES_2_OFFSET_PARTY_TYPE = 1,
+ PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS = 2,
+ PLAYER_BYTES_2_OFFSET_REST_STATE = 3
+};
+
+enum PlayerBytes3Offsets
+{
+ PLAYER_BYTES_3_OFFSET_GENDER = 0,
+ PLAYER_BYTES_3_OFFSET_INEBRIATION = 1,
+ PLAYER_BYTES_3_OFFSET_PVP_TITLE = 2,
+ PLAYER_BYTES_3_OFFSET_ARENA_FACTION = 3
+};
+
+enum PlayerFieldBytesOffsets
+{
+ PLAYER_FIELD_BYTES_OFFSET_FLAGS = 0,
+ PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL = 1,
+ PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES = 2,
+ PLAYER_FIELD_BYTES_OFFSET_LIFETIME_MAX_PVP_RANK = 3
+};
+
+enum PlayerFieldBytes2Offsets
+{
+ PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID = 0, // uint16!
+ PLAYER_FIELD_BYTES_2_OFFSET_IGNORE_POWER_REGEN_PREDICTION_MASK = 2,
+ PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION = 3
+};
+
+static_assert((PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID & 1) == 0, "PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID must be aligned to 2 byte boundary");
+
+#define PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET (PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID / 2)
#define KNOWN_TITLES_SIZE 3
#define MAX_TITLE_INDEX (KNOWN_TITLES_SIZE*64) // 3 uint64 fields
@@ -584,7 +582,7 @@ enum PlayerSlots
#define INVENTORY_SLOT_BAG_0 255
-enum EquipmentSlots // 19 slots
+enum EquipmentSlots : uint8 // 19 slots
{
EQUIPMENT_SLOT_START = 0,
EQUIPMENT_SLOT_HEAD = 0,
@@ -609,13 +607,13 @@ enum EquipmentSlots // 19 slots
EQUIPMENT_SLOT_END = 19
};
-enum InventorySlots // 4 slots
+enum InventorySlots : uint8 // 4 slots
{
INVENTORY_SLOT_BAG_START = 19,
INVENTORY_SLOT_BAG_END = 23
};
-enum InventoryPackSlots // 16 slots
+enum InventoryPackSlots : uint8 // 16 slots
{
INVENTORY_SLOT_ITEM_START = 23,
INVENTORY_SLOT_ITEM_END = 39
@@ -640,7 +638,7 @@ enum BuyBackSlots // 12 slots
BUYBACK_SLOT_END = 86
};
-enum KeyRingSlots // 32 slots
+enum KeyRingSlots : uint8 // 32 slots
{
KEYRING_SLOT_START = 86,
KEYRING_SLOT_END = 118
@@ -750,7 +748,7 @@ enum TeleportToOptions
};
/// Type of environmental damages
-enum EnviromentalDamage
+enum EnviromentalDamage : uint8
{
DAMAGE_EXHAUSTED = 0,
DAMAGE_DROWNING = 1,
@@ -902,7 +900,7 @@ enum ReferAFriendError
ERR_REFER_A_FRIEND_SUMMON_OFFLINE_S = 0x0D
};
-enum PlayerRestState
+enum PlayerRestState : uint8
{
REST_STATE_RESTED = 0x01,
REST_STATE_NOT_RAF_LINKED = 0x02,
@@ -1185,8 +1183,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
static bool IsBankPos(uint8 bag, uint8 slot);
bool IsValidPos(uint16 pos, bool explicit_pos) const { return IsValidPos(pos >> 8, pos & 255, explicit_pos); }
bool IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const;
- uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, 2); }
- void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, 2, count); }
+ uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS); }
+ void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS, count); }
bool HasItemCount(uint32 item, uint32 count = 1, bool inBankAlso = false) const;
bool HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item const* ignoreItem = nullptr) const;
bool CanNoReagentCast(SpellInfo const* spellInfo) const;
@@ -1911,7 +1909,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
//End of PvP System
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
- uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, 1); }
+ uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION); }
static DrunkenState GetDrunkenstateByValue(uint8 value);
uint32 GetDeathTimer() const { return m_deathTimer; }
@@ -2131,7 +2129,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void ResummonPetTemporaryUnSummonedIfAny();
bool IsPetNeedBeTemporaryUnsummoned() const;
- void SendCinematicStart(uint32 CinematicSequenceId) const;
+ void SendCinematicStart(uint32 CinematicSequenceId);
void SendMovieStart(uint32 MovieId) const;
/*********************************************************/
@@ -2267,6 +2265,17 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
std::string GetMapAreaAndZoneString() const;
std::string GetCoordsMapAreaAndZoneString() const;
+ // Cinematic camera data and remote sight functions
+ uint32 GetActiveCinematicCamera() const { return m_activeCinematicCameraId; }
+ void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { m_activeCinematicCameraId = cinematicCameraId; }
+ bool IsOnCinematic() const { return (m_cinematicCamera != nullptr); }
+ void BeginCinematic();
+ void EndCinematic();
+ void UpdateCinematicLocation(uint32 diff);
+
+ std::string GetMapAreaAndZoneString();
+ std::string GetCoordsMapAreaAndZoneString();
+
protected:
// Gamemaster whisper whitelist
GuidList WhisperList;
@@ -2592,6 +2601,14 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
uint32 manaBeforeDuel;
WorldLocation _corpseLocation;
+
+ // Remote location information
+ uint32 m_cinematicDiff;
+ uint32 m_lastCinematicCheck;
+ uint32 m_activeCinematicCameraId;
+ FlyByCameraCollection* m_cinematicCamera;
+ Position m_remoteSightPosition;
+ Creature* m_CinematicObject;
};
TC_GAME_API void AddItemsSetItem(Player* player, Item* item);
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index a8e13a9f7db..82792a49c96 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -1152,7 +1152,7 @@ bool Guardian::UpdateStats(Stats stat)
if (itr != ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
AddPct(mod, spellInfo->Effects[EFFECT_0].CalcValue());
}
}
@@ -1321,7 +1321,7 @@ void Guardian::UpdateAttackPowerAndDamage(bool ranged)
if (itr != ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* sProto = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* sProto = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
mod += CalculatePct(1.0f, sProto->Effects[1].CalcValue());
}
}
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 912f0a75ab3..41f6f02b32b 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -8715,7 +8715,7 @@ void Unit::setPowerType(Powers new_powertype)
if (getPowerType() == new_powertype)
return;
- SetByteValue(UNIT_FIELD_BYTES_0, 3, new_powertype);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, new_powertype);
if (GetTypeId() == TYPEID_PLAYER)
{
@@ -14721,7 +14721,7 @@ void Unit::SendMovementFlagUpdate(bool self /* = false */)
bool Unit::IsSitState() const
{
- uint8 s = getStandState();
+ uint8 s = GetStandState();
return
s == UNIT_STAND_STATE_SIT_CHAIR || s == UNIT_STAND_STATE_SIT_LOW_CHAIR ||
s == UNIT_STAND_STATE_SIT_MEDIUM_CHAIR || s == UNIT_STAND_STATE_SIT_HIGH_CHAIR ||
@@ -14730,7 +14730,7 @@ bool Unit::IsSitState() const
bool Unit::IsStandState() const
{
- uint8 s = getStandState();
+ uint8 s = GetStandState();
return !IsSitState() && s != UNIT_STAND_STATE_SLEEP && s != UNIT_STAND_STATE_KNEEL;
}
@@ -14767,7 +14767,7 @@ void Unit::SetDisplayId(uint32 modelId)
SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId);
// Set Gender by modelId
if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId))
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
void Unit::RestoreDisplayId()
@@ -16078,7 +16078,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
if (cinfo && cinfo->type == CREATURE_TYPE_DEMON)
{
// to prevent client crash
- SetByteValue(UNIT_FIELD_BYTES_0, 1, (uint8)CLASS_MAGE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, (uint8)CLASS_MAGE);
// just to enable stat window
if (GetCharmInfo())
@@ -16180,7 +16180,7 @@ void Unit::RemoveCharmedBy(Unit* charmer)
CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate();
if (cinfo && cinfo->type == CREATURE_TYPE_DEMON)
{
- SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(cinfo->unit_class));
if (GetCharmInfo())
GetCharmInfo()->SetPetNumber(0, true);
else
@@ -16698,7 +16698,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Hair color
if (getRace() == RACE_NIGHTELF)
{
- uint8 hairColor = GetByteValue(PLAYER_BYTES, 3);
+ uint8 hairColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
switch (hairColor)
{
case 7: // Violet
@@ -16719,7 +16719,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Skin color
else if (getRace() == RACE_TAUREN)
{
- uint8 skinColor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 skinColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
// Male
if (getGender() == GENDER_MALE)
{
@@ -16778,7 +16778,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Hair color
if (getRace() == RACE_NIGHTELF)
{
- uint8 hairColor = GetByteValue(PLAYER_BYTES, 3);
+ uint8 hairColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
switch (hairColor)
{
case 0: // Green
@@ -16798,7 +16798,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Skin color
else if (getRace() == RACE_TAUREN)
{
- uint8 skinColor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 skinColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
// Male
if (getGender() == GENDER_MALE)
{
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 4841cac84cf..9255ce039f3 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -190,6 +190,30 @@ enum UnitStandFlags
UNIT_STAND_FLAGS_ALL = 0xFF
};
+enum UnitBytes0Offsets
+{
+ UNIT_BYTES_0_OFFSET_RACE = 0,
+ UNIT_BYTES_0_OFFSET_CLASS = 1,
+ UNIT_BYTES_0_OFFSET_GENDER = 2,
+ UNIT_BYTES_0_OFFSET_POWER_TYPE = 3,
+};
+
+enum UnitBytes1Offsets
+{
+ UNIT_BYTES_1_OFFSET_STAND_STATE = 0,
+ UNIT_BYTES_1_OFFSET_PET_TALENTS = 1,
+ UNIT_BYTES_1_OFFSET_VIS_FLAG = 2,
+ UNIT_BYTES_1_OFFSET_ANIM_TIER = 3
+};
+
+enum UnitBytes2Offsets
+{
+ UNIT_BYTES_2_OFFSET_SHEATH_STATE = 0,
+ UNIT_BYTES_2_OFFSET_PVP_FLAG = 1,
+ UNIT_BYTES_2_OFFSET_PET_FLAG = 2,
+ UNIT_BYTES_2_OFFSET_SHAPESHIFT = 3,
+};
+
// byte flags value (UNIT_FIELD_BYTES_1, 3)
enum UnitBytes1_Flags
{
@@ -536,7 +560,7 @@ enum UnitMoveType
TC_GAME_API extern float baseMoveSpeed[MAX_MOVE_TYPE];
TC_GAME_API extern float playerBaseMoveSpeed[MAX_MOVE_TYPE];
-enum WeaponAttackType
+enum WeaponAttackType : uint8
{
BASE_ATTACK = 0,
OFF_ATTACK = 1,
@@ -1043,7 +1067,7 @@ enum ReactStates
REACT_AGGRESSIVE = 2
};
-enum CommandStates
+enum CommandStates : uint8
{
COMMAND_STAY = 0,
COMMAND_FOLLOW = 1,
@@ -1314,11 +1338,11 @@ class TC_GAME_API Unit : public WorldObject
uint8 getLevel() const { return uint8(GetUInt32Value(UNIT_FIELD_LEVEL)); }
uint8 getLevelForTarget(WorldObject const* /*target*/) const override { return getLevel(); }
void SetLevel(uint8 lvl);
- uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, 0); }
+ uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE); }
uint32 getRaceMask() const { return 1 << (getRace()-1); }
- uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); }
+ uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS); }
uint32 getClassMask() const { return 1 << (getClass()-1); }
- uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); }
+ uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER); }
float GetStat(Stats stat) const { return float(GetUInt32Value(UNIT_FIELD_STAT0+stat)); }
void SetStat(Stats stat, int32 val) { SetStatInt32Value(UNIT_FIELD_STAT0+stat, val); }
@@ -1347,7 +1371,7 @@ class TC_GAME_API Unit : public WorldObject
int32 ModifyHealth(int32 val);
int32 GetHealthGain(int32 dVal);
- Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, 3)); }
+ Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE)); }
void setPowerType(Powers power);
uint32 GetPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_POWER1 +power); }
uint32 GetMaxPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_MAXPOWER1+power); }
@@ -1388,7 +1412,7 @@ class TC_GAME_API Unit : public WorldObject
uint32 GetCreatureType() const;
uint32 GetCreatureTypeMask() const;
- uint8 getStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
+ uint8 GetStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
bool IsSitState() const;
bool IsStandState() const;
void SetStandState(uint8 state);
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index fc7d668ae0b..3efeb1ca273 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -4715,7 +4715,7 @@ void ObjectMgr::LoadScripts(ScriptsType type)
if (tableName.empty())
return;
- if (sScriptMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
+ if (sMapMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
return;
TC_LOG_INFO("server.loading", "Loading %s...", tableName.c_str());
@@ -5162,7 +5162,7 @@ void ObjectMgr::LoadSpellScriptNames()
while (spellInfo)
{
- _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
+ _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
spellInfo = spellInfo->GetNextRankSpell();
}
}
@@ -5171,7 +5171,7 @@ void ObjectMgr::LoadSpellScriptNames()
if (spellInfo->IsRanked())
TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName.c_str(), spellId);
- _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
+ _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
}
++count;
@@ -5193,45 +5193,59 @@ void ObjectMgr::ValidateSpellScripts()
uint32 count = 0;
- for (SpellScriptsContainer::iterator itr = _spellScriptsStore.begin(); itr != _spellScriptsStore.end();)
+ for (auto spell : _spellScriptsStore)
{
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(itr->first);
- std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> > SpellScriptLoaders;
- sScriptMgr->CreateSpellScriptLoaders(itr->first, SpellScriptLoaders);
- itr = _spellScriptsStore.upper_bound(itr->first);
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell.first);
- for (std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> >::iterator sitr = SpellScriptLoaders.begin(); sitr != SpellScriptLoaders.end(); ++sitr)
+ auto const bounds = sObjectMgr->GetSpellScriptsBounds(spell.first);
+
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
{
- SpellScript* spellScript = sitr->first->GetSpellScript();
- AuraScript* auraScript = sitr->first->GetAuraScript();
- bool valid = true;
- if (!spellScript && !auraScript)
- {
- TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second).c_str());
- valid = false;
- }
- if (spellScript)
- {
- spellScript->_Init(&sitr->first->GetName(), spellEntry->Id);
- spellScript->_Register();
- if (!spellScript->_Validate(spellEntry))
- valid = false;
- delete spellScript;
- }
- if (auraScript)
+ if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(itr->second.first))
{
- auraScript->_Init(&sitr->first->GetName(), spellEntry->Id);
- auraScript->_Register();
- if (!auraScript->_Validate(spellEntry))
- valid = false;
- delete auraScript;
- }
- if (!valid)
- {
- _spellScriptsStore.erase(sitr->second);
+ ++count;
+
+ std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
+ std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
+
+ if (!spellScript && !auraScript)
+ {
+ TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(itr->second.first).c_str());
+
+ itr->second.second = false;
+ continue;
+ }
+
+ if (spellScript)
+ {
+ spellScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
+ spellScript->_Register();
+
+ if (!spellScript->_Validate(spellEntry))
+ {
+ itr->second.second = false;
+ continue;
+ }
+ }
+
+ if (auraScript)
+ {
+ auraScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
+ auraScript->_Register();
+
+ if (!auraScript->_Validate(spellEntry))
+ {
+ itr->second.second = false;
+ continue;
+ }
+ }
+
+ // Enable the script when all checks passed
+ itr->second.second = true;
}
+ else
+ itr->second.second = false;
}
- ++count;
}
TC_LOG_INFO("server.loading", ">> Validated %u scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
@@ -8571,6 +8585,8 @@ void ObjectMgr::LoadScriptNames()
{
uint32 oldMSTime = getMSTime();
+ // We insert an empty placeholder here so we can use the
+ // script id 0 as dummy for "no script found".
_scriptNamesStore.emplace_back("");
QueryResult result = WorldDatabase.Query(
@@ -8612,18 +8628,18 @@ void ObjectMgr::LoadScriptNames()
std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
-#ifdef SCRIPTS
- for (size_t i = 1; i < _scriptNamesStore.size(); ++i)
- UnusedScriptNames.push_back(_scriptNamesStore[i]);
-#endif
-
TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " ScriptNames in %u ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime));
}
+ObjectMgr::ScriptNameContainer const& ObjectMgr::GetAllScriptNames() const
+{
+ return _scriptNamesStore;
+}
+
std::string const& ObjectMgr::GetScriptName(uint32 id) const
{
static std::string const empty = "";
- return id < _scriptNamesStore.size() ? _scriptNamesStore[id] : empty;
+ return (id < _scriptNamesStore.size()) ? _scriptNamesStore[id] : empty;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 73c03849699..576a8d2ccac 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -370,7 +370,7 @@ struct ScriptInfo
typedef std::multimap<uint32, ScriptInfo> ScriptMap;
typedef std::map<uint32, ScriptMap > ScriptMapMap;
-typedef std::multimap<uint32, uint32> SpellScriptsContainer;
+typedef std::multimap<uint32 /*spell id*/, std::pair<uint32 /*script id*/, bool /*enabled*/>> SpellScriptsContainer;
typedef std::pair<SpellScriptsContainer::iterator, SpellScriptsContainer::iterator> SpellScriptsBounds;
TC_GAME_API extern ScriptMapMap sSpellScripts;
TC_GAME_API extern ScriptMapMap sEventScripts;
@@ -1272,6 +1272,7 @@ class TC_GAME_API ObjectMgr
bool IsVendorItemValid(uint32 vendor_entry, uint32 item, int32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* player = NULL, std::set<uint32>* skip_vendors = NULL, uint32 ORnpcflag = 0) const;
void LoadScriptNames();
+ ScriptNameContainer const& GetAllScriptNames() const;
std::string const& GetScriptName(uint32 id) const;
uint32 GetScriptId(std::string const& name);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 2b4b77dc7a9..19638ec1bf8 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -642,7 +642,7 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte
TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), GetRemoteAddress().c_str(), createInfo->Name.c_str(), newChar.GetGUID().GetCounter());
sScriptMgr->OnPlayerCreate(&newChar);
- sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetByteValue(PLAYER_BYTES_3, 0), newChar.getRace(), newChar.getClass(), newChar.getLevel());
+ sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER), newChar.getRace(), newChar.getClass(), newChar.getLevel());
newChar.CleanupsBeforeDelete();
delete createInfo;
@@ -1242,20 +1242,25 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair);
- if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->GetByteValue(PLAYER_BYTES_3, 0))
+ if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER))
return;
BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair);
- if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->GetByteValue(PLAYER_BYTES_3, 0))
+ if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER))
return;
BarberShopStyleEntry const* bs_skinColor = sBarberShopStyleStore.LookupEntry(SkinColor);
- if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->GetByteValue(PLAYER_BYTES_3, 0)))
+ if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)))
return;
- if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->GetByteValue(PLAYER_BYTES_3, 0), bs_hair->hair_id, Color, _player->GetByteValue(PLAYER_BYTES, 1), bs_facialHair->hair_id, bs_skinColor ? bs_skinColor->hair_id : _player->GetByteValue(PLAYER_BYTES, 0)))
+ if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER),
+ bs_hair->hair_id,
+ Color,
+ _player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID),
+ bs_facialHair->hair_id,
+ bs_skinColor ? bs_skinColor->hair_id : _player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID)))
return;
GameObject* go = _player->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_BARBER_CHAIR, 5.0f);
@@ -1265,7 +1270,7 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
return;
}
- if (_player->getStandState() != UNIT_STAND_STATE_SIT_LOW_CHAIR + go->GetGOInfo()->barberChair.chairheight)
+ if (_player->GetStandState() != UNIT_STAND_STATE_SIT_LOW_CHAIR + go->GetGOInfo()->barberChair.chairheight)
{
SendBarberShopResult(BARBER_SHOP_RESULT_NOT_ON_CHAIR);
return;
@@ -1287,11 +1292,11 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
_player->ModifyMoney(-int32(cost)); // it isn't free
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER, cost);
- _player->SetByteValue(PLAYER_BYTES, 2, uint8(bs_hair->hair_id));
- _player->SetByteValue(PLAYER_BYTES, 3, uint8(Color));
- _player->SetByteValue(PLAYER_BYTES_2, 0, uint8(bs_facialHair->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, uint8(bs_hair->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, uint8(Color));
+ _player->SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, uint8(bs_facialHair->hair_id));
if (bs_skinColor)
- _player->SetByteValue(PLAYER_BYTES, 0, uint8(bs_skinColor->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, uint8(bs_skinColor->hair_id));
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1);
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 91df877e936..467d3730ab2 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -284,7 +284,7 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData)
continue;
uint32 pzoneid = target->GetZoneId();
- uint8 gender = target->GetByteValue(PLAYER_BYTES_3, 0);
+ uint8 gender = target->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER);
bool z_show = true;
for (uint32 i = 0; i < zones_count; ++i)
@@ -405,7 +405,7 @@ void WorldSession::HandleLogoutRequestOpcode(WorldPacket& /*recvData*/)
// not set flags if player can't free move to prevent lost state at logout cancel
if (GetPlayer()->CanFreeMove())
{
- if (GetPlayer()->getStandState() == UNIT_STAND_STATE_STAND)
+ if (GetPlayer()->GetStandState() == UNIT_STAND_STATE_STAND)
GetPlayer()->SetStandState(UNIT_STAND_STATE_SIT);
WorldPacket data(SMSG_FORCE_MOVE_ROOT, (8+4)); // guess size
@@ -1056,12 +1056,14 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recvData)
void WorldSession::HandleCompleteCinematic(WorldPacket& /*recvData*/)
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_COMPLETE_CINEMATIC");
+ // If player has sight bound to visual waypoint NPC we should remove it
+ GetPlayer()->EndCinematic();
}
void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recvData*/)
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_NEXT_CINEMATIC_CAMERA");
+ // Sent by client when cinematic actually begun. So we begin the server side process
+ GetPlayer()->BeginCinematic();
}
void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData)
@@ -1157,7 +1159,7 @@ void WorldSession::HandleSetActionBarToggles(WorldPacket& recvData)
return;
}
- GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, actionBar);
+ GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, actionBar);
}
void WorldSession::HandlePlayedTime(WorldPacket& recvData)
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 6be1fd30ae3..e07e10ab00c 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -594,11 +594,11 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
if (creator->GetTypeId() == TYPEID_PLAYER)
{
Player* player = creator->ToPlayer();
- data << uint8(player->GetByteValue(PLAYER_BYTES, 0)); // skin
- data << uint8(player->GetByteValue(PLAYER_BYTES, 1)); // face
- data << uint8(player->GetByteValue(PLAYER_BYTES, 2)); // hair
- data << uint8(player->GetByteValue(PLAYER_BYTES, 3)); // haircolor
- data << uint8(player->GetByteValue(PLAYER_BYTES_2, 0)); // facialhair
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
data << uint32(player->GetGuildId()); // unk
static EquipmentSlots const itemSlots[] =
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index 1575b50098f..0887d183a8b 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -29,6 +29,8 @@
#include "Pet.h"
#include "WorldSession.h"
#include "Opcodes.h"
+#include "ScriptReloadMgr.h"
+#include "ScriptMgr.h"
BossBoundaryData::~BossBoundaryData()
{
@@ -36,6 +38,18 @@ BossBoundaryData::~BossBoundaryData()
delete it->Boundary;
}
+InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0)
+{
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId;
+ auto const scriptname = sObjectMgr->GetScriptName(scriptId);
+ ASSERT(!scriptname.empty());
+ // Acquire a strong reference from the script module
+ // to keep it loaded until this object is destroyed.
+ module_reference = sScriptMgr->AcquireModuleReferenceOfScriptName(scriptname);
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
+}
+
void InstanceScript::SaveToDB()
{
std::string data = GetSaveData();
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index 949cdf5abcb..3814afe2f4b 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -36,6 +36,7 @@ class Unit;
class Player;
class GameObject;
class Creature;
+class ModuleReference;
enum EncounterFrameType
{
@@ -137,7 +138,7 @@ typedef std::map<uint32 /*entry*/, uint32 /*type*/> ObjectInfoMap;
class TC_GAME_API InstanceScript : public ZoneScript
{
public:
- explicit InstanceScript(Map* map) : instance(map), completedEncounters(0) { }
+ explicit InstanceScript(Map* map);
virtual ~InstanceScript() { }
@@ -289,6 +290,11 @@ class TC_GAME_API InstanceScript : public ZoneScript
ObjectInfoMap _gameObjectInfo;
ObjectGuidMap _objectGuids;
uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
+
+ #ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Strong reference to the associated script module
+ std::shared_ptr<ModuleReference> module_reference;
+ #endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
};
template<class AI, class T>
diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h
index 41feb7b26e2..0afb327d7f0 100644
--- a/src/server/game/Loot/LootMgr.h
+++ b/src/server/game/Loot/LootMgr.h
@@ -54,7 +54,7 @@ enum RollMask
#define MAX_NR_QUEST_ITEMS 32
// unrelated to the number of quest items shown, just for reserve
-enum LootMethod
+enum LootMethod : uint8
{
FREE_FOR_ALL = 0,
ROUND_ROBIN = 1,
@@ -74,7 +74,7 @@ enum PermissionTypes
NONE_PERMISSION = 6
};
-enum LootType
+enum LootType : uint8
{
LOOT_NONE = 0,
diff --git a/src/server/game/Maps/AreaBoundary.h b/src/server/game/Maps/AreaBoundary.h
index a8780ddb60f..0973d1a6e86 100644
--- a/src/server/game/Maps/AreaBoundary.h
+++ b/src/server/game/Maps/AreaBoundary.h
@@ -40,7 +40,7 @@ class TC_GAME_API AreaBoundary
{
double d_positionX, d_positionY, d_positionZ;
DoublePosition(double x = 0.0, double y = 0.0, double z = 0.0, float o = 0.0f)
- : Position(x, y, z, o), d_positionX(x), d_positionY(y), d_positionZ(z) { }
+ : Position(float(x), float(y), float(z), o), d_positionX(x), d_positionY(y), d_positionZ(z) { }
DoublePosition(float x, float y = 0.0f, float z = 0.0f, float o = 0.0f)
: Position(x, y, z, o), d_positionX(x), d_positionY(y), d_positionZ(z) { }
DoublePosition(const Position& pos)
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 8640150d86b..664ed3dc8ec 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -17,6 +17,7 @@
*/
#include "Map.h"
+#include "MapManager.h"
#include "Battleground.h"
#include "MMapFactory.h"
#include "CellImpl.h"
@@ -64,7 +65,7 @@ Map::~Map()
}
if (!m_scriptSchedule.empty())
- sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
+ sMapMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
}
@@ -713,6 +714,15 @@ void Map::Update(const uint32 t_diff)
VisitNearbyCellsOf(player, grid_object_update, world_object_update);
+ // If player is using far sight, visit that object too
+ if (WorldObject* viewPoint = player->GetViewpoint())
+ {
+ if (Creature* viewCreature = viewPoint->ToCreature())
+ VisitNearbyCellsOf(viewCreature, grid_object_update, world_object_update);
+ else if (DynamicObject* viewObject = viewPoint->ToDynObject())
+ VisitNearbyCellsOf(viewObject, grid_object_update, world_object_update);
+ }
+
// Handle updates for creatures in combat with player and are more than 60 yards away
if (player->IsInCombat())
{
@@ -2657,8 +2667,8 @@ void Map::UpdateObjectsVisibilityFor(Player* player, Cell cell, CellCoord cellpa
cell.SetNoCreate();
TypeContainerVisitor<Trinity::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
TypeContainerVisitor<Trinity::VisibleNotifier, GridTypeMapContainer > grid_notifier(notifier);
- cell.Visit(cellpair, world_notifier, *this, *player, player->GetSightRange());
- cell.Visit(cellpair, grid_notifier, *this, *player, player->GetSightRange());
+ cell.Visit(cellpair, world_notifier, *this, *player->m_seer, player->GetSightRange());
+ cell.Visit(cellpair, grid_notifier, *this, *player->m_seer, player->GetSightRange());
// send data
notifier.SendToSelf();
diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp
index c1882b3dc75..79a8b3855e8 100644
--- a/src/server/game/Maps/MapManager.cpp
+++ b/src/server/game/Maps/MapManager.cpp
@@ -38,10 +38,10 @@
#include "AchievementMgr.h"
MapManager::MapManager()
+ : _nextInstanceId(0), _scheduledScripts(0)
{
i_gridCleanUpDelay = sWorld->getIntConfig(CONFIG_INTERVAL_GRIDCLEAN);
i_timer.SetInterval(sWorld->getIntConfig(CONFIG_INTERVAL_MAPUPDATE));
- _nextInstanceId = 0;
}
MapManager::~MapManager() { }
diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h
index e74453a1196..a7fdc37d324 100644
--- a/src/server/game/Maps/MapManager.h
+++ b/src/server/game/Maps/MapManager.h
@@ -126,6 +126,11 @@ class TC_GAME_API MapManager
template<typename Worker>
void DoForAllMapsWithMapId(uint32 mapId, Worker&& worker);
+ uint32 IncreaseScheduledScriptsCount() { return ++_scheduledScripts; }
+ uint32 DecreaseScheduledScriptCount() { return --_scheduledScripts; }
+ uint32 DecreaseScheduledScriptCount(size_t count) { return _scheduledScripts -= count; }
+ bool IsScriptScheduled() const { return _scheduledScripts > 0; }
+
private:
typedef std::unordered_map<uint32, Map*> MapMapType;
typedef std::vector<bool> InstanceIds;
@@ -150,6 +155,9 @@ class TC_GAME_API MapManager
InstanceIds _instanceIds;
uint32 _nextInstanceId;
MapUpdater m_updater;
+
+ // atomic op counter for active scripts amount
+ std::atomic<uint32> _scheduledScripts;
};
template<typename Worker>
diff --git a/src/server/game/Scripting/MapScripts.cpp b/src/server/game/Maps/MapScripts.cpp
index a01c109b9ca..c5d7bdc64ce 100644
--- a/src/server/game/Scripting/MapScripts.cpp
+++ b/src/server/game/Maps/MapScripts.cpp
@@ -21,6 +21,7 @@
#include "GridNotifiersImpl.h"
#include "GossipDef.h"
#include "Map.h"
+#include "MapManager.h"
#include "ObjectMgr.h"
#include "Pet.h"
#include "Item.h"
@@ -58,7 +59,7 @@ void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, O
if (iter->first == 0)
immedScript = true;
- sScriptMgr->IncreaseScheduledScriptsCount();
+ sMapMgr->IncreaseScheduledScriptsCount();
}
///- If one of the effects should be immediate, launch the script execution
if (/*start &&*/ immedScript && !i_scriptLock)
@@ -86,7 +87,7 @@ void Map::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* sou
sa.script = &script;
m_scriptSchedule.insert(ScriptScheduleMap::value_type(time_t(sWorld->GetGameTime() + delay), sa));
- sScriptMgr->IncreaseScheduledScriptsCount();
+ sMapMgr->IncreaseScheduledScriptsCount();
///- If effects should be immediate, launch the script execution
if (delay == 0 && !i_scriptLock)
@@ -878,6 +879,6 @@ void Map::ScriptsProcess()
m_scriptSchedule.erase(iter);
iter = m_scriptSchedule.begin();
- sScriptMgr->DecreaseScheduledScriptCount();
+ sMapMgr->DecreaseScheduledScriptCount();
}
}
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index dcee5ff7071..ca63137ac93 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -887,7 +887,9 @@ enum TrinityStrings
LANG_CHARACTER_DELETED_LIST_LINE_CHAT = 1026,
LANG_SQLDRIVER_QUERY_LOGGING_ENABLED = 1027,
LANG_SQLDRIVER_QUERY_LOGGING_DISABLED = 1028,
- // Room for more level 4 1029-1099 not used
+ LANG_ACCOUNT_INVALID_BNET_NAME = 1029, // 6.x ONLY
+ LANG_ACCOUNT_USE_BNET_COMMANDS = 1030, // 6.x enum value name but different text in DB
+ // Room for more level 4 1031-1099 not used
// Level 3 (continue)
LANG_ACCOUNT_SETADDON = 1100,
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 77d6f739da6..aa45c5024e6 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -23,7 +23,7 @@
#include "DetourNavMesh.h"
#include <cassert>
-enum SpellEffIndex
+enum SpellEffIndex : uint8
{
EFFECT_0 = 0,
EFFECT_1 = 1,
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index 2a57524cb3c..b01ddcb060d 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -302,6 +302,27 @@ void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generate
}
}
+void MotionMaster::MoveCloserAndStop(uint32 id, Unit* target, float distance)
+{
+ float distanceToTravel = _owner->GetExactDist2d(target) - distance;
+ if (distanceToTravel > 0.0f)
+ {
+ float angle = _owner->GetAngle(target);
+ float destx = _owner->GetPositionX() + distanceToTravel * std::cos(angle);
+ float desty = _owner->GetPositionY() + distanceToTravel * std::sin(angle);
+ MovePoint(id, destx, desty, target->GetPositionZ());
+ }
+ else
+ {
+ // we are already close enough. We just need to turn toward the target without changing position.
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZMinusOffset());
+ init.SetFacing(target);
+ init.Launch();
+ Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE);
+ }
+}
+
void MotionMaster::MoveLand(uint32 id, Position const& pos)
{
float x, y, z;
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index d1c6474fb36..7d6d512e88d 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -173,6 +173,12 @@ class TC_GAME_API MotionMaster //: private std::stack<MovementGenerator *>
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ, generatePath); }
void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true);
+ /* Makes the unit move toward the target until it is at a certain distance from it. The unit then stops.
+ Only works in 2D.
+ This method doesn't account for any movement done by the target. in other words, it only works if the target is stationary.
+ */
+ void MoveCloserAndStop(uint32 id, Unit* target, float distance);
+
// These two movement types should only be used with creatures having landing/takeoff animations
void MoveLand(uint32 id, Position const& pos);
void MoveTakeoff(uint32 id, Position const& pos);
diff --git a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
index 7f0695e16b3..aa00d211d6e 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
@@ -33,8 +33,14 @@ void OutdoorPvPMgr::Die()
for (OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
delete *itr;
+ m_OutdoorPvPSet.clear();
+
for (OutdoorPvPDataMap::iterator itr = m_OutdoorPvPDatas.begin(); itr != m_OutdoorPvPDatas.end(); ++itr)
delete itr->second;
+
+ m_OutdoorPvPDatas.clear();
+
+ m_OutdoorPvPMap.clear();
}
OutdoorPvPMgr* OutdoorPvPMgr::instance()
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index 75b52bb4537..5e3bb4889ab 100644
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -60,7 +60,7 @@ enum QuestFailedReason
INVALIDREASON_DAILY_QUEST_COMPLETED_TODAY = 29 // You have completed that daily quest today.
};
-enum QuestShareMessages
+enum QuestShareMessages : uint8
{
QUEST_PARTY_MSG_SHARING_QUEST = 0,
QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1,
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 4218746893f..ca1cd71363e 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -17,6 +17,7 @@
*/
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "Config.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
@@ -34,11 +35,9 @@
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Chat.h"
-
-// namespace
-// {
- UnusedScriptNamesContainer UnusedScriptNames;
-// }
+#include "MapManager.h"
+#include "LFGScripts.h"
+#include "InstanceScript.h"
// Trait which indicates whether this script type
// must be assigned in the database.
@@ -67,6 +66,10 @@ struct is_script_database_bound<GameObjectScript>
: std::true_type { };
template<>
+struct is_script_database_bound<VehicleScript>
+ : std::true_type { };
+
+template<>
struct is_script_database_bound<AreaTriggerScript>
: std::true_type { };
@@ -94,143 +97,846 @@ template<>
struct is_script_database_bound<AchievementCriteriaScript>
: std::true_type { };
+enum Spells
+{
+ SPELL_HOTSWAP_VISUAL_SPELL_EFFECT = 40162 // 59084
+};
+
+class ScriptRegistryInterface
+{
+public:
+ ScriptRegistryInterface() { }
+ virtual ~ScriptRegistryInterface() { }
+
+ ScriptRegistryInterface(ScriptRegistryInterface const&) = delete;
+ ScriptRegistryInterface(ScriptRegistryInterface&&) = delete;
+
+ ScriptRegistryInterface& operator= (ScriptRegistryInterface const&) = delete;
+ ScriptRegistryInterface& operator= (ScriptRegistryInterface&&) = delete;
+
+ /// Removes all scripts associated with the given script context.
+ /// Requires ScriptRegistryBase::SwapContext to be called after all transfers have finished.
+ virtual void ReleaseContext(std::string const& context) = 0;
+
+ /// Injects and updates the changed script objects.
+ virtual void SwapContext(bool initialize) = 0;
+
+ /// Removes the scripts used by this registry from the given container.
+ /// Used to find unused script names.
+ virtual void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) = 0;
+
+ /// Unloads the script registry.
+ virtual void Unload() = 0;
+};
+
+template<class>
+class ScriptRegistry;
+
+class ScriptRegistryCompositum
+ : public ScriptRegistryInterface
+{
+ ScriptRegistryCompositum() { }
+
+ template<class>
+ friend class ScriptRegistry;
+
+ /// Type erasure wrapper for objects
+ class DeleteableObjectBase
+ {
+ public:
+ DeleteableObjectBase() { }
+ virtual ~DeleteableObjectBase() { }
+
+ DeleteableObjectBase(DeleteableObjectBase const&) = delete;
+ DeleteableObjectBase& operator= (DeleteableObjectBase const&) = delete;
+ };
+
+ template<typename T>
+ class DeleteableObject
+ : public DeleteableObjectBase
+ {
+ public:
+ DeleteableObject(T&& object)
+ : _object(std::forward<T>(object)) { }
+
+ private:
+ T _object;
+ };
+
+public:
+ void SetScriptNameInContext(std::string const& scriptname, std::string const& context)
+ {
+ ASSERT(_scriptnames_to_context.find(scriptname) == _scriptnames_to_context.end(),
+ "Scriptname was assigned to this context already!");
+ _scriptnames_to_context.insert(std::make_pair(scriptname, context));
+ }
+
+ std::string const& GetScriptContextOfScriptName(std::string const& scriptname) const
+ {
+ auto itr = _scriptnames_to_context.find(scriptname);
+ ASSERT(itr != _scriptnames_to_context.end() &&
+ "Given scriptname doesn't exist!");
+ return itr->second;
+ }
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ for (auto const registry : _registries)
+ registry->ReleaseContext(context);
+
+ // Clear the script names in context after calling the release hooks
+ // since it's possible that new references to a shared library
+ // are acquired when releasing.
+ for (auto itr = _scriptnames_to_context.begin();
+ itr != _scriptnames_to_context.end();)
+ if (itr->second == context)
+ itr = _scriptnames_to_context.erase(itr);
+ else
+ ++itr;
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ for (auto const registry : _registries)
+ registry->SwapContext(initialize);
+
+ DoDelayedDelete();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const registry : _registries)
+ registry->RemoveUsedScriptsFromContainer(scripts);
+ }
+
+ void Unload() final override
+ {
+ for (auto const registry : _registries)
+ registry->Unload();
+ }
+
+ template<typename T>
+ void QueueForDelayedDelete(T&& any)
+ {
+ _delayed_delete_queue.push_back(
+ Trinity::make_unique<
+ DeleteableObject<typename std::decay<T>::type>
+ >(std::forward<T>(any))
+ );
+ }
+
+ static ScriptRegistryCompositum* Instance()
+ {
+ static ScriptRegistryCompositum instance;
+ return &instance;
+ }
+
+private:
+ void Register(ScriptRegistryInterface* registry)
+ {
+ _registries.insert(registry);
+ }
+
+ void DoDelayedDelete()
+ {
+ _delayed_delete_queue.clear();
+ }
+
+ std::unordered_set<ScriptRegistryInterface*> _registries;
+
+ std::vector<std::unique_ptr<DeleteableObjectBase>> _delayed_delete_queue;
+
+ std::unordered_map<
+ std::string /*script name*/,
+ std::string /*context*/
+ > _scriptnames_to_context;
+};
+
+#define sScriptRegistryCompositum ScriptRegistryCompositum::Instance()
+
+template<typename /*ScriptType*/, bool /*IsDatabaseBound*/>
+class SpecializedScriptRegistry;
+
// This is the global static registry of scripts.
-template<class TScript>
-class ScriptRegistry
+template<class ScriptType>
+class ScriptRegistry final
+ : public SpecializedScriptRegistry<
+ ScriptType, is_script_database_bound<ScriptType>::value>
+{
+ ScriptRegistry()
+ {
+ sScriptRegistryCompositum->Register(this);
+ }
+
+public:
+ static ScriptRegistry* Instance()
+ {
+ static ScriptRegistry instance;
+ return &instance;
+ }
+
+ void LogDuplicatedScriptPointerError(ScriptType const* first, ScriptType const* second)
+ {
+ // See if the script is using the same memory as another script. If this happens, it means that
+ // someone forgot to allocate new memory for a script.
+ TC_LOG_ERROR("scripts", "Script '%s' has same memory pointer as '%s'.",
+ first->GetName().c_str(), second->GetName().c_str());
+ }
+};
+
+class ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHookBase() { }
+ virtual ~ScriptRegistrySwapHookBase() { }
+
+ ScriptRegistrySwapHookBase(ScriptRegistrySwapHookBase const&) = delete;
+ ScriptRegistrySwapHookBase(ScriptRegistrySwapHookBase&&) = delete;
+
+ ScriptRegistrySwapHookBase& operator= (ScriptRegistrySwapHookBase const&) = delete;
+ ScriptRegistrySwapHookBase& operator= (ScriptRegistrySwapHookBase&&) = delete;
+
+ /// Called before the actual context release happens
+ virtual void BeforeReleaseContext(std::string const& /*context*/) { }
+
+ /// Called before SwapContext
+ virtual void BeforeSwapContext(bool /*initialize*/) { }
+
+ /// Called before Unload
+ virtual void BeforeUnload() { }
+};
+
+template<typename ScriptType, typename Base>
+class ScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
+{
+};
+
+/// This hook is responsible for swapping OutdoorPvP's
+template<typename Base>
+class UnsupportedScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ ASSERT(bounds.first == bounds.second);
+ }
+};
+
+/// This hook is responsible for swapping Creature and GameObject AI's
+template<typename ObjectType, typename ScriptType, typename Base>
+class CreatureGameObjectScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
{
+ template<typename W>
+ class AIFunctionMapWorker
+ {
+ public:
+ template<typename T>
+ AIFunctionMapWorker(T&& worker)
+ : _worker(std::forward<T>(worker)) { }
+
+ void Visit(std::unordered_map<ObjectGuid, ObjectType*>& objects)
+ {
+ _worker(objects);
+ }
+
+ template<typename O>
+ void Visit(std::unordered_map<ObjectGuid, O*>&) { }
+
+ private:
+ W _worker;
+ };
+
+ class AsyncCastHotswapEffectEvent : public BasicEvent
+ {
public:
+ explicit AsyncCastHotswapEffectEvent(Unit* owner) : owner_(owner) { }
+
+ bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override
+ {
+ owner_->CastSpell(owner_, SPELL_HOTSWAP_VISUAL_SPELL_EFFECT, true);
+ return true;
+ }
+
+ private:
+ Unit* owner_;
+ };
+
+ // Hook which is called before a creature is swapped
+ static void UnloadStage1(Creature* creature)
+ {
+ if (creature->IsCharmed())
+ creature->RemoveCharmedBy(nullptr);
+
+ ASSERT(!creature->IsCharmed(),
+ "There is a disabled AI which is still loaded.");
- typedef std::map<uint32, TScript*> ScriptMap;
- typedef typename ScriptMap::iterator ScriptMapIterator;
+ creature->AI()->EnterEvadeMode();
+ }
+
+ static void UnloadStage2(Creature* creature)
+ {
+ bool const destroyed = creature->AIM_Destroy();
+ ASSERT(destroyed,
+ "Destroying the AI should never fail here!");
+ (void)destroyed;
- // The actual list of scripts. This will be accessed concurrently, so it must not be modified
- // after server startup.
- static ScriptMap ScriptPointerList;
- static std::vector<TScript*> Scripts;
+ ASSERT(!creature->AI(),
+ "The AI should be null here!");
+ }
- static void AddScript(TScript* const script, bool addToDeleteContainer = true)
+ // Hook which is called before a gameobject is swapped
+ static void UnloadStage1(GameObject* gameobject)
+ {
+ gameobject->AI()->Reset();
+ }
+
+ static void UnloadStage2(GameObject* gameobject)
+ {
+ gameobject->AIM_Destroy();
+
+ ASSERT(!gameobject->AI(),
+ "The AI should be null here!");
+ }
+
+ // Hook which is called after a creature was swapped
+ static void LoadStage1(Creature* creature)
+ {
+ ASSERT(!creature->AI(),
+ "The AI should be null here!");
+
+ if (creature->IsAlive())
+ creature->ClearUnitState(UNIT_STATE_EVADE);
+
+ bool const created = creature->AIM_Initialize();
+ ASSERT(created,
+ "Creating the AI should never fail here!");
+ (void)created;
+ }
+
+ static void LoadStage2(Creature* creature)
+ {
+ if (!creature->IsAlive())
+ return;
+
+ creature->AI()->EnterEvadeMode();
+
+ // Cast a dummy visual spell asynchronously here to signal
+ // that the AI was hot swapped
+ creature->m_Events.AddEvent(new AsyncCastHotswapEffectEvent(creature),
+ creature->m_Events.CalculateTime(0));
+ }
+
+ // Hook which is called after a gameobject was swapped
+ static void LoadStage1(GameObject* gameobject)
+ {
+ ASSERT(!gameobject->AI(),
+ "The AI should be null here!");
+
+ gameobject->AIM_Initialize();
+ }
+
+ static void LoadStage2(GameObject* gameobject)
+ {
+ gameobject->AI()->Reset();
+ }
+
+ template<typename T>
+ void RunOverAllEntities(T fn)
+ {
+ auto evaluator = [&](std::unordered_map<ObjectGuid, ObjectType*>& objects)
{
- ASSERT(script);
+ for (auto object : objects)
+ fn(object.second);
+ };
- // See if the script is using the same memory as another script. If this happens, it means that
- // someone forgot to allocate new memory for a script.
- for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it)
+ AIFunctionMapWorker<typename std::decay<decltype(evaluator)>::type> worker(std::move(evaluator));
+ TypeContainerVisitor<decltype(worker), MapStoredObjectTypesContainer> visitor(worker);
+
+ sMapMgr->DoForAllMaps([&](Map* map)
+ {
+ // Run the worker over all maps
+ visitor.Visit(map->GetObjectsStore());
+ });
+ }
+
+public:
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto ids_to_remove = static_cast<Base*>(this)->GetScriptIDsToRemove(context);
+
+ std::vector<ObjectType*> stage2;
+
+ RunOverAllEntities([&](ObjectType* object)
+ {
+ if (ids_to_remove.find(object->GetScriptId()) != ids_to_remove.end())
{
- if (it->second == script)
- {
- TC_LOG_ERROR("scripts", "Script '%s' has same memory pointer as '%s'.",
- script->GetName().c_str(), it->second->GetName().c_str());
+ UnloadStage1(object);
+ stage2.push_back(object);
+ }
+ });
- return;
+ for (auto object : stage2)
+ UnloadStage2(object);
+
+ // Add the new ids which are removed to the global ids to remove set
+ ids_removed_.insert(ids_to_remove.begin(), ids_to_remove.end());
+ }
+
+ void BeforeSwapContext(bool initialize) override
+ {
+ // Never swap creature or gameobject scripts when initializing
+ if (initialize)
+ return;
+
+ // Add the recently added scripts to the deleted scripts to replace
+ // default AI's with recently added core scripts.
+ ids_removed_.insert(static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().begin(),
+ static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().end());
+
+ std::vector<ObjectType*> remove;
+ std::vector<ObjectType*> stage2;
+
+ RunOverAllEntities([&](ObjectType* object)
+ {
+ if (ids_removed_.find(object->GetScriptId()) != ids_removed_.end())
+ {
+ if (object->AI())
+ {
+ // Overwrite existing (default) AI's which are replaced by a new script
+ UnloadStage1(object);
+ remove.push_back(object);
}
+
+ stage2.push_back(object);
}
+ });
+
+ for (auto object : remove)
+ UnloadStage2(object);
- AddScript(is_script_database_bound<TScript>{}, script);
- if (addToDeleteContainer)
- Scripts.push_back(script);
+ for (auto object : stage2)
+ LoadStage1(object);
+
+ for (auto object : stage2)
+ LoadStage2(object);
+
+ ids_removed_.clear();
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(ids_removed_.empty());
+ }
+
+private:
+ std::unordered_set<uint32> ids_removed_;
+};
+
+// This hook is responsible for swapping CreatureAI's
+template<typename Base>
+class ScriptRegistrySwapHooks<CreatureScript, Base>
+ : public CreatureGameObjectScriptRegistrySwapHooks<
+ Creature, CreatureScript, Base
+ > { };
+
+// This hook is responsible for swapping GameObjectAI's
+template<typename Base>
+class ScriptRegistrySwapHooks<GameObjectScript, Base>
+ : public CreatureGameObjectScriptRegistrySwapHooks<
+ GameObject, GameObjectScript, Base
+ > { };
+
+/// This hook is responsible for swapping BattlegroundScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<BattlegroundScript, Base>
+ : public UnsupportedScriptRegistrySwapHooks<Base> { };
+
+/// This hook is responsible for swapping OutdoorPvP's
+template<typename Base>
+class ScriptRegistrySwapHooks<OutdoorPvPScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+
+ if ((!swapped) && (bounds.first != bounds.second))
+ {
+ swapped = true;
+ sOutdoorPvPMgr->Die();
}
+ }
- // Gets a script by its ID (assigned by ObjectMgr).
- static TScript* GetScriptById(uint32 id)
+ void BeforeSwapContext(bool initialize) override
+ {
+ // Never swap outdoor pvp scripts when initializing
+ if ((!initialize) && swapped)
{
- ScriptMapIterator it = ScriptPointerList.find(id);
- if (it != ScriptPointerList.end())
- return it->second;
+ sOutdoorPvPMgr->InitOutdoorPvP();
+ swapped = false;
+ }
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+/// This hook is responsible for swapping InstanceMapScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<InstanceMapScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
- return NULL;
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ swapped = false;
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+/// This hook is responsible for swapping SpellScriptLoader's
+template<typename Base>
+class ScriptRegistrySwapHooks<SpellScriptLoader, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ if (swapped)
+ {
+ sObjectMgr->ValidateSpellScripts();
+ swapped = false;
}
+ }
- private:
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+// Database bound script registry
+template<typename ScriptType>
+class SpecializedScriptRegistry<ScriptType, true>
+ : public ScriptRegistryInterface,
+ public ScriptRegistrySwapHooks<ScriptType, ScriptRegistry<ScriptType>>
+{
+ template<typename>
+ friend class UnsupportedScriptRegistrySwapHooks;
+
+ template<typename, typename>
+ friend class ScriptRegistrySwapHooks;
+
+ template<typename, typename, typename>
+ friend class CreatureGameObjectScriptRegistrySwapHooks;
+
+public:
+ SpecializedScriptRegistry() { }
+
+ typedef std::unordered_map<
+ uint32 /*script id*/,
+ std::unique_ptr<ScriptType>
+ > ScriptStoreType;
+
+ typedef typename ScriptStoreType::iterator ScriptStoreIteratorType;
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ this->BeforeReleaseContext(context);
+
+ auto const bounds = _ids_of_contexts.equal_range(context);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ _scripts.erase(itr->second);
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ this->BeforeSwapContext(initialize);
+
+ _recently_added_ids.clear();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const& script : _scripts)
+ scripts.erase(script.second->GetName());
+ }
+
+ void Unload() final override
+ {
+ this->BeforeUnload();
+
+ ASSERT(_recently_added_ids.empty(),
+ "Recently added script ids should be empty here!");
+
+ _scripts.clear();
+ _ids_of_contexts.clear();
+ }
+
+ // Adds a database bound script
+ void AddScript(ScriptType* script)
+ {
+ ASSERT(script,
+ "Tried to call AddScript with a nullpointer!");
+ ASSERT(!sScriptMgr->GetCurrentScriptContext().empty(),
+ "Tried to register a script without being in a valid script context!");
- // Adds a database bound script
- static void AddScript(std::true_type, TScript* const script)
+ std::unique_ptr<ScriptType> script_ptr(script);
+
+ // Get an ID for the script. An ID only exists if it's a script that is assigned in the database
+ // through a script name (or similar).
+ if (uint32 const id = sObjectMgr->GetScriptId(script->GetName()))
{
- // Get an ID for the script. An ID only exists if it's a script that is assigned in the database
- // through a script name (or similar).
- uint32 id = sObjectMgr->GetScriptId(script->GetName());
- if (id)
+ // Try to find an existing script.
+ for (auto const& stored_script : _scripts)
{
- // Try to find an existing script.
- bool existing = false;
- for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it)
- {
- // If the script names match...
- if (it->second->GetName() == script->GetName())
- {
- // ... It exists.
- existing = true;
- break;
- }
- }
-
- // If the script isn't assigned -> assign it!
- if (!existing)
- {
- ScriptPointerList[id] = script;
- sScriptMgr->IncrementScriptCount();
-
- #ifdef SCRIPTS
- UnusedScriptNamesContainer::iterator itr = std::lower_bound(UnusedScriptNames.begin(), UnusedScriptNames.end(), script->GetName());
- if (itr != UnusedScriptNames.end() && *itr == script->GetName())
- UnusedScriptNames.erase(itr);
- #endif
- }
- else
+ // If the script names match...
+ if (stored_script.second->GetName() == script->GetName())
{
// If the script is already assigned -> delete it!
- TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, so the script can't work.",
- script->GetName().c_str());
+ TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, "
+ "so the script can't work.", script->GetName().c_str());
- ABORT(); // Error that should be fixed ASAP.
+ // Error that should be fixed ASAP.
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ ABORT();
+ return;
}
}
- else
- {
- // The script uses a script name from database, but isn't assigned to anything.
- TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.", script->GetName().c_str());
- }
- }
- // Adds a non database bound script
- static void AddScript(std::false_type, TScript* const script)
+ // If the script isn't assigned -> assign it!
+ _scripts.insert(std::make_pair(id, std::move(script_ptr)));
+ _ids_of_contexts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), id));
+ _recently_added_ids.insert(id);
+
+ sScriptRegistryCompositum->SetScriptNameInContext(script->GetName(),
+ sScriptMgr->GetCurrentScriptContext());
+ }
+ else
{
- // We're dealing with a code-only script; just add it.
- ScriptPointerList[_scriptIdCounter++] = script;
- sScriptMgr->IncrementScriptCount();
+ // The script uses a script name from database, but isn't assigned to anything.
+ TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.",
+ script->GetName().c_str());
+
+ // Avoid calling "delete script;" because we are currently in the script constructor
+ // In a valid scenario this will not happen because every script has a name assigned in the database
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ return;
}
+ }
+
+ // Gets a script by its ID (assigned by ObjectMgr).
+ ScriptType* GetScriptById(uint32 id)
+ {
+ auto const itr = _scripts.find(id);
+ if (itr != _scripts.end())
+ return itr->second.get();
+
+ return nullptr;
+ }
+
+ ScriptStoreType& GetScripts()
+ {
+ return _scripts;
+ }
- // Counter used for code-only scripts.
- static uint32 _scriptIdCounter;
+protected:
+ // Returns the script id's which are registered to a certain context
+ std::unordered_set<uint32> GetScriptIDsToRemove(std::string const& context) const
+ {
+ // Create a set of all ids which are removed
+ std::unordered_set<uint32> scripts_to_remove;
+
+ auto const bounds = _ids_of_contexts.equal_range(context);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ scripts_to_remove.insert(itr->second);
+
+ return scripts_to_remove;
+ }
+
+ std::unordered_set<uint32> const& GetRecentlyAddedScriptIDs() const
+ {
+ return _recently_added_ids;
+ }
+
+private:
+ ScriptStoreType _scripts;
+
+ // Scripts of a specific context
+ std::unordered_multimap<std::string /*context*/, uint32 /*id*/> _ids_of_contexts;
+
+ // Script id's which were registered recently
+ std::unordered_set<uint32> _recently_added_ids;
+};
+
+/// This hook is responsible for swapping CommandScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<CommandScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ void BeforeReleaseContext(std::string const& /*context*/) final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+
+ void BeforeUnload() final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+};
+
+// Database unbound script registry
+template<typename ScriptType>
+class SpecializedScriptRegistry<ScriptType, false>
+ : public ScriptRegistryInterface,
+ public ScriptRegistrySwapHooks<ScriptType, ScriptRegistry<ScriptType>>
+{
+ template<typename, typename>
+ friend class ScriptRegistrySwapHooks;
+
+public:
+ typedef std::unordered_multimap<std::string /*context*/, std::unique_ptr<ScriptType>> ScriptStoreType;
+ typedef typename ScriptStoreType::iterator ScriptStoreIteratorType;
+
+ SpecializedScriptRegistry() { }
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ this->BeforeReleaseContext(context);
+
+ _scripts.erase(context);
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ this->BeforeSwapContext(initialize);
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const& script : _scripts)
+ scripts.erase(script.second->GetName());
+ }
+
+ void Unload() final override
+ {
+ this->BeforeUnload();
+
+ _scripts.clear();
+ }
+
+ // Adds a non database bound script
+ void AddScript(ScriptType* script)
+ {
+ ASSERT(script,
+ "Tried to call AddScript with a nullpointer!");
+ ASSERT(!sScriptMgr->GetCurrentScriptContext().empty(),
+ "Tried to register a script without being in a valid script context!");
+
+ std::unique_ptr<ScriptType> script_ptr(script);
+
+ for (auto const& entry : _scripts)
+ if (entry.second.get() == script)
+ {
+ static_cast<ScriptRegistry<ScriptType>*>(this)->
+ LogDuplicatedScriptPointerError(script, entry.second.get());
+
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ return;
+ }
+
+ // We're dealing with a code-only script, just add it.
+ _scripts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), std::move(script_ptr)));
+ }
+
+ ScriptStoreType& GetScripts()
+ {
+ return _scripts;
+ }
+
+private:
+ ScriptStoreType _scripts;
};
// Utility macros to refer to the script registry.
-#define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptMap
-#define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptMapIterator
-#define SCR_REG_LST(T) ScriptRegistry<T>::ScriptPointerList
-#define SCR_REG_VEC(T) ScriptRegistry<T>::Scripts
+#define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptStoreType
+#define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptStoreIteratorType
+#define SCR_REG_LST(T) ScriptRegistry<T>::Instance()->GetScripts()
// Utility macros for looping over scripts.
#define FOR_SCRIPTS(T, C, E) \
if (SCR_REG_LST(T).empty()) \
return; \
+ \
for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
C != SCR_REG_LST(T).end(); ++C)
+
#define FOR_SCRIPTS_RET(T, C, E, R) \
if (SCR_REG_LST(T).empty()) \
return R; \
+ \
for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
C != SCR_REG_LST(T).end(); ++C)
+
#define FOREACH_SCRIPT(T) \
FOR_SCRIPTS(T, itr, end) \
- itr->second
+ itr->second
// Utility macros for finding specific scripts.
#define GET_SCRIPT(T, I, V) \
- T* V = ScriptRegistry<T>::GetScriptById(I); \
+ T* V = ScriptRegistry<T>::Instance()->GetScriptById(I); \
if (!V) \
return;
+
#define GET_SCRIPT_RET(T, I, V, R) \
- T* V = ScriptRegistry<T>::GetScriptById(I); \
+ T* V = ScriptRegistry<T>::Instance()->GetScriptById(I); \
if (!V) \
return R;
@@ -240,8 +946,18 @@ struct TSpellSummary
uint8 Effects; // set of enum SelectEffect
} *SpellSummary;
+ScriptObject::ScriptObject(const char* name) : _name(name)
+{
+ sScriptMgr->IncreaseScriptCount();
+}
+
+ScriptObject::~ScriptObject()
+{
+ sScriptMgr->DecreaseScriptCount();
+}
+
ScriptMgr::ScriptMgr()
- : _scriptCount(0), _scheduledScripts(0), _script_loader_callback(nullptr)
+ : _scriptCount(0), _script_loader_callback(nullptr)
{
}
@@ -255,6 +971,9 @@ ScriptMgr* ScriptMgr::instance()
void ScriptMgr::Initialize()
{
+ ASSERT(sSpellMgr->GetSpellInfo(SPELL_HOTSWAP_VISUAL_SPELL_EFFECT)
+ && "Reload hotswap spell effect for creatures isn't valid!");
+
uint32 oldMSTime = getMSTime();
LoadDatabase();
@@ -263,59 +982,85 @@ void ScriptMgr::Initialize()
FillSpellSummary();
+ // Load core scripts
+ SetScriptContext("___static___");
+
+ // SmartAI
AddSC_SmartScripts();
+ // LFGScripts
+ lfg::AddSC_LFGScripts();
+
+ // Load all static linked scripts through the script loader function.
ASSERT(_script_loader_callback,
"Script loader callback wasn't registered!");
-
_script_loader_callback();
-#ifdef SCRIPTS
- for (std::string const& scriptName : UnusedScriptNames)
+ // Initialize all dynamic scripts
+ // and finishes the context switch to do
+ // bulk loading
+ sScriptReloadMgr->Initialize();
+
+ // Loads all scripts from the current context
+ sScriptMgr->SwapScriptContext(true);
+
+ // Print unused script names.
+ std::unordered_set<std::string> unusedScriptNames(
+ sObjectMgr->GetAllScriptNames().begin(),
+ sObjectMgr->GetAllScriptNames().end());
+
+ // Remove the used scripts from the given container.
+ sScriptRegistryCompositum->RemoveUsedScriptsFromContainer(unusedScriptNames);
+
+ for (std::string const& scriptName : unusedScriptNames)
{
- TC_LOG_ERROR("sql.sql", "ScriptName '%s' exists in database, but no core script found!", scriptName.c_str());
+ // Avoid complaining about empty script names since the
+ // script name container contains a placeholder as the 0 element.
+ if (scriptName.empty())
+ continue;
+
+ TC_LOG_ERROR("sql.sql", "ScriptName '%s' exists in database, "
+ "but no core script found!", scriptName.c_str());
}
-#endif
- TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms",
+ GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
+}
+
+void ScriptMgr::SetScriptContext(std::string const& context)
+{
+ _currentContext = context;
+}
+
+void ScriptMgr::SwapScriptContext(bool initialize)
+{
+ sScriptRegistryCompositum->SwapContext(initialize);
+ _currentContext.clear();
+}
+
+void ScriptMgr::ReleaseScriptContext(std::string const& context)
+{
+ sScriptRegistryCompositum->ReleaseContext(context);
+}
+
+std::shared_ptr<ModuleReference>
+ ScriptMgr::AcquireModuleReferenceOfScriptName(std::string const& scriptname) const
+{
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Returns the reference to the module of the given scriptname
+ return ScriptReloadMgr::AcquireModuleReferenceOfContext(
+ sScriptRegistryCompositum->GetScriptContextOfScriptName(scriptname));
+#else
+ (void)scriptname;
+ // Something went wrong when this function is used in
+ // a static linked context.
+ WPAbort();
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
}
void ScriptMgr::Unload()
{
- #define SCR_CLEAR(T) \
- for (T* scr : SCR_REG_VEC(T)) \
- delete scr; \
- SCR_REG_VEC(T).clear();
-
- // Clear scripts for every script type.
- SCR_CLEAR(SpellScriptLoader);
- SCR_CLEAR(ServerScript);
- SCR_CLEAR(WorldScript);
- SCR_CLEAR(FormulaScript);
- SCR_CLEAR(WorldMapScript);
- SCR_CLEAR(InstanceMapScript);
- SCR_CLEAR(BattlegroundMapScript);
- SCR_CLEAR(ItemScript);
- SCR_CLEAR(CreatureScript);
- SCR_CLEAR(GameObjectScript);
- SCR_CLEAR(AreaTriggerScript);
- SCR_CLEAR(BattlegroundScript);
- SCR_CLEAR(OutdoorPvPScript);
- SCR_CLEAR(CommandScript);
- SCR_CLEAR(WeatherScript);
- SCR_CLEAR(AuctionHouseScript);
- SCR_CLEAR(ConditionScript);
- SCR_CLEAR(VehicleScript);
- SCR_CLEAR(DynamicObjectScript);
- SCR_CLEAR(TransportScript);
- SCR_CLEAR(AchievementCriteriaScript);
- SCR_CLEAR(PlayerScript);
- SCR_CLEAR(AccountScript);
- SCR_CLEAR(GuildScript);
- SCR_CLEAR(GroupScript);
- SCR_CLEAR(UnitScript);
-
- #undef SCR_CLEAR
+ sScriptRegistryCompositum->Unload();
delete[] SpellSummary;
delete[] UnitAI::AISpellInfo;
@@ -413,38 +1158,22 @@ void ScriptMgr::FillSpellSummary()
}
}
-void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector)
+template<typename T, typename F>
+void CreateSpellOrAuraScripts(uint32 spellId, std::list<T*>& scriptVector, F&& extractor)
{
SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
{
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
- if (!tmpscript)
+ // When the script is disabled continue with the next one
+ if (!itr->second.second)
continue;
- SpellScript* script = tmpscript->GetSpellScript();
-
- if (!script)
- continue;
-
- script->_Init(&tmpscript->GetName(), spellId);
-
- scriptVector.push_back(script);
- }
-}
-
-void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector)
-{
- SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
-
- for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
+ SpellScriptLoader* tmpscript = sScriptMgr->GetSpellScriptLoader(itr->second.first);
if (!tmpscript)
continue;
- AuraScript* script = tmpscript->GetAuraScript();
+ T* script = (*tmpscript.*extractor)();
if (!script)
continue;
@@ -455,19 +1184,19 @@ void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& script
}
}
-void ScriptMgr::CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, SpellScriptsContainer::iterator> >& scriptVector)
+void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector)
{
- SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
- scriptVector.reserve(std::distance(bounds.first, bounds.second));
+ CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetSpellScript);
+}
- for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
- if (!tmpscript)
- continue;
+void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector)
+{
+ CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetAuraScript);
+}
- scriptVector.push_back(std::make_pair(tmpscript, itr));
- }
+SpellScriptLoader* ScriptMgr::GetSpellScriptLoader(uint32 scriptId)
+{
+ return ScriptRegistry<SpellScriptLoader>::Instance()->GetScriptById(scriptId);
}
void ScriptMgr::OnNetworkStart()
@@ -1495,56 +2224,62 @@ void ScriptMgr::OnGroupDisband(Group* group)
void ScriptMgr::OnHeal(Unit* healer, Unit* reciever, uint32& gain)
{
FOREACH_SCRIPT(UnitScript)->OnHeal(healer, reciever, gain);
+ FOREACH_SCRIPT(PlayerScript)->OnHeal(healer, reciever, gain);
}
void ScriptMgr::OnDamage(Unit* attacker, Unit* victim, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->OnDamage(attacker, victim, damage);
+ FOREACH_SCRIPT(PlayerScript)->OnDamage(attacker, victim, damage);
}
void ScriptMgr::ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage);
}
void ScriptMgr::ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifyMeleeDamage(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifyMeleeDamage(target, attacker, damage);
}
void ScriptMgr::ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifySpellDamageTaken(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifySpellDamageTaken(target, attacker, damage);
}
SpellScriptLoader::SpellScriptLoader(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<SpellScriptLoader>::AddScript(this);
+ ScriptRegistry<SpellScriptLoader>::Instance()->AddScript(this);
}
ServerScript::ServerScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ServerScript>::AddScript(this);
+ ScriptRegistry<ServerScript>::Instance()->AddScript(this);
}
WorldScript::WorldScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<WorldScript>::AddScript(this);
+ ScriptRegistry<WorldScript>::Instance()->AddScript(this);
}
FormulaScript::FormulaScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<FormulaScript>::AddScript(this);
+ ScriptRegistry<FormulaScript>::Instance()->AddScript(this);
}
UnitScript::UnitScript(const char* name, bool addToScripts)
: ScriptObject(name)
{
- ScriptRegistry<UnitScript>::AddScript(this, addToScripts);
+ if (addToScripts)
+ ScriptRegistry<UnitScript>::Instance()->AddScript(this);
}
WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
@@ -1553,7 +2288,7 @@ WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsWorldMap())
TC_LOG_ERROR("scripts", "WorldMapScript for map %u is invalid.", mapId);
- ScriptRegistry<WorldMapScript>::AddScript(this);
+ ScriptRegistry<WorldMapScript>::Instance()->AddScript(this);
}
InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
@@ -1562,7 +2297,7 @@ InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsDungeon())
TC_LOG_ERROR("scripts", "InstanceMapScript for map %u is invalid.", mapId);
- ScriptRegistry<InstanceMapScript>::AddScript(this);
+ ScriptRegistry<InstanceMapScript>::Instance()->AddScript(this);
}
BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
@@ -1571,122 +2306,117 @@ BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsBattleground())
TC_LOG_ERROR("scripts", "BattlegroundMapScript for map %u is invalid.", mapId);
- ScriptRegistry<BattlegroundMapScript>::AddScript(this);
+ ScriptRegistry<BattlegroundMapScript>::Instance()->AddScript(this);
}
ItemScript::ItemScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ItemScript>::AddScript(this);
+ ScriptRegistry<ItemScript>::Instance()->AddScript(this);
}
CreatureScript::CreatureScript(const char* name)
: UnitScript(name, false)
{
- ScriptRegistry<CreatureScript>::AddScript(this);
+ ScriptRegistry<CreatureScript>::Instance()->AddScript(this);
}
GameObjectScript::GameObjectScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GameObjectScript>::AddScript(this);
+ ScriptRegistry<GameObjectScript>::Instance()->AddScript(this);
}
AreaTriggerScript::AreaTriggerScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AreaTriggerScript>::AddScript(this);
+ ScriptRegistry<AreaTriggerScript>::Instance()->AddScript(this);
}
BattlegroundScript::BattlegroundScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<BattlegroundScript>::AddScript(this);
+ ScriptRegistry<BattlegroundScript>::Instance()->AddScript(this);
}
OutdoorPvPScript::OutdoorPvPScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<OutdoorPvPScript>::AddScript(this);
+ ScriptRegistry<OutdoorPvPScript>::Instance()->AddScript(this);
}
CommandScript::CommandScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<CommandScript>::AddScript(this);
+ ScriptRegistry<CommandScript>::Instance()->AddScript(this);
}
WeatherScript::WeatherScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<WeatherScript>::AddScript(this);
+ ScriptRegistry<WeatherScript>::Instance()->AddScript(this);
}
AuctionHouseScript::AuctionHouseScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AuctionHouseScript>::AddScript(this);
+ ScriptRegistry<AuctionHouseScript>::Instance()->AddScript(this);
}
ConditionScript::ConditionScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ConditionScript>::AddScript(this);
+ ScriptRegistry<ConditionScript>::Instance()->AddScript(this);
}
VehicleScript::VehicleScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<VehicleScript>::AddScript(this);
+ ScriptRegistry<VehicleScript>::Instance()->AddScript(this);
}
DynamicObjectScript::DynamicObjectScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<DynamicObjectScript>::AddScript(this);
+ ScriptRegistry<DynamicObjectScript>::Instance()->AddScript(this);
}
TransportScript::TransportScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<TransportScript>::AddScript(this);
+ ScriptRegistry<TransportScript>::Instance()->AddScript(this);
}
AchievementCriteriaScript::AchievementCriteriaScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AchievementCriteriaScript>::AddScript(this);
+ ScriptRegistry<AchievementCriteriaScript>::Instance()->AddScript(this);
}
PlayerScript::PlayerScript(const char* name)
: UnitScript(name, false)
{
- ScriptRegistry<PlayerScript>::AddScript(this);
+ ScriptRegistry<PlayerScript>::Instance()->AddScript(this);
}
AccountScript::AccountScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AccountScript>::AddScript(this);
+ ScriptRegistry<AccountScript>::Instance()->AddScript(this);
}
GuildScript::GuildScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GuildScript>::AddScript(this);
+ ScriptRegistry<GuildScript>::Instance()->AddScript(this);
}
GroupScript::GroupScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GroupScript>::AddScript(this);
+ ScriptRegistry<GroupScript>::Instance()->AddScript(this);
}
-// Instantiate static members of ScriptRegistry.
-template<class TScript> std::map<uint32, TScript*> ScriptRegistry<TScript>::ScriptPointerList;
-template<class TScript> std::vector<TScript*> ScriptRegistry<TScript>::Scripts;
-template<class TScript> uint32 ScriptRegistry<TScript>::_scriptIdCounter = 0;
-
// Specialize for each script type class like so:
template class TC_GAME_API ScriptRegistry<SpellScriptLoader>;
template class TC_GAME_API ScriptRegistry<ServerScript>;
@@ -1714,13 +2444,3 @@ template class TC_GAME_API ScriptRegistry<GuildScript>;
template class TC_GAME_API ScriptRegistry<GroupScript>;
template class TC_GAME_API ScriptRegistry<UnitScript>;
template class TC_GAME_API ScriptRegistry<AccountScript>;
-
-// Undefine utility macros.
-#undef GET_SCRIPT_RET
-#undef GET_SCRIPT
-#undef FOREACH_SCRIPT
-#undef FOR_SCRIPTS_RET
-#undef FOR_SCRIPTS
-#undef SCR_REG_LST
-#undef SCR_REG_ITR
-#undef SCR_REG_MAP
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 337e7de4861..cc1b65fa593 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -46,6 +46,7 @@ class InstanceMap;
class InstanceScript;
class Item;
class Map;
+class ModuleReference;
class OutdoorPvP;
class Player;
class Quest;
@@ -157,14 +158,8 @@ class TC_GAME_API ScriptObject
protected:
- ScriptObject(const char* name)
- : _name(name)
- {
- }
-
- virtual ~ScriptObject()
- {
- }
+ ScriptObject(const char* name);
+ virtual ~ScriptObject();
private:
@@ -338,7 +333,8 @@ class TC_GAME_API WorldMapScript : public ScriptObject, public MapScript<Map>
WorldMapScript(const char* name, uint32 mapId);
};
-class TC_GAME_API InstanceMapScript : public ScriptObject, public MapScript<InstanceMap>
+class TC_GAME_API InstanceMapScript
+ : public ScriptObject, public MapScript<InstanceMap>
{
protected:
@@ -834,15 +830,6 @@ class TC_GAME_API GroupScript : public ScriptObject
virtual void OnDisband(Group* /*group*/) { }
};
-// Placed here due to ScriptRegistry::AddScript dependency.
-#define sScriptMgr ScriptMgr::instance()
-
-// namespace
-// {
- typedef std::list<std::string> UnusedScriptNamesContainer;
- TC_GAME_API extern UnusedScriptNamesContainer UnusedScriptNames;
-// }
-
// Manages registration, loading, and execution of scripts.
class TC_GAME_API ScriptMgr
{
@@ -852,16 +839,17 @@ class TC_GAME_API ScriptMgr
ScriptMgr();
virtual ~ScriptMgr();
+ void FillSpellSummary();
+ void LoadDatabase();
+
+ void IncreaseScriptCount() { ++_scriptCount; }
+ void DecreaseScriptCount() { --_scriptCount; }
+
public: /* Initialization */
static ScriptMgr* instance();
void Initialize();
- void LoadDatabase();
- void FillSpellSummary();
-
- const char* ScriptsVersion() const { return "Integrated Trinity Scripts"; }
- void IncrementScriptCount() { ++_scriptCount; }
uint32 GetScriptCount() const { return _scriptCount; }
typedef void(*ScriptLoaderCallbackType)();
@@ -873,6 +861,27 @@ class TC_GAME_API ScriptMgr
_script_loader_callback = script_loader_callback;
}
+ public: /* Script contexts */
+ /// Set the current script context, which allows the ScriptMgr
+ /// to accept new scripts in this context.
+ /// Requires a SwapScriptContext() call afterwards to load the new scripts.
+ void SetScriptContext(std::string const& context);
+ /// Returns the current script context.
+ std::string const& GetCurrentScriptContext() const { return _currentContext; }
+ /// Releases all scripts associated with the given script context immediately.
+ /// Requires a SwapScriptContext() call afterwards to finish the unloading.
+ void ReleaseScriptContext(std::string const& context);
+ /// Executes all changed introduced by SetScriptContext and ReleaseScriptContext.
+ /// It is possible to combine multiple SetScriptContext and ReleaseScriptContext
+ /// calls for better performance (bulk changes).
+ void SwapScriptContext(bool initialize = false);
+
+ /// Acquires a strong module reference to the module containing the given script name,
+ /// which prevents the shared library which contains the script from unloading.
+ /// The shared library is lazy unloaded as soon as all references to it are released.
+ std::shared_ptr<ModuleReference> AcquireModuleReferenceOfScriptName(
+ std::string const& scriptname) const;
+
public: /* Unloading */
void Unload();
@@ -881,7 +890,7 @@ class TC_GAME_API ScriptMgr
void CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector);
void CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector);
- void CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, std::multimap<uint32, uint32>::iterator> >& scriptVector);
+ SpellScriptLoader* GetSpellScriptLoader(uint32 scriptId);
public: /* ServerScript */
@@ -1094,21 +1103,14 @@ class TC_GAME_API ScriptMgr
void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage);
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage);
- public: /* Scheduled scripts */
-
- uint32 IncreaseScheduledScriptsCount() { return ++_scheduledScripts; }
- uint32 DecreaseScheduledScriptCount() { return --_scheduledScripts; }
- uint32 DecreaseScheduledScriptCount(size_t count) { return _scheduledScripts -= count; }
- bool IsScriptScheduled() const { return _scheduledScripts > 0; }
-
private:
-
uint32 _scriptCount;
- //atomic op counter for active scripts amount
- std::atomic<uint32> _scheduledScripts;
-
ScriptLoaderCallbackType _script_loader_callback;
+
+ std::string _currentContext;
};
+#define sScriptMgr ScriptMgr::instance()
+
#endif
diff --git a/src/server/game/Scripting/ScriptReloadMgr.cpp b/src/server/game/Scripting/ScriptReloadMgr.cpp
new file mode 100644
index 00000000000..aac0e26f02b
--- /dev/null
+++ b/src/server/game/Scripting/ScriptReloadMgr.cpp
@@ -0,0 +1,1520 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ScriptReloadMgr.h"
+#include "Errors.h"
+
+#ifndef TRINITY_API_USE_DYNAMIC_LINKING
+
+// This method should never be called
+std::shared_ptr<ModuleReference>
+ ScriptReloadMgr::AcquireModuleReferenceOfContext(std::string const& /*context*/)
+{
+ WPAbort();
+}
+
+// Returns the empty implemented ScriptReloadMgr
+ScriptReloadMgr* ScriptReloadMgr::instance()
+{
+ static ScriptReloadMgr instance;
+ return &instance;
+}
+
+#else
+
+#include <algorithm>
+#include <regex>
+#include <vector>
+#include <future>
+#include <memory>
+#include <fstream>
+#include <type_traits>
+#include <unordered_set>
+#include <unordered_map>
+
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "efsw/efsw.hpp"
+
+#include "Log.h"
+#include "Config.h"
+#include "BuiltInConfig.h"
+#include "ScriptMgr.h"
+#include "StartProcess.h"
+#include "MPSCQueue.h"
+#include "GitRevision.h"
+
+namespace fs = boost::filesystem;
+
+#ifdef _WIN32
+ #include <windows.h>
+#else // Posix
+ #include <dlfcn.h>
+#endif
+
+// Promote the sScriptReloadMgr to a HotSwapScriptReloadMgr
+// in this compilation unit.
+#undef sScriptReloadMgr
+#define sScriptReloadMgr static_cast<HotSwapScriptReloadMgr*>(ScriptReloadMgr::instance())
+
+// Returns "" on Windows and "lib" on posix.
+static char const* GetSharedLibraryPrefix()
+{
+#ifdef _WIN32
+ return "";
+#else // Posix
+ return "lib";
+#endif
+}
+
+// Returns "dll" on Windows and "so" on posix.
+static char const* GetSharedLibraryExtension()
+{
+#ifdef _WIN32
+ return "dll";
+#else // Posix
+ return "so";
+#endif
+}
+
+#ifdef _WIN32
+typedef HMODULE HandleType;
+#else // Posix
+typedef void* HandleType;
+#endif
+
+class SharedLibraryUnloader
+{
+public:
+ SharedLibraryUnloader()
+ : _path() { }
+ explicit SharedLibraryUnloader(fs::path const& path)
+ : _path(path) { }
+
+ void operator() (HandleType handle) const
+ {
+ // Unload the associated shared library.
+#ifdef _WIN32
+ bool success = (FreeLibrary(handle) != 0);
+#else // Posix
+ bool success = (dlclose(handle) == 0);
+#endif
+
+ if (!success)
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to unload (syscall) the shared library \"%s\".",
+ _path.generic_string().c_str());
+
+ return;
+ }
+
+ boost::system::error_code error;
+ if (fs::remove(_path, error))
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded and deleted the shared library \"%s\".",
+ _path.generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to delete the shared library \"%s\" (%s).",
+ _path.generic_string().c_str(), error.message().c_str());
+ }
+ }
+
+private:
+ fs::path _path;
+};
+
+typedef std::unique_ptr<typename std::remove_pointer<HandleType>::type, SharedLibraryUnloader> HandleHolder;
+
+typedef char const* (*GetScriptModuleRevisionHashType)();
+typedef void (*AddScriptsType)();
+typedef char const* (*GetScriptModuleType)();
+typedef char const* (*GetBuildDirectiveType)();
+
+class ScriptModule
+ : public ModuleReference
+{
+public:
+ explicit ScriptModule(HandleHolder handle, GetScriptModuleRevisionHashType getScriptModuleRevisionHash,
+ AddScriptsType addScripts, GetScriptModuleType getScriptModule,
+ GetBuildDirectiveType getBuildDirective, fs::path const& path)
+ : _handle(std::forward<HandleHolder>(handle)), _getScriptModuleRevisionHash(getScriptModuleRevisionHash),
+ _addScripts(addScripts), _getScriptModule(getScriptModule),
+ _getBuildDirective(getBuildDirective), _path(path) { }
+
+ ScriptModule(ScriptModule const&) = delete;
+ ScriptModule(ScriptModule&& right) = delete;
+
+ ScriptModule& operator= (ScriptModule const&) = delete;
+ ScriptModule& operator= (ScriptModule&& right) = delete;
+
+ static Optional<std::shared_ptr<ScriptModule>> CreateFromPath(fs::path const& path);
+
+ char const* GetScriptModuleRevisionHash() const override
+ {
+ return _getScriptModuleRevisionHash();
+ }
+
+ void AddScripts() const
+ {
+ return _addScripts();
+ }
+
+ char const* GetScriptModule() const override
+ {
+ return _getScriptModule();
+ }
+
+ char const* GetBuildDirective() const
+ {
+ return _getBuildDirective();
+ }
+
+ fs::path const& GetModulePath() const override
+ {
+ return _path;
+ }
+
+private:
+ HandleHolder _handle;
+
+ GetScriptModuleRevisionHashType _getScriptModuleRevisionHash;
+ AddScriptsType _addScripts;
+ GetScriptModuleType _getScriptModule;
+ GetBuildDirectiveType _getBuildDirective;
+
+ fs::path _path;
+};
+
+template<typename Fn>
+static bool GetFunctionFromSharedLibrary(HandleType handle, std::string const& name, Fn& fn)
+{
+#ifdef _WIN32
+ fn = reinterpret_cast<Fn>(GetProcAddress(handle, name.c_str()));
+#else // Posix
+ fn = reinterpret_cast<Fn>(dlsym(handle, name.c_str()));
+#endif
+ return fn != nullptr;
+}
+
+// Load a shared library from the given path.
+Optional<std::shared_ptr<ScriptModule>> ScriptModule::CreateFromPath(fs::path const& path)
+{
+#ifdef _WIN32
+ HandleType handle = LoadLibrary(path.generic_string().c_str());
+#else // Posix
+ HandleType handle = dlopen(path.c_str(), RTLD_LAZY);
+#endif
+
+ if (!handle)
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Could not load the shared library \"%s\" for reading.",
+ path.generic_string().c_str());
+ return boost::none;
+ }
+
+ // Use RAII to release the library on failure.
+ HandleHolder holder(handle, SharedLibraryUnloader(path));
+
+ GetScriptModuleRevisionHashType getScriptModuleRevisionHash;
+ AddScriptsType addScripts;
+ GetScriptModuleType getScriptModule;
+ GetBuildDirectiveType getBuildDirective;
+
+ if (GetFunctionFromSharedLibrary(handle, "GetScriptModuleRevisionHash", getScriptModuleRevisionHash) &&
+ GetFunctionFromSharedLibrary(handle, "AddScripts", addScripts) &&
+ GetFunctionFromSharedLibrary(handle, "GetScriptModule", getScriptModule) &&
+ GetFunctionFromSharedLibrary(handle, "GetBuildDirective", getBuildDirective))
+ return std::make_shared<ScriptModule>(std::move(holder), getScriptModuleRevisionHash,
+ addScripts, getScriptModule, getBuildDirective, path);
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Could not extract all required functions from the shared library \"%s\"!",
+ path.generic_string().c_str());
+
+ return boost::none;
+ }
+}
+
+static bool HasValidScriptModuleName(std::string const& name)
+{
+ // Detects scripts_NAME.dll's / .so's
+ static std::regex const regex(
+ Trinity::StringFormat("^%s[sS]cripts_[a-zA-Z0-9_]+\\.%s$",
+ GetSharedLibraryPrefix(),
+ GetSharedLibraryExtension()));
+
+ return std::regex_match(name, regex);
+}
+
+/// File watcher responsible for watching shared libraries
+class LibraryUpdateListener : public efsw::FileWatchListener
+{
+public:
+ LibraryUpdateListener() { }
+ virtual ~LibraryUpdateListener() { }
+
+ void handleFileAction(efsw::WatchID /*watchid*/, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename = "") final override;
+};
+
+static LibraryUpdateListener libraryUpdateListener;
+
+/// File watcher responsible for watching source files
+class SourceUpdateListener : public efsw::FileWatchListener
+{
+ fs::path const path_;
+
+ std::string const script_module_name_;
+
+ efsw::WatchID const watcher_id_;
+
+public:
+ explicit SourceUpdateListener(fs::path path, std::string script_module_name);
+
+ virtual ~SourceUpdateListener();
+
+ void handleFileAction(efsw::WatchID /*watchid*/, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename = "") final override;
+};
+
+namespace std
+{
+ template <>
+ struct hash<fs::path>
+ {
+ hash<string> hasher;
+
+ std::size_t operator()(fs::path const& key) const
+ {
+ return hasher(key.generic_string());
+ }
+ };
+}
+
+/// Invokes a synchronous CMake process with the given arguments
+template<typename... T>
+static int InvokeCMakeCommand(T&&... args)
+{
+ auto const executable = BuiltInConfig::GetCMakeCommand();
+ return Trinity::StartProcess(executable, {
+ executable,
+ std::forward<T>(args)...
+ }, "scripts.hotswap");
+}
+
+/// Invokes an asynchronous CMake process with the given arguments
+template<typename... T>
+static std::shared_ptr<Trinity::AsyncProcessResult> InvokeAsyncCMakeCommand(T&&... args)
+{
+ auto const executable = BuiltInConfig::GetCMakeCommand();
+ return Trinity::StartAsyncProcess(executable, {
+ executable,
+ std::forward<T>(args)...
+ }, "scripts.hotswap");
+}
+
+/// Calculates the C++ project name of the given module which is
+/// the lowercase string of scripts_${module}.
+static std::string CalculateScriptModuleProjectName(std::string const& module)
+{
+ std::string module_project = "scripts_" + module;
+ std::transform(module_project.begin(), module_project.end(),
+ module_project.begin(), ::tolower);
+
+ return module_project;
+}
+
+/// Returns false when there isn't any attached debugger to the process which
+/// could block the rebuild of new shared libraries.
+static bool IsDebuggerBlockingRebuild()
+{
+#ifdef _WIN32
+ if (IsDebuggerPresent())
+ return true;
+#endif
+ return false;
+}
+
+/// ScriptReloadMgr which is used when dynamic linking is enabled
+///
+/// This class manages shared library loading/unloading through watching
+/// the script module directory. Loaded shared libraries are mirrored
+/// into a .cache subdirectory to allow lazy unloading as long as
+/// the shared library is still used which is useful for scripts
+/// which can't be instantly replaced like spells or instances.
+/// Several modules which reference different versions can be kept loaded
+/// to serve scripts of different versions to entities and spells.
+///
+/// Also this class invokes rebuilds as soon as the source of loaded
+/// scripts change and installs the modules correctly through CMake.
+class HotSwapScriptReloadMgr final
+ : public ScriptReloadMgr
+{
+ friend class ScriptReloadMgr;
+ friend class SourceUpdateListener;
+
+ /// Reflects a queued change on a shared library or shared library
+ /// which is waiting for processing
+ enum class ChangeStateRequest : uint8
+ {
+ CHANGE_REQUEST_ADDED,
+ CHANGE_REQUEST_MODIFIED,
+ CHANGE_REQUEST_REMOVED
+ };
+
+ /// Reflects a running job of an invoked asynchronous external process
+ enum class BuildJobType : uint8
+ {
+ BUILD_JOB_NONE,
+ BUILD_JOB_RERUN_CMAKE,
+ BUILD_JOB_COMPILE,
+ BUILD_JOB_INSTALL,
+ };
+
+ // Represents a job which was invoked through a source or shared library change
+ class BuildJob
+ {
+ // Script module which is processed in the current running job
+ std::string script_module_name_;
+ // The C++ project name of the module which is processed
+ std::string script_module_project_name_;
+ // The build directive of the current module which is processed
+ // like "Release" or "Debug". The build directive from the
+ // previous same module is used if there was any.
+ std::string script_module_build_directive_;
+
+ // Type of the current running job
+ BuildJobType type_;
+ // The async process result of the current job
+ std::shared_ptr<Trinity::AsyncProcessResult> async_result_;
+
+ public:
+ explicit BuildJob(std::string script_module_name, std::string script_module_project_name,
+ std::string script_module_build_directive)
+ : script_module_name_(std::move(script_module_name)),
+ script_module_project_name_(std::move(script_module_project_name)),
+ script_module_build_directive_(std::move(script_module_build_directive)),
+ type_(BuildJobType::BUILD_JOB_NONE) { }
+
+ bool IsValid() const
+ {
+ return type_ != BuildJobType::BUILD_JOB_NONE;
+ }
+
+ std::string const& GetModuleName() const { return script_module_name_; }
+
+ std::string const& GetProjectName() const { return script_module_project_name_; }
+
+ std::string const& GetBuildDirective() const { return script_module_build_directive_; }
+
+ BuildJobType GetType() const { return type_; }
+
+ std::shared_ptr<Trinity::AsyncProcessResult> const& GetProcess() const
+ {
+ ASSERT(async_result_, "Tried to access an empty process handle!");
+ return async_result_;
+ }
+
+ /// Updates the current running job with the given type and async result
+ void UpdateCurrentJob(BuildJobType type,
+ std::shared_ptr<Trinity::AsyncProcessResult> async_result)
+ {
+ ASSERT(type != BuildJobType::BUILD_JOB_NONE, "None isn't allowed here!");
+ ASSERT(async_result, "The async result must not be empty!");
+
+ type_ = type;
+ async_result_ = std::move(async_result);
+ }
+ };
+
+ /// Base class for lockfree asynchronous messages to the script reloader
+ class ScriptReloaderMessage
+ {
+ public:
+ virtual ~ScriptReloaderMessage() { }
+
+ /// Invoke this function to run a message thread safe on the reloader
+ virtual void operator() (HotSwapScriptReloadMgr* reloader) = 0;
+ };
+
+ /// Implementation class which wraps functional types and dispatches
+ /// it in the overwritten implementation of the reloader messages.
+ template<typename T>
+ class ScriptReloaderMessageImplementation
+ : public ScriptReloaderMessage
+ {
+ T dispatcher_;
+
+ public:
+ explicit ScriptReloaderMessageImplementation(T dispatcher)
+ : dispatcher_(std::move(dispatcher)) { }
+
+ void operator() (HotSwapScriptReloadMgr* reloader) final override
+ {
+ dispatcher_(reloader);
+ }
+ };
+
+ /// Uses the given functional type and creates a asynchronous reloader
+ /// message on the heap, which requires deletion.
+ template<typename T>
+ auto MakeMessage(T&& dispatcher)
+ -> ScriptReloaderMessageImplementation<typename std::decay<T>::type>*
+ {
+ return new ScriptReloaderMessageImplementation<typename std::decay<T>::type>
+ (std::forward<T>(dispatcher));
+ }
+
+public:
+ HotSwapScriptReloadMgr()
+ : _libraryWatcher(-1), _unique_library_name_counter(0),
+ _last_time_library_changed(0), _last_time_sources_changed(0),
+ _last_time_user_informed(0), terminate_early(false) { }
+
+ virtual ~HotSwapScriptReloadMgr()
+ {
+ // Delete all messages
+ ScriptReloaderMessage* message;
+ while (_messages.Dequeue(message))
+ delete message;
+ }
+
+ /// Returns the absolute path to the script module directory
+ static fs::path GetLibraryDirectory()
+ {
+ return fs::absolute(sConfigMgr->GetStringDefault("HotSwap.ScriptDir", "scripts"));
+ }
+
+ /// Returns the absolute path to the scripts directory in the source tree.
+ static fs::path GetSourceDirectory()
+ {
+ fs::path dir = BuiltInConfig::GetSourceDirectory();
+ dir /= "src";
+ dir /= "server";
+ dir /= "scripts";
+ return dir;
+ }
+
+ /// Initializes the file watchers and loads all existing shared libraries
+ /// into the running server.
+ void Initialize() final override
+ {
+ if (!sWorld->getBoolConfig(CONFIG_HOTSWAP_ENABLED))
+ return;
+
+ if (BuiltInConfig::GetBuildDirectory().find(" ") != std::string::npos)
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Your build directory path \"%s\" "
+ "contains spaces, which isn't allowed for compatibility reasons! "
+ "You need to create a build directory which doesn't contain any space character "
+ "in it's path!",
+ BuiltInConfig::GetBuildDirectory().c_str());
+
+ return;
+ }
+
+ {
+ auto const library_directory = GetLibraryDirectory();
+ if (!fs::exists(library_directory) || !fs::is_directory(library_directory))
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Library directory \"%s\" doesn't exist!.",
+ library_directory.generic_string().c_str());
+ return;
+ }
+ }
+
+ // Get the cache directory path
+ fs::path const cache_path = []
+ {
+ auto path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory());
+ path /= ".cache";
+ return path;
+ }();
+
+ // We use the boost filesystem function versions which accept
+ // an error code to prevent it from throwing exceptions.
+ boost::system::error_code code;
+ if ((!fs::exists(cache_path, code) || (fs::remove_all(cache_path, code) > 0)) &&
+ !fs::create_directory(cache_path, code))
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Couldn't create the cache directory \"%s\".",
+ cache_path.generic_string().c_str());
+ return;
+ }
+
+ // Used to silent compiler warnings
+ (void)code;
+
+ // Correct the CMake prefix when needed
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED))
+ DoCMakePrefixCorrectionIfNeeded();
+
+ InitializeDefaultLibraries();
+ InitializeFileWatchers();
+ }
+
+ /// Needs to be called periodically from the worldserver loop
+ /// to invoke queued actions like module loading/unloading and script
+ /// compilation.
+ /// This method should be invoked from a thread safe point to
+ /// prevent misbehavior.
+ void Update() final override
+ {
+ // Consume all messages
+ ScriptReloaderMessage* message;
+ while (_messages.Dequeue(message))
+ {
+ (*message)(this);
+ delete message;
+ }
+
+ DispatchRunningBuildJobs();
+ DispatchModuleChanges();
+ }
+
+ /// Unloads the manager and cancels all runnings jobs immediately
+ void Unload() final override
+ {
+ if (_libraryWatcher >= 0)
+ {
+ _fileWatcher.removeWatch(_libraryWatcher);
+ _libraryWatcher = -1;
+ }
+
+ // If a build is in progress cancel it
+ if (_build_job)
+ {
+ _build_job->GetProcess()->Terminate();
+ _build_job.reset();
+ }
+
+ // Release all strong references to script modules
+ // to trigger unload actions as early as possible,
+ // otherwise the worldserver will crash on exit.
+ _running_script_modules.clear();
+ }
+
+ /// Queue's a thread safe message to the reloader which is executed on
+ /// the next world server update tick.
+ template<typename T>
+ void QueueMessage(T&& message)
+ {
+ _messages.Enqueue(MakeMessage(std::forward<T>(message)));
+ }
+
+ /// Queues an action which marks the given shared library as changed
+ /// which will add, unload or reload it at the next world update tick.
+ /// This method is thread safe.
+ void QueueSharedLibraryChanged(fs::path const& path)
+ {
+ _last_time_library_changed = getMSTime();
+ _libraries_changed.insert(path);
+ }
+
+ /// Queues a notification that a source file was added
+ /// This method is thread unsafe.
+ void QueueAddSourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_ADDED);
+ }
+
+ /// Queues a notification that a source file was modified
+ /// This method is thread unsafe.
+ void QueueModifySourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_MODIFIED);
+ }
+
+ /// Queues a notification that a source file was removed
+ /// This method is thread unsafe.
+ void QueueRemoveSourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_REMOVED);
+ }
+
+private:
+ // Loads all shared libraries which are contained in the
+ // scripts directory on startup.
+ void InitializeDefaultLibraries()
+ {
+ fs::path const libraryDirectory(GetLibraryDirectory());
+ fs::directory_iterator const dir_end;
+
+ uint32 count = 0;
+
+ // Iterate through all shared libraries in the script directory and load it
+ for (fs::directory_iterator dir_itr(libraryDirectory); dir_itr != dir_end ; ++dir_itr)
+ if (fs::is_regular_file(dir_itr->path()) && HasValidScriptModuleName(dir_itr->path().filename().generic_string()))
+ {
+ TC_LOG_INFO("scripts.hotswap", "Loading script module \"%s\"...",
+ dir_itr->path().filename().generic_string().c_str());
+
+ // Don't swap the script context to do bulk loading
+ ProcessLoadScriptModule(dir_itr->path(), false);
+ ++count;
+ }
+
+ TC_LOG_INFO("scripts.hotswap", ">> Loaded %u script modules.", count);
+ }
+
+ // Initialize all enabled file watchers.
+ // Needs to be called after InitializeDefaultLibraries()!
+ void InitializeFileWatchers()
+ {
+ _libraryWatcher = _fileWatcher.addWatch(GetLibraryDirectory().generic_string(), &libraryUpdateListener, false);
+ if (_libraryWatcher >= 0)
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Library reloader is listening on \"%s\".",
+ GetLibraryDirectory().generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to initialize the library reloader on \"%s\".",
+ GetLibraryDirectory().generic_string().c_str());
+ }
+
+ _fileWatcher.watch();
+ }
+
+ /// Updates the current state of the given source path
+ void UpdateSourceChangeRequest(std::string const& module_name,
+ fs::path const& path,
+ ChangeStateRequest state)
+ {
+ _last_time_sources_changed = getMSTime();
+
+ // Write when there is no module with the given name known
+ auto module_itr = _sources_changed.find(module_name);
+
+ // When the file was modified it's enough to mark the module as
+ // dirty by initializing the associated map.
+ if (module_itr == _sources_changed.end())
+ module_itr = _sources_changed.insert(std::make_pair(
+ module_name, decltype(_sources_changed)::mapped_type{})).first;
+
+ // Leave when the file was just modified as explained above
+ if (state == ChangeStateRequest::CHANGE_REQUEST_MODIFIED)
+ return;
+
+ // Insert when the given path isn't existent
+ auto const itr = module_itr->second.find(path);
+ if (itr == module_itr->second.end())
+ {
+ module_itr->second.insert(std::make_pair(path, state));
+ return;
+ }
+
+ ASSERT((itr->second == ChangeStateRequest::CHANGE_REQUEST_ADDED)
+ || (itr->second == ChangeStateRequest::CHANGE_REQUEST_REMOVED),
+ "Stored value is invalid!");
+
+ ASSERT((state == ChangeStateRequest::CHANGE_REQUEST_ADDED)
+ || (state == ChangeStateRequest::CHANGE_REQUEST_REMOVED),
+ "The given state is invalid!");
+
+ ASSERT(state != itr->second,
+ "Tried to apply a state which is stored already!");
+
+ module_itr->second.erase(itr);
+ }
+
+ /// Called periodically on the worldserver tick to process all
+ /// load/unload/reload requests of shared libraries.
+ void DispatchModuleChanges()
+ {
+ // When there are no libraries to change return
+ if (_libraries_changed.empty())
+ return;
+
+ // Wait some time after changes to catch bulk changes
+ if (GetMSTimeDiffToNow(_last_time_library_changed) < 500)
+ return;
+
+ for (auto const& path : _libraries_changed)
+ {
+ bool const is_running =
+ _running_script_module_names.find(path) != _running_script_module_names.end();
+
+ bool const exists = fs::exists(path);
+
+ if (is_running)
+ {
+ if (exists)
+ ProcessReloadScriptModule(path);
+ else
+ ProcessUnloadScriptModule(path);
+ }
+ else if (exists)
+ ProcessLoadScriptModule(path);
+ }
+
+ _libraries_changed.clear();
+ }
+
+ void ProcessLoadScriptModule(fs::path const& path, bool swap_context = true)
+ {
+ ASSERT(_running_script_module_names.find(path) == _running_script_module_names.end(),
+ "Can't load a module which is running already!");
+
+ // Create the cache path and increment the library counter to use an unique name for each library
+ fs::path cache_path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory());
+ cache_path /= ".cache";
+ cache_path /= Trinity::StringFormat("%s.%u%s",
+ path.stem().generic_string().c_str(),
+ _unique_library_name_counter++,
+ path.extension().generic_string().c_str());
+
+ if ([&]
+ {
+ boost::system::error_code code;
+ fs::copy_file(path, cache_path, fs::copy_option::fail_if_exists, code);
+ return code;
+ }())
+ {
+ TC_LOG_FATAL("scripts.hotswap", ">> Failed to create cache entry for module \"%s\"!",
+ path.filename().generic_string().c_str());
+
+ // Find a better solution for this but it's much better
+ // to start the core without scripts
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ ABORT();
+ return;
+ }
+
+ auto module = ScriptModule::CreateFromPath(cache_path);
+ if (!module)
+ {
+ TC_LOG_FATAL("scripts.hotswap", ">> Failed to load script module \"%s\"!",
+ path.filename().generic_string().c_str());
+
+ // Find a better solution for this but it's much better
+ // to start the core without scripts
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ ABORT();
+ return;
+ }
+
+ // Limit the git revision hash to 7 characters.
+ std::string module_revision((*module)->GetScriptModuleRevisionHash());
+ if (module_revision.size() >= 7)
+ module_revision = module_revision.substr(0, 7);
+
+ std::string const module_name = (*module)->GetScriptModule();
+ TC_LOG_INFO("scripts.hotswap", ">> Loaded script module \"%s\" (\"%s\" - %s).",
+ path.filename().generic_string().c_str(), module_name.c_str(), module_revision.c_str());
+
+ if (module_revision.empty())
+ {
+ TC_LOG_WARN("scripts.hotswap", ">> Script module \"%s\" has an empty revision hash!",
+ path.filename().generic_string().c_str());
+ }
+ else
+ {
+ // Trim the revision hash
+ std::string my_revision_hash = GitRevision::GetHash();
+ std::size_t const trim = std::min(module_revision.size(), my_revision_hash.size());
+ my_revision_hash = my_revision_hash.substr(0, trim);
+ module_revision = module_revision.substr(0, trim);
+
+ if (my_revision_hash != module_revision)
+ {
+ TC_LOG_WARN("scripts.hotswap", ">> Script module \"%s\" has a different revision hash! "
+ "Binary incompatibility could lead to unknown behaviour!", path.filename().generic_string().c_str());
+ }
+ }
+
+ {
+ auto const itr = _running_script_modules.find(module_name);
+ if (itr != _running_script_modules.end())
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Attempt to load a module twice \"%s\" (loaded module is at %s)!",
+ path.generic_string().c_str(), itr->second.first->GetModulePath().generic_string().c_str());
+
+ return;
+ }
+ }
+
+ sScriptMgr->SetScriptContext(module_name);
+ (*module)->AddScripts();
+ TC_LOG_TRACE("scripts.hotswap", ">> Registered all scripts of module %s.", module_name.c_str());
+
+ if (swap_context)
+ sScriptMgr->SwapScriptContext();
+
+ // Create the source listener
+ auto listener = Trinity::make_unique<SourceUpdateListener>(
+ sScriptReloadMgr->GetSourceDirectory() / module_name,
+ module_name);
+
+ // Store the module
+ _known_modules_build_directives.insert(std::make_pair(module_name, (*module)->GetBuildDirective()));
+ _running_script_modules.insert(std::make_pair(module_name,
+ std::make_pair(std::move(*module), std::move(listener))));
+ _running_script_module_names.insert(std::make_pair(path, module_name));
+ }
+
+ void ProcessReloadScriptModule(fs::path const& path)
+ {
+ ProcessUnloadScriptModule(path, false);
+ ProcessLoadScriptModule(path);
+ }
+
+ void ProcessUnloadScriptModule(fs::path const& path, bool finish = true)
+ {
+ auto const itr = _running_script_module_names.find(path);
+
+ ASSERT(itr != _running_script_module_names.end(),
+ "Can't unload a module which isn't running!");
+
+ // Unload the script context
+ sScriptMgr->ReleaseScriptContext(itr->second);
+
+ if (finish)
+ sScriptMgr->SwapScriptContext();
+
+ TC_LOG_INFO("scripts.hotswap", "Released script module \"%s\" (\"%s\")...",
+ path.filename().generic_string().c_str(), itr->second.c_str());
+
+ // Unload the script module
+ auto ref = _running_script_modules.find(itr->second);
+ ASSERT(ref != _running_script_modules.end() &&
+ "Expected the script reference to be present!");
+
+ // Yield a message when there are other owning references to
+ // the module which prevents it from unloading.
+ // The module will be unloaded once all scripts provided from the module
+ // are destroyed.
+ if (!ref->second.first.unique())
+ {
+ TC_LOG_INFO("scripts.hotswap",
+ "Script module %s is still used by %lu spell, aura or instance scripts. "
+ "Will lazy unload the module once all scripts stopped using it, "
+ "to use the latest version of an edited script unbind yourself from "
+ "the instance or re-cast the spell.",
+ ref->second.first->GetScriptModule(), ref->second.first.use_count() - 1);
+ }
+
+ // Remove the owning reference from the reloader
+ _running_script_modules.erase(ref);
+ _running_script_module_names.erase(itr);
+ }
+
+ /// Called periodically on the worldserver tick to process all recompile
+ /// requests. This method invokes one build or install job at the time
+ void DispatchRunningBuildJobs()
+ {
+ if (_build_job)
+ {
+ // Terminate the current build job when an associated source was changed
+ // while compiling and the terminate early option is enabled.
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED))
+ {
+ if (!terminate_early && _sources_changed.find(_build_job->GetModuleName()) != _sources_changed.end())
+ {
+ /*
+ FIXME: Currently crashes the server
+ TC_LOG_INFO("scripts.hotswap", "Terminating the running build of module \"%s\"...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->GetProcess()->Terminate();
+ _build_job.reset();
+
+ // Continue with the default execution path
+ DispatchRunningBuildJobs();
+ return;
+ */
+
+ terminate_early = true;
+ return;
+ }
+ }
+
+ // Wait for the current build job to finish, if the job finishes in time
+ // evaluate it and continue with the next one.
+ if (_build_job->GetProcess()->GetFutureResult().
+ wait_for(std::chrono::seconds(0)) == std::future_status::ready)
+ ProcessReadyBuildJob();
+ else
+ return; // Return when the job didn't finish in time
+
+ // Skip this cycle when the previous job scheduled a new one
+ if (_build_job)
+ return;
+ }
+
+ // Avoid burst updates through waiting for a short time after changes
+ if ((_last_time_sources_changed != 0) &&
+ (GetMSTimeDiffToNow(_last_time_sources_changed) < 500))
+ return;
+
+ // If the changed sources are empty do nothing
+ if (_sources_changed.empty())
+ return;
+
+ // Wait until are attached debugger were detached.
+ if (IsDebuggerBlockingRebuild())
+ {
+ if ((_last_time_user_informed == 0) ||
+ (GetMSTimeDiffToNow(_last_time_user_informed) > 7500))
+ {
+ _last_time_user_informed = getMSTime();
+
+ // Informs the user that the attached debugger is blocking the automatic script rebuild.
+ TC_LOG_INFO("scripts.hotswap", "Your attached debugger is blocking the TrinityCore "
+ "automatic script rebuild, please detach it!");
+ }
+
+ return;
+ }
+
+ // Find all source files of a changed script module and removes
+ // it from the changed source list, invoke the build afterwards.
+ bool rebuild_buildfiles;
+ auto module_name = [&]
+ {
+ auto itr = _sources_changed.begin();
+ auto name = itr->first;
+ rebuild_buildfiles = !itr->second.empty();
+
+ if (sLog->ShouldLog("scripts.hotswap", LogLevel::LOG_LEVEL_TRACE))
+ for (auto const& entry : itr->second)
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s was %s.",
+ entry.first.generic_string().c_str(),
+ ((entry.second == ChangeStateRequest::CHANGE_REQUEST_ADDED) ?
+ "added" : "removed"));
+ }
+
+ _sources_changed.erase(itr);
+ return name;
+ }();
+
+ // Erase the added delete history all modules when we
+ // invoke a cmake rebuild since we add all
+ // added files of other modules to the build as well
+ if (rebuild_buildfiles)
+ {
+ for (auto& entry : _sources_changed)
+ entry.second.clear();
+ }
+
+ ASSERT(!module_name.empty(),
+ "The current module name is invalid!");
+
+ TC_LOG_INFO("scripts.hotswap", "Recompiling Module \"%s\"...",
+ module_name.c_str());
+
+ // Calculate the project name of the script module
+ auto project_name = CalculateScriptModuleProjectName(module_name);
+
+ // Find the best build directive for the module
+ auto build_directive = [&] () -> std::string
+ {
+ auto directive = sConfigMgr->GetStringDefault("HotSwap.ReCompilerBuildType", "");
+ if (!directive.empty())
+ return directive;
+
+ auto const itr = _known_modules_build_directives.find(module_name);
+ if (itr != _known_modules_build_directives.end())
+ return itr->second;
+ else // If no build directive of the module was found use the one from the game library
+ return _BUILD_DIRECTIVE;
+ }();
+
+ // Initiate the new build job
+ _build_job = BuildJob(std::move(module_name),
+ std::move(project_name), std::move(build_directive));
+
+ // Rerun CMake when we need to recreate the build files
+ if (rebuild_buildfiles
+ && sWorld->getBoolConfig(CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED))
+ DoRerunCMake();
+ else
+ DoCompileCurrentProcessedModule();
+ }
+
+ void ProcessReadyBuildJob()
+ {
+ ASSERT(_build_job->IsValid(), "Invalid build job!");
+
+ // Retrieve the result
+ auto const error = _build_job->GetProcess()->GetFutureResult().get();
+
+ if (terminate_early)
+ {
+ _build_job.reset();
+ terminate_early = false;
+ return;
+ }
+
+ switch (_build_job->GetType())
+ {
+ case BuildJobType::BUILD_JOB_RERUN_CMAKE:
+ {
+ if (!error)
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Successfully updated the build files!");
+ }
+ else
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Failed to update the build files at \"%s\", "
+ "it's possible that recently added sources are not included "
+ "in your next builds, rerun CMake manually.",
+ BuiltInConfig::GetBuildDirectory().c_str());
+ }
+ // Continue with building the changes sources
+ DoCompileCurrentProcessedModule();
+ return;
+ }
+ case BuildJobType::BUILD_JOB_COMPILE:
+ {
+ if (!error) // Build was successful
+ {
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_INSTALL_ENABLED))
+ {
+ // Continue with the installation when it's enabled
+ TC_LOG_INFO("scripts.hotswap",
+ ">> Successfully build module %s, continue with installing...",
+ _build_job->GetModuleName().c_str());
+
+ DoInstallCurrentProcessedModule();
+ return;
+ }
+
+ // Skip the installation because it's disabled in config
+ TC_LOG_INFO("scripts.hotswap",
+ ">> Successfully build module %s, skipped the installation.",
+ _build_job->GetModuleName().c_str());
+ }
+ else // Build wasn't successful
+ {
+ TC_LOG_ERROR("scripts.hotswap",
+ ">> The build of module %s failed! See the log for details.",
+ _build_job->GetModuleName().c_str());
+ }
+ break;
+ }
+ case BuildJobType::BUILD_JOB_INSTALL:
+ {
+ if (!error)
+ {
+ // Installation was successful
+ TC_LOG_INFO("scripts.hotswap", ">> Successfully installed module %s.",
+ _build_job->GetModuleName().c_str());
+ }
+ else
+ {
+ // Installation wasn't successful
+ TC_LOG_INFO("scripts.hotswap",
+ ">> The installation of module %s failed! See the log for details.",
+ _build_job->GetModuleName().c_str());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Clear the current job
+ _build_job.reset();
+ }
+
+ /// Reruns CMake asynchronously over the build directory
+ void DoRerunCMake()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Rerunning CMake because there were sources added or removed...");
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_RERUN_CMAKE,
+ InvokeAsyncCMakeCommand(BuiltInConfig::GetBuildDirectory()));
+ }
+
+ /// Invokes a new build of the current active module job
+ void DoCompileCurrentProcessedModule()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Starting asynchronous build job for module %s...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_COMPILE,
+ InvokeAsyncCMakeCommand(
+ "--build", BuiltInConfig::GetBuildDirectory(),
+ "--target", _build_job->GetProjectName(),
+ "--config", _build_job->GetBuildDirective()));
+ }
+
+ /// Invokes a new asynchronous install of the current active module job
+ void DoInstallCurrentProcessedModule()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Starting asynchronous install job for module %s...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_INSTALL,
+ InvokeAsyncCMakeCommand(
+ "-DCOMPONENT=" + _build_job->GetProjectName(),
+ "-DBUILD_TYPE=" + _build_job->GetBuildDirective(),
+ "-P", fs::absolute("cmake_install.cmake",
+ BuiltInConfig::GetBuildDirectory()).generic_string()));
+ }
+
+ /// Sets the CMAKE_INSTALL_PREFIX variable in the CMake cache
+ /// to point to the current worldserver position,
+ /// since most users will forget this.
+ void DoCMakePrefixCorrectionIfNeeded()
+ {
+ TC_LOG_INFO("scripts.hotswap", "Correcting your CMAKE_INSTALL_PREFIX in \"%s\"...",
+ BuiltInConfig::GetBuildDirectory().c_str());
+
+ auto const cmake_cache_path = fs::absolute("CMakeCache.txt",
+ BuiltInConfig::GetBuildDirectory());
+
+ // Stop when the CMakeCache wasn't found
+ if (![&]
+ {
+ boost::system::error_code error;
+ if (!fs::exists(cmake_cache_path, error))
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> CMake cache \"%s\" doesn't exist, "
+ "set the \"BuildDirectory\" option in your worldserver.conf to point"
+ "to your build directory!",
+ cmake_cache_path.generic_string().c_str());
+
+ return false;
+ }
+ else
+ return true;
+ }())
+ return;
+
+ TC_LOG_TRACE("scripts.hotswap", "Checking CMake cache (\"%s\") "
+ "for the correct CMAKE_INSTALL_PREFIX location...",
+ cmake_cache_path.generic_string().c_str());
+
+ std::string cmake_cache_content;
+ {
+ std::ifstream in(cmake_cache_path.generic_string());
+ if (!in.is_open())
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Failed to read the CMake cache at \"%s\"!",
+ cmake_cache_path.generic_string().c_str());
+
+ return;
+ }
+
+ std::ostringstream ss;
+ ss << in.rdbuf();
+ cmake_cache_content = ss.str();
+
+ in.close();
+ }
+
+ static std::string const prefix_key = "CMAKE_INSTALL_PREFIX:PATH=";
+
+ // Extract the value of CMAKE_INSTALL_PREFIX
+ auto begin = cmake_cache_content.find(prefix_key);
+ if (begin != std::string::npos)
+ {
+ begin += prefix_key.length();
+ auto const end = cmake_cache_content.find("\n", begin);
+ if (end != std::string::npos)
+ {
+ fs::path value = cmake_cache_content.substr(begin, end - begin);
+
+ auto current_path = fs::current_path();
+
+ #ifndef _WIN32
+ // The worldserver location is ${CMAKE_INSTALL_PREFIX}/bin
+ // on all other platforms then windows
+ current_path = current_path.remove_leaf();
+ #endif
+
+ if (value != current_path)
+ {
+ // Prevent correction of the install prefix
+ // when we are starting the core from inside the build tree
+ bool const is_in_path = [&]
+ {
+ fs::path base = BuiltInConfig::GetBuildDirectory();
+ fs::path branch = value;
+ while (!branch.empty())
+ {
+ if (base == branch)
+ return true;
+
+ branch = branch.remove_leaf();
+ }
+
+ return false;
+ }();
+
+ if (is_in_path)
+ return;
+
+ TC_LOG_INFO("scripts.hotswap", ">> Found outdated CMAKE_INSTALL_PREFIX (\"%s\"), "
+ "worldserver is currently installed at %s...",
+ value.generic_string().c_str(), current_path.generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> CMAKE_INSTALL_PREFIX is equal to the current path of execution.");
+ return;
+ }
+ }
+ }
+
+ TC_LOG_INFO("scripts.hotswap", "Invoking CMake cache correction...");
+
+ auto const error = InvokeCMakeCommand(
+ "-DCMAKE_INSTALL_PREFIX:PATH=" + fs::current_path().generic_string(),
+ BuiltInConfig::GetBuildDirectory());
+
+ if (error)
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Failed to update the CMAKE_INSTALL_PREFIX! "
+ "This could lead to unexpected behaviour!");
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Successfully corrected your CMAKE_INSTALL_PREFIX variable"
+ "to point at your current path of execution.");
+ }
+ }
+
+ // File watcher instance and watcher ID's
+ efsw::FileWatcher _fileWatcher;
+ efsw::WatchID _libraryWatcher;
+
+ // Unique library name counter which is used to
+ // generate unique names for every shared library version.
+ uint32 _unique_library_name_counter;
+
+ // Queue which is used for thread safe message processing
+ MPSCQueue<ScriptReloaderMessage> _messages;
+
+ // Change requests to load or unload shared libraries
+ std::unordered_set<fs::path /*path*/> _libraries_changed;
+ // The timestamp which indicates the last time a library was changed
+ uint32 _last_time_library_changed;
+
+ // Contains all running script modules
+ // The associated shared libraries are unloaded immediately
+ // on loosing ownership through RAII.
+ std::unordered_map<std::string /*module name*/,
+ std::pair<std::shared_ptr<ScriptModule>, std::unique_ptr<SourceUpdateListener>>
+ > _running_script_modules;
+ // Container which maps the path of a shared library to it's module name
+ std::unordered_map<fs::path, std::string /*module name*/> _running_script_module_names;
+ // Container which maps the module name to it's last known build directive
+ std::unordered_map<std::string /*module name*/, std::string /*build directive*/> _known_modules_build_directives;
+
+ // Modules which were changed and are queued for recompilation
+ std::unordered_map<std::string /*module*/,
+ std::unordered_map<fs::path /*path*/, ChangeStateRequest /*state*/>> _sources_changed;
+ // Tracks the time since the last module has changed to avoid burst updates
+ uint32 _last_time_sources_changed;
+
+ // Tracks the last timestamp the user was informed about a certain repeating event.
+ uint32 _last_time_user_informed;
+
+ // Represents the current build job which is in progress
+ Optional<BuildJob> _build_job;
+
+ // Is true when the build job dispatcher should stop after
+ // the current job has finished
+ bool terminate_early;
+};
+
+/// Maps efsw actions to strings
+static char const* ActionToString(efsw::Action action)
+{
+ switch (action)
+ {
+ case efsw::Action::Add:
+ return "added";
+ case efsw::Action::Delete:
+ return "deleted";
+ case efsw::Action::Moved:
+ return "moved";
+ default:
+ return "modified";
+ }
+}
+
+void LibraryUpdateListener::handleFileAction(efsw::WatchID watchid, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename)
+{
+ // TC_LOG_TRACE("scripts.hotswap", "Library listener detected change on possible module \"%s\ (%s)".", filename.c_str(), ActionToString(action));
+
+ // Split moved actions into a delete and an add action
+ if (action == efsw::Action::Moved)
+ {
+ ASSERT(!oldFilename.empty(), "Old filename doesn't exist!");
+ handleFileAction(watchid, dir, oldFilename, efsw::Action::Delete);
+ handleFileAction(watchid, dir, filename, efsw::Action::Add);
+ return;
+ }
+
+ sScriptReloadMgr->QueueMessage([=](HotSwapScriptReloadMgr* reloader) mutable
+ {
+ auto const path = fs::absolute(
+ filename,
+ sScriptReloadMgr->GetLibraryDirectory());
+
+ if (!HasValidScriptModuleName(filename))
+ return;
+
+ switch (action)
+ {
+ case efsw::Actions::Add:
+ TC_LOG_TRACE("scripts.hotswap", ">> Loading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ case efsw::Actions::Delete:
+ TC_LOG_TRACE("scripts.hotswap", ">> Unloading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ case efsw::Actions::Modified:
+ TC_LOG_TRACE("scripts.hotswap", ">> Reloading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ default:
+ WPAbort();
+ break;
+ }
+ });
+}
+
+/// Returns true when the given path has a known C++ file extension
+static bool HasCXXSourceFileExtension(fs::path const& path)
+{
+ static std::regex const regex("^\\.(h|hpp|c|cc|cpp)$");
+ return std::regex_match(path.extension().generic_string(), regex);
+}
+
+SourceUpdateListener::SourceUpdateListener(fs::path path, std::string script_module_name)
+ : path_(std::move(path)), script_module_name_(std::move(script_module_name)),
+ watcher_id_(sScriptReloadMgr->_fileWatcher.addWatch(path_.generic_string(), this, true))
+{
+ if (watcher_id_ >= 0)
+ {
+ TC_LOG_TRACE("scripts.hotswap", ">> Attached the source recompiler to \"%s\".",
+ path_.generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to initialize thesource recompiler on \"%s\".",
+ path_.generic_string().c_str());
+ }
+}
+
+SourceUpdateListener::~SourceUpdateListener()
+{
+ if (watcher_id_ >= 0)
+ {
+ sScriptReloadMgr->_fileWatcher.removeWatch(watcher_id_);
+
+ TC_LOG_TRACE("scripts.hotswap", ">> Detached the source recompiler from \"%s\".",
+ path_.generic_string().c_str());
+ }
+}
+
+void SourceUpdateListener::handleFileAction(efsw::WatchID watchid, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename)
+{
+ // TC_LOG_TRACE("scripts.hotswap", "Source listener detected change on possible file \"%s/%s\" (%s).", dir.c_str(), filename.c_str(), ActionToString(action));
+
+ // Skip the file change notification if the recompiler is disabled
+ if (!sWorld->getBoolConfig(CONFIG_HOTSWAP_RECOMPILER_ENABLED))
+ return;
+
+ // Split moved actions into a delete and an add action
+ if (action == efsw::Action::Moved)
+ {
+ ASSERT(!oldFilename.empty(), "Old filename doesn't exist!");
+ handleFileAction(watchid, dir, oldFilename, efsw::Action::Delete);
+ handleFileAction(watchid, dir, filename, efsw::Action::Add);
+ return;
+ }
+
+ auto const path = fs::absolute(
+ filename,
+ dir);
+
+ // Check if the file is a C/C++ source file.
+ if (!path.has_extension() || !HasCXXSourceFileExtension(path))
+ return;
+
+ /// Thread safe part
+ sScriptReloadMgr->QueueMessage([=](HotSwapScriptReloadMgr* reloader)
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Detected source change on module \"%s\", "
+ "queued for recompilation...", script_module_name_.c_str());
+
+ switch (action)
+ {
+ case efsw::Actions::Add:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was added.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueAddSourceFile(script_module_name_, path);
+ break;
+ case efsw::Actions::Delete:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was deleted.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueRemoveSourceFile(script_module_name_, path);
+ break;
+ case efsw::Actions::Modified:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was modified.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueModifySourceFile(script_module_name_, path);
+ break;
+ default:
+ WPAbort();
+ break;
+ }
+ });
+}
+
+// Returns the module reference of the given context
+std::shared_ptr<ModuleReference>
+ ScriptReloadMgr::AcquireModuleReferenceOfContext(std::string const& context)
+{
+ auto const itr = sScriptReloadMgr->_running_script_modules.find(context);
+ if (itr != sScriptReloadMgr->_running_script_modules.end())
+ return itr->second.first;
+ else
+ return { };
+}
+
+// Returns the full hot swap implemented ScriptReloadMgr
+ScriptReloadMgr* ScriptReloadMgr::instance()
+{
+ static HotSwapScriptReloadMgr instance;
+ return &instance;
+}
+
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
diff --git a/src/server/game/Scripting/ScriptReloadMgr.h b/src/server/game/Scripting/ScriptReloadMgr.h
new file mode 100644
index 00000000000..f9b388f8eb0
--- /dev/null
+++ b/src/server/game/Scripting/ScriptReloadMgr.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SCRIPT_RELOADER_H
+#define SCRIPT_RELOADER_H
+
+#include <memory>
+#include <string>
+#include "Define.h"
+#include <boost/filesystem/path.hpp>
+
+/// Represents a strong reference to a dynamic library which
+/// provides C++ scripts. As long as one reference to the library exists
+/// the library is kept loaded in the server, which makes it possible to lazy
+/// unload several script types on demand (like SpellScripts), and to
+/// provide multiple versions of the same script to the script factories.
+///
+/// Acquire a new reference through using:
+/// `ScriptReloadMgr::AcquireModuleReferenceOfContext`
+class ModuleReference
+{
+public:
+ virtual ~ModuleReference() { }
+
+ /// Returns the git revision hash of the referenced script module
+ virtual char const* GetScriptModuleRevisionHash() const = 0;
+ /// Returns the name of the referenced script module
+ virtual char const* GetScriptModule() const = 0;
+ /// Returns the path to the script module
+ virtual boost::filesystem::path const& GetModulePath() const = 0;
+};
+
+/// Provides the whole physical dynamic library unloading capability.
+/// Loads, Reloads and Unloads dynamic libraries on changes and
+/// informs the ScriptMgr about changes which were made.
+/// The ScriptReloadMgr is also responsible for watching the source directory
+/// and to invoke a build on changes.
+class TC_GAME_API ScriptReloadMgr
+{
+protected:
+ ScriptReloadMgr() { }
+
+public:
+ virtual ~ScriptReloadMgr() { }
+
+ /// Initializes the ScriptReloadMgr
+ virtual void Initialize() { }
+
+ /// Needs to be called periodically to check for updates on script modules.
+ /// Expects to be invoked in a thread safe way which means it's required that
+ /// the current thread is the only one which accesses the world data.
+ virtual void Update() { }
+
+ /// Unloads the ScriptReloadMgr
+ virtual void Unload() { }
+
+ /// Returns an owning reference to the current module of the given context
+ static std::shared_ptr<ModuleReference> AcquireModuleReferenceOfContext(
+ std::string const& context);
+
+ /// Returns the unique ScriptReloadMgr singleton instance
+ static ScriptReloadMgr* instance();
+};
+
+#define sScriptReloadMgr ScriptReloadMgr::instance()
+
+#endif // SCRIPT_RELOADER_H
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 207908c6d51..5585dc8762b 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -1325,7 +1325,7 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode
{
// apply glow vision
if (target->GetTypeId() == TYPEID_PLAYER)
- target->SetByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+ target->SetByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
target->m_invisibility.AddFlag(type);
target->m_invisibility.AddValue(type, GetAmount());
@@ -1337,7 +1337,7 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode
// if not have different invisibility auras.
// remove glow vision
if (target->GetTypeId() == TYPEID_PLAYER)
- target->RemoveByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+ target->RemoveByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
target->m_invisibility.DelFlag(type);
}
@@ -1409,7 +1409,7 @@ void AuraEffect::HandleModStealth(AuraApplication const* aurApp, uint8 mode, boo
target->SetStandFlags(UNIT_STAND_FLAGS_CREEP);
if (target->GetTypeId() == TYPEID_PLAYER)
- target->SetByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_STEALTH);
+ target->SetByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_STEALTH);
}
else
{
@@ -1421,7 +1421,7 @@ void AuraEffect::HandleModStealth(AuraApplication const* aurApp, uint8 mode, boo
target->RemoveStandFlags(UNIT_STAND_FLAGS_CREEP);
if (target->GetTypeId() == TYPEID_PLAYER)
- target->RemoveByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_STEALTH);
+ target->RemoveByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_STEALTH);
}
}
@@ -2400,7 +2400,7 @@ void AuraEffect::HandleAuraTrackStealthed(AuraApplication const* aurApp, uint8 m
if (target->HasAuraType(GetAuraType()))
return;
}
- target->ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_TRACK_STEALTHED, apply);
+ target->ApplyModByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_TRACK_STEALTHED, apply);
}
void AuraEffect::HandleAuraModStalked(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -4808,7 +4808,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
uint32 spellId = 24659;
if (apply && caster)
{
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(spellId);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(spellId);
for (uint32 i = 0; i < spell->StackAmount; ++i)
caster->CastSpell(target, spell->Id, true, NULL, NULL, GetCasterGUID());
@@ -4823,7 +4823,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
uint32 spellId = 24662;
if (apply && caster)
{
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(spellId);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(spellId);
for (uint32 i = 0; i < spell->StackAmount; ++i)
caster->CastSpell(target, spell->Id, true, NULL, NULL, GetCasterGUID());
break;
@@ -5248,7 +5248,7 @@ void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 m
if (apply)
{
- target->SetUInt16Value(PLAYER_FIELD_BYTES2, 0, overrideId);
+ target->SetUInt16Value(PLAYER_FIELD_BYTES2, PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET, overrideId);
if (OverrideSpellDataEntry const* overrideSpells = sOverrideSpellDataStore.LookupEntry(overrideId))
for (uint8 i = 0; i < MAX_OVERRIDE_SPELL; ++i)
if (uint32 spellId = overrideSpells->spellId[i])
@@ -5256,7 +5256,7 @@ void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 m
}
else
{
- target->SetUInt16Value(PLAYER_FIELD_BYTES2, 0, 0);
+ target->SetUInt16Value(PLAYER_FIELD_BYTES2, PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET, 0);
if (OverrideSpellDataEntry const* overrideSpells = sOverrideSpellDataStore.LookupEntry(overrideId))
for (uint8 i = 0; i < MAX_OVERRIDE_SPELL; ++i)
if (uint32 spellId = overrideSpells->spellId[i])
@@ -5302,9 +5302,9 @@ void AuraEffect::HandlePreventResurrection(AuraApplication const* aurApp, uint8
return;
if (apply)
- aurApp->GetTarget()->RemoveByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER);
+ aurApp->GetTarget()->RemoveByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER);
else if (!aurApp->GetTarget()->GetBaseMap()->Instanceable())
- aurApp->GetTarget()->SetByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER);
+ aurApp->GetTarget()->SetByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER);
}
void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const
@@ -5974,14 +5974,14 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
caster->CalcAbsorbResist(target, GetSpellInfo()->GetSchoolMask(), DOT, damage, &absorb, &resist, m_spellInfo);
- if (target->GetHealth() < damage)
- damage = uint32(target->GetHealth());
-
TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s health leech of %s for %u dmg inflicted by %u abs is %u",
GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId(), absorb);
caster->SendSpellNonMeleeDamageLog(target, GetId(), damage, GetSpellInfo()->GetSchoolMask(), absorb, resist, false, 0, crit);
+ if (target->GetHealth() < damage)
+ damage = uint32(target->GetHealth());
+
// Set trigger flag
uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index b2fb2766fb0..fff69ae48df 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -4831,6 +4831,10 @@ SpellCastResult Spell::CheckCast(bool strict)
m_customError = SpellCustomErrors(condInfo.mLastFailedCondition->ErrorTextId);
return SpellCastResult(condInfo.mLastFailedCondition->ErrorType);
}
+
+ if (_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR)
+ return SPELL_FAILED_DONT_REPORT;
+
if (!condInfo.mLastFailedCondition || !condInfo.mLastFailedCondition->ConditionTarget)
return SPELL_FAILED_CASTER_AURASTATE;
return SPELL_FAILED_BAD_TARGETS;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 2a9db6f0449..3f0ff9a0f19 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -1830,7 +1830,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex)
sSpellMgr->GetSetOfSpellsInSpellGroup(SPELL_GROUP_ELIXIR_BATTLE, avalibleElixirs);
for (std::set<uint32>::iterator itr = avalibleElixirs.begin(); itr != avalibleElixirs.end();)
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(*itr);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(*itr);
if (spellInfo->SpellLevel < m_spellInfo->SpellLevel || spellInfo->SpellLevel > unitTarget->getLevel())
avalibleElixirs.erase(itr++);
else if (sSpellMgr->IsSpellMemberOfSpellGroup(*itr, SPELL_GROUP_ELIXIR_SHATTRATH))
@@ -2110,8 +2110,7 @@ void Spell::EffectSummonChangeItem(SpellEffIndex effIndex)
else if (player->IsBankPos(pos))
{
ItemPosCountVec dest;
- uint8 msg = player->CanBankItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true);
- if (msg == EQUIP_ERR_OK)
+ if (player->CanBankItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true) == EQUIP_ERR_OK)
{
player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), true);
@@ -2133,7 +2132,7 @@ void Spell::EffectSummonChangeItem(SpellEffIndex effIndex)
player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), true);
- uint8 msg = player->CanEquipItem(m_CastItem->GetSlot(), dest, pNewItem, true);
+ InventoryResult msg = player->CanEquipItem(m_CastItem->GetSlot(), dest, pNewItem, true);
if (msg == EQUIP_ERR_OK || msg == EQUIP_ERR_CANT_DO_RIGHT_NOW)
{
diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp
index adf5fc47c77..4f74197fed2 100644
--- a/src/server/game/Spells/SpellHistory.cpp
+++ b/src/server/game/Spells/SpellHistory.cpp
@@ -373,7 +373,7 @@ void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /
player->SendDirectMessage(&data);
if (startCooldown)
- StartCooldown(sSpellMgr->EnsureSpellInfo(categoryItr->second->SpellId), itemId, spell);
+ StartCooldown(sSpellMgr->AssertSpellInfo(categoryItr->second->SpellId), itemId, spell);
}
WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
@@ -483,7 +483,7 @@ bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/
bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const
{
- return HasCooldown(sSpellMgr->EnsureSpellInfo(spellId), itemId, ignoreCategoryCooldown);
+ return HasCooldown(sSpellMgr->AssertSpellInfo(spellId), itemId, ignoreCategoryCooldown);
}
uint32 SpellHistory::GetRemainingCooldown(SpellInfo const* spellInfo) const
@@ -542,7 +542,7 @@ void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTim
WorldPacket spellCooldowns;
for (uint32 spellId : knownSpells)
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(spellId);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(spellId);
if (spellInfo->IsCooldownStartedOnEvent())
continue;
@@ -688,7 +688,7 @@ void SpellHistory::RestoreCooldownStateAfterDuel()
// add all profession CDs created while in duel (if any)
for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
if (spellInfo->RecoveryTime > 10 * MINUTE * IN_MILLISECONDS ||
spellInfo->CategoryRecoveryTime > 10 * MINUTE * IN_MILLISECONDS)
diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h
index 269a3a90e9d..fea7513b092 100644
--- a/src/server/game/Spells/SpellMgr.h
+++ b/src/server/game/Spells/SpellMgr.h
@@ -692,7 +692,7 @@ class TC_GAME_API SpellMgr
// SpellInfo object management
SpellInfo const* GetSpellInfo(uint32 spellId) const { return spellId < GetSpellInfoStoreSize() ? mSpellInfoMap[spellId] : NULL; }
// Use this only with 100% valid spellIds
- SpellInfo const* EnsureSpellInfo(uint32 spellId) const
+ SpellInfo const* AssertSpellInfo(uint32 spellId) const
{
ASSERT(spellId < GetSpellInfoStoreSize());
SpellInfo const* spellInfo = mSpellInfoMap[spellId];
diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp
index 6876f8fa7ef..e2598386466 100644
--- a/src/server/game/Spells/SpellScript.cpp
+++ b/src/server/game/Spells/SpellScript.cpp
@@ -16,6 +16,7 @@
*/
#include "Spell.h"
+#include "ScriptMgr.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "SpellMgr.h"
@@ -50,6 +51,12 @@ void _SpellScript::_Init(std::string const* scriptname, uint32 spellId)
m_currentScriptState = SPELL_SCRIPT_STATE_NONE;
m_scriptName = scriptname;
m_scriptSpellId = spellId;
+
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Acquire a strong reference to the binary code
+ // to keep it loaded until all spells are destroyed.
+ m_moduleReference = sScriptMgr->AcquireModuleReferenceOfScriptName(*scriptname);
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
}
std::string const* _SpellScript::_GetScriptName() const
diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h
index 1d4efd8073d..539bc54cc94 100644
--- a/src/server/game/Spells/SpellScript.h
+++ b/src/server/game/Spells/SpellScript.h
@@ -22,6 +22,7 @@
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
#include "Spell.h"
+#include "ScriptReloadMgr.h"
#include <stack>
class Unit;
@@ -105,6 +106,16 @@ class TC_GAME_API _SpellScript
uint8 m_currentScriptState;
std::string const* m_scriptName;
uint32 m_scriptSpellId;
+
+ private:
+
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+
+ // Strong reference to keep the binary code loaded
+ std::shared_ptr<ModuleReference> m_moduleReference;
+
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
+
public:
//
// SpellScript/AuraScript interface base
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 6380aece941..4b64ef0bbd8 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -54,6 +54,7 @@
#include "PoolMgr.h"
#include "GitRevision.h"
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "SkillDiscovery.h"
#include "SkillExtraItems.h"
#include "SmartAI.h"
@@ -1319,6 +1320,14 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Creature.Zone.Area.Data", false);
m_bool_configs[CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Gameoject.Zone.Area.Data", false);
+ // HotSwap
+ m_bool_configs[CONFIG_HOTSWAP_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.Enabled", true);
+ m_bool_configs[CONFIG_HOTSWAP_RECOMPILER_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableReCompiler", true);
+ m_bool_configs[CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableEarlyTermination", true);
+ m_bool_configs[CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableBuildFileRecreation", true);
+ m_bool_configs[CONFIG_HOTSWAP_INSTALL_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableInstall", true);
+ m_bool_configs[CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnablePrefixCorrection", true);
+
// call ScriptMgr if we're reloading the configuration
if (reload)
sScriptMgr->OnConfigLoad(reload);
@@ -1826,6 +1835,8 @@ void World::SetInitialWorldSettings()
m_timers[WUPDATE_PINGDB].SetInterval(getIntConfig(CONFIG_DB_PING_INTERVAL)*MINUTE*IN_MILLISECONDS); // Mysql ping time in minutes
+ m_timers[WUPDATE_CHECK_FILECHANGES].SetInterval(500);
+
//to set mailtimer to return mails every day between 4 and 5 am
//mailtimer is increased when updating auctions
//one second is 1000 -(tested on win system)
@@ -2109,6 +2120,13 @@ void World::Update(uint32 diff)
m_timers[WUPDATE_AHBOT].Reset();
}
+ /// <li> Handle file changes
+ if (m_timers[WUPDATE_CHECK_FILECHANGES].Passed())
+ {
+ sScriptReloadMgr->Update();
+ m_timers[WUPDATE_CHECK_FILECHANGES].Reset();
+ }
+
/// <li> Handle session updates when the timer has passed
ResetTimeDiffRecord();
UpdateSessions(diff);
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index a59d9ef882a..abc0ea452ac 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -81,6 +81,7 @@ enum WorldTimers
WUPDATE_DELETECHARS,
WUPDATE_AHBOT,
WUPDATE_PINGDB,
+ WUPDATE_CHECK_FILECHANGES,
WUPDATE_COUNT
};
@@ -167,6 +168,12 @@ enum WorldBoolConfigs
CONFIG_RESET_DUEL_HEALTH_MANA,
CONFIG_BASEMAP_LOAD_GRIDS,
CONFIG_INSTANCEMAP_LOAD_GRIDS,
+ CONFIG_HOTSWAP_ENABLED,
+ CONFIG_HOTSWAP_RECOMPILER_ENABLED,
+ CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED,
+ CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED,
+ CONFIG_HOTSWAP_INSTALL_ENABLED,
+ CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED,
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index d12c44c447c..31ba073e77d 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -8,69 +8,232 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# Enable precompiled headers when using the GCC compiler.
-
-message(STATUS "SCRIPT PREPARATIONS")
-
-macro(PrepareScripts name out)
- file(GLOB_RECURSE found
- ${name}/*.h
- ${name}/*.cpp
- )
- list(APPEND ${out} ${found})
- message(STATUS " -> Prepared: ${name}")
-endmacro(PrepareScripts)
-
-PrepareScripts(Spells PRIVATE_SOURCES)
-PrepareScripts(Commands PRIVATE_SOURCES)
-
-if(SCRIPTS)
- PrepareScripts(Custom PRIVATE_SOURCES)
- PrepareScripts(World PRIVATE_SOURCES)
- PrepareScripts(OutdoorPvP PRIVATE_SOURCES)
- PrepareScripts(EasternKingdoms PRIVATE_SOURCES)
- PrepareScripts(Kalimdor PRIVATE_SOURCES)
- PrepareScripts(Outland PRIVATE_SOURCES)
- PrepareScripts(Northrend PRIVATE_SOURCES)
- PrepareScripts(Events PRIVATE_SOURCES)
- PrepareScripts(Pet PRIVATE_SOURCES)
+message("")
+
+# Make the script module list available in the current scope
+GetScriptModuleList(SCRIPT_MODULE_LIST)
+
+# Make the native install offset available in this scope
+GetInstallOffset(INSTALL_OFFSET)
+
+# Sets the SCRIPTS_${SCRIPT_MODULE} variables
+# when using predefined templates for script building
+# like dynamic, static, minimal-static...
+# Sets SCRIPTS_DEFAULT_LINKAGE
+if (SCRIPTS MATCHES "dynamic")
+ set(SCRIPTS_DEFAULT_LINKAGE "dynamic")
+elseif(SCRIPTS MATCHES "static")
+ set(SCRIPTS_DEFAULT_LINKAGE "static")
+else()
+ set(SCRIPTS_DEFAULT_LINKAGE "disabled")
+endif()
+# Sets SCRIPTS_USE_WHITELIST
+# Sets SCRIPTS_WHITELIST
+if (SCRIPTS MATCHES "minimal")
+ set(SCRIPTS_USE_WHITELIST ON)
+ # Whitelist which is used when minimal is selected
+ list(APPEND SCRIPTS_WHITELIST Commands Spells)
endif()
-message(STATUS "SCRIPT PREPARATION COMPLETE")
-message("")
+# Set the SCRIPTS_${SCRIPT_MODULE} variables from the
+# variables set above
+foreach(SCRIPT_MODULE ${SCRIPT_MODULE_LIST})
+ ScriptModuleNameToVariable(${SCRIPT_MODULE} SCRIPT_MODULE_VARIABLE)
+ if (${SCRIPT_MODULE_VARIABLE} STREQUAL "default")
+ if(SCRIPTS_USE_WHITELIST)
+ list(FIND SCRIPTS_WHITELIST "${SCRIPT_MODULE}" INDEX)
+ if (${INDEX} GREATER -1)
+ set(${SCRIPT_MODULE_VARIABLE} ${SCRIPTS_DEFAULT_LINKAGE})
+ else()
+ set(${SCRIPT_MODULE_VARIABLE} "disabled")
+ endif()
+ else()
+ set(${SCRIPT_MODULE_VARIABLE} ${SCRIPTS_DEFAULT_LINKAGE})
+ endif()
+ endif()
+ # Build the Graph values
+ if (${SCRIPT_MODULE_VARIABLE} MATCHES "dynamic")
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ GetNativeSharedLibraryName(${SCRIPT_MODULE_PROJECT_NAME} SCRIPT_PROJECT_LIBRARY)
+ list(APPEND GRAPH_KEYS ${SCRIPT_MODULE_PROJECT_NAME})
+ set(GRAPH_VALUE_DISPLAY_${SCRIPT_MODULE_PROJECT_NAME} ${SCRIPT_PROJECT_LIBRARY})
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_${SCRIPT_MODULE_PROJECT_NAME} ${SCRIPT_MODULE})
+ elseif(${SCRIPT_MODULE_VARIABLE} MATCHES "static")
+ list(APPEND GRAPH_KEYS worldserver)
+ set(GRAPH_VALUE_DISPLAY_worldserver worldserver)
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_worldserver ${SCRIPT_MODULE})
+ else()
+ list(APPEND GRAPH_KEYS disabled)
+ set(GRAPH_VALUE_DISPLAY_disabled disabled)
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_disabled ${SCRIPT_MODULE})
+ endif()
+endforeach()
-list(APPEND PRIVATE_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.h
- ${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.cpp)
+list(SORT GRAPH_KEYS)
+list(REMOVE_DUPLICATES GRAPH_KEYS)
+# Display the script graph
+message("* Script configuration (${SCRIPTS}):
+ |")
+
+foreach(GRAPH_KEY ${GRAPH_KEYS})
+ if (NOT GRAPH_KEY STREQUAL "disabled")
+ message(" +- ${GRAPH_VALUE_DISPLAY_${GRAPH_KEY}}")
+ else()
+ message(" | ${GRAPH_VALUE_DISPLAY_${GRAPH_KEY}}")
+ endif()
+ foreach(GRAPH_PROJECT_ENTRY ${GRAPH_VALUE_CONTAINS_MODULES_${GRAPH_KEY}})
+ message(" | +- ${GRAPH_PROJECT_ENTRY}")
+ endforeach()
+ message(" |")
+endforeach()
+
+# Base sources which are used by every script project
if (USE_SCRIPTPCH)
- set(PRIVATE_PCH_HEADER PrecompiledHeaders/ScriptPCH.h)
- set(PRIVATE_PCH_SOURCE PrecompiledHeaders/ScriptPCH.cpp)
+ set(PRIVATE_PCH_HEADER ScriptPCH.h)
+ set(PRIVATE_PCH_SOURCE ScriptPCH.cpp)
endif ()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+# Configures the scriptloader with the given name and stores the output in the LOADER_OUT variable.
+# It is possible to expose multiple subdirectories from the same scriptloader through passing
+# it to the variable arguments
+function(ConfigureScriptLoader SCRIPTLOADER_NAME LOADER_OUT IS_DYNAMIC_SCRIPTLOADER)
+ # Deduces following variables which are referenced by thge template:
+ # TRINITY_IS_DYNAMIC_SCRIPTLOADER
+ # TRINITY_SCRIPTS_FORWARD_DECL
+ # TRINITY_SCRIPTS_INVOKE
+ # TRINITY_CURRENT_SCRIPT_PROJECT
+
+ # To generate export macros
+ set(TRINITY_IS_DYNAMIC_SCRIPTLOADER ${IS_DYNAMIC_SCRIPTLOADER})
+ # To generate forward declarations of the loading functions
+ unset(TRINITY_SCRIPTS_FORWARD_DECL)
+ unset(TRINITY_SCRIPTS_INVOKE)
+ # The current script project which is built in
+ set(TRINITY_CURRENT_SCRIPT_PROJECT ${SCRIPTLOADER_NAME})
+ foreach(LOCALE_SCRIPT_MODULE ${ARGN})
+ # Determine the loader function ("Add##${NameOfDirectory}##Scripts()")
+ set(LOADER_FUNCTION
+ "Add${LOCALE_SCRIPT_MODULE}Scripts()")
+ # Generate the funciton call and the forward declarations
+ set(TRINITY_SCRIPTS_FORWARD_DECL
+ "${TRINITY_SCRIPTS_FORWARD_DECL}void ${LOADER_FUNCTION};\n")
+ set(TRINITY_SCRIPTS_INVOKE
+ "${TRINITY_SCRIPTS_INVOKE} ${LOADER_FUNCTION};\n")
+ endforeach()
+ set(GENERATED_LOADER ${CMAKE_CURRENT_BINARY_DIR}/gen_scriptloader/${SCRIPTLOADER_NAME}/ScriptLoader.cpp)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.cpp.in.cmake ${GENERATED_LOADER})
+ set(${LOADER_OUT} ${GENERATED_LOADER} PARENT_SCOPE)
+endfunction()
+
+# Generates the actual script projects
+# Fills the STATIC_SCRIPT_MODULES and DYNAMIC_SCRIPT_MODULE_PROJECTS variables
+# which contain the names which scripts are linked statically/dynamically and
+# adds the sources of the static modules to the PRIVATE_SOURCES variable.
+foreach(SCRIPT_MODULE ${SCRIPT_MODULE_LIST})
+ GetPathToScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PATH)
+ ScriptModuleNameToVariable(${SCRIPT_MODULE} SCRIPT_MODULE_VARIABLE)
+
+ if ((${SCRIPT_MODULE_VARIABLE} STREQUAL "disabled") OR
+ (${SCRIPT_MODULE_VARIABLE} STREQUAL "static"))
+ # Uninstall disabled modules
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ GetNativeSharedLibraryName(${SCRIPT_MODULE_PROJECT_NAME} SCRIPT_MODULE_OUTPUT_NAME)
+ list(APPEND DISABLED_SCRIPT_MODULE_PROJECTS ${INSTALL_OFFSET}/${SCRIPT_MODULE_OUTPUT_NAME})
+ if (${SCRIPT_MODULE_VARIABLE} STREQUAL "static")
+ # Add the module name to STATIC_SCRIPT_MODULES
+ list(APPEND STATIC_SCRIPT_MODULES ${SCRIPT_MODULE})
+ # Add the module content to the whole static module
+ CollectSourceFiles(${SCRIPT_MODULE_PATH} PRIVATE_SOURCES)
+ endif()
+ elseif(${SCRIPT_MODULE_VARIABLE} STREQUAL "dynamic")
+ # Generate an own dynamic module which is loadable on runtime
+ # Add the module content to the whole static module
+ unset(SCRIPT_MODULE_PRIVATE_SOURCES)
+ CollectSourceFiles(${SCRIPT_MODULE_PATH} SCRIPT_MODULE_PRIVATE_SOURCES)
+ # Configure the scriptloader
+ ConfigureScriptLoader(${SCRIPT_MODULE} SCRIPT_MODULE_PRIVATE_SCRIPTLOADER ON ${SCRIPT_MODULE})
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ # Add the module name to DYNAMIC_SCRIPT_MODULES
+ list(APPEND DYNAMIC_SCRIPT_MODULE_PROJECTS ${SCRIPT_MODULE_PROJECT_NAME})
+ # Create the script module project
+ add_library(${SCRIPT_MODULE_PROJECT_NAME} SHARED
+ ${PRIVATE_PCH_SOURCE}
+ ${SCRIPT_MODULE_PRIVATE_SOURCES}
+ ${SCRIPT_MODULE_PRIVATE_SCRIPTLOADER})
+ target_link_libraries(${SCRIPT_MODULE_PROJECT_NAME}
+ PUBLIC
+ game)
+ set_target_properties(${SCRIPT_MODULE_PROJECT_NAME}
+ PROPERTIES
+ FOLDER
+ "scripts")
+
+ if(UNIX)
+ install(TARGETS ${SCRIPT_MODULE_PROJECT_NAME}
+ DESTINATION ${INSTALL_OFFSET}
+ COMPONENT ${SCRIPT_MODULE_PROJECT_NAME})
+ elseif(WIN32)
+ install(TARGETS ${SCRIPT_MODULE_PROJECT_NAME}
+ RUNTIME DESTINATION ${INSTALL_OFFSET}
+ COMPONENT ${SCRIPT_MODULE_PROJECT_NAME})
+ if(MSVC)
+ # Place the script modules in the script subdirectory
+ set_target_properties(${SCRIPT_MODULE_PROJECT_NAME} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/Debug/scripts
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/Release/scripts
+ RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/bin/RelWithDebInfo/scripts
+ RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/bin/MinSizeRel/scripts)
+ endif()
+ endif()
+ else()
+ message(FATAL_ERROR "Unknown value \"${${SCRIPT_MODULE_VARIABLE}}\"!")
+ endif()
+endforeach()
+
+# Add the dynamic script modules to the worldserver as dependency
+set(WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES ${DYNAMIC_SCRIPT_MODULE_PROJECTS} PARENT_SCOPE)
+
+ConfigureScriptLoader("static" SCRIPT_MODULE_PRIVATE_SCRIPTLOADER OFF ${STATIC_SCRIPT_MODULES})
+
add_library(scripts STATIC
- ${PRIVATE_SOURCES}
+ ScriptLoader.h
${PRIVATE_PCH_SOURCE}
-)
+ ${SCRIPT_MODULE_PRIVATE_SCRIPTLOADER}
+ ${PRIVATE_SOURCES})
-target_include_directories(scripts
+target_link_libraries(scripts
PUBLIC
- ${CMAKE_CURRENT_SOURCE_DIR}
- PRIVATE
- ${CMAKE_CURRENT_BINARY_DIR})
+ game-interface)
-target_link_libraries(scripts
+target_include_directories(scripts
PUBLIC
- game)
+ ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(scripts
- PROPERTIES
- FOLDER
- "server")
+ PROPERTIES
+ FOLDER
+ "scripts")
# Generate precompiled header
if (USE_SCRIPTPCH)
- add_cxx_pch(scripts ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
+ list(APPEND ALL_SCRIPT_PROJECTS scripts ${DYNAMIC_SCRIPT_MODULE_PROJECTS})
+ add_cxx_pch("${ALL_SCRIPT_PROJECTS}" ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
+endif()
+
+# Remove all shared libraries in the installl directory which
+# are contained in the static library already.
+if (DISABLED_SCRIPT_MODULE_PROJECTS)
+ install(CODE "
+ foreach(SCRIPT_TO_UNINSTALL ${DISABLED_SCRIPT_MODULE_PROJECTS})
+ if (EXISTS \"\${SCRIPT_TO_UNINSTALL}\")
+ message(STATUS \"Uninstalling: \${SCRIPT_TO_UNINSTALL}\")
+ file(REMOVE \"\${SCRIPT_TO_UNINSTALL}\")
+ endif()
+ endforeach()
+ ")
endif()
+
+message("")
diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp
index 6ffb92d9684..4bce2d168a9 100644
--- a/src/server/scripts/Commands/cs_account.cpp
+++ b/src/server/scripts/Commands/cs_account.cpp
@@ -121,8 +121,14 @@ public:
if (!accountName || !password)
return false;
- AccountOpResult result = sAccountMgr->CreateAccount(std::string(accountName), std::string(password), email);
- switch (result)
+ if (strchr(accountName, '@'))
+ {
+ handler->PSendSysMessage(LANG_ACCOUNT_USE_BNET_COMMANDS);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ switch (sAccountMgr->CreateAccount(std::string(accountName), std::string(password), email))
{
case AccountOpResult::AOR_OK:
handler->PSendSysMessage(LANG_ACCOUNT_CREATED, accountName);
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index 01a048c8795..1d8094885d4 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -124,6 +124,23 @@ public:
return false;
}
+ // Dump camera locations
+ if (CinematicSequencesEntry const* cineSeq = sCinematicSequencesStore.LookupEntry(id))
+ {
+ std::unordered_map<uint32, FlyByCameraCollection>::const_iterator itr = sFlyByCameraStore.find(cineSeq->cinematicCamera);
+ if (itr != sFlyByCameraStore.end())
+ {
+ handler->PSendSysMessage("Waypoints for sequence %u, camera %u", id, cineSeq->cinematicCamera);
+ uint32 count = 1 ;
+ for (FlyByCamera cam : itr->second)
+ {
+ handler->PSendSysMessage("%02u - %7ums [%f, %f, %f] Facing %f (%f degrees)", count, cam.timeStamp, cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w, cam.locations.w * (180 / M_PI));
+ count++;
+ }
+ handler->PSendSysMessage("%u waypoints dumped", itr->second.size());
+ }
+ }
+
handler->GetSession()->GetPlayer()->SendCinematicStart(id);
return true;
}
diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp
index 3a062b21c3e..861b45efa95 100644
--- a/src/server/scripts/Commands/cs_modify.cpp
+++ b/src/server/scripts/Commands/cs_modify.cpp
@@ -1339,8 +1339,8 @@ public:
}
// Set gender
- target->SetByteValue(UNIT_FIELD_BYTES_0, 2, gender);
- target->SetByteValue(PLAYER_BYTES_3, 0, gender);
+ target->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, gender);
+ target->SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, gender);
// Change display ID
target->InitDisplayIds();
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index 56b0dbf43d0..eb28a8adae4 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -251,7 +251,7 @@ public:
static bool HandleReloadAllScriptsCommand(ChatHandler* handler, const char* /*args*/)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->PSendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -393,7 +393,7 @@ public:
static bool HandleReloadCommandCommand(ChatHandler* handler, const char* /*args*/)
{
- handler->SetLoadCommandTable(true);
+ ChatHandler::invalidateCommandTable();
handler->SendGlobalGMSysMessage("DB table `command` will be reloaded at next chat command use.");
return true;
}
@@ -893,7 +893,7 @@ public:
static bool HandleReloadEventScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -913,7 +913,7 @@ public:
static bool HandleReloadWpScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -946,7 +946,7 @@ public:
static bool HandleReloadSpellScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp
index 05941120423..ba1dab28350 100644
--- a/src/server/scripts/Commands/cs_reset.cpp
+++ b/src/server/scripts/Commands/cs_reset.cpp
@@ -102,7 +102,7 @@ public:
player->setFactionForRace(player->getRace());
- player->SetUInt32Value(UNIT_FIELD_BYTES_0, ((player->getRace()) | (player->getClass() << 8) | (player->getGender() << 16) | (powerType << 24)));
+ player->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, powerType);
// reset only if player not in some form;
if (player->GetShapeshiftForm() == FORM_NONE)
diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
index 8c781bb9001..b794a653791 100644
--- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
+++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
@@ -89,7 +89,6 @@ void AddSC_boss_sulfuron();
void AddSC_boss_majordomo();
void AddSC_boss_ragnaros();
void AddSC_instance_molten_core();
-void AddSC_instance_ragefire_chasm(); //Ragefire Chasm
void AddSC_the_scarlet_enclave(); //Scarlet Enclave
void AddSC_the_scarlet_enclave_c1();
void AddSC_the_scarlet_enclave_c2();
@@ -268,7 +267,6 @@ void AddEasternKingdomsScripts()
AddSC_boss_majordomo();
AddSC_boss_ragnaros();
AddSC_instance_molten_core();
- AddSC_instance_ragefire_chasm(); //Ragefire Chasm
AddSC_the_scarlet_enclave(); //Scarlet Enclave
AddSC_the_scarlet_enclave_c1();
AddSC_the_scarlet_enclave_c2();
diff --git a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
index f1969a063d6..87edf4781d4 100644
--- a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
+++ b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
@@ -47,6 +47,7 @@ void AddSC_boss_meathook();
void AddSC_culling_of_stratholme();
void AddSC_instance_culling_of_stratholme();
void AddSC_instance_dire_maul(); //Dire Maul
+void AddSC_instance_ragefire_chasm(); //Ragefire Chasm
void AddSC_boss_celebras_the_cursed(); //Maraudon
void AddSC_boss_landslide();
void AddSC_boss_noxxion();
@@ -143,6 +144,7 @@ void AddKalimdorScripts()
AddSC_culling_of_stratholme();
AddSC_instance_culling_of_stratholme();
AddSC_instance_dire_maul(); //Dire Maul
+ AddSC_instance_ragefire_chasm(); //Ragefire Chasm
AddSC_boss_celebras_the_cursed(); //Maraudon
AddSC_boss_landslide();
AddSC_boss_noxxion();
diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
index 852cd62c277..b113615ca50 100644
--- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp
+++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
@@ -552,7 +552,7 @@ public:
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
{
- if (me->getStandState() == UNIT_STAND_STATE_DEAD)
+ if (me->GetStandState() == UNIT_STAND_STATE_DEAD)
me->SetStandState(UNIT_STAND_STATE_STAND);
IsPostEvent = false;
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
index bc8c7f877a9..3d21388ca11 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
@@ -809,7 +809,7 @@ class spell_blood_queen_pact_of_the_darkfallen_dmg : public SpellScriptLoader
// this is an additional effect to be executed
void PeriodicTick(AuraEffect const* aurEff)
{
- SpellInfo const* damageSpell = sSpellMgr->EnsureSpellInfo(SPELL_PACT_OF_THE_DARKFALLEN_DAMAGE);
+ SpellInfo const* damageSpell = sSpellMgr->AssertSpellInfo(SPELL_PACT_OF_THE_DARKFALLEN_DAMAGE);
int32 damage = damageSpell->Effects[EFFECT_0].CalcValue();
float multiplier = 0.3375f + 0.1f * uint32(aurEff->GetTickNumber()/10); // do not convert to 0.01f - we need tick number/10 as INT (damage increases every 10 ticks)
damage = int32(damage * multiplier);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
index 9b0693ec95e..d77842fff0a 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
@@ -1843,7 +1843,7 @@ class spell_igb_rocket_pack : public SpellScriptLoader
void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{
- SpellInfo const* damageInfo = sSpellMgr->EnsureSpellInfo(SPELL_ROCKET_PACK_DAMAGE);
+ SpellInfo const* damageInfo = sSpellMgr->AssertSpellInfo(SPELL_ROCKET_PACK_DAMAGE);
GetTarget()->CastCustomSpell(SPELL_ROCKET_PACK_DAMAGE, SPELLVALUE_BASE_POINT0, 2 * (damageInfo->Effects[EFFECT_0].CalcValue() + aurEff->GetTickNumber() * aurEff->GetAmplitude()), NULL, TRIGGERED_FULL_MASK);
GetTarget()->CastSpell(NULL, SPELL_ROCKET_BURST, TRIGGERED_FULL_MASK);
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
index f4e9d4673f2..280a0aaa13a 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
@@ -1257,7 +1257,7 @@ class spell_putricide_mutated_plague : public SpellScriptLoader
return;
uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell;
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(triggerSpell);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(triggerSpell);
spell = sSpellMgr->GetSpellForDifficultyFromSpell(spell, caster);
int32 damage = spell->Effects[EFFECT_0].CalcValue(caster);
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
index 309d5d4c62f..0714f2426bc 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
@@ -359,9 +359,9 @@ static bool IsEncounterFinished(Unit* who)
if (!mkii || !vx001 || !aerial)
return false;
- if (mkii->getStandState() == UNIT_STAND_STATE_DEAD &&
- vx001->getStandState() == UNIT_STAND_STATE_DEAD &&
- aerial->getStandState() == UNIT_STAND_STATE_DEAD)
+ if (mkii->GetStandState() == UNIT_STAND_STATE_DEAD &&
+ vx001->GetStandState() == UNIT_STAND_STATE_DEAD &&
+ aerial->GetStandState() == UNIT_STAND_STATE_DEAD)
{
who->Kill(mkii);
who->Kill(vx001);
diff --git a/src/server/scripts/ScriptLoader.cpp b/src/server/scripts/ScriptLoader.cpp
deleted file mode 100644
index 57c848d5981..00000000000
--- a/src/server/scripts/ScriptLoader.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "ScriptLoader.h"
-#include "World.h"
-
-void AddSpellsScripts();
-void AddCommandsScripts();
-
-#ifdef SCRIPTS
-void AddWorldScripts();
-void AddEasternKingdomsScripts();
-void AddKalimdorScripts();
-void AddOutlandScripts();
-void AddNorthrendScripts();
-void AddEventsScripts();
-void AddPetScripts();
-void AddOutdoorPvPScripts();
-void AddCustomScripts();
-#endif
-
-void AddScripts()
-{
- AddSpellsScripts();
- AddCommandsScripts();
-#ifdef SCRIPTS
- AddWorldScripts();
- AddEasternKingdomsScripts();
- AddKalimdorScripts();
- AddOutlandScripts();
- AddNorthrendScripts();
- AddEventsScripts();
- AddPetScripts();
- AddOutdoorPvPScripts();
- AddCustomScripts();
-#endif
-}
diff --git a/src/server/scripts/ScriptLoader.cpp.in.cmake b/src/server/scripts/ScriptLoader.cpp.in.cmake
new file mode 100644
index 00000000000..33c336a9a93
--- /dev/null
+++ b/src/server/scripts/ScriptLoader.cpp.in.cmake
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// This file was created automatically from your script configuration!
+// Use CMake to reconfigure this file, never change it on your own!
+
+#cmakedefine TRINITY_IS_DYNAMIC_SCRIPTLOADER
+
+#include "Define.h"
+#include <vector>
+#include <string>
+
+@TRINITY_SCRIPTS_FORWARD_DECL@
+#ifdef TRINITY_IS_DYNAMIC_SCRIPTLOADER
+# include "revision_data.h"
+# define TC_SCRIPT_API TC_API_EXPORT
+extern "C" {
+
+/// Exposed in script modules to return the script module revision hash.
+TC_SCRIPT_API char const* GetScriptModuleRevisionHash()
+{
+ return _HASH;
+}
+
+/// Exposed in script module to return the name of the script module
+/// contained in this shared library.
+TC_SCRIPT_API char const* GetScriptModule()
+{
+ return "@TRINITY_CURRENT_SCRIPT_PROJECT@";
+}
+
+#else
+# include "ScriptLoader.h"
+# define TC_SCRIPT_API
+#endif
+
+/// Exposed in script modules to register all scripts to the ScriptMgr.
+TC_SCRIPT_API void AddScripts()
+{
+@TRINITY_SCRIPTS_INVOKE@}
+
+/// Exposed in script modules to get the build directive of the module.
+TC_SCRIPT_API char const* GetBuildDirective()
+{
+ return _BUILD_DIRECTIVE;
+}
+
+#ifdef TRINITY_IS_DYNAMIC_SCRIPTLOADER
+} // extern "C"
+#endif
diff --git a/src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp b/src/server/scripts/ScriptPCH.cpp
index 41fecf3c60d..41fecf3c60d 100644
--- a/src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp
+++ b/src/server/scripts/ScriptPCH.cpp
diff --git a/src/server/scripts/PrecompiledHeaders/ScriptPCH.h b/src/server/scripts/ScriptPCH.h
index 1cd25309055..1cd25309055 100644
--- a/src/server/scripts/PrecompiledHeaders/ScriptPCH.h
+++ b/src/server/scripts/ScriptPCH.h
diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp
index d210eda9045..724019a1b19 100644
--- a/src/server/scripts/Spells/spell_dk.cpp
+++ b/src/server/scripts/Spells/spell_dk.cpp
@@ -226,7 +226,7 @@ class spell_dk_anti_magic_zone : public SpellScriptLoader
void CalculateAmount(AuraEffect const* /*aurEff*/, int32 & amount, bool & /*canBeRecalculated*/)
{
- SpellInfo const* talentSpell = sSpellMgr->EnsureSpellInfo(SPELL_DK_ANTI_MAGIC_SHELL_TALENT);
+ SpellInfo const* talentSpell = sSpellMgr->AssertSpellInfo(SPELL_DK_ANTI_MAGIC_SHELL_TALENT);
amount = talentSpell->Effects[EFFECT_0].CalcValue(GetCaster());
if (Player* player = GetCaster()->ToPlayer())
amount += int32(2 * player->GetTotalAttackPowerValue(BASE_ATTACK));
@@ -1634,7 +1634,7 @@ class spell_dk_will_of_the_necropolis : public SpellScriptLoader
{
// min pct of hp is stored in effect 0 of talent spell
uint8 rank = GetSpellInfo()->GetRank();
- SpellInfo const* talentProto = sSpellMgr->EnsureSpellInfo(sSpellMgr->GetSpellWithRank(SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1, rank));
+ SpellInfo const* talentProto = sSpellMgr->AssertSpellInfo(sSpellMgr->GetSpellWithRank(SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1, rank));
int32 remainingHp = int32(GetTarget()->GetHealth() - dmgInfo.GetDamage());
int32 minHp = int32(GetTarget()->CountPctFromMaxHealth(talentProto->Effects[EFFECT_0].CalcValue(GetCaster())));
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index ce729277e42..8b8c5300a9e 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -1245,7 +1245,7 @@ class spell_gen_defend : public SpellScriptLoader
void Register() override
{
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(m_scriptSpellId);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(m_scriptSpellId);
// Defend spells cast by NPCs (add visuals)
if (spell->Effects[EFFECT_0].ApplyAuraName == SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN)
@@ -2148,7 +2148,7 @@ class spell_gen_mounted_charge: public SpellScriptLoader
void Register() override
{
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(m_scriptSpellId);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(m_scriptSpellId);
if (spell->HasEffect(SPELL_EFFECT_SCRIPT_EFFECT))
OnEffectHitTarget += SpellEffectFn(spell_gen_mounted_charge_SpellScript::HandleScriptEffect, EFFECT_FIRST_FOUND, SPELL_EFFECT_SCRIPT_EFFECT);
diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp
index a3359fdf6f9..6442eb8acca 100644
--- a/src/server/scripts/Spells/spell_holiday.cpp
+++ b/src/server/scripts/Spells/spell_holiday.cpp
@@ -65,7 +65,7 @@ class spell_love_is_in_the_air_romantic_picnic : public SpellScriptLoader
Unit* caster = GetCaster();
// If our player is no longer sit, remove all auras
- if (target->getStandState() != UNIT_STAND_STATE_SIT)
+ if (target->GetStandState() != UNIT_STAND_STATE_SIT)
{
target->RemoveAura(SPELL_ROMANTIC_PICNIC_ACHIEV);
target->RemoveAura(GetAura());
@@ -84,7 +84,7 @@ class spell_love_is_in_the_air_romantic_picnic : public SpellScriptLoader
target->VisitNearbyWorldObject(INTERACTION_DISTANCE*2, searcher);
for (std::list<Player*>::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
{
- if ((*itr) != target && (*itr)->HasAura(GetId())) // && (*itr)->getStandState() == UNIT_STAND_STATE_SIT)
+ if ((*itr) != target && (*itr)->HasAura(GetId())) // && (*itr)->GetStandState() == UNIT_STAND_STATE_SIT)
{
if (caster)
{
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index 4d7cc277ff6..82d9d134445 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -763,7 +763,7 @@ class spell_hun_readiness : public SpellScriptLoader
// immediately finishes the cooldown on your other Hunter abilities except Bestial Wrath
GetCaster()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
///! If spellId in cooldown map isn't valid, the above will return a null pointer.
if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER &&
diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp
index d08968fbf55..2f4e4fa6f44 100644
--- a/src/server/scripts/Spells/spell_mage.cpp
+++ b/src/server/scripts/Spells/spell_mage.cpp
@@ -180,7 +180,7 @@ class spell_mage_cold_snap : public SpellScriptLoader
{
GetCaster()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
return spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) &&
spellInfo->Id != SPELL_MAGE_COLD_SNAP && spellInfo->GetRecoveryTime() > 0;
}, true);
@@ -405,7 +405,7 @@ class spell_mage_ignite : public SpellScriptLoader
{
PreventDefaultAction();
- SpellInfo const* igniteDot = sSpellMgr->EnsureSpellInfo(SPELL_MAGE_IGNITE);
+ SpellInfo const* igniteDot = sSpellMgr->AssertSpellInfo(SPELL_MAGE_IGNITE);
int32 pct = 8 * GetSpellInfo()->GetRank();
int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks());
diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp
index 8bd4b3eb070..d9fd36f5fd4 100644
--- a/src/server/scripts/Spells/spell_paladin.cpp
+++ b/src/server/scripts/Spells/spell_paladin.cpp
@@ -1195,7 +1195,7 @@ class spell_pal_light_s_beacon : public SpellScriptLoader
if (!procSpell)
return;
- uint32 healSpellId = procSpell->IsRankOf(sSpellMgr->EnsureSpellInfo(SPELL_PALADIN_HOLY_LIGHT)) ? SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_1 : SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_3;
+ uint32 healSpellId = procSpell->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_PALADIN_HOLY_LIGHT)) ? SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_1 : SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_3;
uint32 heal = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), aurEff->GetAmount());
Unit* beaconTarget = GetCaster();
diff --git a/src/server/scripts/Spells/spell_pet.cpp b/src/server/scripts/Spells/spell_pet.cpp
index 7393a7d3bcb..cde291cd82b 100644
--- a/src/server/scripts/Spells/spell_pet.cpp
+++ b/src/server/scripts/Spells/spell_pet.cpp
@@ -916,7 +916,7 @@ public:
if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
AddPct(mod, spellInfo->Effects[EFFECT_0].CalcValue());
}
@@ -959,7 +959,7 @@ public:
if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
mod += CalculatePct(1.0f, spellInfo->Effects[EFFECT_1].CalcValue());
}
@@ -989,7 +989,7 @@ public:
if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
mod += CalculatePct(1.0f, spellInfo->Effects[EFFECT_1].CalcValue());
}
diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp
index ecf5b7b5acf..51f03346df1 100644
--- a/src/server/scripts/Spells/spell_priest.cpp
+++ b/src/server/scripts/Spells/spell_priest.cpp
@@ -236,7 +236,7 @@ class spell_pri_glyph_of_prayer_of_healing : public SpellScriptLoader
{
PreventDefaultAction();
- SpellInfo const* triggeredSpellInfo = sSpellMgr->EnsureSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL);
+ SpellInfo const* triggeredSpellInfo = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL);
int32 heal = int32(CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks());
GetTarget()->CastCustomSpell(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL, SPELLVALUE_BASE_POINT0, heal, eventInfo.GetProcTarget(), true, NULL, aurEff);
}
diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp
index 9b577d4e140..affc4d1c26c 100644
--- a/src/server/scripts/Spells/spell_rogue.cpp
+++ b/src/server/scripts/Spells/spell_rogue.cpp
@@ -450,7 +450,7 @@ class spell_rog_preparation : public SpellScriptLoader
Unit* caster = GetCaster();
caster->GetSpellHistory()->ResetCooldowns([caster](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
if (spellInfo->SpellFamilyName != SPELLFAMILY_ROGUE)
return false;
diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp
index 7cc6fe888e9..a0a6189cbe2 100644
--- a/src/server/scripts/Spells/spell_warlock.cpp
+++ b/src/server/scripts/Spells/spell_warlock.cpp
@@ -271,7 +271,7 @@ class spell_warl_demonic_circle_summon : public SpellScriptLoader
// WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST; allowing him to cast the WARLOCK_DEMONIC_CIRCLE_TELEPORT.
// If not in range remove the WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST.
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT);
if (GetTarget()->IsWithinDist(circle, spellInfo->GetMaxRange(true)))
{
@@ -362,7 +362,7 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader
break;
case CREATURE_FAMILY_VOIDWALKER:
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER);
int32 hp = int32(targetCreature->CountPctFromMaxHealth(GetCaster()->CalculateSpellDamage(targetCreature, spellInfo, 0)));
targetCreature->CastCustomSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER, &hp, NULL, NULL, true);
//unitTarget->CastSpell(unitTarget, 54441, true);
diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp
index d224c234cb4..ea9ccc956e5 100644
--- a/src/server/scripts/Spells/spell_warrior.cpp
+++ b/src/server/scripts/Spells/spell_warrior.cpp
@@ -271,7 +271,7 @@ class spell_warr_deep_wounds : public SpellScriptLoader
{
ApplyPct(damage, 16 * GetSpellInfo()->GetRank());
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC);
uint32 ticks = uint32(spellInfo->GetDuration()) / spellInfo->Effects[EFFECT_0].Amplitude;
// Add remaining ticks to damage done
diff --git a/src/server/scripts/World/duel_reset.cpp b/src/server/scripts/World/duel_reset.cpp
index 7982a5639ec..b04f3ec0aa7 100644
--- a/src/server/scripts/World/duel_reset.cpp
+++ b/src/server/scripts/World/duel_reset.cpp
@@ -105,7 +105,7 @@ class DuelResetScript : public PlayerScript
{
SpellHistory::Clock::time_point now = SpellHistory::Clock::now();
uint32 cooldownDuration = itr->second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(itr->second.CooldownEnd - now).count() : 0;
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS
&& spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS
&& !itr->second.OnHold
@@ -119,7 +119,7 @@ class DuelResetScript : public PlayerScript
// remove cooldowns on spells that have < 10 min CD and has no onHold
player->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS
&& spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS
&& !itr->second.OnHold;
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt
index 1ab340a4f3e..e99a81a084b 100644
--- a/src/server/shared/CMakeLists.txt
+++ b/src/server/shared/CMakeLists.txt
@@ -24,8 +24,8 @@ GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_definitions(-DTRINITY_API_EXPORT_SHARED)
add_library(shared
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
CollectIncludeDirectories(
diff --git a/src/server/shared/DataStores/DBCStore.h b/src/server/shared/DataStores/DBCStore.h
index b93bbdaea12..7c2cab1e36a 100644
--- a/src/server/shared/DataStores/DBCStore.h
+++ b/src/server/shared/DataStores/DBCStore.h
@@ -25,6 +25,121 @@
#include "DatabaseWorkerPool.h"
#include "Implementation/WorldDatabase.h"
#include "DatabaseEnv.h"
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+
+ // Structures for M4 file. Source: https://wowdev.wiki
+template<typename T>
+struct M2SplineKey
+{
+ T p0;
+ T p1;
+ T p2;
+};
+
+struct M2Header
+{
+ char Magic[4]; // "MD20"
+ uint32 Version; // The version of the format.
+ uint32 lName; // Length of the model's name including the trailing \0
+ uint32 ofsName; // Offset to the name, it seems like models can get reloaded by this name.should be unique, i guess.
+ uint32 GlobalModelFlags; // 0x0001: tilt x, 0x0002: tilt y, 0x0008: add 2 fields in header, 0x0020: load .phys data (MoP+), 0x0080: has _lod .skin files (MoP?+), 0x0100: is camera related.
+ uint32 nGlobalSequences;
+ uint32 ofsGlobalSequences; // A list of timestamps.
+ uint32 nAnimations;
+ uint32 ofsAnimations; // Information about the animations in the model.
+ uint32 nAnimationLookup;
+ uint32 ofsAnimationLookup; // Mapping of global IDs to the entries in the Animation sequences block.
+ uint32 nBones; // MAX_BONES = 0x100
+ uint32 ofsBones; // Information about the bones in this model.
+ uint32 nKeyBoneLookup;
+ uint32 ofsKeyBoneLookup; // Lookup table for key skeletal bones.
+ uint32 nVertices;
+ uint32 ofsVertices; // Vertices of the model.
+ uint32 nViews; // Views (LOD) are now in .skins.
+ uint32 nSubmeshAnimations;
+ uint32 ofsSubmeshAnimations; // Submesh color and alpha animations definitions.
+ uint32 nTextures;
+ uint32 ofsTextures; // Textures of this model.
+ uint32 nTransparency;
+ uint32 ofsTransparency; // Transparency of textures.
+ uint32 nUVAnimation;
+ uint32 ofsUVAnimation;
+ uint32 nTexReplace;
+ uint32 ofsTexReplace; // Replaceable Textures.
+ uint32 nRenderFlags;
+ uint32 ofsRenderFlags; // Blending modes / render flags.
+ uint32 nBoneLookupTable;
+ uint32 ofsBoneLookupTable; // A bone lookup table.
+ uint32 nTexLookup;
+ uint32 ofsTexLookup; // The same for textures.
+ uint32 nTexUnits; // possibly removed with cata?!
+ uint32 ofsTexUnits; // And texture units. Somewhere they have to be too.
+ uint32 nTransLookup;
+ uint32 ofsTransLookup; // Everything needs its lookup. Here are the transparencies.
+ uint32 nUVAnimLookup;
+ uint32 ofsUVAnimLookup;
+ G3D::AABox BoundingBox; // min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height
+ float BoundingSphereRadius;
+ G3D::AABox CollisionBox;
+ float CollisionSphereRadius;
+ uint32 nBoundingTriangles;
+ uint32 ofsBoundingTriangles; // Our bounding volumes. Similar structure like in the old ofsViews.
+ uint32 nBoundingVertices;
+ uint32 ofsBoundingVertices;
+ uint32 nBoundingNormals;
+ uint32 ofsBoundingNormals;
+ uint32 nAttachments;
+ uint32 ofsAttachments; // Attachments are for weapons etc.
+ uint32 nAttachLookup;
+ uint32 ofsAttachLookup; // Of course with a lookup.
+ uint32 nEvents;
+ uint32 ofsEvents; // Used for playing sounds when dying and a lot else.
+ uint32 nLights;
+ uint32 ofsLights; // Lights are mainly used in loginscreens but in wands and some doodads too.
+ uint32 nCameras; // Format of Cameras changed with version 271!
+ uint32 ofsCameras; // The cameras are present in most models for having a model in the Character-Tab.
+ uint32 nCameraLookup;
+ uint32 ofsCameraLookup; // And lookup-time again.
+ uint32 nRibbonEmitters;
+ uint32 ofsRibbonEmitters; // Things swirling around. See the CoT-entrance for light-trails.
+ uint32 nParticleEmitters;
+ uint32 ofsParticleEmitters; // Spells and weapons, doodads and loginscreens use them. Blood dripping of a blade? Particles.
+ uint32 nBlendMaps; // This has to deal with blending. Exists IFF (flags & 0x8) != 0. When set, textures blending is overriden by the associated array. See M2/WotLK#Blend_mode_overrides
+ uint32 ofsBlendMaps; // Same as above. Points to an array of uint16 of nBlendMaps entries -- From WoD information.};
+};
+
+struct M2Array
+{
+ uint32_t number;
+ uint32 offset_elements;
+};
+struct M2Track
+{
+ uint16_t interpolation_type;
+ uint16_t global_sequence;
+ M2Array timestamps;
+ M2Array values;
+};
+
+struct M2Camera
+{
+ uint32_t type; // 0: portrait, 1: characterinfo; -1: else (flyby etc.); referenced backwards in the lookup table.
+ float fov; // No radians, no degrees. Multiply by 35 to get degrees.
+ float far_clip;
+ float near_clip;
+ M2Track positions; // How the camera's position moves. Should be 3*3 floats.
+ G3D::Vector3 position_base;
+ M2Track target_positions; // How the target moves. Should be 3*3 floats.
+ G3D::Vector3 target_position_base;
+ M2Track rolldata; // The camera can have some roll-effect. Its 0 to 2*Pi.
+};
+
+struct FlyByCamera
+{
+ uint32 timeStamp;
+ G3D::Vector4 locations;
+};
struct SqlDbc
{
diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h
index a779075e2ed..d24a91ed458 100644
--- a/src/server/shared/Packets/ByteBuffer.h
+++ b/src/server/shared/Packets/ByteBuffer.h
@@ -23,16 +23,7 @@
#include "Errors.h"
#include "ByteConverter.h"
#include "Util.h"
-
-#include <exception>
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
#include <cstring>
-#include <time.h>
-#include <cmath>
-#include <type_traits>
class MessageBuffer;
diff --git a/src/server/shared/Realm/Realm.cpp b/src/server/shared/Realm/Realm.cpp
index 0c8f4d1d492..11c52f281a9 100644
--- a/src/server/shared/Realm/Realm.cpp
+++ b/src/server/shared/Realm/Realm.cpp
@@ -16,3 +16,38 @@
*/
#include "Realm.h"
+
+ip::tcp::endpoint Realm::GetAddressForClient(ip::address const& clientAddr) const
+{
+ ip::address realmIp;
+
+ // Attempt to send best address for client
+ if (clientAddr.is_loopback())
+ {
+ // Try guessing if realm is also connected locally
+ if (LocalAddress.is_loopback() || ExternalAddress.is_loopback())
+ realmIp = clientAddr;
+ else
+ {
+ // Assume that user connecting from the machine that bnetserver is located on
+ // has all realms available in his local network
+ realmIp = LocalAddress;
+ }
+ }
+ else
+ {
+ if (clientAddr.is_v4() &&
+ (clientAddr.to_v4().to_ulong() & LocalSubnetMask.to_v4().to_ulong()) ==
+ (LocalAddress.to_v4().to_ulong() & LocalSubnetMask.to_v4().to_ulong()))
+ {
+ realmIp = LocalAddress;
+ }
+ else
+ realmIp = ExternalAddress;
+ }
+
+ ip::tcp::endpoint endpoint(realmIp, Port);
+
+ // Return external IP
+ return endpoint;
+}
diff --git a/src/server/shared/Realm/Realm.h b/src/server/shared/Realm/Realm.h
index 83a344dd817..241ccd2bca8 100644
--- a/src/server/shared/Realm/Realm.h
+++ b/src/server/shared/Realm/Realm.h
@@ -37,7 +37,7 @@ enum RealmFlags
REALM_FLAG_FULL = 0x80
};
-struct RealmHandle
+struct TC_SHARED_API RealmHandle
{
RealmHandle() : Realm(0) { }
RealmHandle(uint32 index) : Realm(index) { }
@@ -53,20 +53,20 @@ struct RealmHandle
/// Type of server, this is values from second column of Cfg_Configs.dbc
enum RealmType
{
- REALM_TYPE_NORMAL = 0,
- REALM_TYPE_PVP = 1,
- REALM_TYPE_NORMAL2 = 4,
- REALM_TYPE_RP = 6,
- REALM_TYPE_RPPVP = 8,
+ REALM_TYPE_NORMAL = 0,
+ REALM_TYPE_PVP = 1,
+ REALM_TYPE_NORMAL2 = 4,
+ REALM_TYPE_RP = 6,
+ REALM_TYPE_RPPVP = 8,
- MAX_CLIENT_REALM_TYPE = 14,
+ MAX_CLIENT_REALM_TYPE = 14,
- REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries
- // replaced by REALM_PVP in realm list
+ REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries
+ // replaced by REALM_PVP in realm list
};
// Storage object for a realm
-struct Realm
+struct TC_SHARED_API Realm
{
RealmHandle Id;
uint32 Build;
@@ -75,11 +75,13 @@ struct Realm
ip::address LocalSubnetMask;
uint16 Port;
std::string Name;
- uint8 Type; // icon
+ uint8 Type;
RealmFlags Flags;
uint8 Timezone;
AccountTypes AllowedSecurityLevel;
float PopulationLevel;
+
+ ip::tcp::endpoint GetAddressForClient(ip::address const& clientAddr) const;
};
#endif // Realm_h__
diff --git a/src/server/authserver/Realms/RealmList.cpp b/src/server/shared/Realm/RealmList.cpp
index f1b25d8554d..e941800cd76 100644
--- a/src/server/authserver/Realms/RealmList.cpp
+++ b/src/server/shared/Realm/RealmList.cpp
@@ -16,20 +16,25 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "Common.h"
-#include "Database/DatabaseEnv.h"
#include "RealmList.h"
-#include <boost/asio/ip/tcp.hpp>
+#include "Database/DatabaseEnv.h"
+#include "Util.h"
-namespace boost { namespace asio { namespace ip { class address; } } }
+RealmList::RealmList() : _updateInterval(0), _updateTimer(nullptr), _resolver(nullptr)
+{
+}
-RealmList::RealmList() : _updateInterval(0), _updateTimer(nullptr), _resolver(nullptr) { }
RealmList::~RealmList()
{
- delete _resolver;
delete _updateTimer;
}
+RealmList* RealmList::Instance()
+{
+ static RealmList instance;
+ return &instance;
+}
+
// Load the realm list from the database
void RealmList::Initialize(boost::asio::io_service& ioService, uint32 updateInterval)
{
@@ -38,7 +43,7 @@ void RealmList::Initialize(boost::asio::io_service& ioService, uint32 updateInte
_resolver = new boost::asio::ip::tcp::resolver(ioService);
// Get the content of the realmlist table in the database
- UpdateRealms(true, boost::system::error_code());
+ UpdateRealms(boost::system::error_code());
}
void RealmList::Close()
@@ -67,16 +72,22 @@ void RealmList::UpdateRealm(RealmHandle const& id, uint32 build, const std::stri
realm.Port = port;
}
-void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
+void RealmList::UpdateRealms(boost::system::error_code const& error)
{
if (error)
return;
- TC_LOG_INFO("server.authserver", "Updating Realm List...");
+ TC_LOG_DEBUG("server.authserver", "Updating Realm List...");
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST);
PreparedQueryResult result = LoginDatabase.Query(stmt);
+ std::map<RealmHandle, std::string> existingRealms;
+ for (auto const& p : _realms)
+ existingRealms[p.first] = p.second.Name;
+
+ _realms.clear();
+
// Circle through results and add them to the realm map
if (result)
{
@@ -95,8 +106,8 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
boost::asio::ip::tcp::resolver::iterator endPoint = _resolver->resolve(externalAddressQuery, ec);
if (endPoint == end || ec)
{
- TC_LOG_ERROR("server.authserver", "Could not resolve address %s", fields[2].GetString().c_str());
- return;
+ TC_LOG_ERROR("server.authserver", "Could not resolve address %s for realm \"%s\" id %u", fields[2].GetString().c_str(), name.c_str(), realmId);
+ continue;
}
ip::address externalAddress = (*endPoint).endpoint().address();
@@ -105,8 +116,8 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
endPoint = _resolver->resolve(localAddressQuery, ec);
if (endPoint == end || ec)
{
- TC_LOG_ERROR("server.authserver", "Could not resolve address %s", fields[3].GetString().c_str());
- return;
+ TC_LOG_ERROR("server.authserver", "Could not resolve localAddress %s for realm \"%s\" id %u", fields[3].GetString().c_str(), name.c_str(), realmId);
+ continue;
}
ip::address localAddress = (*endPoint).endpoint().address();
@@ -115,8 +126,8 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
endPoint = _resolver->resolve(localSubmaskQuery, ec);
if (endPoint == end || ec)
{
- TC_LOG_ERROR("server.authserver", "Could not resolve address %s", fields[4].GetString().c_str());
- return;
+ TC_LOG_ERROR("server.authserver", "Could not resolve localSubnetMask %s for realm \"%s\" id %u", fields[4].GetString().c_str(), name.c_str(), realmId);
+ continue;
}
ip::address localSubmask = (*endPoint).endpoint().address();
@@ -138,8 +149,12 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
UpdateRealm(id, build, name, externalAddress, localAddress, localSubmask, port, icon, flag,
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
- if (init)
+ if (!existingRealms.count(id))
TC_LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddress.to_string().c_str(), port);
+ else
+ TC_LOG_DEBUG("server.authserver", "Updating realm \"%s\" at %s:%u.", name.c_str(), externalAddress.to_string().c_str(), port);
+
+ existingRealms.erase(id);
}
catch (std::exception& ex)
{
@@ -150,10 +165,13 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
while (result->NextRow());
}
+ for (auto itr = existingRealms.begin(); itr != existingRealms.end(); ++itr)
+ TC_LOG_INFO("server.authserver", "Removed realm \"%s\".", itr->second.c_str());
+
if (_updateInterval)
{
_updateTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
- _updateTimer->async_wait(std::bind(&RealmList::UpdateRealms, this, false, std::placeholders::_1));
+ _updateTimer->async_wait(std::bind(&RealmList::UpdateRealms, this, std::placeholders::_1));
}
}
diff --git a/src/server/authserver/Realms/RealmList.h b/src/server/shared/Realm/RealmList.h
index e35975b215a..3b81337e762 100644
--- a/src/server/authserver/Realms/RealmList.h
+++ b/src/server/shared/Realm/RealmList.h
@@ -29,16 +29,12 @@
using namespace boost::asio;
/// Storage object for the list of realms on the server
-class RealmList
+class TC_SHARED_API RealmList
{
public:
typedef std::map<RealmHandle, Realm> RealmMap;
- static RealmList* instance()
- {
- static RealmList instance;
- return &instance;
- }
+ static RealmList* Instance();
~RealmList();
@@ -51,7 +47,7 @@ public:
private:
RealmList();
- void UpdateRealms(bool init, boost::system::error_code const& error);
+ void UpdateRealms(boost::system::error_code const& error);
void UpdateRealm(RealmHandle const& id, uint32 build, const std::string& name, ip::address const& address, ip::address const& localAddr,
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population);
@@ -61,5 +57,5 @@ private:
boost::asio::ip::tcp::resolver* _resolver;
};
-#define sRealmList RealmList::instance()
+#define sRealmList RealmList::Instance()
#endif
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index 7fb4c6d2b75..0de8d6054f3 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -29,8 +29,8 @@ endif()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(worldserver
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
if( NOT WIN32 )
@@ -48,6 +48,7 @@ set_target_properties(worldserver PROPERTIES LINK_FLAGS "${worldserver_LINK_FLAG
target_link_libraries(worldserver
PUBLIC
scripts
+ game
gsoap
readline)
@@ -68,6 +69,11 @@ set_target_properties(worldserver
FOLDER
"server")
+# Add all dynamic projects as dependency to the worldserver
+if (WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES)
+ add_dependencies(worldserver ${WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES})
+endif()
+
if( WIN32 )
if ( MSVC )
add_custom_command(TARGET worldserver
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp
index c047c1fafc3..2b5553b4bb0 100644
--- a/src/server/worldserver/Main.cpp
+++ b/src/server/worldserver/Main.cpp
@@ -39,6 +39,7 @@
#include "InstanceSaveMgr.h"
#include "ObjectAccessor.h"
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "ScriptLoader.h"
#include "OutdoorPvP/OutdoorPvPMgr.h"
#include "BattlegroundMgr.h"
@@ -271,6 +272,7 @@ extern int main(int argc, char** argv)
sOutdoorPvPMgr->Die();
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
sScriptMgr->Unload();
+ sScriptReloadMgr->Unload();
// set server offline
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 7c512d8ae6e..c2694c640c5 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -12,6 +12,7 @@
# SERVER LOGGING
# SERVER SETTINGS
# UPDATE SETTINGS
+# HOTSWAP SETTINGS
# WARDEN SETTINGS
# PLAYER INTERACTION
# CREATURE SETTINGS
@@ -1280,6 +1281,78 @@ Updates.CleanDeadRefMaxCount = 3
###################################################################################################
###################################################################################################
+# HOTSWAP SETTINGS
+#
+# HotSwap.Enabled (Requires compilation with DYNAMIC_LINKING=1)
+# Description: Enables dynamic script hotswapping.
+# Reloads scripts on changes.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.Enabled = 1
+
+#
+# HotSwap.ScriptDir
+# Description: Directory containing the script shared libraries (.dll/.so).
+# Example: "/usr/local/scripts"
+# Default: "scripts"
+
+HotSwap.ScriptDir = "scripts"
+
+# HotSwap.EnableReCompiler
+# Description: Enables the dynamic script recompiler.
+# Watches your script source directories and recompiles the
+# script modules on changes.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableReCompiler = 1
+
+# HotSwap.EnableEarlyTermination
+# Description: Terminate the build of a module when an associated
+# source file was changed meanwhile.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableEarlyTermination = 1
+
+# HotSwap.EnableBuildFileRecreation
+# Description: Recreate build files when sources to a module
+# were added or removed.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableBuildFileRecreation = 1
+
+#
+# HotSwap.EnableInstall
+# Description: Enables cmake install after automatic builds have finished
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableInstall = 1
+
+#
+# HotSwap.EnablePrefixCorrection
+# Description: Allows the core to automatic set the CMAKE_INSTALL_PREFIX
+# to it's current location in the filesystem.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnablePrefixCorrection = 1
+
+# HotSwap.ReCompilerBuildType
+# Description: Defines the build type of the builds invoked by the recompiler.
+# Default: "" - Built-in build type of the module is used.
+# "Release" - Release builds only
+# "Debug" - Debug builds only
+
+HotSwap.ReCompilerBuildType = ""
+
+#
+###################################################################################################
+
+###################################################################################################
# WARDEN SETTINGS
#
# Warden.Enabled
@@ -3220,7 +3293,6 @@ AuctionHouseBot.Class.Key = 1
AuctionHouseBot.Class.Misc = 5
AuctionHouseBot.Class.Glyph = 3
-
#
###################################################################################################
@@ -3551,6 +3623,7 @@ Logger.mmaps=3,Server
#Logger.rbac=3,Console Server
#Logger.scripts=3,Console Server
#Logger.scripts.ai=3,Console Server
+#Logger.scripts.hotswap=3,Console Server
#Logger.server.authserver=3,Console Server
#Logger.spells=3,Console Server
#Logger.spells.periodic=3,Console Server
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index f3a761fd437..1d84fc75d27 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -53,12 +53,13 @@ char input_path[MAX_PATH_LENGTH] = ".";
// **************************************************
enum Extract
{
- EXTRACT_MAP = 1,
- EXTRACT_DBC = 2
+ EXTRACT_MAP = 1,
+ EXTRACT_DBC = 2,
+ EXTRACT_CAMERA = 4
};
// Select data for extract
-int CONF_extract = EXTRACT_MAP | EXTRACT_DBC;
+int CONF_extract = EXTRACT_MAP | EXTRACT_DBC | EXTRACT_CAMERA;
// This option allow limit minimum height to some value (Allow save some memory)
bool CONF_allow_height_limit = true;
float CONF_use_minHeight = -500.0f;
@@ -103,7 +104,7 @@ void Usage(char* prg)
"%s -[var] [value]\n"\
"-i set input path (max %d characters)\n"\
"-o set output path (max %d characters)\n"\
- "-e extract only MAP(1)/DBC(2) - standard: both(3)\n"\
+ "-e extract only MAP(1)/DBC(2)/Camera(4) - standard: all(7)\n"\
"-f height stored as int (less map size but lost some accuracy) 1 by default\n"\
"Example: %s -f 0 -i \"c:\\games\\game\"", prg, MAX_PATH_LENGTH - 1, MAX_PATH_LENGTH - 1, prg);
exit(1);
@@ -151,7 +152,7 @@ void HandleArgs(int argc, char * arg[])
if(c + 1 < argc) // all ok
{
CONF_extract=atoi(arg[(c++) + 1]);
- if(!(CONF_extract > 0 && CONF_extract < 4))
+ if(!(CONF_extract > 0 && CONF_extract < 8))
Usage(arg[0]);
}
else
@@ -1025,6 +1026,56 @@ void ExtractDBCFiles(int locale, bool basicLocale)
printf("Extracted %u DBC files\n\n", count);
}
+void ExtractCameraFiles(int locale, bool basicLocale)
+{
+ printf("Extracting camera files...\n");
+ DBCFile camdbc("DBFilesClient\\CinematicCamera.dbc");
+
+ if (!camdbc.open())
+ {
+ printf("Unable to open CinematicCamera.dbc. Camera extract aborted.\n");
+ return;
+ }
+
+ // get camera file list from DBC
+ std::vector<std::string> camerafiles;
+ size_t cam_count = camdbc.getRecordCount();
+
+ for (uint32 i = 0; i < cam_count; ++i)
+ {
+ std::string camFile(camdbc.getRecord(i).getString(1));
+ size_t loc = camFile.find(".mdx");
+ if (loc != std::string::npos)
+ camFile.replace(loc, 4, ".m2");
+ camerafiles.push_back(std::string(camFile));
+ }
+
+ std::string path = output_path;
+ path += "/Cameras/";
+ CreateDir(path);
+ if (!basicLocale)
+ {
+ path += langs[locale];
+ path += "/";
+ CreateDir(path);
+ }
+
+ // extract M2s
+ uint32 count = 0;
+ for (std::string thisFile : camerafiles)
+ {
+ std::string filename = path;
+ filename += (thisFile.c_str() + strlen("Cameras\\"));
+
+ if (boost::filesystem::exists(filename))
+ continue;
+
+ if (ExtractFile(thisFile.c_str(), filename))
+ ++count;
+ }
+ printf("Extracted %u camera files\n", count);
+}
+
void LoadLocaleMPQFiles(int const locale)
{
std::string fileName = Trinity::StringFormat("%s/Data/%s/locale-%s.MPQ", input_path, langs[locale], langs[locale]);
@@ -1111,6 +1162,19 @@ int main(int argc, char * arg[])
return 0;
}
+ if (CONF_extract & EXTRACT_CAMERA)
+ {
+ printf("Using locale: %s\n", langs[FirstLocale]);
+
+ // Open MPQs
+ LoadLocaleMPQFiles(FirstLocale);
+ LoadCommonMPQFiles();
+
+ ExtractCameraFiles(FirstLocale, true);
+ // Close MPQs
+ CloseMPQFiles();
+ }
+
if (CONF_extract & EXTRACT_MAP)
{
printf("Using locale: %s\n", langs[FirstLocale]);