aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt91
-rw-r--r--src/common/Collision/BoundingIntervalHierarchy.cpp310
-rw-r--r--src/common/Collision/BoundingIntervalHierarchy.h400
-rw-r--r--src/common/Collision/BoundingIntervalHierarchyWrapper.h119
-rw-r--r--src/common/Collision/DynamicTree.cpp260
-rw-r--r--src/common/Collision/DynamicTree.h64
-rw-r--r--src/common/Collision/Management/IVMapManager.h100
-rw-r--r--src/common/Collision/Management/MMapFactory.cpp44
-rw-r--r--src/common/Collision/Management/MMapFactory.h51
-rw-r--r--src/common/Collision/Management/MMapManager.cpp339
-rw-r--r--src/common/Collision/Management/MMapManager.h90
-rw-r--r--src/common/Collision/Management/VMapFactory.cpp42
-rw-r--r--src/common/Collision/Management/VMapFactory.h40
-rw-r--r--src/common/Collision/Management/VMapManager2.cpp334
-rw-r--r--src/common/Collision/Management/VMapManager2.h142
-rw-r--r--src/common/Collision/Maps/MapDefines.h36
-rw-r--r--src/common/Collision/Maps/MapTree.cpp483
-rw-r--r--src/common/Collision/Maps/MapTree.h103
-rw-r--r--src/common/Collision/Maps/TileAssembler.cpp550
-rw-r--r--src/common/Collision/Maps/TileAssembler.h119
-rw-r--r--src/common/Collision/Models/GameObjectModel.cpp216
-rw-r--r--src/common/Collision/Models/GameObjectModel.h87
-rw-r--r--src/common/Collision/Models/ModelInstance.cpp222
-rw-r--r--src/common/Collision/Models/ModelInstance.h81
-rw-r--r--src/common/Collision/Models/WorldModel.cpp604
-rw-r--r--src/common/Collision/Models/WorldModel.h127
-rw-r--r--src/common/Collision/RegularGrid.h212
-rw-r--r--src/common/Collision/VMapDefinitions.h47
-rw-r--r--src/common/Collision/VMapTools.h148
-rw-r--r--src/common/Common.cpp41
-rw-r--r--src/common/Common.h179
-rw-r--r--src/common/CompilerDefs.h58
-rw-r--r--src/common/Configuration/Config.cpp114
-rw-r--r--src/common/Configuration/Config.h63
-rw-r--r--src/common/Cryptography/ARC4.cpp51
-rw-r--r--src/common/Cryptography/ARC4.h37
-rw-r--r--src/common/Cryptography/Authentication/AuthCrypt.cpp75
-rw-r--r--src/common/Cryptography/Authentication/AuthCrypt.h42
-rw-r--r--src/common/Cryptography/BigNumber.cpp201
-rw-r--r--src/common/Cryptography/BigNumber.h100
-rw-r--r--src/common/Cryptography/HMACSHA1.cpp57
-rw-r--r--src/common/Cryptography/HMACSHA1.h47
-rw-r--r--src/common/Cryptography/OpenSSLCrypto.cpp59
-rw-r--r--src/common/Cryptography/OpenSSLCrypto.h33
-rw-r--r--src/common/Cryptography/SHA1.cpp69
-rw-r--r--src/common/Cryptography/SHA1.h50
-rw-r--r--src/common/Cryptography/WardenKeyGeneration.h81
-rw-r--r--src/common/Debugging/Errors.cpp99
-rw-r--r--src/common/Debugging/Errors.h62
-rw-r--r--src/common/Debugging/WheatyExceptionReport.cpp1463
-rw-r--r--src/common/Debugging/WheatyExceptionReport.h214
-rw-r--r--src/common/Define.h109
-rw-r--r--src/common/GitRevision.cpp78
-rw-r--r--src/common/GitRevision.h40
-rw-r--r--src/common/Logging/Appender.cpp109
-rw-r--r--src/common/Logging/Appender.h132
-rw-r--r--src/common/Logging/AppenderConsole.cpp200
-rw-r--r--src/common/Logging/AppenderConsole.h62
-rw-r--r--src/common/Logging/AppenderFile.cpp126
-rw-r--r--src/common/Logging/AppenderFile.h46
-rw-r--r--src/common/Logging/Log.cpp347
-rw-r--r--src/common/Logging/Log.h216
-rw-r--r--src/common/Logging/LogOperation.cpp25
-rw-r--r--src/common/Logging/LogOperation.h42
-rw-r--r--src/common/Logging/Logger.cpp64
-rw-r--r--src/common/Logging/Logger.h43
-rw-r--r--src/common/PrecompiledHeaders/commonPCH.cpp1
-rw-r--r--src/common/PrecompiledHeaders/commonPCH.h13
-rw-r--r--src/common/Threading/Callback.h209
-rw-r--r--src/common/Threading/LockedQueue.h144
-rw-r--r--src/common/Threading/ProcessPriority.h105
-rw-r--r--src/common/Threading/ProducerConsumerQueue.h112
-rw-r--r--src/common/Utilities/ByteConverter.h68
-rw-r--r--src/common/Utilities/Duration.h39
-rw-r--r--src/common/Utilities/EventMap.cpp136
-rw-r--r--src/common/Utilities/EventMap.h342
-rw-r--r--src/common/Utilities/EventProcessor.cpp99
-rw-r--r--src/common/Utilities/EventProcessor.h73
-rw-r--r--src/common/Utilities/StringFormat.h46
-rw-r--r--src/common/Utilities/TaskScheduler.cpp229
-rw-r--r--src/common/Utilities/TaskScheduler.h650
-rw-r--r--src/common/Utilities/Timer.h203
-rw-r--r--src/common/Utilities/Util.cpp569
-rw-r--r--src/common/Utilities/Util.h548
84 files changed, 13881 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
new file mode 100644
index 00000000000..7f20fe36ab6
--- /dev/null
+++ b/src/common/CMakeLists.txt
@@ -0,0 +1,91 @@
+# Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+if( USE_COREPCH )
+ include_directories(${CMAKE_CURRENT_BINARY_DIR})
+endif()
+
+file(GLOB_RECURSE sources_Common Common.cpp Common.h)
+file(GLOB_RECURSE sources_Collision Collision/*.cpp Collision/*.h)
+file(GLOB_RECURSE sources_Threading Threading/*.cpp Threading/*.h)
+file(GLOB_RECURSE sources_Utilities Utilities/*.cpp Utilities/*.h)
+file(GLOB_RECURSE sources_Configuration Configuration/*.cpp Configuration/*.h)
+file(GLOB_RECURSE sources_Logging Logging/*.cpp Logging/*.h)
+if (SERVERS)
+ file(GLOB_RECURSE sources_Cryptography Cryptography/*.cpp Cryptography/*.h)
+endif (SERVERS)
+
+# Manually set sources for Debugging directory as we don't want to include WheatyExceptionReport in common project
+# It needs to be included both in authserver and worldserver for the static global variable to be properly initialized
+# and to handle crash logs on windows
+set(sources_Debugging Debugging/Errors.cpp Debugging/Errors.h)
+file(GLOB sources_localdir *.cpp *.h)
+
+if (USE_COREPCH)
+ set(common_STAT_PCH_HDR PrecompiledHeaders/commonPCH.h)
+ set(common_STAT_PCH_SRC PrecompiledHeaders/commonPCH.cpp)
+endif (USE_COREPCH)
+
+set(common_STAT_SRCS
+ ${common_STAT_SRCS}
+ ${sources_Common}
+ ${sources_Collision}
+ ${sources_Threading}
+ ${sources_Utilities}
+ ${sources_Debugging}
+ ${sources_Configuration}
+ ${sources_Logging}
+ ${sources_Cryptography}
+ ${sources_localdir}
+)
+
+# Do NOT add any extra include directory here, as we don't want the common
+# library to depend on anything else than TC deps, and itself.
+# This way we ensure that if either a PR does that without modifying this file,
+# a compile error will be generated, either this file will be modified so it
+# is detected more easily.
+# While it is OK to include files from other libs as long as they don't require
+# linkage (enums, defines...) it is discouraged to do so unless necessary, as it will pullute
+# include_directories leading to further unnoticed dependency aditions
+# Linker Depencency requirements: none
+include_directories(
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/Collision
+ ${CMAKE_CURRENT_SOURCE_DIR}/Collision/Management
+ ${CMAKE_CURRENT_SOURCE_DIR}/Collision/Maps
+ ${CMAKE_CURRENT_SOURCE_DIR}/Collision/Models
+ ${CMAKE_CURRENT_SOURCE_DIR}/Configuration
+ ${CMAKE_CURRENT_SOURCE_DIR}/Cryptography
+ ${CMAKE_CURRENT_SOURCE_DIR}/Debugging
+ ${CMAKE_CURRENT_SOURCE_DIR}/Logging
+ ${CMAKE_CURRENT_SOURCE_DIR}/Utilities
+ ${CMAKE_SOURCE_DIR}/dep/cppformat
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour/Include
+ ${CMAKE_SOURCE_DIR}/dep/SFMT
+ ${CMAKE_SOURCE_DIR}/dep/utf8cpp
+ ${OPENSSL_INCLUDE_DIR}
+ ${VALGRIND_INCLUDE_DIR}
+)
+
+GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+
+add_library(common STATIC
+ ${common_STAT_SRCS}
+ ${common_STAT_PCH_SRC}
+)
+
+add_dependencies(common revision_data.h)
+
+# Generate precompiled header
+if (USE_COREPCH)
+ add_cxx_pch(common ${common_STAT_PCH_HDR} ${common_STAT_PCH_SRC})
+endif ()
diff --git a/src/common/Collision/BoundingIntervalHierarchy.cpp b/src/common/Collision/BoundingIntervalHierarchy.cpp
new file mode 100644
index 00000000000..12af680712e
--- /dev/null
+++ b/src/common/Collision/BoundingIntervalHierarchy.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "BoundingIntervalHierarchy.h"
+
+#ifdef _MSC_VER
+ #define isnan _isnan
+#else
+ #define isnan std::isnan
+#endif
+
+void BIH::buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats)
+{
+ // create space for the first node
+ tempTree.push_back(uint32(3 << 30)); // dummy leaf
+ tempTree.insert(tempTree.end(), 2, 0);
+ //tempTree.add(0);
+
+ // seed bbox
+ AABound gridBox = { bounds.low(), bounds.high() };
+ AABound nodeBox = gridBox;
+ // seed subdivide function
+ subdivide(0, dat.numPrims - 1, tempTree, dat, gridBox, nodeBox, 0, 1, stats);
+}
+
+void BIH::subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats)
+{
+ if ((right - left + 1) <= dat.maxPrims || depth >= MAX_STACK_SIZE)
+ {
+ // write leaf node
+ stats.updateLeaf(depth, right - left + 1);
+ createNode(tempTree, nodeIndex, left, right);
+ return;
+ }
+ // calculate extents
+ int axis = -1, prevAxis, rightOrig;
+ float clipL = G3D::fnan(), clipR = G3D::fnan(), prevClip = G3D::fnan();
+ float split = G3D::fnan(), prevSplit;
+ bool wasLeft = true;
+ while (true)
+ {
+ prevAxis = axis;
+ prevSplit = split;
+ // perform quick consistency checks
+ G3D::Vector3 d( gridBox.hi - gridBox.lo );
+ if (d.x < 0 || d.y < 0 || d.z < 0)
+ throw std::logic_error("negative node extents");
+ for (int i = 0; i < 3; i++)
+ {
+ if (nodeBox.hi[i] < gridBox.lo[i] || nodeBox.lo[i] > gridBox.hi[i])
+ {
+ //UI.printError(Module.ACCEL, "Reached tree area in error - discarding node with: %d objects", right - left + 1);
+ throw std::logic_error("invalid node overlap");
+ }
+ }
+ // find longest axis
+ axis = d.primaryAxis();
+ split = 0.5f * (gridBox.lo[axis] + gridBox.hi[axis]);
+ // partition L/R subsets
+ clipL = -G3D::finf();
+ clipR = G3D::finf();
+ rightOrig = right; // save this for later
+ float nodeL = G3D::finf();
+ float nodeR = -G3D::finf();
+ for (int i = left; i <= right;)
+ {
+ int obj = dat.indices[i];
+ float minb = dat.primBound[obj].low()[axis];
+ float maxb = dat.primBound[obj].high()[axis];
+ float center = (minb + maxb) * 0.5f;
+ if (center <= split)
+ {
+ // stay left
+ i++;
+ if (clipL < maxb)
+ clipL = maxb;
+ }
+ else
+ {
+ // move to the right most
+ int t = dat.indices[i];
+ dat.indices[i] = dat.indices[right];
+ dat.indices[right] = t;
+ right--;
+ if (clipR > minb)
+ clipR = minb;
+ }
+ nodeL = std::min(nodeL, minb);
+ nodeR = std::max(nodeR, maxb);
+ }
+ // check for empty space
+ if (nodeL > nodeBox.lo[axis] && nodeR < nodeBox.hi[axis])
+ {
+ float nodeBoxW = nodeBox.hi[axis] - nodeBox.lo[axis];
+ float nodeNewW = nodeR - nodeL;
+ // node box is too big compare to space occupied by primitives?
+ if (1.3f * nodeNewW < nodeBoxW)
+ {
+ stats.updateBVH2();
+ int nextIndex = tempTree.size();
+ // allocate child
+ tempTree.push_back(0);
+ tempTree.push_back(0);
+ tempTree.push_back(0);
+ // write bvh2 clip node
+ stats.updateInner();
+ tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex;
+ tempTree[nodeIndex + 1] = floatToRawIntBits(nodeL);
+ tempTree[nodeIndex + 2] = floatToRawIntBits(nodeR);
+ // update nodebox and recurse
+ nodeBox.lo[axis] = nodeL;
+ nodeBox.hi[axis] = nodeR;
+ subdivide(left, rightOrig, tempTree, dat, gridBox, nodeBox, nextIndex, depth + 1, stats);
+ return;
+ }
+ }
+ // ensure we are making progress in the subdivision
+ if (right == rightOrig)
+ {
+ // all left
+ if (prevAxis == axis && G3D::fuzzyEq(prevSplit, split)) {
+ // we are stuck here - create a leaf
+ stats.updateLeaf(depth, right - left + 1);
+ createNode(tempTree, nodeIndex, left, right);
+ return;
+ }
+ if (clipL <= split) {
+ // keep looping on left half
+ gridBox.hi[axis] = split;
+ prevClip = clipL;
+ wasLeft = true;
+ continue;
+ }
+ gridBox.hi[axis] = split;
+ prevClip = G3D::fnan();
+ }
+ else if (left > right)
+ {
+ // all right
+ if (prevAxis == axis && G3D::fuzzyEq(prevSplit, split)) {
+ // we are stuck here - create a leaf
+ stats.updateLeaf(depth, right - left + 1);
+ createNode(tempTree, nodeIndex, left, right);
+ return;
+ }
+ right = rightOrig;
+ if (clipR >= split) {
+ // keep looping on right half
+ gridBox.lo[axis] = split;
+ prevClip = clipR;
+ wasLeft = false;
+ continue;
+ }
+ gridBox.lo[axis] = split;
+ prevClip = G3D::fnan();
+ }
+ else
+ {
+ // we are actually splitting stuff
+ if (prevAxis != -1 && !isnan(prevClip))
+ {
+ // second time through - lets create the previous split
+ // since it produced empty space
+ int nextIndex = tempTree.size();
+ // allocate child node
+ tempTree.push_back(0);
+ tempTree.push_back(0);
+ tempTree.push_back(0);
+ if (wasLeft) {
+ // create a node with a left child
+ // write leaf node
+ stats.updateInner();
+ tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex;
+ tempTree[nodeIndex + 1] = floatToRawIntBits(prevClip);
+ tempTree[nodeIndex + 2] = floatToRawIntBits(G3D::finf());
+ } else {
+ // create a node with a right child
+ // write leaf node
+ stats.updateInner();
+ tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3);
+ tempTree[nodeIndex + 1] = floatToRawIntBits(-G3D::finf());
+ tempTree[nodeIndex + 2] = floatToRawIntBits(prevClip);
+ }
+ // count stats for the unused leaf
+ depth++;
+ stats.updateLeaf(depth, 0);
+ // now we keep going as we are, with a new nodeIndex:
+ nodeIndex = nextIndex;
+ }
+ break;
+ }
+ }
+ // compute index of child nodes
+ int nextIndex = tempTree.size();
+ // allocate left node
+ int nl = right - left + 1;
+ int nr = rightOrig - (right + 1) + 1;
+ if (nl > 0) {
+ tempTree.push_back(0);
+ tempTree.push_back(0);
+ tempTree.push_back(0);
+ } else
+ nextIndex -= 3;
+ // allocate right node
+ if (nr > 0) {
+ tempTree.push_back(0);
+ tempTree.push_back(0);
+ tempTree.push_back(0);
+ }
+ // write leaf node
+ stats.updateInner();
+ tempTree[nodeIndex + 0] = (axis << 30) | nextIndex;
+ tempTree[nodeIndex + 1] = floatToRawIntBits(clipL);
+ tempTree[nodeIndex + 2] = floatToRawIntBits(clipR);
+ // prepare L/R child boxes
+ AABound gridBoxL(gridBox), gridBoxR(gridBox);
+ AABound nodeBoxL(nodeBox), nodeBoxR(nodeBox);
+ gridBoxL.hi[axis] = gridBoxR.lo[axis] = split;
+ nodeBoxL.hi[axis] = clipL;
+ nodeBoxR.lo[axis] = clipR;
+ // recurse
+ if (nl > 0)
+ subdivide(left, right, tempTree, dat, gridBoxL, nodeBoxL, nextIndex, depth + 1, stats);
+ else
+ stats.updateLeaf(depth + 1, 0);
+ if (nr > 0)
+ subdivide(right + 1, rightOrig, tempTree, dat, gridBoxR, nodeBoxR, nextIndex + 3, depth + 1, stats);
+ else
+ stats.updateLeaf(depth + 1, 0);
+}
+
+bool BIH::writeToFile(FILE* wf) const
+{
+ uint32 treeSize = tree.size();
+ uint32 check=0, count;
+ check += fwrite(&bounds.low(), sizeof(float), 3, wf);
+ check += fwrite(&bounds.high(), sizeof(float), 3, wf);
+ check += fwrite(&treeSize, sizeof(uint32), 1, wf);
+ check += fwrite(&tree[0], sizeof(uint32), treeSize, wf);
+ count = objects.size();
+ check += fwrite(&count, sizeof(uint32), 1, wf);
+ check += fwrite(&objects[0], sizeof(uint32), count, wf);
+ return check == (3 + 3 + 2 + treeSize + count);
+}
+
+bool BIH::readFromFile(FILE* rf)
+{
+ uint32 treeSize;
+ G3D::Vector3 lo, hi;
+ uint32 check=0, count=0;
+ check += fread(&lo, sizeof(float), 3, rf);
+ check += fread(&hi, sizeof(float), 3, rf);
+ bounds = G3D::AABox(lo, hi);
+ check += fread(&treeSize, sizeof(uint32), 1, rf);
+ tree.resize(treeSize);
+ check += fread(&tree[0], sizeof(uint32), treeSize, rf);
+ check += fread(&count, sizeof(uint32), 1, rf);
+ objects.resize(count); // = new uint32[nObjects];
+ check += fread(&objects[0], sizeof(uint32), count, rf);
+ return uint64(check) == uint64(3 + 3 + 1 + 1 + uint64(treeSize) + uint64(count));
+}
+
+void BIH::BuildStats::updateLeaf(int depth, int n)
+{
+ numLeaves++;
+ minDepth = std::min(depth, minDepth);
+ maxDepth = std::max(depth, maxDepth);
+ sumDepth += depth;
+ minObjects = std::min(n, minObjects);
+ maxObjects = std::max(n, maxObjects);
+ sumObjects += n;
+ int nl = std::min(n, 5);
+ ++numLeavesN[nl];
+}
+
+void BIH::BuildStats::printStats()
+{
+ printf("Tree stats:\n");
+ printf(" * Nodes: %d\n", numNodes);
+ printf(" * Leaves: %d\n", numLeaves);
+ printf(" * Objects: min %d\n", minObjects);
+ printf(" avg %.2f\n", (float) sumObjects / numLeaves);
+ printf(" avg(n>0) %.2f\n", (float) sumObjects / (numLeaves - numLeavesN[0]));
+ printf(" max %d\n", maxObjects);
+ printf(" * Depth: min %d\n", minDepth);
+ printf(" avg %.2f\n", (float) sumDepth / numLeaves);
+ printf(" max %d\n", maxDepth);
+ printf(" * Leaves w/: N=0 %3d%%\n", 100 * numLeavesN[0] / numLeaves);
+ printf(" N=1 %3d%%\n", 100 * numLeavesN[1] / numLeaves);
+ printf(" N=2 %3d%%\n", 100 * numLeavesN[2] / numLeaves);
+ printf(" N=3 %3d%%\n", 100 * numLeavesN[3] / numLeaves);
+ printf(" N=4 %3d%%\n", 100 * numLeavesN[4] / numLeaves);
+ printf(" N>4 %3d%%\n", 100 * numLeavesN[5] / numLeaves);
+ printf(" * BVH2 nodes: %d (%3d%%)\n", numBVH2, 100 * numBVH2 / (numNodes + numLeaves - 2 * numBVH2));
+}
diff --git a/src/common/Collision/BoundingIntervalHierarchy.h b/src/common/Collision/BoundingIntervalHierarchy.h
new file mode 100644
index 00000000000..3a09772c41f
--- /dev/null
+++ b/src/common/Collision/BoundingIntervalHierarchy.h
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _BIH_H
+#define _BIH_H
+
+#include "G3D/Vector3.h"
+#include "G3D/Ray.h"
+#include "G3D/AABox.h"
+
+#include "Define.h"
+
+#include <stdexcept>
+#include <vector>
+#include <algorithm>
+#include <limits>
+#include <cmath>
+
+#define MAX_STACK_SIZE 64
+
+static inline uint32 floatToRawIntBits(float f)
+{
+ union
+ {
+ uint32 ival;
+ float fval;
+ } temp;
+ temp.fval=f;
+ return temp.ival;
+}
+
+static inline float intBitsToFloat(uint32 i)
+{
+ union
+ {
+ uint32 ival;
+ float fval;
+ } temp;
+ temp.ival=i;
+ return temp.fval;
+}
+
+struct AABound
+{
+ G3D::Vector3 lo, hi;
+};
+
+/** Bounding Interval Hierarchy Class.
+ Building and Ray-Intersection functions based on BIH from
+ Sunflow, a Java Raytracer, released under MIT/X11 License
+ http://sunflow.sourceforge.net/
+ Copyright (c) 2003-2007 Christopher Kulla
+*/
+
+class BIH
+{
+ private:
+ void init_empty()
+ {
+ tree.clear();
+ objects.clear();
+ // create space for the first node
+ tree.push_back(3u << 30u); // dummy leaf
+ tree.insert(tree.end(), 2, 0);
+ }
+ public:
+ BIH() { init_empty(); }
+ template< class BoundsFunc, class PrimArray >
+ void build(const PrimArray &primitives, BoundsFunc &getBounds, uint32 leafSize = 3, bool printStats=false)
+ {
+ if (primitives.size() == 0)
+ {
+ init_empty();
+ return;
+ }
+
+ buildData dat;
+ dat.maxPrims = leafSize;
+ dat.numPrims = primitives.size();
+ dat.indices = new uint32[dat.numPrims];
+ dat.primBound = new G3D::AABox[dat.numPrims];
+ getBounds(primitives[0], bounds);
+ for (uint32 i=0; i<dat.numPrims; ++i)
+ {
+ dat.indices[i] = i;
+ getBounds(primitives[i], dat.primBound[i]);
+ bounds.merge(dat.primBound[i]);
+ }
+ std::vector<uint32> tempTree;
+ BuildStats stats;
+ buildHierarchy(tempTree, dat, stats);
+ if (printStats)
+ stats.printStats();
+
+ objects.resize(dat.numPrims);
+ for (uint32 i=0; i<dat.numPrims; ++i)
+ objects[i] = dat.indices[i];
+ //nObjects = dat.numPrims;
+ tree = tempTree;
+ delete[] dat.primBound;
+ delete[] dat.indices;
+ }
+ uint32 primCount() const { return objects.size(); }
+
+ template<typename RayCallback>
+ void intersectRay(const G3D::Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst=false) const
+ {
+ float intervalMin = -1.f;
+ float intervalMax = -1.f;
+ G3D::Vector3 org = r.origin();
+ G3D::Vector3 dir = r.direction();
+ G3D::Vector3 invDir;
+ for (int i=0; i<3; ++i)
+ {
+ invDir[i] = 1.f / dir[i];
+ if (G3D::fuzzyNe(dir[i], 0.0f))
+ {
+ float t1 = (bounds.low()[i] - org[i]) * invDir[i];
+ float t2 = (bounds.high()[i] - org[i]) * invDir[i];
+ if (t1 > t2)
+ std::swap(t1, t2);
+ if (t1 > intervalMin)
+ intervalMin = t1;
+ if (t2 < intervalMax || intervalMax < 0.f)
+ intervalMax = t2;
+ // intervalMax can only become smaller for other axis,
+ // and intervalMin only larger respectively, so stop early
+ if (intervalMax <= 0 || intervalMin >= maxDist)
+ return;
+ }
+ }
+
+ if (intervalMin > intervalMax)
+ return;
+ intervalMin = std::max(intervalMin, 0.f);
+ intervalMax = std::min(intervalMax, maxDist);
+
+ uint32 offsetFront[3];
+ uint32 offsetBack[3];
+ uint32 offsetFront3[3];
+ uint32 offsetBack3[3];
+ // compute custom offsets from direction sign bit
+
+ for (int i=0; i<3; ++i)
+ {
+ offsetFront[i] = floatToRawIntBits(dir[i]) >> 31;
+ offsetBack[i] = offsetFront[i] ^ 1;
+ offsetFront3[i] = offsetFront[i] * 3;
+ offsetBack3[i] = offsetBack[i] * 3;
+
+ // avoid always adding 1 during the inner loop
+ ++offsetFront[i];
+ ++offsetBack[i];
+ }
+
+ StackNode stack[MAX_STACK_SIZE];
+ int stackPos = 0;
+ int node = 0;
+
+ while (true) {
+ while (true)
+ {
+ uint32 tn = tree[node];
+ uint32 axis = (tn & (3 << 30)) >> 30;
+ bool BVH2 = (tn & (1 << 29)) != 0;
+ int offset = tn & ~(7 << 29);
+ if (!BVH2)
+ {
+ if (axis < 3)
+ {
+ // "normal" interior node
+ float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
+ float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
+ // ray passes between clip zones
+ if (tf < intervalMin && tb > intervalMax)
+ break;
+ int back = offset + offsetBack3[axis];
+ node = back;
+ // ray passes through far node only
+ if (tf < intervalMin) {
+ intervalMin = (tb >= intervalMin) ? tb : intervalMin;
+ continue;
+ }
+ node = offset + offsetFront3[axis]; // front
+ // ray passes through near node only
+ if (tb > intervalMax) {
+ intervalMax = (tf <= intervalMax) ? tf : intervalMax;
+ continue;
+ }
+ // ray passes through both nodes
+ // push back node
+ stack[stackPos].node = back;
+ stack[stackPos].tnear = (tb >= intervalMin) ? tb : intervalMin;
+ stack[stackPos].tfar = intervalMax;
+ stackPos++;
+ // update ray interval for front node
+ intervalMax = (tf <= intervalMax) ? tf : intervalMax;
+ continue;
+ }
+ else
+ {
+ // leaf - test some objects
+ int n = tree[node + 1];
+ while (n > 0) {
+ bool hit = intersectCallback(r, objects[offset], maxDist, stopAtFirst);
+ if (stopAtFirst && hit) return;
+ --n;
+ ++offset;
+ }
+ break;
+ }
+ }
+ else
+ {
+ if (axis>2)
+ return; // should not happen
+ float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
+ float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
+ node = offset;
+ intervalMin = (tf >= intervalMin) ? tf : intervalMin;
+ intervalMax = (tb <= intervalMax) ? tb : intervalMax;
+ if (intervalMin > intervalMax)
+ break;
+ continue;
+ }
+ } // traversal loop
+ do
+ {
+ // stack is empty?
+ if (stackPos == 0)
+ return;
+ // move back up the stack
+ stackPos--;
+ intervalMin = stack[stackPos].tnear;
+ if (maxDist < intervalMin)
+ continue;
+ node = stack[stackPos].node;
+ intervalMax = stack[stackPos].tfar;
+ break;
+ } while (true);
+ }
+ }
+
+ template<typename IsectCallback>
+ void intersectPoint(const G3D::Vector3 &p, IsectCallback& intersectCallback) const
+ {
+ if (!bounds.contains(p))
+ return;
+
+ StackNode stack[MAX_STACK_SIZE];
+ int stackPos = 0;
+ int node = 0;
+
+ while (true) {
+ while (true)
+ {
+ uint32 tn = tree[node];
+ uint32 axis = (tn & (3 << 30)) >> 30;
+ bool BVH2 = (tn & (1 << 29)) != 0;
+ int offset = tn & ~(7 << 29);
+ if (!BVH2)
+ {
+ if (axis < 3)
+ {
+ // "normal" interior node
+ float tl = intBitsToFloat(tree[node + 1]);
+ float tr = intBitsToFloat(tree[node + 2]);
+ // point is between clip zones
+ if (tl < p[axis] && tr > p[axis])
+ break;
+ int right = offset + 3;
+ node = right;
+ // point is in right node only
+ if (tl < p[axis]) {
+ continue;
+ }
+ node = offset; // left
+ // point is in left node only
+ if (tr > p[axis]) {
+ continue;
+ }
+ // point is in both nodes
+ // push back right node
+ stack[stackPos].node = right;
+ stackPos++;
+ continue;
+ }
+ else
+ {
+ // leaf - test some objects
+ int n = tree[node + 1];
+ while (n > 0) {
+ intersectCallback(p, objects[offset]); // !!!
+ --n;
+ ++offset;
+ }
+ break;
+ }
+ }
+ else // BVH2 node (empty space cut off left and right)
+ {
+ if (axis>2)
+ return; // should not happen
+ float tl = intBitsToFloat(tree[node + 1]);
+ float tr = intBitsToFloat(tree[node + 2]);
+ node = offset;
+ if (tl > p[axis] || tr < p[axis])
+ break;
+ continue;
+ }
+ } // traversal loop
+
+ // stack is empty?
+ if (stackPos == 0)
+ return;
+ // move back up the stack
+ stackPos--;
+ node = stack[stackPos].node;
+ }
+ }
+
+ bool writeToFile(FILE* wf) const;
+ bool readFromFile(FILE* rf);
+
+ protected:
+ std::vector<uint32> tree;
+ std::vector<uint32> objects;
+ G3D::AABox bounds;
+
+ struct buildData
+ {
+ uint32 *indices;
+ G3D::AABox *primBound;
+ uint32 numPrims;
+ int maxPrims;
+ };
+ struct StackNode
+ {
+ uint32 node;
+ float tnear;
+ float tfar;
+ };
+
+ class BuildStats
+ {
+ private:
+ int numNodes;
+ int numLeaves;
+ int sumObjects;
+ int minObjects;
+ int maxObjects;
+ int sumDepth;
+ int minDepth;
+ int maxDepth;
+ int numLeavesN[6];
+ int numBVH2;
+
+ public:
+ BuildStats():
+ numNodes(0), numLeaves(0), sumObjects(0), minObjects(0x0FFFFFFF),
+ maxObjects(0xFFFFFFFF), sumDepth(0), minDepth(0x0FFFFFFF),
+ maxDepth(0xFFFFFFFF), numBVH2(0)
+ {
+ for (int i=0; i<6; ++i) numLeavesN[i] = 0;
+ }
+
+ void updateInner() { numNodes++; }
+ void updateBVH2() { numBVH2++; }
+ void updateLeaf(int depth, int n);
+ void printStats();
+ };
+
+ void buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats);
+
+ void createNode(std::vector<uint32> &tempTree, int nodeIndex, uint32 left, uint32 right) const
+ {
+ // write leaf node
+ tempTree[nodeIndex + 0] = (3 << 30) | left;
+ tempTree[nodeIndex + 1] = right - left + 1;
+ }
+
+ void subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats);
+};
+
+#endif // _BIH_H
diff --git a/src/common/Collision/BoundingIntervalHierarchyWrapper.h b/src/common/Collision/BoundingIntervalHierarchyWrapper.h
new file mode 100644
index 00000000000..60bb6a569df
--- /dev/null
+++ b/src/common/Collision/BoundingIntervalHierarchyWrapper.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _BIH_WRAP
+#define _BIH_WRAP
+
+#include "G3D/Table.h"
+#include "G3D/Array.h"
+#include "G3D/Set.h"
+#include "BoundingIntervalHierarchy.h"
+
+
+template<class T, class BoundsFunc = BoundsTrait<T> >
+class BIHWrap
+{
+ template<class RayCallback>
+ struct MDLCallback
+ {
+ const T* const* objects;
+ RayCallback& _callback;
+ uint32 objects_size;
+
+ MDLCallback(RayCallback& callback, const T* const* objects_array, uint32 objects_size ) : objects(objects_array), _callback(callback), objects_size(objects_size) { }
+
+ /// Intersect ray
+ bool operator() (const G3D::Ray& ray, uint32 idx, float& maxDist, bool /*stopAtFirst*/)
+ {
+ if (idx >= objects_size)
+ return false;
+ if (const T* obj = objects[idx])
+ return _callback(ray, *obj, maxDist/*, stopAtFirst*/);
+ return false;
+ }
+
+ /// Intersect point
+ void operator() (const G3D::Vector3& p, uint32 idx)
+ {
+ if (idx >= objects_size)
+ return;
+ if (const T* obj = objects[idx])
+ _callback(p, *obj);
+ }
+ };
+
+ typedef G3D::Array<const T*> ObjArray;
+
+ BIH m_tree;
+ ObjArray m_objects;
+ G3D::Table<const T*, uint32> m_obj2Idx;
+ G3D::Set<const T*> m_objects_to_push;
+ int unbalanced_times;
+
+public:
+ BIHWrap() : unbalanced_times(0) { }
+
+ void insert(const T& obj)
+ {
+ ++unbalanced_times;
+ m_objects_to_push.insert(&obj);
+ }
+
+ void remove(const T& obj)
+ {
+ ++unbalanced_times;
+ uint32 Idx = 0;
+ const T * temp;
+ if (m_obj2Idx.getRemove(&obj, temp, Idx))
+ m_objects[Idx] = NULL;
+ else
+ m_objects_to_push.remove(&obj);
+ }
+
+ void balance()
+ {
+ if (unbalanced_times == 0)
+ return;
+
+ unbalanced_times = 0;
+ m_objects.fastClear();
+ m_obj2Idx.getKeys(m_objects);
+ m_objects_to_push.getMembers(m_objects);
+ //assert that m_obj2Idx has all the keys
+
+ m_tree.build(m_objects, BoundsFunc::getBounds2);
+ }
+
+ template<typename RayCallback>
+ void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& maxDist)
+ {
+ balance();
+ MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray(), m_objects.size());
+ m_tree.intersectRay(ray, temp_cb, maxDist, true);
+ }
+
+ template<typename IsectCallback>
+ void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
+ {
+ balance();
+ MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray(), m_objects.size());
+ m_tree.intersectPoint(point, callback);
+ }
+};
+
+#endif // _BIH_WRAP
diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp
new file mode 100644
index 00000000000..1de2543543d
--- /dev/null
+++ b/src/common/Collision/DynamicTree.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "DynamicTree.h"
+//#include "QuadTree.h"
+//#include "RegularGrid.h"
+#include "BoundingIntervalHierarchyWrapper.h"
+
+#include "Log.h"
+#include "RegularGrid.h"
+#include "Timer.h"
+#include "GameObjectModel.h"
+#include "ModelInstance.h"
+
+#include <G3D/AABox.h>
+#include <G3D/Ray.h>
+#include <G3D/Vector3.h>
+
+using VMAP::ModelInstance;
+
+namespace {
+
+int CHECK_TREE_PERIOD = 200;
+
+} // namespace
+
+template<> struct HashTrait< GameObjectModel>{
+ static size_t hashCode(const GameObjectModel& g) { return (size_t)(void*)&g; }
+};
+
+template<> struct PositionTrait< GameObjectModel> {
+ static void getPosition(const GameObjectModel& g, G3D::Vector3& p) { p = g.getPosition(); }
+};
+
+template<> struct BoundsTrait< GameObjectModel> {
+ static void getBounds(const GameObjectModel& g, G3D::AABox& out) { out = g.getBounds();}
+ static void getBounds2(const GameObjectModel* g, G3D::AABox& out) { out = g->getBounds();}
+};
+
+/*
+static bool operator == (const GameObjectModel& mdl, const GameObjectModel& mdl2){
+ return &mdl == &mdl2;
+}
+*/
+
+typedef RegularGrid2D<GameObjectModel, BIHWrap<GameObjectModel> > ParentTree;
+
+struct DynTreeImpl : public ParentTree/*, public Intersectable*/
+{
+ typedef GameObjectModel Model;
+ typedef ParentTree base;
+
+ DynTreeImpl() :
+ rebalance_timer(CHECK_TREE_PERIOD),
+ unbalanced_times(0)
+ {
+ }
+
+ void insert(const Model& mdl)
+ {
+ base::insert(mdl);
+ ++unbalanced_times;
+ }
+
+ void remove(const Model& mdl)
+ {
+ base::remove(mdl);
+ ++unbalanced_times;
+ }
+
+ void balance()
+ {
+ base::balance();
+ unbalanced_times = 0;
+ }
+
+ void update(uint32 difftime)
+ {
+ if (!size())
+ return;
+
+ rebalance_timer.Update(difftime);
+ if (rebalance_timer.Passed())
+ {
+ rebalance_timer.Reset(CHECK_TREE_PERIOD);
+ if (unbalanced_times > 0)
+ balance();
+ }
+ }
+
+ TimeTrackerSmall rebalance_timer;
+ int unbalanced_times;
+};
+
+DynamicMapTree::DynamicMapTree() : impl(new DynTreeImpl()) { }
+
+DynamicMapTree::~DynamicMapTree()
+{
+ delete impl;
+}
+
+void DynamicMapTree::insert(const GameObjectModel& mdl)
+{
+ impl->insert(mdl);
+}
+
+void DynamicMapTree::remove(const GameObjectModel& mdl)
+{
+ impl->remove(mdl);
+}
+
+bool DynamicMapTree::contains(const GameObjectModel& mdl) const
+{
+ return impl->contains(mdl);
+}
+
+void DynamicMapTree::balance()
+{
+ impl->balance();
+}
+
+int DynamicMapTree::size() const
+{
+ return impl->size();
+}
+
+void DynamicMapTree::update(uint32 t_diff)
+{
+ impl->update(t_diff);
+}
+
+struct DynamicTreeIntersectionCallback
+{
+ bool did_hit;
+ uint32 phase_mask;
+ DynamicTreeIntersectionCallback(uint32 phasemask) : did_hit(false), phase_mask(phasemask) { }
+ bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance)
+ {
+ did_hit = obj.intersectRay(r, distance, true, phase_mask);
+ return did_hit;
+ }
+ bool didHit() const { return did_hit;}
+};
+
+struct DynamicTreeIntersectionCallback_WithLogger
+{
+ bool did_hit;
+ uint32 phase_mask;
+ DynamicTreeIntersectionCallback_WithLogger(uint32 phasemask) : did_hit(false), phase_mask(phasemask)
+ {
+ TC_LOG_DEBUG("maps", "Dynamic Intersection log");
+ }
+ bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance)
+ {
+ TC_LOG_DEBUG("maps", "testing intersection with %s", obj.name.c_str());
+ bool hit = obj.intersectRay(r, distance, true, phase_mask);
+ if (hit)
+ {
+ did_hit = true;
+ TC_LOG_DEBUG("maps", "result: intersects");
+ }
+ return hit;
+ }
+ bool didHit() const { return did_hit;}
+};
+
+bool DynamicMapTree::getIntersectionTime(const uint32 phasemask, const G3D::Ray& ray,
+ const G3D::Vector3& endPos, float& maxDist) const
+{
+ float distance = maxDist;
+ DynamicTreeIntersectionCallback callback(phasemask);
+ impl->intersectRay(ray, callback, distance, endPos);
+ if (callback.didHit())
+ maxDist = distance;
+ return callback.didHit();
+}
+
+bool DynamicMapTree::getObjectHitPos(const uint32 phasemask, const G3D::Vector3& startPos,
+ const G3D::Vector3& endPos, G3D::Vector3& resultHit,
+ float modifyDist) const
+{
+ bool result = false;
+ float maxDist = (endPos - startPos).magnitude();
+ // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
+ ASSERT(maxDist < std::numeric_limits<float>::max());
+ // prevent NaN values which can cause BIH intersection to enter infinite loop
+ if (maxDist < 1e-10f)
+ {
+ resultHit = endPos;
+ return false;
+ }
+ G3D::Vector3 dir = (endPos - startPos)/maxDist; // direction with length of 1
+ G3D::Ray ray(startPos, dir);
+ float dist = maxDist;
+ if (getIntersectionTime(phasemask, ray, endPos, dist))
+ {
+ resultHit = startPos + dir * dist;
+ if (modifyDist < 0)
+ {
+ if ((resultHit - startPos).magnitude() > -modifyDist)
+ resultHit = resultHit + dir*modifyDist;
+ else
+ resultHit = startPos;
+ }
+ else
+ resultHit = resultHit + dir*modifyDist;
+
+ result = true;
+ }
+ else
+ {
+ resultHit = endPos;
+ result = false;
+ }
+ return result;
+}
+
+bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const
+{
+ G3D::Vector3 v1(x1, y1, z1), v2(x2, y2, z2);
+
+ float maxDist = (v2 - v1).magnitude();
+
+ if (!G3D::fuzzyGt(maxDist, 0) )
+ return true;
+
+ G3D::Ray r(v1, (v2-v1) / maxDist);
+ DynamicTreeIntersectionCallback callback(phasemask);
+ impl->intersectRay(r, callback, maxDist, v2);
+
+ return !callback.did_hit;
+}
+
+float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const
+{
+ G3D::Vector3 v(x, y, z);
+ G3D::Ray r(v, G3D::Vector3(0, 0, -1));
+ DynamicTreeIntersectionCallback callback(phasemask);
+ impl->intersectZAllignedRay(r, callback, maxSearchDist);
+
+ if (callback.didHit())
+ return v.z - maxSearchDist;
+ else
+ return -G3D::finf();
+}
diff --git a/src/common/Collision/DynamicTree.h b/src/common/Collision/DynamicTree.h
new file mode 100644
index 00000000000..5e905323640
--- /dev/null
+++ b/src/common/Collision/DynamicTree.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _DYNTREE_H
+#define _DYNTREE_H
+
+#include "Define.h"
+
+namespace G3D
+{
+ class Ray;
+ class Vector3;
+}
+
+class GameObjectModel;
+struct DynTreeImpl;
+
+class DynamicMapTree
+{
+ DynTreeImpl *impl;
+
+public:
+
+ DynamicMapTree();
+ ~DynamicMapTree();
+
+ bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2,
+ float z2, uint32 phasemask) const;
+
+ bool getIntersectionTime(uint32 phasemask, const G3D::Ray& ray,
+ const G3D::Vector3& endPos, float& maxDist) const;
+
+ bool getObjectHitPos(uint32 phasemask, const G3D::Vector3& pPos1,
+ const G3D::Vector3& pPos2, G3D::Vector3& pResultHitPos,
+ float pModifyDist) const;
+
+ float getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const;
+
+ void insert(const GameObjectModel&);
+ void remove(const GameObjectModel&);
+ bool contains(const GameObjectModel&) const;
+ int size() const;
+
+ void balance();
+ void update(uint32 diff);
+};
+
+#endif // _DYNTREE_H
diff --git a/src/common/Collision/Management/IVMapManager.h b/src/common/Collision/Management/IVMapManager.h
new file mode 100644
index 00000000000..b890554257c
--- /dev/null
+++ b/src/common/Collision/Management/IVMapManager.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _IVMAPMANAGER_H
+#define _IVMAPMANAGER_H
+
+#include <string>
+#include "Define.h"
+
+//===========================================================
+
+/**
+This is the minimum interface to the VMapMamager.
+*/
+
+namespace VMAP
+{
+
+ enum VMAP_LOAD_RESULT
+ {
+ VMAP_LOAD_RESULT_ERROR,
+ VMAP_LOAD_RESULT_OK,
+ VMAP_LOAD_RESULT_IGNORED
+ };
+
+ #define VMAP_INVALID_HEIGHT -100000.0f // for check
+ #define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
+
+ //===========================================================
+ class IVMapManager
+ {
+ private:
+ bool iEnableLineOfSightCalc;
+ bool iEnableHeightCalc;
+
+ public:
+ IVMapManager() : iEnableLineOfSightCalc(true), iEnableHeightCalc(true) { }
+
+ virtual ~IVMapManager(void) { }
+
+ virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
+
+ virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
+
+ virtual void unloadMap(unsigned int pMapId, int x, int y) = 0;
+ virtual void unloadMap(unsigned int pMapId) = 0;
+
+ virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0;
+ virtual float getHeight(unsigned int pMapId, float x, float y, float z, float maxSearchDist) = 0;
+ /**
+ test if we hit an object. return true if we hit one. rx, ry, rz will hold the hit position or the dest position, if no intersection was found
+ return a position, that is pReduceDist closer to the origin
+ */
+ virtual bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist) = 0;
+ /**
+ send debug commands
+ */
+ virtual bool processCommand(char *pCommand)= 0;
+
+ /**
+ Enable/disable LOS calculation
+ It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
+ */
+ void setEnableLineOfSightCalc(bool pVal) { iEnableLineOfSightCalc = pVal; }
+ /**
+ Enable/disable model height calculation
+ It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
+ */
+ void setEnableHeightCalc(bool pVal) { iEnableHeightCalc = pVal; }
+
+ bool isLineOfSightCalcEnabled() const { return(iEnableLineOfSightCalc); }
+ bool isHeightCalcEnabled() const { return(iEnableHeightCalc); }
+ bool isMapLoadingEnabled() const { return(iEnableLineOfSightCalc || iEnableHeightCalc ); }
+
+ virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const =0;
+ /**
+ Query world model area info.
+ \param z gets adjusted to the ground height for which this are info is valid
+ */
+ virtual bool getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const=0;
+ virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const=0;
+ };
+
+}
+#endif
diff --git a/src/common/Collision/Management/MMapFactory.cpp b/src/common/Collision/Management/MMapFactory.cpp
new file mode 100644
index 00000000000..667b8378c56
--- /dev/null
+++ b/src/common/Collision/Management/MMapFactory.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "MMapFactory.h"
+#include "Config.h"
+
+namespace MMAP
+{
+ // ######################## MMapFactory ########################
+ // our global singleton copy
+ MMapManager* g_MMapManager = NULL;
+
+ MMapManager* MMapFactory::createOrGetMMapManager()
+ {
+ if (g_MMapManager == NULL)
+ g_MMapManager = new MMapManager();
+
+ return g_MMapManager;
+ }
+
+ void MMapFactory::clear()
+ {
+ if (g_MMapManager)
+ {
+ delete g_MMapManager;
+ g_MMapManager = NULL;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/common/Collision/Management/MMapFactory.h b/src/common/Collision/Management/MMapFactory.h
new file mode 100644
index 00000000000..773983f81eb
--- /dev/null
+++ b/src/common/Collision/Management/MMapFactory.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _MMAP_FACTORY_H
+#define _MMAP_FACTORY_H
+
+#include "Define.h"
+#include "MMapManager.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+#include <unordered_map>
+
+namespace MMAP
+{
+ enum MMAP_LOAD_RESULT
+ {
+ MMAP_LOAD_RESULT_ERROR,
+ MMAP_LOAD_RESULT_OK,
+ MMAP_LOAD_RESULT_IGNORED
+ };
+
+ // static class
+ // holds all mmap global data
+ // access point to MMapManager singleton
+ class MMapFactory
+ {
+ public:
+ static MMapManager* createOrGetMMapManager();
+ static void clear();
+ static bool IsPathfindingEnabled(uint32 mapId);
+ };
+}
+
+#endif
+
diff --git a/src/common/Collision/Management/MMapManager.cpp b/src/common/Collision/Management/MMapManager.cpp
new file mode 100644
index 00000000000..00985e4fa1d
--- /dev/null
+++ b/src/common/Collision/Management/MMapManager.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "MMapManager.h"
+#include "Log.h"
+#include "Config.h"
+#include "MapDefines.h"
+
+namespace MMAP
+{
+ // ######################## MMapManager ########################
+ MMapManager::~MMapManager()
+ {
+ for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
+ delete i->second;
+
+ // by now we should not have maps loaded
+ // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
+ }
+
+ void MMapManager::InitializeThreadUnsafe(const std::vector<uint32>& mapIds)
+ {
+ // the caller must pass the list of all mapIds that will be used in the VMapManager2 lifetime
+ for (const uint32& mapId : mapIds)
+ loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr));
+
+ thread_safe_environment = false;
+ }
+
+ MMapDataSet::const_iterator MMapManager::GetMMapData(uint32 mapId) const
+ {
+ // return the iterator if found or end() if not found/NULL
+ MMapDataSet::const_iterator itr = loadedMMaps.find(mapId);
+ if (itr != loadedMMaps.cend() && !itr->second)
+ itr = loadedMMaps.cend();
+
+ return itr;
+ }
+
+ bool MMapManager::loadMapData(uint32 mapId)
+ {
+ // we already have this map loaded?
+ MMapDataSet::iterator itr = loadedMMaps.find(mapId);
+ if (itr != loadedMMaps.end())
+ {
+ if (itr->second)
+ return true;
+ }
+ else
+ {
+ if (thread_safe_environment)
+ itr = loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr)).first;
+ else
+ ASSERT(false, "Invalid mapId %u passed to MMapManager after startup in thread unsafe environment", mapId);
+ }
+
+ // load and init dtNavMesh - read parameters from file
+ std::string dataDir = sConfigMgr->GetStringDefault("DataDir", "./");
+ uint32 pathLen = dataDir.length() + strlen("/mmaps/%03i.mmap") + 1;
+ char *fileName = new char[pathLen];
+ snprintf(fileName, pathLen, (dataDir + "/mmaps/%03i.mmap").c_str(), mapId);
+
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ {
+ TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMeshParams params;
+ int count = fread(&params, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+ if (count != 1)
+ {
+ TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMesh* mesh = dtAllocNavMesh();
+ ASSERT(mesh);
+ if (dtStatusFailed(mesh->init(&params)))
+ {
+ dtFreeNavMesh(mesh);
+ TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ delete [] fileName;
+
+ TC_LOG_DEBUG("maps", "MMAP:loadMapData: Loaded %03i.mmap", mapId);
+
+ // store inside our map list
+ MMapData* mmap_data = new MMapData(mesh);
+ mmap_data->mmapLoadedTiles.clear();
+
+ itr->second = mmap_data;
+ return true;
+ }
+
+ uint32 MMapManager::packTileID(int32 x, int32 y)
+ {
+ return uint32(x << 16 | y);
+ }
+
+ bool MMapManager::loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y)
+ {
+ // make sure the mmap is loaded and ready to load tiles
+ if (!loadMapData(mapId))
+ return false;
+
+ // get this mmap data
+ MMapData* mmap = loadedMMaps[mapId];
+ ASSERT(mmap->navMesh);
+
+ // check if we already have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end())
+ return false;
+
+ // load this tile :: mmaps/MMMXXYY.mmtile
+ uint32 pathLen = basePath.length() + strlen("/%03i%02i%02i.mmtile") + 1;
+ char *fileName = new char[pathLen];
+
+ snprintf(fileName, pathLen, (basePath + "/%03i%02i%02i.mmtile").c_str(), mapId, x, y);
+
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ {
+ TC_LOG_DEBUG("maps", "MMAP:loadMap: Could not open mmtile file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+ delete [] fileName;
+
+ // read header
+ MmapTileHeader fileHeader;
+ if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC)
+ {
+ TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, x, y);
+ fclose(file);
+ return false;
+ }
+
+ if (fileHeader.mmapVersion != MMAP_VERSION)
+ {
+ TC_LOG_ERROR("maps", "MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i",
+ mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
+ fclose(file);
+ return false;
+ }
+
+ unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
+ ASSERT(data);
+
+ size_t result = fread(data, fileHeader.size, 1, file);
+ if (!result)
+ {
+ TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, x, y);
+ fclose(file);
+ return false;
+ }
+
+ fclose(file);
+
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ dtTileRef tileRef = 0;
+
+ // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
+ if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
+ {
+ mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
+ ++loadedTiles;
+ TC_LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile %03i[%02i, %02i] into %03i[%02i, %02i]", mapId, x, y, mapId, header->x, header->y);
+ return true;
+ }
+ else
+ {
+ TC_LOG_ERROR("maps", "MMAP:loadMap: Could not load %03u%02i%02i.mmtile into navmesh", mapId, x, y);
+ dtFree(data);
+ return false;
+ }
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
+ {
+ // check if we have this map loaded
+ MMapDataSet::const_iterator itr = GetMMapData(mapId);
+ if (itr == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ MMapData* mmap = itr->second;
+
+ // check if we have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end())
+ {
+ // file may not exist, therefore not loaded
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ dtTileRef tileRef = mmap->mmapLoadedTiles[packedGridPos];
+
+ // unload, and mark as non loaded
+ if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, NULL, NULL)))
+ {
+ // this is technically a memory leak
+ // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
+ // we cannot recover from this error - assert out
+ TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
+ ABORT();
+ }
+ else
+ {
+ mmap->mmapLoadedTiles.erase(packedGridPos);
+ --loadedTiles;
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId)
+ {
+ MMapDataSet::iterator itr = loadedMMaps.find(mapId);
+ if (itr == loadedMMaps.end() || !itr->second)
+ {
+ // file may not exist, therefore not loaded
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ // unload all tiles from given map
+ MMapData* mmap = itr->second;
+ for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i)
+ {
+ uint32 x = (i->first >> 16);
+ uint32 y = (i->first & 0x0000FFFF);
+ if (dtStatusFailed(mmap->navMesh->removeTile(i->second, NULL, NULL)))
+ TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
+ else
+ {
+ --loadedTiles;
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId);
+ }
+ }
+
+ delete mmap;
+ itr->second = nullptr;
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded %03i.mmap", mapId);
+
+ return true;
+ }
+
+ bool MMapManager::unloadMapInstance(uint32 mapId, uint32 instanceId)
+ {
+ // check if we have this map loaded
+ MMapDataSet::const_iterator itr = GetMMapData(mapId);
+ if (itr == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ MMapData* mmap = itr->second;
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId);
+ return false;
+ }
+
+ dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
+
+ dtFreeNavMeshQuery(query);
+ mmap->navMeshQueries.erase(instanceId);
+ TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId);
+
+ return true;
+ }
+
+ dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
+ {
+ MMapDataSet::const_iterator itr = GetMMapData(mapId);
+ if (itr == loadedMMaps.end())
+ return NULL;
+
+ return itr->second->navMesh;
+ }
+
+ dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
+ {
+ MMapDataSet::const_iterator itr = GetMMapData(mapId);
+ if (itr == loadedMMaps.end())
+ return NULL;
+
+ MMapData* mmap = itr->second;
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ // allocate mesh query
+ dtNavMeshQuery* query = dtAllocNavMeshQuery();
+ ASSERT(query);
+ if (dtStatusFailed(query->init(mmap->navMesh, 1024)))
+ {
+ dtFreeNavMeshQuery(query);
+ TC_LOG_ERROR("maps", "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ return NULL;
+ }
+
+ TC_LOG_DEBUG("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
+ }
+
+ return mmap->navMeshQueries[instanceId];
+ }
+}
diff --git a/src/common/Collision/Management/MMapManager.h b/src/common/Collision/Management/MMapManager.h
new file mode 100644
index 00000000000..42292b76942
--- /dev/null
+++ b/src/common/Collision/Management/MMapManager.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _MMAP_MANAGER_H
+#define _MMAP_MANAGER_H
+
+#include "Define.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+// move map related classes
+namespace MMAP
+{
+ typedef std::unordered_map<uint32, dtTileRef> MMapTileSet;
+ typedef std::unordered_map<uint32, dtNavMeshQuery*> NavMeshQuerySet;
+
+ // dummy struct to hold map's mmap data
+ struct MMapData
+ {
+ MMapData(dtNavMesh* mesh) : navMesh(mesh) { }
+ ~MMapData()
+ {
+ for (NavMeshQuerySet::iterator i = navMeshQueries.begin(); i != navMeshQueries.end(); ++i)
+ dtFreeNavMeshQuery(i->second);
+
+ if (navMesh)
+ dtFreeNavMesh(navMesh);
+ }
+
+ dtNavMesh* navMesh;
+
+ // we have to use single dtNavMeshQuery for every instance, since those are not thread safe
+ NavMeshQuerySet navMeshQueries; // instanceId to query
+ MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile]
+ };
+
+
+ typedef std::unordered_map<uint32, MMapData*> MMapDataSet;
+
+ // singleton class
+ // holds all all access to mmap loading unloading and meshes
+ class MMapManager
+ {
+ public:
+ MMapManager() : loadedTiles(0), thread_safe_environment(true) {}
+ ~MMapManager();
+
+ void InitializeThreadUnsafe(const std::vector<uint32>& mapIds);
+ bool loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId);
+ bool unloadMapInstance(uint32 mapId, uint32 instanceId);
+
+ // the returned [dtNavMeshQuery const*] is NOT threadsafe
+ dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
+ dtNavMesh const* GetNavMesh(uint32 mapId);
+
+ uint32 getLoadedTilesCount() const { return loadedTiles; }
+ uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
+ private:
+ bool loadMapData(uint32 mapId);
+ uint32 packTileID(int32 x, int32 y);
+
+ MMapDataSet::const_iterator GetMMapData(uint32 mapId) const;
+ MMapDataSet loadedMMaps;
+ uint32 loadedTiles;
+ bool thread_safe_environment;
+ };
+}
+
+#endif \ No newline at end of file
diff --git a/src/common/Collision/Management/VMapFactory.cpp b/src/common/Collision/Management/VMapFactory.cpp
new file mode 100644
index 00000000000..4c2750a9e5c
--- /dev/null
+++ b/src/common/Collision/Management/VMapFactory.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "VMapFactory.h"
+#include "VMapManager2.h"
+
+namespace VMAP
+{
+ IVMapManager* gVMapManager = NULL;
+
+ //===============================================
+ // just return the instance
+ IVMapManager* VMapFactory::createOrGetVMapManager()
+ {
+ if (gVMapManager == nullptr)
+ gVMapManager= new VMapManager2(); // should be taken from config ... Please change if you like :-)
+ return gVMapManager;
+ }
+
+ //===============================================
+ // delete all internal data structures
+ void VMapFactory::clear()
+ {
+ delete gVMapManager;
+ gVMapManager = NULL;
+ }
+}
diff --git a/src/common/Collision/Management/VMapFactory.h b/src/common/Collision/Management/VMapFactory.h
new file mode 100644
index 00000000000..3067c2919d5
--- /dev/null
+++ b/src/common/Collision/Management/VMapFactory.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _VMAPFACTORY_H
+#define _VMAPFACTORY_H
+
+#include "IVMapManager.h"
+
+/**
+This is the access point to the VMapManager.
+*/
+
+namespace VMAP
+{
+ //===========================================================
+
+ class VMapFactory
+ {
+ public:
+ static IVMapManager* createOrGetVMapManager();
+ static void clear();
+ };
+
+}
+#endif
diff --git a/src/common/Collision/Management/VMapManager2.cpp b/src/common/Collision/Management/VMapManager2.cpp
new file mode 100644
index 00000000000..a63eac1b935
--- /dev/null
+++ b/src/common/Collision/Management/VMapManager2.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <iostream>
+#include <iomanip>
+#include <string>
+#include <sstream>
+#include "VMapManager2.h"
+#include "MapTree.h"
+#include "ModelInstance.h"
+#include "WorldModel.h"
+#include <G3D/Vector3.h>
+#include "Log.h"
+#include "VMapDefinitions.h"
+#include "Errors.h"
+
+using G3D::Vector3;
+
+namespace VMAP
+{
+ VMapManager2::VMapManager2()
+ {
+ GetLiquidFlagsPtr = &GetLiquidFlagsDummy;
+ IsVMAPDisabledForPtr = &IsVMAPDisabledForDummy;
+ thread_safe_environment = true;
+ }
+
+ VMapManager2::~VMapManager2(void)
+ {
+ for (InstanceTreeMap::iterator i = iInstanceMapTrees.begin(); i != iInstanceMapTrees.end(); ++i)
+ {
+ delete i->second;
+ }
+ for (ModelFileMap::iterator i = iLoadedModelFiles.begin(); i != iLoadedModelFiles.end(); ++i)
+ {
+ delete i->second.getModel();
+ }
+ }
+
+ void VMapManager2::InitializeThreadUnsafe(const std::vector<uint32>& mapIds)
+ {
+ // the caller must pass the list of all mapIds that will be used in the VMapManager2 lifetime
+ for (const uint32& mapId : mapIds)
+ iInstanceMapTrees.insert(InstanceTreeMap::value_type(mapId, nullptr));
+
+ thread_safe_environment = false;
+ }
+
+ Vector3 VMapManager2::convertPositionToInternalRep(float x, float y, float z) const
+ {
+ Vector3 pos;
+ const float mid = 0.5f * 64.0f * 533.33333333f;
+ pos.x = mid - x;
+ pos.y = mid - y;
+ pos.z = z;
+
+ return pos;
+ }
+
+ // move to MapTree too?
+ std::string VMapManager2::getMapFileName(unsigned int mapId)
+ {
+ std::stringstream fname;
+ fname.width(3);
+ fname << std::setfill('0') << mapId << std::string(MAP_FILENAME_EXTENSION2);
+
+ return fname.str();
+ }
+
+ int VMapManager2::loadMap(const char* basePath, unsigned int mapId, int x, int y)
+ {
+ int result = VMAP_LOAD_RESULT_IGNORED;
+ if (isMapLoadingEnabled())
+ {
+ if (_loadMap(mapId, basePath, x, y))
+ result = VMAP_LOAD_RESULT_OK;
+ else
+ result = VMAP_LOAD_RESULT_ERROR;
+ }
+
+ return result;
+ }
+
+ InstanceTreeMap::const_iterator VMapManager2::GetMapTree(uint32 mapId) const
+ {
+ // return the iterator if found or end() if not found/NULL
+ InstanceTreeMap::const_iterator itr = iInstanceMapTrees.find(mapId);
+ if (itr != iInstanceMapTrees.cend() && !itr->second)
+ itr = iInstanceMapTrees.cend();
+
+ return itr;
+ }
+
+ // load one tile (internal use only)
+ bool VMapManager2::_loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY)
+ {
+ InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
+ if (instanceTree == iInstanceMapTrees.end())
+ {
+ if (thread_safe_environment)
+ instanceTree = iInstanceMapTrees.insert(InstanceTreeMap::value_type(mapId, nullptr)).first;
+ else
+ ASSERT(false, "Invalid mapId %u tile [%u, %u] passed to VMapManager2 after startup in thread unsafe environment",
+ mapId, tileX, tileY);
+ }
+
+ if (!instanceTree->second)
+ {
+ std::string mapFileName = getMapFileName(mapId);
+ StaticMapTree* newTree = new StaticMapTree(mapId, basePath);
+ if (!newTree->InitMap(mapFileName, this))
+ {
+ delete newTree;
+ return false;
+ }
+ instanceTree->second = newTree;
+ }
+
+ return instanceTree->second->LoadMapTile(tileX, tileY, this);
+ }
+
+ void VMapManager2::unloadMap(unsigned int mapId)
+ {
+ InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
+ if (instanceTree != iInstanceMapTrees.end() && instanceTree->second)
+ {
+ instanceTree->second->UnloadMap(this);
+ if (instanceTree->second->numLoadedTiles() == 0)
+ {
+ delete instanceTree->second;
+ instanceTree->second = nullptr;
+ }
+ }
+ }
+
+ void VMapManager2::unloadMap(unsigned int mapId, int x, int y)
+ {
+ InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
+ if (instanceTree != iInstanceMapTrees.end() && instanceTree->second)
+ {
+ instanceTree->second->UnloadMapTile(x, y, this);
+ if (instanceTree->second->numLoadedTiles() == 0)
+ {
+ delete instanceTree->second;
+ instanceTree->second = nullptr;
+ }
+ }
+ }
+
+ bool VMapManager2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2)
+ {
+ if (!isLineOfSightCalcEnabled() || IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS))
+ return true;
+
+ InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
+ if (instanceTree != iInstanceMapTrees.end())
+ {
+ Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1);
+ Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2);
+ if (pos1 != pos2)
+ {
+ return instanceTree->second->isInLineOfSight(pos1, pos2);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ get the hit position and return true if we hit something
+ otherwise the result pos will be the dest pos
+ */
+ bool VMapManager2::getObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float modifyDist)
+ {
+ if (isLineOfSightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS))
+ {
+ InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
+ if (instanceTree != iInstanceMapTrees.end())
+ {
+ Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1);
+ Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2);
+ Vector3 resultPos;
+ bool result = instanceTree->second->getObjectHitPos(pos1, pos2, resultPos, modifyDist);
+ resultPos = convertPositionToInternalRep(resultPos.x, resultPos.y, resultPos.z);
+ rx = resultPos.x;
+ ry = resultPos.y;
+ rz = resultPos.z;
+ return result;
+ }
+ }
+
+ rx = x2;
+ ry = y2;
+ rz = z2;
+
+ return false;
+ }
+
+ /**
+ get height or INVALID_HEIGHT if no height available
+ */
+
+ float VMapManager2::getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist)
+ {
+ if (isHeightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_HEIGHT))
+ {
+ InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
+ if (instanceTree != iInstanceMapTrees.end())
+ {
+ Vector3 pos = convertPositionToInternalRep(x, y, z);
+ float height = instanceTree->second->getHeight(pos, maxSearchDist);
+ if (!(height < G3D::finf()))
+ return height = VMAP_INVALID_HEIGHT_VALUE; // No height
+
+ return height;
+ }
+ }
+
+ return VMAP_INVALID_HEIGHT_VALUE;
+ }
+
+ bool VMapManager2::getAreaInfo(unsigned int mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
+ {
+ if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG))
+ {
+ InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
+ if (instanceTree != iInstanceMapTrees.end())
+ {
+ Vector3 pos = convertPositionToInternalRep(x, y, z);
+ bool result = instanceTree->second->getAreaInfo(pos, flags, adtId, rootId, groupId);
+ // z is not touched by convertPositionToInternalRep(), so just copy
+ z = pos.z;
+ return result;
+ }
+ }
+
+ return false;
+ }
+
+ bool VMapManager2::GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const
+ {
+ if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS))
+ {
+ InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
+ if (instanceTree != iInstanceMapTrees.end())
+ {
+ LocationInfo info;
+ Vector3 pos = convertPositionToInternalRep(x, y, z);
+ if (instanceTree->second->GetLocationInfo(pos, info))
+ {
+ floor = info.ground_Z;
+ ASSERT(floor < std::numeric_limits<float>::max());
+ ASSERT(info.hitModel);
+ type = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc
+ if (reqLiquidType && !(GetLiquidFlagsPtr(type) & reqLiquidType))
+ return false;
+ ASSERT(info.hitInstance);
+ if (info.hitInstance->GetLiquidLevel(pos, info, level))
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ WorldModel* VMapManager2::acquireModelInstance(const std::string& basepath, const std::string& filename)
+ {
+ //! Critical section, thread safe access to iLoadedModelFiles
+ std::lock_guard<std::mutex> lock(LoadedModelFilesLock);
+
+ ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
+ if (model == iLoadedModelFiles.end())
+ {
+ WorldModel* worldmodel = new WorldModel();
+ if (!worldmodel->readFile(basepath + filename + ".vmo"))
+ {
+ VMAP_ERROR_LOG("misc", "VMapManager2: could not load '%s%s.vmo'", basepath.c_str(), filename.c_str());
+ delete worldmodel;
+ return NULL;
+ }
+ VMAP_DEBUG_LOG("maps", "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str());
+ model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first;
+ model->second.setModel(worldmodel);
+ }
+ model->second.incRefCount();
+ return model->second.getModel();
+ }
+
+ void VMapManager2::releaseModelInstance(const std::string &filename)
+ {
+ //! Critical section, thread safe access to iLoadedModelFiles
+ std::lock_guard<std::mutex> lock(LoadedModelFilesLock);
+
+ ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
+ if (model == iLoadedModelFiles.end())
+ {
+ VMAP_ERROR_LOG("misc", "VMapManager2: trying to unload non-loaded file '%s'", filename.c_str());
+ return;
+ }
+ if (model->second.decRefCount() == 0)
+ {
+ VMAP_DEBUG_LOG("maps", "VMapManager2: unloading file '%s'", filename.c_str());
+ delete model->second.getModel();
+ iLoadedModelFiles.erase(model);
+ }
+ }
+
+ bool VMapManager2::existsMap(const char* basePath, unsigned int mapId, int x, int y)
+ {
+ return StaticMapTree::CanLoadMap(std::string(basePath), mapId, x, y);
+ }
+
+ void VMapManager2::getInstanceMapTree(InstanceTreeMap &instanceMapTree)
+ {
+ instanceMapTree = iInstanceMapTrees;
+ }
+
+} // namespace VMAP
diff --git a/src/common/Collision/Management/VMapManager2.h b/src/common/Collision/Management/VMapManager2.h
new file mode 100644
index 00000000000..c2e1aee1ff7
--- /dev/null
+++ b/src/common/Collision/Management/VMapManager2.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _VMAPMANAGER2_H
+#define _VMAPMANAGER2_H
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+#include "Define.h"
+#include "IVMapManager.h"
+
+//===========================================================
+
+#define MAP_FILENAME_EXTENSION2 ".vmtree"
+
+#define FILENAMEBUFFER_SIZE 500
+
+/**
+This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on.
+For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile.
+Each global map or instance has its own dynamic BSP-Tree.
+The loaded ModelContainers are included in one of these BSP-Trees.
+Additionally a table to match map ids and map names is used.
+*/
+
+//===========================================================
+
+namespace G3D
+{
+ class Vector3;
+}
+
+namespace VMAP
+{
+ class StaticMapTree;
+ class WorldModel;
+
+ class ManagedModel
+ {
+ public:
+ ManagedModel() : iModel(nullptr), iRefCount(0) { }
+ void setModel(WorldModel* model) { iModel = model; }
+ WorldModel* getModel() { return iModel; }
+ void incRefCount() { ++iRefCount; }
+ int decRefCount() { return --iRefCount; }
+ protected:
+ WorldModel* iModel;
+ int iRefCount;
+ };
+
+ typedef std::unordered_map<uint32, StaticMapTree*> InstanceTreeMap;
+ typedef std::unordered_map<std::string, ManagedModel> ModelFileMap;
+
+ enum DisableTypes
+ {
+ VMAP_DISABLE_AREAFLAG = 0x1,
+ VMAP_DISABLE_HEIGHT = 0x2,
+ VMAP_DISABLE_LOS = 0x4,
+ VMAP_DISABLE_LIQUIDSTATUS = 0x8
+ };
+
+ class VMapManager2 : public IVMapManager
+ {
+ protected:
+ // Tree to check collision
+ ModelFileMap iLoadedModelFiles;
+ InstanceTreeMap iInstanceMapTrees;
+ bool thread_safe_environment;
+ // Mutex for iLoadedModelFiles
+ std::mutex LoadedModelFilesLock;
+
+ bool _loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY);
+ /* void _unloadMap(uint32 pMapId, uint32 x, uint32 y); */
+
+ static uint32 GetLiquidFlagsDummy(uint32) { return 0; }
+ static bool IsVMAPDisabledForDummy(uint32 /*entry*/, uint8 /*flags*/) { return false; }
+
+ InstanceTreeMap::const_iterator GetMapTree(uint32 mapId) const;
+
+ public:
+ // public for debug
+ G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const;
+ static std::string getMapFileName(unsigned int mapId);
+
+ VMapManager2();
+ ~VMapManager2(void);
+
+ void InitializeThreadUnsafe(const std::vector<uint32>& mapIds);
+ int loadMap(const char* pBasePath, unsigned int mapId, int x, int y) override;
+
+ void unloadMap(unsigned int mapId, int x, int y) override;
+ void unloadMap(unsigned int mapId) override;
+
+ bool isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2) override ;
+ /**
+ fill the hit pos and return true, if an object was hit
+ */
+ bool getObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) override;
+ float getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist) override;
+
+ bool processCommand(char* /*command*/) override { return false; } // for debug and extensions
+
+ bool getAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const override;
+ bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const override;
+
+ WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename);
+ void releaseModelInstance(const std::string& filename);
+
+ // what's the use of this? o.O
+ virtual std::string getDirFileName(unsigned int mapId, int /*x*/, int /*y*/) const override
+ {
+ return getMapFileName(mapId);
+ }
+ virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y) override;
+
+ void getInstanceMapTree(InstanceTreeMap &instanceMapTree);
+
+ typedef uint32(*GetLiquidFlagsFn)(uint32 liquidType);
+ GetLiquidFlagsFn GetLiquidFlagsPtr;
+
+ typedef bool(*IsVMAPDisabledForFn)(uint32 entry, uint8 flags);
+ IsVMAPDisabledForFn IsVMAPDisabledForPtr;
+ };
+}
+
+#endif
diff --git a/src/common/Collision/Maps/MapDefines.h b/src/common/Collision/Maps/MapDefines.h
new file mode 100644
index 00000000000..538a78a38d4
--- /dev/null
+++ b/src/common/Collision/Maps/MapDefines.h
@@ -0,0 +1,36 @@
+#ifndef _MAPDEFINES_H
+#define _MAPDEFINES_H
+
+#include "Define.h"
+#include "DetourNavMesh.h"
+
+const uint32 MMAP_MAGIC = 0x4d4d4150; // 'MMAP'
+#define MMAP_VERSION 5
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids : 1;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) { }
+};
+
+enum NavTerrain
+{
+ NAV_EMPTY = 0x00,
+ NAV_GROUND = 0x01,
+ NAV_MAGMA = 0x02,
+ NAV_SLIME = 0x04,
+ NAV_WATER = 0x08,
+ NAV_UNUSED1 = 0x10,
+ NAV_UNUSED2 = 0x20,
+ NAV_UNUSED3 = 0x40,
+ NAV_UNUSED4 = 0x80
+ // we only have 8 bits
+};
+
+#endif
diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp
new file mode 100644
index 00000000000..e374da4f1b9
--- /dev/null
+++ b/src/common/Collision/Maps/MapTree.cpp
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "MapTree.h"
+#include "ModelInstance.h"
+#include "VMapManager2.h"
+#include "VMapDefinitions.h"
+#include "Log.h"
+#include "Errors.h"
+
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include <limits>
+
+using G3D::Vector3;
+
+namespace VMAP
+{
+
+ class MapRayCallback
+ {
+ public:
+ MapRayCallback(ModelInstance* val): prims(val), hit(false) { }
+ bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit=true)
+ {
+ bool result = prims[entry].intersectRay(ray, distance, pStopAtFirstHit);
+ if (result)
+ hit = true;
+ return result;
+ }
+ bool didHit() { return hit; }
+ protected:
+ ModelInstance* prims;
+ bool hit;
+ };
+
+ class AreaInfoCallback
+ {
+ public:
+ AreaInfoCallback(ModelInstance* val): prims(val) { }
+ void operator()(const Vector3& point, uint32 entry)
+ {
+#ifdef VMAP_DEBUG
+ TC_LOG_DEBUG("maps", "AreaInfoCallback: trying to intersect '%s'", prims[entry].name.c_str());
+#endif
+ prims[entry].intersectPoint(point, aInfo);
+ }
+
+ ModelInstance* prims;
+ AreaInfo aInfo;
+ };
+
+ class LocationInfoCallback
+ {
+ public:
+ LocationInfoCallback(ModelInstance* val, LocationInfo &info): prims(val), locInfo(info), result(false) { }
+ void operator()(const Vector3& point, uint32 entry)
+ {
+#ifdef VMAP_DEBUG
+ TC_LOG_DEBUG("maps", "LocationInfoCallback: trying to intersect '%s'", prims[entry].name.c_str());
+#endif
+ if (prims[entry].GetLocationInfo(point, locInfo))
+ result = true;
+ }
+
+ ModelInstance* prims;
+ LocationInfo &locInfo;
+ bool result;
+ };
+
+ //=========================================================
+
+ std::string StaticMapTree::getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ std::stringstream tilefilename;
+ tilefilename.fill('0');
+ tilefilename << std::setw(3) << mapID << '_';
+ //tilefilename << std::setw(2) << tileX << '_' << std::setw(2) << tileY << ".vmtile";
+ tilefilename << std::setw(2) << tileY << '_' << std::setw(2) << tileX << ".vmtile";
+ return tilefilename.str();
+ }
+
+ bool StaticMapTree::getAreaInfo(Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
+ {
+ AreaInfoCallback intersectionCallBack(iTreeValues);
+ iTree.intersectPoint(pos, intersectionCallBack);
+ if (intersectionCallBack.aInfo.result)
+ {
+ flags = intersectionCallBack.aInfo.flags;
+ adtId = intersectionCallBack.aInfo.adtId;
+ rootId = intersectionCallBack.aInfo.rootId;
+ groupId = intersectionCallBack.aInfo.groupId;
+ pos.z = intersectionCallBack.aInfo.ground_Z;
+ return true;
+ }
+ return false;
+ }
+
+ bool StaticMapTree::GetLocationInfo(const Vector3 &pos, LocationInfo &info) const
+ {
+ LocationInfoCallback intersectionCallBack(iTreeValues, info);
+ iTree.intersectPoint(pos, intersectionCallBack);
+ return intersectionCallBack.result;
+ }
+
+ StaticMapTree::StaticMapTree(uint32 mapID, const std::string &basePath) :
+ iMapID(mapID), iIsTiled(false), iTreeValues(NULL),
+ iNTreeValues(0), iBasePath(basePath)
+ {
+ if (iBasePath.length() > 0 && iBasePath[iBasePath.length()-1] != '/' && iBasePath[iBasePath.length()-1] != '\\')
+ {
+ iBasePath.push_back('/');
+ }
+ }
+
+ //=========================================================
+ //! Make sure to call unloadMap() to unregister acquired model references before destroying
+ StaticMapTree::~StaticMapTree()
+ {
+ delete[] iTreeValues;
+ }
+
+ //=========================================================
+ /**
+ If intersection is found within pMaxDist, sets pMaxDist to intersection distance and returns true.
+ Else, pMaxDist is not modified and returns false;
+ */
+
+ bool StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool pStopAtFirstHit) const
+ {
+ float distance = pMaxDist;
+ MapRayCallback intersectionCallBack(iTreeValues);
+ iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit);
+ if (intersectionCallBack.didHit())
+ pMaxDist = distance;
+ return intersectionCallBack.didHit();
+ }
+ //=========================================================
+
+ bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) const
+ {
+ float maxDist = (pos2 - pos1).magnitude();
+ // return false if distance is over max float, in case of cheater teleporting to the end of the universe
+ if (maxDist == std::numeric_limits<float>::max() || !std::isfinite(maxDist))
+ return false;
+
+ // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
+ ASSERT(maxDist < std::numeric_limits<float>::max());
+ // prevent NaN values which can cause BIH intersection to enter infinite loop
+ if (maxDist < 1e-10f)
+ return true;
+ // direction with length of 1
+ G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist);
+ if (getIntersectionTime(ray, maxDist, true))
+ return false;
+
+ return true;
+ }
+ //=========================================================
+ /**
+ When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
+ Return the hit pos or the original dest pos
+ */
+
+ bool StaticMapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) const
+ {
+ bool result=false;
+ float maxDist = (pPos2 - pPos1).magnitude();
+ // valid map coords should *never ever* produce float overflow, but this would produce NaNs too
+ ASSERT(maxDist < std::numeric_limits<float>::max());
+ // prevent NaN values which can cause BIH intersection to enter infinite loop
+ if (maxDist < 1e-10f)
+ {
+ pResultHitPos = pPos2;
+ return false;
+ }
+ Vector3 dir = (pPos2 - pPos1)/maxDist; // direction with length of 1
+ G3D::Ray ray(pPos1, dir);
+ float dist = maxDist;
+ if (getIntersectionTime(ray, dist, false))
+ {
+ pResultHitPos = pPos1 + dir * dist;
+ if (pModifyDist < 0)
+ {
+ if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
+ {
+ pResultHitPos = pResultHitPos + dir*pModifyDist;
+ }
+ else
+ {
+ pResultHitPos = pPos1;
+ }
+ }
+ else
+ {
+ pResultHitPos = pResultHitPos + dir*pModifyDist;
+ }
+ result = true;
+ }
+ else
+ {
+ pResultHitPos = pPos2;
+ result = false;
+ }
+ return result;
+ }
+
+ //=========================================================
+
+ float StaticMapTree::getHeight(const Vector3& pPos, float maxSearchDist) const
+ {
+ float height = G3D::finf();
+ Vector3 dir = Vector3(0, 0, -1);
+ G3D::Ray ray(pPos, dir); // direction with length of 1
+ float maxDist = maxSearchDist;
+ if (getIntersectionTime(ray, maxDist, false))
+ {
+ height = pPos.z - maxDist;
+ }
+ return(height);
+ }
+
+ //=========================================================
+
+ bool StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ std::string basePath = vmapPath;
+ if (basePath.length() > 0 && basePath[basePath.length()-1] != '/' && basePath[basePath.length()-1] != '\\')
+ basePath.push_back('/');
+ std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
+ bool success = true;
+ FILE* rf = fopen(fullname.c_str(), "rb");
+ if (!rf)
+ return false;
+ /// @todo check magic number when implemented...
+ char tiled;
+ char chunk[8];
+ if (!readChunk(rf, chunk, VMAP_MAGIC, 8) || fread(&tiled, sizeof(char), 1, rf) != 1)
+ {
+ fclose(rf);
+ return false;
+ }
+ if (tiled)
+ {
+ std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
+ FILE* tf = fopen(tilefile.c_str(), "rb");
+ if (!tf)
+ success = false;
+ else
+ {
+ if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
+ success = false;
+ fclose(tf);
+ }
+ }
+ fclose(rf);
+ return success;
+ }
+
+ //=========================================================
+
+ bool StaticMapTree::InitMap(const std::string &fname, VMapManager2* vm)
+ {
+ VMAP_DEBUG_LOG("maps", "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str());
+ bool success = false;
+ std::string fullname = iBasePath + fname;
+ FILE* rf = fopen(fullname.c_str(), "rb");
+ if (!rf)
+ return false;
+
+ char chunk[8];
+ char tiled = '\0';
+
+ if (readChunk(rf, chunk, VMAP_MAGIC, 8) && fread(&tiled, sizeof(char), 1, rf) == 1 &&
+ readChunk(rf, chunk, "NODE", 4) && iTree.readFromFile(rf))
+ {
+ iNTreeValues = iTree.primCount();
+ iTreeValues = new ModelInstance[iNTreeValues];
+ success = readChunk(rf, chunk, "GOBJ", 4);
+ }
+
+ iIsTiled = tiled != '\0';
+
+ // global model spawns
+ // only non-tiled maps have them, and if so exactly one (so far at least...)
+ ModelSpawn spawn;
+#ifdef VMAP_DEBUG
+ TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : map isTiled: %u", static_cast<uint32>(iIsTiled));
+#endif
+ if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
+ {
+ WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
+ VMAP_DEBUG_LOG("maps", "StaticMapTree::InitMap() : loading %s", spawn.name.c_str());
+ if (model)
+ {
+ // assume that global model always is the first and only tree value (could be improved...)
+ iTreeValues[0] = ModelInstance(spawn, model);
+ iLoadedSpawns[0] = 1;
+ }
+ else
+ {
+ success = false;
+ VMAP_ERROR_LOG("misc", "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str());
+ }
+ }
+
+ fclose(rf);
+ return success;
+ }
+
+ //=========================================================
+
+ void StaticMapTree::UnloadMap(VMapManager2* vm)
+ {
+ for (loadedSpawnMap::iterator i = iLoadedSpawns.begin(); i != iLoadedSpawns.end(); ++i)
+ {
+ iTreeValues[i->first].setUnloaded();
+ for (uint32 refCount = 0; refCount < i->second; ++refCount)
+ vm->releaseModelInstance(iTreeValues[i->first].name);
+ }
+ iLoadedSpawns.clear();
+ iLoadedTiles.clear();
+ }
+
+ //=========================================================
+
+ bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm)
+ {
+ if (!iIsTiled)
+ {
+ // currently, core creates grids for all maps, whether it has terrain tiles or not
+ // so we need "fake" tile loads to know when we can unload map geometry
+ iLoadedTiles[packTileID(tileX, tileY)] = false;
+ return true;
+ }
+ if (!iTreeValues)
+ {
+ VMAP_ERROR_LOG("misc", "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY);
+ return false;
+ }
+ bool result = true;
+
+ std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
+ FILE* tf = fopen(tilefile.c_str(), "rb");
+ if (tf)
+ {
+ char chunk[8];
+
+ if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
+ result = false;
+ uint32 numSpawns = 0;
+ if (result && fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
+ result = false;
+ for (uint32 i=0; i<numSpawns && result; ++i)
+ {
+ // read model spawns
+ ModelSpawn spawn;
+ result = ModelSpawn::readFromFile(tf, spawn);
+ if (result)
+ {
+ // acquire model instance
+ WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
+ if (!model)
+ VMAP_ERROR_LOG("misc", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY);
+
+ // update tree
+ uint32 referencedVal;
+
+ if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1)
+ {
+ if (!iLoadedSpawns.count(referencedVal))
+ {
+ if (referencedVal > iNTreeValues)
+ {
+ VMAP_ERROR_LOG("maps", "StaticMapTree::LoadMapTile() : invalid tree element (%u/%u) referenced in tile %s", referencedVal, iNTreeValues, tilefile.c_str());
+ continue;
+ }
+
+ iTreeValues[referencedVal] = ModelInstance(spawn, model);
+ iLoadedSpawns[referencedVal] = 1;
+ }
+ else
+ {
+ ++iLoadedSpawns[referencedVal];
+#ifdef VMAP_DEBUG
+ if (iTreeValues[referencedVal].ID != spawn.ID)
+ TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
+ else if (iTreeValues[referencedVal].name != spawn.name)
+ TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID=%u", spawn.ID);
+#endif
+ }
+ }
+ else
+ result = false;
+ }
+ }
+ iLoadedTiles[packTileID(tileX, tileY)] = true;
+ fclose(tf);
+ }
+ else
+ iLoadedTiles[packTileID(tileX, tileY)] = false;
+ return result;
+ }
+
+ //=========================================================
+
+ void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm)
+ {
+ uint32 tileID = packTileID(tileX, tileY);
+ loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
+ if (tile == iLoadedTiles.end())
+ {
+ VMAP_ERROR_LOG("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY);
+ return;
+ }
+ if (tile->second) // file associated with tile
+ {
+ std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
+ FILE* tf = fopen(tilefile.c_str(), "rb");
+ if (tf)
+ {
+ bool result=true;
+ char chunk[8];
+ if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
+ result = false;
+ uint32 numSpawns;
+ if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
+ result = false;
+ for (uint32 i=0; i<numSpawns && result; ++i)
+ {
+ // read model spawns
+ ModelSpawn spawn;
+ result = ModelSpawn::readFromFile(tf, spawn);
+ if (result)
+ {
+ // release model instance
+ vm->releaseModelInstance(spawn.name);
+
+ // update tree
+ uint32 referencedNode;
+
+ if (fread(&referencedNode, sizeof(uint32), 1, tf) != 1)
+ result = false;
+ else
+ {
+ if (!iLoadedSpawns.count(referencedNode))
+ VMAP_ERROR_LOG("misc", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
+ else if (--iLoadedSpawns[referencedNode] == 0)
+ {
+ iTreeValues[referencedNode].setUnloaded();
+ iLoadedSpawns.erase(referencedNode);
+ }
+ }
+ }
+ }
+ fclose(tf);
+ }
+ }
+ iLoadedTiles.erase(tile);
+ }
+
+ void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
+ {
+ models = iTreeValues;
+ count = iNTreeValues;
+ }
+}
diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h
new file mode 100644
index 00000000000..08bd5c3d3a1
--- /dev/null
+++ b/src/common/Collision/Maps/MapTree.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _MAPTREE_H
+#define _MAPTREE_H
+
+#include "Define.h"
+#include "BoundingIntervalHierarchy.h"
+#include <unordered_map>
+
+namespace VMAP
+{
+ class ModelInstance;
+ class GroupModel;
+ class VMapManager2;
+
+ struct LocationInfo
+ {
+ LocationInfo(): hitInstance(nullptr), hitModel(nullptr), ground_Z(-G3D::finf()) { }
+ const ModelInstance* hitInstance;
+ const GroupModel* hitModel;
+ float ground_Z;
+ };
+
+ class StaticMapTree
+ {
+ typedef std::unordered_map<uint32, bool> loadedTileMap;
+ typedef std::unordered_map<uint32, uint32> loadedSpawnMap;
+ private:
+ uint32 iMapID;
+ bool iIsTiled;
+ BIH iTree;
+ ModelInstance* iTreeValues; // the tree entries
+ uint32 iNTreeValues;
+
+ // Store all the map tile idents that are loaded for that map
+ // some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
+ // empty tiles have no tile file, hence map with bool instead of just a set (consistency check)
+ loadedTileMap iLoadedTiles;
+ // stores <tree_index, reference_count> to invalidate tree values, unload map, and to be able to report errors
+ loadedSpawnMap iLoadedSpawns;
+ std::string iBasePath;
+
+ private:
+ bool getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool pStopAtFirstHit) const;
+ //bool containsLoadedMapTile(unsigned int pTileIdent) const { return(iLoadedMapTiles.containsKey(pTileIdent)); }
+ public:
+ static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY);
+ static uint32 packTileID(uint32 tileX, uint32 tileY) { return tileX<<16 | tileY; }
+ static void unpackTileID(uint32 ID, uint32 &tileX, uint32 &tileY) { tileX = ID>>16; tileY = ID&0xFF; }
+ static bool CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY);
+
+ StaticMapTree(uint32 mapID, const std::string &basePath);
+ ~StaticMapTree();
+
+ bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2) const;
+ bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const;
+ float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const;
+ bool getAreaInfo(G3D::Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const;
+ bool GetLocationInfo(const G3D::Vector3 &pos, LocationInfo &info) const;
+
+ bool InitMap(const std::string &fname, VMapManager2* vm);
+ void UnloadMap(VMapManager2* vm);
+ bool LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
+ void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
+ bool isTiled() const { return iIsTiled; }
+ uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
+ void getModelInstances(ModelInstance* &models, uint32 &count);
+
+ private:
+ StaticMapTree(StaticMapTree const& right) = delete;
+ StaticMapTree& operator=(StaticMapTree const& right) = delete;
+ };
+
+ struct AreaInfo
+ {
+ AreaInfo(): result(false), ground_Z(-G3D::finf()), flags(0), adtId(0),
+ rootId(0), groupId(0) { }
+ bool result;
+ float ground_Z;
+ uint32 flags;
+ int32 adtId;
+ int32 rootId;
+ int32 groupId;
+ };
+} // VMAP
+
+#endif // _MAPTREE_H
diff --git a/src/common/Collision/Maps/TileAssembler.cpp b/src/common/Collision/Maps/TileAssembler.cpp
new file mode 100644
index 00000000000..ec7b759f975
--- /dev/null
+++ b/src/common/Collision/Maps/TileAssembler.cpp
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "TileAssembler.h"
+#include "MapTree.h"
+#include "BoundingIntervalHierarchy.h"
+#include "VMapDefinitions.h"
+
+#include <set>
+#include <iomanip>
+#include <sstream>
+
+using G3D::Vector3;
+using G3D::AABox;
+using G3D::inf;
+using std::pair;
+
+template<> struct BoundsTrait<VMAP::ModelSpawn*>
+{
+ static void getBounds(const VMAP::ModelSpawn* const &obj, G3D::AABox& out) { out = obj->getBounds(); }
+};
+
+namespace VMAP
+{
+ bool readChunk(FILE* rf, char *dest, const char *compare, uint32 len)
+ {
+ if (fread(dest, sizeof(char), len, rf) != len) return false;
+ return memcmp(dest, compare, len) == 0;
+ }
+
+ Vector3 ModelPosition::transform(const Vector3& pIn) const
+ {
+ Vector3 out = pIn * iScale;
+ out = iRotation * out;
+ return(out);
+ }
+
+ //=================================================================
+
+ TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName)
+ : iDestDir(pDestDirName), iSrcDir(pSrcDirName), iFilterMethod(NULL), iCurrentUniqueNameId(0)
+ {
+ //mkdir(iDestDir);
+ //init();
+ }
+
+ TileAssembler::~TileAssembler()
+ {
+ //delete iCoordModelMapping;
+ }
+
+ bool TileAssembler::convertWorld2()
+ {
+ bool success = readMapSpawns();
+ if (!success)
+ return false;
+
+ // export Map data
+ for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end() && success; ++map_iter)
+ {
+ // build global map tree
+ std::vector<ModelSpawn*> mapSpawns;
+ UniqueEntryMap::iterator entry;
+ printf("Calculating model bounds for map %u...\n", map_iter->first);
+ for (entry = map_iter->second->UniqueEntries.begin(); entry != map_iter->second->UniqueEntries.end(); ++entry)
+ {
+ // M2 models don't have a bound set in WDT/ADT placement data, i still think they're not used for LoS at all on retail
+ if (entry->second.flags & MOD_M2)
+ {
+ if (!calculateTransformedBound(entry->second))
+ break;
+ }
+ else if (entry->second.flags & MOD_WORLDSPAWN) // WMO maps and terrain maps use different origin, so we need to adapt :/
+ {
+ /// @todo remove extractor hack and uncomment below line:
+ //entry->second.iPos += Vector3(533.33333f*32, 533.33333f*32, 0.f);
+ entry->second.iBound = entry->second.iBound + Vector3(533.33333f*32, 533.33333f*32, 0.f);
+ }
+ mapSpawns.push_back(&(entry->second));
+ spawnedModelFiles.insert(entry->second.name);
+ }
+
+ printf("Creating map tree for map %u...\n", map_iter->first);
+ BIH pTree;
+
+ try
+ {
+ pTree.build(mapSpawns, BoundsTrait<ModelSpawn*>::getBounds);
+ }
+ catch (std::exception& e)
+ {
+ printf("Exception ""%s"" when calling pTree.build", e.what());
+ return false;
+ }
+
+ // ===> possibly move this code to StaticMapTree class
+ std::map<uint32, uint32> modelNodeIdx;
+ for (uint32 i=0; i<mapSpawns.size(); ++i)
+ modelNodeIdx.insert(pair<uint32, uint32>(mapSpawns[i]->ID, i));
+
+ // write map tree file
+ std::stringstream mapfilename;
+ mapfilename << iDestDir << '/' << std::setfill('0') << std::setw(3) << map_iter->first << ".vmtree";
+ FILE* mapfile = fopen(mapfilename.str().c_str(), "wb");
+ if (!mapfile)
+ {
+ success = false;
+ printf("Cannot open %s\n", mapfilename.str().c_str());
+ break;
+ }
+
+ //general info
+ if (success && fwrite(VMAP_MAGIC, 1, 8, mapfile) != 8) success = false;
+ uint32 globalTileID = StaticMapTree::packTileID(65, 65);
+ pair<TileMap::iterator, TileMap::iterator> globalRange = map_iter->second->TileEntries.equal_range(globalTileID);
+ char isTiled = globalRange.first == globalRange.second; // only maps without terrain (tiles) have global WMO
+ if (success && fwrite(&isTiled, sizeof(char), 1, mapfile) != 1) success = false;
+ // Nodes
+ if (success && fwrite("NODE", 4, 1, mapfile) != 1) success = false;
+ if (success) success = pTree.writeToFile(mapfile);
+ // global map spawns (WDT), if any (most instances)
+ if (success && fwrite("GOBJ", 4, 1, mapfile) != 1) success = false;
+
+ for (TileMap::iterator glob=globalRange.first; glob != globalRange.second && success; ++glob)
+ {
+ success = ModelSpawn::writeToFile(mapfile, map_iter->second->UniqueEntries[glob->second]);
+ }
+
+ fclose(mapfile);
+
+ // <====
+
+ // write map tile files, similar to ADT files, only with extra BSP tree node info
+ TileMap &tileEntries = map_iter->second->TileEntries;
+ TileMap::iterator tile;
+ for (tile = tileEntries.begin(); tile != tileEntries.end(); ++tile)
+ {
+ const ModelSpawn &spawn = map_iter->second->UniqueEntries[tile->second];
+ if (spawn.flags & MOD_WORLDSPAWN) // WDT spawn, saved as tile 65/65 currently...
+ continue;
+ uint32 nSpawns = tileEntries.count(tile->first);
+ std::stringstream tilefilename;
+ tilefilename.fill('0');
+ tilefilename << iDestDir << '/' << std::setw(3) << map_iter->first << '_';
+ uint32 x, y;
+ StaticMapTree::unpackTileID(tile->first, x, y);
+ tilefilename << std::setw(2) << x << '_' << std::setw(2) << y << ".vmtile";
+ if (FILE* tilefile = fopen(tilefilename.str().c_str(), "wb"))
+ {
+ // file header
+ if (success && fwrite(VMAP_MAGIC, 1, 8, tilefile) != 8) success = false;
+ // write number of tile spawns
+ if (success && fwrite(&nSpawns, sizeof(uint32), 1, tilefile) != 1) success = false;
+ // write tile spawns
+ for (uint32 s=0; s<nSpawns; ++s)
+ {
+ if (s)
+ ++tile;
+ const ModelSpawn &spawn2 = map_iter->second->UniqueEntries[tile->second];
+ success = success && ModelSpawn::writeToFile(tilefile, spawn2);
+ // MapTree nodes to update when loading tile:
+ std::map<uint32, uint32>::iterator nIdx = modelNodeIdx.find(spawn2.ID);
+ if (success && fwrite(&nIdx->second, sizeof(uint32), 1, tilefile) != 1) success = false;
+ }
+ fclose(tilefile);
+ }
+ }
+ // break; //test, extract only first map; TODO: remvoe this line
+ }
+
+ // add an object models, listed in temp_gameobject_models file
+ exportGameobjectModels();
+ // export objects
+ std::cout << "\nConverting Model Files" << std::endl;
+ for (std::set<std::string>::iterator mfile = spawnedModelFiles.begin(); mfile != spawnedModelFiles.end(); ++mfile)
+ {
+ std::cout << "Converting " << *mfile << std::endl;
+ if (!convertRawFile(*mfile))
+ {
+ std::cout << "error converting " << *mfile << std::endl;
+ success = false;
+ break;
+ }
+ }
+
+ //cleanup:
+ for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end(); ++map_iter)
+ {
+ delete map_iter->second;
+ }
+ return success;
+ }
+
+ bool TileAssembler::readMapSpawns()
+ {
+ std::string fname = iSrcDir + "/dir_bin";
+ FILE* dirf = fopen(fname.c_str(), "rb");
+ if (!dirf)
+ {
+ printf("Could not read dir_bin file!\n");
+ return false;
+ }
+ printf("Read coordinate mapping...\n");
+ uint32 mapID, tileX, tileY, check=0;
+ G3D::Vector3 v1, v2;
+ ModelSpawn spawn;
+ while (!feof(dirf))
+ {
+ check = 0;
+ // read mapID, tileX, tileY, Flags, adtID, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
+ check += fread(&mapID, sizeof(uint32), 1, dirf);
+ if (check == 0) // EoF...
+ break;
+ check += fread(&tileX, sizeof(uint32), 1, dirf);
+ check += fread(&tileY, sizeof(uint32), 1, dirf);
+ if (!ModelSpawn::readFromFile(dirf, spawn))
+ break;
+
+ MapSpawns *current;
+ MapData::iterator map_iter = mapData.find(mapID);
+ if (map_iter == mapData.end())
+ {
+ printf("spawning Map %u\n", mapID);
+ mapData[mapID] = current = new MapSpawns();
+ }
+ else current = (*map_iter).second;
+ current->UniqueEntries.insert(pair<uint32, ModelSpawn>(spawn.ID, spawn));
+ current->TileEntries.insert(pair<uint32, uint32>(StaticMapTree::packTileID(tileX, tileY), spawn.ID));
+ }
+ bool success = (ferror(dirf) == 0);
+ fclose(dirf);
+ return success;
+ }
+
+ bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn)
+ {
+ std::string modelFilename(iSrcDir);
+ modelFilename.push_back('/');
+ modelFilename.append(spawn.name);
+
+ ModelPosition modelPosition;
+ modelPosition.iDir = spawn.iRot;
+ modelPosition.iScale = spawn.iScale;
+ modelPosition.init();
+
+ WorldModel_Raw raw_model;
+ if (!raw_model.Read(modelFilename.c_str()))
+ return false;
+
+ uint32 groups = raw_model.groupsArray.size();
+ if (groups != 1)
+ printf("Warning: '%s' does not seem to be a M2 model!\n", modelFilename.c_str());
+
+ AABox modelBound;
+ bool boundEmpty=true;
+
+ for (uint32 g=0; g<groups; ++g) // should be only one for M2 files...
+ {
+ std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
+
+ if (vertices.empty())
+ {
+ std::cout << "error: model '" << spawn.name << "' has no geometry!" << std::endl;
+ continue;
+ }
+
+ uint32 nvectors = vertices.size();
+ for (uint32 i = 0; i < nvectors; ++i)
+ {
+ Vector3 v = modelPosition.transform(vertices[i]);
+
+ if (boundEmpty)
+ modelBound = AABox(v, v), boundEmpty=false;
+ else
+ modelBound.merge(v);
+ }
+ }
+ spawn.iBound = modelBound + spawn.iPos;
+ spawn.flags |= MOD_HAS_BOUND;
+ return true;
+ }
+
+ struct WMOLiquidHeader
+ {
+ int xverts, yverts, xtiles, ytiles;
+ float pos_x;
+ float pos_y;
+ float pos_z;
+ short type;
+ };
+ //=================================================================
+ bool TileAssembler::convertRawFile(const std::string& pModelFilename)
+ {
+ bool success = true;
+ std::string filename = iSrcDir;
+ if (filename.length() >0)
+ filename.push_back('/');
+ filename.append(pModelFilename);
+
+ WorldModel_Raw raw_model;
+ if (!raw_model.Read(filename.c_str()))
+ return false;
+
+ // write WorldModel
+ WorldModel model;
+ model.setRootWmoID(raw_model.RootWMOID);
+ if (!raw_model.groupsArray.empty())
+ {
+ std::vector<GroupModel> groupsArray;
+
+ uint32 groups = raw_model.groupsArray.size();
+ for (uint32 g = 0; g < groups; ++g)
+ {
+ GroupModel_Raw& raw_group = raw_model.groupsArray[g];
+ groupsArray.push_back(GroupModel(raw_group.mogpflags, raw_group.GroupWMOID, raw_group.bounds ));
+ groupsArray.back().setMeshData(raw_group.vertexArray, raw_group.triangles);
+ groupsArray.back().setLiquidData(raw_group.liquid);
+ }
+
+ model.setGroupModels(groupsArray);
+ }
+
+ success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo");
+ //std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl;
+ return success;
+ }
+
+ void TileAssembler::exportGameobjectModels()
+ {
+ FILE* model_list = fopen((iSrcDir + "/" + "temp_gameobject_models").c_str(), "rb");
+ if (!model_list)
+ return;
+
+ FILE* model_list_copy = fopen((iDestDir + "/" + GAMEOBJECT_MODELS).c_str(), "wb");
+ if (!model_list_copy)
+ {
+ fclose(model_list);
+ return;
+ }
+
+ uint32 name_length, displayId;
+ char buff[500];
+ while (!feof(model_list))
+ {
+ if (fread(&displayId, sizeof(uint32), 1, model_list) != 1
+ || fread(&name_length, sizeof(uint32), 1, model_list) != 1
+ || name_length >= sizeof(buff)
+ || fread(&buff, sizeof(char), name_length, model_list) != name_length)
+ {
+ std::cout << "\nFile 'temp_gameobject_models' seems to be corrupted" << std::endl;
+ break;
+ }
+
+ std::string model_name(buff, name_length);
+
+ WorldModel_Raw raw_model;
+ if ( !raw_model.Read((iSrcDir + "/" + model_name).c_str()) )
+ continue;
+
+ spawnedModelFiles.insert(model_name);
+ AABox bounds;
+ bool boundEmpty = true;
+ for (uint32 g = 0; g < raw_model.groupsArray.size(); ++g)
+ {
+ std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
+
+ uint32 nvectors = vertices.size();
+ for (uint32 i = 0; i < nvectors; ++i)
+ {
+ Vector3& v = vertices[i];
+ if (boundEmpty)
+ bounds = AABox(v, v), boundEmpty = false;
+ else
+ bounds.merge(v);
+ }
+ }
+
+ if (bounds.isEmpty())
+ {
+ std::cout << "\nModel " << std::string(buff, name_length) << " has empty bounding box" << std::endl;
+ continue;
+ }
+
+ if (!bounds.isFinite())
+ {
+ std::cout << "\nModel " << std::string(buff, name_length) << " has invalid bounding box" << std::endl;
+ continue;
+ }
+
+ fwrite(&displayId, sizeof(uint32), 1, model_list_copy);
+ fwrite(&name_length, sizeof(uint32), 1, model_list_copy);
+ fwrite(&buff, sizeof(char), name_length, model_list_copy);
+ fwrite(&bounds.low(), sizeof(Vector3), 1, model_list_copy);
+ fwrite(&bounds.high(), sizeof(Vector3), 1, model_list_copy);
+ }
+
+ fclose(model_list);
+ fclose(model_list_copy);
+ }
+ // temporary use defines to simplify read/check code (close file and return at fail)
+ #define READ_OR_RETURN(V, S) if (fread((V), (S), 1, rf) != 1) { \
+ fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }
+ #define READ_OR_RETURN_WITH_DELETE(V, S) if (fread((V), (S), 1, rf) != 1) { \
+ fclose(rf); printf("readfail, op = %i\n", readOperation); delete[] V; return(false); };
+ #define CMP_OR_RETURN(V, S) if (strcmp((V), (S)) != 0) { \
+ fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
+
+ bool GroupModel_Raw::Read(FILE* rf)
+ {
+ char blockId[5];
+ blockId[4] = 0;
+ int blocksize;
+ int readOperation = 0;
+
+ READ_OR_RETURN(&mogpflags, sizeof(uint32));
+ READ_OR_RETURN(&GroupWMOID, sizeof(uint32));
+
+
+ Vector3 vec1, vec2;
+ READ_OR_RETURN(&vec1, sizeof(Vector3));
+
+ READ_OR_RETURN(&vec2, sizeof(Vector3));
+ bounds.set(vec1, vec2);
+
+ READ_OR_RETURN(&liquidflags, sizeof(uint32));
+
+ // will this ever be used? what is it good for anyway??
+ uint32 branches;
+ READ_OR_RETURN(&blockId, 4);
+ CMP_OR_RETURN(blockId, "GRP ");
+ READ_OR_RETURN(&blocksize, sizeof(int));
+ READ_OR_RETURN(&branches, sizeof(uint32));
+ for (uint32 b=0; b<branches; ++b)
+ {
+ uint32 indexes;
+ // indexes for each branch (not used jet)
+ READ_OR_RETURN(&indexes, sizeof(uint32));
+ }
+
+ // ---- indexes
+ READ_OR_RETURN(&blockId, 4);
+ CMP_OR_RETURN(blockId, "INDX");
+ READ_OR_RETURN(&blocksize, sizeof(int));
+ uint32 nindexes;
+ READ_OR_RETURN(&nindexes, sizeof(uint32));
+ if (nindexes >0)
+ {
+ uint16 *indexarray = new uint16[nindexes];
+ READ_OR_RETURN_WITH_DELETE(indexarray, nindexes*sizeof(uint16));
+ triangles.reserve(nindexes / 3);
+ for (uint32 i=0; i<nindexes; i+=3)
+ triangles.push_back(MeshTriangle(indexarray[i], indexarray[i+1], indexarray[i+2]));
+
+ delete[] indexarray;
+ }
+
+ // ---- vectors
+ READ_OR_RETURN(&blockId, 4);
+ CMP_OR_RETURN(blockId, "VERT");
+ READ_OR_RETURN(&blocksize, sizeof(int));
+ uint32 nvectors;
+ READ_OR_RETURN(&nvectors, sizeof(uint32));
+
+ if (nvectors >0)
+ {
+ float *vectorarray = new float[nvectors*3];
+ READ_OR_RETURN_WITH_DELETE(vectorarray, nvectors*sizeof(float)*3);
+ for (uint32 i=0; i<nvectors; ++i)
+ vertexArray.push_back( Vector3(vectorarray + 3*i) );
+
+ delete[] vectorarray;
+ }
+ // ----- liquid
+ liquid = 0;
+ if (liquidflags& 1)
+ {
+ WMOLiquidHeader hlq;
+ READ_OR_RETURN(&blockId, 4);
+ CMP_OR_RETURN(blockId, "LIQU");
+ READ_OR_RETURN(&blocksize, sizeof(int));
+ READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
+ liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), hlq.type);
+ uint32 size = hlq.xverts*hlq.yverts;
+ READ_OR_RETURN(liquid->GetHeightStorage(), size*sizeof(float));
+ size = hlq.xtiles*hlq.ytiles;
+ READ_OR_RETURN(liquid->GetFlagsStorage(), size);
+ }
+
+ return true;
+ }
+
+
+ GroupModel_Raw::~GroupModel_Raw()
+ {
+ delete liquid;
+ }
+
+ bool WorldModel_Raw::Read(const char * path)
+ {
+ FILE* rf = fopen(path, "rb");
+ if (!rf)
+ {
+ printf("ERROR: Can't open raw model file: %s\n", path);
+ return false;
+ }
+
+ char ident[9];
+ ident[8] = '\0';
+ int readOperation = 0;
+
+ READ_OR_RETURN(&ident, 8);
+ CMP_OR_RETURN(ident, RAW_VMAP_MAGIC);
+
+ // we have to read one int. This is needed during the export and we have to skip it here
+ uint32 tempNVectors;
+ READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors));
+
+ uint32 groups;
+ READ_OR_RETURN(&groups, sizeof(uint32));
+ READ_OR_RETURN(&RootWMOID, sizeof(uint32));
+
+ groupsArray.resize(groups);
+ bool succeed = true;
+ for (uint32 g = 0; g < groups && succeed; ++g)
+ succeed = groupsArray[g].Read(rf);
+
+ if (succeed) /// rf will be freed inside Read if the function had any errors.
+ fclose(rf);
+ return succeed;
+ }
+
+ // drop of temporary use defines
+ #undef READ_OR_RETURN
+ #undef CMP_OR_RETURN
+}
diff --git a/src/common/Collision/Maps/TileAssembler.h b/src/common/Collision/Maps/TileAssembler.h
new file mode 100644
index 00000000000..581622c6b73
--- /dev/null
+++ b/src/common/Collision/Maps/TileAssembler.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _TILEASSEMBLER_H_
+#define _TILEASSEMBLER_H_
+
+#include <G3D/Vector3.h>
+#include <G3D/Matrix3.h>
+#include <map>
+#include <set>
+
+#include "ModelInstance.h"
+#include "WorldModel.h"
+
+namespace VMAP
+{
+ /**
+ This Class is used to convert raw vector data into balanced BSP-Trees.
+ To start the conversion call convertWorld().
+ */
+ //===============================================
+
+ class ModelPosition
+ {
+ private:
+ G3D::Matrix3 iRotation;
+ public:
+ ModelPosition(): iScale(0.0f) { }
+ G3D::Vector3 iPos;
+ G3D::Vector3 iDir;
+ float iScale;
+ void init()
+ {
+ iRotation = G3D::Matrix3::fromEulerAnglesZYX(G3D::pif()*iDir.y/180.f, G3D::pif()*iDir.x/180.f, G3D::pif()*iDir.z/180.f);
+ }
+ G3D::Vector3 transform(const G3D::Vector3& pIn) const;
+ void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; }
+ };
+
+ typedef std::map<uint32, ModelSpawn> UniqueEntryMap;
+ typedef std::multimap<uint32, uint32> TileMap;
+
+ struct MapSpawns
+ {
+ UniqueEntryMap UniqueEntries;
+ TileMap TileEntries;
+ };
+
+ typedef std::map<uint32, MapSpawns*> MapData;
+ //===============================================
+
+ struct GroupModel_Raw
+ {
+ uint32 mogpflags;
+ uint32 GroupWMOID;
+
+ G3D::AABox bounds;
+ uint32 liquidflags;
+ std::vector<MeshTriangle> triangles;
+ std::vector<G3D::Vector3> vertexArray;
+ class WmoLiquid* liquid;
+
+ GroupModel_Raw() : mogpflags(0), GroupWMOID(0), liquidflags(0),
+ liquid(NULL) { }
+ ~GroupModel_Raw();
+
+ bool Read(FILE* f);
+ };
+
+ struct WorldModel_Raw
+ {
+ uint32 RootWMOID;
+ std::vector<GroupModel_Raw> groupsArray;
+
+ bool Read(const char * path);
+ };
+
+ class TileAssembler
+ {
+ private:
+ std::string iDestDir;
+ std::string iSrcDir;
+ bool (*iFilterMethod)(char *pName);
+ G3D::Table<std::string, unsigned int > iUniqueNameIds;
+ unsigned int iCurrentUniqueNameId;
+ MapData mapData;
+ std::set<std::string> spawnedModelFiles;
+
+ public:
+ TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName);
+ virtual ~TileAssembler();
+
+ bool convertWorld2();
+ bool readMapSpawns();
+ bool calculateTransformedBound(ModelSpawn &spawn);
+ void exportGameobjectModels();
+
+ bool convertRawFile(const std::string& pModelFilename);
+ void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; }
+ std::string getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName);
+ };
+
+} // VMAP
+#endif /*_TILEASSEMBLER_H_*/
diff --git a/src/common/Collision/Models/GameObjectModel.cpp b/src/common/Collision/Models/GameObjectModel.cpp
new file mode 100644
index 00000000000..dbdc0554e06
--- /dev/null
+++ b/src/common/Collision/Models/GameObjectModel.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "VMapFactory.h"
+#include "VMapManager2.h"
+#include "VMapDefinitions.h"
+#include "WorldModel.h"
+#include "GameObjectModel.h"
+#include "Log.h"
+#include "Timer.h"
+
+using G3D::Vector3;
+using G3D::Ray;
+using G3D::AABox;
+
+struct GameobjectModelData
+{
+ GameobjectModelData(const std::string& name_, const AABox& box) :
+ bound(box), name(name_) { }
+
+ AABox bound;
+ std::string name;
+};
+
+typedef std::unordered_map<uint32, GameobjectModelData> ModelList;
+ModelList model_list;
+
+void LoadGameObjectModelList(std::string const& dataPath)
+{
+#ifndef NO_CORE_FUNCS
+ uint32 oldMSTime = getMSTime();
+#endif
+
+ FILE* model_list_file = fopen((dataPath + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb");
+ if (!model_list_file)
+ {
+ VMAP_ERROR_LOG("misc", "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS);
+ return;
+ }
+
+ uint32 name_length, displayId;
+ char buff[500];
+ while (true)
+ {
+ Vector3 v1, v2;
+ if (fread(&displayId, sizeof(uint32), 1, model_list_file) != 1)
+ if (feof(model_list_file)) // EOF flag is only set after failed reading attempt
+ break;
+
+ if (fread(&name_length, sizeof(uint32), 1, model_list_file) != 1
+ || name_length >= sizeof(buff)
+ || fread(&buff, sizeof(char), name_length, model_list_file) != name_length
+ || fread(&v1, sizeof(Vector3), 1, model_list_file) != 1
+ || fread(&v2, sizeof(Vector3), 1, model_list_file) != 1)
+ {
+ VMAP_ERROR_LOG("misc", "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
+ break;
+ }
+
+ if (v1.isNaN() || v2.isNaN())
+ {
+ VMAP_ERROR_LOG("misc", "File '%s' Model '%s' has invalid v1%s v2%s values!", VMAP::GAMEOBJECT_MODELS, std::string(buff, name_length).c_str(), v1.toString().c_str(), v2.toString().c_str());
+ continue;
+ }
+
+ model_list.insert
+ (
+ ModelList::value_type(displayId, GameobjectModelData(std::string(buff, name_length), AABox(v1, v2)))
+ );
+ }
+
+ fclose(model_list_file);
+ VMAP_INFO_LOG("server.loading", ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
+}
+
+GameObjectModel::~GameObjectModel()
+{
+ if (iModel)
+ ((VMAP::VMapManager2*)VMAP::VMapFactory::createOrGetVMapManager())->releaseModelInstance(name);
+}
+
+bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
+{
+ ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId());
+ if (it == model_list.end())
+ return false;
+
+ G3D::AABox mdl_box(it->second.bound);
+ // ignore models with no bounds
+ if (mdl_box == G3D::AABox::zero())
+ {
+ VMAP_ERROR_LOG("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
+ return false;
+ }
+
+ iModel = ((VMAP::VMapManager2*)VMAP::VMapFactory::createOrGetVMapManager())->acquireModelInstance(dataPath + "vmaps/", it->second.name);
+
+ if (!iModel)
+ return false;
+
+ name = it->second.name;
+ iPos = modelOwner->GetPosition();
+ phasemask = modelOwner->GetPhaseMask();
+ iScale = modelOwner->GetScale();
+ iInvScale = 1.f / iScale;
+
+ G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(modelOwner->GetOrientation(), 0, 0);
+ iInvRot = iRotation.inverse();
+ // transform bounding box:
+ mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
+ AABox rotated_bounds;
+ for (int i = 0; i < 8; ++i)
+ rotated_bounds.merge(iRotation * mdl_box.corner(i));
+
+ iBound = rotated_bounds + iPos;
+#ifdef SPAWN_CORNERS
+ // test:
+ for (int i = 0; i < 8; ++i)
+ {
+ Vector3 pos(iBound.corner(i));
+ modelOwner->DebugVisualizeCorner(pos);
+ }
+#endif
+
+ owner = std::move(modelOwner);
+ return true;
+}
+
+GameObjectModel* GameObjectModel::Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
+{
+ GameObjectModel* mdl = new GameObjectModel();
+ if (!mdl->initialize(std::move(modelOwner), dataPath))
+ {
+ delete mdl;
+ return NULL;
+ }
+
+ return mdl;
+}
+
+bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const
+{
+ if (!(phasemask & ph_mask) || !owner->IsSpawned())
+ return false;
+
+ float time = ray.intersectionTime(iBound);
+ if (time == G3D::finf())
+ return false;
+
+ // child bounds are defined in object space:
+ Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale;
+ Ray modRay(p, iInvRot * ray.direction());
+ float distance = MaxDist * iInvScale;
+ bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit);
+ if (hit)
+ {
+ distance *= iScale;
+ MaxDist = distance;
+ }
+ return hit;
+}
+
+bool GameObjectModel::UpdatePosition()
+{
+ if (!iModel)
+ return false;
+
+ ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
+ if (it == model_list.end())
+ return false;
+
+ G3D::AABox mdl_box(it->second.bound);
+ // ignore models with no bounds
+ if (mdl_box == G3D::AABox::zero())
+ {
+ VMAP_ERROR_LOG("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
+ return false;
+ }
+
+ iPos = owner->GetPosition();
+
+ G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(owner->GetOrientation(), 0, 0);
+ iInvRot = iRotation.inverse();
+ // transform bounding box:
+ mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
+ AABox rotated_bounds;
+ for (int i = 0; i < 8; ++i)
+ rotated_bounds.merge(iRotation * mdl_box.corner(i));
+
+ iBound = rotated_bounds + iPos;
+#ifdef SPAWN_CORNERS
+ // test:
+ for (int i = 0; i < 8; ++i)
+ {
+ Vector3 pos(iBound.corner(i));
+ owner->DebugVisualizeCorner(pos);
+ }
+#endif
+
+ return true;
+}
diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h
new file mode 100644
index 00000000000..17669189af5
--- /dev/null
+++ b/src/common/Collision/Models/GameObjectModel.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _GAMEOBJECT_MODEL_H
+#define _GAMEOBJECT_MODEL_H
+
+#include <G3D/Matrix3.h>
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+#include <G3D/Ray.h>
+
+#include "Define.h"
+#include <memory>
+
+namespace VMAP
+{
+ class WorldModel;
+}
+
+class GameObject;
+struct GameObjectDisplayInfoEntry;
+
+class GameObjectModelOwnerBase
+{
+public:
+ virtual bool IsSpawned() const { return false; }
+ virtual uint32 GetDisplayId() const { return 0; }
+ virtual uint32 GetPhaseMask() const { return 0; }
+ virtual G3D::Vector3 GetPosition() const { return G3D::Vector3::zero(); }
+ virtual float GetOrientation() const { return 0.0f; }
+ virtual float GetScale() const { return 1.0f; }
+ virtual void DebugVisualizeCorner(G3D::Vector3 const& /*corner*/) const { }
+};
+
+class GameObjectModel /*, public Intersectable*/
+{
+ GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(NULL) { }
+public:
+ std::string name;
+
+ const G3D::AABox& getBounds() const { return iBound; }
+
+ ~GameObjectModel();
+
+ const G3D::Vector3& getPosition() const { return iPos;}
+
+ /** Enables\disables collision. */
+ void disable() { phasemask = 0;}
+ void enable(uint32 ph_mask) { phasemask = ph_mask;}
+
+ bool isEnabled() const {return phasemask != 0;}
+
+ bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const;
+
+ static GameObjectModel* Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
+
+ bool UpdatePosition();
+
+private:
+ bool initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
+
+ uint32 phasemask;
+ G3D::AABox iBound;
+ G3D::Matrix3 iInvRot;
+ G3D::Vector3 iPos;
+ float iInvScale;
+ float iScale;
+ VMAP::WorldModel* iModel;
+ std::unique_ptr<GameObjectModelOwnerBase> owner;
+};
+
+#endif // _GAMEOBJECT_MODEL_H
diff --git a/src/common/Collision/Models/ModelInstance.cpp b/src/common/Collision/Models/ModelInstance.cpp
new file mode 100644
index 00000000000..45440a99666
--- /dev/null
+++ b/src/common/Collision/Models/ModelInstance.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "ModelInstance.h"
+#include "WorldModel.h"
+#include "MapTree.h"
+
+using G3D::Vector3;
+using G3D::Ray;
+
+namespace VMAP
+{
+ ModelInstance::ModelInstance(const ModelSpawn &spawn, WorldModel* model): ModelSpawn(spawn), iModel(model)
+ {
+ iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pif()*iRot.y/180.f, G3D::pif()*iRot.x/180.f, G3D::pif()*iRot.z/180.f).inverse();
+ iInvScale = 1.f/iScale;
+ }
+
+ bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const
+ {
+ if (!iModel)
+ {
+ //std::cout << "<object not loaded>\n";
+ return false;
+ }
+ float time = pRay.intersectionTime(iBound);
+ if (time == G3D::finf())
+ {
+// std::cout << "Ray does not hit '" << name << "'\n";
+
+ return false;
+ }
+// std::cout << "Ray crosses bound of '" << name << "'\n";
+/* std::cout << "ray from:" << pRay.origin().x << ", " << pRay.origin().y << ", " << pRay.origin().z
+ << " dir:" << pRay.direction().x << ", " << pRay.direction().y << ", " << pRay.direction().z
+ << " t/tmax:" << time << '/' << pMaxDist;
+ std::cout << "\nBound lo:" << iBound.low().x << ", " << iBound.low().y << ", " << iBound.low().z << " hi: "
+ << iBound.high().x << ", " << iBound.high().y << ", " << iBound.high().z << std::endl; */
+ // child bounds are defined in object space:
+ Vector3 p = iInvRot * (pRay.origin() - iPos) * iInvScale;
+ Ray modRay(p, iInvRot * pRay.direction());
+ float distance = pMaxDist * iInvScale;
+ bool hit = iModel->IntersectRay(modRay, distance, pStopAtFirstHit);
+ if (hit)
+ {
+ distance *= iScale;
+ pMaxDist = distance;
+ }
+ return hit;
+ }
+
+ void ModelInstance::intersectPoint(const G3D::Vector3& p, AreaInfo &info) const
+ {
+ if (!iModel)
+ {
+#ifdef VMAP_DEBUG
+ std::cout << "<object not loaded>\n";
+#endif
+ return;
+ }
+
+ // M2 files don't contain area info, only WMO files
+ if (flags & MOD_M2)
+ return;
+ if (!iBound.contains(p))
+ return;
+ // child bounds are defined in object space:
+ Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
+ Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
+ float zDist;
+ if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
+ {
+ Vector3 modelGround = pModel + zDist * zDirModel;
+ // Transform back to world space. Note that:
+ // Mat * vec == vec * Mat.transpose()
+ // and for rotation matrices: Mat.inverse() == Mat.transpose()
+ float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
+ if (info.ground_Z < world_Z)
+ {
+ info.ground_Z = world_Z;
+ info.adtId = adtId;
+ }
+ }
+ }
+
+ bool ModelInstance::GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const
+ {
+ if (!iModel)
+ {
+#ifdef VMAP_DEBUG
+ std::cout << "<object not loaded>\n";
+#endif
+ return false;
+ }
+
+ // M2 files don't contain area info, only WMO files
+ if (flags & MOD_M2)
+ return false;
+ if (!iBound.contains(p))
+ return false;
+ // child bounds are defined in object space:
+ Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
+ Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
+ float zDist;
+ if (iModel->GetLocationInfo(pModel, zDirModel, zDist, info))
+ {
+ Vector3 modelGround = pModel + zDist * zDirModel;
+ // Transform back to world space. Note that:
+ // Mat * vec == vec * Mat.transpose()
+ // and for rotation matrices: Mat.inverse() == Mat.transpose()
+ float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
+ if (info.ground_Z < world_Z) // hm...could it be handled automatically with zDist at intersection?
+ {
+ info.ground_Z = world_Z;
+ info.hitInstance = this;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool ModelInstance::GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const
+ {
+ // child bounds are defined in object space:
+ Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
+ //Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
+ float zDist;
+ if (info.hitModel->GetLiquidLevel(pModel, zDist))
+ {
+ // calculate world height (zDist in model coords):
+ // assume WMO not tilted (wouldn't make much sense anyway)
+ liqHeight = zDist * iScale + iPos.z;
+ return true;
+ }
+ return false;
+ }
+
+ bool ModelSpawn::readFromFile(FILE* rf, ModelSpawn &spawn)
+ {
+ uint32 check = 0, nameLen;
+ check += fread(&spawn.flags, sizeof(uint32), 1, rf);
+ // EoF?
+ if (!check)
+ {
+ if (ferror(rf))
+ std::cout << "Error reading ModelSpawn!\n";
+ return false;
+ }
+ check += fread(&spawn.adtId, sizeof(uint16), 1, rf);
+ check += fread(&spawn.ID, sizeof(uint32), 1, rf);
+ check += fread(&spawn.iPos, sizeof(float), 3, rf);
+ check += fread(&spawn.iRot, sizeof(float), 3, rf);
+ check += fread(&spawn.iScale, sizeof(float), 1, rf);
+ bool has_bound = (spawn.flags & MOD_HAS_BOUND) != 0;
+ if (has_bound) // only WMOs have bound in MPQ, only available after computation
+ {
+ Vector3 bLow, bHigh;
+ check += fread(&bLow, sizeof(float), 3, rf);
+ check += fread(&bHigh, sizeof(float), 3, rf);
+ spawn.iBound = G3D::AABox(bLow, bHigh);
+ }
+ check += fread(&nameLen, sizeof(uint32), 1, rf);
+ if (check != uint32(has_bound ? 17 : 11))
+ {
+ std::cout << "Error reading ModelSpawn!\n";
+ return false;
+ }
+ char nameBuff[500];
+ if (nameLen > 500) // file names should never be that long, must be file error
+ {
+ std::cout << "Error reading ModelSpawn, file name too long!\n";
+ return false;
+ }
+ check = fread(nameBuff, sizeof(char), nameLen, rf);
+ if (check != nameLen)
+ {
+ std::cout << "Error reading ModelSpawn!\n";
+ return false;
+ }
+ spawn.name = std::string(nameBuff, nameLen);
+ return true;
+ }
+
+ bool ModelSpawn::writeToFile(FILE* wf, const ModelSpawn &spawn)
+ {
+ uint32 check=0;
+ check += fwrite(&spawn.flags, sizeof(uint32), 1, wf);
+ check += fwrite(&spawn.adtId, sizeof(uint16), 1, wf);
+ check += fwrite(&spawn.ID, sizeof(uint32), 1, wf);
+ check += fwrite(&spawn.iPos, sizeof(float), 3, wf);
+ check += fwrite(&spawn.iRot, sizeof(float), 3, wf);
+ check += fwrite(&spawn.iScale, sizeof(float), 1, wf);
+ bool has_bound = (spawn.flags & MOD_HAS_BOUND) != 0;
+ if (has_bound) // only WMOs have bound in MPQ, only available after computation
+ {
+ check += fwrite(&spawn.iBound.low(), sizeof(float), 3, wf);
+ check += fwrite(&spawn.iBound.high(), sizeof(float), 3, wf);
+ }
+ uint32 nameLen = spawn.name.length();
+ check += fwrite(&nameLen, sizeof(uint32), 1, wf);
+ if (check != uint32(has_bound ? 17 : 11)) return false;
+ check = fwrite(spawn.name.c_str(), sizeof(char), nameLen, wf);
+ if (check != nameLen) return false;
+ return true;
+ }
+
+}
diff --git a/src/common/Collision/Models/ModelInstance.h b/src/common/Collision/Models/ModelInstance.h
new file mode 100644
index 00000000000..f8bbfa4fa73
--- /dev/null
+++ b/src/common/Collision/Models/ModelInstance.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _MODELINSTANCE_H_
+#define _MODELINSTANCE_H_
+
+#include <G3D/Matrix3.h>
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+#include <G3D/Ray.h>
+
+#include "Define.h"
+
+namespace VMAP
+{
+ class WorldModel;
+ struct AreaInfo;
+ struct LocationInfo;
+
+ enum ModelFlags
+ {
+ MOD_M2 = 1,
+ MOD_WORLDSPAWN = 1<<1,
+ MOD_HAS_BOUND = 1<<2
+ };
+
+ class ModelSpawn
+ {
+ public:
+ //mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
+ uint32 flags;
+ uint16 adtId;
+ uint32 ID;
+ G3D::Vector3 iPos;
+ G3D::Vector3 iRot;
+ float iScale;
+ G3D::AABox iBound;
+ std::string name;
+ bool operator==(const ModelSpawn &other) const { return ID == other.ID; }
+ //uint32 hashCode() const { return ID; }
+ // temp?
+ const G3D::AABox& getBounds() const { return iBound; }
+
+ static bool readFromFile(FILE* rf, ModelSpawn &spawn);
+ static bool writeToFile(FILE* rw, const ModelSpawn &spawn);
+ };
+
+ class ModelInstance: public ModelSpawn
+ {
+ public:
+ ModelInstance(): iInvScale(0.0f), iModel(nullptr) { }
+ ModelInstance(const ModelSpawn &spawn, WorldModel* model);
+ void setUnloaded() { iModel = nullptr; }
+ bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const;
+ void intersectPoint(const G3D::Vector3& p, AreaInfo &info) const;
+ bool GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const;
+ bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const;
+ WorldModel* getWorldModel() { return iModel; }
+ protected:
+ G3D::Matrix3 iInvRot;
+ float iInvScale;
+ WorldModel* iModel;
+ };
+} // namespace VMAP
+
+#endif // _MODELINSTANCE
diff --git a/src/common/Collision/Models/WorldModel.cpp b/src/common/Collision/Models/WorldModel.cpp
new file mode 100644
index 00000000000..7f4d76b244a
--- /dev/null
+++ b/src/common/Collision/Models/WorldModel.cpp
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "WorldModel.h"
+#include "VMapDefinitions.h"
+#include "MapTree.h"
+
+using G3D::Vector3;
+using G3D::Ray;
+
+template<> struct BoundsTrait<VMAP::GroupModel>
+{
+ static void getBounds(const VMAP::GroupModel& obj, G3D::AABox& out) { out = obj.GetBound(); }
+};
+
+namespace VMAP
+{
+ bool IntersectTriangle(const MeshTriangle &tri, std::vector<Vector3>::const_iterator points, const G3D::Ray &ray, float &distance)
+ {
+ static const float EPS = 1e-5f;
+
+ // See RTR2 ch. 13.7 for the algorithm.
+
+ const Vector3 e1 = points[tri.idx1] - points[tri.idx0];
+ const Vector3 e2 = points[tri.idx2] - points[tri.idx0];
+ const Vector3 p(ray.direction().cross(e2));
+ const float a = e1.dot(p);
+
+ if (std::fabs(a) < EPS) {
+ // Determinant is ill-conditioned; abort early
+ return false;
+ }
+
+ const float f = 1.0f / a;
+ const Vector3 s(ray.origin() - points[tri.idx0]);
+ const float u = f * s.dot(p);
+
+ if ((u < 0.0f) || (u > 1.0f)) {
+ // We hit the plane of the m_geometry, but outside the m_geometry
+ return false;
+ }
+
+ const Vector3 q(s.cross(e1));
+ const float v = f * ray.direction().dot(q);
+
+ if ((v < 0.0f) || ((u + v) > 1.0f)) {
+ // We hit the plane of the triangle, but outside the triangle
+ return false;
+ }
+
+ const float t = f * e2.dot(q);
+
+ if ((t > 0.0f) && (t < distance))
+ {
+ // This is a new hit, closer than the previous one
+ distance = t;
+
+ /* baryCoord[0] = 1.0 - u - v;
+ baryCoord[1] = u;
+ baryCoord[2] = v; */
+
+ return true;
+ }
+ // This hit is after the previous hit, so ignore it
+ return false;
+ }
+
+ class TriBoundFunc
+ {
+ public:
+ TriBoundFunc(std::vector<Vector3> &vert): vertices(vert.begin()) { }
+ void operator()(const MeshTriangle &tri, G3D::AABox &out) const
+ {
+ G3D::Vector3 lo = vertices[tri.idx0];
+ G3D::Vector3 hi = lo;
+
+ lo = (lo.min(vertices[tri.idx1])).min(vertices[tri.idx2]);
+ hi = (hi.max(vertices[tri.idx1])).max(vertices[tri.idx2]);
+
+ out = G3D::AABox(lo, hi);
+ }
+ protected:
+ const std::vector<Vector3>::const_iterator vertices;
+ };
+
+ // ===================== WmoLiquid ==================================
+
+ WmoLiquid::WmoLiquid(uint32 width, uint32 height, const Vector3 &corner, uint32 type):
+ iTilesX(width), iTilesY(height), iCorner(corner), iType(type)
+ {
+ iHeight = new float[(width+1)*(height+1)];
+ iFlags = new uint8[width*height];
+ }
+
+ WmoLiquid::WmoLiquid(const WmoLiquid &other): iHeight(nullptr), iFlags(nullptr)
+ {
+ *this = other; // use assignment operator...
+ }
+
+ WmoLiquid::~WmoLiquid()
+ {
+ delete[] iHeight;
+ delete[] iFlags;
+ }
+
+ WmoLiquid& WmoLiquid::operator=(const WmoLiquid &other)
+ {
+ if (this == &other)
+ return *this;
+ iTilesX = other.iTilesX;
+ iTilesY = other.iTilesY;
+ iCorner = other.iCorner;
+ iType = other.iType;
+ delete iHeight;
+ delete iFlags;
+ if (other.iHeight)
+ {
+ iHeight = new float[(iTilesX+1)*(iTilesY+1)];
+ memcpy(iHeight, other.iHeight, (iTilesX+1)*(iTilesY+1)*sizeof(float));
+ }
+ else
+ iHeight = nullptr;
+ if (other.iFlags)
+ {
+ iFlags = new uint8[iTilesX * iTilesY];
+ memcpy(iFlags, other.iFlags, iTilesX * iTilesY);
+ }
+ else
+ iFlags = nullptr;
+ return *this;
+ }
+
+ bool WmoLiquid::GetLiquidHeight(const Vector3 &pos, float &liqHeight) const
+ {
+ float tx_f = (pos.x - iCorner.x)/LIQUID_TILE_SIZE;
+ uint32 tx = uint32(tx_f);
+ if (tx_f < 0.0f || tx >= iTilesX)
+ return false;
+ float ty_f = (pos.y - iCorner.y)/LIQUID_TILE_SIZE;
+ uint32 ty = uint32(ty_f);
+ if (ty_f < 0.0f || ty >= iTilesY)
+ return false;
+
+ // check if tile shall be used for liquid level
+ // checking for 0x08 *might* be enough, but disabled tiles always are 0x?F:
+ if ((iFlags[tx + ty*iTilesX] & 0x0F) == 0x0F)
+ return false;
+
+ // (dx, dy) coordinates inside tile, in [0, 1]^2
+ float dx = tx_f - (float)tx;
+ float dy = ty_f - (float)ty;
+
+ /* Tesselate tile to two triangles (not sure if client does it exactly like this)
+
+ ^ dy
+ |
+ 1 x---------x (1, 1)
+ | (b) / |
+ | / |
+ | / |
+ | / (a) |
+ x---------x---> dx
+ 0 1
+ */
+
+ const uint32 rowOffset = iTilesX + 1;
+ if (dx > dy) // case (a)
+ {
+ float sx = iHeight[tx+1 + ty * rowOffset] - iHeight[tx + ty * rowOffset];
+ float sy = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx+1 + ty * rowOffset];
+ liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
+ }
+ else // case (b)
+ {
+ float sx = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx + (ty+1) * rowOffset];
+ float sy = iHeight[tx + (ty+1) * rowOffset] - iHeight[tx + ty * rowOffset];
+ liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
+ }
+ return true;
+ }
+
+ uint32 WmoLiquid::GetFileSize()
+ {
+ return 2 * sizeof(uint32) +
+ sizeof(Vector3) +
+ (iTilesX + 1)*(iTilesY + 1) * sizeof(float) +
+ iTilesX * iTilesY;
+ }
+
+ bool WmoLiquid::writeToFile(FILE* wf)
+ {
+ bool result = false;
+ if (fwrite(&iTilesX, sizeof(uint32), 1, wf) == 1 &&
+ fwrite(&iTilesY, sizeof(uint32), 1, wf) == 1 &&
+ fwrite(&iCorner, sizeof(Vector3), 1, wf) == 1 &&
+ fwrite(&iType, sizeof(uint32), 1, wf) == 1)
+ {
+ uint32 size = (iTilesX + 1) * (iTilesY + 1);
+ if (fwrite(iHeight, sizeof(float), size, wf) == size)
+ {
+ size = iTilesX*iTilesY;
+ result = fwrite(iFlags, sizeof(uint8), size, wf) == size;
+ }
+ }
+
+ return result;
+ }
+
+ bool WmoLiquid::readFromFile(FILE* rf, WmoLiquid* &out)
+ {
+ bool result = false;
+ WmoLiquid* liquid = new WmoLiquid();
+
+ if (fread(&liquid->iTilesX, sizeof(uint32), 1, rf) == 1 &&
+ fread(&liquid->iTilesY, sizeof(uint32), 1, rf) == 1 &&
+ fread(&liquid->iCorner, sizeof(Vector3), 1, rf) == 1 &&
+ fread(&liquid->iType, sizeof(uint32), 1, rf) == 1)
+ {
+ uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1);
+ liquid->iHeight = new float[size];
+ if (fread(liquid->iHeight, sizeof(float), size, rf) == size)
+ {
+ size = liquid->iTilesX * liquid->iTilesY;
+ liquid->iFlags = new uint8[size];
+ result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size;
+ }
+ }
+
+ if (!result)
+ delete liquid;
+ else
+ out = liquid;
+
+ return result;
+ }
+
+ void WmoLiquid::getPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const
+ {
+ tilesX = iTilesX;
+ tilesY = iTilesY;
+ corner = iCorner;
+ }
+
+ // ===================== GroupModel ==================================
+
+ GroupModel::GroupModel(const GroupModel &other):
+ iBound(other.iBound), iMogpFlags(other.iMogpFlags), iGroupWMOID(other.iGroupWMOID),
+ vertices(other.vertices), triangles(other.triangles), meshTree(other.meshTree), iLiquid(nullptr)
+ {
+ if (other.iLiquid)
+ iLiquid = new WmoLiquid(*other.iLiquid);
+ }
+
+ void GroupModel::setMeshData(std::vector<Vector3> &vert, std::vector<MeshTriangle> &tri)
+ {
+ vertices.swap(vert);
+ triangles.swap(tri);
+ TriBoundFunc bFunc(vertices);
+ meshTree.build(triangles, bFunc);
+ }
+
+ bool GroupModel::writeToFile(FILE* wf)
+ {
+ bool result = true;
+ uint32 chunkSize, count;
+
+ if (result && fwrite(&iBound, sizeof(G3D::AABox), 1, wf) != 1) result = false;
+ if (result && fwrite(&iMogpFlags, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&iGroupWMOID, sizeof(uint32), 1, wf) != 1) result = false;
+
+ // write vertices
+ if (result && fwrite("VERT", 1, 4, wf) != 4) result = false;
+ count = vertices.size();
+ chunkSize = sizeof(uint32)+ sizeof(Vector3)*count;
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
+ if (!count) // models without (collision) geometry end here, unsure if they are useful
+ return result;
+ if (result && fwrite(&vertices[0], sizeof(Vector3), count, wf) != count) result = false;
+
+ // write triangle mesh
+ if (result && fwrite("TRIM", 1, 4, wf) != 4) result = false;
+ count = triangles.size();
+ chunkSize = sizeof(uint32)+ sizeof(MeshTriangle)*count;
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&triangles[0], sizeof(MeshTriangle), count, wf) != count) result = false;
+
+ // write mesh BIH
+ if (result && fwrite("MBIH", 1, 4, wf) != 4) result = false;
+ if (result) result = meshTree.writeToFile(wf);
+
+ // write liquid data
+ if (result && fwrite("LIQU", 1, 4, wf) != 4) result = false;
+ if (!iLiquid)
+ {
+ chunkSize = 0;
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ return result;
+ }
+ chunkSize = iLiquid->GetFileSize();
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result) result = iLiquid->writeToFile(wf);
+
+ return result;
+ }
+
+ bool GroupModel::readFromFile(FILE* rf)
+ {
+ char chunk[8];
+ bool result = true;
+ uint32 chunkSize = 0;
+ uint32 count = 0;
+ triangles.clear();
+ vertices.clear();
+ delete iLiquid;
+ iLiquid = NULL;
+
+ if (result && fread(&iBound, sizeof(G3D::AABox), 1, rf) != 1) result = false;
+ if (result && fread(&iMogpFlags, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && fread(&iGroupWMOID, sizeof(uint32), 1, rf) != 1) result = false;
+
+ // read vertices
+ if (result && !readChunk(rf, chunk, "VERT", 4)) result = false;
+ if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
+ if (!count) // models without (collision) geometry end here, unsure if they are useful
+ return result;
+ if (result) vertices.resize(count);
+ if (result && fread(&vertices[0], sizeof(Vector3), count, rf) != count) result = false;
+
+ // read triangle mesh
+ if (result && !readChunk(rf, chunk, "TRIM", 4)) result = false;
+ if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result) triangles.resize(count);
+ if (result && fread(&triangles[0], sizeof(MeshTriangle), count, rf) != count) result = false;
+
+ // read mesh BIH
+ if (result && !readChunk(rf, chunk, "MBIH", 4)) result = false;
+ if (result) result = meshTree.readFromFile(rf);
+
+ // write liquid data
+ if (result && !readChunk(rf, chunk, "LIQU", 4)) result = false;
+ if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && chunkSize > 0)
+ result = WmoLiquid::readFromFile(rf, iLiquid);
+ return result;
+ }
+
+ struct GModelRayCallback
+ {
+ GModelRayCallback(const std::vector<MeshTriangle> &tris, const std::vector<Vector3> &vert):
+ vertices(vert.begin()), triangles(tris.begin()), hit(false) { }
+ bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool /*pStopAtFirstHit*/)
+ {
+ bool result = IntersectTriangle(triangles[entry], vertices, ray, distance);
+ if (result) hit=true;
+ return hit;
+ }
+ std::vector<Vector3>::const_iterator vertices;
+ std::vector<MeshTriangle>::const_iterator triangles;
+ bool hit;
+ };
+
+ bool GroupModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
+ {
+ if (triangles.empty())
+ return false;
+
+ GModelRayCallback callback(triangles, vertices);
+ meshTree.intersectRay(ray, callback, distance, stopAtFirstHit);
+ return callback.hit;
+ }
+
+ bool GroupModel::IsInsideObject(const Vector3 &pos, const Vector3 &down, float &z_dist) const
+ {
+ if (triangles.empty() || !iBound.contains(pos))
+ return false;
+ GModelRayCallback callback(triangles, vertices);
+ Vector3 rPos = pos - 0.1f * down;
+ float dist = G3D::finf();
+ G3D::Ray ray(rPos, down);
+ bool hit = IntersectRay(ray, dist, false);
+ if (hit)
+ z_dist = dist - 0.1f;
+ return hit;
+ }
+
+ bool GroupModel::GetLiquidLevel(const Vector3 &pos, float &liqHeight) const
+ {
+ if (iLiquid)
+ return iLiquid->GetLiquidHeight(pos, liqHeight);
+ return false;
+ }
+
+ uint32 GroupModel::GetLiquidType() const
+ {
+ if (iLiquid)
+ return iLiquid->GetType();
+ return 0;
+ }
+
+ void GroupModel::getMeshData(std::vector<G3D::Vector3>& outVertices, std::vector<MeshTriangle>& outTriangles, WmoLiquid*& liquid)
+ {
+ outVertices = vertices;
+ outTriangles = triangles;
+ liquid = iLiquid;
+ }
+
+ // ===================== WorldModel ==================================
+
+ void WorldModel::setGroupModels(std::vector<GroupModel> &models)
+ {
+ groupModels.swap(models);
+ groupTree.build(groupModels, BoundsTrait<GroupModel>::getBounds, 1);
+ }
+
+ struct WModelRayCallBack
+ {
+ WModelRayCallBack(const std::vector<GroupModel> &mod): models(mod.begin()), hit(false) { }
+ bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit)
+ {
+ bool result = models[entry].IntersectRay(ray, distance, pStopAtFirstHit);
+ if (result) hit=true;
+ return hit;
+ }
+ std::vector<GroupModel>::const_iterator models;
+ bool hit;
+ };
+
+ bool WorldModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
+ {
+ // small M2 workaround, maybe better make separate class with virtual intersection funcs
+ // in any case, there's no need to use a bound tree if we only have one submodel
+ if (groupModels.size() == 1)
+ return groupModels[0].IntersectRay(ray, distance, stopAtFirstHit);
+
+ WModelRayCallBack isc(groupModels);
+ groupTree.intersectRay(ray, isc, distance, stopAtFirstHit);
+ return isc.hit;
+ }
+
+ class WModelAreaCallback {
+ public:
+ WModelAreaCallback(const std::vector<GroupModel> &vals, const Vector3 &down):
+ prims(vals.begin()), hit(vals.end()), minVol(G3D::finf()), zDist(G3D::finf()), zVec(down) { }
+ std::vector<GroupModel>::const_iterator prims;
+ std::vector<GroupModel>::const_iterator hit;
+ float minVol;
+ float zDist;
+ Vector3 zVec;
+ void operator()(const Vector3& point, uint32 entry)
+ {
+ float group_Z;
+ //float pVol = prims[entry].GetBound().volume();
+ //if (pVol < minVol)
+ //{
+ /* if (prims[entry].iBound.contains(point)) */
+ if (prims[entry].IsInsideObject(point, zVec, group_Z))
+ {
+ //minVol = pVol;
+ //hit = prims + entry;
+ if (group_Z < zDist)
+ {
+ zDist = group_Z;
+ hit = prims + entry;
+ }
+#ifdef VMAP_DEBUG
+ const GroupModel &gm = prims[entry];
+ printf("%10u %8X %7.3f, %7.3f, %7.3f | %7.3f, %7.3f, %7.3f | z=%f, p_z=%f\n", gm.GetWmoID(), gm.GetMogpFlags(),
+ gm.GetBound().low().x, gm.GetBound().low().y, gm.GetBound().low().z,
+ gm.GetBound().high().x, gm.GetBound().high().y, gm.GetBound().high().z, group_Z, point.z);
+#endif
+ }
+ //}
+ //std::cout << "trying to intersect '" << prims[entry].name << "'\n";
+ }
+ };
+
+ bool WorldModel::IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const
+ {
+ if (groupModels.empty())
+ return false;
+
+ WModelAreaCallback callback(groupModels, down);
+ groupTree.intersectPoint(p, callback);
+ if (callback.hit != groupModels.end())
+ {
+ info.rootId = RootWMOID;
+ info.groupId = callback.hit->GetWmoID();
+ info.flags = callback.hit->GetMogpFlags();
+ info.result = true;
+ dist = callback.zDist;
+ return true;
+ }
+ return false;
+ }
+
+ bool WorldModel::GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const
+ {
+ if (groupModels.empty())
+ return false;
+
+ WModelAreaCallback callback(groupModels, down);
+ groupTree.intersectPoint(p, callback);
+ if (callback.hit != groupModels.end())
+ {
+ info.hitModel = &(*callback.hit);
+ dist = callback.zDist;
+ return true;
+ }
+ return false;
+ }
+
+ bool WorldModel::writeFile(const std::string &filename)
+ {
+ FILE* wf = fopen(filename.c_str(), "wb");
+ if (!wf)
+ return false;
+
+ uint32 chunkSize, count;
+ bool result = fwrite(VMAP_MAGIC, 1, 8, wf) == 8;
+ if (result && fwrite("WMOD", 1, 4, wf) != 4) result = false;
+ chunkSize = sizeof(uint32) + sizeof(uint32);
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&RootWMOID, sizeof(uint32), 1, wf) != 1) result = false;
+
+ // write group models
+ count=groupModels.size();
+ if (count)
+ {
+ if (result && fwrite("GMOD", 1, 4, wf) != 4) result = false;
+ //chunkSize = sizeof(uint32)+ sizeof(GroupModel)*count;
+ //if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
+ for (uint32 i=0; i<groupModels.size() && result; ++i)
+ result = groupModels[i].writeToFile(wf);
+
+ // write group BIH
+ if (result && fwrite("GBIH", 1, 4, wf) != 4) result = false;
+ if (result) result = groupTree.writeToFile(wf);
+ }
+
+ fclose(wf);
+ return result;
+ }
+
+ bool WorldModel::readFile(const std::string &filename)
+ {
+ FILE* rf = fopen(filename.c_str(), "rb");
+ if (!rf)
+ return false;
+
+ bool result = true;
+ uint32 chunkSize = 0;
+ uint32 count = 0;
+ char chunk[8]; // Ignore the added magic header
+ if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) result = false;
+
+ if (result && !readChunk(rf, chunk, "WMOD", 4)) result = false;
+ if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && fread(&RootWMOID, sizeof(uint32), 1, rf) != 1) result = false;
+
+ // read group models
+ if (result && readChunk(rf, chunk, "GMOD", 4))
+ {
+ //if (fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+
+ if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result) groupModels.resize(count);
+ //if (result && fread(&groupModels[0], sizeof(GroupModel), count, rf) != count) result = false;
+ for (uint32 i=0; i<count && result; ++i)
+ result = groupModels[i].readFromFile(rf);
+
+ // read group BIH
+ if (result && !readChunk(rf, chunk, "GBIH", 4)) result = false;
+ if (result) result = groupTree.readFromFile(rf);
+ }
+
+ fclose(rf);
+ return result;
+ }
+
+ void WorldModel::getGroupModels(std::vector<GroupModel>& outGroupModels)
+ {
+ outGroupModels = groupModels;
+ }
+}
diff --git a/src/common/Collision/Models/WorldModel.h b/src/common/Collision/Models/WorldModel.h
new file mode 100644
index 00000000000..afa9d15b264
--- /dev/null
+++ b/src/common/Collision/Models/WorldModel.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _WORLDMODEL_H
+#define _WORLDMODEL_H
+
+#include <G3D/HashTrait.h>
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+#include <G3D/Ray.h>
+#include "BoundingIntervalHierarchy.h"
+
+#include "Define.h"
+
+namespace VMAP
+{
+ class TreeNode;
+ struct AreaInfo;
+ struct LocationInfo;
+
+ class MeshTriangle
+ {
+ public:
+ MeshTriangle() : idx0(0), idx1(0), idx2(0) { }
+ MeshTriangle(uint32 na, uint32 nb, uint32 nc): idx0(na), idx1(nb), idx2(nc) { }
+
+ uint32 idx0;
+ uint32 idx1;
+ uint32 idx2;
+ };
+
+ class WmoLiquid
+ {
+ public:
+ WmoLiquid(uint32 width, uint32 height, const G3D::Vector3 &corner, uint32 type);
+ WmoLiquid(const WmoLiquid &other);
+ ~WmoLiquid();
+ WmoLiquid& operator=(const WmoLiquid &other);
+ bool GetLiquidHeight(const G3D::Vector3 &pos, float &liqHeight) const;
+ uint32 GetType() const { return iType; }
+ float *GetHeightStorage() { return iHeight; }
+ uint8 *GetFlagsStorage() { return iFlags; }
+ uint32 GetFileSize();
+ bool writeToFile(FILE* wf);
+ static bool readFromFile(FILE* rf, WmoLiquid* &liquid);
+ void getPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const;
+ private:
+ WmoLiquid() : iTilesX(0), iTilesY(0), iCorner(), iType(0), iHeight(NULL), iFlags(NULL) { }
+ uint32 iTilesX; //!< number of tiles in x direction, each
+ uint32 iTilesY;
+ G3D::Vector3 iCorner; //!< the lower corner
+ uint32 iType; //!< liquid type
+ float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
+ uint8 *iFlags; //!< info if liquid tile is used
+ };
+
+ /*! holding additional info for WMO group files */
+ class GroupModel
+ {
+ public:
+ GroupModel() : iBound(), iMogpFlags(0), iGroupWMOID(0), iLiquid(NULL) { }
+ GroupModel(const GroupModel &other);
+ GroupModel(uint32 mogpFlags, uint32 groupWMOID, const G3D::AABox &bound):
+ iBound(bound), iMogpFlags(mogpFlags), iGroupWMOID(groupWMOID), iLiquid(NULL) { }
+ ~GroupModel() { delete iLiquid; }
+
+ //! pass mesh data to object and create BIH. Passed vectors get get swapped with old geometry!
+ void setMeshData(std::vector<G3D::Vector3> &vert, std::vector<MeshTriangle> &tri);
+ void setLiquidData(WmoLiquid*& liquid) { iLiquid = liquid; liquid = NULL; }
+ bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
+ bool IsInsideObject(const G3D::Vector3 &pos, const G3D::Vector3 &down, float &z_dist) const;
+ bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const;
+ uint32 GetLiquidType() const;
+ bool writeToFile(FILE* wf);
+ bool readFromFile(FILE* rf);
+ const G3D::AABox& GetBound() const { return iBound; }
+ uint32 GetMogpFlags() const { return iMogpFlags; }
+ uint32 GetWmoID() const { return iGroupWMOID; }
+ void getMeshData(std::vector<G3D::Vector3>& outVertices, std::vector<MeshTriangle>& outTriangles, WmoLiquid*& liquid);
+ protected:
+ G3D::AABox iBound;
+ uint32 iMogpFlags;// 0x8 outdor; 0x2000 indoor
+ uint32 iGroupWMOID;
+ std::vector<G3D::Vector3> vertices;
+ std::vector<MeshTriangle> triangles;
+ BIH meshTree;
+ WmoLiquid* iLiquid;
+ };
+
+ /*! Holds a model (converted M2 or WMO) in its original coordinate space */
+ class WorldModel
+ {
+ public:
+ WorldModel(): RootWMOID(0) { }
+
+ //! pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry!
+ void setGroupModels(std::vector<GroupModel> &models);
+ void setRootWmoID(uint32 id) { RootWMOID = id; }
+ bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
+ bool IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const;
+ bool GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const;
+ bool writeFile(const std::string &filename);
+ bool readFile(const std::string &filename);
+ void getGroupModels(std::vector<GroupModel>& outGroupModels);
+ protected:
+ uint32 RootWMOID;
+ std::vector<GroupModel> groupModels;
+ BIH groupTree;
+ };
+} // namespace VMAP
+
+#endif // _WORLDMODEL_H
diff --git a/src/common/Collision/RegularGrid.h b/src/common/Collision/RegularGrid.h
new file mode 100644
index 00000000000..6a2a07968ad
--- /dev/null
+++ b/src/common/Collision/RegularGrid.h
@@ -0,0 +1,212 @@
+#ifndef _REGULAR_GRID_H
+#define _REGULAR_GRID_H
+
+
+#include <G3D/Ray.h>
+#include <G3D/Table.h>
+#include <G3D/BoundsTrait.h>
+#include <G3D/PositionTrait.h>
+
+#include "Errors.h"
+
+template<class Node>
+struct NodeCreator{
+ static Node * makeNode(int /*x*/, int /*y*/) { return new Node();}
+};
+
+template<class T,
+class Node,
+class NodeCreatorFunc = NodeCreator<Node>,
+ /*class BoundsFunc = BoundsTrait<T>,*/
+class PositionFunc = PositionTrait<T>
+>
+class RegularGrid2D
+{
+public:
+
+ enum{
+ CELL_NUMBER = 64,
+ };
+
+ #define HGRID_MAP_SIZE (533.33333f * 64.f) // shouldn't be changed
+ #define CELL_SIZE float(HGRID_MAP_SIZE/(float)CELL_NUMBER)
+
+ typedef G3D::Table<const T*, Node*> MemberTable;
+
+ MemberTable memberTable;
+ Node* nodes[CELL_NUMBER][CELL_NUMBER];
+
+ RegularGrid2D(){
+ memset(nodes, 0, sizeof(nodes));
+ }
+
+ ~RegularGrid2D(){
+ for (int x = 0; x < CELL_NUMBER; ++x)
+ for (int y = 0; y < CELL_NUMBER; ++y)
+ delete nodes[x][y];
+ }
+
+ void insert(const T& value)
+ {
+ G3D::Vector3 pos;
+ PositionFunc::getPosition(value, pos);
+ Node& node = getGridFor(pos.x, pos.y);
+ node.insert(value);
+ memberTable.set(&value, &node);
+ }
+
+ void remove(const T& value)
+ {
+ memberTable[&value]->remove(value);
+ // Remove the member
+ memberTable.remove(&value);
+ }
+
+ void balance()
+ {
+ for (int x = 0; x < CELL_NUMBER; ++x)
+ for (int y = 0; y < CELL_NUMBER; ++y)
+ if (Node* n = nodes[x][y])
+ n->balance();
+ }
+
+ bool contains(const T& value) const { return memberTable.containsKey(&value); }
+ int size() const { return memberTable.size(); }
+
+ struct Cell
+ {
+ int x, y;
+ bool operator == (const Cell& c2) const { return x == c2.x && y == c2.y;}
+
+ static Cell ComputeCell(float fx, float fy)
+ {
+ Cell c = { int(fx * (1.f/CELL_SIZE) + (CELL_NUMBER/2)), int(fy * (1.f/CELL_SIZE) + (CELL_NUMBER/2)) };
+ return c;
+ }
+
+ bool isValid() const { return x >= 0 && x < CELL_NUMBER && y >= 0 && y < CELL_NUMBER;}
+ };
+
+
+ Node& getGridFor(float fx, float fy)
+ {
+ Cell c = Cell::ComputeCell(fx, fy);
+ return getGrid(c.x, c.y);
+ }
+
+ Node& getGrid(int x, int y)
+ {
+ ASSERT(x < CELL_NUMBER && y < CELL_NUMBER);
+ if (!nodes[x][y])
+ nodes[x][y] = NodeCreatorFunc::makeNode(x, y);
+ return *nodes[x][y];
+ }
+
+ template<typename RayCallback>
+ void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float max_dist)
+ {
+ intersectRay(ray, intersectCallback, max_dist, ray.origin() + ray.direction() * max_dist);
+ }
+
+ template<typename RayCallback>
+ void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist, const G3D::Vector3& end)
+ {
+ Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
+ if (!cell.isValid())
+ return;
+
+ Cell last_cell = Cell::ComputeCell(end.x, end.y);
+
+ if (cell == last_cell)
+ {
+ if (Node* node = nodes[cell.x][cell.y])
+ node->intersectRay(ray, intersectCallback, max_dist);
+ return;
+ }
+
+ float voxel = (float)CELL_SIZE;
+ float kx_inv = ray.invDirection().x, bx = ray.origin().x;
+ float ky_inv = ray.invDirection().y, by = ray.origin().y;
+
+ int stepX, stepY;
+ float tMaxX, tMaxY;
+ if (kx_inv >= 0)
+ {
+ stepX = 1;
+ float x_border = (cell.x+1) * voxel;
+ tMaxX = (x_border - bx) * kx_inv;
+ }
+ else
+ {
+ stepX = -1;
+ float x_border = (cell.x-1) * voxel;
+ tMaxX = (x_border - bx) * kx_inv;
+ }
+
+ if (ky_inv >= 0)
+ {
+ stepY = 1;
+ float y_border = (cell.y+1) * voxel;
+ tMaxY = (y_border - by) * ky_inv;
+ }
+ else
+ {
+ stepY = -1;
+ float y_border = (cell.y-1) * voxel;
+ tMaxY = (y_border - by) * ky_inv;
+ }
+
+ //int Cycles = std::max((int)ceilf(max_dist/tMaxX),(int)ceilf(max_dist/tMaxY));
+ //int i = 0;
+
+ float tDeltaX = voxel * std::fabs(kx_inv);
+ float tDeltaY = voxel * std::fabs(ky_inv);
+ do
+ {
+ if (Node* node = nodes[cell.x][cell.y])
+ {
+ //float enterdist = max_dist;
+ node->intersectRay(ray, intersectCallback, max_dist);
+ }
+ if (cell == last_cell)
+ break;
+ if (tMaxX < tMaxY)
+ {
+ tMaxX += tDeltaX;
+ cell.x += stepX;
+ }
+ else
+ {
+ tMaxY += tDeltaY;
+ cell.y += stepY;
+ }
+ //++i;
+ } while (cell.isValid());
+ }
+
+ template<typename IsectCallback>
+ void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
+ {
+ Cell cell = Cell::ComputeCell(point.x, point.y);
+ if (!cell.isValid())
+ return;
+ if (Node* node = nodes[cell.x][cell.y])
+ node->intersectPoint(point, intersectCallback);
+ }
+
+ // Optimized verson of intersectRay function for rays with vertical directions
+ template<typename RayCallback>
+ void intersectZAllignedRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist)
+ {
+ Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
+ if (!cell.isValid())
+ return;
+ if (Node* node = nodes[cell.x][cell.y])
+ node->intersectRay(ray, intersectCallback, max_dist);
+ }
+};
+
+#undef CELL_SIZE
+#undef HGRID_MAP_SIZE
+
+#endif
diff --git a/src/common/Collision/VMapDefinitions.h b/src/common/Collision/VMapDefinitions.h
new file mode 100644
index 00000000000..1035d5307cc
--- /dev/null
+++ b/src/common/Collision/VMapDefinitions.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _VMAPDEFINITIONS_H
+#define _VMAPDEFINITIONS_H
+#include <cstring>
+#include <cstdio>
+
+#define LIQUID_TILE_SIZE (533.333f / 128.f)
+
+namespace VMAP
+{
+ const char VMAP_MAGIC[] = "VMAP_4.1";
+ const char RAW_VMAP_MAGIC[] = "VMAP041"; // used in extracted vmap files with raw data
+ const char GAMEOBJECT_MODELS[] = "GameObjectModels.dtree";
+
+ // defined in TileAssembler.cpp currently...
+ bool readChunk(FILE* rf, char *dest, const char *compare, uint32 len);
+}
+
+// Set of helper macros for extractors (VMAP and MMAP)
+#ifndef NO_CORE_FUNCS
+#define VMAP_ERROR_LOG(FILTER, ...) TC_LOG_ERROR(FILTER, __VA_ARGS__)
+#define VMAP_DEBUG_LOG(FILTER, ...) TC_LOG_DEBUG(FILTER, __VA_ARGS__)
+#define VMAP_INFO_LOG(FILTER, ...) TC_LOG_INFO(FILTER, __VA_ARGS__)
+#else
+#define VMAP_ERROR_LOG(FILTER, ...) (void)sizeof(FILTER)
+#define VMAP_DEBUG_LOG(FILTER, ...) (void)sizeof(FILTER)
+#define VMAP_INFO_LOG(FILTER, ...) (void)sizeof(FILTER)
+#endif
+
+#endif
diff --git a/src/common/Collision/VMapTools.h b/src/common/Collision/VMapTools.h
new file mode 100644
index 00000000000..662dc43e9e7
--- /dev/null
+++ b/src/common/Collision/VMapTools.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _VMAPTOOLS_H
+#define _VMAPTOOLS_H
+
+#include <G3D/CollisionDetection.h>
+#include <G3D/AABox.h>
+
+/**
+The Class is mainly taken from G3D/AABSPTree.h but modified to be able to use our internal data structure.
+This is an iterator that helps us analysing the BSP-Trees.
+The collision detection is modified to return true, if we are inside an object.
+*/
+
+namespace VMAP
+{
+ template<class TValue>
+ class IntersectionCallBack {
+ public:
+ TValue* closestEntity;
+ G3D::Vector3 hitLocation;
+ G3D::Vector3 hitNormal;
+
+ void operator()(const G3D::Ray& ray, const TValue* entity, bool pStopAtFirstHit, float& distance) {
+ entity->intersect(ray, distance, pStopAtFirstHit, hitLocation, hitNormal);
+ }
+ };
+
+ //==============================================================
+ //==============================================================
+ //==============================================================
+
+ class MyCollisionDetection
+ {
+ private:
+ public:
+
+ static bool collisionLocationForMovingPointFixedAABox(
+ const G3D::Vector3& origin,
+ const G3D::Vector3& dir,
+ const G3D::AABox& box,
+ G3D::Vector3& location,
+ bool& Inside)
+ {
+
+ // Integer representation of a floating-point value.
+#define IR(x) (reinterpret_cast<G3D::uint32 const&>(x))
+
+ Inside = true;
+ const G3D::Vector3& MinB = box.low();
+ const G3D::Vector3& MaxB = box.high();
+ G3D::Vector3 MaxT(-1.0f, -1.0f, -1.0f);
+
+ // Find candidate planes.
+ for (int i = 0; i < 3; ++i)
+ {
+ if (origin[i] < MinB[i])
+ {
+ location[i] = MinB[i];
+ Inside = false;
+
+ // Calculate T distances to candidate planes
+ if (IR(dir[i]))
+ {
+ MaxT[i] = (MinB[i] - origin[i]) / dir[i];
+ }
+ }
+ else if (origin[i] > MaxB[i])
+ {
+ location[i] = MaxB[i];
+ Inside = false;
+
+ // Calculate T distances to candidate planes
+ if (IR(dir[i]))
+ {
+ MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
+ }
+ }
+ }
+
+ if (Inside)
+ {
+ // definite hit
+ location = origin;
+ return true;
+ }
+
+ // Get largest of the maxT's for final choice of intersection
+ int WhichPlane = 0;
+ if (MaxT[1] > MaxT[WhichPlane])
+ {
+ WhichPlane = 1;
+ }
+
+ if (MaxT[2] > MaxT[WhichPlane])
+ {
+ WhichPlane = 2;
+ }
+
+ // Check final candidate actually inside box
+ if (IR(MaxT[WhichPlane]) & 0x80000000)
+ {
+ // Miss the box
+ return false;
+ }
+
+ for (int i = 0; i < 3; ++i)
+ {
+ if (i != WhichPlane)
+ {
+ location[i] = origin[i] + MaxT[WhichPlane] * dir[i];
+ if ((location[i] < MinB[i]) ||
+ (location[i] > MaxB[i]))
+ {
+ // On this plane we're outside the box extents, so
+ // we miss the box
+ return false;
+ }
+ }
+ }
+ /*
+ // Choose the normal to be the plane normal facing into the ray
+ normal = G3D::Vector3::zero();
+ normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0;
+ */
+ return true;
+
+#undef IR
+ }
+ };
+}
+#endif
diff --git a/src/common/Common.cpp b/src/common/Common.cpp
new file mode 100644
index 00000000000..56e3c4faaf5
--- /dev/null
+++ b/src/common/Common.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "Common.h"
+
+char const* localeNames[TOTAL_LOCALES] = {
+ "enUS",
+ "koKR",
+ "frFR",
+ "deDE",
+ "zhCN",
+ "zhTW",
+ "esES",
+ "esMX",
+ "ruRU"
+};
+
+LocaleConstant GetLocaleByName(const std::string& name)
+{
+ for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
+ if (name==localeNames[i])
+ return LocaleConstant(i);
+
+ return LOCALE_enUS; // including enGB case
+}
+
diff --git a/src/common/Common.h b/src/common/Common.h
new file mode 100644
index 00000000000..7a1ea247a94
--- /dev/null
+++ b/src/common/Common.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 TRINITYCORE_COMMON_H
+#define TRINITYCORE_COMMON_H
+
+#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 <list>
+#include <string>
+#include <map>
+#include <unordered_map>
+#include <queue>
+#include <sstream>
+#include <algorithm>
+#include <memory>
+#include <vector>
+#include <array>
+
+#include <boost/functional/hash.hpp>
+
+#include "Debugging/Errors.h"
+
+#include "Threading/LockedQueue.h"
+
+#if PLATFORM == PLATFORM_WINDOWS
+# include <ws2tcpip.h>
+
+# if defined(__INTEL_COMPILER)
+# if !defined(BOOST_ASIO_HAS_MOVE)
+# define BOOST_ASIO_HAS_MOVE
+# endif // !defined(BOOST_ASIO_HAS_MOVE)
+# endif // if defined(__INTEL_COMPILER)
+
+#else
+# include <sys/types.h>
+# include <sys/ioctl.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <unistd.h>
+# include <netdb.h>
+#endif
+
+#if COMPILER == COMPILER_MICROSOFT
+
+#include <float.h>
+
+#define snprintf _snprintf
+#define atoll _atoi64
+#define vsnprintf _vsnprintf
+#define llabs _abs64
+
+#else
+
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+
+#endif
+
+inline float finiteAlways(float f) { return std::isfinite(f) ? f : 0.0f; }
+
+inline unsigned long atoul(char const* str) { return strtoul(str, nullptr, 10); }
+inline unsigned long long atoull(char const* str) { return strtoull(str, nullptr, 10); }
+
+#define STRINGIZE(a) #a
+
+enum TimeConstants
+{
+ MINUTE = 60,
+ HOUR = MINUTE*60,
+ DAY = HOUR*24,
+ WEEK = DAY*7,
+ MONTH = DAY*30,
+ YEAR = MONTH*12,
+ IN_MILLISECONDS = 1000
+};
+
+enum AccountTypes
+{
+ SEC_PLAYER = 0,
+ SEC_MODERATOR = 1,
+ SEC_GAMEMASTER = 2,
+ SEC_ADMINISTRATOR = 3,
+ SEC_CONSOLE = 4 // must be always last in list, accounts must have less security level always also
+};
+
+enum LocaleConstant
+{
+ LOCALE_enUS = 0,
+ LOCALE_koKR = 1,
+ LOCALE_frFR = 2,
+ LOCALE_deDE = 3,
+ LOCALE_zhCN = 4,
+ LOCALE_zhTW = 5,
+ LOCALE_esES = 6,
+ LOCALE_esMX = 7,
+ LOCALE_ruRU = 8,
+
+ TOTAL_LOCALES
+};
+
+#define DEFAULT_LOCALE LOCALE_enUS
+
+#define MAX_LOCALES 8
+#define MAX_ACCOUNT_TUTORIAL_VALUES 8
+
+extern char const* localeNames[TOTAL_LOCALES];
+
+LocaleConstant GetLocaleByName(const std::string& name);
+
+typedef std::vector<std::string> StringVector;
+
+// we always use stdlibc++ std::max/std::min, undefine some not C++ standard defines (Win API and some other platforms)
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define MAX_QUERY_LEN 32*1024
+
+namespace Trinity
+{
+ //! std::make_unique implementation (TODO: remove this once C++14 is supported)
+ template<typename T, typename ...Args>
+ std::unique_ptr<T> make_unique(Args&& ...args)
+ {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+}
+
+//! Hash implementation for std::pair to allow using pairs in unordered_set or as key for unordered_map
+//! Individual types used in pair must be hashable by boost::hash
+namespace std
+{
+ template<class K, class V>
+ struct hash<std::pair<K, V>>
+ {
+ public:
+ size_t operator()(std::pair<K, V> const& key) const
+ {
+ return boost::hash_value(key);
+ }
+ };
+}
+
+#endif
diff --git a/src/common/CompilerDefs.h b/src/common/CompilerDefs.h
new file mode 100644
index 00000000000..c7867be517b
--- /dev/null
+++ b/src/common/CompilerDefs.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 TRINITY_COMPILERDEFS_H
+#define TRINITY_COMPILERDEFS_H
+
+#define PLATFORM_WINDOWS 0
+#define PLATFORM_UNIX 1
+#define PLATFORM_APPLE 2
+#define PLATFORM_INTEL 3
+
+// must be first (win 64 also define _WIN32)
+#if defined( _WIN64 )
+# define PLATFORM PLATFORM_WINDOWS
+#elif defined( __WIN32__ ) || defined( WIN32 ) || defined( _WIN32 )
+# define PLATFORM PLATFORM_WINDOWS
+#elif defined( __APPLE_CC__ )
+# define PLATFORM PLATFORM_APPLE
+#elif defined( __INTEL_COMPILER )
+# define PLATFORM PLATFORM_INTEL
+#else
+# define PLATFORM PLATFORM_UNIX
+#endif
+
+#define COMPILER_MICROSOFT 0
+#define COMPILER_GNU 1
+#define COMPILER_BORLAND 2
+#define COMPILER_INTEL 3
+
+#ifdef _MSC_VER
+# define COMPILER COMPILER_MICROSOFT
+#elif defined( __BORLANDC__ )
+# define COMPILER COMPILER_BORLAND
+#elif defined( __INTEL_COMPILER )
+# define COMPILER COMPILER_INTEL
+#elif defined( __GNUC__ )
+# define COMPILER COMPILER_GNU
+# define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#else
+# error "FATAL ERROR: Unknown compiler."
+#endif
+
+#endif
diff --git a/src/common/Configuration/Config.cpp b/src/common/Configuration/Config.cpp
new file mode 100644
index 00000000000..ea426a5d33e
--- /dev/null
+++ b/src/common/Configuration/Config.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <algorithm>
+#include <mutex>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/ini_parser.hpp>
+#include "Config.h"
+
+using namespace boost::property_tree;
+
+bool ConfigMgr::LoadInitial(std::string const& file, std::string& error)
+{
+ std::lock_guard<std::mutex> lock(_configLock);
+
+ _filename = file;
+
+ try
+ {
+ ptree fullTree;
+ ini_parser::read_ini(file, fullTree);
+
+ if (fullTree.empty())
+ {
+ error = "empty file (" + file + ")";
+ return false;
+ }
+
+ // Since we're using only one section per config file, we skip the section and have direct property access
+ _config = fullTree.begin()->second;
+ }
+ catch (ini_parser::ini_parser_error const& e)
+ {
+ if (e.line() == 0)
+ error = e.message() + " (" + e.filename() + ")";
+ else
+ error = e.message() + " (" + e.filename() + ":" + std::to_string(e.line()) + ")";
+ return false;
+ }
+
+ return true;
+}
+
+bool ConfigMgr::Reload(std::string& error)
+{
+ return LoadInitial(_filename, error);
+}
+
+std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def)
+{
+ std::string value = _config.get<std::string>(ptree::path_type(name, '/'), def);
+
+ value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
+
+ return value;
+}
+
+bool ConfigMgr::GetBoolDefault(std::string const& name, bool def)
+{
+ try
+ {
+ std::string val = _config.get<std::string>(ptree::path_type(name, '/'));
+ val.erase(std::remove(val.begin(), val.end(), '"'), val.end());
+ return (val == "true" || val == "TRUE" || val == "yes" || val == "YES" || val == "1");
+ }
+ catch (std::exception const& /*ex*/)
+ {
+ return def;
+ }
+}
+
+int ConfigMgr::GetIntDefault(std::string const& name, int def)
+{
+ return _config.get<int>(ptree::path_type(name, '/'), def);
+}
+
+float ConfigMgr::GetFloatDefault(std::string const& name, float def)
+{
+ return _config.get<float>(ptree::path_type(name, '/'), def);
+}
+
+std::string const& ConfigMgr::GetFilename()
+{
+ std::lock_guard<std::mutex> lock(_configLock);
+ return _filename;
+}
+
+std::list<std::string> ConfigMgr::GetKeysByString(std::string const& name)
+{
+ std::lock_guard<std::mutex> lock(_configLock);
+
+ std::list<std::string> keys;
+
+ for (const ptree::value_type& child : _config)
+ if (child.first.compare(0, name.length(), name) == 0)
+ keys.push_back(child.first);
+
+ return keys;
+}
diff --git a/src/common/Configuration/Config.h b/src/common/Configuration/Config.h
new file mode 100644
index 00000000000..5fb7cef9241
--- /dev/null
+++ b/src/common/Configuration/Config.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 CONFIG_H
+#define CONFIG_H
+
+#include <string>
+#include <list>
+#include <mutex>
+#include <boost/property_tree/ptree.hpp>
+
+class ConfigMgr
+{
+ ConfigMgr() { }
+ ~ConfigMgr() { }
+
+public:
+ /// Method used only for loading main configuration files (authserver.conf and worldserver.conf)
+ bool LoadInitial(std::string const& file, std::string& error);
+
+ static ConfigMgr* instance()
+ {
+ static ConfigMgr instance;
+ return &instance;
+ }
+
+ bool Reload(std::string& error);
+
+ std::string GetStringDefault(std::string const& name, const std::string& def);
+ bool GetBoolDefault(std::string const& name, bool def);
+ int GetIntDefault(std::string const& name, int def);
+ float GetFloatDefault(std::string const& name, float def);
+
+ std::string const& GetFilename();
+ std::list<std::string> GetKeysByString(std::string const& name);
+
+private:
+ std::string _filename;
+ boost::property_tree::ptree _config;
+ std::mutex _configLock;
+
+ ConfigMgr(ConfigMgr const&);
+ ConfigMgr& operator=(ConfigMgr const&);
+};
+
+#define sConfigMgr ConfigMgr::instance()
+
+#endif
diff --git a/src/common/Cryptography/ARC4.cpp b/src/common/Cryptography/ARC4.cpp
new file mode 100644
index 00000000000..d1082b39347
--- /dev/null
+++ b/src/common/Cryptography/ARC4.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "ARC4.h"
+
+ARC4::ARC4(uint8 len) : m_ctx()
+{
+ EVP_CIPHER_CTX_init(&m_ctx);
+ EVP_EncryptInit_ex(&m_ctx, EVP_rc4(), NULL, NULL, NULL);
+ EVP_CIPHER_CTX_set_key_length(&m_ctx, len);
+}
+
+ARC4::ARC4(uint8 *seed, uint8 len) : m_ctx()
+{
+ EVP_CIPHER_CTX_init(&m_ctx);
+ EVP_EncryptInit_ex(&m_ctx, EVP_rc4(), NULL, NULL, NULL);
+ EVP_CIPHER_CTX_set_key_length(&m_ctx, len);
+ EVP_EncryptInit_ex(&m_ctx, NULL, NULL, seed, NULL);
+}
+
+ARC4::~ARC4()
+{
+ EVP_CIPHER_CTX_cleanup(&m_ctx);
+}
+
+void ARC4::Init(uint8 *seed)
+{
+ EVP_EncryptInit_ex(&m_ctx, NULL, NULL, seed, NULL);
+}
+
+void ARC4::UpdateData(int len, uint8 *data)
+{
+ int outlen = 0;
+ EVP_EncryptUpdate(&m_ctx, data, &outlen, data, len);
+ EVP_EncryptFinal_ex(&m_ctx, data, &outlen);
+}
diff --git a/src/common/Cryptography/ARC4.h b/src/common/Cryptography/ARC4.h
new file mode 100644
index 00000000000..16a0cb92eb9
--- /dev/null
+++ b/src/common/Cryptography/ARC4.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _AUTH_SARC4_H
+#define _AUTH_SARC4_H
+
+#include <openssl/evp.h>
+#include "Define.h"
+
+class ARC4
+{
+ public:
+ ARC4(uint8 len);
+ ARC4(uint8 *seed, uint8 len);
+ ~ARC4();
+ void Init(uint8 *seed);
+ void UpdateData(int len, uint8 *data);
+ private:
+ EVP_CIPHER_CTX m_ctx;
+};
+
+#endif
diff --git a/src/common/Cryptography/Authentication/AuthCrypt.cpp b/src/common/Cryptography/Authentication/AuthCrypt.cpp
new file mode 100644
index 00000000000..153726e6950
--- /dev/null
+++ b/src/common/Cryptography/Authentication/AuthCrypt.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "AuthCrypt.h"
+#include "Cryptography/HMACSHA1.h"
+#include "Cryptography/BigNumber.h"
+
+#include <cstring>
+
+AuthCrypt::AuthCrypt() :
+ _clientDecrypt(SHA_DIGEST_LENGTH), _serverEncrypt(SHA_DIGEST_LENGTH),
+ _initialized(false)
+{ }
+
+void AuthCrypt::Init(BigNumber* K)
+{
+ uint8 ServerEncryptionKey[SEED_KEY_SIZE] = { 0xCC, 0x98, 0xAE, 0x04, 0xE8, 0x97, 0xEA, 0xCA, 0x12, 0xDD, 0xC0, 0x93, 0x42, 0x91, 0x53, 0x57 };
+ HmacHash serverEncryptHmac(SEED_KEY_SIZE, (uint8*)ServerEncryptionKey);
+ uint8 *encryptHash = serverEncryptHmac.ComputeHash(K);
+
+ uint8 ServerDecryptionKey[SEED_KEY_SIZE] = { 0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE };
+ HmacHash clientDecryptHmac(SEED_KEY_SIZE, (uint8*)ServerDecryptionKey);
+ uint8 *decryptHash = clientDecryptHmac.ComputeHash(K);
+
+ //ARC4 _serverDecrypt(encryptHash);
+ _clientDecrypt.Init(decryptHash);
+ _serverEncrypt.Init(encryptHash);
+ //ARC4 _clientEncrypt(decryptHash);
+
+ // Drop first 1024 bytes, as WoW uses ARC4-drop1024.
+ uint8 syncBuf[1024];
+ memset(syncBuf, 0, 1024);
+
+ _serverEncrypt.UpdateData(1024, syncBuf);
+ //_clientEncrypt.UpdateData(1024, syncBuf);
+
+ memset(syncBuf, 0, 1024);
+
+ //_serverDecrypt.UpdateData(1024, syncBuf);
+ _clientDecrypt.UpdateData(1024, syncBuf);
+
+ _initialized = true;
+}
+
+void AuthCrypt::DecryptRecv(uint8 *data, size_t len)
+{
+ if (!_initialized)
+ return;
+
+ _clientDecrypt.UpdateData(len, data);
+}
+
+void AuthCrypt::EncryptSend(uint8 *data, size_t len)
+{
+ if (!_initialized)
+ return;
+
+ _serverEncrypt.UpdateData(len, data);
+}
+
diff --git a/src/common/Cryptography/Authentication/AuthCrypt.h b/src/common/Cryptography/Authentication/AuthCrypt.h
new file mode 100644
index 00000000000..b8913c95a2c
--- /dev/null
+++ b/src/common/Cryptography/Authentication/AuthCrypt.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _AUTHCRYPT_H
+#define _AUTHCRYPT_H
+
+#include "Cryptography/ARC4.h"
+
+class BigNumber;
+
+class AuthCrypt
+{
+ public:
+ AuthCrypt();
+
+ void Init(BigNumber* K);
+ void DecryptRecv(uint8 *, size_t);
+ void EncryptSend(uint8 *, size_t);
+
+ bool IsInitialized() const { return _initialized; }
+
+ private:
+ ARC4 _clientDecrypt;
+ ARC4 _serverEncrypt;
+ bool _initialized;
+};
+#endif
diff --git a/src/common/Cryptography/BigNumber.cpp b/src/common/Cryptography/BigNumber.cpp
new file mode 100644
index 00000000000..720e8e30441
--- /dev/null
+++ b/src/common/Cryptography/BigNumber.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "Cryptography/BigNumber.h"
+#include <openssl/bn.h>
+#include <cstring>
+#include <algorithm>
+#include <memory>
+
+BigNumber::BigNumber()
+ : _bn(BN_new())
+{ }
+
+BigNumber::BigNumber(BigNumber const& bn)
+ : _bn(BN_dup(bn._bn))
+{ }
+
+BigNumber::BigNumber(uint32 val)
+ : _bn(BN_new())
+{
+ BN_set_word(_bn, val);
+}
+
+BigNumber::~BigNumber()
+{
+ BN_free(_bn);
+}
+
+void BigNumber::SetDword(uint32 val)
+{
+ BN_set_word(_bn, val);
+}
+
+void BigNumber::SetQword(uint64 val)
+{
+ BN_set_word(_bn, (uint32)(val >> 32));
+ BN_lshift(_bn, _bn, 32);
+ BN_add_word(_bn, (uint32)(val & 0xFFFFFFFF));
+}
+
+void BigNumber::SetBinary(uint8 const* bytes, int32 len)
+{
+ uint8* array = new uint8[len];
+
+ for (int i = 0; i < len; i++)
+ array[i] = bytes[len - 1 - i];
+
+ BN_bin2bn(array, len, _bn);
+
+ delete[] array;
+}
+
+void BigNumber::SetHexStr(char const* str)
+{
+ BN_hex2bn(&_bn, str);
+}
+
+void BigNumber::SetRand(int32 numbits)
+{
+ BN_rand(_bn, numbits, 0, 1);
+}
+
+BigNumber& BigNumber::operator=(BigNumber const& bn)
+{
+ if (this == &bn)
+ return *this;
+
+ BN_copy(_bn, bn._bn);
+ return *this;
+}
+
+BigNumber BigNumber::operator+=(BigNumber const& bn)
+{
+ BN_add(_bn, _bn, bn._bn);
+ return *this;
+}
+
+BigNumber BigNumber::operator-=(BigNumber const& bn)
+{
+ BN_sub(_bn, _bn, bn._bn);
+ return *this;
+}
+
+BigNumber BigNumber::operator*=(BigNumber const& bn)
+{
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ BN_mul(_bn, _bn, bn._bn, bnctx);
+ BN_CTX_free(bnctx);
+
+ return *this;
+}
+
+BigNumber BigNumber::operator/=(BigNumber const& bn)
+{
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ BN_div(_bn, NULL, _bn, bn._bn, bnctx);
+ BN_CTX_free(bnctx);
+
+ return *this;
+}
+
+BigNumber BigNumber::operator%=(BigNumber const& bn)
+{
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ BN_mod(_bn, _bn, bn._bn, bnctx);
+ BN_CTX_free(bnctx);
+
+ return *this;
+}
+
+BigNumber BigNumber::Exp(BigNumber const& bn)
+{
+ BigNumber ret;
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ BN_exp(ret._bn, _bn, bn._bn, bnctx);
+ BN_CTX_free(bnctx);
+
+ return ret;
+}
+
+BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2)
+{
+ BigNumber ret;
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ BN_mod_exp(ret._bn, _bn, bn1._bn, bn2._bn, bnctx);
+ BN_CTX_free(bnctx);
+
+ return ret;
+}
+
+int32 BigNumber::GetNumBytes(void)
+{
+ return BN_num_bytes(_bn);
+}
+
+uint32 BigNumber::AsDword()
+{
+ return (uint32)BN_get_word(_bn);
+}
+
+bool BigNumber::isZero() const
+{
+ return BN_is_zero(_bn);
+}
+
+std::unique_ptr<uint8[]> BigNumber::AsByteArray(int32 minSize, bool littleEndian)
+{
+ int numBytes = GetNumBytes();
+ int length = (minSize >= numBytes) ? minSize : numBytes;
+
+ uint8* array = new uint8[length];
+
+ // If we need more bytes than length of BigNumber set the rest to 0
+ if (length > numBytes)
+ memset((void*)array, 0, length);
+
+ BN_bn2bin(_bn, (unsigned char *)array);
+
+ // openssl's BN stores data internally in big endian format, reverse if little endian desired
+ if (littleEndian)
+ std::reverse(array, array + numBytes);
+
+ std::unique_ptr<uint8[]> ret(array);
+ return ret;
+}
+
+char * BigNumber::AsHexStr() const
+{
+ return BN_bn2hex(_bn);
+}
+
+char * BigNumber::AsDecStr() const
+{
+ return BN_bn2dec(_bn);
+}
+
diff --git a/src/common/Cryptography/BigNumber.h b/src/common/Cryptography/BigNumber.h
new file mode 100644
index 00000000000..e6a056b5baa
--- /dev/null
+++ b/src/common/Cryptography/BigNumber.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _AUTH_BIGNUMBER_H
+#define _AUTH_BIGNUMBER_H
+
+#include <memory>
+#include "Define.h"
+
+struct bignum_st;
+
+class BigNumber
+{
+ public:
+ BigNumber();
+ BigNumber(BigNumber const& bn);
+ BigNumber(uint32);
+ ~BigNumber();
+
+ void SetDword(uint32);
+ void SetQword(uint64);
+ void SetBinary(uint8 const* bytes, int32 len);
+ void SetHexStr(char const* str);
+
+ void SetRand(int32 numbits);
+
+ BigNumber& operator=(BigNumber const& bn);
+
+ BigNumber operator+=(BigNumber const& bn);
+ BigNumber operator+(BigNumber const& bn)
+ {
+ BigNumber t(*this);
+ return t += bn;
+ }
+
+ BigNumber operator-=(BigNumber const& bn);
+ BigNumber operator-(BigNumber const& bn)
+ {
+ BigNumber t(*this);
+ return t -= bn;
+ }
+
+ BigNumber operator*=(BigNumber const& bn);
+ BigNumber operator*(BigNumber const& bn)
+ {
+ BigNumber t(*this);
+ return t *= bn;
+ }
+
+ BigNumber operator/=(BigNumber const& bn);
+ BigNumber operator/(BigNumber const& bn)
+ {
+ BigNumber t(*this);
+ return t /= bn;
+ }
+
+ BigNumber operator%=(BigNumber const& bn);
+ BigNumber operator%(BigNumber const& bn)
+ {
+ BigNumber t(*this);
+ return t %= bn;
+ }
+
+ bool isZero() const;
+
+ BigNumber ModExp(BigNumber const& bn1, BigNumber const& bn2);
+ BigNumber Exp(BigNumber const&);
+
+ int32 GetNumBytes(void);
+
+ struct bignum_st *BN() { return _bn; }
+
+ uint32 AsDword();
+
+ std::unique_ptr<uint8[]> AsByteArray(int32 minSize = 0, bool littleEndian = true);
+
+ char * AsHexStr() const;
+ char * AsDecStr() const;
+
+ private:
+ struct bignum_st *_bn;
+
+};
+#endif
+
diff --git a/src/common/Cryptography/HMACSHA1.cpp b/src/common/Cryptography/HMACSHA1.cpp
new file mode 100644
index 00000000000..304633cbc03
--- /dev/null
+++ b/src/common/Cryptography/HMACSHA1.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "HMACSHA1.h"
+#include "BigNumber.h"
+#include "Common.h"
+
+HmacHash::HmacHash(uint32 len, uint8 *seed)
+{
+ HMAC_CTX_init(&m_ctx);
+ HMAC_Init_ex(&m_ctx, seed, len, EVP_sha1(), NULL);
+ memset(m_digest, 0, sizeof(m_digest));
+}
+
+HmacHash::~HmacHash()
+{
+ HMAC_CTX_cleanup(&m_ctx);
+}
+
+void HmacHash::UpdateData(const std::string &str)
+{
+ HMAC_Update(&m_ctx, (uint8 const*)str.c_str(), str.length());
+}
+
+void HmacHash::UpdateData(const uint8* data, size_t len)
+{
+ HMAC_Update(&m_ctx, data, len);
+}
+
+void HmacHash::Finalize()
+{
+ uint32 length = 0;
+ HMAC_Final(&m_ctx, (uint8*)m_digest, &length);
+ ASSERT(length == SHA_DIGEST_LENGTH);
+}
+
+uint8 *HmacHash::ComputeHash(BigNumber* bn)
+{
+ HMAC_Update(&m_ctx, bn->AsByteArray().get(), bn->GetNumBytes());
+ Finalize();
+ return (uint8*)m_digest;
+}
diff --git a/src/common/Cryptography/HMACSHA1.h b/src/common/Cryptography/HMACSHA1.h
new file mode 100644
index 00000000000..66cf214d9b1
--- /dev/null
+++ b/src/common/Cryptography/HMACSHA1.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _AUTH_HMAC_H
+#define _AUTH_HMAC_H
+
+#include "Define.h"
+#include <string>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+class BigNumber;
+
+#define SEED_KEY_SIZE 16
+
+class HmacHash
+{
+ public:
+ HmacHash(uint32 len, uint8 *seed);
+ ~HmacHash();
+ void UpdateData(const std::string &str);
+ void UpdateData(const uint8* data, size_t len);
+ void Finalize();
+ uint8 *ComputeHash(BigNumber* bn);
+ uint8 *GetDigest() { return (uint8*)m_digest; }
+ int GetLength() const { return SHA_DIGEST_LENGTH; }
+ private:
+ HMAC_CTX m_ctx;
+ uint8 m_digest[SHA_DIGEST_LENGTH];
+};
+#endif
+
diff --git a/src/common/Cryptography/OpenSSLCrypto.cpp b/src/common/Cryptography/OpenSSLCrypto.cpp
new file mode 100644
index 00000000000..f122888292f
--- /dev/null
+++ b/src/common/Cryptography/OpenSSLCrypto.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <OpenSSLCrypto.h>
+#include <openssl/crypto.h>
+#include <vector>
+#include <thread>
+#include <mutex>
+
+std::vector<std::mutex*> cryptoLocks;
+
+static void lockingCallback(int mode, int type, const char* /*file*/, int /*line*/)
+{
+ if (mode & CRYPTO_LOCK)
+ cryptoLocks[type]->lock();
+ else
+ cryptoLocks[type]->unlock();
+}
+
+static void threadIdCallback(CRYPTO_THREADID * id)
+{
+ CRYPTO_THREADID_set_numeric(id, std::hash<std::thread::id>()(std::this_thread::get_id()));
+}
+
+void OpenSSLCrypto::threadsSetup()
+{
+ cryptoLocks.resize(CRYPTO_num_locks());
+ for(int i = 0 ; i < CRYPTO_num_locks(); ++i)
+ {
+ cryptoLocks[i] = new std::mutex;
+ }
+ CRYPTO_THREADID_set_callback(threadIdCallback);
+ CRYPTO_set_locking_callback(lockingCallback);
+}
+
+void OpenSSLCrypto::threadsCleanup()
+{
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_THREADID_set_callback(NULL);
+ for(int i = 0 ; i < CRYPTO_num_locks(); ++i)
+ {
+ delete cryptoLocks[i];
+ }
+ cryptoLocks.resize(0);
+}
diff --git a/src/common/Cryptography/OpenSSLCrypto.h b/src/common/Cryptography/OpenSSLCrypto.h
new file mode 100644
index 00000000000..0daa20c4780
--- /dev/null
+++ b/src/common/Cryptography/OpenSSLCrypto.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef OPENSSL_CRYPTO_H
+#define OPENSSL_CRYPTO_H
+
+/**
+* A group of functions which setup openssl crypto module to work properly in multithreaded enviroment
+* If not setup properly - it will crash
+*/
+namespace OpenSSLCrypto
+{
+ /// Needs to be called before threads using openssl are spawned
+ void threadsSetup();
+ /// Needs to be called after threads using openssl are despawned
+ void threadsCleanup();
+}
+
+#endif \ No newline at end of file
diff --git a/src/common/Cryptography/SHA1.cpp b/src/common/Cryptography/SHA1.cpp
new file mode 100644
index 00000000000..bd7101075de
--- /dev/null
+++ b/src/common/Cryptography/SHA1.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "SHA1.h"
+#include "BigNumber.h"
+#include <cstring>
+#include <stdarg.h>
+
+SHA1Hash::SHA1Hash()
+{
+ SHA1_Init(&mC);
+ memset(mDigest, 0, SHA_DIGEST_LENGTH * sizeof(uint8));
+}
+
+SHA1Hash::~SHA1Hash()
+{
+ SHA1_Init(&mC);
+}
+
+void SHA1Hash::UpdateData(const uint8 *dta, int len)
+{
+ SHA1_Update(&mC, dta, len);
+}
+
+void SHA1Hash::UpdateData(const std::string &str)
+{
+ UpdateData((uint8 const*)str.c_str(), str.length());
+}
+
+void SHA1Hash::UpdateBigNumbers(BigNumber* bn0, ...)
+{
+ va_list v;
+ BigNumber* bn;
+
+ va_start(v, bn0);
+ bn = bn0;
+ while (bn)
+ {
+ UpdateData(bn->AsByteArray().get(), bn->GetNumBytes());
+ bn = va_arg(v, BigNumber*);
+ }
+ va_end(v);
+}
+
+void SHA1Hash::Initialize()
+{
+ SHA1_Init(&mC);
+}
+
+void SHA1Hash::Finalize(void)
+{
+ SHA1_Final(mDigest, &mC);
+}
+
diff --git a/src/common/Cryptography/SHA1.h b/src/common/Cryptography/SHA1.h
new file mode 100644
index 00000000000..f59bdc25556
--- /dev/null
+++ b/src/common/Cryptography/SHA1.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _AUTH_SHA1_H
+#define _AUTH_SHA1_H
+
+#include "Define.h"
+#include <string>
+#include <openssl/sha.h>
+
+class BigNumber;
+
+class SHA1Hash
+{
+ public:
+ SHA1Hash();
+ ~SHA1Hash();
+
+ void UpdateBigNumbers(BigNumber* bn0, ...);
+
+ void UpdateData(const uint8 *dta, int len);
+ void UpdateData(const std::string &str);
+
+ void Initialize();
+ void Finalize();
+
+ uint8 *GetDigest(void) { return mDigest; }
+ int GetLength(void) const { return SHA_DIGEST_LENGTH; }
+
+ private:
+ SHA_CTX mC;
+ uint8 mDigest[SHA_DIGEST_LENGTH];
+};
+#endif
+
diff --git a/src/common/Cryptography/WardenKeyGeneration.h b/src/common/Cryptography/WardenKeyGeneration.h
new file mode 100644
index 00000000000..bfa0337d347
--- /dev/null
+++ b/src/common/Cryptography/WardenKeyGeneration.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "SHA1.h"
+
+#include <cstring>
+
+#ifndef _WARDEN_KEY_GENERATION_H
+#define _WARDEN_KEY_GENERATION_H
+
+class SHA1Randx
+{
+public:
+ SHA1Randx(uint8* buff, uint32 size)
+ {
+ uint32 halfSize = size / 2;
+
+ sh.Initialize();
+ sh.UpdateData(buff, halfSize);
+ sh.Finalize();
+
+ memcpy(o1, sh.GetDigest(), 20);
+
+ sh.Initialize();
+ sh.UpdateData(buff + halfSize, size - halfSize);
+ sh.Finalize();
+
+ memcpy(o2, sh.GetDigest(), 20);
+
+ memset(o0, 0x00, 20);
+
+ FillUp();
+ }
+
+ void Generate(uint8* buf, uint32 sz)
+ {
+ for (uint32 i = 0; i < sz; ++i)
+ {
+ if (taken == 20)
+ FillUp();
+
+ buf[i] = o0[taken];
+ taken++;
+ }
+ }
+
+private:
+ void FillUp()
+ {
+ sh.Initialize();
+ sh.UpdateData(o1, 20);
+ sh.UpdateData(o0, 20);
+ sh.UpdateData(o2, 20);
+ sh.Finalize();
+
+ memcpy(o0, sh.GetDigest(), 20);
+
+ taken = 0;
+ }
+
+ SHA1Hash sh;
+ uint32 taken;
+ uint8 o0[20], o1[20], o2[20];
+};
+
+#endif
diff --git a/src/common/Debugging/Errors.cpp b/src/common/Debugging/Errors.cpp
new file mode 100644
index 00000000000..4c7e91a8219
--- /dev/null
+++ b/src/common/Debugging/Errors.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "Errors.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <thread>
+#include <cstdarg>
+
+/**
+ @file Errors.cpp
+
+ @brief This file contains definitions of functions used for reporting critical application errors
+
+ It is very important that (std::)abort is NEVER called in place of *((volatile int*)NULL) = 0;
+ Calling abort() on Windows does not invoke unhandled exception filters - a mechanism used by WheatyExceptionReport
+ to log crashes. exit(1) calls here are for static analysis tools to indicate that calling functions defined in this file
+ terminates the application.
+ */
+
+namespace Trinity {
+
+void Assert(char const* file, int line, char const* function, char const* message)
+{
+ fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n",
+ file, line, function, message);
+ *((volatile int*)NULL) = 0;
+ exit(1);
+}
+
+void Assert(char const* file, int line, char const* function, char const* message, char const* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s ", file, line, function, message);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+
+ va_end(args);
+ *((volatile int*)NULL) = 0;
+ exit(1);
+}
+
+void Fatal(char const* file, int line, char const* function, char const* message, ...)
+{
+ va_list args;
+ va_start(args, message);
+
+ fprintf(stderr, "\n%s:%i in %s FATAL ERROR:\n ", file, line, function);
+ vfprintf(stderr, message, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ *((volatile int*)NULL) = 0;
+ exit(1);
+}
+
+void Error(char const* file, int line, char const* function, char const* message)
+{
+ fprintf(stderr, "\n%s:%i in %s ERROR:\n %s\n",
+ file, line, function, message);
+ *((volatile int*)NULL) = 0;
+ exit(1);
+}
+
+void Warning(char const* file, int line, char const* function, char const* message)
+{
+ fprintf(stderr, "\n%s:%i in %s WARNING:\n %s\n",
+ file, line, function, message);
+}
+
+void Abort(char const* file, int line, char const* function)
+{
+ fprintf(stderr, "\n%s:%i in %s ABORTED\n",
+ file, line, function);
+ *((volatile int*)NULL) = 0;
+ exit(1);
+}
+
+} // namespace Trinity
diff --git a/src/common/Debugging/Errors.h b/src/common/Debugging/Errors.h
new file mode 100644
index 00000000000..9e526933acc
--- /dev/null
+++ b/src/common/Debugging/Errors.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 TRINITYCORE_ERRORS_H
+#define TRINITYCORE_ERRORS_H
+
+#include "Define.h"
+
+namespace Trinity
+{
+ DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
+ DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, char const* message, char const* format, ...) ATTR_NORETURN ATTR_PRINTF(5, 6);
+
+ DECLSPEC_NORETURN void Fatal(char const* file, int line, char const* function, char const* message, ...) ATTR_NORETURN ATTR_PRINTF(4, 5);
+
+ DECLSPEC_NORETURN void Error(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
+
+ DECLSPEC_NORETURN void Abort(char const* file, int line, char const* function) ATTR_NORETURN;
+
+ void Warning(char const* file, int line, char const* function, char const* message);
+
+} // namespace Trinity
+
+#if COMPILER == COMPILER_MICROSOFT
+#define ASSERT_BEGIN __pragma(warning(push)) __pragma(warning(disable: 4127))
+#define ASSERT_END __pragma(warning(pop))
+#else
+#define ASSERT_BEGIN
+#define ASSERT_END
+#endif
+
+#define WPAssert(cond, ...) ASSERT_BEGIN do { if (!(cond)) Trinity::Assert(__FILE__, __LINE__, __FUNCTION__, #cond, ##__VA_ARGS__); } while(0) ASSERT_END
+#define WPFatal(cond, ...) ASSERT_BEGIN do { if (!(cond)) Trinity::Fatal(__FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); } while(0) ASSERT_END
+#define WPError(cond, msg) ASSERT_BEGIN do { if (!(cond)) Trinity::Error(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) ASSERT_END
+#define WPWarning(cond, msg) ASSERT_BEGIN do { if (!(cond)) Trinity::Warning(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) ASSERT_END
+#define WPAbort() ASSERT_BEGIN do { Trinity::Abort(__FILE__, __LINE__, __FUNCTION__); } while(0) ASSERT_END
+
+#define ASSERT WPAssert
+#define ABORT WPAbort
+
+template <typename T> inline T* ASSERT_NOTNULL(T* pointer)
+{
+ ASSERT(pointer);
+ return pointer;
+}
+
+#endif
diff --git a/src/common/Debugging/WheatyExceptionReport.cpp b/src/common/Debugging/WheatyExceptionReport.cpp
new file mode 100644
index 00000000000..5b9a1b1bd6c
--- /dev/null
+++ b/src/common/Debugging/WheatyExceptionReport.cpp
@@ -0,0 +1,1463 @@
+//==========================================
+// Matt Pietrek
+// MSDN Magazine, 2002
+// FILE: WheatyExceptionReport.CPP
+//==========================================
+#include "CompilerDefs.h"
+
+#if PLATFORM == PLATFORM_WINDOWS && !defined(__MINGW32__)
+#define WIN32_LEAN_AND_MEAN
+#pragma warning(disable:4996)
+#pragma warning(disable:4312)
+#pragma warning(disable:4311)
+#include <windows.h>
+#include <tlhelp32.h>
+#include <stdio.h>
+#include <tchar.h>
+#define _NO_CVCONST_H
+#include <dbghelp.h>
+
+#include "WheatyExceptionReport.h"
+
+#include "Common.h"
+#include "GitRevision.h"
+
+#define CrashFolder _T("Crashes")
+#pragma comment(linker, "/DEFAULTLIB:dbghelp.lib")
+
+inline LPTSTR ErrorMessage(DWORD dw)
+{
+ LPVOID lpMsgBuf;
+ DWORD formatResult = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ dw,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0, NULL);
+ if (formatResult != 0)
+ return (LPTSTR)lpMsgBuf;
+ else
+ {
+ LPTSTR msgBuf = (LPTSTR)LocalAlloc(LPTR, 30);
+ sprintf(msgBuf, "Unknown error: %u", dw);
+ return msgBuf;
+ }
+
+}
+
+//============================== Global Variables =============================
+
+//
+// Declare the static variables of the WheatyExceptionReport class
+//
+TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH];
+TCHAR WheatyExceptionReport::m_szDumpFileName[MAX_PATH];
+LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter;
+HANDLE WheatyExceptionReport::m_hReportFile;
+HANDLE WheatyExceptionReport::m_hDumpFile;
+HANDLE WheatyExceptionReport::m_hProcess;
+SymbolPairs WheatyExceptionReport::symbols;
+std::stack<SymbolDetail> WheatyExceptionReport::symbolDetails;
+bool WheatyExceptionReport::stackOverflowException;
+bool WheatyExceptionReport::alreadyCrashed;
+std::mutex WheatyExceptionReport::alreadyCrashedLock;
+WheatyExceptionReport::pRtlGetVersion WheatyExceptionReport::RtlGetVersion;
+
+
+// Declare global instance of class
+WheatyExceptionReport g_WheatyExceptionReport;
+
+//============================== Class Methods =============================
+
+WheatyExceptionReport::WheatyExceptionReport() // Constructor
+{
+ // Install the unhandled exception filter function
+ m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
+ m_hProcess = GetCurrentProcess();
+ stackOverflowException = false;
+ alreadyCrashed = false;
+ RtlGetVersion = (pRtlGetVersion)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "RtlGetVersion");
+ if (!IsDebuggerPresent())
+ {
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ }
+}
+
+//============
+// Destructor
+//============
+WheatyExceptionReport::~WheatyExceptionReport()
+{
+ if (m_previousFilter)
+ SetUnhandledExceptionFilter(m_previousFilter);
+ ClearSymbols();
+}
+
+//===========================================================
+// Entry point where control comes on an unhandled exception
+//===========================================================
+LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
+PEXCEPTION_POINTERS pExceptionInfo)
+{
+ std::unique_lock<std::mutex> guard(alreadyCrashedLock);
+ // Handle only 1 exception in the whole process lifetime
+ if (alreadyCrashed)
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ alreadyCrashed = true;
+
+ if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW)
+ stackOverflowException = true;
+
+ TCHAR module_folder_name[MAX_PATH];
+ GetModuleFileName(0, module_folder_name, MAX_PATH);
+ TCHAR* pos = _tcsrchr(module_folder_name, '\\');
+ if (!pos)
+ return 0;
+ pos[0] = '\0';
+ ++pos;
+
+ TCHAR crash_folder_path[MAX_PATH];
+ sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder);
+ if (!CreateDirectory(crash_folder_path, NULL))
+ {
+ if (GetLastError() != ERROR_ALREADY_EXISTS)
+ return 0;
+ }
+
+ SYSTEMTIME systime;
+ GetLocalTime(&systime);
+ sprintf(m_szDumpFileName, "%s\\%s_%s_[%u-%u_%u-%u-%u].dmp",
+ crash_folder_path, GitRevision::GetHash(), pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
+
+ sprintf(m_szLogFileName, "%s\\%s_%s_[%u-%u_%u-%u-%u].txt",
+ crash_folder_path, GitRevision::GetHash(), pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
+
+ m_hDumpFile = CreateFile(m_szDumpFileName,
+ GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_ALWAYS,
+ FILE_FLAG_WRITE_THROUGH,
+ 0);
+
+ m_hReportFile = CreateFile(m_szLogFileName,
+ GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_ALWAYS,
+ FILE_FLAG_WRITE_THROUGH,
+ 0);
+
+ if (m_hDumpFile)
+ {
+ MINIDUMP_EXCEPTION_INFORMATION info;
+ info.ClientPointers = FALSE;
+ info.ExceptionPointers = pExceptionInfo;
+ info.ThreadId = GetCurrentThreadId();
+
+ MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
+ m_hDumpFile, MiniDumpWithIndirectlyReferencedMemory, &info, 0, 0);
+
+ CloseHandle(m_hDumpFile);
+ }
+
+ if (m_hReportFile)
+ {
+ SetFilePointer(m_hReportFile, 0, 0, FILE_END);
+
+ GenerateExceptionReport(pExceptionInfo);
+
+ CloseHandle(m_hReportFile);
+ m_hReportFile = 0;
+ }
+
+ if (m_previousFilter)
+ return m_previousFilter(pExceptionInfo);
+ else
+ return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
+}
+
+BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount)
+{
+ if (!sProcessorName)
+ return FALSE;
+
+ HKEY hKey;
+ LONG lRet;
+ lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
+ 0, KEY_QUERY_VALUE, &hKey);
+ if (lRet != ERROR_SUCCESS)
+ return FALSE;
+ TCHAR szTmp[2048];
+ DWORD cntBytes = sizeof(szTmp);
+ lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL,
+ (LPBYTE)szTmp, &cntBytes);
+ if (lRet != ERROR_SUCCESS)
+ return FALSE;
+ ::RegCloseKey(hKey);
+ sProcessorName[0] = '\0';
+ // Skip spaces
+ TCHAR* psz = szTmp;
+ while (iswspace(*psz))
+ ++psz;
+ _tcsncpy(sProcessorName, psz, maxcount);
+ return TRUE;
+}
+
+template<size_t size>
+void ToTchar(wchar_t const* src, TCHAR (&dst)[size], std::true_type)
+{
+ wcstombs_s(nullptr, dst, src, size);
+}
+
+template<size_t size>
+void ToTchar(wchar_t const* src, TCHAR (&dst)[size], std::false_type)
+{
+ wcscpy_s(dst, src);
+}
+
+BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
+{
+ // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+ // If that fails, try using the OSVERSIONINFO structure.
+ RTL_OSVERSIONINFOEXW osvi = { 0 };
+ osvi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+ NTSTATUS bVersionEx = RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi);
+ if (bVersionEx < 0)
+ {
+ osvi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);
+ if (!RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi))
+ return FALSE;
+ }
+ *szVersion = _T('\0');
+
+ TCHAR szCSDVersion[256];
+ ToTchar(osvi.szCSDVersion, szCSDVersion, std::is_same<TCHAR, char>::type());
+
+ TCHAR wszTmp[128];
+ switch (osvi.dwPlatformId)
+ {
+ // Windows NT product family.
+ case VER_PLATFORM_WIN32_NT:
+ {
+ #if WINVER < 0x0500
+ BYTE suiteMask = osvi.wReserved[0];
+ BYTE productType = osvi.wReserved[1];
+ #else
+ WORD suiteMask = osvi.wSuiteMask;
+ BYTE productType = osvi.wProductType;
+ #endif // WINVER < 0x0500
+
+ // Test for the specific product family.
+ if (osvi.dwMajorVersion == 10)
+ {
+ if (productType == VER_NT_WORKSTATION)
+ _tcsncat(szVersion, _T("Windows 10 "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Windows Server 2016 "), cntMax);
+ }
+ else if (osvi.dwMajorVersion == 6)
+ {
+ if (productType == VER_NT_WORKSTATION)
+ {
+ if (osvi.dwMinorVersion == 3)
+ _tcsncat(szVersion, _T("Windows 8.1 "), cntMax);
+ else if (osvi.dwMinorVersion == 2)
+ _tcsncat(szVersion, _T("Windows 8 "), cntMax);
+ else if (osvi.dwMinorVersion == 1)
+ _tcsncat(szVersion, _T("Windows 7 "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Windows Vista "), cntMax);
+ }
+ else if (osvi.dwMinorVersion == 3)
+ _tcsncat(szVersion, _T("Windows Server 2012 R2 "), cntMax);
+ else if (osvi.dwMinorVersion == 2)
+ _tcsncat(szVersion, _T("Windows Server 2012 "), cntMax);
+ else if (osvi.dwMinorVersion == 1)
+ _tcsncat(szVersion, _T("Windows Server 2008 R2 "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Windows Server 2008 "), cntMax);
+ }
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax);
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
+ _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax);
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+ _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
+ else if (osvi.dwMajorVersion <= 4)
+ _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
+
+ // Test for specific product on Windows NT 4.0 SP6 and later.
+ if (bVersionEx >= 0)
+ {
+ // Test for the workstation type.
+ if (productType == VER_NT_WORKSTATION)
+ {
+ if (osvi.dwMajorVersion == 4)
+ _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
+ else if (suiteMask & VER_SUITE_PERSONAL)
+ _tcsncat(szVersion, _T("Home Edition "), cntMax);
+ else if (suiteMask & VER_SUITE_EMBEDDEDNT)
+ _tcsncat(szVersion, _T("Embedded "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Professional "), cntMax);
+ }
+ // Test for the server type.
+ else if (productType == VER_NT_SERVER)
+ {
+ if (osvi.dwMajorVersion == 6 || osvi.dwMajorVersion == 10)
+ {
+ if (suiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED)
+ _tcsncat(szVersion, _T("Essentials "), cntMax);
+ else if (suiteMask & VER_SUITE_DATACENTER)
+ _tcsncat(szVersion, _T("Datacenter "), cntMax);
+ else if (suiteMask & VER_SUITE_ENTERPRISE)
+ _tcsncat(szVersion, _T("Enterprise "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Standard "), cntMax);
+ }
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ {
+ if (suiteMask & VER_SUITE_DATACENTER)
+ _tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
+ else if (suiteMask & VER_SUITE_ENTERPRISE)
+ _tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
+ else if (suiteMask == VER_SUITE_BLADE)
+ _tcsncat(szVersion, _T("Web Edition "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Standard Edition "), cntMax);
+ }
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+ {
+ if (suiteMask & VER_SUITE_DATACENTER)
+ _tcsncat(szVersion, _T("Datacenter Server "), cntMax);
+ else if (suiteMask & VER_SUITE_ENTERPRISE)
+ _tcsncat(szVersion, _T("Advanced Server "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Server "), cntMax);
+ }
+ else // Windows NT 4.0
+ {
+ if (suiteMask & VER_SUITE_ENTERPRISE)
+ _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
+ }
+ }
+ }
+
+ // Display service pack (if any) and build number.
+ if (osvi.dwMajorVersion == 4 && _tcsicmp(szCSDVersion, _T("Service Pack 6")) == 0)
+ {
+ HKEY hKey;
+ LONG lRet;
+
+ // Test for SP6 versus SP6a.
+ lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
+ if (lRet == ERROR_SUCCESS)
+ {
+ _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
+ osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ }
+ else // Windows NT 4.0 prior to SP6a
+ {
+ _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
+ szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ }
+ ::RegCloseKey(hKey);
+ }
+ else // Windows NT 3.51 and earlier or Windows 2000 and later
+ {
+ if (!_tcslen(szCSDVersion))
+ _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"),
+ osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ else
+ _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
+ szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ }
+ break;
+ }
+ default:
+ _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
+ szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ break;
+ }
+
+ return TRUE;
+}
+
+void WheatyExceptionReport::PrintSystemInfo()
+{
+ SYSTEM_INFO SystemInfo;
+ ::GetSystemInfo(&SystemInfo);
+
+ MEMORYSTATUS MemoryStatus;
+ MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
+ ::GlobalMemoryStatus(&MemoryStatus);
+ TCHAR sString[1024];
+ _tprintf(_T("//=====================================================\r\n"));
+ if (_GetProcessorName(sString, countof(sString)))
+ _tprintf(_T("*** Hardware ***\r\nProcessor: %s\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
+ sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
+ else
+ _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
+ SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
+
+ if (_GetWindowsVersion(sString, countof(sString)))
+ _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString);
+ else
+ _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
+}
+
+//===========================================================================
+void WheatyExceptionReport::printTracesForAllThreads(bool bWriteVariables)
+{
+ THREADENTRY32 te32;
+
+ DWORD dwOwnerPID = GetCurrentProcessId();
+ m_hProcess = GetCurrentProcess();
+ // Take a snapshot of all running threads
+ HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+ if (hThreadSnap == INVALID_HANDLE_VALUE)
+ return;
+
+ // Fill in the size of the structure before using it.
+ te32.dwSize = sizeof(THREADENTRY32);
+
+ // Retrieve information about the first thread,
+ // and exit if unsuccessful
+ if (!Thread32First(hThreadSnap, &te32))
+ {
+ CloseHandle(hThreadSnap); // Must clean up the
+ // snapshot object!
+ return;
+ }
+
+ // Now walk the thread list of the system,
+ // and display information about each thread
+ // associated with the specified process
+ do
+ {
+ if (te32.th32OwnerProcessID == dwOwnerPID)
+ {
+ CONTEXT context;
+ context.ContextFlags = 0xffffffff;
+ HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, false, te32.th32ThreadID);
+ if (threadHandle)
+ {
+ if (GetThreadContext(threadHandle, &context))
+ WriteStackDetails(&context, bWriteVariables, threadHandle);
+ CloseHandle(threadHandle);
+ }
+ }
+ } while (Thread32Next(hThreadSnap, &te32));
+
+// Don't forget to clean up the snapshot object.
+ CloseHandle(hThreadSnap);
+}
+
+//===========================================================================
+// Open the report file, and write the desired information to it. Called by
+// WheatyUnhandledExceptionFilter
+//===========================================================================
+void WheatyExceptionReport::GenerateExceptionReport(
+PEXCEPTION_POINTERS pExceptionInfo)
+{
+ __try
+ {
+ SYSTEMTIME systime;
+ GetLocalTime(&systime);
+
+ // Start out with a banner
+ _tprintf(_T("Revision: %s\r\n"), GitRevision::GetFullVersion());
+ _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute);
+ PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
+
+ PrintSystemInfo();
+ // First print information about the type of fault
+ _tprintf(_T("\r\n//=====================================================\r\n"));
+ _tprintf(_T("Exception code: %08X %s\r\n"),
+ pExceptionRecord->ExceptionCode,
+ GetExceptionString(pExceptionRecord->ExceptionCode));
+
+ // Now print information about where the fault occured
+ TCHAR szFaultingModule[MAX_PATH];
+ DWORD section;
+ DWORD_PTR offset;
+ GetLogicalAddress(pExceptionRecord->ExceptionAddress,
+ szFaultingModule,
+ sizeof(szFaultingModule),
+ section, offset);
+
+#ifdef _M_IX86
+ _tprintf(_T("Fault address: %08X %02X:%08X %s\r\n"),
+ pExceptionRecord->ExceptionAddress,
+ section, offset, szFaultingModule);
+#endif
+#ifdef _M_X64
+ _tprintf(_T("Fault address: %016I64X %02X:%016I64X %s\r\n"),
+ pExceptionRecord->ExceptionAddress,
+ section, offset, szFaultingModule);
+#endif
+
+ PCONTEXT pCtx = pExceptionInfo->ContextRecord;
+
+ // Show the registers
+#ifdef _M_IX86 // X86 Only!
+ _tprintf(_T("\r\nRegisters:\r\n"));
+
+ _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n")
+ , pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
+ pCtx->Esi, pCtx->Edi);
+
+ _tprintf(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip);
+ _tprintf(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"),
+ pCtx->SegSs, pCtx->Esp, pCtx->Ebp);
+ _tprintf(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
+ pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
+ _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags);
+#endif
+
+#ifdef _M_X64
+ _tprintf(_T("\r\nRegisters:\r\n"));
+ _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n")
+ _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n")
+ , pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
+ pCtx->Rsi, pCtx->Rdi, pCtx->R9, pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13, pCtx->R14, pCtx->R15);
+ _tprintf(_T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip);
+ _tprintf(_T("SS:RSP:%04X:%016X RBP:%08X\r\n"),
+ pCtx->SegSs, pCtx->Rsp, pCtx->Rbp);
+ _tprintf(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
+ pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
+ _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags);
+#endif
+
+ SymSetOptions(SYMOPT_DEFERRED_LOADS);
+
+ // Initialize DbgHelp
+ if (!SymInitialize(GetCurrentProcess(), 0, TRUE))
+ {
+ _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"),
+ ErrorMessage(GetLastError()));
+ }
+
+ CONTEXT trashableContext = *pCtx;
+
+ WriteStackDetails(&trashableContext, false, NULL);
+ printTracesForAllThreads(false);
+
+ // #ifdef _M_IX86 // X86 Only!
+
+ _tprintf(_T("========================\r\n"));
+ _tprintf(_T("Local Variables And Parameters\r\n"));
+
+ trashableContext = *pCtx;
+ WriteStackDetails(&trashableContext, true, NULL);
+ printTracesForAllThreads(true);
+
+ /*_tprintf(_T("========================\r\n"));
+ _tprintf(_T("Global Variables\r\n"));
+
+ SymEnumSymbols(GetCurrentProcess(),
+ (UINT_PTR)GetModuleHandle(szFaultingModule),
+ 0, EnumerateSymbolsCallback, 0);*/
+ // #endif // X86 Only!
+
+ SymCleanup(GetCurrentProcess());
+
+ _tprintf(_T("\r\n"));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ _tprintf(_T("Error writing the crash log\r\n"));
+ }
+}
+
+//======================================================================
+// Given an exception code, returns a pointer to a static string with a
+// description of the exception
+//======================================================================
+LPTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode)
+{
+ #define EXCEPTION(x) case EXCEPTION_##x: return _T(#x);
+
+ switch (dwCode)
+ {
+ EXCEPTION(ACCESS_VIOLATION)
+ EXCEPTION(DATATYPE_MISALIGNMENT)
+ EXCEPTION(BREAKPOINT)
+ EXCEPTION(SINGLE_STEP)
+ EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
+ EXCEPTION(FLT_DENORMAL_OPERAND)
+ EXCEPTION(FLT_DIVIDE_BY_ZERO)
+ EXCEPTION(FLT_INEXACT_RESULT)
+ EXCEPTION(FLT_INVALID_OPERATION)
+ EXCEPTION(FLT_OVERFLOW)
+ EXCEPTION(FLT_STACK_CHECK)
+ EXCEPTION(FLT_UNDERFLOW)
+ EXCEPTION(INT_DIVIDE_BY_ZERO)
+ EXCEPTION(INT_OVERFLOW)
+ EXCEPTION(PRIV_INSTRUCTION)
+ EXCEPTION(IN_PAGE_ERROR)
+ EXCEPTION(ILLEGAL_INSTRUCTION)
+ EXCEPTION(NONCONTINUABLE_EXCEPTION)
+ EXCEPTION(STACK_OVERFLOW)
+ EXCEPTION(INVALID_DISPOSITION)
+ EXCEPTION(GUARD_PAGE)
+ EXCEPTION(INVALID_HANDLE)
+ }
+
+ // If not one of the "known" exceptions, try to get the string
+ // from NTDLL.DLL's message table.
+
+ static TCHAR szBuffer[512] = { 0 };
+
+ FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
+ GetModuleHandle(_T("NTDLL.DLL")),
+ dwCode, 0, szBuffer, sizeof(szBuffer), 0);
+
+ return szBuffer;
+}
+
+//=============================================================================
+// Given a linear address, locates the module, section, and offset containing
+// that address.
+//
+// Note: the szModule paramater buffer is an output buffer of length specified
+// by the len parameter (in characters!)
+//=============================================================================
+BOOL WheatyExceptionReport::GetLogicalAddress(
+PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset)
+{
+ MEMORY_BASIC_INFORMATION mbi;
+
+ if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase;
+
+ if (!hMod)
+ return FALSE;
+
+ if (!GetModuleFileName((HMODULE)hMod, szModule, len))
+ return FALSE;
+
+ // Point to the DOS header in memory
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
+
+ // From the DOS header, find the NT (PE) header
+ PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew));
+
+ PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
+
+ DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address
+
+ // Iterate through the section table, looking for the one that encompasses
+ // the linear address.
+ for (unsigned i = 0;
+ i < pNtHdr->FileHeader.NumberOfSections;
+ i++, pSection++)
+ {
+ DWORD_PTR sectionStart = pSection->VirtualAddress;
+ DWORD_PTR sectionEnd = sectionStart
+ + DWORD_PTR(std::max(pSection->SizeOfRawData, pSection->Misc.VirtualSize));
+
+ // Is the address in this section???
+ if ((rva >= sectionStart) && (rva <= sectionEnd))
+ {
+ // Yes, address is in the section. Calculate section and offset,
+ // and store in the "section" & "offset" params, which were
+ // passed by reference.
+ section = i+1;
+ offset = rva - sectionStart;
+ return TRUE;
+ }
+ }
+
+ return FALSE; // Should never get here!
+}
+
+// It contains SYMBOL_INFO structure plus additional
+// space for the name of the symbol
+struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE
+{
+ CSymbolInfoPackage()
+ {
+ si.SizeOfStruct = sizeof(SYMBOL_INFO);
+ si.MaxNameLen = sizeof(name);
+ }
+};
+
+//============================================================
+// Walks the stack, and writes the results to the report file
+//============================================================
+void WheatyExceptionReport::WriteStackDetails(
+PCONTEXT pContext,
+bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output
+{
+ _tprintf(_T("\r\nCall stack:\r\n"));
+
+ _tprintf(_T("Address Frame Function SourceFile\r\n"));
+
+ DWORD dwMachineType = 0;
+ // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
+
+ STACKFRAME64 sf;
+ memset(&sf, 0, sizeof(sf));
+
+ #ifdef _M_IX86
+ // Initialize the STACKFRAME structure for the first call. This is only
+ // necessary for Intel CPUs, and isn't mentioned in the documentation.
+ sf.AddrPC.Offset = pContext->Eip;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = pContext->Esp;
+ sf.AddrStack.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = pContext->Ebp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+
+ dwMachineType = IMAGE_FILE_MACHINE_I386;
+ #endif
+
+#ifdef _M_X64
+ sf.AddrPC.Offset = pContext->Rip;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = pContext->Rsp;
+ sf.AddrStack.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = pContext->Rbp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+ dwMachineType = IMAGE_FILE_MACHINE_AMD64;
+#endif
+
+ for (;;)
+ {
+ // Get the next stack frame
+ if (! StackWalk64(dwMachineType,
+ m_hProcess,
+ pThreadHandle != NULL ? pThreadHandle : GetCurrentThread(),
+ &sf,
+ pContext,
+ 0,
+ SymFunctionTableAccess64,
+ SymGetModuleBase64,
+ 0))
+ break;
+ if (0 == sf.AddrFrame.Offset) // Basic sanity check to make sure
+ break; // the frame is OK. Bail if not.
+#ifdef _M_IX86
+ _tprintf(_T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
+#endif
+#ifdef _M_X64
+ _tprintf(_T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
+#endif
+
+ DWORD64 symDisplacement = 0; // Displacement of the input address,
+ // relative to the start of the symbol
+
+ // Get the name of the function for this stack frame entry
+ CSymbolInfoPackage sip;
+ if (SymFromAddr(
+ m_hProcess, // Process handle of the current process
+ sf.AddrPC.Offset, // Symbol address
+ &symDisplacement, // Address of the variable that will receive the displacement
+ &sip.si)) // Address of the SYMBOL_INFO structure (inside "sip" object)
+ {
+ _tprintf(_T("%hs+%I64X"), sip.si.Name, symDisplacement);
+
+ }
+ else // No symbol found. Print out the logical address instead.
+ {
+ TCHAR szModule[MAX_PATH] = _T("");
+ DWORD section = 0;
+ DWORD_PTR offset = 0;
+
+ GetLogicalAddress((PVOID)sf.AddrPC.Offset,
+ szModule, sizeof(szModule), section, offset);
+#ifdef _M_IX86
+ _tprintf(_T("%04X:%08X %s"), section, offset, szModule);
+#endif
+#ifdef _M_X64
+ _tprintf(_T("%04X:%016I64X %s"), section, offset, szModule);
+#endif
+ }
+
+ // Get the source line for this stack frame entry
+ IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };
+ DWORD dwLineDisplacement;
+ if (SymGetLineFromAddr64(m_hProcess, sf.AddrPC.Offset,
+ &dwLineDisplacement, &lineInfo))
+ {
+ _tprintf(_T(" %s line %u"), lineInfo.FileName, lineInfo.LineNumber);
+ }
+
+ _tprintf(_T("\r\n"));
+
+ // Write out the variables, if desired
+ if (bWriteVariables)
+ {
+ // Use SymSetContext to get just the locals/params for this frame
+ IMAGEHLP_STACK_FRAME imagehlpStackFrame;
+ imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
+ SymSetContext(m_hProcess, &imagehlpStackFrame, 0);
+
+ // Enumerate the locals/parameters
+ SymEnumSymbols(m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf);
+
+ _tprintf(_T("\r\n"));
+ }
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// The function invoked by SymEnumSymbols
+//////////////////////////////////////////////////////////////////////////////
+
+BOOL CALLBACK
+WheatyExceptionReport::EnumerateSymbolsCallback(
+PSYMBOL_INFO pSymInfo,
+ULONG /*SymbolSize*/,
+PVOID UserContext)
+{
+
+ char szBuffer[WER_LARGE_BUFFER_SIZE];
+ memset(szBuffer, 0, sizeof(szBuffer));
+
+ __try
+ {
+ ClearSymbols();
+ if (FormatSymbolValue(pSymInfo, (STACKFRAME64*)UserContext,
+ szBuffer, sizeof(szBuffer)))
+ _tprintf(_T("%s"), szBuffer);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ _tprintf(_T("punting on symbol %s, partial output:\r\n"), pSymInfo->Name);
+ if (szBuffer[0] != '\0')
+ _tprintf(_T("%s"), szBuffer);
+ }
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Given a SYMBOL_INFO representing a particular variable, displays its
+// contents. If it's a user defined type, display the members and their
+// values.
+//////////////////////////////////////////////////////////////////////////////
+bool WheatyExceptionReport::FormatSymbolValue(
+PSYMBOL_INFO pSym,
+STACKFRAME64 * sf,
+char * pszBuffer,
+unsigned /*cbBuffer*/)
+{
+ char * pszCurrBuffer = pszBuffer;
+
+ // If it's a function, don't do anything.
+ if (pSym->Tag == SymTagFunction) // SymTagFunction from CVCONST.H from the DIA SDK
+ return false;
+
+ DWORD_PTR pVariable = 0; // Will point to the variable's data in memory
+
+ if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE)
+ {
+ // if (pSym->Register == 8) // EBP is the value 8 (in DBGHELP 5.1)
+ { // This may change!!!
+#ifdef _M_IX86
+ pVariable = sf->AddrFrame.Offset;
+#elif _M_X64
+ pVariable = sf->AddrStack.Offset;
+#endif
+ pVariable += (DWORD_PTR)pSym->Address;
+ }
+ // else
+ // return false;
+ }
+ else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
+ return false; // Don't try to report register variable
+ else
+ {
+ pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable
+ }
+
+ pszCurrBuffer = PushSymbolDetail(pszCurrBuffer);
+
+ // Indicate if the variable is a local or parameter
+ if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
+ symbolDetails.top().Prefix = "Parameter ";
+ else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
+ symbolDetails.top().Prefix = "Local ";
+
+ // Determine if the variable is a user defined type (UDT). IF so, bHandled
+ // will return true.
+ bool bHandled;
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, pSym->ModBase, pSym->TypeIndex,
+ 0, pVariable, bHandled, pSym->Name, "", false, true);
+
+ if (!bHandled)
+ {
+ // The symbol wasn't a UDT, so do basic, stupid formatting of the
+ // variable. Based on the size, we're assuming it's a char, WORD, or
+ // DWORD.
+ BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
+ if (symbolDetails.top().Type.empty())
+ symbolDetails.top().Type = rgBaseType[basicType];
+
+ // Emit the variable name
+ if (pSym->Name[0] != '\0')
+ symbolDetails.top().Name = pSym->Name;
+
+ char buffer[50];
+ FormatOutputValue(buffer, basicType, pSym->Size, (PVOID)pVariable, sizeof(buffer));
+ symbolDetails.top().Value = buffer;
+ }
+
+ pszCurrBuffer = PopSymbolDetail(pszCurrBuffer);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// If it's a user defined type (UDT), recurse through its members until we're
+// at fundamental types. When he hit fundamental types, return
+// bHandled = false, so that FormatSymbolValue() will format them.
+//////////////////////////////////////////////////////////////////////////////
+char * WheatyExceptionReport::DumpTypeIndex(
+char * pszCurrBuffer,
+DWORD64 modBase,
+DWORD dwTypeIndex,
+unsigned nestingLevel,
+DWORD_PTR offset,
+bool & bHandled,
+const char* Name,
+char* /*suffix*/,
+bool newSymbol,
+bool logChildren)
+{
+ bHandled = false;
+
+ if (newSymbol)
+ pszCurrBuffer = PushSymbolDetail(pszCurrBuffer);
+
+ DWORD typeTag;
+ if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &typeTag))
+ return pszCurrBuffer;
+
+ // Get the name of the symbol. This will either be a Type name (if a UDT),
+ // or the structure member name.
+ WCHAR * pwszTypeName;
+ if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME,
+ &pwszTypeName))
+ {
+ // handle special cases
+ if (wcscmp(pwszTypeName, L"std::basic_string<char,std::char_traits<char>,std::allocator<char> >") == 0)
+ {
+ LocalFree(pwszTypeName);
+ symbolDetails.top().Type = "std::string";
+ char buffer[50];
+ FormatOutputValue(buffer, btStdString, 0, (PVOID)offset, sizeof(buffer));
+ symbolDetails.top().Value = buffer;
+ if (Name != NULL && Name[0] != '\0')
+ symbolDetails.top().Name = Name;
+ bHandled = true;
+ return pszCurrBuffer;
+ }
+
+ char buffer[200];
+ wcstombs(buffer, pwszTypeName, sizeof(buffer));
+ buffer[199] = '\0';
+ if (Name != NULL && Name[0] != '\0')
+ {
+ symbolDetails.top().Type = buffer;
+ symbolDetails.top().Name = Name;
+ }
+ else if (buffer[0] != '\0')
+ symbolDetails.top().Name = buffer;
+
+ LocalFree(pwszTypeName);
+ }
+ else if (Name != NULL && Name[0] != '\0')
+ symbolDetails.top().Name = Name;
+
+ if (!StoreSymbol(dwTypeIndex, offset))
+ {
+ // Skip printing address and base class if it has been printed already
+ if (typeTag == SymTagBaseClass)
+ bHandled = true;
+ return pszCurrBuffer;
+ }
+
+ DWORD innerTypeID;
+ switch (typeTag)
+ {
+ case SymTagPointerType:
+ if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
+ {
+ if (Name != NULL && Name[0] != '\0')
+ symbolDetails.top().Name = Name;
+
+ BOOL isReference;
+ SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_IS_REFERENCE, &isReference);
+
+ char addressStr[40];
+ memset(addressStr, 0, sizeof(addressStr));
+
+ if (isReference)
+ symbolDetails.top().Suffix += "&";
+ else
+ symbolDetails.top().Suffix += "*";
+
+ // Try to dereference the pointer in a try/except block since it might be invalid
+ DWORD_PTR address = DereferenceUnsafePointer(offset);
+
+ char buffer[50];
+ FormatOutputValue(buffer, btVoid, sizeof(PVOID), (PVOID)offset, sizeof(buffer));
+ symbolDetails.top().Value = buffer;
+
+ if (nestingLevel >= WER_MAX_NESTING_LEVEL)
+ logChildren = false;
+
+ // no need to log any children since the address is invalid anyway
+ if (address == NULL || address == DWORD_PTR(-1))
+ logChildren = false;
+
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ address, bHandled, Name, addressStr, false, logChildren);
+
+ if (!bHandled)
+ {
+ BasicType basicType = GetBasicType(dwTypeIndex, modBase);
+ if (symbolDetails.top().Type.empty())
+ symbolDetails.top().Type = rgBaseType[basicType];
+
+ if (address == NULL)
+ symbolDetails.top().Value = "NULL";
+ else if (address == DWORD_PTR(-1))
+ symbolDetails.top().Value = "<Unable to read memory>";
+ else
+ {
+ // Get the size of the child member
+ ULONG64 length;
+ SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
+ char buffer2[50];
+ FormatOutputValue(buffer2, basicType, length, (PVOID)address, sizeof(buffer));
+ symbolDetails.top().Value = buffer2;
+ }
+ bHandled = true;
+ return pszCurrBuffer;
+ }
+ else if (address == NULL)
+ symbolDetails.top().Value = "NULL";
+ else if (address == DWORD_PTR(-1))
+ {
+ symbolDetails.top().Value = "<Unable to read memory>";
+ bHandled = true;
+ return pszCurrBuffer;
+ }
+ }
+ break;
+ case SymTagData:
+ if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
+ {
+ DWORD innerTypeTag;
+ if (!SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_SYMTAG, &innerTypeTag))
+ break;
+
+ switch (innerTypeTag)
+ {
+ case SymTagUDT:
+ if (nestingLevel >= WER_MAX_NESTING_LEVEL)
+ logChildren = false;
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
+ break;
+ case SymTagPointerType:
+ if (Name != NULL && Name[0] != '\0')
+ symbolDetails.top().Name = Name;
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
+ break;
+ case SymTagArrayType:
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case SymTagArrayType:
+ if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
+ {
+ symbolDetails.top().HasChildren = true;
+
+ BasicType basicType = btNoType;
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ offset, bHandled, Name, "", false, false);
+
+ // Set Value back to an empty string since the Array object itself has no value, only its elements have
+ symbolDetails.top().Value.clear();
+
+ DWORD elementsCount;
+ if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_COUNT, &elementsCount))
+ symbolDetails.top().Suffix += "[" + std::to_string(elementsCount) + "]";
+ else
+ symbolDetails.top().Suffix += "[<unknown count>]";
+
+ if (!bHandled)
+ {
+ basicType = GetBasicType(dwTypeIndex, modBase);
+ if (symbolDetails.top().Type.empty())
+ symbolDetails.top().Type = rgBaseType[basicType];
+ bHandled = true;
+ }
+
+ // Get the size of the child member
+ ULONG64 length;
+ SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
+
+ char buffer[50];
+ switch (basicType)
+ {
+ case btChar:
+ case btStdString:
+ FormatOutputValue(buffer, basicType, length, (PVOID)offset, sizeof(buffer), elementsCount);
+ symbolDetails.top().Value = buffer;
+ break;
+ default:
+ for (DWORD index = 0; index < elementsCount && index < WER_MAX_ARRAY_ELEMENTS_COUNT; index++)
+ {
+ pszCurrBuffer = PushSymbolDetail(pszCurrBuffer);
+ symbolDetails.top().Suffix += "[" + std::to_string(index) + "]";
+ FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer));
+ symbolDetails.top().Value = buffer;
+ pszCurrBuffer = PopSymbolDetail(pszCurrBuffer);
+ }
+ break;
+ }
+
+ return pszCurrBuffer;
+ }
+ break;
+ case SymTagBaseType:
+ break;
+ case SymTagEnum:
+ return pszCurrBuffer;
+ default:
+ break;
+ }
+
+ // Determine how many children this type has.
+ DWORD dwChildrenCount = 0;
+ SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount);
+
+ if (!dwChildrenCount) // If no children, we're done
+ return pszCurrBuffer;
+
+ // Prepare to get an array of "TypeIds", representing each of the children.
+ // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
+ // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
+ struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS
+ {
+ ULONG MoreChildIds[1024*2];
+ FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);}
+ } children;
+
+ children.Count = dwChildrenCount;
+ children.Start= 0;
+
+ // Get the array of TypeIds, one for each child type
+ if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
+ &children))
+ {
+ return pszCurrBuffer;
+ }
+
+ // Iterate through each of the children
+ for (unsigned i = 0; i < dwChildrenCount; i++)
+ {
+ DWORD symTag;
+ SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_SYMTAG, &symTag);
+
+ if (symTag == SymTagFunction ||
+ symTag == SymTagEnum ||
+ symTag == SymTagTypedef ||
+ symTag == SymTagVTable)
+ continue;
+
+ // Ignore static fields
+ DWORD dataKind;
+ SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_DATAKIND, &dataKind);
+ if (dataKind == DataIsStaticLocal ||
+ dataKind == DataIsGlobal ||
+ dataKind == DataIsStaticMember)
+ continue;
+
+
+ symbolDetails.top().HasChildren = true;
+ if (!logChildren)
+ {
+ bHandled = false;
+ return pszCurrBuffer;
+ }
+
+ // Recurse for each of the child types
+ bool bHandled2;
+ BasicType basicType = GetBasicType(children.ChildId[i], modBase);
+
+ // Get the offset of the child member, relative to its parent
+ DWORD dwMemberOffset;
+ SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
+ TI_GET_OFFSET, &dwMemberOffset);
+
+ // Calculate the address of the member
+ DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
+
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase,
+ children.ChildId[i], nestingLevel+1,
+ dwFinalOffset, bHandled2, ""/*Name */, "", true, true);
+
+ // If the child wasn't a UDT, format it appropriately
+ if (!bHandled2)
+ {
+ if (symbolDetails.top().Type.empty())
+ symbolDetails.top().Type = rgBaseType[basicType];
+
+ // Get the real "TypeId" of the child. We need this for the
+ // SymGetTypeInfo(TI_GET_TYPEID) call below.
+ DWORD typeId;
+ SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
+ TI_GET_TYPEID, &typeId);
+
+ // Get the size of the child member
+ ULONG64 length;
+ SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH, &length);
+
+ char buffer[50];
+ FormatOutputValue(buffer, basicType, length, (PVOID)dwFinalOffset, sizeof(buffer));
+ symbolDetails.top().Value = buffer;
+ }
+
+ pszCurrBuffer = PopSymbolDetail(pszCurrBuffer);
+ }
+
+ bHandled = true;
+ return pszCurrBuffer;
+}
+
+void WheatyExceptionReport::FormatOutputValue(char * pszCurrBuffer,
+BasicType basicType,
+DWORD64 length,
+PVOID pAddress,
+size_t bufferSize,
+size_t countOverride)
+{
+ __try
+ {
+ switch (basicType)
+ {
+ case btChar:
+ {
+ // Special case handling for char[] type
+ if (countOverride != 0)
+ length = countOverride;
+ else
+ length = strlen((char*)pAddress);
+ if (length > bufferSize - 6)
+ pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", (DWORD)(bufferSize - 6), (char*)pAddress);
+ else
+ pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s\"", (DWORD)length, (char*)pAddress);
+ break;
+ }
+ case btStdString:
+ {
+ std::string* value = static_cast<std::string*>(pAddress);
+ if (value->length() > bufferSize - 6)
+ pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", (DWORD)(bufferSize - 6), value->c_str());
+ else
+ pszCurrBuffer += sprintf(pszCurrBuffer, "\"%s\"", value->c_str());
+ break;
+ }
+ default:
+ // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
+ if (length == 1)
+ pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PBYTE)pAddress);
+ else if (length == 2)
+ pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PWORD)pAddress);
+ else if (length == 4)
+ {
+ if (basicType == btFloat)
+ pszCurrBuffer += sprintf(pszCurrBuffer, "%f", *(PFLOAT)pAddress);
+ else
+ pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PDWORD)pAddress);
+ }
+ else if (length == 8)
+ {
+ if (basicType == btFloat)
+ {
+ pszCurrBuffer += sprintf(pszCurrBuffer, "%f",
+ *(double *)pAddress);
+ }
+ else
+ pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X",
+ *(DWORD64*)pAddress);
+ }
+ else
+ {
+ #if _WIN64
+ pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X", (DWORD64)pAddress);
+ #else
+ pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", (DWORD)pAddress);
+ #endif
+ }
+ break;
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+#if _WIN64
+ pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X <Unable to read memory>", (DWORD64)pAddress);
+#else
+ pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X <Unable to read memory>", (DWORD)pAddress);
+#endif
+ }
+}
+
+BasicType
+WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase)
+{
+ BasicType basicType;
+ if (SymGetTypeInfo(m_hProcess, modBase, typeIndex,
+ TI_GET_BASETYPE, &basicType))
+ {
+ return basicType;
+ }
+
+ // Get the real "TypeId" of the child. We need this for the
+ // SymGetTypeInfo(TI_GET_TYPEID) call below.
+ DWORD typeId;
+ if (SymGetTypeInfo(m_hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId))
+ {
+ if (SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_BASETYPE,
+ &basicType))
+ {
+ return basicType;
+ }
+ }
+
+ return btNoType;
+}
+
+DWORD_PTR WheatyExceptionReport::DereferenceUnsafePointer(DWORD_PTR address)
+{
+ __try
+ {
+ return *(PDWORD_PTR)address;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return DWORD_PTR(-1);
+ }
+}
+
+//============================================================================
+// Helper function that writes to the report file, and allows the user to use
+// printf style formating
+//============================================================================
+int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...)
+{
+ int retValue;
+ va_list argptr;
+ va_start(argptr, format);
+ if (stackOverflowException)
+ {
+ retValue = heapprintf(format, argptr);
+ va_end(argptr);
+ }
+ else
+ {
+ retValue = stackprintf(format, argptr);
+ va_end(argptr);
+ }
+
+ return retValue;
+}
+
+int __cdecl WheatyExceptionReport::stackprintf(const TCHAR * format, va_list argptr)
+{
+ int retValue;
+ DWORD cbWritten;
+
+ TCHAR szBuff[WER_LARGE_BUFFER_SIZE];
+ retValue = vsprintf(szBuff, format, argptr);
+ WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
+
+ return retValue;
+}
+
+int __cdecl WheatyExceptionReport::heapprintf(const TCHAR * format, va_list argptr)
+{
+ int retValue;
+ DWORD cbWritten;
+ TCHAR* szBuff = (TCHAR*)malloc(sizeof(TCHAR) * WER_LARGE_BUFFER_SIZE);
+ retValue = vsprintf(szBuff, format, argptr);
+ WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
+ free(szBuff);
+
+ return retValue;
+}
+
+bool WheatyExceptionReport::StoreSymbol(DWORD type, DWORD_PTR offset)
+{
+ return symbols.insert(SymbolPair(type, offset)).second;
+}
+
+void WheatyExceptionReport::ClearSymbols()
+{
+ symbols.clear();
+ while (!symbolDetails.empty())
+ symbolDetails.pop();
+}
+
+char* WheatyExceptionReport::PushSymbolDetail(char* pszCurrBuffer)
+{
+ // Log current symbol and then add another to the stack to keep the hierarchy format
+ pszCurrBuffer = PrintSymbolDetail(pszCurrBuffer);
+ symbolDetails.emplace();
+ return pszCurrBuffer;
+}
+
+char* WheatyExceptionReport::PopSymbolDetail(char* pszCurrBuffer)
+{
+ pszCurrBuffer = PrintSymbolDetail(pszCurrBuffer);
+ symbolDetails.pop();
+ return pszCurrBuffer;
+}
+
+char* WheatyExceptionReport::PrintSymbolDetail(char* pszCurrBuffer)
+{
+ if (symbolDetails.empty())
+ return pszCurrBuffer;
+
+ // Don't log anything if has been logged already or if it's empty
+ if (symbolDetails.top().Logged || symbolDetails.top().empty())
+ return pszCurrBuffer;
+
+ // Add appropriate indentation level (since this routine is recursive)
+ for (size_t i = 0; i < symbolDetails.size(); i++)
+ pszCurrBuffer += sprintf(pszCurrBuffer, "\t");
+
+ pszCurrBuffer += sprintf(pszCurrBuffer, "%s\r\n", symbolDetails.top().ToString().c_str());
+
+ return pszCurrBuffer;
+}
+
+#endif // _WIN32
diff --git a/src/common/Debugging/WheatyExceptionReport.h b/src/common/Debugging/WheatyExceptionReport.h
new file mode 100644
index 00000000000..eb62d8bceef
--- /dev/null
+++ b/src/common/Debugging/WheatyExceptionReport.h
@@ -0,0 +1,214 @@
+#ifndef _WHEATYEXCEPTIONREPORT_
+#define _WHEATYEXCEPTIONREPORT_
+
+#if PLATFORM == PLATFORM_WINDOWS && !defined(__MINGW32__)
+
+#include <winnt.h>
+#include <winternl.h>
+#include <dbghelp.h>
+#include <set>
+#include <stdlib.h>
+#include <stack>
+#include <mutex>
+#define countof _countof
+
+#define WER_MAX_ARRAY_ELEMENTS_COUNT 10
+#define WER_MAX_NESTING_LEVEL 5
+#define WER_LARGE_BUFFER_SIZE 1024 * 128
+
+enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK
+{
+ btNoType = 0,
+ btVoid = 1,
+ btChar = 2,
+ btWChar = 3,
+ btInt = 6,
+ btUInt = 7,
+ btFloat = 8,
+ btBCD = 9,
+ btBool = 10,
+ btLong = 13,
+ btULong = 14,
+ btCurrency = 25,
+ btDate = 26,
+ btVariant = 27,
+ btComplex = 28,
+ btBit = 29,
+ btBSTR = 30,
+ btHresult = 31,
+
+ // Custom types
+ btStdString = 101
+};
+
+enum DataKind // Stolen from CVCONST.H in the DIA 2.0 SDK
+{
+ DataIsUnknown,
+ DataIsLocal,
+ DataIsStaticLocal,
+ DataIsParam,
+ DataIsObjectPtr,
+ DataIsFileStatic,
+ DataIsGlobal,
+ DataIsMember,
+ DataIsStaticMember,
+ DataIsConstant
+};
+
+const char* const rgBaseType[] =
+{
+ "<user defined>", // btNoType = 0,
+ "void", // btVoid = 1,
+ "char",//char* // btChar = 2,
+ "wchar_t*", // btWChar = 3,
+ "signed char",
+ "unsigned char",
+ "int", // btInt = 6,
+ "unsigned int", // btUInt = 7,
+ "float", // btFloat = 8,
+ "<BCD>", // btBCD = 9,
+ "bool", // btBool = 10,
+ "short",
+ "unsigned short",
+ "long", // btLong = 13,
+ "unsigned long", // btULong = 14,
+ "int8",
+ "int16",
+ "int32",
+ "int64",
+ "int128",
+ "uint8",
+ "uint16",
+ "uint32",
+ "uint64",
+ "uint128",
+ "<currency>", // btCurrency = 25,
+ "<date>", // btDate = 26,
+ "VARIANT", // btVariant = 27,
+ "<complex>", // btComplex = 28,
+ "<bit>", // btBit = 29,
+ "BSTR", // btBSTR = 30,
+ "HRESULT" // btHresult = 31
+};
+
+struct SymbolPair
+{
+ SymbolPair(DWORD type, DWORD_PTR offset)
+ {
+ _type = type;
+ _offset = offset;
+ }
+
+ bool operator<(const SymbolPair& other) const
+ {
+ return _offset < other._offset ||
+ (_offset == other._offset && _type < other._type);
+ }
+
+ DWORD _type;
+ DWORD_PTR _offset;
+};
+typedef std::set<SymbolPair> SymbolPairs;
+
+struct SymbolDetail
+{
+ SymbolDetail() : Prefix(), Type(), Suffix(), Name(), Value(), Logged(false), HasChildren(false) {}
+
+ std::string ToString()
+ {
+ Logged = true;
+ std::string formatted = Prefix + Type + Suffix;
+ if (!Name.empty())
+ {
+ if (!formatted.empty())
+ formatted += " ";
+ formatted += Name;
+ }
+ if (!Value.empty())
+ formatted += " = " + Value;
+ return formatted;
+ }
+
+ bool empty() const
+ {
+ return Value.empty() && !HasChildren;
+ }
+
+ std::string Prefix;
+ std::string Type;
+ std::string Suffix;
+ std::string Name;
+ std::string Value;
+ bool Logged;
+ bool HasChildren;
+};
+
+class WheatyExceptionReport
+{
+ public:
+
+ WheatyExceptionReport();
+ ~WheatyExceptionReport();
+
+ // entry point where control comes on an unhandled exception
+ static LONG WINAPI WheatyUnhandledExceptionFilter(
+ PEXCEPTION_POINTERS pExceptionInfo);
+
+ static void printTracesForAllThreads(bool);
+ private:
+ // where report info is extracted and generated
+ static void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo);
+ static void PrintSystemInfo();
+ static BOOL _GetWindowsVersion(TCHAR* szVersion, DWORD cntMax);
+ static BOOL _GetProcessorName(TCHAR* sProcessorName, DWORD maxcount);
+
+ // Helper functions
+ static LPTSTR GetExceptionString(DWORD dwCode);
+ static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len,
+ DWORD& section, DWORD_PTR& offset);
+
+ static void WriteStackDetails(PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle);
+
+ static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO, ULONG, PVOID);
+
+ static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME64 *, char * pszBuffer, unsigned cbBuffer);
+
+ static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool &, const char*, char*, bool, bool);
+
+ static void FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride = 0);
+
+ static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase);
+ static DWORD_PTR DereferenceUnsafePointer(DWORD_PTR address);
+
+ static int __cdecl _tprintf(const TCHAR * format, ...);
+ static int __cdecl stackprintf(const TCHAR * format, va_list argptr);
+ static int __cdecl heapprintf(const TCHAR * format, va_list argptr);
+
+ static bool StoreSymbol(DWORD type , DWORD_PTR offset);
+ static void ClearSymbols();
+
+ // Variables used by the class
+ static TCHAR m_szLogFileName[MAX_PATH];
+ static TCHAR m_szDumpFileName[MAX_PATH];
+ static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;
+ static HANDLE m_hReportFile;
+ static HANDLE m_hDumpFile;
+ static HANDLE m_hProcess;
+ static SymbolPairs symbols;
+ static std::stack<SymbolDetail> symbolDetails;
+ static bool stackOverflowException;
+ static bool alreadyCrashed;
+ static std::mutex alreadyCrashedLock;
+ typedef NTSTATUS(NTAPI* pRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation);
+ static pRtlGetVersion RtlGetVersion;
+
+ static char* PushSymbolDetail(char* pszCurrBuffer);
+ static char* PopSymbolDetail(char* pszCurrBuffer);
+ static char* PrintSymbolDetail(char* pszCurrBuffer);
+
+};
+
+extern WheatyExceptionReport g_WheatyExceptionReport; // global instance of class
+#endif // _WIN32
+#endif // _WHEATYEXCEPTIONREPORT_
+
diff --git a/src/common/Define.h b/src/common/Define.h
new file mode 100644
index 00000000000..7332bdc4cff
--- /dev/null
+++ b/src/common/Define.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 TRINITY_DEFINE_H
+#define TRINITY_DEFINE_H
+
+#include "CompilerDefs.h"
+
+#if COMPILER == COMPILER_GNU
+# if !defined(__STDC_FORMAT_MACROS)
+# define __STDC_FORMAT_MACROS
+# endif
+# if !defined(__STDC_CONSTANT_MACROS)
+# define __STDC_CONSTANT_MACROS
+# endif
+# if !defined(_GLIBCXX_USE_NANOSLEEP)
+# define _GLIBCXX_USE_NANOSLEEP
+# endif
+# if defined(HELGRIND)
+# include <valgrind/helgrind.h>
+# undef _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE
+# undef _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER
+# define _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(A) ANNOTATE_HAPPENS_BEFORE(A)
+# define _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(A) ANNOTATE_HAPPENS_AFTER(A)
+# endif
+#endif
+
+#include <cstddef>
+#include <cinttypes>
+#include <climits>
+
+#define TRINITY_LITTLEENDIAN 0
+#define TRINITY_BIGENDIAN 1
+
+#if !defined(TRINITY_ENDIAN)
+# if defined (BOOST_BIG_ENDIAN)
+# define TRINITY_ENDIAN TRINITY_BIGENDIAN
+# else
+# define TRINITY_ENDIAN TRINITY_LITTLEENDIAN
+# endif
+#endif
+
+#if PLATFORM == PLATFORM_WINDOWS
+# define TRINITY_PATH_MAX MAX_PATH
+# define _USE_MATH_DEFINES
+# ifndef DECLSPEC_NORETURN
+# define DECLSPEC_NORETURN __declspec(noreturn)
+# endif //DECLSPEC_NORETURN
+# ifndef DECLSPEC_DEPRECATED
+# define DECLSPEC_DEPRECATED __declspec(deprecated)
+# endif //DECLSPEC_DEPRECATED
+#else //PLATFORM != PLATFORM_WINDOWS
+# define TRINITY_PATH_MAX PATH_MAX
+# define DECLSPEC_NORETURN
+# define DECLSPEC_DEPRECATED
+#endif //PLATFORM
+
+#if !defined(COREDEBUG)
+# define TRINITY_INLINE inline
+#else //COREDEBUG
+# if !defined(TRINITY_DEBUG)
+# define TRINITY_DEBUG
+# endif //TRINITY_DEBUG
+# define TRINITY_INLINE
+#endif //!COREDEBUG
+
+#if COMPILER == COMPILER_GNU
+# define ATTR_NORETURN __attribute__((__noreturn__))
+# define ATTR_PRINTF(F, V) __attribute__ ((__format__ (__printf__, F, V)))
+# define ATTR_DEPRECATED __attribute__((__deprecated__))
+#else //COMPILER != COMPILER_GNU
+# define ATTR_NORETURN
+# define ATTR_PRINTF(F, V)
+# define ATTR_DEPRECATED
+#endif //COMPILER == COMPILER_GNU
+
+#define UI64FMTD "%" PRIu64
+#define UI64LIT(N) UINT64_C(N)
+
+#define SI64FMTD "%" PRId64
+#define SI64LIT(N) INT64_C(N)
+
+#define SZFMTD "%" PRIuPTR
+
+typedef int64_t int64;
+typedef int32_t int32;
+typedef int16_t int16;
+typedef int8_t int8;
+typedef uint64_t uint64;
+typedef uint32_t uint32;
+typedef uint16_t uint16;
+typedef uint8_t uint8;
+
+#endif //TRINITY_DEFINE_H
diff --git a/src/common/GitRevision.cpp b/src/common/GitRevision.cpp
new file mode 100644
index 00000000000..d0719c09959
--- /dev/null
+++ b/src/common/GitRevision.cpp
@@ -0,0 +1,78 @@
+#include "GitRevision.h"
+#include "CompilerDefs.h"
+#include "revision_data.h"
+
+char const* GitRevision::GetHash()
+{
+ return _HASH;
+}
+
+char const* GitRevision::GetDate()
+{
+ return _DATE;
+}
+
+char const* GitRevision::GetBranch()
+{
+ return _BRANCH;
+}
+
+char const* GitRevision::GetSourceDirectory()
+{
+ return _SOURCE_DIRECTORY;
+}
+
+char const* GitRevision::GetMySQLExecutable()
+{
+ return _MYSQL_EXECUTABLE;
+}
+
+char const* GitRevision::GetFullDatabase()
+{
+ return _FULL_DATABASE;
+}
+
+#define _PACKAGENAME "TrinityCore"
+
+char const* GitRevision::GetFullVersion()
+{
+#if PLATFORM == PLATFORM_WINDOWS
+# ifdef _WIN64
+ return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Win64, " _BUILD_DIRECTIVE ")";
+# else
+ return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Win32, " _BUILD_DIRECTIVE ")";
+# endif
+#else
+ return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Unix, " _BUILD_DIRECTIVE ")";
+#endif
+}
+
+char const* GitRevision::GetCompanyNameStr()
+{
+ return VER_COMPANYNAME_STR;
+}
+
+char const* GitRevision::GetLegalCopyrightStr()
+{
+ return VER_LEGALCOPYRIGHT_STR;
+}
+
+char const* GitRevision::GetFileVersionStr()
+{
+ return VER_FILEVERSION_STR;
+}
+
+char const* GitRevision::GetProductVersionStr()
+{
+ return VER_PRODUCTVERSION_STR;
+}
+
+char const* GitRevision::GetCompilerCFlags()
+{
+ return COMPILER_C_FLAGS;
+}
+
+char const* GitRevision::GetCompilerCXXFlags()
+{
+ return COMPILER_CXX_FLAGS;
+}
diff --git a/src/common/GitRevision.h b/src/common/GitRevision.h
new file mode 100644
index 00000000000..d9b292031f6
--- /dev/null
+++ b/src/common/GitRevision.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GITREVISION_H__
+#define __GITREVISION_H__
+
+#include <string>
+
+namespace GitRevision
+{
+ char const* GetHash();
+ char const* GetDate();
+ char const* GetBranch();
+ char const* GetSourceDirectory();
+ char const* GetMySQLExecutable();
+ char const* GetFullDatabase();
+ char const* GetFullVersion();
+ char const* GetCompanyNameStr();
+ char const* GetLegalCopyrightStr();
+ char const* GetFileVersionStr();
+ char const* GetProductVersionStr();
+ char const* GetCompilerCFlags();
+ char const* GetCompilerCXXFlags();
+}
+
+#endif
diff --git a/src/common/Logging/Appender.cpp b/src/common/Logging/Appender.cpp
new file mode 100644
index 00000000000..d19ef8cf96f
--- /dev/null
+++ b/src/common/Logging/Appender.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Appender.h"
+#include "Common.h"
+#include "Util.h"
+#include "StringFormat.h"
+
+#include <utility>
+#include <sstream>
+
+std::string LogMessage::getTimeStr(time_t time)
+{
+ tm aTm;
+ localtime_r(&time, &aTm);
+ char buf[20];
+ snprintf(buf, 20, "%04d-%02d-%02d_%02d:%02d:%02d", aTm.tm_year+1900, aTm.tm_mon+1, aTm.tm_mday, aTm.tm_hour, aTm.tm_min, aTm.tm_sec);
+ return std::string(buf);
+}
+
+std::string LogMessage::getTimeStr()
+{
+ return getTimeStr(mtime);
+}
+
+Appender::Appender(uint8 _id, std::string const& _name, LogLevel _level /* = LOG_LEVEL_DISABLED */, AppenderFlags _flags /* = APPENDER_FLAGS_NONE */):
+id(_id), name(_name), level(_level), flags(_flags) { }
+
+Appender::~Appender() { }
+
+uint8 Appender::getId() const
+{
+ return id;
+}
+
+std::string const& Appender::getName() const
+{
+ return name;
+}
+
+LogLevel Appender::getLogLevel() const
+{
+ return level;
+}
+
+AppenderFlags Appender::getFlags() const
+{
+ return flags;
+}
+
+void Appender::setLogLevel(LogLevel _level)
+{
+ level = _level;
+}
+
+void Appender::write(LogMessage* message)
+{
+ if (!level || level > message->level)
+ return;
+
+ std::ostringstream ss;
+
+ if (flags & APPENDER_FLAGS_PREFIX_TIMESTAMP)
+ ss << message->getTimeStr() << ' ';
+
+ if (flags & APPENDER_FLAGS_PREFIX_LOGLEVEL)
+ ss << Trinity::StringFormat("%-5s ", Appender::getLogLevelString(message->level));
+
+ if (flags & APPENDER_FLAGS_PREFIX_LOGFILTERTYPE)
+ ss << '[' << message->type << "] ";
+
+ message->prefix = ss.str();
+ _write(message);
+}
+
+const char* Appender::getLogLevelString(LogLevel level)
+{
+ switch (level)
+ {
+ case LOG_LEVEL_FATAL:
+ return "FATAL";
+ case LOG_LEVEL_ERROR:
+ return "ERROR";
+ case LOG_LEVEL_WARN:
+ return "WARN";
+ case LOG_LEVEL_INFO:
+ return "INFO";
+ case LOG_LEVEL_DEBUG:
+ return "DEBUG";
+ case LOG_LEVEL_TRACE:
+ return "TRACE";
+ default:
+ return "DISABLED";
+ }
+}
diff --git a/src/common/Logging/Appender.h b/src/common/Logging/Appender.h
new file mode 100644
index 00000000000..6382399a0b4
--- /dev/null
+++ b/src/common/Logging/Appender.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef APPENDER_H
+#define APPENDER_H
+
+#include <unordered_map>
+#include <stdexcept>
+#include <string>
+#include <time.h>
+#include <type_traits>
+#include <vector>
+#include <utility>
+#include "Define.h"
+
+// Values assigned have their equivalent in enum ACE_Log_Priority
+enum LogLevel
+{
+ LOG_LEVEL_DISABLED = 0,
+ LOG_LEVEL_TRACE = 1,
+ LOG_LEVEL_DEBUG = 2,
+ LOG_LEVEL_INFO = 3,
+ LOG_LEVEL_WARN = 4,
+ LOG_LEVEL_ERROR = 5,
+ LOG_LEVEL_FATAL = 6
+};
+
+const uint8 MaxLogLevels = 6;
+
+enum AppenderType
+{
+ APPENDER_NONE,
+ APPENDER_CONSOLE,
+ APPENDER_FILE,
+ APPENDER_DB
+};
+
+enum AppenderFlags
+{
+ APPENDER_FLAGS_NONE = 0x00,
+ APPENDER_FLAGS_PREFIX_TIMESTAMP = 0x01,
+ APPENDER_FLAGS_PREFIX_LOGLEVEL = 0x02,
+ APPENDER_FLAGS_PREFIX_LOGFILTERTYPE = 0x04,
+ APPENDER_FLAGS_USE_TIMESTAMP = 0x08, // only used by FileAppender
+ APPENDER_FLAGS_MAKE_FILE_BACKUP = 0x10 // only used by FileAppender
+};
+
+struct LogMessage
+{
+ LogMessage(LogLevel _level, std::string const& _type, std::string&& _text)
+ : level(_level), type(_type), text(std::forward<std::string>(_text)), mtime(time(NULL))
+ { }
+
+ LogMessage(LogMessage const& /*other*/) = delete;
+ LogMessage& operator=(LogMessage const& /*other*/) = delete;
+
+ static std::string getTimeStr(time_t time);
+ std::string getTimeStr();
+
+ LogLevel const level;
+ std::string const type;
+ std::string const text;
+ std::string prefix;
+ std::string param1;
+ time_t mtime;
+
+ ///@ Returns size of the log message content in bytes
+ uint32 Size() const
+ {
+ return static_cast<uint32>(prefix.size() + text.size());
+ }
+};
+
+class Appender
+{
+ public:
+ Appender(uint8 _id, std::string const& name, LogLevel level = LOG_LEVEL_DISABLED, AppenderFlags flags = APPENDER_FLAGS_NONE);
+ virtual ~Appender();
+
+ uint8 getId() const;
+ std::string const& getName() const;
+ virtual AppenderType getType() const = 0;
+ LogLevel getLogLevel() const;
+ AppenderFlags getFlags() const;
+
+ void setLogLevel(LogLevel);
+ void write(LogMessage* message);
+ static const char* getLogLevelString(LogLevel level);
+ virtual void setRealmId(uint32 /*realmId*/) { }
+
+ private:
+ virtual void _write(LogMessage const* /*message*/) = 0;
+
+ uint8 id;
+ std::string name;
+ LogLevel level;
+ AppenderFlags flags;
+};
+
+typedef std::unordered_map<uint8, Appender*> AppenderMap;
+
+typedef std::vector<char const*> ExtraAppenderArgs;
+typedef Appender*(*AppenderCreatorFn)(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, ExtraAppenderArgs extraArgs);
+typedef std::unordered_map<uint8, AppenderCreatorFn> AppenderCreatorMap;
+
+template<class AppenderImpl>
+Appender* CreateAppender(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, ExtraAppenderArgs extraArgs)
+{
+ return new AppenderImpl(id, name, level, flags, std::forward<ExtraAppenderArgs>(extraArgs));
+}
+
+class InvalidAppenderArgsException : public std::length_error
+{
+public:
+ explicit InvalidAppenderArgsException(std::string const& message) : std::length_error(message) { }
+};
+
+#endif
diff --git a/src/common/Logging/AppenderConsole.cpp b/src/common/Logging/AppenderConsole.cpp
new file mode 100644
index 00000000000..531df266aa1
--- /dev/null
+++ b/src/common/Logging/AppenderConsole.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sstream>
+
+#include "AppenderConsole.h"
+#include "Config.h"
+#include "Util.h"
+
+#if PLATFORM == PLATFORM_WINDOWS
+ #include <Windows.h>
+#endif
+
+AppenderConsole::AppenderConsole(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, ExtraAppenderArgs extraArgs)
+ : Appender(id, name, level, flags), _colored(false)
+{
+ for (uint8 i = 0; i < MaxLogLevels; ++i)
+ _colors[i] = ColorTypes(MaxColors);
+
+ if (!extraArgs.empty())
+ InitColors(extraArgs[0]);
+}
+
+void AppenderConsole::InitColors(std::string const& str)
+{
+ if (str.empty())
+ {
+ _colored = false;
+ return;
+ }
+
+ int color[MaxLogLevels];
+
+ std::istringstream ss(str);
+
+ for (uint8 i = 0; i < MaxLogLevels; ++i)
+ {
+ ss >> color[i];
+
+ if (!ss)
+ return;
+
+ if (color[i] < 0 || color[i] >= MaxColors)
+ return;
+ }
+
+ for (uint8 i = 0; i < MaxLogLevels; ++i)
+ _colors[i] = ColorTypes(color[i]);
+
+ _colored = true;
+}
+
+void AppenderConsole::SetColor(bool stdout_stream, ColorTypes color)
+{
+#if PLATFORM == PLATFORM_WINDOWS
+ static WORD WinColorFG[MaxColors] =
+ {
+ 0, // BLACK
+ FOREGROUND_RED, // RED
+ FOREGROUND_GREEN, // GREEN
+ FOREGROUND_RED | FOREGROUND_GREEN, // BROWN
+ FOREGROUND_BLUE, // BLUE
+ FOREGROUND_RED | FOREGROUND_BLUE, // MAGENTA
+ FOREGROUND_GREEN | FOREGROUND_BLUE, // CYAN
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // WHITE
+ // YELLOW
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
+ // RED_BOLD
+ FOREGROUND_RED | FOREGROUND_INTENSITY,
+ // GREEN_BOLD
+ FOREGROUND_GREEN | FOREGROUND_INTENSITY,
+ FOREGROUND_BLUE | FOREGROUND_INTENSITY, // BLUE_BOLD
+ // MAGENTA_BOLD
+ FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
+ // CYAN_BOLD
+ FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
+ // WHITE_BOLD
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
+ };
+
+ HANDLE hConsole = GetStdHandle(stdout_stream ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
+ SetConsoleTextAttribute(hConsole, WinColorFG[color]);
+#else
+ enum ANSITextAttr
+ {
+ TA_NORMAL = 0,
+ TA_BOLD = 1,
+ TA_BLINK = 5,
+ TA_REVERSE = 7
+ };
+
+ enum ANSIFgTextAttr
+ {
+ FG_BLACK = 30,
+ FG_RED,
+ FG_GREEN,
+ FG_BROWN,
+ FG_BLUE,
+ FG_MAGENTA,
+ FG_CYAN,
+ FG_WHITE,
+ FG_YELLOW
+ };
+
+ enum ANSIBgTextAttr
+ {
+ BG_BLACK = 40,
+ BG_RED,
+ BG_GREEN,
+ BG_BROWN,
+ BG_BLUE,
+ BG_MAGENTA,
+ BG_CYAN,
+ BG_WHITE
+ };
+
+ static uint8 UnixColorFG[MaxColors] =
+ {
+ FG_BLACK, // BLACK
+ FG_RED, // RED
+ FG_GREEN, // GREEN
+ FG_BROWN, // BROWN
+ FG_BLUE, // BLUE
+ FG_MAGENTA, // MAGENTA
+ FG_CYAN, // CYAN
+ FG_WHITE, // WHITE
+ FG_YELLOW, // YELLOW
+ FG_RED, // LRED
+ FG_GREEN, // LGREEN
+ FG_BLUE, // LBLUE
+ FG_MAGENTA, // LMAGENTA
+ FG_CYAN, // LCYAN
+ FG_WHITE // LWHITE
+ };
+
+ fprintf((stdout_stream? stdout : stderr), "\x1b[%d%sm", UnixColorFG[color], (color >= YELLOW && color < MaxColors ? ";1" : ""));
+ #endif
+}
+
+void AppenderConsole::ResetColor(bool stdout_stream)
+{
+ #if PLATFORM == PLATFORM_WINDOWS
+ HANDLE hConsole = GetStdHandle(stdout_stream ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
+ SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
+ #else
+ fprintf((stdout_stream ? stdout : stderr), "\x1b[0m");
+ #endif
+}
+
+void AppenderConsole::_write(LogMessage const* message)
+{
+ bool stdout_stream = !(message->level == LOG_LEVEL_ERROR || message->level == LOG_LEVEL_FATAL);
+
+ if (_colored)
+ {
+ uint8 index;
+ switch (message->level)
+ {
+ case LOG_LEVEL_TRACE:
+ index = 5;
+ break;
+ case LOG_LEVEL_DEBUG:
+ index = 4;
+ break;
+ case LOG_LEVEL_INFO:
+ index = 3;
+ break;
+ case LOG_LEVEL_WARN:
+ index = 2;
+ break;
+ case LOG_LEVEL_FATAL:
+ index = 0;
+ break;
+ case LOG_LEVEL_ERROR: // No break on purpose
+ default:
+ index = 1;
+ break;
+ }
+
+ SetColor(stdout_stream, _colors[index]);
+ utf8printf(stdout_stream ? stdout : stderr, "%s%s\n", message->prefix.c_str(), message->text.c_str());
+ ResetColor(stdout_stream);
+ }
+ else
+ utf8printf(stdout_stream ? stdout : stderr, "%s%s\n", message->prefix.c_str(), message->text.c_str());
+}
diff --git a/src/common/Logging/AppenderConsole.h b/src/common/Logging/AppenderConsole.h
new file mode 100644
index 00000000000..6b30505c6bd
--- /dev/null
+++ b/src/common/Logging/AppenderConsole.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef APPENDERCONSOLE_H
+#define APPENDERCONSOLE_H
+
+#include <string>
+#include "Appender.h"
+
+enum ColorTypes
+{
+ BLACK,
+ RED,
+ GREEN,
+ BROWN,
+ BLUE,
+ MAGENTA,
+ CYAN,
+ GREY,
+ YELLOW,
+ LRED,
+ LGREEN,
+ LBLUE,
+ LMAGENTA,
+ LCYAN,
+ WHITE
+};
+
+const uint8 MaxColors = uint8(WHITE) + 1;
+
+class AppenderConsole : public Appender
+{
+ public:
+ typedef std::integral_constant<AppenderType, APPENDER_CONSOLE>::type TypeIndex;
+
+ AppenderConsole(uint8 _id, std::string const& name, LogLevel level, AppenderFlags flags, ExtraAppenderArgs extraArgs);
+ void InitColors(const std::string& init_str);
+ AppenderType getType() const override { return TypeIndex::value; }
+
+ private:
+ void SetColor(bool stdout_stream, ColorTypes color);
+ void ResetColor(bool stdout_stream);
+ void _write(LogMessage const* message) override;
+ bool _colored;
+ ColorTypes _colors[MaxLogLevels];
+};
+
+#endif
diff --git a/src/common/Logging/AppenderFile.cpp b/src/common/Logging/AppenderFile.cpp
new file mode 100644
index 00000000000..c90c8f6ccea
--- /dev/null
+++ b/src/common/Logging/AppenderFile.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AppenderFile.h"
+#include "Common.h"
+#include "StringFormat.h"
+#include "Log.h"
+
+#if PLATFORM == PLATFORM_WINDOWS
+# include <Windows.h>
+#endif
+
+AppenderFile::AppenderFile(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, ExtraAppenderArgs extraArgs) :
+ Appender(id, name, level, flags),
+ logfile(NULL),
+ _logDir(sLog->GetLogsDir()),
+ _fileSize(0)
+{
+ if (extraArgs.empty())
+ throw InvalidAppenderArgsException(Trinity::StringFormat("Log::CreateAppenderFromConfig: Missing file name for appender %s\n", name.c_str()));
+
+ _fileName = extraArgs[0];
+
+ char const* mode = "a";
+ if (extraArgs.size() > 1)
+ mode = extraArgs[1];
+
+ if (flags & APPENDER_FLAGS_USE_TIMESTAMP)
+ {
+ size_t dot_pos = _fileName.find_last_of(".");
+ if (dot_pos != std::string::npos)
+ _fileName.insert(dot_pos, sLog->GetLogsTimestamp());
+ else
+ _fileName += sLog->GetLogsTimestamp();
+ }
+
+ if (extraArgs.size() > 2)
+ _maxFileSize = atoi(extraArgs[2]);
+ else
+ _maxFileSize = 0;
+
+ _dynamicName = std::string::npos != _fileName.find("%s");
+ _backup = (flags & APPENDER_FLAGS_MAKE_FILE_BACKUP) != 0;
+
+ if (!_dynamicName)
+ logfile = OpenFile(_fileName, mode, !strcmp(mode, "w") && _backup);
+}
+
+AppenderFile::~AppenderFile()
+{
+ CloseFile();
+}
+
+void AppenderFile::_write(LogMessage const* message)
+{
+ bool exceedMaxSize = _maxFileSize > 0 && (_fileSize.load() + message->Size()) > _maxFileSize;
+
+ if (_dynamicName)
+ {
+ char namebuf[TRINITY_PATH_MAX];
+ snprintf(namebuf, TRINITY_PATH_MAX, _fileName.c_str(), message->param1.c_str());
+ // always use "a" with dynamic name otherwise it could delete the log we wrote in last _write() call
+ FILE* file = OpenFile(namebuf, "a", _backup || exceedMaxSize);
+ if (!file)
+ return;
+ fprintf(file, "%s%s\n", message->prefix.c_str(), message->text.c_str());
+ fflush(file);
+ _fileSize += uint64(message->Size());
+ fclose(file);
+ return;
+ }
+ else if (exceedMaxSize)
+ logfile = OpenFile(_fileName, "w", true);
+
+ if (!logfile)
+ return;
+
+ fprintf(logfile, "%s%s\n", message->prefix.c_str(), message->text.c_str());
+ fflush(logfile);
+ _fileSize += uint64(message->Size());
+}
+
+FILE* AppenderFile::OpenFile(std::string const& filename, std::string const& mode, bool backup)
+{
+ std::string fullName(_logDir + filename);
+ if (backup)
+ {
+ CloseFile();
+ std::string newName(fullName);
+ newName.push_back('.');
+ newName.append(LogMessage::getTimeStr(time(NULL)));
+ std::replace(newName.begin(), newName.end(), ':', '-');
+ rename(fullName.c_str(), newName.c_str()); // no error handling... if we couldn't make a backup, just ignore
+ }
+
+ if (FILE* ret = fopen(fullName.c_str(), mode.c_str()))
+ {
+ _fileSize = ftell(ret);
+ return ret;
+ }
+
+ return NULL;
+}
+
+void AppenderFile::CloseFile()
+{
+ if (logfile)
+ {
+ fclose(logfile);
+ logfile = NULL;
+ }
+}
diff --git a/src/common/Logging/AppenderFile.h b/src/common/Logging/AppenderFile.h
new file mode 100644
index 00000000000..c2781eb1ee9
--- /dev/null
+++ b/src/common/Logging/AppenderFile.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef APPENDERFILE_H
+#define APPENDERFILE_H
+
+#include <atomic>
+#include "Appender.h"
+
+class AppenderFile : public Appender
+{
+ public:
+ typedef std::integral_constant<AppenderType, APPENDER_FILE>::type TypeIndex;
+
+ AppenderFile(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, ExtraAppenderArgs extraArgs);
+ ~AppenderFile();
+ FILE* OpenFile(std::string const& name, std::string const& mode, bool backup);
+ AppenderType getType() const override { return TypeIndex::value; }
+
+ private:
+ void CloseFile();
+ void _write(LogMessage const* message) override;
+ FILE* logfile;
+ std::string _fileName;
+ std::string _logDir;
+ bool _dynamicName;
+ bool _backup;
+ uint64 _maxFileSize;
+ std::atomic<uint64> _fileSize;
+};
+
+#endif
diff --git a/src/common/Logging/Log.cpp b/src/common/Logging/Log.cpp
new file mode 100644
index 00000000000..57d399a9d96
--- /dev/null
+++ b/src/common/Logging/Log.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "Log.h"
+#include "Common.h"
+#include "Config.h"
+#include "Util.h"
+#include "AppenderConsole.h"
+#include "AppenderFile.h"
+#include "LogOperation.h"
+
+#include <cstdio>
+#include <sstream>
+
+Log::Log() : AppenderId(0), lowestLogLevel(LOG_LEVEL_FATAL), _ioService(nullptr), _strand(nullptr)
+{
+ m_logsTimestamp = "_" + GetTimestampStr();
+ RegisterAppender<AppenderConsole>();
+ RegisterAppender<AppenderFile>();
+}
+
+Log::~Log()
+{
+ delete _strand;
+ Close();
+}
+
+uint8 Log::NextAppenderId()
+{
+ return AppenderId++;
+}
+
+int32 GetConfigIntDefault(std::string base, const char* name, int32 value)
+{
+ base.append(name);
+ return sConfigMgr->GetIntDefault(base.c_str(), value);
+}
+
+std::string GetConfigStringDefault(std::string base, const char* name, const char* value)
+{
+ base.append(name);
+ return sConfigMgr->GetStringDefault(base.c_str(), value);
+}
+
+Appender* Log::GetAppenderByName(std::string const& name)
+{
+ AppenderMap::iterator it = appenders.begin();
+ while (it != appenders.end() && it->second && it->second->getName() != name)
+ ++it;
+
+ return it == appenders.end() ? NULL : it->second;
+}
+
+void Log::CreateAppenderFromConfig(std::string const& appenderName)
+{
+ if (appenderName.empty())
+ return;
+
+ // Format=type, level, flags, optional1, optional2
+ // if type = File. optional1 = file and option2 = mode
+ // if type = Console. optional1 = Color
+ std::string options = sConfigMgr->GetStringDefault(appenderName.c_str(), "");
+
+ Tokenizer tokens(options, ',');
+ Tokenizer::const_iterator iter = tokens.begin();
+
+ size_t size = tokens.size();
+ std::string name = appenderName.substr(9);
+
+ if (size < 2)
+ {
+ fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong configuration for appender %s. Config line: %s\n", name.c_str(), options.c_str());
+ return;
+ }
+
+ AppenderFlags flags = APPENDER_FLAGS_NONE;
+ AppenderType type = AppenderType(atoi(*iter++));
+ LogLevel level = LogLevel(atoi(*iter++));
+
+ if (level > LOG_LEVEL_FATAL)
+ {
+ fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong Log Level %d for appender %s\n", level, name.c_str());
+ return;
+ }
+
+ if (size > 2)
+ flags = AppenderFlags(atoi(*iter++));
+
+ auto factoryFunction = appenderFactory.find(type);
+ if (factoryFunction == appenderFactory.end())
+ {
+ fprintf(stderr, "Log::CreateAppenderFromConfig: Unknown type %d for appender %s\n", type, name.c_str());
+ return;
+ }
+
+ try
+ {
+ Appender* appender = factoryFunction->second(NextAppenderId(), name, level, flags, ExtraAppenderArgs(iter, tokens.end()));
+ appenders[appender->getId()] = appender;
+ }
+ catch (InvalidAppenderArgsException const& iaae)
+ {
+ fprintf(stderr, "%s", iaae.what());
+ }
+}
+
+void Log::CreateLoggerFromConfig(std::string const& appenderName)
+{
+ if (appenderName.empty())
+ return;
+
+ LogLevel level = LOG_LEVEL_DISABLED;
+ uint8 type = uint8(-1);
+
+ std::string options = sConfigMgr->GetStringDefault(appenderName.c_str(), "");
+ std::string name = appenderName.substr(7);
+
+ if (options.empty())
+ {
+ fprintf(stderr, "Log::CreateLoggerFromConfig: Missing config option Logger.%s\n", name.c_str());
+ return;
+ }
+
+ Tokenizer tokens(options, ',');
+ Tokenizer::const_iterator iter = tokens.begin();
+
+ if (tokens.size() != 2)
+ {
+ fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong config option Logger.%s=%s\n", name.c_str(), options.c_str());
+ return;
+ }
+
+ Logger& logger = loggers[name];
+ if (!logger.getName().empty())
+ {
+ fprintf(stderr, "Error while configuring Logger %s. Already defined\n", name.c_str());
+ return;
+ }
+
+ level = LogLevel(atoi(*iter++));
+ if (level > LOG_LEVEL_FATAL)
+ {
+ fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong Log Level %u for logger %s\n", type, name.c_str());
+ return;
+ }
+
+ if (level < lowestLogLevel)
+ lowestLogLevel = level;
+
+ logger.Create(name, level);
+ //fprintf(stdout, "Log::CreateLoggerFromConfig: Created Logger %s, Level %u\n", name.c_str(), level);
+
+ std::istringstream ss(*iter);
+ std::string str;
+
+ ss >> str;
+ while (ss)
+ {
+ if (Appender* appender = GetAppenderByName(str))
+ {
+ logger.addAppender(appender->getId(), appender);
+ //fprintf(stdout, "Log::CreateLoggerFromConfig: Added Appender %s to Logger %s\n", appender->getName().c_str(), name.c_str());
+ }
+ else
+ fprintf(stderr, "Error while configuring Appender %s in Logger %s. Appender does not exist", str.c_str(), name.c_str());
+ ss >> str;
+ }
+}
+
+void Log::ReadAppendersFromConfig()
+{
+ std::list<std::string> keys = sConfigMgr->GetKeysByString("Appender.");
+
+ while (!keys.empty())
+ {
+ CreateAppenderFromConfig(keys.front());
+ keys.pop_front();
+ }
+}
+
+void Log::ReadLoggersFromConfig()
+{
+ std::list<std::string> keys = sConfigMgr->GetKeysByString("Logger.");
+
+ while (!keys.empty())
+ {
+ CreateLoggerFromConfig(keys.front());
+ keys.pop_front();
+ }
+
+ // Bad config configuration, creating default config
+ if (loggers.find(LOGGER_ROOT) == loggers.end())
+ {
+ fprintf(stderr, "Wrong Loggers configuration. Review your Logger config section.\n"
+ "Creating default loggers [root (Error), server (Info)] to console\n");
+
+ Close(); // Clean any Logger or Appender created
+
+ AppenderConsole* appender = new AppenderConsole(NextAppenderId(), "Console", LOG_LEVEL_DEBUG, APPENDER_FLAGS_NONE, ExtraAppenderArgs());
+ appenders[appender->getId()] = appender;
+
+ Logger& logger = loggers[LOGGER_ROOT];
+ logger.Create(LOGGER_ROOT, LOG_LEVEL_ERROR);
+ logger.addAppender(appender->getId(), appender);
+
+ logger = loggers["server"];
+ logger.Create("server", LOG_LEVEL_ERROR);
+ logger.addAppender(appender->getId(), appender);
+ }
+}
+
+void Log::write(std::unique_ptr<LogMessage>&& msg) const
+{
+ Logger const* logger = GetLoggerByType(msg->type);
+
+ if (_ioService)
+ {
+ auto logOperation = std::shared_ptr<LogOperation>(new LogOperation(logger, std::move(msg)));
+
+ _ioService->post(_strand->wrap([logOperation](){ logOperation->call(); }));
+ }
+ else
+ logger->write(msg.get());
+}
+
+std::string Log::GetTimestampStr()
+{
+ time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+
+ std::tm aTm;
+ localtime_r(&tt, &aTm);
+
+ // YYYY year
+ // MM month (2 digits 01-12)
+ // DD day (2 digits 01-31)
+ // HH hour (2 digits 00-23)
+ // MM minutes (2 digits 00-59)
+ // SS seconds (2 digits 00-59)
+ return Trinity::StringFormat("%04d-%02d-%02d_%02d-%02d-%02d",
+ aTm.tm_year + 1900, aTm.tm_mon + 1, aTm.tm_mday, aTm.tm_hour, aTm.tm_min, aTm.tm_sec);
+}
+
+bool Log::SetLogLevel(std::string const& name, const char* newLevelc, bool isLogger /* = true */)
+{
+ LogLevel newLevel = LogLevel(atoi(newLevelc));
+ if (newLevel < 0)
+ return false;
+
+ if (isLogger)
+ {
+ LoggerMap::iterator it = loggers.begin();
+ while (it != loggers.end() && it->second.getName() != name)
+ ++it;
+
+ if (it == loggers.end())
+ return false;
+
+ it->second.setLogLevel(newLevel);
+
+ if (newLevel != LOG_LEVEL_DISABLED && newLevel < lowestLogLevel)
+ lowestLogLevel = newLevel;
+ }
+ else
+ {
+ Appender* appender = GetAppenderByName(name);
+ if (!appender)
+ return false;
+
+ appender->setLogLevel(newLevel);
+ }
+
+ return true;
+}
+
+void Log::outCharDump(char const* str, uint32 accountId, uint64 guid, char const* name)
+{
+ if (!str || !ShouldLog("entities.player.dump", LOG_LEVEL_INFO))
+ return;
+
+ std::ostringstream ss;
+ ss << "== START DUMP == (account: " << accountId << " guid: " << guid << " name: " << name
+ << ")\n" << str << "\n== END DUMP ==\n";
+
+ std::unique_ptr<LogMessage> msg(new LogMessage(LOG_LEVEL_INFO, "entities.player.dump", ss.str()));
+ std::ostringstream param;
+ param << guid << '_' << name;
+
+ msg->param1 = param.str();
+
+ write(std::move(msg));
+}
+
+void Log::SetRealmId(uint32 id)
+{
+ for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it)
+ it->second->setRealmId(id);
+}
+
+void Log::Close()
+{
+ loggers.clear();
+ for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it)
+ delete it->second;
+
+ appenders.clear();
+}
+
+void Log::Initialize(boost::asio::io_service* ioService)
+{
+ if (ioService)
+ {
+ _ioService = ioService;
+ _strand = new boost::asio::strand(*ioService);
+ }
+
+ LoadFromConfig();
+}
+
+void Log::LoadFromConfig()
+{
+ Close();
+
+ lowestLogLevel = LOG_LEVEL_FATAL;
+ AppenderId = 0;
+ m_logsDir = sConfigMgr->GetStringDefault("LogsDir", "");
+ if (!m_logsDir.empty())
+ if ((m_logsDir.at(m_logsDir.length() - 1) != '/') && (m_logsDir.at(m_logsDir.length() - 1) != '\\'))
+ m_logsDir.push_back('/');
+
+ ReadAppendersFromConfig();
+ ReadLoggersFromConfig();
+}
diff --git a/src/common/Logging/Log.h b/src/common/Logging/Log.h
new file mode 100644
index 00000000000..a15bb4ad485
--- /dev/null
+++ b/src/common/Logging/Log.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 TRINITYCORE_LOG_H
+#define TRINITYCORE_LOG_H
+
+#include "Define.h"
+#include "Appender.h"
+#include "Logger.h"
+#include "StringFormat.h"
+#include "Common.h"
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/strand.hpp>
+
+#include <stdarg.h>
+#include <unordered_map>
+#include <string>
+#include <memory>
+
+#define LOGGER_ROOT "root"
+
+class Log
+{
+ typedef std::unordered_map<std::string, Logger> LoggerMap;
+
+ private:
+ Log();
+ ~Log();
+
+ public:
+
+ static Log* instance()
+ {
+ static Log instance;
+ return &instance;
+ }
+
+ void Initialize(boost::asio::io_service* ioService);
+ void LoadFromConfig();
+ void Close();
+ bool ShouldLog(std::string const& type, LogLevel level) const;
+ bool SetLogLevel(std::string const& name, char const* level, bool isLogger = true);
+
+ template<typename Format, typename... Args>
+ inline void outMessage(std::string const& filter, LogLevel const level, Format&& fmt, Args&&... args)
+ {
+ write(Trinity::make_unique<LogMessage>(level, filter,
+ Trinity::StringFormat(std::forward<Format>(fmt), std::forward<Args>(args)...)));
+ }
+
+ template<typename Format, typename... Args>
+ void outCommand(uint32 account, Format&& fmt, Args&&... args)
+ {
+ if (!ShouldLog("commands.gm", LOG_LEVEL_INFO))
+ return;
+
+ std::unique_ptr<LogMessage> msg =
+ Trinity::make_unique<LogMessage>(LOG_LEVEL_INFO, "commands.gm",
+ Trinity::StringFormat(std::forward<Format>(fmt), std::forward<Args>(args)...));
+
+ msg->param1 = std::to_string(account);
+
+ write(std::move(msg));
+ }
+
+ void outCharDump(char const* str, uint32 account_id, uint64 guid, char const* name);
+
+ void SetRealmId(uint32 id);
+
+ template<class AppenderImpl>
+ void RegisterAppender()
+ {
+ using Index = typename AppenderImpl::TypeIndex;
+ auto itr = appenderFactory.find(Index::value);
+ ASSERT(itr == appenderFactory.end());
+ appenderFactory[Index::value] = &CreateAppender<AppenderImpl>;
+ }
+
+ std::string const& GetLogsDir() const { return m_logsDir; }
+ std::string const& GetLogsTimestamp() const { return m_logsTimestamp; }
+
+ private:
+ static std::string GetTimestampStr();
+ void write(std::unique_ptr<LogMessage>&& msg) const;
+
+ Logger const* GetLoggerByType(std::string const& type) const;
+ Appender* GetAppenderByName(std::string const& name);
+ uint8 NextAppenderId();
+ void CreateAppenderFromConfig(std::string const& name);
+ void CreateLoggerFromConfig(std::string const& name);
+ void ReadAppendersFromConfig();
+ void ReadLoggersFromConfig();
+
+ AppenderCreatorMap appenderFactory;
+ AppenderMap appenders;
+ LoggerMap loggers;
+ uint8 AppenderId;
+ LogLevel lowestLogLevel;
+
+ std::string m_logsDir;
+ std::string m_logsTimestamp;
+
+ boost::asio::io_service* _ioService;
+ boost::asio::strand* _strand;
+};
+
+inline Logger const* Log::GetLoggerByType(std::string const& type) const
+{
+ LoggerMap::const_iterator it = loggers.find(type);
+ if (it != loggers.end())
+ return &(it->second);
+
+ if (type == LOGGER_ROOT)
+ return NULL;
+
+ std::string parentLogger = LOGGER_ROOT;
+ size_t found = type.find_last_of(".");
+ if (found != std::string::npos)
+ parentLogger = type.substr(0,found);
+
+ return GetLoggerByType(parentLogger);
+}
+
+inline bool Log::ShouldLog(std::string const& type, LogLevel level) const
+{
+ // TODO: Use cache to store "Type.sub1.sub2": "Type" equivalence, should
+ // Speed up in cases where requesting "Type.sub1.sub2" but only configured
+ // Logger "Type"
+
+ // Don't even look for a logger if the LogLevel is lower than lowest log levels across all loggers
+ if (level < lowestLogLevel)
+ return false;
+
+ Logger const* logger = GetLoggerByType(type);
+ if (!logger)
+ return false;
+
+ LogLevel logLevel = logger->getLogLevel();
+ return logLevel != LOG_LEVEL_DISABLED && logLevel <= level;
+}
+
+#define sLog Log::instance()
+
+#define LOG_EXCEPTION_FREE(filterType__, level__, ...) \
+ { \
+ try \
+ { \
+ sLog->outMessage(filterType__, level__, __VA_ARGS__); \
+ } \
+ catch (std::exception& e) \
+ { \
+ sLog->outMessage("server", LOG_LEVEL_ERROR, "Wrong format occurred (%s) at %s:%u.", \
+ e.what(), __FILE__, __LINE__); \
+ } \
+ }
+
+#if PLATFORM != PLATFORM_WINDOWS
+void check_args(const char*, ...) ATTR_PRINTF(1, 2);
+void check_args(std::string const&, ...);
+
+// This will catch format errors on build time
+#define TC_LOG_MESSAGE_BODY(filterType__, level__, ...) \
+ do { \
+ if (sLog->ShouldLog(filterType__, level__)) \
+ { \
+ if (false) \
+ check_args(__VA_ARGS__); \
+ \
+ LOG_EXCEPTION_FREE(filterType__, level__, __VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define TC_LOG_MESSAGE_BODY(filterType__, level__, ...) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { \
+ if (sLog->ShouldLog(filterType__, level__)) \
+ LOG_EXCEPTION_FREE(filterType__, level__, __VA_ARGS__); \
+ } while (0) \
+ __pragma(warning(pop))
+#endif
+
+#define TC_LOG_TRACE(filterType__, ...) \
+ TC_LOG_MESSAGE_BODY(filterType__, LOG_LEVEL_TRACE, __VA_ARGS__)
+
+#define TC_LOG_DEBUG(filterType__, ...) \
+ TC_LOG_MESSAGE_BODY(filterType__, LOG_LEVEL_DEBUG, __VA_ARGS__)
+
+#define TC_LOG_INFO(filterType__, ...) \
+ TC_LOG_MESSAGE_BODY(filterType__, LOG_LEVEL_INFO, __VA_ARGS__)
+
+#define TC_LOG_WARN(filterType__, ...) \
+ TC_LOG_MESSAGE_BODY(filterType__, LOG_LEVEL_WARN, __VA_ARGS__)
+
+#define TC_LOG_ERROR(filterType__, ...) \
+ TC_LOG_MESSAGE_BODY(filterType__, LOG_LEVEL_ERROR, __VA_ARGS__)
+
+#define TC_LOG_FATAL(filterType__, ...) \
+ TC_LOG_MESSAGE_BODY(filterType__, LOG_LEVEL_FATAL, __VA_ARGS__)
+
+#endif
diff --git a/src/common/Logging/LogOperation.cpp b/src/common/Logging/LogOperation.cpp
new file mode 100644
index 00000000000..bcd923c705e
--- /dev/null
+++ b/src/common/Logging/LogOperation.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "LogOperation.h"
+#include "Logger.h"
+
+int LogOperation::call()
+{
+ logger->write(msg.get());
+ return 0;
+}
diff --git a/src/common/Logging/LogOperation.h b/src/common/Logging/LogOperation.h
new file mode 100644
index 00000000000..ffdd35c3c09
--- /dev/null
+++ b/src/common/Logging/LogOperation.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LOGOPERATION_H
+#define LOGOPERATION_H
+
+#include <memory>
+
+class Logger;
+struct LogMessage;
+
+class LogOperation
+{
+ public:
+ LogOperation(Logger const* _logger, std::unique_ptr<LogMessage>&& _msg)
+ : logger(_logger), msg(std::forward<std::unique_ptr<LogMessage>>(_msg))
+ { }
+
+ ~LogOperation() { }
+
+ int call();
+
+ protected:
+ Logger const* logger;
+ std::unique_ptr<LogMessage> msg;
+};
+
+#endif
diff --git a/src/common/Logging/Logger.cpp b/src/common/Logging/Logger.cpp
new file mode 100644
index 00000000000..3b02eb47575
--- /dev/null
+++ b/src/common/Logging/Logger.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Logger.h"
+
+Logger::Logger(): name(""), level(LOG_LEVEL_DISABLED) { }
+
+void Logger::Create(std::string const& _name, LogLevel _level)
+{
+ name = _name;
+ level = _level;
+}
+
+std::string const& Logger::getName() const
+{
+ return name;
+}
+
+LogLevel Logger::getLogLevel() const
+{
+ return level;
+}
+
+void Logger::addAppender(uint8 id, Appender* appender)
+{
+ appenders[id] = appender;
+}
+
+void Logger::delAppender(uint8 id)
+{
+ appenders.erase(id);
+}
+
+void Logger::setLogLevel(LogLevel _level)
+{
+ level = _level;
+}
+
+void Logger::write(LogMessage* message) const
+{
+ if (!level || level > message->level || message->text.empty())
+ {
+ //fprintf(stderr, "Logger::write: Logger %s, Level %u. Msg %s Level %u WRONG LEVEL MASK OR EMPTY MSG\n", getName().c_str(), getLogLevel(), message.text.c_str(), message.level);
+ return;
+ }
+
+ for (AppenderMap::const_iterator it = appenders.begin(); it != appenders.end(); ++it)
+ if (it->second)
+ it->second->write(message);
+}
diff --git a/src/common/Logging/Logger.h b/src/common/Logging/Logger.h
new file mode 100644
index 00000000000..1aee75c5d72
--- /dev/null
+++ b/src/common/Logging/Logger.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include "Appender.h"
+
+class Logger
+{
+ public:
+ Logger();
+
+ void Create(std::string const& name, LogLevel level);
+ void addAppender(uint8 type, Appender *);
+ void delAppender(uint8 type);
+
+ std::string const& getName() const;
+ LogLevel getLogLevel() const;
+ void setLogLevel(LogLevel level);
+ void write(LogMessage* message) const;
+
+ private:
+ std::string name;
+ LogLevel level;
+ AppenderMap appenders;
+};
+
+#endif
diff --git a/src/common/PrecompiledHeaders/commonPCH.cpp b/src/common/PrecompiledHeaders/commonPCH.cpp
new file mode 100644
index 00000000000..52c623ca64a
--- /dev/null
+++ b/src/common/PrecompiledHeaders/commonPCH.cpp
@@ -0,0 +1 @@
+#include "PrecompiledHeaders/commonPCH.h"
diff --git a/src/common/PrecompiledHeaders/commonPCH.h b/src/common/PrecompiledHeaders/commonPCH.h
new file mode 100644
index 00000000000..2d4f1ff4694
--- /dev/null
+++ b/src/common/PrecompiledHeaders/commonPCH.h
@@ -0,0 +1,13 @@
+#include "Define.h"
+#include "Common.h"
+#include "BoundingIntervalHierarchy.h"
+#include "BoundingIntervalHierarchyWrapper.h"
+#include "RegularGrid.h"
+#include "Collision/VMapDefinitions.h"
+#include "Collision/Maps/MapTree.h"
+#include "Collision/Models/WorldModel.h"
+#include "Collision/Models/ModelInstance.h"
+#include "Collision/Models/GameObjectModel.h"
+#include "Threading/ProducerConsumerQueue.h"
+#include "Utilities/TaskScheduler.h"
+#include "Utilities/EventMap.h"
diff --git a/src/common/Threading/Callback.h b/src/common/Threading/Callback.h
new file mode 100644
index 00000000000..1f4ffc97cfc
--- /dev/null
+++ b/src/common/Threading/Callback.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CALLBACK_H
+#define _CALLBACK_H
+
+#include <future>
+#include "QueryResult.h"
+
+typedef std::future<QueryResult> QueryResultFuture;
+typedef std::promise<QueryResult> QueryResultPromise;
+typedef std::future<PreparedQueryResult> PreparedQueryResultFuture;
+typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise;
+
+#define CALLBACK_STAGE_INVALID uint8(-1)
+
+template <typename Result, typename ParamType, bool chain = false>
+class QueryCallback
+{
+ public:
+ QueryCallback() : _param(), _stage(chain ? 0 : CALLBACK_STAGE_INVALID) { }
+
+ //! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery
+ void SetFutureResult(std::future<Result> value)
+ {
+ _result = std::move(value);
+ }
+
+ std::future<Result>& GetFutureResult()
+ {
+ return _result;
+ }
+
+ int IsReady()
+ {
+ return _result.valid() && _result.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
+ }
+
+ void GetResult(Result& res)
+ {
+ res = _result.get();
+ }
+
+ void FreeResult()
+ {
+ // Nothing to do here, the constructor of std::future will take care of the cleanup
+ }
+
+ void SetParam(ParamType value)
+ {
+ _param = value;
+ }
+
+ ParamType GetParam()
+ {
+ return _param;
+ }
+
+ //! Resets the stage of the callback chain
+ void ResetStage()
+ {
+ if (!chain)
+ return;
+
+ _stage = 0;
+ }
+
+ //! Advances the callback chain to the next stage, so upper level code can act on its results accordingly
+ void NextStage()
+ {
+ if (!chain)
+ return;
+
+ ++_stage;
+ }
+
+ //! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid)
+ uint8 GetStage()
+ {
+ return _stage;
+ }
+
+ //! Resets all underlying variables (param, result and stage)
+ void Reset()
+ {
+ SetParam(ParamType());
+ FreeResult();
+ ResetStage();
+ }
+
+ private:
+ std::future<Result> _result;
+ ParamType _param;
+ uint8 _stage;
+
+ QueryCallback(QueryCallback const& right) = delete;
+ QueryCallback& operator=(QueryCallback const& right) = delete;
+};
+
+template <typename Result, typename ParamType1, typename ParamType2, bool chain = false>
+class QueryCallback_2
+{
+ public:
+ QueryCallback_2() : _stage(chain ? 0 : CALLBACK_STAGE_INVALID) { }
+
+ //! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery
+ void SetFutureResult(std::future<Result> value)
+ {
+ _result = std::move(value);
+ }
+
+ std::future<Result>& GetFutureResult()
+ {
+ return _result;
+ }
+
+ int IsReady()
+ {
+ return _result.valid() && _result.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
+ }
+
+ void GetResult(Result& res)
+ {
+ res = _result.get();
+ }
+
+ void FreeResult()
+ {
+ // Nothing to do here, the constructor of std::future will take care of the cleanup
+ }
+
+ void SetFirstParam(ParamType1 value)
+ {
+ _param_1 = value;
+ }
+
+ void SetSecondParam(ParamType2 value)
+ {
+ _param_2 = value;
+ }
+
+ ParamType1 GetFirstParam()
+ {
+ return _param_1;
+ }
+
+ ParamType2 GetSecondParam()
+ {
+ return _param_2;
+ }
+
+ //! Resets the stage of the callback chain
+ void ResetStage()
+ {
+ if (!chain)
+ return;
+
+ _stage = 0;
+ }
+
+ //! Advances the callback chain to the next stage, so upper level code can act on its results accordingly
+ void NextStage()
+ {
+ if (!chain)
+ return;
+
+ ++_stage;
+ }
+
+ //! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid)
+ uint8 GetStage()
+ {
+ return _stage;
+ }
+
+ //! Resets all underlying variables (param, result and stage)
+ void Reset()
+ {
+ SetFirstParam(NULL);
+ SetSecondParam(NULL);
+ FreeResult();
+ ResetStage();
+ }
+
+ private:
+ std::future<Result> _result;
+ ParamType1 _param_1;
+ ParamType2 _param_2;
+ uint8 _stage;
+
+ QueryCallback_2(QueryCallback_2 const& right) = delete;
+ QueryCallback_2& operator=(QueryCallback_2 const& right) = delete;
+};
+
+#endif
diff --git a/src/common/Threading/LockedQueue.h b/src/common/Threading/LockedQueue.h
new file mode 100644
index 00000000000..3abb0f4b8bc
--- /dev/null
+++ b/src/common/Threading/LockedQueue.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
+ *
+ * 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 LOCKEDQUEUE_H
+#define LOCKEDQUEUE_H
+
+#include <deque>
+#include <mutex>
+
+template <class T, typename StorageType = std::deque<T> >
+class LockedQueue
+{
+ //! Lock access to the queue.
+ std::mutex _lock;
+
+ //! Storage backing the queue.
+ StorageType _queue;
+
+ //! Cancellation flag.
+ volatile bool _canceled;
+
+public:
+
+ //! Create a LockedQueue.
+ LockedQueue()
+ : _canceled(false)
+ {
+ }
+
+ //! Destroy a LockedQueue.
+ virtual ~LockedQueue()
+ {
+ }
+
+ //! Adds an item to the queue.
+ void add(const T& item)
+ {
+ lock();
+
+ _queue.push_back(item);
+
+ unlock();
+ }
+
+ //! Gets the next result in the queue, if any.
+ bool next(T& result)
+ {
+ std::lock_guard<std::mutex> lock(_lock);
+
+ if (_queue.empty())
+ return false;
+
+ result = _queue.front();
+ _queue.pop_front();
+
+ return true;
+ }
+
+ template<class Checker>
+ bool next(T& result, Checker& check)
+ {
+ std::lock_guard<std::mutex> lock(_lock);
+
+ if (_queue.empty())
+ return false;
+
+ result = _queue.front();
+ if (!check.Process(result))
+ return false;
+
+ _queue.pop_front();
+ return true;
+ }
+
+ //! Peeks at the top of the queue. Check if the queue is empty before calling! Remember to unlock after use if autoUnlock == false.
+ T& peek(bool autoUnlock = false)
+ {
+ lock();
+
+ T& result = _queue.front();
+
+ if (autoUnlock)
+ unlock();
+
+ return result;
+ }
+
+ //! Cancels the queue.
+ void cancel()
+ {
+ std::lock_guard<std::mutex> lock(_lock);
+
+ _canceled = true;
+ }
+
+ //! Checks if the queue is cancelled.
+ bool cancelled()
+ {
+ std::lock_guard<std::mutex> lock(_lock);
+ return _canceled;
+ }
+
+ //! Locks the queue for access.
+ void lock()
+ {
+ this->_lock.lock();
+ }
+
+ //! Unlocks the queue.
+ void unlock()
+ {
+ this->_lock.unlock();
+ }
+
+ ///! Calls pop_front of the queue
+ void pop_front()
+ {
+ std::lock_guard<std::mutex> lock(_lock);
+ _queue.pop_front();
+ }
+
+ ///! Checks if we're empty or not with locks held
+ bool empty()
+ {
+ std::lock_guard<std::mutex> lock(_lock);
+ return _queue.empty();
+ }
+};
+#endif
diff --git a/src/common/Threading/ProcessPriority.h b/src/common/Threading/ProcessPriority.h
new file mode 100644
index 00000000000..2a8501a0249
--- /dev/null
+++ b/src/common/Threading/ProcessPriority.h
@@ -0,0 +1,105 @@
+/*
+* Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PROCESSPRIO_H
+#define _PROCESSPRIO_H
+
+#include "Configuration/Config.h"
+
+#ifdef __linux__
+#include <sched.h>
+#include <sys/resource.h>
+#define PROCESS_HIGH_PRIORITY -15 // [-20, 19], default is 0
+#endif
+
+void SetProcessPriority(const std::string& logChannel)
+{
+// Suppresses Mac OS X Warning since logChannel isn't used.
+#if PLATFORM_APPLE
+ (void)logChannel;
+#endif
+
+#if defined(_WIN32) || defined(__linux__)
+
+ ///- Handle affinity for multiple processors and process priority
+ uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0);
+ bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false);
+
+#ifdef _WIN32 // Windows
+
+ HANDLE hProcess = GetCurrentProcess();
+ if (affinity > 0)
+ {
+ ULONG_PTR appAff;
+ ULONG_PTR sysAff;
+
+ if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
+ {
+ // remove non accessible processors
+ ULONG_PTR currentAffinity = affinity & appAff;
+
+ if (!currentAffinity)
+ TC_LOG_ERROR(logChannel, "Processors marked in UseProcessors bitmask (hex) %x are not accessible. Accessible processors bitmask (hex): %x", affinity, appAff);
+ else if (SetProcessAffinityMask(hProcess, currentAffinity))
+ TC_LOG_INFO(logChannel, "Using processors (bitmask, hex): %x", currentAffinity);
+ else
+ TC_LOG_ERROR(logChannel, "Can't set used processors (hex): %x", currentAffinity);
+ }
+ }
+
+ if (highPriority)
+ {
+ if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
+ TC_LOG_INFO(logChannel, "Process priority class set to HIGH");
+ else
+ TC_LOG_ERROR(logChannel, "Can't set process priority class.");
+ }
+
+#else // Linux
+
+ if (affinity > 0)
+ {
+ cpu_set_t mask;
+ CPU_ZERO(&mask);
+
+ for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i)
+ if (affinity & (1 << i))
+ CPU_SET(i, &mask);
+
+ if (sched_setaffinity(0, sizeof(mask), &mask))
+ TC_LOG_ERROR(logChannel, "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno));
+ else
+ {
+ CPU_ZERO(&mask);
+ sched_getaffinity(0, sizeof(mask), &mask);
+ TC_LOG_INFO(logChannel, "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask));
+ }
+ }
+
+ if (highPriority)
+ {
+ if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY))
+ TC_LOG_ERROR(logChannel, "Can't set process priority class, error: %s", strerror(errno));
+ else
+ TC_LOG_INFO(logChannel, "Process priority class set to %i", getpriority(PRIO_PROCESS, 0));
+ }
+
+#endif
+#endif
+}
+
+#endif
diff --git a/src/common/Threading/ProducerConsumerQueue.h b/src/common/Threading/ProducerConsumerQueue.h
new file mode 100644
index 00000000000..96546960393
--- /dev/null
+++ b/src/common/Threading/ProducerConsumerQueue.h
@@ -0,0 +1,112 @@
+/*
+* Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PCQ_H
+#define _PCQ_H
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <atomic>
+#include <type_traits>
+
+template <typename T>
+class ProducerConsumerQueue
+{
+private:
+ std::mutex _queueLock;
+ std::queue<T> _queue;
+ std::condition_variable _condition;
+ std::atomic<bool> _shutdown;
+
+public:
+
+ ProducerConsumerQueue<T>() : _shutdown(false) { }
+
+ void Push(const T& value)
+ {
+ std::lock_guard<std::mutex> lock(_queueLock);
+ _queue.push(std::move(value));
+
+ _condition.notify_one();
+ }
+
+ bool Empty()
+ {
+ std::lock_guard<std::mutex> lock(_queueLock);
+
+ return _queue.empty();
+ }
+
+ bool Pop(T& value)
+ {
+ std::lock_guard<std::mutex> lock(_queueLock);
+
+ if (_queue.empty() || _shutdown)
+ return false;
+
+ value = _queue.front();
+
+ _queue.pop();
+
+ return true;
+ }
+
+ void WaitAndPop(T& value)
+ {
+ std::unique_lock<std::mutex> lock(_queueLock);
+
+ // we could be using .wait(lock, predicate) overload here but it is broken
+ // https://connect.microsoft.com/VisualStudio/feedback/details/1098841
+ while (_queue.empty() && !_shutdown)
+ _condition.wait(lock);
+
+ if (_queue.empty() || _shutdown)
+ return;
+
+ value = _queue.front();
+
+ _queue.pop();
+ }
+
+ void Cancel()
+ {
+ std::unique_lock<std::mutex> lock(_queueLock);
+
+ while (!_queue.empty())
+ {
+ T& value = _queue.front();
+
+ DeleteQueuedObject(value);
+
+ _queue.pop();
+ }
+
+ _shutdown = true;
+
+ _condition.notify_all();
+ }
+
+private:
+ template<typename E = T>
+ typename std::enable_if<std::is_pointer<E>::value>::type DeleteQueuedObject(E& obj) { delete obj; }
+
+ template<typename E = T>
+ typename std::enable_if<!std::is_pointer<E>::value>::type DeleteQueuedObject(E const& /*packet*/) { }
+};
+
+#endif
diff --git a/src/common/Utilities/ByteConverter.h b/src/common/Utilities/ByteConverter.h
new file mode 100644
index 00000000000..a077de3eb0b
--- /dev/null
+++ b/src/common/Utilities/ByteConverter.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 TRINITY_BYTECONVERTER_H
+#define TRINITY_BYTECONVERTER_H
+
+/** ByteConverter reverse your byte order. This is use
+ for cross platform where they have different endians.
+ */
+
+#include "Define.h"
+#include <algorithm>
+
+namespace ByteConverter
+{
+ template<size_t T>
+ inline void convert(char *val)
+ {
+ std::swap(*val, *(val + T - 1));
+ convert<T - 2>(val + 1);
+ }
+
+ template<> inline void convert<0>(char *) { }
+ template<> inline void convert<1>(char *) { } // ignore central byte
+
+ template<typename T> inline void apply(T *val)
+ {
+ convert<sizeof(T)>((char *)(val));
+ }
+}
+
+#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
+template<typename T> inline void EndianConvert(T& val) { ByteConverter::apply<T>(&val); }
+template<typename T> inline void EndianConvertReverse(T&) { }
+template<typename T> inline void EndianConvertPtr(void* val) { ByteConverter::apply<T>(val); }
+template<typename T> inline void EndianConvertPtrReverse(void*) { }
+#else
+template<typename T> inline void EndianConvert(T&) { }
+template<typename T> inline void EndianConvertReverse(T& val) { ByteConverter::apply<T>(&val); }
+template<typename T> inline void EndianConvertPtr(void*) { }
+template<typename T> inline void EndianConvertPtrReverse(void* val) { ByteConverter::apply<T>(val); }
+#endif
+
+template<typename T> void EndianConvert(T*); // will generate link error
+template<typename T> void EndianConvertReverse(T*); // will generate link error
+
+inline void EndianConvert(uint8&) { }
+inline void EndianConvert( int8&) { }
+inline void EndianConvertReverse(uint8&) { }
+inline void EndianConvertReverse( int8&) { }
+
+#endif
+
diff --git a/src/common/Utilities/Duration.h b/src/common/Utilities/Duration.h
new file mode 100644
index 00000000000..58a08e5842f
--- /dev/null
+++ b/src/common/Utilities/Duration.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _DURATION_H_
+#define _DURATION_H_
+
+#include <chrono>
+
+/// Milliseconds shorthand typedef.
+typedef std::chrono::milliseconds Milliseconds;
+
+/// Seconds shorthand typedef.
+typedef std::chrono::seconds Seconds;
+
+/// Minutes shorthand typedef.
+typedef std::chrono::minutes Minutes;
+
+/// Hours shorthand typedef.
+typedef std::chrono::hours Hours;
+
+/// Makes std::chrono_literals globally available.
+// ToDo: Enable this when TC supports C++14.
+// using namespace std::chrono_literals;
+
+#endif // _DURATION_H_
diff --git a/src/common/Utilities/EventMap.cpp b/src/common/Utilities/EventMap.cpp
new file mode 100644
index 00000000000..8c3f60afe82
--- /dev/null
+++ b/src/common/Utilities/EventMap.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "EventMap.h"
+
+void EventMap::Reset()
+{
+ _eventMap.clear();
+ _time = 0;
+ _phase = 0;
+}
+
+void EventMap::SetPhase(uint8 phase)
+{
+ if (!phase)
+ _phase = 0;
+ else if (phase <= 8)
+ _phase = uint8(1 << (phase - 1));
+}
+
+void EventMap::ScheduleEvent(uint32 eventId, uint32 time, uint32 group /*= 0*/, uint8 phase /*= 0*/)
+{
+ if (group && group <= 8)
+ eventId |= (1 << (group + 15));
+
+ if (phase && phase <= 8)
+ eventId |= (1 << (phase + 23));
+
+ _eventMap.insert(EventStore::value_type(_time + time, eventId));
+}
+
+uint32 EventMap::ExecuteEvent()
+{
+ while (!Empty())
+ {
+ EventStore::iterator itr = _eventMap.begin();
+
+ if (itr->first > _time)
+ return 0;
+ else if (_phase && (itr->second & 0xFF000000) && !((itr->second >> 24) & _phase))
+ _eventMap.erase(itr);
+ else
+ {
+ uint32 eventId = (itr->second & 0x0000FFFF);
+ _lastEvent = itr->second; // include phase/group
+ _eventMap.erase(itr);
+ return eventId;
+ }
+ }
+
+ return 0;
+}
+
+void EventMap::DelayEvents(uint32 delay, uint32 group)
+{
+ if (!group || group > 8 || Empty())
+ return;
+
+ EventStore delayed;
+
+ for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();)
+ {
+ if (itr->second & (1 << (group + 15)))
+ {
+ delayed.insert(EventStore::value_type(itr->first + delay, itr->second));
+ _eventMap.erase(itr++);
+ }
+ else
+ ++itr;
+ }
+
+ _eventMap.insert(delayed.begin(), delayed.end());
+}
+
+void EventMap::CancelEvent(uint32 eventId)
+{
+ if (Empty())
+ return;
+
+ for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();)
+ {
+ if (eventId == (itr->second & 0x0000FFFF))
+ _eventMap.erase(itr++);
+ else
+ ++itr;
+ }
+}
+
+void EventMap::CancelEventGroup(uint32 group)
+{
+ if (!group || group > 8 || Empty())
+ return;
+
+ for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();)
+ {
+ if (itr->second & (1 << (group + 15)))
+ _eventMap.erase(itr++);
+ else
+ ++itr;
+ }
+}
+
+uint32 EventMap::GetNextEventTime(uint32 eventId) const
+{
+ if (Empty())
+ return 0;
+
+ for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr)
+ if (eventId == (itr->second & 0x0000FFFF))
+ return itr->first;
+
+ return 0;
+}
+
+uint32 EventMap::GetTimeUntilEvent(uint32 eventId) const
+{
+ for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr)
+ if (eventId == (itr->second & 0x0000FFFF))
+ return itr->first - _time;
+
+ return std::numeric_limits<uint32>::max();
+}
diff --git a/src/common/Utilities/EventMap.h b/src/common/Utilities/EventMap.h
new file mode 100644
index 00000000000..021dffc4940
--- /dev/null
+++ b/src/common/Utilities/EventMap.h
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _EVENT_MAP_H_
+#define _EVENT_MAP_H_
+
+#include "Common.h"
+#include "Duration.h"
+#include "Util.h"
+
+class EventMap
+{
+ /**
+ * Internal storage type.
+ * Key: Time as uint32 when the event should occur.
+ * Value: The event data as uint32.
+ *
+ * Structure of event data:
+ * - Bit 0 - 15: Event Id.
+ * - Bit 16 - 23: Group
+ * - Bit 24 - 31: Phase
+ * - Pattern: 0xPPGGEEEE
+ */
+ typedef std::multimap<uint32, uint32> EventStore;
+
+public:
+ EventMap() : _time(0), _phase(0), _lastEvent(0) { }
+
+ /**
+ * @name Reset
+ * @brief Removes all scheduled events and resets time and phase.
+ */
+ void Reset();
+
+ /**
+ * @name Update
+ * @brief Updates the timer of the event map.
+ * @param time Value in ms to be added to time.
+ */
+ void Update(uint32 time)
+ {
+ _time += time;
+ }
+
+ /**
+ * @name GetTimer
+ * @return Current timer in ms value.
+ */
+ uint32 GetTimer() const
+ {
+ return _time;
+ }
+
+ /**
+ * @name GetPhaseMask
+ * @return Active phases as mask.
+ */
+ uint8 GetPhaseMask() const
+ {
+ return _phase;
+ }
+
+ /**
+ * @name Empty
+ * @return True, if there are no events scheduled.
+ */
+ bool Empty() const
+ {
+ return _eventMap.empty();
+ }
+
+ /**
+ * @name SetPhase
+ * @brief Sets the phase of the map (absolute).
+ * @param phase Phase which should be set. Values: 1 - 8. 0 resets phase.
+ */
+ void SetPhase(uint8 phase);
+
+ /**
+ * @name AddPhase
+ * @brief Activates the given phase (bitwise).
+ * @param phase Phase which should be activated. Values: 1 - 8
+ */
+ void AddPhase(uint8 phase)
+ {
+ if (phase && phase <= 8)
+ _phase |= uint8(1 << (phase - 1));
+ }
+
+ /**
+ * @name RemovePhase
+ * @brief Deactivates the given phase (bitwise).
+ * @param phase Phase which should be deactivated. Values: 1 - 8.
+ */
+ void RemovePhase(uint8 phase)
+ {
+ if (phase && phase <= 8)
+ _phase &= uint8(~(1 << (phase - 1)));
+ }
+
+ /**
+ * @name ScheduleEvent
+ * @brief Creates new event entry in map.
+ * @param eventId The id of the new event.
+ * @param time The time in milliseconds as std::chrono::duration until the event occurs.
+ * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group.
+ * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases.
+ */
+ void ScheduleEvent(uint32 eventId, Milliseconds const& time, uint32 group = 0, uint8 phase = 0)
+ {
+ ScheduleEvent(eventId, time.count(), group, phase);
+ }
+
+ /**
+ * @name ScheduleEvent
+ * @brief Creates new event entry in map.
+ * @param eventId The id of the new event.
+ * @param time The time in milliseconds until the event occurs.
+ * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group.
+ * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases.
+ */
+ void ScheduleEvent(uint32 eventId, uint32 time, uint32 group = 0, uint8 phase = 0);
+
+ /**
+ * @name RescheduleEvent
+ * @brief Cancels the given event and reschedules it.
+ * @param eventId The id of the event.
+ * @param time The time in milliseconds as std::chrono::duration until the event occurs.
+ * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group.
+ * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases.
+ */
+ void RescheduleEvent(uint32 eventId, Milliseconds const& time, uint32 group = 0, uint8 phase = 0)
+ {
+ RescheduleEvent(eventId, time.count(), group, phase);
+ }
+
+ /**
+ * @name RescheduleEvent
+ * @brief Cancels the given event and reschedules it.
+ * @param eventId The id of the event.
+ * @param time The time in milliseconds until the event occurs.
+ * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group.
+ * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases.
+ */
+ void RescheduleEvent(uint32 eventId, uint32 time, uint32 group = 0, uint8 phase = 0)
+ {
+ CancelEvent(eventId);
+ ScheduleEvent(eventId, time, group, phase);
+ }
+
+ /**
+ * @name RepeatEvent
+ * @brief Repeats the mostly recently executed event.
+ * @param time Time until in milliseconds as std::chrono::duration the event occurs.
+ */
+ void Repeat(Milliseconds const& time)
+ {
+ Repeat(time.count());
+ }
+
+ /**
+ * @name RepeatEvent
+ * @brief Repeats the mostly recently executed event.
+ * @param time Time until the event occurs.
+ */
+ void Repeat(uint32 time)
+ {
+ _eventMap.insert(EventStore::value_type(_time + time, _lastEvent));
+ }
+
+ /**
+ * @name RepeatEvent
+ * @brief Repeats the mostly recently executed event.
+ * @param minTime Minimum time as std::chrono::duration until the event occurs.
+ * @param maxTime Maximum time as std::chrono::duration until the event occurs.
+ */
+ void Repeat(Milliseconds const& minTime, Milliseconds const& maxTime)
+ {
+ Repeat(minTime.count(), maxTime.count());
+ }
+
+ /**
+ * @name RepeatEvent
+ * @brief Repeats the mostly recently executed event, Equivalent to Repeat(urand(minTime, maxTime).
+ * @param minTime Minimum time until the event occurs.
+ * @param maxTime Maximum time until the event occurs.
+ */
+ void Repeat(uint32 minTime, uint32 maxTime)
+ {
+ Repeat(urand(minTime, maxTime));
+ }
+
+ /**
+ * @name ExecuteEvent
+ * @brief Returns the next event to execute and removes it from map.
+ * @return Id of the event to execute.
+ */
+ uint32 ExecuteEvent();
+
+ /**
+ * @name DelayEvents
+ * @brief Delays all events in the map. If delay is greater than or equal internal timer, delay will be 0.
+ * @param delay Amount of delay in ms as std::chrono::duration.
+ */
+ void DelayEvents(Milliseconds const& delay)
+ {
+ DelayEvents(delay.count());
+ }
+
+ /**
+ * @name DelayEvents
+ * @brief Delays all events in the map. If delay is greater than or equal internal timer, delay will be 0.
+ * @param delay Amount of delay.
+ */
+ void DelayEvents(uint32 delay)
+ {
+ _time = delay < _time ? _time - delay : 0;
+ }
+
+ /**
+ * @name DelayEvents
+ * @brief Delay all events of the same group.
+ * @param delay Amount of delay in ms as std::chrono::duration.
+ * @param group Group of the events.
+ */
+ void DelayEvents(Milliseconds const& delay, uint32 group)
+ {
+ DelayEvents(delay.count(), group);
+ }
+
+ /**
+ * @name DelayEvents
+ * @brief Delay all events of the same group.
+ * @param delay Amount of delay.
+ * @param group Group of the events.
+ */
+ void DelayEvents(uint32 delay, uint32 group);
+
+ /**
+ * @name CancelEvent
+ * @brief Cancels all events of the specified id.
+ * @param eventId Event id to cancel.
+ */
+ void CancelEvent(uint32 eventId);
+
+ /**
+ * @name CancelEventGroup
+ * @brief Cancel events belonging to specified group.
+ * @param group Group to cancel.
+ */
+ void CancelEventGroup(uint32 group);
+
+ /**
+ * @name GetNextEventTime
+ * @brief Returns closest occurence of specified event.
+ * @param eventId Wanted event id.
+ * @return Time of found event.
+ */
+ uint32 GetNextEventTime(uint32 eventId) const;
+
+ /**
+ * @name GetNextEventTime
+ * @return Time of next event.
+ */
+ uint32 GetNextEventTime() const
+ {
+ return Empty() ? 0 : _eventMap.begin()->first;
+ }
+
+ /**
+ * @name IsInPhase
+ * @brief Returns whether event map is in specified phase or not.
+ * @param phase Wanted phase.
+ * @return True, if phase of event map contains specified phase.
+ */
+ bool IsInPhase(uint8 phase) const
+ {
+ return phase <= 8 && (!phase || _phase & (1 << (phase - 1)));
+ }
+
+ /**
+ * @name GetTimeUntilEvent
+ * @brief Returns time in milliseconds until next event.
+ * @param eventId of the event.
+ * @return Time of next event.
+ */
+ uint32 GetTimeUntilEvent(uint32 eventId) const;
+
+private:
+ /**
+ * @name _time
+ * @brief Internal timer.
+ *
+ * This does not represent the real date/time value.
+ * It's more like a stopwatch: It can run, it can be stopped,
+ * it can be resetted and so on. Events occur when this timer
+ * has reached their time value. Its value is changed in the
+ * Update method.
+ */
+ uint32 _time;
+
+ /**
+ * @name _phase
+ * @brief Phase mask of the event map.
+ *
+ * Contains the phases the event map is in. Multiple
+ * phases from 1 to 8 can be set with SetPhase or
+ * AddPhase. RemovePhase deactives a phase.
+ */
+ uint8 _phase;
+
+ /**
+ * @name _eventMap
+ * @brief Internal event storage map. Contains the scheduled events.
+ *
+ * See typedef at the beginning of the class for more
+ * details.
+ */
+ EventStore _eventMap;
+
+ /**
+ * @name _lastEvent
+ * @brief Stores information on the most recently executed event
+ */
+ uint32 _lastEvent;
+};
+
+#endif // _EVENT_MAP_H_
diff --git a/src/common/Utilities/EventProcessor.cpp b/src/common/Utilities/EventProcessor.cpp
new file mode 100644
index 00000000000..34695665443
--- /dev/null
+++ b/src/common/Utilities/EventProcessor.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "EventProcessor.h"
+
+EventProcessor::EventProcessor()
+{
+ m_time = 0;
+ m_aborting = false;
+}
+
+EventProcessor::~EventProcessor()
+{
+ KillAllEvents(true);
+}
+
+void EventProcessor::Update(uint32 p_time)
+{
+ // update time
+ m_time += p_time;
+
+ // main event loop
+ EventList::iterator i;
+ while (((i = m_events.begin()) != m_events.end()) && i->first <= m_time)
+ {
+ // get and remove event from queue
+ BasicEvent* Event = i->second;
+ m_events.erase(i);
+
+ if (!Event->to_Abort)
+ {
+ if (Event->Execute(m_time, p_time))
+ {
+ // completely destroy event if it is not re-added
+ delete Event;
+ }
+ }
+ else
+ {
+ Event->Abort(m_time);
+ delete Event;
+ }
+ }
+}
+
+void EventProcessor::KillAllEvents(bool force)
+{
+ // prevent event insertions
+ m_aborting = true;
+
+ // first, abort all existing events
+ for (EventList::iterator i = m_events.begin(); i != m_events.end();)
+ {
+ EventList::iterator i_old = i;
+ ++i;
+
+ i_old->second->to_Abort = true;
+ i_old->second->Abort(m_time);
+ if (force || i_old->second->IsDeletable())
+ {
+ delete i_old->second;
+
+ if (!force) // need per-element cleanup
+ m_events.erase (i_old);
+ }
+ }
+
+ // fast clear event list (in force case)
+ if (force)
+ m_events.clear();
+}
+
+void EventProcessor::AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime)
+{
+ if (set_addtime) Event->m_addTime = m_time;
+ Event->m_execTime = e_time;
+ m_events.insert(std::pair<uint64, BasicEvent*>(e_time, Event));
+}
+
+uint64 EventProcessor::CalculateTime(uint64 t_offset) const
+{
+ return(m_time + t_offset);
+}
+
diff --git a/src/common/Utilities/EventProcessor.h b/src/common/Utilities/EventProcessor.h
new file mode 100644
index 00000000000..3d54bd6f9f2
--- /dev/null
+++ b/src/common/Utilities/EventProcessor.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 __EVENTPROCESSOR_H
+#define __EVENTPROCESSOR_H
+
+#include "Define.h"
+
+#include <map>
+
+// Note. All times are in milliseconds here.
+
+class BasicEvent
+{
+ public:
+ BasicEvent()
+ {
+ to_Abort = false;
+ m_addTime = 0;
+ m_execTime = 0;
+ }
+ virtual ~BasicEvent() { } // override destructor to perform some actions on event removal
+
+ // this method executes when the event is triggered
+ // return false if event does not want to be deleted
+ // e_time is execution time, p_time is update interval
+ virtual bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) { return true; }
+
+ virtual bool IsDeletable() const { return true; } // this event can be safely deleted
+
+ virtual void Abort(uint64 /*e_time*/) { } // this method executes when the event is aborted
+
+ bool to_Abort; // set by externals when the event is aborted, aborted events don't execute
+ // and get Abort call when deleted
+
+ // these can be used for time offset control
+ uint64 m_addTime; // time when the event was added to queue, filled by event handler
+ uint64 m_execTime; // planned time of next execution, filled by event handler
+};
+
+typedef std::multimap<uint64, BasicEvent*> EventList;
+
+class EventProcessor
+{
+ public:
+ EventProcessor();
+ ~EventProcessor();
+
+ void Update(uint32 p_time);
+ void KillAllEvents(bool force);
+ void AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime = true);
+ uint64 CalculateTime(uint64 t_offset) const;
+ protected:
+ uint64 m_time;
+ EventList m_events;
+ bool m_aborting;
+};
+#endif
diff --git a/src/common/Utilities/StringFormat.h b/src/common/Utilities/StringFormat.h
new file mode 100644
index 00000000000..67e0100e7c8
--- /dev/null
+++ b/src/common/Utilities/StringFormat.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 TRINITYCORE_STRING_FORMAT_H
+#define TRINITYCORE_STRING_FORMAT_H
+
+#include "format.h"
+
+namespace Trinity
+{
+ /// Default TC string format function.
+ template<typename Format, typename... Args>
+ inline std::string StringFormat(Format&& fmt, Args&&... args)
+ {
+ return fmt::sprintf(std::forward<Format>(fmt), std::forward<Args>(args)...);
+ }
+
+ /// Returns true if the given char pointer is null.
+ inline bool IsFormatEmptyOrNull(const char* fmt)
+ {
+ return fmt == nullptr;
+ }
+
+ /// Returns true if the given std::string is empty.
+ inline bool IsFormatEmptyOrNull(std::string const& fmt)
+ {
+ return fmt.empty();
+ }
+}
+
+#endif
diff --git a/src/common/Utilities/TaskScheduler.cpp b/src/common/Utilities/TaskScheduler.cpp
new file mode 100644
index 00000000000..801cc96cf77
--- /dev/null
+++ b/src/common/Utilities/TaskScheduler.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TaskScheduler.h"
+
+TaskScheduler& TaskScheduler::ClearValidator()
+{
+ _predicate = EmptyValidator;
+ return *this;
+}
+
+TaskScheduler& TaskScheduler::Update(success_t const& callback)
+{
+ _now = clock_t::now();
+ Dispatch(callback);
+ return *this;
+}
+
+TaskScheduler& TaskScheduler::Update(size_t const milliseconds, success_t const& callback)
+{
+ return Update(std::chrono::milliseconds(milliseconds), callback);
+}
+
+TaskScheduler& TaskScheduler::Async(std::function<void()> const& callable)
+{
+ _asyncHolder.push(callable);
+ return *this;
+}
+
+TaskScheduler& TaskScheduler::CancelAll()
+{
+ /// Clear the task holder
+ _task_holder.Clear();
+ _asyncHolder = AsyncHolder();
+ return *this;
+}
+
+TaskScheduler& TaskScheduler::CancelGroup(group_t const group)
+{
+ _task_holder.RemoveIf([group](TaskContainer const& task) -> bool
+ {
+ return task->IsInGroup(group);
+ });
+ return *this;
+}
+
+TaskScheduler& TaskScheduler::CancelGroupsOf(std::vector<group_t> const& groups)
+{
+ std::for_each(groups.begin(), groups.end(),
+ std::bind(&TaskScheduler::CancelGroup, this, std::placeholders::_1));
+
+ return *this;
+}
+
+TaskScheduler& TaskScheduler::InsertTask(TaskContainer task)
+{
+ _task_holder.Push(std::move(task));
+ return *this;
+}
+
+void TaskScheduler::Dispatch(success_t const& callback)
+{
+ // If the validation failed abort the dispatching here.
+ if (!_predicate())
+ return;
+
+ // Process all asyncs
+ while (!_asyncHolder.empty())
+ {
+ _asyncHolder.front()();
+ _asyncHolder.pop();
+
+ // If the validation failed abort the dispatching here.
+ if (!_predicate())
+ return;
+ }
+
+ while (!_task_holder.IsEmpty())
+ {
+ if (_task_holder.First()->_end > _now)
+ break;
+
+ // Perfect forward the context to the handler
+ // Use weak references to catch destruction before callbacks.
+ TaskContext context(_task_holder.Pop(), std::weak_ptr<TaskScheduler>(self_reference));
+
+ // Invoke the context
+ context.Invoke();
+
+ // If the validation failed abort the dispatching here.
+ if (!_predicate())
+ return;
+ }
+
+ // On finish call the final callback
+ callback();
+}
+
+void TaskScheduler::TaskQueue::Push(TaskContainer&& task)
+{
+ container.insert(task);
+}
+
+auto TaskScheduler::TaskQueue::Pop() -> TaskContainer
+{
+ TaskContainer result = *container.begin();
+ container.erase(container.begin());
+ return result;
+}
+
+auto TaskScheduler::TaskQueue::First() const -> TaskContainer const&
+{
+ return *container.begin();
+}
+
+void TaskScheduler::TaskQueue::Clear()
+{
+ container.clear();
+}
+
+void TaskScheduler::TaskQueue::RemoveIf(std::function<bool(TaskContainer const&)> const& filter)
+{
+ for (auto itr = container.begin(); itr != container.end();)
+ if (filter(*itr))
+ itr = container.erase(itr);
+ else
+ ++itr;
+}
+
+void TaskScheduler::TaskQueue::ModifyIf(std::function<bool(TaskContainer const&)> const& filter)
+{
+ std::vector<TaskContainer> cache;
+ for (auto itr = container.begin(); itr != container.end();)
+ if (filter(*itr))
+ {
+ cache.push_back(*itr);
+ itr = container.erase(itr);
+ }
+ else
+ ++itr;
+
+ container.insert(cache.begin(), cache.end());
+}
+
+bool TaskScheduler::TaskQueue::IsEmpty() const
+{
+ return container.empty();
+}
+
+TaskContext& TaskContext::Dispatch(std::function<TaskScheduler&(TaskScheduler&)> const& apply)
+{
+ if (auto const owner = _owner.lock())
+ apply(*owner);
+
+ return *this;
+}
+
+bool TaskContext::IsExpired() const
+{
+ return _owner.expired();
+}
+
+bool TaskContext::IsInGroup(TaskScheduler::group_t const group) const
+{
+ return _task->IsInGroup(group);
+}
+
+TaskContext& TaskContext::SetGroup(TaskScheduler::group_t const group)
+{
+ _task->_group = group;
+ return *this;
+}
+
+TaskContext& TaskContext::ClearGroup()
+{
+ _task->_group = boost::none;
+ return *this;
+}
+
+TaskScheduler::repeated_t TaskContext::GetRepeatCounter() const
+{
+ return _task->_repeated;
+}
+
+TaskContext& TaskContext::Async(std::function<void()> const& callable)
+{
+ return Dispatch(std::bind(&TaskScheduler::Async, std::placeholders::_1, callable));
+}
+
+TaskContext& TaskContext::CancelAll()
+{
+ return Dispatch(std::mem_fn(&TaskScheduler::CancelAll));
+}
+
+TaskContext& TaskContext::CancelGroup(TaskScheduler::group_t const group)
+{
+ return Dispatch(std::bind(&TaskScheduler::CancelGroup, std::placeholders::_1, group));
+}
+
+TaskContext& TaskContext::CancelGroupsOf(std::vector<TaskScheduler::group_t> const& groups)
+{
+ return Dispatch(std::bind(&TaskScheduler::CancelGroupsOf, std::placeholders::_1, std::cref(groups)));
+}
+
+void TaskContext::AssertOnConsumed() const
+{
+ // This was adapted to TC to prevent static analysis tools from complaining.
+ // If you encounter this assertion check if you repeat a TaskContext more then 1 time!
+ ASSERT(!(*_consumed) && "Bad task logic, task context was consumed already!");
+}
+
+void TaskContext::Invoke()
+{
+ _task->_task(*this);
+}
diff --git a/src/common/Utilities/TaskScheduler.h b/src/common/Utilities/TaskScheduler.h
new file mode 100644
index 00000000000..f1fe7ea0a21
--- /dev/null
+++ b/src/common/Utilities/TaskScheduler.h
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _TASK_SCHEDULER_H_
+#define _TASK_SCHEDULER_H_
+
+#include <algorithm>
+#include <chrono>
+#include <vector>
+#include <queue>
+#include <memory>
+#include <utility>
+#include <set>
+
+#include <boost/optional.hpp>
+
+#include "Util.h"
+#include "Duration.h"
+
+class TaskContext;
+
+/// The TaskScheduler class provides the ability to schedule std::function's in the near future.
+/// Use TaskScheduler::Update to update the scheduler.
+/// Popular methods are:
+/// * Schedule (Schedules a std::function which will be executed in the near future).
+/// * Schedules an asynchronous function which will be executed at the next update tick.
+/// * Cancel, Delay & Reschedule (Methods to manipulate already scheduled tasks).
+/// Tasks are organized in groups (uint), multiple tasks can have the same group id,
+/// you can provide a group or not, but keep in mind that you can only manipulate specific tasks through its group id!
+/// Tasks callbacks use the function signature void(TaskContext) where TaskContext provides
+/// access to the function schedule plan which makes it possible to repeat the task
+/// with the same duration or a new one.
+/// It also provides access to the repeat counter which is useful for task that repeat itself often
+/// but behave different every time (spoken event dialogs for example).
+class TaskScheduler
+{
+ friend class TaskContext;
+
+ // Time definitions (use steady clock)
+ typedef std::chrono::steady_clock clock_t;
+ typedef clock_t::time_point timepoint_t;
+ typedef clock_t::duration duration_t;
+
+ // Task group type
+ typedef uint32 group_t;
+ // Task repeated type
+ typedef uint32 repeated_t;
+ // Task handle type
+ typedef std::function<void(TaskContext)> task_handler_t;
+ // Predicate type
+ typedef std::function<bool()> predicate_t;
+ // Success handle type
+ typedef std::function<void()> success_t;
+
+ class Task
+ {
+ friend class TaskContext;
+ friend class TaskScheduler;
+
+ timepoint_t _end;
+ duration_t _duration;
+ boost::optional<group_t> _group;
+ repeated_t _repeated;
+ task_handler_t _task;
+
+ public:
+ // All Argument construct
+ Task(timepoint_t const& end, duration_t const& duration, boost::optional<group_t> const& group,
+ repeated_t const repeated, task_handler_t const& task)
+ : _end(end), _duration(duration), _group(group), _repeated(repeated), _task(task) { }
+
+ // Minimal Argument construct
+ Task(timepoint_t const& end, duration_t const& duration, task_handler_t const& task)
+ : _end(end), _duration(duration), _group(boost::none), _repeated(0), _task(task) { }
+
+ // Copy construct
+ Task(Task const&) = delete;
+ // Move construct
+ Task(Task&&) = delete;
+ // Copy Assign
+ Task& operator= (Task const&) = default;
+ // Move Assign
+ Task& operator= (Task&& right) = delete;
+
+ // Order tasks by its end
+ inline bool operator< (Task const& other) const
+ {
+ return _end < other._end;
+ }
+
+ inline bool operator> (Task const& other) const
+ {
+ return _end > other._end;
+ }
+
+ // Compare tasks with its end
+ inline bool operator== (Task const& other)
+ {
+ return _end == other._end;
+ }
+
+ // Returns true if the task is in the given group
+ inline bool IsInGroup(group_t const group) const
+ {
+ return _group == group;
+ }
+ };
+
+ typedef std::shared_ptr<Task> TaskContainer;
+
+ /// Container which provides Task order, insert and reschedule operations.
+ struct Compare
+ {
+ bool operator() (TaskContainer const& left, TaskContainer const& right)
+ {
+ return (*left.get()) < (*right.get());
+ };
+ };
+
+ class TaskQueue
+ {
+ std::multiset<TaskContainer, Compare> container;
+
+ public:
+ // Pushes the task in the container
+ void Push(TaskContainer&& task);
+
+ /// Pops the task out of the container
+ TaskContainer Pop();
+
+ TaskContainer const& First() const;
+
+ void Clear();
+
+ void RemoveIf(std::function<bool(TaskContainer const&)> const& filter);
+
+ void ModifyIf(std::function<bool(TaskContainer const&)> const& filter);
+
+ bool IsEmpty() const;
+ };
+
+ /// Contains a self reference to track if this object was deleted or not.
+ std::shared_ptr<TaskScheduler> self_reference;
+
+ /// The current time point (now)
+ timepoint_t _now;
+
+ /// The Task Queue which contains all task objects.
+ TaskQueue _task_holder;
+
+ typedef std::queue<std::function<void()>> AsyncHolder;
+
+ /// Contains all asynchronous tasks which will be invoked at
+ /// the next update tick.
+ AsyncHolder _asyncHolder;
+
+ predicate_t _predicate;
+
+ static bool EmptyValidator()
+ {
+ return true;
+ }
+
+ static void EmptyCallback()
+ {
+ }
+
+public:
+ TaskScheduler()
+ : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(EmptyValidator) { }
+
+ template<typename P>
+ TaskScheduler(P&& predicate)
+ : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(std::forward<P>(predicate)) { }
+
+ TaskScheduler(TaskScheduler const&) = delete;
+ TaskScheduler(TaskScheduler&&) = delete;
+ TaskScheduler& operator= (TaskScheduler const&) = delete;
+ TaskScheduler& operator= (TaskScheduler&&) = delete;
+
+ /// Sets a validator which is asked if tasks are allowed to be executed.
+ template<typename P>
+ TaskScheduler& SetValidator(P&& predicate)
+ {
+ _predicate = std::forward<P>(predicate);
+ return *this;
+ }
+
+ /// Clears the validator which is asked if tasks are allowed to be executed.
+ TaskScheduler& ClearValidator();
+
+ /// Update the scheduler to the current time.
+ /// Calls the optional callback on successfully finish.
+ TaskScheduler& Update(success_t const& callback = EmptyCallback);
+
+ /// Update the scheduler with a difftime in ms.
+ /// Calls the optional callback on successfully finish.
+ TaskScheduler& Update(size_t const milliseconds, success_t const& callback = EmptyCallback);
+
+ /// Update the scheduler with a difftime.
+ /// Calls the optional callback on successfully finish.
+ template<class _Rep, class _Period>
+ TaskScheduler& Update(std::chrono::duration<_Rep, _Period> const& difftime,
+ success_t const& callback = EmptyCallback)
+ {
+ _now += difftime;
+ Dispatch(callback);
+ return *this;
+ }
+
+ /// Schedule an callable function that is executed at the next update tick.
+ /// Its safe to modify the TaskScheduler from within the callable.
+ TaskScheduler& Async(std::function<void()> const& callable);
+
+ /// Schedule an event with a fixed rate.
+ /// Never call this from within a task context! Use TaskContext::Schedule instead!
+ template<class _Rep, class _Period>
+ TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time,
+ task_handler_t const& task)
+ {
+ return ScheduleAt(_now, time, task);
+ }
+
+ /// Schedule an event with a fixed rate.
+ /// Never call this from within a task context! Use TaskContext::Schedule instead!
+ template<class _Rep, class _Period>
+ TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time,
+ group_t const group, task_handler_t const& task)
+ {
+ return ScheduleAt(_now, time, group, task);
+ }
+
+ /// Schedule an event with a randomized rate between min and max rate.
+ /// Never call this from within a task context! Use TaskContext::Schedule instead!
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max, task_handler_t const& task)
+ {
+ return Schedule(RandomDurationBetween(min, max), task);
+ }
+
+ /// Schedule an event with a fixed rate.
+ /// Never call this from within a task context! Use TaskContext::Schedule instead!
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max, group_t const group,
+ task_handler_t const& task)
+ {
+ return Schedule(RandomDurationBetween(min, max), group, task);
+ }
+
+ /// Cancels all tasks.
+ /// Never call this from within a task context! Use TaskContext::CancelAll instead!
+ TaskScheduler& CancelAll();
+
+ /// Cancel all tasks of a single group.
+ /// Never call this from within a task context! Use TaskContext::CancelGroup instead!
+ TaskScheduler& CancelGroup(group_t const group);
+
+ /// Cancels all groups in the given std::vector.
+ /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}"
+ TaskScheduler& CancelGroupsOf(std::vector<group_t> const& groups);
+
+ /// Delays all tasks with the given duration.
+ template<class _Rep, class _Period>
+ TaskScheduler& DelayAll(std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ _task_holder.ModifyIf([&duration](TaskContainer const& task) -> bool
+ {
+ task->_end += duration;
+ return true;
+ });
+ return *this;
+ }
+
+ /// Delays all tasks with a random duration between min and max.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskScheduler& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return DelayAll(RandomDurationBetween(min, max));
+ }
+
+ /// Delays all tasks of a group with the given duration.
+ template<class _Rep, class _Period>
+ TaskScheduler& DelayGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ _task_holder.ModifyIf([&duration, group](TaskContainer const& task) -> bool
+ {
+ if (task->IsInGroup(group))
+ {
+ task->_end += duration;
+ return true;
+ }
+ else
+ return false;
+ });
+ return *this;
+ }
+
+ /// Delays all tasks of a group with a random duration between min and max.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskScheduler& DelayGroup(group_t const group,
+ std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return DelayGroup(group, RandomDurationBetween(min, max));
+ }
+
+ /// Reschedule all tasks with a given duration.
+ template<class _Rep, class _Period>
+ TaskScheduler& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ auto const end = _now + duration;
+ _task_holder.ModifyIf([end](TaskContainer const& task) -> bool
+ {
+ task->_end = end;
+ return true;
+ });
+ return *this;
+ }
+
+ /// Reschedule all tasks with a random duration between min and max.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskScheduler& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return RescheduleAll(RandomDurationBetween(min, max));
+ }
+
+ /// Reschedule all tasks of a group with the given duration.
+ template<class _Rep, class _Period>
+ TaskScheduler& RescheduleGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ auto const end = _now + duration;
+ _task_holder.ModifyIf([end, group](TaskContainer const& task) -> bool
+ {
+ if (task->IsInGroup(group))
+ {
+ task->_end = end;
+ return true;
+ }
+ else
+ return false;
+ });
+ return *this;
+ }
+
+ /// Reschedule all tasks of a group with a random duration between min and max.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskScheduler& RescheduleGroup(group_t const group,
+ std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return RescheduleGroup(group, RandomDurationBetween(min, max));
+ }
+
+private:
+ /// Insert a new task to the enqueued tasks.
+ TaskScheduler& InsertTask(TaskContainer task);
+
+ template<class _Rep, class _Period>
+ TaskScheduler& ScheduleAt(timepoint_t const& end,
+ std::chrono::duration<_Rep, _Period> const& time, task_handler_t const& task)
+ {
+ return InsertTask(TaskContainer(new Task(end + time, time, task)));
+ }
+
+ /// Schedule an event with a fixed rate.
+ /// Never call this from within a task context! Use TaskContext::schedule instead!
+ template<class _Rep, class _Period>
+ TaskScheduler& ScheduleAt(timepoint_t const& end,
+ std::chrono::duration<_Rep, _Period> const& time,
+ group_t const group, task_handler_t const& task)
+ {
+ static repeated_t const DEFAULT_REPEATED = 0;
+ return InsertTask(TaskContainer(new Task(end + time, time, group, DEFAULT_REPEATED, task)));
+ }
+
+ // Returns a random duration between min and max
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ static std::chrono::milliseconds
+ RandomDurationBetween(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ auto const milli_min = std::chrono::duration_cast<std::chrono::milliseconds>(min);
+ 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()));
+ }
+
+ /// Dispatch remaining tasks
+ void Dispatch(success_t const& callback);
+};
+
+class TaskContext
+{
+ friend class TaskScheduler;
+
+ /// Associated task
+ TaskScheduler::TaskContainer _task;
+
+ /// Owner
+ std::weak_ptr<TaskScheduler> _owner;
+
+ /// Marks the task as consumed
+ std::shared_ptr<bool> _consumed;
+
+ /// Dispatches an action safe on the TaskScheduler
+ TaskContext& Dispatch(std::function<TaskScheduler&(TaskScheduler&)> const& apply);
+
+public:
+ // Empty constructor
+ TaskContext()
+ : _task(), _owner(), _consumed(std::make_shared<bool>(true)) { }
+
+ // Construct from task and owner
+ explicit TaskContext(TaskScheduler::TaskContainer&& task, std::weak_ptr<TaskScheduler>&& owner)
+ : _task(task), _owner(owner), _consumed(std::make_shared<bool>(false)) { }
+
+ // Copy construct
+ TaskContext(TaskContext const& right)
+ : _task(right._task), _owner(right._owner), _consumed(right._consumed) { }
+
+ // Move construct
+ TaskContext(TaskContext&& right)
+ : _task(std::move(right._task)), _owner(std::move(right._owner)), _consumed(std::move(right._consumed)) { }
+
+ // Copy assign
+ TaskContext& operator= (TaskContext const& right)
+ {
+ _task = right._task;
+ _owner = right._owner;
+ _consumed = right._consumed;
+ return *this;
+ }
+
+ // Move assign
+ TaskContext& operator= (TaskContext&& right)
+ {
+ _task = std::move(right._task);
+ _owner = std::move(right._owner);
+ _consumed = std::move(right._consumed);
+ return *this;
+ }
+
+ /// Returns true if the owner was deallocated and this context has expired.
+ bool IsExpired() const;
+
+ /// Returns true if the event is in the given group
+ bool IsInGroup(TaskScheduler::group_t const group) const;
+
+ /// Sets the event in the given group
+ TaskContext& SetGroup(TaskScheduler::group_t const group);
+
+ /// Removes the group from the event
+ TaskContext& ClearGroup();
+
+ /// Returns the repeat counter which increases every time the task is repeated.
+ TaskScheduler::repeated_t GetRepeatCounter() const;
+
+ /// Repeats the event and sets a new duration.
+ /// std::chrono::seconds(5) for example.
+ /// This will consume the task context, its not possible to repeat the task again
+ /// from the same task context!
+ template<class _Rep, class _Period>
+ TaskContext& Repeat(std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ AssertOnConsumed();
+
+ // Set new duration, in-context timing and increment repeat counter
+ _task->_duration = duration;
+ _task->_end += duration;
+ _task->_repeated += 1;
+ (*_consumed) = true;
+ return Dispatch(std::bind(&TaskScheduler::InsertTask, std::placeholders::_1, _task));
+ }
+
+ /// Repeats the event with the same duration.
+ /// This will consume the task context, its not possible to repeat the task again
+ /// from the same task context!
+ TaskContext& Repeat()
+ {
+ return Repeat(_task->_duration);
+ }
+
+ /// Repeats the event and set a new duration that is randomized between min and max.
+ /// std::chrono::seconds(5) for example.
+ /// This will consume the task context, its not possible to repeat the task again
+ /// from the same task context!
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskContext& Repeat(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return Repeat(TaskScheduler::RandomDurationBetween(min, max));
+ }
+
+ /// Schedule a callable function that is executed at the next update tick from within the context.
+ /// Its safe to modify the TaskScheduler from within the callable.
+ TaskContext& Async(std::function<void()> const& callable);
+
+ /// Schedule an event with a fixed rate from within the context.
+ /// Its possible that the new event is executed immediately!
+ /// Use TaskScheduler::Async to create a task
+ /// which will be called at the next update tick.
+ template<class _Rep, class _Period>
+ TaskContext& Schedule(std::chrono::duration<_Rep, _Period> const& time,
+ TaskScheduler::task_handler_t const& task)
+ {
+ auto const end = _task->_end;
+ return Dispatch([end, time, task](TaskScheduler& scheduler) -> TaskScheduler&
+ {
+ return scheduler.ScheduleAt<_Rep, _Period>(end, time, task);
+ });
+ }
+
+ /// Schedule an event with a fixed rate from within the context.
+ /// Its possible that the new event is executed immediately!
+ /// Use TaskScheduler::Async to create a task
+ /// which will be called at the next update tick.
+ template<class _Rep, class _Period>
+ TaskContext& Schedule(std::chrono::duration<_Rep, _Period> const& time,
+ TaskScheduler::group_t const group, TaskScheduler::task_handler_t const& task)
+ {
+ auto const end = _task->_end;
+ return Dispatch([end, time, group, task](TaskScheduler& scheduler) -> TaskScheduler&
+ {
+ return scheduler.ScheduleAt<_Rep, _Period>(end, time, group, task);
+ });
+ }
+
+ /// Schedule an event with a randomized rate between min and max rate from within the context.
+ /// Its possible that the new event is executed immediately!
+ /// Use TaskScheduler::Async to create a task
+ /// which will be called at the next update tick.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskContext& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::task_handler_t const& task)
+ {
+ return Schedule(TaskScheduler::RandomDurationBetween(min, max), task);
+ }
+
+ /// Schedule an event with a randomized rate between min and max rate from within the context.
+ /// Its possible that the new event is executed immediately!
+ /// Use TaskScheduler::Async to create a task
+ /// which will be called at the next update tick.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskContext& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::group_t const group,
+ TaskScheduler::task_handler_t const& task)
+ {
+ return Schedule(TaskScheduler::RandomDurationBetween(min, max), group, task);
+ }
+
+ /// Cancels all tasks from within the context.
+ TaskContext& CancelAll();
+
+ /// Cancel all tasks of a single group from within the context.
+ TaskContext& CancelGroup(TaskScheduler::group_t const group);
+
+ /// Cancels all groups in the given std::vector from within the context.
+ /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}"
+ TaskContext& CancelGroupsOf(std::vector<TaskScheduler::group_t> const& groups);
+
+ /// Delays all tasks with the given duration from within the context.
+ template<class _Rep, class _Period>
+ TaskContext& DelayAll(std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ return Dispatch(std::bind(&TaskScheduler::DelayAll<_Rep, _Period>, std::placeholders::_1, duration));
+ }
+
+ /// Delays all tasks with a random duration between min and max from within the context.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskContext& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return DelayAll(TaskScheduler::RandomDurationBetween(min, max));
+ }
+
+ /// Delays all tasks of a group with the given duration from within the context.
+ template<class _Rep, class _Period>
+ TaskContext& DelayGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ return Dispatch(std::bind(&TaskScheduler::DelayGroup<_Rep, _Period>, std::placeholders::_1, group, duration));
+ }
+
+ /// Delays all tasks of a group with a random duration between min and max from within the context.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskContext& DelayGroup(TaskScheduler::group_t const group,
+ std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return DelayGroup(group, TaskScheduler::RandomDurationBetween(min, max));
+ }
+
+ /// Reschedule all tasks with the given duration.
+ template<class _Rep, class _Period>
+ TaskContext& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ return Dispatch(std::bind(&TaskScheduler::RescheduleAll, std::placeholders::_1, duration));
+ }
+
+ /// Reschedule all tasks with a random duration between min and max.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskContext& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return RescheduleAll(TaskScheduler::RandomDurationBetween(min, max));
+ }
+
+ /// Reschedule all tasks of a group with the given duration.
+ template<class _Rep, class _Period>
+ TaskContext& RescheduleGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration)
+ {
+ return Dispatch(std::bind(&TaskScheduler::RescheduleGroup<_Rep, _Period>, std::placeholders::_1, group, duration));
+ }
+
+ /// Reschedule all tasks of a group with a random duration between min and max.
+ template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight>
+ TaskContext& RescheduleGroup(TaskScheduler::group_t const group,
+ std::chrono::duration<_RepLeft, _PeriodLeft> const& min,
+ std::chrono::duration<_RepRight, _PeriodRight> const& max)
+ {
+ return RescheduleGroup(group, TaskScheduler::RandomDurationBetween(min, max));
+ }
+
+private:
+ /// Asserts if the task was consumed already.
+ void AssertOnConsumed() const;
+
+ /// Invokes the associated hook of the task.
+ void Invoke();
+};
+
+#endif /// _TASK_SCHEDULER_H_
diff --git a/src/common/Utilities/Timer.h b/src/common/Utilities/Timer.h
new file mode 100644
index 00000000000..c54903d7be2
--- /dev/null
+++ b/src/common/Utilities/Timer.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 TRINITY_TIMER_H
+#define TRINITY_TIMER_H
+
+#include <chrono>
+
+inline uint32 getMSTime()
+{
+ using namespace std::chrono;
+
+ static const system_clock::time_point ApplicationStartTime = system_clock::now();
+
+ return uint32(duration_cast<milliseconds>(system_clock::now() - ApplicationStartTime).count());
+}
+
+inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
+{
+ // getMSTime() have limited data range and this is case when it overflow in this tick
+ if (oldMSTime > newMSTime)
+ return (0xFFFFFFFF - oldMSTime) + newMSTime;
+ else
+ return newMSTime - oldMSTime;
+}
+
+inline uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
+{
+ return getMSTimeDiff(oldMSTime, getMSTime());
+}
+
+struct IntervalTimer
+{
+public:
+
+ IntervalTimer()
+ : _interval(0), _current(0)
+ {
+ }
+
+ void Update(time_t diff)
+ {
+ _current += diff;
+ if (_current < 0)
+ _current = 0;
+ }
+
+ bool Passed()
+ {
+ return _current >= _interval;
+ }
+
+ void Reset()
+ {
+ if (_current >= _interval)
+ _current %= _interval;
+ }
+
+ void SetCurrent(time_t current)
+ {
+ _current = current;
+ }
+
+ void SetInterval(time_t interval)
+ {
+ _interval = interval;
+ }
+
+ time_t GetInterval() const
+ {
+ return _interval;
+ }
+
+ time_t GetCurrent() const
+ {
+ return _current;
+ }
+
+private:
+
+ time_t _interval;
+ time_t _current;
+};
+
+struct TimeTracker
+{
+public:
+
+ TimeTracker(time_t expiry)
+ : i_expiryTime(expiry)
+ {
+ }
+
+ void Update(time_t diff)
+ {
+ i_expiryTime -= diff;
+ }
+
+ bool Passed() const
+ {
+ return i_expiryTime <= 0;
+ }
+
+ void Reset(time_t interval)
+ {
+ i_expiryTime = interval;
+ }
+
+ time_t GetExpiry() const
+ {
+ return i_expiryTime;
+ }
+
+private:
+
+ time_t i_expiryTime;
+};
+
+struct TimeTrackerSmall
+{
+public:
+
+ TimeTrackerSmall(uint32 expiry = 0)
+ : i_expiryTime(expiry)
+ {
+ }
+
+ void Update(int32 diff)
+ {
+ i_expiryTime -= diff;
+ }
+
+ bool Passed() const
+ {
+ return i_expiryTime <= 0;
+ }
+
+ void Reset(uint32 interval)
+ {
+ i_expiryTime = interval;
+ }
+
+ int32 GetExpiry() const
+ {
+ return i_expiryTime;
+ }
+
+private:
+
+ int32 i_expiryTime;
+};
+
+struct PeriodicTimer
+{
+public:
+
+ PeriodicTimer(int32 period, int32 start_time)
+ : i_period(period), i_expireTime(start_time)
+ {
+ }
+
+ bool Update(const uint32 diff)
+ {
+ if ((i_expireTime -= diff) > 0)
+ return false;
+
+ i_expireTime += i_period > int32(diff) ? i_period : diff;
+ return true;
+ }
+
+ void SetPeriodic(int32 period, int32 start_time)
+ {
+ i_expireTime = start_time;
+ i_period = period;
+ }
+
+ // Tracker interface
+ void TUpdate(int32 diff) { i_expireTime -= diff; }
+ bool TPassed() const { return i_expireTime <= 0; }
+ void TReset(int32 diff, int32 period) { i_expireTime += period > diff ? period : diff; }
+
+private:
+
+ int32 i_period;
+ int32 i_expireTime;
+};
+
+#endif
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp
new file mode 100644
index 00000000000..9f61dd12e4c
--- /dev/null
+++ b/src/common/Utilities/Util.cpp
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 "Util.h"
+#include "Common.h"
+#include "CompilerDefs.h"
+#include "utf8.h"
+#include "SFMT.h"
+#include "Errors.h" // for ASSERT
+#include <stdarg.h>
+#include <boost/thread/tss.hpp>
+
+#if COMPILER == COMPILER_GNU
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+#endif
+
+static boost::thread_specific_ptr<SFMTRand> sfmtRand;
+
+static SFMTRand* GetRng()
+{
+ SFMTRand* rand = sfmtRand.get();
+
+ if (!rand)
+ {
+ rand = new SFMTRand();
+ sfmtRand.reset(rand);
+ }
+
+ return rand;
+}
+
+int32 irand(int32 min, int32 max)
+{
+ ASSERT(max >= min);
+ return int32(GetRng()->IRandom(min, max));
+}
+
+uint32 urand(uint32 min, uint32 max)
+{
+ ASSERT(max >= min);
+ return GetRng()->URandom(min, max);
+}
+
+uint32 urandms(uint32 min, uint32 max)
+{
+ ASSERT(max >= min);
+ ASSERT(INT_MAX/IN_MILLISECONDS >= max);
+ return GetRng()->URandom(min * IN_MILLISECONDS, max * IN_MILLISECONDS);
+}
+
+float frand(float min, float max)
+{
+ ASSERT(max >= min);
+ return float(GetRng()->Random() * (max - min) + min);
+}
+
+uint32 rand32()
+{
+ return GetRng()->BRandom();
+}
+
+double rand_norm()
+{
+ return GetRng()->Random();
+}
+
+double rand_chance()
+{
+ return GetRng()->Random() * 100.0;
+}
+
+Tokenizer::Tokenizer(const std::string &src, const char sep, uint32 vectorReserve)
+{
+ m_str = new char[src.length() + 1];
+ memcpy(m_str, src.c_str(), src.length() + 1);
+
+ if (vectorReserve)
+ m_storage.reserve(vectorReserve);
+
+ char* posold = m_str;
+ char* posnew = m_str;
+
+ for (;;)
+ {
+ if (*posnew == sep)
+ {
+ m_storage.push_back(posold);
+ posold = posnew + 1;
+
+ *posnew = '\0';
+ }
+ else if (*posnew == '\0')
+ {
+ // Hack like, but the old code accepted these kind of broken strings,
+ // so changing it would break other things
+ if (posold != posnew)
+ m_storage.push_back(posold);
+
+ break;
+ }
+
+ ++posnew;
+ }
+}
+
+void stripLineInvisibleChars(std::string &str)
+{
+ static std::string const invChars = " \t\7\n";
+
+ size_t wpos = 0;
+
+ bool space = false;
+ for (size_t pos = 0; pos < str.size(); ++pos)
+ {
+ if (invChars.find(str[pos])!=std::string::npos)
+ {
+ if (!space)
+ {
+ str[wpos++] = ' ';
+ space = true;
+ }
+ }
+ else
+ {
+ if (wpos!=pos)
+ str[wpos++] = str[pos];
+ else
+ ++wpos;
+ space = false;
+ }
+ }
+
+ if (wpos < str.size())
+ str.erase(wpos, str.size());
+ if (str.find("|TInterface")!=std::string::npos)
+ str.clear();
+
+}
+
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
+struct tm* localtime_r(const time_t* time, struct tm *result)
+{
+ localtime_s(result, time);
+ return result;
+}
+#endif
+
+std::string secsToTimeString(uint64 timeInSecs, bool shortText, bool hoursOnly)
+{
+ uint64 secs = timeInSecs % MINUTE;
+ uint64 minutes = timeInSecs % HOUR / MINUTE;
+ uint64 hours = timeInSecs % DAY / HOUR;
+ uint64 days = timeInSecs / DAY;
+
+ std::ostringstream ss;
+ if (days)
+ ss << days << (shortText ? "d" : " Day(s) ");
+ if (hours || hoursOnly)
+ ss << hours << (shortText ? "h" : " Hour(s) ");
+ if (!hoursOnly)
+ {
+ if (minutes)
+ ss << minutes << (shortText ? "m" : " Minute(s) ");
+ if (secs || (!days && !hours && !minutes) )
+ ss << secs << (shortText ? "s" : " Second(s).");
+ }
+
+ return ss.str();
+}
+
+int32 MoneyStringToMoney(const std::string& moneyString)
+{
+ int32 money = 0;
+
+ if (!(std::count(moneyString.begin(), moneyString.end(), 'g') == 1 ||
+ std::count(moneyString.begin(), moneyString.end(), 's') == 1 ||
+ std::count(moneyString.begin(), moneyString.end(), 'c') == 1))
+ return 0; // Bad format
+
+ Tokenizer tokens(moneyString, ' ');
+ for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
+ {
+ std::string tokenString(*itr);
+ size_t gCount = std::count(tokenString.begin(), tokenString.end(), 'g');
+ size_t sCount = std::count(tokenString.begin(), tokenString.end(), 's');
+ size_t cCount = std::count(tokenString.begin(), tokenString.end(), 'c');
+ if (gCount + sCount + cCount != 1)
+ return 0;
+
+ uint32 amount = atoi(*itr);
+ if (gCount == 1)
+ money += amount * 100 * 100;
+ else if (sCount == 1)
+ money += amount * 100;
+ else if (cCount == 1)
+ money += amount;
+ }
+
+ return money;
+}
+
+uint32 TimeStringToSecs(const std::string& timestring)
+{
+ uint32 secs = 0;
+ uint32 buffer = 0;
+ uint32 multiplier = 0;
+
+ for (std::string::const_iterator itr = timestring.begin(); itr != timestring.end(); ++itr)
+ {
+ if (isdigit(*itr))
+ {
+ buffer*=10;
+ buffer+= (*itr)-'0';
+ }
+ else
+ {
+ switch (*itr)
+ {
+ case 'd': multiplier = DAY; break;
+ case 'h': multiplier = HOUR; break;
+ case 'm': multiplier = MINUTE; break;
+ case 's': multiplier = 1; break;
+ default : return 0; //bad format
+ }
+ buffer*=multiplier;
+ secs+=buffer;
+ buffer=0;
+ }
+ }
+
+ return secs;
+}
+
+std::string TimeToTimestampStr(time_t t)
+{
+ tm aTm;
+ localtime_r(&t, &aTm);
+ // YYYY year
+ // MM month (2 digits 01-12)
+ // DD day (2 digits 01-31)
+ // HH hour (2 digits 00-23)
+ // MM minutes (2 digits 00-59)
+ // SS seconds (2 digits 00-59)
+ char buf[20];
+ snprintf(buf, 20, "%04d-%02d-%02d_%02d-%02d-%02d", aTm.tm_year+1900, aTm.tm_mon+1, aTm.tm_mday, aTm.tm_hour, aTm.tm_min, aTm.tm_sec);
+ return std::string(buf);
+}
+
+/// Check if the string is a valid ip address representation
+bool IsIPAddress(char const* ipaddress)
+{
+ if (!ipaddress)
+ return false;
+
+ // Let the big boys do it.
+ // Drawback: all valid ip address formats are recognized e.g.: 12.23, 121234, 0xABCD)
+ return inet_addr(ipaddress) != INADDR_NONE;
+}
+
+/// create PID file
+uint32 CreatePIDFile(const std::string& filename)
+{
+ FILE* pid_file = fopen (filename.c_str(), "w" );
+ if (pid_file == NULL)
+ return 0;
+
+#ifdef _WIN32
+ DWORD pid = GetCurrentProcessId();
+#else
+ pid_t pid = getpid();
+#endif
+
+ fprintf(pid_file, "%u", pid );
+ fclose(pid_file);
+
+ return (uint32)pid;
+}
+
+size_t utf8length(std::string& utf8str)
+{
+ try
+ {
+ return utf8::distance(utf8str.c_str(), utf8str.c_str()+utf8str.size());
+ }
+ catch(std::exception)
+ {
+ utf8str.clear();
+ return 0;
+ }
+}
+
+void utf8truncate(std::string& utf8str, size_t len)
+{
+ try
+ {
+ size_t wlen = utf8::distance(utf8str.c_str(), utf8str.c_str()+utf8str.size());
+ if (wlen <= len)
+ return;
+
+ std::wstring wstr;
+ wstr.resize(wlen);
+ utf8::utf8to16(utf8str.c_str(), utf8str.c_str()+utf8str.size(), &wstr[0]);
+ wstr.resize(len);
+ char* oend = utf8::utf16to8(wstr.c_str(), wstr.c_str()+wstr.size(), &utf8str[0]);
+ utf8str.resize(oend-(&utf8str[0])); // remove unused tail
+ }
+ catch(std::exception)
+ {
+ utf8str.clear();
+ }
+}
+
+bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize)
+{
+ try
+ {
+ size_t len = utf8::distance(utf8str, utf8str+csize);
+ if (len > wsize)
+ {
+ if (wsize > 0)
+ wstr[0] = L'\0';
+ wsize = 0;
+ return false;
+ }
+
+ wsize = len;
+ utf8::utf8to16(utf8str, utf8str+csize, wstr);
+ wstr[len] = L'\0';
+ }
+ catch(std::exception)
+ {
+ if (wsize > 0)
+ wstr[0] = L'\0';
+ wsize = 0;
+ return false;
+ }
+
+ return true;
+}
+
+bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr)
+{
+ try
+ {
+ if (size_t len = utf8::distance(utf8str.c_str(), utf8str.c_str()+utf8str.size()))
+ {
+ wstr.resize(len);
+ utf8::utf8to16(utf8str.c_str(), utf8str.c_str()+utf8str.size(), &wstr[0]);
+ }
+ }
+ catch(std::exception)
+ {
+ wstr.clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str)
+{
+ try
+ {
+ std::string utf8str2;
+ utf8str2.resize(size*4); // allocate for most long case
+
+ if (size)
+ {
+ char* oend = utf8::utf16to8(wstr, wstr+size, &utf8str2[0]);
+ utf8str2.resize(oend-(&utf8str2[0])); // remove unused tail
+ }
+ utf8str = utf8str2;
+ }
+ catch(std::exception)
+ {
+ utf8str.clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool WStrToUtf8(std::wstring const& wstr, std::string& utf8str)
+{
+ try
+ {
+ std::string utf8str2;
+ utf8str2.resize(wstr.size()*4); // allocate for most long case
+
+ if (wstr.size())
+ {
+ char* oend = utf8::utf16to8(wstr.c_str(), wstr.c_str()+wstr.size(), &utf8str2[0]);
+ utf8str2.resize(oend-(&utf8str2[0])); // remove unused tail
+ }
+ utf8str = utf8str2;
+ }
+ catch(std::exception)
+ {
+ utf8str.clear();
+ return false;
+ }
+
+ return true;
+}
+
+typedef wchar_t const* const* wstrlist;
+
+std::wstring GetMainPartOfName(std::wstring const& wname, uint32 declension)
+{
+ // supported only Cyrillic cases
+ if (wname.size() < 1 || !isCyrillicCharacter(wname[0]) || declension > 5)
+ return wname;
+
+ // Important: end length must be <= MAX_INTERNAL_PLAYER_NAME-MAX_PLAYER_NAME (3 currently)
+
+ static wchar_t const a_End[] = { wchar_t(1), wchar_t(0x0430), wchar_t(0x0000)};
+ static wchar_t const o_End[] = { wchar_t(1), wchar_t(0x043E), wchar_t(0x0000)};
+ static wchar_t const ya_End[] = { wchar_t(1), wchar_t(0x044F), wchar_t(0x0000)};
+ static wchar_t const ie_End[] = { wchar_t(1), wchar_t(0x0435), wchar_t(0x0000)};
+ static wchar_t const i_End[] = { wchar_t(1), wchar_t(0x0438), wchar_t(0x0000)};
+ static wchar_t const yeru_End[] = { wchar_t(1), wchar_t(0x044B), wchar_t(0x0000)};
+ static wchar_t const u_End[] = { wchar_t(1), wchar_t(0x0443), wchar_t(0x0000)};
+ static wchar_t const yu_End[] = { wchar_t(1), wchar_t(0x044E), wchar_t(0x0000)};
+ static wchar_t const oj_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x0439), wchar_t(0x0000)};
+ static wchar_t const ie_j_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x0439), wchar_t(0x0000)};
+ static wchar_t const io_j_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x0439), wchar_t(0x0000)};
+ static wchar_t const o_m_End[] = { wchar_t(2), wchar_t(0x043E), wchar_t(0x043C), wchar_t(0x0000)};
+ static wchar_t const io_m_End[] = { wchar_t(2), wchar_t(0x0451), wchar_t(0x043C), wchar_t(0x0000)};
+ static wchar_t const ie_m_End[] = { wchar_t(2), wchar_t(0x0435), wchar_t(0x043C), wchar_t(0x0000)};
+ static wchar_t const soft_End[] = { wchar_t(1), wchar_t(0x044C), wchar_t(0x0000)};
+ static wchar_t const j_End[] = { wchar_t(1), wchar_t(0x0439), wchar_t(0x0000)};
+
+ static wchar_t const* const dropEnds[6][8] = {
+ { &a_End[1], &o_End[1], &ya_End[1], &ie_End[1], &soft_End[1], &j_End[1], NULL, NULL },
+ { &a_End[1], &ya_End[1], &yeru_End[1], &i_End[1], NULL, NULL, NULL, NULL },
+ { &ie_End[1], &u_End[1], &yu_End[1], &i_End[1], NULL, NULL, NULL, NULL },
+ { &u_End[1], &yu_End[1], &o_End[1], &ie_End[1], &soft_End[1], &ya_End[1], &a_End[1], NULL },
+ { &oj_End[1], &io_j_End[1], &ie_j_End[1], &o_m_End[1], &io_m_End[1], &ie_m_End[1], &yu_End[1], NULL },
+ { &ie_End[1], &i_End[1], NULL, NULL, NULL, NULL, NULL, NULL }
+ };
+
+ for (wchar_t const* const* itr = &dropEnds[declension][0]; *itr; ++itr)
+ {
+ size_t len = size_t((*itr)[-1]); // get length from string size field
+
+ if (wname.substr(wname.size()-len, len)==*itr)
+ return wname.substr(0, wname.size()-len);
+ }
+
+ return wname;
+}
+
+bool utf8ToConsole(const std::string& utf8str, std::string& conStr)
+{
+#if PLATFORM == PLATFORM_WINDOWS
+ std::wstring wstr;
+ if (!Utf8toWStr(utf8str, wstr))
+ return false;
+
+ conStr.resize(wstr.size());
+ CharToOemBuffW(&wstr[0], &conStr[0], wstr.size());
+#else
+ // not implemented yet
+ conStr = utf8str;
+#endif
+
+ return true;
+}
+
+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());
+
+ return WStrToUtf8(wstr, utf8str);
+#else
+ // not implemented yet
+ utf8str = conStr;
+ return true;
+#endif
+}
+
+bool Utf8FitTo(const std::string& str, std::wstring const& search)
+{
+ std::wstring temp;
+
+ if (!Utf8toWStr(str, temp))
+ return false;
+
+ // converting to lower case
+ wstrToLower( temp );
+
+ if (temp.find(search) == std::wstring::npos)
+ return false;
+
+ return true;
+}
+
+void utf8printf(FILE* out, const char *str, ...)
+{
+ va_list ap;
+ va_start(ap, str);
+ vutf8printf(out, str, &ap);
+ va_end(ap);
+}
+
+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];
+
+ 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;
+
+ 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);
+ fprintf(out, "%s", temp_buf);
+#else
+ vfprintf(out, str, *ap);
+#endif
+}
+
+std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse /* = false */)
+{
+ int32 init = 0;
+ int32 end = arrayLen;
+ int8 op = 1;
+
+ if (reverse)
+ {
+ init = arrayLen - 1;
+ end = -1;
+ op = -1;
+ }
+
+ std::ostringstream ss;
+ for (int32 i = init; i != end; i += op)
+ {
+ char buffer[4];
+ sprintf(buffer, "%02X", bytes[i]);
+ ss << buffer;
+ }
+
+ return ss.str();
+}
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h
new file mode 100644
index 00000000000..b748e83408b
--- /dev/null
+++ b/src/common/Utilities/Util.h
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 _UTIL_H
+#define _UTIL_H
+
+#include "Define.h"
+#include "Errors.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <list>
+#include <map>
+
+// Searcher for map of structs
+template<typename T, class S> struct Finder
+{
+ T val_;
+ T S::* idMember_;
+
+ Finder(T val, T S::* idMember) : val_(val), idMember_(idMember) {}
+ bool operator()(const std::pair<int, S> &obj) { return obj.second.*idMember_ == val_; }
+};
+
+class Tokenizer
+{
+public:
+ typedef std::vector<char const*> StorageType;
+
+ typedef StorageType::size_type size_type;
+
+ typedef StorageType::const_iterator const_iterator;
+ typedef StorageType::reference reference;
+ typedef StorageType::const_reference const_reference;
+
+public:
+ Tokenizer(const std::string &src, char const sep, uint32 vectorReserve = 0);
+ ~Tokenizer() { delete[] m_str; }
+
+ const_iterator begin() const { return m_storage.begin(); }
+ const_iterator end() const { return m_storage.end(); }
+
+ size_type size() const { return m_storage.size(); }
+
+ reference operator [] (size_type i) { return m_storage[i]; }
+ const_reference operator [] (size_type i) const { return m_storage[i]; }
+
+private:
+ char* m_str;
+ StorageType m_storage;
+};
+
+void stripLineInvisibleChars(std::string &src);
+
+int32 MoneyStringToMoney(const std::string& moneyString);
+
+struct tm* localtime_r(const time_t* time, struct tm *result);
+
+std::string secsToTimeString(uint64 timeInSecs, bool shortText = false, bool hoursOnly = false);
+uint32 TimeStringToSecs(const std::string& timestring);
+std::string TimeToTimestampStr(time_t t);
+
+/* Return a random number in the range min..max. */
+int32 irand(int32 min, int32 max);
+
+/* Return a random number in the range min..max (inclusive). */
+uint32 urand(uint32 min, uint32 max);
+
+/* Return a random millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */
+uint32 urandms(uint32 min, uint32 max);
+
+/* Return a random number in the range 0 .. UINT32_MAX. */
+uint32 rand32();
+
+/* Return a random number in the range min..max */
+float frand(float min, float max);
+
+/* Return a random double from 0.0 to 1.0 (exclusive). */
+double rand_norm();
+
+/* Return a random double from 0.0 to 100.0 (exclusive). */
+double rand_chance();
+
+/* Return true if a random roll fits in the specified chance (range 0-100). */
+inline bool roll_chance_f(float chance)
+{
+ return chance > rand_chance();
+}
+
+/* Return true if a random roll fits in the specified chance (range 0-100). */
+inline bool roll_chance_i(int chance)
+{
+ return chance > irand(0, 99);
+}
+
+inline void ApplyPercentModFloatVar(float& var, float val, bool apply)
+{
+ if (val == -100.0f) // prevent set var to zero
+ val = -99.99f;
+ var *= (apply ? (100.0f + val) / 100.0f : 100.0f / (100.0f + val));
+}
+
+// Percentage calculation
+template <class T, class U>
+inline T CalculatePct(T base, U pct)
+{
+ return T(base * static_cast<float>(pct) / 100.0f);
+}
+
+template <class T, class U>
+inline T AddPct(T &base, U pct)
+{
+ return base += CalculatePct(base, pct);
+}
+
+template <class T, class U>
+inline T ApplyPct(T &base, U pct)
+{
+ return base = CalculatePct(base, pct);
+}
+
+template <class T>
+inline T RoundToInterval(T& num, T floor, T ceil)
+{
+ return num = std::min(std::max(num, floor), ceil);
+}
+
+// UTF8 handling
+bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr);
+// in wsize==max size of buffer, out wsize==real string size
+bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize);
+inline bool Utf8toWStr(const std::string& utf8str, wchar_t* wstr, size_t& wsize)
+{
+ return Utf8toWStr(utf8str.c_str(), utf8str.size(), wstr, wsize);
+}
+
+bool WStrToUtf8(std::wstring const& wstr, std::string& utf8str);
+// size==real string size
+bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str);
+
+size_t utf8length(std::string& utf8str); // set string to "" if invalid utf8 sequence
+void utf8truncate(std::string& utf8str, size_t len);
+
+inline bool isBasicLatinCharacter(wchar_t wchar)
+{
+ if (wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z
+ return true;
+ if (wchar >= L'A' && wchar <= L'Z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z
+ return true;
+ return false;
+}
+
+inline bool isExtendedLatinCharacter(wchar_t wchar)
+{
+ if (isBasicLatinCharacter(wchar))
+ return true;
+ if (wchar >= 0x00C0 && wchar <= 0x00D6) // LATIN CAPITAL LETTER A WITH GRAVE - LATIN CAPITAL LETTER O WITH DIAERESIS
+ return true;
+ if (wchar >= 0x00D8 && wchar <= 0x00DE) // LATIN CAPITAL LETTER O WITH STROKE - LATIN CAPITAL LETTER THORN
+ return true;
+ if (wchar == 0x00DF) // LATIN SMALL LETTER SHARP S
+ return true;
+ if (wchar >= 0x00E0 && wchar <= 0x00F6) // LATIN SMALL LETTER A WITH GRAVE - LATIN SMALL LETTER O WITH DIAERESIS
+ return true;
+ if (wchar >= 0x00F8 && wchar <= 0x00FE) // LATIN SMALL LETTER O WITH STROKE - LATIN SMALL LETTER THORN
+ return true;
+ if (wchar >= 0x0100 && wchar <= 0x012F) // LATIN CAPITAL LETTER A WITH MACRON - LATIN SMALL LETTER I WITH OGONEK
+ return true;
+ if (wchar == 0x1E9E) // LATIN CAPITAL LETTER SHARP S
+ return true;
+ return false;
+}
+
+inline bool isCyrillicCharacter(wchar_t wchar)
+{
+ if (wchar >= 0x0410 && wchar <= 0x044F) // CYRILLIC CAPITAL LETTER A - CYRILLIC SMALL LETTER YA
+ return true;
+ if (wchar == 0x0401 || wchar == 0x0451) // CYRILLIC CAPITAL LETTER IO, CYRILLIC SMALL LETTER IO
+ return true;
+ return false;
+}
+
+inline bool isEastAsianCharacter(wchar_t wchar)
+{
+ if (wchar >= 0x1100 && wchar <= 0x11F9) // Hangul Jamo
+ return true;
+ if (wchar >= 0x3041 && wchar <= 0x30FF) // Hiragana + Katakana
+ return true;
+ if (wchar >= 0x3131 && wchar <= 0x318E) // Hangul Compatibility Jamo
+ return true;
+ if (wchar >= 0x31F0 && wchar <= 0x31FF) // Katakana Phonetic Ext.
+ return true;
+ if (wchar >= 0x3400 && wchar <= 0x4DB5) // CJK Ideographs Ext. A
+ return true;
+ if (wchar >= 0x4E00 && wchar <= 0x9FC3) // Unified CJK Ideographs
+ return true;
+ if (wchar >= 0xAC00 && wchar <= 0xD7A3) // Hangul Syllables
+ return true;
+ if (wchar >= 0xFF01 && wchar <= 0xFFEE) // Halfwidth forms
+ return true;
+ return false;
+}
+
+inline bool isNumeric(wchar_t wchar)
+{
+ return (wchar >= L'0' && wchar <=L'9');
+}
+
+inline bool isNumeric(char c)
+{
+ return (c >= '0' && c <='9');
+}
+
+inline bool isNumeric(char const* str)
+{
+ for (char const* c = str; *c; ++c)
+ if (!isNumeric(*c))
+ return false;
+
+ return true;
+}
+
+inline bool isNumericOrSpace(wchar_t wchar)
+{
+ return isNumeric(wchar) || wchar == L' ';
+}
+
+inline bool isBasicLatinString(const std::wstring &wstr, bool numericOrSpace)
+{
+ for (size_t i = 0; i < wstr.size(); ++i)
+ if (!isBasicLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
+ return false;
+ return true;
+}
+
+inline bool isExtendedLatinString(const std::wstring &wstr, bool numericOrSpace)
+{
+ for (size_t i = 0; i < wstr.size(); ++i)
+ if (!isExtendedLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
+ return false;
+ return true;
+}
+
+inline bool isCyrillicString(const std::wstring &wstr, bool numericOrSpace)
+{
+ for (size_t i = 0; i < wstr.size(); ++i)
+ if (!isCyrillicCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
+ return false;
+ return true;
+}
+
+inline bool isEastAsianString(const std::wstring &wstr, bool numericOrSpace)
+{
+ for (size_t i = 0; i < wstr.size(); ++i)
+ if (!isEastAsianCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i])))
+ return false;
+ return true;
+}
+
+inline wchar_t wcharToUpper(wchar_t wchar)
+{
+ if (wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z
+ return wchar_t(uint16(wchar)-0x0020);
+ if (wchar == 0x00DF) // LATIN SMALL LETTER SHARP S
+ return wchar_t(0x1E9E);
+ if (wchar >= 0x00E0 && wchar <= 0x00F6) // LATIN SMALL LETTER A WITH GRAVE - LATIN SMALL LETTER O WITH DIAERESIS
+ return wchar_t(uint16(wchar)-0x0020);
+ if (wchar >= 0x00F8 && wchar <= 0x00FE) // LATIN SMALL LETTER O WITH STROKE - LATIN SMALL LETTER THORN
+ return wchar_t(uint16(wchar)-0x0020);
+ if (wchar >= 0x0101 && wchar <= 0x012F) // LATIN SMALL LETTER A WITH MACRON - LATIN SMALL LETTER I WITH OGONEK (only %2=1)
+ {
+ if (wchar % 2 == 1)
+ return wchar_t(uint16(wchar)-0x0001);
+ }
+ if (wchar >= 0x0430 && wchar <= 0x044F) // CYRILLIC SMALL LETTER A - CYRILLIC SMALL LETTER YA
+ return wchar_t(uint16(wchar)-0x0020);
+ if (wchar == 0x0451) // CYRILLIC SMALL LETTER IO
+ return wchar_t(0x0401);
+
+ return wchar;
+}
+
+inline wchar_t wcharToUpperOnlyLatin(wchar_t wchar)
+{
+ return isBasicLatinCharacter(wchar) ? wcharToUpper(wchar) : wchar;
+}
+
+inline wchar_t wcharToLower(wchar_t wchar)
+{
+ if (wchar >= L'A' && wchar <= L'Z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z
+ return wchar_t(uint16(wchar)+0x0020);
+ if (wchar >= 0x00C0 && wchar <= 0x00D6) // LATIN CAPITAL LETTER A WITH GRAVE - LATIN CAPITAL LETTER O WITH DIAERESIS
+ return wchar_t(uint16(wchar)+0x0020);
+ if (wchar >= 0x00D8 && wchar <= 0x00DE) // LATIN CAPITAL LETTER O WITH STROKE - LATIN CAPITAL LETTER THORN
+ return wchar_t(uint16(wchar)+0x0020);
+ if (wchar >= 0x0100 && wchar <= 0x012E) // LATIN CAPITAL LETTER A WITH MACRON - LATIN CAPITAL LETTER I WITH OGONEK (only %2=0)
+ {
+ if (wchar % 2 == 0)
+ return wchar_t(uint16(wchar)+0x0001);
+ }
+ if (wchar == 0x1E9E) // LATIN CAPITAL LETTER SHARP S
+ return wchar_t(0x00DF);
+ if (wchar == 0x0401) // CYRILLIC CAPITAL LETTER IO
+ return wchar_t(0x0451);
+ if (wchar >= 0x0410 && wchar <= 0x042F) // CYRILLIC CAPITAL LETTER A - CYRILLIC CAPITAL LETTER YA
+ return wchar_t(uint16(wchar)+0x0020);
+
+ return wchar;
+}
+
+inline void wstrToUpper(std::wstring& str)
+{
+ std::transform( str.begin(), str.end(), str.begin(), wcharToUpper );
+}
+
+inline void wstrToLower(std::wstring& str)
+{
+ std::transform( str.begin(), str.end(), str.begin(), wcharToLower );
+}
+
+std::wstring GetMainPartOfName(std::wstring const& wname, uint32 declension);
+
+bool utf8ToConsole(const std::string& utf8str, std::string& conStr);
+bool consoleToUtf8(const std::string& conStr, std::string& utf8str);
+bool Utf8FitTo(const std::string& str, std::wstring const& search);
+void utf8printf(FILE* out, const char *str, ...);
+void vutf8printf(FILE* out, const char *str, va_list* ap);
+
+bool IsIPAddress(char const* ipaddress);
+
+uint32 CreatePIDFile(const std::string& filename);
+
+std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false);
+
+// simple class for not-modifyable list
+template <typename T>
+class HookList
+{
+ typedef typename std::list<T>::iterator ListIterator;
+ private:
+ typename std::list<T> m_list;
+ public:
+ HookList<T> & operator+=(T t)
+ {
+ m_list.push_back(t);
+ return *this;
+ }
+ HookList<T> & operator-=(T t)
+ {
+ m_list.remove(t);
+ return *this;
+ }
+ size_t size()
+ {
+ return m_list.size();
+ }
+ ListIterator begin()
+ {
+ return m_list.begin();
+ }
+ ListIterator end()
+ {
+ return m_list.end();
+ }
+};
+
+class flag96
+{
+private:
+ uint32 part[3];
+
+public:
+ flag96(uint32 p1 = 0, uint32 p2 = 0, uint32 p3 = 0)
+ {
+ part[0] = p1;
+ part[1] = p2;
+ part[2] = p3;
+ }
+
+ inline bool IsEqual(uint32 p1 = 0, uint32 p2 = 0, uint32 p3 = 0) const
+ {
+ return (part[0] == p1 && part[1] == p2 && part[2] == p3);
+ }
+
+ inline bool HasFlag(uint32 p1 = 0, uint32 p2 = 0, uint32 p3 = 0) const
+ {
+ return (part[0] & p1 || part[1] & p2 || part[2] & p3);
+ }
+
+ inline void Set(uint32 p1 = 0, uint32 p2 = 0, uint32 p3 = 0)
+ {
+ part[0] = p1;
+ part[1] = p2;
+ part[2] = p3;
+ }
+
+ inline bool operator <(const flag96 &right) const
+ {
+ for (uint8 i = 3; i > 0; --i)
+ {
+ if (part[i - 1] < right.part[i - 1])
+ return true;
+ else if (part[i - 1] > right.part[i - 1])
+ return false;
+ }
+ return false;
+ }
+
+ inline bool operator ==(const flag96 &right) const
+ {
+ return
+ (
+ part[0] == right.part[0] &&
+ part[1] == right.part[1] &&
+ part[2] == right.part[2]
+ );
+ }
+
+ inline bool operator !=(const flag96 &right) const
+ {
+ return !this->operator ==(right);
+ }
+
+ inline flag96 & operator =(const flag96 &right)
+ {
+ part[0] = right.part[0];
+ part[1] = right.part[1];
+ part[2] = right.part[2];
+ return *this;
+ }
+
+ inline flag96 operator &(const flag96 &right) const
+ {
+ return flag96(part[0] & right.part[0], part[1] & right.part[1],
+ part[2] & right.part[2]);
+ }
+
+ inline flag96 & operator &=(const flag96 &right)
+ {
+ part[0] &= right.part[0];
+ part[1] &= right.part[1];
+ part[2] &= right.part[2];
+ return *this;
+ }
+
+ inline flag96 operator |(const flag96 &right) const
+ {
+ return flag96(part[0] | right.part[0], part[1] | right.part[1],
+ part[2] | right.part[2]);
+ }
+
+ inline flag96 & operator |=(const flag96 &right)
+ {
+ part[0] |= right.part[0];
+ part[1] |= right.part[1];
+ part[2] |= right.part[2];
+ return *this;
+ }
+
+ inline flag96 operator ~() const
+ {
+ return flag96(~part[0], ~part[1], ~part[2]);
+ }
+
+ inline flag96 operator ^(const flag96 &right) const
+ {
+ return flag96(part[0] ^ right.part[0], part[1] ^ right.part[1],
+ part[2] ^ right.part[2]);
+ }
+
+ inline flag96 & operator ^=(const flag96 &right)
+ {
+ part[0] ^= right.part[0];
+ part[1] ^= right.part[1];
+ part[2] ^= right.part[2];
+ return *this;
+ }
+
+ inline operator bool() const
+ {
+ return (part[0] != 0 || part[1] != 0 || part[2] != 0);
+ }
+
+ inline bool operator !() const
+ {
+ return !this->operator bool();
+ }
+
+ inline uint32 & operator [](uint8 el)
+ {
+ return part[el];
+ }
+
+ inline const uint32 & operator [](uint8 el) const
+ {
+ return part[el];
+ }
+};
+
+enum ComparisionType
+{
+ COMP_TYPE_EQ = 0,
+ COMP_TYPE_HIGH,
+ COMP_TYPE_LOW,
+ COMP_TYPE_HIGH_EQ,
+ COMP_TYPE_LOW_EQ,
+ COMP_TYPE_MAX
+};
+
+template <class T>
+bool CompareValues(ComparisionType type, T val1, T val2)
+{
+ switch (type)
+ {
+ case COMP_TYPE_EQ:
+ return val1 == val2;
+ case COMP_TYPE_HIGH:
+ return val1 > val2;
+ case COMP_TYPE_LOW:
+ return val1 < val2;
+ case COMP_TYPE_HIGH_EQ:
+ return val1 >= val2;
+ case COMP_TYPE_LOW_EQ:
+ return val1 <= val2;
+ default:
+ // incorrect parameter
+ ABORT();
+ return false;
+ }
+}
+
+#endif