diff options
Diffstat (limited to 'src/common')
68 files changed, 1980 insertions, 586 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 8dee99c65e7..0428738f2dd 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -8,42 +8,33 @@ # 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) +CollectSourceFiles( + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE_SOURCES + # Exclude + ${CMAKE_CURRENT_SOURCE_DIR}/Debugging + ${CMAKE_CURRENT_SOURCE_DIR}/Platform + ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders) # 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) +list(APPEND PRIVATE_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/Debugging/Errors.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Debugging/Errors.h) if (USE_COREPCH) - set(common_STAT_PCH_HDR PrecompiledHeaders/commonPCH.h) - set(common_STAT_PCH_SRC PrecompiledHeaders/commonPCH.cpp) + set(PRIVATE_PCH_HEADER PrecompiledHeaders/commonPCH.h) + set(PRIVATE_PCH_SOURCE 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} +GroupSources(${CMAKE_CURRENT_SOURCE_DIR}) + +add_definitions(-DTRINITY_API_EXPORT_COMMON) + +add_library(common + ${PRIVATE_PCH_SOURCE} + ${PRIVATE_SOURCES} ) # Do NOT add any extra include directory here, as we don't want the common @@ -55,37 +46,55 @@ set(common_STAT_SRCS # 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} +CollectIncludeDirectories( ${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} -) + PUBLIC_INCLUDES + # Exclude + ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders) -GroupSources(${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(common + PUBLIC + # Provide the binary dir for all child targets + ${CMAKE_BINARY_DIR} + ${PUBLIC_INCLUDES} + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}) -add_library(common STATIC - ${common_STAT_SRCS} - ${common_STAT_PCH_SRC} -) +target_link_libraries(common + PUBLIC + boost + cppformat + g3dlib + Detour + sfmt + utf8cpp + openssl + valgrind + threads + jemalloc + PRIVATE + process) add_dependencies(common revision_data.h) +set_target_properties(common + PROPERTIES + FOLDER + "server") + +if( BUILD_SHARED_LIBS ) + if( UNIX ) + install(TARGETS common + LIBRARY + DESTINATION lib) + elseif( WIN32 ) + install(TARGETS common + RUNTIME + DESTINATION "${CMAKE_INSTALL_PREFIX}") + endif() +endif() + # Generate precompiled header if (USE_COREPCH) - add_cxx_pch(common ${common_STAT_PCH_HDR} ${common_STAT_PCH_SRC}) + add_cxx_pch(common ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE}) endif () diff --git a/src/common/Collision/BoundingIntervalHierarchy.h b/src/common/Collision/BoundingIntervalHierarchy.h index 0b15bd2d92f..d84ee3f6bf3 100644 --- a/src/common/Collision/BoundingIntervalHierarchy.h +++ b/src/common/Collision/BoundingIntervalHierarchy.h @@ -67,7 +67,7 @@ struct AABound Copyright (c) 2003-2007 Christopher Kulla */ -class BIH +class TC_COMMON_API BIH { private: void init_empty() @@ -91,7 +91,7 @@ class BIH buildData dat; dat.maxPrims = leafSize; - dat.numPrims = primitives.size(); + dat.numPrims = uint32(primitives.size()); dat.indices = new uint32[dat.numPrims]; dat.primBound = new G3D::AABox[dat.numPrims]; getBounds(primitives[0], bounds); @@ -115,7 +115,7 @@ class BIH delete[] dat.primBound; delete[] dat.indices; } - uint32 primCount() const { return objects.size(); } + uint32 primCount() const { return uint32(objects.size()); } template<typename RayCallback> void intersectRay(const G3D::Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst=false) const diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp index 5d2e18b1a2e..111606644de 100644 --- a/src/common/Collision/DynamicTree.cpp +++ b/src/common/Collision/DynamicTree.cpp @@ -248,7 +248,7 @@ bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, flo float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const { - G3D::Vector3 v(x, y, z); + G3D::Vector3 v(x, y, z + 0.5f); G3D::Ray r(v, G3D::Vector3(0, 0, -1)); DynamicTreeIntersectionCallback callback(phasemask); impl->intersectZAllignedRay(r, callback, maxSearchDist); diff --git a/src/common/Collision/DynamicTree.h b/src/common/Collision/DynamicTree.h index 0f18bb265f8..85707efebd2 100644 --- a/src/common/Collision/DynamicTree.h +++ b/src/common/Collision/DynamicTree.h @@ -31,7 +31,7 @@ namespace G3D class GameObjectModel; struct DynTreeImpl; -class DynamicMapTree +class TC_COMMON_API DynamicMapTree { DynTreeImpl *impl; diff --git a/src/common/Collision/Management/IVMapManager.h b/src/common/Collision/Management/IVMapManager.h index 1e64551956c..35437b7b816 100644 --- a/src/common/Collision/Management/IVMapManager.h +++ b/src/common/Collision/Management/IVMapManager.h @@ -42,7 +42,7 @@ namespace VMAP #define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case //=========================================================== - class IVMapManager + class TC_COMMON_API IVMapManager { private: bool iEnableLineOfSightCalc; diff --git a/src/common/Collision/Management/MMapFactory.h b/src/common/Collision/Management/MMapFactory.h index edd074fc93d..f89f44ba4df 100644 --- a/src/common/Collision/Management/MMapFactory.h +++ b/src/common/Collision/Management/MMapFactory.h @@ -38,12 +38,11 @@ namespace MMAP // static class // holds all mmap global data // access point to MMapManager singleton - class MMapFactory + class TC_COMMON_API MMapFactory { public: static MMapManager* createOrGetMMapManager(); static void clear(); - static bool IsPathfindingEnabled(uint32 mapId); }; } diff --git a/src/common/Collision/Management/MMapManager.cpp b/src/common/Collision/Management/MMapManager.cpp index 04b1e62399b..66808e08e34 100644 --- a/src/common/Collision/Management/MMapManager.cpp +++ b/src/common/Collision/Management/MMapManager.cpp @@ -23,6 +23,9 @@ namespace MMAP { + static char const* const MAP_FILE_NAME_FORMAT = "%s/mmaps/%03i.mmap"; + static char const* const TILE_FILE_NAME_FORMAT = "%s/mmaps/%03i%02i%02i.mmtile"; + // ######################## MMapManager ######################## MMapManager::~MMapManager() { @@ -70,26 +73,20 @@ namespace MMAP } // 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"); + std::string fileName = Trinity::StringFormat(MAP_FILE_NAME_FORMAT, sConfigMgr->GetStringDefault("DataDir", ".").c_str(), mapId); + FILE* file = fopen(fileName.c_str(), "rb"); if (!file) { - TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName); - delete [] fileName; + TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName.c_str()); return false; } dtNavMeshParams params; - int count = fread(¶ms, sizeof(dtNavMeshParams), 1, file); + uint32 count = uint32(fread(¶ms, 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; + TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '%s'", fileName.c_str()); return false; } @@ -98,18 +95,14 @@ namespace MMAP if (dtStatusFailed(mesh->init(¶ms))) { dtFreeNavMesh(mesh); - TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName); - delete [] fileName; + TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName.c_str()); 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; @@ -120,7 +113,7 @@ namespace MMAP return uint32(x << 16 | y); } - bool MMapManager::loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 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)) @@ -132,23 +125,17 @@ namespace MMAP // check if we already have this tile loaded uint32 packedGridPos = packTileID(x, y); - if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end()) + if (mmap->loadedTileRefs.find(packedGridPos) != mmap->loadedTileRefs.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"); + std::string fileName = Trinity::StringFormat(TILE_FILE_NAME_FORMAT, sConfigMgr->GetStringDefault("DataDir", ".").c_str(), mapId, x, y); + FILE* file = fopen(fileName.c_str(), "rb"); if (!file) { - TC_LOG_DEBUG("maps", "MMAP:loadMap: Could not open mmtile file '%s'", fileName); - delete [] fileName; + TC_LOG_DEBUG("maps", "MMAP:loadMap: Could not open mmtile file '%s'", fileName.c_str()); return false; } - delete [] fileName; // read header MmapTileHeader fileHeader; @@ -186,7 +173,7 @@ namespace MMAP // 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)); + mmap->loadedTileRefs.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; @@ -214,14 +201,14 @@ namespace MMAP // check if we have this tile loaded uint32 packedGridPos = packTileID(x, y); - if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end()) + if (mmap->loadedTileRefs.find(packedGridPos) == mmap->loadedTileRefs.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]; + dtTileRef tileRef = mmap->loadedTileRefs[packedGridPos]; // unload, and mark as non loaded if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, NULL, NULL))) @@ -234,7 +221,7 @@ namespace MMAP } else { - mmap->mmapLoadedTiles.erase(packedGridPos); + mmap->loadedTileRefs.erase(packedGridPos); --loadedTiles; TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId); return true; @@ -255,7 +242,7 @@ namespace MMAP // unload all tiles from given map MMapData* mmap = itr->second; - for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i) + for (MMapTileSet::iterator i = mmap->loadedTileRefs.begin(); i != mmap->loadedTileRefs.end(); ++i) { uint32 x = (i->first >> 16); uint32 y = (i->first & 0x0000FFFF); diff --git a/src/common/Collision/Management/MMapManager.h b/src/common/Collision/Management/MMapManager.h index 1a502a916dd..0b85351f6a5 100644 --- a/src/common/Collision/Management/MMapManager.h +++ b/src/common/Collision/Management/MMapManager.h @@ -22,7 +22,6 @@ #include "Define.h" #include "DetourNavMesh.h" #include "DetourNavMeshQuery.h" - #include <string> #include <unordered_map> #include <vector> @@ -46,11 +45,11 @@ namespace MMAP 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] + + dtNavMesh* navMesh; + MMapTileSet loadedTileRefs; // maps [map grid coords] to [dtTile] }; @@ -58,7 +57,7 @@ namespace MMAP // singleton class // holds all all access to mmap loading unloading and meshes - class MMapManager + class TC_COMMON_API MMapManager { public: MMapManager() : loadedTiles(0), thread_safe_environment(true) {} @@ -75,7 +74,7 @@ namespace MMAP dtNavMesh const* GetNavMesh(uint32 mapId); uint32 getLoadedTilesCount() const { return loadedTiles; } - uint32 getLoadedMapsCount() const { return loadedMMaps.size(); } + uint32 getLoadedMapsCount() const { return uint32(loadedMMaps.size()); } private: bool loadMapData(uint32 mapId); uint32 packTileID(int32 x, int32 y); diff --git a/src/common/Collision/Management/VMapFactory.h b/src/common/Collision/Management/VMapFactory.h index 1a45bd5094b..a730fa12ef2 100644 --- a/src/common/Collision/Management/VMapFactory.h +++ b/src/common/Collision/Management/VMapFactory.h @@ -29,7 +29,7 @@ namespace VMAP { //=========================================================== - class VMapFactory + class TC_COMMON_API VMapFactory { public: static IVMapManager* createOrGetVMapManager(); diff --git a/src/common/Collision/Management/VMapManager2.h b/src/common/Collision/Management/VMapManager2.h index 722ac4935c6..61c5025e41f 100644 --- a/src/common/Collision/Management/VMapManager2.h +++ b/src/common/Collision/Management/VMapManager2.h @@ -51,7 +51,7 @@ namespace VMAP class StaticMapTree; class WorldModel; - class ManagedModel + class TC_COMMON_API ManagedModel { public: ManagedModel() : iModel(nullptr), iRefCount(0) { } @@ -75,7 +75,7 @@ namespace VMAP VMAP_DISABLE_LIQUIDSTATUS = 0x8 }; - class VMapManager2 : public IVMapManager + class TC_COMMON_API VMapManager2 : public IVMapManager { protected: // Tree to check collision diff --git a/src/common/Collision/Maps/MapDefines.h b/src/common/Collision/Maps/MapDefines.h index 538a78a38d4..a523b0c0186 100644 --- a/src/common/Collision/Maps/MapDefines.h +++ b/src/common/Collision/Maps/MapDefines.h @@ -33,4 +33,4 @@ enum NavTerrain // we only have 8 bits }; -#endif +#endif /* _MAPDEFINES_H */ diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp index 4d0996e1961..5907971efb9 100644 --- a/src/common/Collision/Maps/MapTree.cpp +++ b/src/common/Collision/Maps/MapTree.cpp @@ -22,6 +22,7 @@ #include "VMapDefinitions.h" #include "Log.h" #include "Errors.h" +#include "Metric.h" #include <string> #include <sstream> @@ -415,6 +416,8 @@ namespace VMAP } else iLoadedTiles[packTileID(tileX, tileY)] = false; + TC_METRIC_EVENT("map_events", "LoadMapTile", + "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY)); return result; } @@ -473,6 +476,8 @@ namespace VMAP } } iLoadedTiles.erase(tile); + TC_METRIC_EVENT("map_events", "UnloadMapTile", + "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY)); } void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count) diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h index 63d542754c1..0227a982b4c 100644 --- a/src/common/Collision/Maps/MapTree.h +++ b/src/common/Collision/Maps/MapTree.h @@ -29,7 +29,7 @@ namespace VMAP class GroupModel; class VMapManager2; - struct LocationInfo + struct TC_COMMON_API LocationInfo { LocationInfo(): hitInstance(nullptr), hitModel(nullptr), ground_Z(-G3D::finf()) { } const ModelInstance* hitInstance; @@ -37,7 +37,7 @@ namespace VMAP float ground_Z; }; - class StaticMapTree + class TC_COMMON_API StaticMapTree { typedef std::unordered_map<uint32, bool> loadedTileMap; typedef std::unordered_map<uint32, uint32> loadedSpawnMap; @@ -79,7 +79,7 @@ namespace VMAP 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(); } + uint32 numLoadedTiles() const { return uint32(iLoadedTiles.size()); } void getModelInstances(ModelInstance* &models, uint32 &count); private: @@ -87,7 +87,7 @@ namespace VMAP StaticMapTree& operator=(StaticMapTree const& right) = delete; }; - struct AreaInfo + struct TC_COMMON_API AreaInfo { AreaInfo(): result(false), ground_Z(-G3D::finf()), flags(0), adtId(0), rootId(0), groupId(0) { } diff --git a/src/common/Collision/Maps/TileAssembler.cpp b/src/common/Collision/Maps/TileAssembler.cpp index 3f58b8f3516..7ee9125a5c5 100644 --- a/src/common/Collision/Maps/TileAssembler.cpp +++ b/src/common/Collision/Maps/TileAssembler.cpp @@ -355,10 +355,13 @@ namespace VMAP uint32 name_length, displayId; char buff[500]; - while (!feof(model_list)) + while (true) { - if (fread(&displayId, sizeof(uint32), 1, model_list) != 1 - || fread(&name_length, sizeof(uint32), 1, model_list) != 1 + if (fread(&displayId, sizeof(uint32), 1, model_list) != 1) + if (feof(model_list)) // EOF flag is only set after failed reading attempt + break; + + if (fread(&name_length, sizeof(uint32), 1, model_list) != 1 || name_length >= sizeof(buff) || fread(&buff, sizeof(char), name_length, model_list) != name_length) { @@ -369,7 +372,7 @@ namespace VMAP std::string model_name(buff, name_length); WorldModel_Raw raw_model; - if ( !raw_model.Read((iSrcDir + "/" + model_name).c_str()) ) + if (!raw_model.Read((iSrcDir + "/" + model_name).c_str()) ) continue; spawnedModelFiles.insert(model_name); @@ -412,13 +415,14 @@ namespace VMAP 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); } + +// 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) { diff --git a/src/common/Collision/Maps/TileAssembler.h b/src/common/Collision/Maps/TileAssembler.h index 1e2dc1924f1..74111f69910 100644 --- a/src/common/Collision/Maps/TileAssembler.h +++ b/src/common/Collision/Maps/TileAssembler.h @@ -35,7 +35,7 @@ namespace VMAP */ //=============================================== - class ModelPosition + class TC_COMMON_API ModelPosition { private: G3D::Matrix3 iRotation; @@ -55,7 +55,7 @@ namespace VMAP typedef std::map<uint32, ModelSpawn> UniqueEntryMap; typedef std::multimap<uint32, uint32> TileMap; - struct MapSpawns + struct TC_COMMON_API MapSpawns { UniqueEntryMap UniqueEntries; TileMap TileEntries; @@ -64,7 +64,7 @@ namespace VMAP typedef std::map<uint32, MapSpawns*> MapData; //=============================================== - struct GroupModel_Raw + struct TC_COMMON_API GroupModel_Raw { uint32 mogpflags; uint32 GroupWMOID; @@ -82,7 +82,7 @@ namespace VMAP bool Read(FILE* f); }; - struct WorldModel_Raw + struct TC_COMMON_API WorldModel_Raw { uint32 RootWMOID; std::vector<GroupModel_Raw> groupsArray; @@ -90,7 +90,7 @@ namespace VMAP bool Read(const char * path); }; - class TileAssembler + class TC_COMMON_API TileAssembler { private: std::string iDestDir; diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h index 9d8687233c1..37f11af20ac 100644 --- a/src/common/Collision/Models/GameObjectModel.h +++ b/src/common/Collision/Models/GameObjectModel.h @@ -35,7 +35,7 @@ namespace VMAP class GameObject; struct GameObjectDisplayInfoEntry; -class GameObjectModelOwnerBase +class TC_COMMON_API GameObjectModelOwnerBase { public: virtual bool IsSpawned() const { return false; } @@ -47,7 +47,7 @@ public: virtual void DebugVisualizeCorner(G3D::Vector3 const& /*corner*/) const { } }; -class GameObjectModel /*, public Intersectable*/ +class TC_COMMON_API GameObjectModel /*, public Intersectable*/ { GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(NULL) { } public: @@ -84,4 +84,6 @@ private: std::unique_ptr<GameObjectModelOwnerBase> owner; }; +TC_COMMON_API void LoadGameObjectModelList(std::string const& dataPath); + #endif // _GAMEOBJECT_MODEL_H diff --git a/src/common/Collision/Models/ModelInstance.h b/src/common/Collision/Models/ModelInstance.h index d101630d1e9..84a41b15ce6 100644 --- a/src/common/Collision/Models/ModelInstance.h +++ b/src/common/Collision/Models/ModelInstance.h @@ -39,7 +39,7 @@ namespace VMAP MOD_HAS_BOUND = 1<<2 }; - class ModelSpawn + class TC_COMMON_API ModelSpawn { public: //mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name @@ -60,7 +60,7 @@ namespace VMAP static bool writeToFile(FILE* rw, const ModelSpawn &spawn); }; - class ModelInstance: public ModelSpawn + class TC_COMMON_API ModelInstance: public ModelSpawn { public: ModelInstance(): iInvScale(0.0f), iModel(nullptr) { } diff --git a/src/common/Collision/Models/WorldModel.h b/src/common/Collision/Models/WorldModel.h index 39787f6c664..bfc0367627f 100644 --- a/src/common/Collision/Models/WorldModel.h +++ b/src/common/Collision/Models/WorldModel.h @@ -33,7 +33,7 @@ namespace VMAP struct AreaInfo; struct LocationInfo; - class MeshTriangle + class TC_COMMON_API MeshTriangle { public: MeshTriangle() : idx0(0), idx1(0), idx2(0) { } @@ -44,7 +44,7 @@ namespace VMAP uint32 idx2; }; - class WmoLiquid + class TC_COMMON_API WmoLiquid { public: WmoLiquid(uint32 width, uint32 height, const G3D::Vector3 &corner, uint32 type); @@ -70,7 +70,7 @@ namespace VMAP }; /*! holding additional info for WMO group files */ - class GroupModel + class TC_COMMON_API GroupModel { public: GroupModel() : iBound(), iMogpFlags(0), iGroupWMOID(0), iLiquid(NULL) { } @@ -103,7 +103,7 @@ namespace VMAP }; /*! Holds a model (converted M2 or WMO) in its original coordinate space */ - class WorldModel + class TC_COMMON_API WorldModel { public: WorldModel(): RootWMOID(0) { } diff --git a/src/common/Collision/RegularGrid.h b/src/common/Collision/RegularGrid.h index 6a2a07968ad..2f3819c872c 100644 --- a/src/common/Collision/RegularGrid.h +++ b/src/common/Collision/RegularGrid.h @@ -20,7 +20,7 @@ class NodeCreatorFunc = NodeCreator<Node>, /*class BoundsFunc = BoundsTrait<T>,*/ class PositionFunc = PositionTrait<T> > -class RegularGrid2D +class TC_COMMON_API RegularGrid2D { public: @@ -71,7 +71,7 @@ public: } bool contains(const T& value) const { return memberTable.containsKey(&value); } - int size() const { return memberTable.size(); } + int size() const { return uint32(memberTable.size()); } struct Cell { diff --git a/src/common/Common.h b/src/common/Common.h index e8adc55d20d..aa04abacd30 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -21,28 +21,31 @@ #include "Define.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <cmath> -#include <errno.h> -#include <signal.h> -#include <assert.h> - -#include <set> -#include <unordered_set> +#include <algorithm> +#include <array> +#include <exception> #include <list> -#include <string> #include <map> -#include <unordered_map> +#include <memory> #include <queue> +#include <set> #include <sstream> -#include <algorithm> -#include <memory> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> #include <vector> -#include <array> +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <cerrno> +#include <csignal> + +#include <boost/optional.hpp> +#include <boost/utility/in_place_factory.hpp> #include <boost/functional/hash.hpp> #include "Debugging/Errors.h" @@ -130,9 +133,9 @@ enum LocaleConstant #define MAX_LOCALES 8 #define MAX_ACCOUNT_TUTORIAL_VALUES 8 -extern char const* localeNames[TOTAL_LOCALES]; +TC_COMMON_API extern char const* localeNames[TOTAL_LOCALES]; -LocaleConstant GetLocaleByName(const std::string& name); +TC_COMMON_API LocaleConstant GetLocaleByName(const std::string& name); typedef std::vector<std::string> StringVector; @@ -151,6 +154,10 @@ typedef std::vector<std::string> StringVector; #define MAX_QUERY_LEN 32*1024 +//! Optional helper class to wrap optional values within. +template <typename T> +using Optional = boost::optional<T>; + namespace Trinity { //! std::make_unique implementation (TODO: remove this once C++14 is supported) diff --git a/src/common/Configuration/BuiltInConfig.cpp b/src/common/Configuration/BuiltInConfig.cpp new file mode 100644 index 00000000000..c2fc3b91766 --- /dev/null +++ b/src/common/Configuration/BuiltInConfig.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "BuiltInConfig.h" +#include "Config.h" +#include "GitRevision.h" + +template<typename Fn> +static std::string GetStringWithDefaultValueFromFunction( + std::string const& key, Fn getter) +{ + std::string const value = sConfigMgr->GetStringDefault(key, ""); + return value.empty() ? getter() : value; +} + +std::string BuiltInConfig::GetCMakeCommand() +{ + return GetStringWithDefaultValueFromFunction( + "CMakeCommand", GitRevision::GetCMakeCommand); +} + +std::string BuiltInConfig::GetBuildDirectory() +{ + return GetStringWithDefaultValueFromFunction( + "BuildDirectory", GitRevision::GetBuildDirectory); +} + +std::string BuiltInConfig::GetSourceDirectory() +{ + return GetStringWithDefaultValueFromFunction( + "SourceDirectory", GitRevision::GetSourceDirectory); +} + +std::string BuiltInConfig::GetMySQLExecutable() +{ + return GetStringWithDefaultValueFromFunction( + "MySQLExecutable", GitRevision::GetMySQLExecutable); +} diff --git a/src/common/Configuration/BuiltInConfig.h b/src/common/Configuration/BuiltInConfig.h new file mode 100644 index 00000000000..0ffa059bc41 --- /dev/null +++ b/src/common/Configuration/BuiltInConfig.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef BUILT_IN_CONFIG_H +#define BUILT_IN_CONFIG_H + +#include <string> +#include "Define.h" + +/// Provides helper functions to access built-in values +/// which can be overwritten in config +namespace BuiltInConfig +{ + /// Returns the CMake command when any is specified in the config, + /// returns the built-in path otherwise + TC_COMMON_API std::string GetCMakeCommand(); + /// Returns the build directory path when any is specified in the config, + /// returns the built-in one otherwise + TC_COMMON_API std::string GetBuildDirectory(); + /// Returns the source directory path when any is specified in the config, + /// returns the built-in one otherwise + TC_COMMON_API std::string GetSourceDirectory(); + /// Returns the path to the mysql executable (`mysql`) when any is specified + /// in the config, returns the built-in one otherwise + TC_COMMON_API std::string GetMySQLExecutable(); + +} // namespace BuiltInConfig + +#endif // BUILT_IN_CONFIG_H diff --git a/src/common/Configuration/Config.cpp b/src/common/Configuration/Config.cpp index 6ac04615315..888aa6ecef3 100644 --- a/src/common/Configuration/Config.cpp +++ b/src/common/Configuration/Config.cpp @@ -21,14 +21,17 @@ #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ini_parser.hpp> #include "Config.h" +#include "Log.h" using namespace boost::property_tree; -bool ConfigMgr::LoadInitial(std::string const& file, std::string& error) +bool ConfigMgr::LoadInitial(std::string const& file, std::vector<std::string> args, + std::string& error) { std::lock_guard<std::mutex> lock(_configLock); _filename = file; + _args = args; try { @@ -56,42 +59,81 @@ bool ConfigMgr::LoadInitial(std::string const& file, std::string& error) return true; } -bool ConfigMgr::Reload(std::string& error) +ConfigMgr* ConfigMgr::instance() { - return LoadInitial(_filename, error); + static ConfigMgr instance; + return &instance; } -std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def) +bool ConfigMgr::Reload(std::string& error) { - std::string value = _config.get<std::string>(ptree::path_type(name, '/'), def); + return LoadInitial(_filename, std::move(_args), error); +} - value.erase(std::remove(value.begin(), value.end(), '"'), value.end()); +template<class T> +T ConfigMgr::GetValueDefault(std::string const& name, T def) const +{ + try + { + return _config.get<T>(ptree::path_type(name, '/')); + } + catch (boost::property_tree::ptree_bad_path) + { + TC_LOG_WARN("server.loading", "Missing name %s in config file %s, add \"%s = %s\" to this file", + name.c_str(), _filename.c_str(), name.c_str(), std::to_string(def).c_str()); + } + catch (boost::property_tree::ptree_bad_data) + { + TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use %s instead", + name.c_str(), _filename.c_str(), std::to_string(def).c_str()); + } - return value; + return def; } -bool ConfigMgr::GetBoolDefault(std::string const& name, bool def) +template<> +std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std::string def) const { 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"); + return _config.get<std::string>(ptree::path_type(name, '/')); + } + catch (boost::property_tree::ptree_bad_path) + { + TC_LOG_WARN("server.loading", "Missing name %s in config file %s, add \"%s = %s\" to this file", + name.c_str(), _filename.c_str(), name.c_str(), def.c_str()); } - catch (std::exception const& /*ex*/) + catch (boost::property_tree::ptree_bad_data) { - return def; + TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use %s instead", + name.c_str(), _filename.c_str(), def.c_str()); } + + return def; +} + +std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def) const +{ + std::string val = GetValueDefault(name, def); + val.erase(std::remove(val.begin(), val.end(), '"'), val.end()); + return val; +} + +bool ConfigMgr::GetBoolDefault(std::string const& name, bool def) const +{ + std::string val = GetValueDefault(name, std::string(def ? "1" : "0")); + val.erase(std::remove(val.begin(), val.end(), '"'), val.end()); + return (val == "1" || val == "true" || val == "TRUE" || val == "yes" || val == "YES"); } -int ConfigMgr::GetIntDefault(std::string const& name, int def) +int ConfigMgr::GetIntDefault(std::string const& name, int def) const { - return _config.get<int>(ptree::path_type(name, '/'), def); + return GetValueDefault(name, def); } -float ConfigMgr::GetFloatDefault(std::string const& name, float def) +float ConfigMgr::GetFloatDefault(std::string const& name, float def) const { - return _config.get<float>(ptree::path_type(name, '/'), def); + return GetValueDefault(name, def); } std::string const& ConfigMgr::GetFilename() diff --git a/src/common/Configuration/Config.h b/src/common/Configuration/Config.h index 5b04212ed7c..573bc7b4a15 100644 --- a/src/common/Configuration/Config.h +++ b/src/common/Configuration/Config.h @@ -19,43 +19,47 @@ #ifndef CONFIG_H #define CONFIG_H +#include "Define.h" + #include <string> #include <list> +#include <vector> #include <mutex> #include <boost/property_tree/ptree.hpp> -class ConfigMgr +class TC_COMMON_API ConfigMgr { - ConfigMgr() { } - ~ConfigMgr() { } + ConfigMgr() = default; + ConfigMgr(ConfigMgr const&) = delete; + ConfigMgr& operator=(ConfigMgr const&) = delete; + ~ConfigMgr() = default; public: /// Method used only for loading main configuration files (authserver.conf and worldserver.conf) - bool LoadInitial(std::string const& file, std::string& error); + bool LoadInitial(std::string const& file, std::vector<std::string> args, + std::string& error); - static ConfigMgr* instance() - { - static ConfigMgr instance; - return &instance; - } + static ConfigMgr* 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 GetStringDefault(std::string const& name, const std::string& def) const; + bool GetBoolDefault(std::string const& name, bool def) const; + int GetIntDefault(std::string const& name, int def) const; + float GetFloatDefault(std::string const& name, float def) const; std::string const& GetFilename(); + std::vector<std::string> const& GetArguments() const { return _args; } std::list<std::string> GetKeysByString(std::string const& name); private: std::string _filename; + std::vector<std::string> _args; boost::property_tree::ptree _config; std::mutex _configLock; - ConfigMgr(ConfigMgr const&); - ConfigMgr& operator=(ConfigMgr const&); + template<class T> + T GetValueDefault(std::string const& name, T def) const; }; #define sConfigMgr ConfigMgr::instance() diff --git a/src/common/Cryptography/ARC4.cpp b/src/common/Cryptography/ARC4.cpp index eea523a2090..ead85a18e67 100644 --- a/src/common/Cryptography/ARC4.cpp +++ b/src/common/Cryptography/ARC4.cpp @@ -18,14 +18,14 @@ #include "ARC4.h" -ARC4::ARC4(uint8 len) : m_ctx() +ARC4::ARC4(uint32 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() +ARC4::ARC4(uint8 *seed, uint32 len) : m_ctx() { EVP_CIPHER_CTX_init(&m_ctx); EVP_EncryptInit_ex(&m_ctx, EVP_rc4(), NULL, NULL, NULL); diff --git a/src/common/Cryptography/ARC4.h b/src/common/Cryptography/ARC4.h index f39e662e295..df412944ef6 100644 --- a/src/common/Cryptography/ARC4.h +++ b/src/common/Cryptography/ARC4.h @@ -22,11 +22,11 @@ #include <openssl/evp.h> #include "Define.h" -class ARC4 +class TC_COMMON_API ARC4 { public: - ARC4(uint8 len); - ARC4(uint8 *seed, uint8 len); + ARC4(uint32 len); + ARC4(uint8 *seed, uint32 len); ~ARC4(); void Init(uint8 *seed); void UpdateData(int len, uint8 *data); diff --git a/src/common/Cryptography/Authentication/AuthCrypt.h b/src/common/Cryptography/Authentication/AuthCrypt.h index 878391e3ce8..db4de8a7bd1 100644 --- a/src/common/Cryptography/Authentication/AuthCrypt.h +++ b/src/common/Cryptography/Authentication/AuthCrypt.h @@ -23,7 +23,7 @@ class BigNumber; -class AuthCrypt +class TC_COMMON_API AuthCrypt { public: AuthCrypt(); diff --git a/src/common/Cryptography/BigNumber.cpp b/src/common/Cryptography/BigNumber.cpp index 3b85122ebab..0d5dafc336b 100644 --- a/src/common/Cryptography/BigNumber.cpp +++ b/src/common/Cryptography/BigNumber.cpp @@ -163,11 +163,16 @@ uint32 BigNumber::AsDword() return (uint32)BN_get_word(_bn); } -bool BigNumber::isZero() const +bool BigNumber::IsZero() const { return BN_is_zero(_bn); } +bool BigNumber::IsNegative() const +{ + return BN_is_negative(_bn); +} + std::unique_ptr<uint8[]> BigNumber::AsByteArray(int32 minSize, bool littleEndian) { int numBytes = GetNumBytes(); @@ -189,13 +194,19 @@ std::unique_ptr<uint8[]> BigNumber::AsByteArray(int32 minSize, bool littleEndian return ret; } -char * BigNumber::AsHexStr() const +std::string BigNumber::AsHexStr() const { - return BN_bn2hex(_bn); + char* ch = BN_bn2hex(_bn); + std::string ret = ch; + OPENSSL_free(ch); + return ret; } -char * BigNumber::AsDecStr() const +std::string BigNumber::AsDecStr() const { - return BN_bn2dec(_bn); + char* ch = BN_bn2dec(_bn); + std::string ret = ch; + OPENSSL_free(ch); + return ret; } diff --git a/src/common/Cryptography/BigNumber.h b/src/common/Cryptography/BigNumber.h index a5bda50dc72..baf338552d0 100644 --- a/src/common/Cryptography/BigNumber.h +++ b/src/common/Cryptography/BigNumber.h @@ -21,10 +21,11 @@ #include <memory> #include "Define.h" +#include <string> struct bignum_st; -class BigNumber +class TC_COMMON_API BigNumber { public: BigNumber(); @@ -76,7 +77,8 @@ class BigNumber return t %= bn; } - bool isZero() const; + bool IsZero() const; + bool IsNegative() const; BigNumber ModExp(BigNumber const& bn1, BigNumber const& bn2); BigNumber Exp(BigNumber const&); @@ -89,8 +91,8 @@ class BigNumber std::unique_ptr<uint8[]> AsByteArray(int32 minSize = 0, bool littleEndian = true); - char * AsHexStr() const; - char * AsDecStr() const; + std::string AsHexStr() const; + std::string AsDecStr() const; private: struct bignum_st *_bn; diff --git a/src/common/Cryptography/HMACSHA1.h b/src/common/Cryptography/HMACSHA1.h index 29a926d5b16..049847489a6 100644 --- a/src/common/Cryptography/HMACSHA1.h +++ b/src/common/Cryptography/HMACSHA1.h @@ -28,7 +28,7 @@ class BigNumber; #define SEED_KEY_SIZE 16 -class HmacHash +class TC_COMMON_API HmacHash { public: HmacHash(uint32 len, uint8 *seed); diff --git a/src/common/Cryptography/OpenSSLCrypto.h b/src/common/Cryptography/OpenSSLCrypto.h index df1b14b5eda..65155df9af8 100644 --- a/src/common/Cryptography/OpenSSLCrypto.h +++ b/src/common/Cryptography/OpenSSLCrypto.h @@ -18,6 +18,8 @@ #ifndef OPENSSL_CRYPTO_H #define OPENSSL_CRYPTO_H +#include "Define.h" + /** * A group of functions which setup openssl crypto module to work properly in multithreaded enviroment * If not setup properly - it will crash @@ -25,9 +27,9 @@ namespace OpenSSLCrypto { /// Needs to be called before threads using openssl are spawned - void threadsSetup(); + TC_COMMON_API void threadsSetup(); /// Needs to be called after threads using openssl are despawned - void threadsCleanup(); + TC_COMMON_API void threadsCleanup(); } -#endif
\ No newline at end of file +#endif diff --git a/src/common/Cryptography/SHA1.cpp b/src/common/Cryptography/SHA1.cpp index a01bd7844ee..aed4a069827 100644 --- a/src/common/Cryptography/SHA1.cpp +++ b/src/common/Cryptography/SHA1.cpp @@ -18,6 +18,7 @@ #include "SHA1.h" #include "BigNumber.h" +#include "Util.h" #include <cstring> #include <stdarg.h> @@ -67,3 +68,10 @@ void SHA1Hash::Finalize(void) SHA1_Final(mDigest, &mC); } +std::string CalculateSHA1Hash(std::string const& content) +{ + unsigned char digest[SHA_DIGEST_LENGTH]; + SHA1((unsigned char*)content.c_str(), content.length(), (unsigned char*)&digest); + + return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH); +} diff --git a/src/common/Cryptography/SHA1.h b/src/common/Cryptography/SHA1.h index ffa02176a2d..37ac2cc0166 100644 --- a/src/common/Cryptography/SHA1.h +++ b/src/common/Cryptography/SHA1.h @@ -25,7 +25,7 @@ class BigNumber; -class SHA1Hash +class TC_COMMON_API SHA1Hash { public: SHA1Hash(); @@ -46,5 +46,8 @@ class SHA1Hash SHA_CTX mC; uint8 mDigest[SHA_DIGEST_LENGTH]; }; -#endif +/// Returns the SHA1 hash of the given content as hex string. +TC_COMMON_API std::string CalculateSHA1Hash(std::string const& content); + +#endif diff --git a/src/common/Debugging/Errors.cpp b/src/common/Debugging/Errors.cpp index 1ec66ff6d59..2ce00229e53 100644 --- a/src/common/Debugging/Errors.cpp +++ b/src/common/Debugging/Errors.cpp @@ -96,4 +96,11 @@ void Abort(char const* file, int line, char const* function) exit(1); } +void AbortHandler(int /*sigval*/) +{ + // nothing useful to log here, no way to pass args + *((volatile int*)NULL) = 0; + exit(1); +} + } // namespace Trinity diff --git a/src/common/Debugging/Errors.h b/src/common/Debugging/Errors.h index 38e311a6b13..e4b3563ca96 100644 --- a/src/common/Debugging/Errors.h +++ b/src/common/Debugging/Errors.h @@ -23,16 +23,18 @@ 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 TC_COMMON_API void Assert(char const* file, int line, char const* function, char const* message) ATTR_NORETURN; + DECLSPEC_NORETURN TC_COMMON_API 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 TC_COMMON_API 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 TC_COMMON_API 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; + DECLSPEC_NORETURN TC_COMMON_API 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); + TC_COMMON_API void Warning(char const* file, int line, char const* function, char const* message); + + DECLSPEC_NORETURN TC_COMMON_API void AbortHandler(int sigval) ATTR_NORETURN; } // namespace Trinity diff --git a/src/common/Debugging/WheatyExceptionReport.cpp b/src/common/Debugging/WheatyExceptionReport.cpp index 5b9a1b1bd6c..72abec161b9 100644 --- a/src/common/Debugging/WheatyExceptionReport.cpp +++ b/src/common/Debugging/WheatyExceptionReport.cpp @@ -901,7 +901,7 @@ unsigned /*cbBuffer*/) // will return true. bool bHandled; pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, pSym->ModBase, pSym->TypeIndex, - 0, pVariable, bHandled, pSym->Name, "", false, true); + pVariable, bHandled, pSym->Name, "", false, true); if (!bHandled) { @@ -934,7 +934,6 @@ char * WheatyExceptionReport::DumpTypeIndex( char * pszCurrBuffer, DWORD64 modBase, DWORD dwTypeIndex, -unsigned nestingLevel, DWORD_PTR offset, bool & bHandled, const char* Name, @@ -1022,14 +1021,14 @@ bool logChildren) FormatOutputValue(buffer, btVoid, sizeof(PVOID), (PVOID)offset, sizeof(buffer)); symbolDetails.top().Value = buffer; - if (nestingLevel >= WER_MAX_NESTING_LEVEL) + if (symbolDetails.size() >= 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, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, address, bHandled, Name, addressStr, false, logChildren); if (!bHandled) @@ -1074,19 +1073,19 @@ bool logChildren) switch (innerTypeTag) { case SymTagUDT: - if (nestingLevel >= WER_MAX_NESTING_LEVEL) + if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL) logChildren = false; - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, 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, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren); break; case SymTagArrayType: - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren); break; default: @@ -1100,7 +1099,7 @@ bool logChildren) symbolDetails.top().HasChildren = true; BasicType basicType = btNoType; - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, offset, bHandled, Name, "", false, false); // Set Value back to an empty string since the Array object itself has no value, only its elements have @@ -1222,7 +1221,7 @@ bool logChildren) DWORD_PTR dwFinalOffset = offset + dwMemberOffset; pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, - children.ChildId[i], nestingLevel+1, + children.ChildId[i], dwFinalOffset, bHandled2, ""/*Name */, "", true, true); // If the child wasn't a UDT, format it appropriately diff --git a/src/common/Debugging/WheatyExceptionReport.h b/src/common/Debugging/WheatyExceptionReport.h index eb62d8bceef..34919b19e01 100644 --- a/src/common/Debugging/WheatyExceptionReport.h +++ b/src/common/Debugging/WheatyExceptionReport.h @@ -13,7 +13,7 @@ #define countof _countof #define WER_MAX_ARRAY_ELEMENTS_COUNT 10 -#define WER_MAX_NESTING_LEVEL 5 +#define WER_MAX_NESTING_LEVEL 4 #define WER_LARGE_BUFFER_SIZE 1024 * 128 enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK @@ -173,7 +173,7 @@ class WheatyExceptionReport 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 char * DumpTypeIndex(char *, DWORD64, DWORD, 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); diff --git a/src/common/Define.h b/src/common/Define.h index b34edb6a549..d03d26ad780 100644 --- a/src/common/Define.h +++ b/src/common/Define.h @@ -95,6 +95,45 @@ #endif #endif //COMPILER == COMPILER_GNU +#ifdef TRINITY_API_USE_DYNAMIC_LINKING +# if COMPILER == COMPILER_MICROSOFT +# define TC_API_EXPORT __declspec(dllexport) +# define TC_API_IMPORT __declspec(dllimport) +# elif COMPILER == COMPILER_GNU +# define TC_API_EXPORT __attribute__((visibility("default"))) +# define TC_API_IMPORT +# else +# error compiler not supported! +# endif +#else +# define TC_API_EXPORT +# define TC_API_IMPORT +#endif + +#ifdef TRINITY_API_EXPORT_COMMON +# define TC_COMMON_API TC_API_EXPORT +#else +# define TC_COMMON_API TC_API_IMPORT +#endif + +#ifdef TRINITY_API_EXPORT_DATABASE +# define TC_DATABASE_API TC_API_EXPORT +#else +# define TC_DATABASE_API TC_API_IMPORT +#endif + +#ifdef TRINITY_API_EXPORT_SHARED +# define TC_SHARED_API TC_API_EXPORT +#else +# define TC_SHARED_API TC_API_IMPORT +#endif + +#ifdef TRINITY_API_EXPORT_GAME +# define TC_GAME_API TC_API_EXPORT +#else +# define TC_GAME_API TC_API_IMPORT +#endif + #define UI64FMTD "%" PRIu64 #define UI64LIT(N) UINT64_C(N) diff --git a/src/common/GitRevision.cpp b/src/common/GitRevision.cpp index d0719c09959..702cd01d84b 100644 --- a/src/common/GitRevision.cpp +++ b/src/common/GitRevision.cpp @@ -1,5 +1,4 @@ #include "GitRevision.h" -#include "CompilerDefs.h" #include "revision_data.h" char const* GitRevision::GetHash() @@ -17,6 +16,16 @@ char const* GitRevision::GetBranch() return _BRANCH; } +char const* GitRevision::GetCMakeCommand() +{ + return _CMAKE_COMMAND; +} + +char const* GitRevision::GetBuildDirectory() +{ + return _BUILD_DIRECTORY; +} + char const* GitRevision::GetSourceDirectory() { return _SOURCE_DIRECTORY; @@ -32,19 +41,30 @@ 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 +# ifdef _WIN64 +# define TRINITY_PLATFORM_STR "Win64" +# else +# define TRINITY_PLATFORM_STR "Win32" +# endif +#elif PLATFORM == PLATFORM_APPLE +# define TRINITY_PLATFORM_STR "MacOSX" +#elif PLATFORM == PLATFORM_INTEL +# define TRINITY_PLATFORM_STR "Intel" +#else // PLATFORM_UNIX +# define TRINITY_PLATFORM_STR "Unix" +#endif + +#ifndef TRINITY_API_USE_DYNAMIC_LINKING +# define TRINITY_LINKAGE_TYPE_STR "Static" #else - return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Unix, " _BUILD_DIRECTIVE ")"; +# define TRINITY_LINKAGE_TYPE_STR "Dynamic" #endif + +char const* GitRevision::GetFullVersion() +{ + return "TrinityCore rev. " VER_PRODUCTVERSION_STR + " (" TRINITY_PLATFORM_STR ", " _BUILD_DIRECTIVE ", " TRINITY_LINKAGE_TYPE_STR ")"; } char const* GitRevision::GetCompanyNameStr() @@ -66,13 +86,3 @@ 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 index 8d2764ba861..aace8ad2076 100644 --- a/src/common/GitRevision.h +++ b/src/common/GitRevision.h @@ -19,22 +19,23 @@ #define __GITREVISION_H__ #include <string> +#include "Define.h" 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(); + TC_COMMON_API char const* GetHash(); + TC_COMMON_API char const* GetDate(); + TC_COMMON_API char const* GetBranch(); + TC_COMMON_API char const* GetCMakeCommand(); + TC_COMMON_API char const* GetBuildDirectory(); + TC_COMMON_API char const* GetSourceDirectory(); + TC_COMMON_API char const* GetMySQLExecutable(); + TC_COMMON_API char const* GetFullDatabase(); + TC_COMMON_API char const* GetFullVersion(); + TC_COMMON_API char const* GetCompanyNameStr(); + TC_COMMON_API char const* GetLegalCopyrightStr(); + TC_COMMON_API char const* GetFileVersionStr(); + TC_COMMON_API char const* GetProductVersionStr(); } #endif diff --git a/src/common/Logging/Appender.h b/src/common/Logging/Appender.h index f0bfe423a66..d24daa2b60d 100644 --- a/src/common/Logging/Appender.h +++ b/src/common/Logging/Appender.h @@ -41,7 +41,7 @@ enum LogLevel const uint8 MaxLogLevels = 6; -enum AppenderType +enum AppenderType : uint8 { APPENDER_NONE, APPENDER_CONSOLE, @@ -59,7 +59,7 @@ enum AppenderFlags APPENDER_FLAGS_MAKE_FILE_BACKUP = 0x10 // only used by FileAppender }; -struct LogMessage +struct TC_COMMON_API LogMessage { LogMessage(LogLevel _level, std::string const& _type, std::string&& _text) : level(_level), type(_type), text(std::forward<std::string>(_text)), mtime(time(NULL)) @@ -85,7 +85,7 @@ struct LogMessage } }; -class Appender +class TC_COMMON_API Appender { public: Appender(uint8 _id, std::string const& name, LogLevel level = LOG_LEVEL_DISABLED, AppenderFlags flags = APPENDER_FLAGS_NONE); @@ -123,7 +123,7 @@ Appender* CreateAppender(uint8 id, std::string const& name, LogLevel level, Appe return new AppenderImpl(id, name, level, flags, std::forward<ExtraAppenderArgs>(extraArgs)); } -class InvalidAppenderArgsException : public std::length_error +class TC_COMMON_API InvalidAppenderArgsException : public std::length_error { public: explicit InvalidAppenderArgsException(std::string const& message) : std::length_error(message) { } diff --git a/src/common/Logging/AppenderConsole.h b/src/common/Logging/AppenderConsole.h index 5d7eae36b40..96d17207158 100644 --- a/src/common/Logging/AppenderConsole.h +++ b/src/common/Logging/AppenderConsole.h @@ -42,7 +42,7 @@ enum ColorTypes const uint8 MaxColors = uint8(WHITE) + 1; -class AppenderConsole : public Appender +class TC_COMMON_API AppenderConsole : public Appender { public: typedef std::integral_constant<AppenderType, APPENDER_CONSOLE>::type TypeIndex; diff --git a/src/common/Logging/AppenderFile.h b/src/common/Logging/AppenderFile.h index 9ba5d59259c..956b7a70b93 100644 --- a/src/common/Logging/AppenderFile.h +++ b/src/common/Logging/AppenderFile.h @@ -21,7 +21,7 @@ #include <atomic> #include "Appender.h" -class AppenderFile : public Appender +class TC_COMMON_API AppenderFile : public Appender { public: typedef std::integral_constant<AppenderType, APPENDER_FILE>::type TypeIndex; diff --git a/src/common/Logging/Log.cpp b/src/common/Logging/Log.cpp index 4bd0487343d..f7a84fb8b47 100644 --- a/src/common/Logging/Log.cpp +++ b/src/common/Logging/Log.cpp @@ -214,13 +214,13 @@ void Log::ReadLoggersFromConfig() 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& rootLogger = loggers[LOGGER_ROOT]; + rootLogger.Create(LOGGER_ROOT, LOG_LEVEL_ERROR); + rootLogger.addAppender(appender->getId(), appender); - logger = loggers["server"]; - logger.Create("server", LOG_LEVEL_ERROR); - logger.addAppender(appender->getId(), appender); + Logger& serverLogger = loggers["server"]; + serverLogger.Create("server", LOG_LEVEL_INFO); + serverLogger.addAppender(appender->getId(), appender); } } @@ -320,6 +320,12 @@ void Log::Close() appenders.clear(); } +Log* Log::instance() +{ + static Log instance; + return &instance; +} + void Log::Initialize(boost::asio::io_service* ioService) { if (ioService) @@ -331,6 +337,13 @@ void Log::Initialize(boost::asio::io_service* ioService) LoadFromConfig(); } +void Log::SetSynchronous() +{ + delete _strand; + _strand = nullptr; + _ioService = nullptr; +} + void Log::LoadFromConfig() { Close(); diff --git a/src/common/Logging/Log.h b/src/common/Logging/Log.h index a90481ad5d2..6460e404c90 100644 --- a/src/common/Logging/Log.h +++ b/src/common/Logging/Log.h @@ -34,7 +34,7 @@ #define LOGGER_ROOT "root" -class Log +class TC_COMMON_API Log { typedef std::unordered_map<std::string, Logger> LoggerMap; @@ -44,13 +44,10 @@ class Log public: - static Log* instance() - { - static Log instance; - return &instance; - } + static Log* instance(); void Initialize(boost::asio::io_service* ioService); + void SetSynchronous(); // Not threadsafe - should only be called from main() after all threads are joined void LoadFromConfig(); void Close(); bool ShouldLog(std::string const& type, LogLevel level) const; diff --git a/src/common/Logging/LogOperation.h b/src/common/Logging/LogOperation.h index 618629b5423..56e2307d492 100644 --- a/src/common/Logging/LogOperation.h +++ b/src/common/Logging/LogOperation.h @@ -19,11 +19,12 @@ #define LOGOPERATION_H #include <memory> +#include "Define.h" class Logger; struct LogMessage; -class LogOperation +class TC_COMMON_API LogOperation { public: LogOperation(Logger const* _logger, std::unique_ptr<LogMessage>&& _msg) diff --git a/src/common/Logging/Logger.h b/src/common/Logging/Logger.h index 67eab4295a4..4ac2e4494cc 100644 --- a/src/common/Logging/Logger.h +++ b/src/common/Logging/Logger.h @@ -20,7 +20,7 @@ #include "Appender.h" -class Logger +class TC_COMMON_API Logger { public: Logger(); diff --git a/src/common/Metric/Metric.cpp b/src/common/Metric/Metric.cpp new file mode 100644 index 00000000000..9484cebcc72 --- /dev/null +++ b/src/common/Metric/Metric.cpp @@ -0,0 +1,235 @@ +/* +* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "Metric.h" +#include "Log.h" +#include "Config.h" +#include "Util.h" + +void Metric::Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger) +{ + _realmName = realmName; + _batchTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService); + _overallStatusTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService); + _overallStatusLogger = overallStatusLogger; + LoadFromConfigs(); +} + +bool Metric::Connect() +{ + _dataStream.connect(_hostname, _port); + auto error = _dataStream.error(); + if (error) + { + TC_LOG_ERROR("metric", "Error connecting to '%s:%s', disabling Metric. Error message : %s", + _hostname.c_str(), _port.c_str(), error.message().c_str()); + _enabled = false; + return false; + } + _dataStream.clear(); + return true; +} + +void Metric::LoadFromConfigs() +{ + bool previousValue = _enabled; + _enabled = sConfigMgr->GetBoolDefault("Metric.Enable", false); + _updateInterval = sConfigMgr->GetIntDefault("Metric.Interval", 10); + if (_updateInterval < 1) + { + TC_LOG_ERROR("metric", "'Metric.Interval' config set to %d, overriding to 1.", _updateInterval); + _updateInterval = 1; + } + + _overallStatusTimerInterval = sConfigMgr->GetIntDefault("Metric.OverallStatusInterval", 1); + if (_overallStatusTimerInterval < 1) + { + TC_LOG_ERROR("metric", "'Metric.OverallStatusInterval' config set to %d, overriding to 1.", _overallStatusTimerInterval); + _overallStatusTimerInterval = 1; + } + + // Schedule a send at this point only if the config changed from Disabled to Enabled. + // Cancel any scheduled operation if the config changed from Enabled to Disabled. + if (_enabled && !previousValue) + { + std::string connectionInfo = sConfigMgr->GetStringDefault("Metric.ConnectionInfo", ""); + if (connectionInfo.empty()) + { + TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' not specified in configuration file."); + return; + } + + Tokenizer tokens(connectionInfo, ';'); + if (tokens.size() != 3) + { + TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' specified with wrong format in configuration file."); + return; + } + + _hostname.assign(tokens[0]); + _port.assign(tokens[1]); + _databaseName.assign(tokens[2]); + Connect(); + + ScheduleSend(); + ScheduleOverallStatusLog(); + } +} + +void Metric::Update() +{ + if (_overallStatusTimerTriggered) + { + _overallStatusTimerTriggered = false; + _overallStatusLogger(); + } +} + +void Metric::LogEvent(std::string const& category, std::string const& title, std::string const& description) +{ + using namespace std::chrono; + + MetricData* data = new MetricData; + data->Category = category; + data->Timestamp = system_clock::now(); + data->Type = METRIC_DATA_EVENT; + data->Title = title; + data->Text = description; + + _queuedData.Enqueue(data); +} + +void Metric::SendBatch() +{ + using namespace std::chrono; + + std::stringstream batchedData; + MetricData* data; + bool firstLoop = true; + while (_queuedData.Dequeue(data)) + { + if (!firstLoop) + batchedData << "\n"; + + batchedData << data->Category; + if (!_realmName.empty()) + batchedData << ",realm=" << _realmName; + + batchedData << " "; + + switch (data->Type) + { + case METRIC_DATA_VALUE: + batchedData << "value=" << data->Value; + break; + case METRIC_DATA_EVENT: + batchedData << "title=\"" << data->Title << "\",text=\"" << data->Text << "\""; + break; + } + + batchedData << " "; + + batchedData << std::to_string(duration_cast<nanoseconds>(data->Timestamp.time_since_epoch()).count()); + + firstLoop = false; + delete data; + } + + // Check if there's any data to send + if (batchedData.tellp() == std::streampos(0)) + { + ScheduleSend(); + return; + } + + if (!_dataStream.good() && !Connect()) + return; + + _dataStream << "POST " << "/write?db=" << _databaseName << " HTTP/1.1\r\n"; + _dataStream << "Host: " << _hostname << ":" << _port << "\r\n"; + _dataStream << "Accept: */*\r\n"; + _dataStream << "Content-Type: application/octet-stream\r\n"; + _dataStream << "Content-Transfer-Encoding: binary\r\n"; + + _dataStream << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n"; + _dataStream << batchedData.rdbuf(); + + std::string http_version; + _dataStream >> http_version; + unsigned int status_code = 0; + _dataStream >> status_code; + if (status_code != 204) + { + TC_LOG_ERROR("metric", "Error sending data, returned HTTP code: %u", status_code); + } + + // Read and ignore the status description + std::string status_description; + std::getline(_dataStream, status_description); + // Read headers + std::string header; + while (std::getline(_dataStream, header) && header != "\r") + { + if (header == "Connection: close\r") + _dataStream.close(); + } + + ScheduleSend(); +} + +void Metric::ScheduleSend() +{ + if (_enabled) + { + _batchTimer->expires_from_now(boost::posix_time::seconds(_updateInterval)); + _batchTimer->async_wait(std::bind(&Metric::SendBatch, this)); + } + else + { + _dataStream.close(); + MetricData* data; + // Clear the queue + while (_queuedData.Dequeue(data)) + ; + } +} + +void Metric::ForceSend() +{ + // Send what's queued only if io_service is stopped (so only on shutdown) + if (_enabled && _batchTimer->get_io_service().stopped()) + SendBatch(); +} + +void Metric::ScheduleOverallStatusLog() +{ + if (_enabled) + { + _overallStatusTimer->expires_from_now(boost::posix_time::seconds(_overallStatusTimerInterval)); + _overallStatusTimer->async_wait([this](const boost::system::error_code&) + { + _overallStatusTimerTriggered = true; + ScheduleOverallStatusLog(); + }); + } +} + +Metric* Metric::instance() +{ + static Metric instance; + return &instance; +} diff --git a/src/common/Metric/Metric.h b/src/common/Metric/Metric.h new file mode 100644 index 00000000000..1855e1d0098 --- /dev/null +++ b/src/common/Metric/Metric.h @@ -0,0 +1,141 @@ +/* +* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef METRIC_H__ +#define METRIC_H__ + +#include "Common.h" +#include "Threading/MPSCQueue.h" +#include <boost/asio/ip/tcp.hpp> +#include <boost/algorithm/string.hpp> +#include <type_traits> + +enum MetricDataType +{ + METRIC_DATA_VALUE, + METRIC_DATA_EVENT +}; + +struct MetricData +{ + std::string Category; + std::chrono::time_point<std::chrono::system_clock> Timestamp; + MetricDataType Type; + + // LogValue-specific fields + std::string Value; + + // LogEvent-specific fields + std::string Title; + std::string Text; +}; + +class TC_COMMON_API Metric +{ +private: + boost::asio::ip::tcp::iostream _dataStream; + MPSCQueue<MetricData> _queuedData; + std::unique_ptr<boost::asio::deadline_timer> _batchTimer; + std::unique_ptr<boost::asio::deadline_timer> _overallStatusTimer; + int32 _updateInterval = 0; + int32 _overallStatusTimerInterval = 0; + bool _enabled = false; + bool _overallStatusTimerTriggered = false; + std::string _hostname; + std::string _port; + std::string _databaseName; + std::function<void()> _overallStatusLogger; + std::string _realmName; + + bool Connect(); + void SendBatch(); + void ScheduleSend(); + void ScheduleOverallStatusLog(); + + template<class T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> + static std::string FormatInfluxDBValue(T value) { return std::to_string(value) + 'i'; } + + static std::string FormatInfluxDBValue(std::string const& value) + { + return '"' + boost::replace_all_copy(value, "\"", "\\\"") + '"'; + } + + static std::string FormatInfluxDBValue(bool value) { return value ? "t" : "f"; } + static std::string FormatInfluxDBValue(const char* value) { return FormatInfluxDBValue(std::string(value)); } + static std::string FormatInfluxDBValue(double value) { return std::to_string(value); } + static std::string FormatInfluxDBValue(float value) { return FormatInfluxDBValue(double(value)); } + +public: + static Metric* instance(); + + void Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger = [](){}); + void LoadFromConfigs(); + void Update(); + + template<class T> + void LogValue(std::string const& category, T value) + { + using namespace std::chrono; + + MetricData* data = new MetricData; + data->Category = category; + data->Timestamp = system_clock::now(); + data->Type = METRIC_DATA_VALUE; + data->Value = FormatInfluxDBValue(value); + + _queuedData.Enqueue(data); + } + + void LogEvent(std::string const& category, std::string const& title, std::string const& description); + + void ForceSend(); + bool IsEnabled() const { return _enabled; } +}; + +#define sMetric Metric::instance() + +#if PLATFORM != PLATFORM_WINDOWS +#define TC_METRIC_EVENT(category, title, description) \ + do { \ + if (sMetric->IsEnabled()) \ + sMetric->LogEvent(category, title, description); \ + } while (0) +#define TC_METRIC_VALUE(category, value) \ + do { \ + if (sMetric->IsEnabled()) \ + sMetric->LogValue(category, value); \ + } while (0) +#else +#define TC_METRIC_EVENT(category, title, description) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + do { \ + if (sMetric->IsEnabled()) \ + sMetric->LogEvent(category, title, description); \ + } while (0) \ + __pragma(warning(pop)) +#define TC_METRIC_VALUE(category, value) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + do { \ + if (sMetric->IsEnabled()) \ + sMetric->LogValue(category, value); \ + } while (0) \ + __pragma(warning(pop)) +#endif + +#endif // METRIC_H__ diff --git a/src/common/Platform/ServiceWin32.cpp b/src/common/Platform/ServiceWin32.cpp new file mode 100644 index 00000000000..3c34f3e322c --- /dev/null +++ b/src/common/Platform/ServiceWin32.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2008-2016 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/>. + */ + +#ifdef _WIN32 + +#include "Common.h" +#include "Log.h" +#include <cstring> +#include <windows.h> +#include <winsvc.h> + +#if !defined(WINADVAPI) +#if !defined(_ADVAPI32_) +#define WINADVAPI DECLSPEC_IMPORT +#else +#define WINADVAPI +#endif +#endif + +extern int main(int argc, char ** argv); +extern char serviceLongName[]; +extern char serviceName[]; +extern char serviceDescription[]; + +extern int m_ServiceStatus; + +SERVICE_STATUS serviceStatus; + +SERVICE_STATUS_HANDLE serviceStatusHandle = 0; + +typedef WINADVAPI BOOL (WINAPI *CSD_T)(SC_HANDLE, DWORD, LPCVOID); + +bool WinServiceInstall() +{ + SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + + if (serviceControlManager) + { + char path[_MAX_PATH + 10]; + if (GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0) + { + SC_HANDLE service; + std::strcat(path, " --service run"); + service = CreateService(serviceControlManager, + serviceName, // name of service + serviceLongName, // service name to display + SERVICE_ALL_ACCESS, // desired access + // service type + SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, + SERVICE_AUTO_START, // start type + SERVICE_ERROR_IGNORE, // error control type + path, // service's binary + 0, // no load ordering group + 0, // no tag identifier + 0, // no dependencies + 0, // LocalSystem account + 0); // no password + if (service) + { + HMODULE advapi32 = GetModuleHandle("ADVAPI32.DLL"); + if (!advapi32) + { + CloseServiceHandle(service); + CloseServiceHandle(serviceControlManager); + return false; + } + + CSD_T ChangeService_Config2 = (CSD_T) GetProcAddress(advapi32, "ChangeServiceConfig2A"); + if (!ChangeService_Config2) + { + CloseServiceHandle(service); + CloseServiceHandle(serviceControlManager); + return false; + } + + SERVICE_DESCRIPTION sdBuf; + sdBuf.lpDescription = serviceDescription; + ChangeService_Config2( + service, // handle to service + SERVICE_CONFIG_DESCRIPTION, // change: description + &sdBuf); // new data + + SC_ACTION _action[1]; + _action[0].Type = SC_ACTION_RESTART; + _action[0].Delay = 10000; + SERVICE_FAILURE_ACTIONS sfa; + ZeroMemory(&sfa, sizeof(SERVICE_FAILURE_ACTIONS)); + sfa.lpsaActions = _action; + sfa.cActions = 1; + sfa.dwResetPeriod =INFINITE; + ChangeService_Config2( + service, // handle to service + SERVICE_CONFIG_FAILURE_ACTIONS, // information level + &sfa); // new data + + CloseServiceHandle(service); + + } + } + CloseServiceHandle(serviceControlManager); + } + + printf("Service installed\n"); + return true; +} + +bool WinServiceUninstall() +{ + SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + + if (serviceControlManager) + { + SC_HANDLE service = OpenService(serviceControlManager, + serviceName, SERVICE_QUERY_STATUS | DELETE); + if (service) + { + SERVICE_STATUS serviceStatus2; + if (QueryServiceStatus(service, &serviceStatus2)) + { + if (serviceStatus2.dwCurrentState == SERVICE_STOPPED) + DeleteService(service); + } + CloseServiceHandle(service); + } + + CloseServiceHandle(serviceControlManager); + } + + printf("Service uninstalled\n"); + return true; +} + +void WINAPI ServiceControlHandler(DWORD controlCode) +{ + switch (controlCode) + { + case SERVICE_CONTROL_INTERROGATE: + break; + + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + m_ServiceStatus = 0; + return; + + case SERVICE_CONTROL_PAUSE: + m_ServiceStatus = 2; + serviceStatus.dwCurrentState = SERVICE_PAUSED; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + break; + + case SERVICE_CONTROL_CONTINUE: + serviceStatus.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + m_ServiceStatus = 1; + break; + + default: + if ( controlCode >= 128 && controlCode <= 255 ) + // user defined control code + break; + else + // unrecognized control code + break; + } + + SetServiceStatus(serviceStatusHandle, &serviceStatus); +} + +void WINAPI ServiceMain(DWORD argc, char *argv[]) +{ + // initialise service status + serviceStatus.dwServiceType = SERVICE_WIN32; + serviceStatus.dwCurrentState = SERVICE_START_PENDING; + serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; + serviceStatus.dwWin32ExitCode = NO_ERROR; + serviceStatus.dwServiceSpecificExitCode = NO_ERROR; + serviceStatus.dwCheckPoint = 0; + serviceStatus.dwWaitHint = 0; + + serviceStatusHandle = RegisterServiceCtrlHandler(serviceName, ServiceControlHandler); + + if ( serviceStatusHandle ) + { + char path[_MAX_PATH + 1]; + unsigned int i, last_slash = 0; + + GetModuleFileName(0, path, sizeof(path)/sizeof(path[0])); + + size_t pathLen = std::strlen(path); + for (i = 0; i < pathLen; i++) + { + if (path[i] == '\\') last_slash = i; + } + + path[last_slash] = 0; + + // service is starting + serviceStatus.dwCurrentState = SERVICE_START_PENDING; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + // do initialisation here + SetCurrentDirectory(path); + + // running + serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + serviceStatus.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus( serviceStatusHandle, &serviceStatus ); + + //////////////////////// + // service main cycle // + //////////////////////// + + m_ServiceStatus = 1; + argc = 1; + main(argc, argv); + + // service was stopped + serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + // do cleanup here + + // service is now stopped + serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + serviceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + } +} + +bool WinServiceRun() +{ + SERVICE_TABLE_ENTRY serviceTable[] = + { + { serviceName, ServiceMain }, + { 0, 0 } + }; + + if (!StartServiceCtrlDispatcher(serviceTable)) + { + TC_LOG_ERROR("server.worldserver", "StartService Failed. Error [%u]", uint32(::GetLastError())); + return false; + } + return true; +} +#endif diff --git a/src/common/Platform/ServiceWin32.h b/src/common/Platform/ServiceWin32.h new file mode 100644 index 00000000000..b892ba4e3b6 --- /dev/null +++ b/src/common/Platform/ServiceWin32.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008-2016 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/>. + */ + +#ifdef _WIN32 +#ifndef _WIN32_SERVICE_ +#define _WIN32_SERVICE_ + +bool WinServiceInstall(); +bool WinServiceUninstall(); +bool WinServiceRun(); + +#endif // _WIN32_SERVICE_ +#endif // _WIN32 diff --git a/src/common/Threading/Callback.h b/src/common/Threading/Callback.h deleted file mode 100644 index f7eab57ddda..00000000000 --- a/src/common/Threading/Callback.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#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 index c6faaaf81ca..21a29d7e53b 100644 --- a/src/common/Threading/LockedQueue.h +++ b/src/common/Threading/LockedQueue.h @@ -57,6 +57,14 @@ public: unlock(); } + //! Adds items back to front of the queue + template<class Iterator> + void readd(Iterator begin, Iterator end) + { + std::lock_guard<std::mutex> lock(_lock); + _queue.insert(_queue.begin(), begin, end); + } + //! Gets the next result in the queue, if any. bool next(T& result) { diff --git a/src/common/Threading/MPSCQueue.h b/src/common/Threading/MPSCQueue.h new file mode 100644 index 00000000000..09648b844be --- /dev/null +++ b/src/common/Threading/MPSCQueue.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <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 MPSCQueue_h__ +#define MPSCQueue_h__ + +#include <atomic> +#include <utility> + +// C++ implementation of Dmitry Vyukov's lock free MPSC queue +// http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue +template<typename T> +class MPSCQueue +{ +public: + MPSCQueue() : _head(new Node()), _tail(_head.load(std::memory_order_relaxed)) + { + Node* front = _head.load(std::memory_order_relaxed); + front->Next.store(nullptr, std::memory_order_relaxed); + } + + ~MPSCQueue() + { + T* output; + while (this->Dequeue(output)) + ; + + Node* front = _head.load(std::memory_order_relaxed); + delete front; + } + + void Enqueue(T* input) + { + Node* node = new Node(input); + Node* prevHead = _head.exchange(node, std::memory_order_acq_rel); + prevHead->Next.store(node, std::memory_order_release); + } + + bool Dequeue(T*& result) + { + Node* tail = _tail.load(std::memory_order_relaxed); + Node* next = tail->Next.load(std::memory_order_acquire); + if (!next) + return false; + + result = next->Data; + _tail.store(next, std::memory_order_release); + delete tail; + return true; + } + +private: + struct Node + { + Node() = default; + explicit Node(T* data) : Data(data) { Next.store(nullptr, std::memory_order_relaxed); } + + T* Data; + std::atomic<Node*> Next; + }; + + std::atomic<Node*> _head; + std::atomic<Node*> _tail; + + MPSCQueue(MPSCQueue const&) = delete; + MPSCQueue& operator=(MPSCQueue const&) = delete; +}; + +#endif // MPSCQueue_h__ diff --git a/src/common/Utilities/Containers.h b/src/common/Utilities/Containers.h index f3e9432ca4c..5edb245fd87 100644 --- a/src/common/Utilities/Containers.h +++ b/src/common/Utilities/Containers.h @@ -20,6 +20,7 @@ #include "Define.h" #include "Random.h" +#include "Util.h" #include <algorithm> #include <functional> #include <list> @@ -30,9 +31,9 @@ namespace Trinity namespace Containers { template<class T> - void RandomResizeList(std::list<T> &list, uint32 size) + void RandomResizeList(std::list<T>& list, uint32 size) { - size_t list_size = list.size(); + uint32 list_size = uint32(list.size()); while (list_size > size) { @@ -55,7 +56,7 @@ namespace Trinity if (size) RandomResizeList(listCopy, size); - list = listCopy; + list = std::move(listCopy); } /* @@ -67,7 +68,7 @@ namespace Trinity typename C::value_type const& SelectRandomContainerElement(C const& container) { typename C::const_iterator it = container.begin(); - std::advance(it, urand(0, container.size() - 1)); + std::advance(it, urand(0, uint32(container.size()) - 1)); return *it; } @@ -117,6 +118,19 @@ namespace Trinity } /** + * @fn void Trinity::Containers::RandomShuffle(C& container) + * + * @brief Reorder the elements of the container randomly. + * + * @param container Container to reorder + */ + template <class C> + void RandomShuffle(C& container) + { + std::shuffle(container.begin(), container.end(), SFMTEngine::Instance()); + } + + /** * @fn bool Trinity::Containers::Intersects(Iterator first1, Iterator last1, Iterator first2, Iterator last2) * * @brief Checks if two SORTED containers have a common element @@ -156,7 +170,6 @@ namespace Trinity ++itr; } } - } //! namespace Containers } diff --git a/src/common/Utilities/EventMap.h b/src/common/Utilities/EventMap.h index a1aaa9af269..6a314a9e633 100644 --- a/src/common/Utilities/EventMap.h +++ b/src/common/Utilities/EventMap.h @@ -22,7 +22,7 @@ #include "Duration.h" #include "Util.h" -class EventMap +class TC_COMMON_API EventMap { /** * Internal storage type. @@ -122,7 +122,7 @@ public: */ void ScheduleEvent(uint32 eventId, Milliseconds const& time, uint32 group = 0, uint8 phase = 0) { - ScheduleEvent(eventId, time.count(), group, phase); + ScheduleEvent(eventId, uint32(time.count()), group, phase); } /** @@ -145,7 +145,7 @@ public: */ void RescheduleEvent(uint32 eventId, Milliseconds const& time, uint32 group = 0, uint8 phase = 0) { - RescheduleEvent(eventId, time.count(), group, phase); + RescheduleEvent(eventId, uint32(time.count()), group, phase); } /** @@ -169,7 +169,7 @@ public: */ void Repeat(Milliseconds const& time) { - Repeat(time.count()); + Repeat(uint32(time.count())); } /** @@ -190,7 +190,7 @@ public: */ void Repeat(Milliseconds const& minTime, Milliseconds const& maxTime) { - Repeat(minTime.count(), maxTime.count()); + Repeat(uint32(minTime.count()), uint32(maxTime.count())); } /** @@ -218,7 +218,7 @@ public: */ void DelayEvents(Milliseconds const& delay) { - DelayEvents(delay.count()); + DelayEvents(uint32(delay.count())); } /** @@ -239,7 +239,7 @@ public: */ void DelayEvents(Milliseconds const& delay, uint32 group) { - DelayEvents(delay.count(), group); + DelayEvents(uint32(delay.count()), group); } /** diff --git a/src/common/Utilities/EventProcessor.cpp b/src/common/Utilities/EventProcessor.cpp index be74d58b790..2341d0a0872 100644 --- a/src/common/Utilities/EventProcessor.cpp +++ b/src/common/Utilities/EventProcessor.cpp @@ -17,11 +17,20 @@ */ #include "EventProcessor.h" +#include "Errors.h" -EventProcessor::EventProcessor() +void BasicEvent::ScheduleAbort() { - m_time = 0; - m_aborting = false; + ASSERT(IsRunning() + && "Tried to scheduled the abortion of an event twice!"); + m_abortState = AbortState::STATE_ABORT_SCHEDULED; +} + +void BasicEvent::SetAborted() +{ + ASSERT(!IsAborted() + && "Tried to abort an already aborted event!"); + m_abortState = AbortState::STATE_ABORTED; } EventProcessor::~EventProcessor() @@ -39,55 +48,73 @@ void EventProcessor::Update(uint32 p_time) while (((i = m_events.begin()) != m_events.end()) && i->first <= m_time) { // get and remove event from queue - BasicEvent* Event = i->second; + BasicEvent* event = i->second; m_events.erase(i); - if (!Event->to_Abort) + if (event->IsRunning()) { - if (Event->Execute(m_time, p_time)) + if (event->Execute(m_time, p_time)) { // completely destroy event if it is not re-added - delete Event; + delete event; } + continue; } - else + + if (event->IsAbortScheduled()) { - Event->Abort(m_time); - delete Event; + event->Abort(m_time); + // Mark the event as aborted + event->SetAborted(); } + + if (event->IsDeletable()) + { + delete event; + continue; + } + + // Reschedule non deletable events to be checked at + // the next update tick + AddEvent(event, CalculateTime(1), false); } } 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();) + for (auto itr = m_events.begin(); itr != 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()) + // Abort events which weren't aborted already + if (!itr->second->IsAborted()) { - delete i_old->second; + itr->second->SetAborted(); + itr->second->Abort(m_time); + } - if (!force) // need per-element cleanup - m_events.erase (i_old); + // Skip non-deletable events when we are + // not forcing the event cancellation. + if (!force && !itr->second->IsDeletable()) + { + ++itr; + continue; } + + delete itr->second; + + if (force) + ++itr; // Clear the whole container when forcing + else + itr = m_events.erase(itr); } - // 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; + if (set_addtime) + Event->m_addTime = m_time; Event->m_execTime = e_time; m_events.insert(std::pair<uint64, BasicEvent*>(e_time, Event)); } @@ -96,4 +123,3 @@ 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 index e5eafed79b9..57f3065f323 100644 --- a/src/common/Utilities/EventProcessor.h +++ b/src/common/Utilities/EventProcessor.h @@ -20,20 +20,27 @@ #define __EVENTPROCESSOR_H #include "Define.h" - #include <map> +class EventProcessor; + // Note. All times are in milliseconds here. -class BasicEvent +class TC_COMMON_API BasicEvent { + friend class EventProcessor; + + enum class AbortState : uint8 + { + STATE_RUNNING, + STATE_ABORT_SCHEDULED, + STATE_ABORTED + }; + public: BasicEvent() - { - to_Abort = false; - m_addTime = 0; - m_execTime = 0; - } + : m_abortState(AbortState::STATE_RUNNING), 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 @@ -45,8 +52,16 @@ class BasicEvent 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 + // Aborts the event at the next update tick + void ScheduleAbort(); + + private: + void SetAborted(); + bool IsRunning() const { return (m_abortState == AbortState::STATE_RUNNING); } + bool IsAbortScheduled() const { return (m_abortState == AbortState::STATE_ABORT_SCHEDULED); } + bool IsAborted() const { return (m_abortState == AbortState::STATE_ABORTED); } + + AbortState m_abortState; // set by externals when the event is aborted, aborted events don't execute // these can be used for time offset control uint64 m_addTime; // time when the event was added to queue, filled by event handler @@ -55,19 +70,20 @@ class BasicEvent typedef std::multimap<uint64, BasicEvent*> EventList; -class EventProcessor +class TC_COMMON_API EventProcessor { public: - EventProcessor(); + EventProcessor() : m_time(0) { } ~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/MessageBuffer.h b/src/common/Utilities/MessageBuffer.h new file mode 100644 index 00000000000..d08c4b25bab --- /dev/null +++ b/src/common/Utilities/MessageBuffer.h @@ -0,0 +1,139 @@ +/* +* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __MESSAGEBUFFER_H_ +#define __MESSAGEBUFFER_H_ + +#include "Define.h" +#include <vector> +#include <cstring> + +class MessageBuffer +{ + typedef std::vector<uint8>::size_type size_type; + +public: + MessageBuffer() : _wpos(0), _rpos(0), _storage() + { + _storage.resize(4096); + } + + explicit MessageBuffer(std::size_t initialSize) : _wpos(0), _rpos(0), _storage() + { + _storage.resize(initialSize); + } + + MessageBuffer(MessageBuffer const& right) : _wpos(right._wpos), _rpos(right._rpos), _storage(right._storage) + { + } + + MessageBuffer(MessageBuffer&& right) : _wpos(right._wpos), _rpos(right._rpos), _storage(right.Move()) { } + + void Reset() + { + _wpos = 0; + _rpos = 0; + } + + void Resize(size_type bytes) + { + _storage.resize(bytes); + } + + uint8* GetBasePointer() { return _storage.data(); } + + uint8* GetReadPointer() { return GetBasePointer() + _rpos; } + + uint8* GetWritePointer() { return GetBasePointer() + _wpos; } + + void ReadCompleted(size_type bytes) { _rpos += bytes; } + + void WriteCompleted(size_type bytes) { _wpos += bytes; } + + size_type GetActiveSize() const { return _wpos - _rpos; } + + size_type GetRemainingSpace() const { return _storage.size() - _wpos; } + + size_type GetBufferSize() const { return _storage.size(); } + + // Discards inactive data + void Normalize() + { + if (_rpos) + { + if (_rpos != _wpos) + memmove(GetBasePointer(), GetReadPointer(), GetActiveSize()); + _wpos -= _rpos; + _rpos = 0; + } + } + + // Ensures there's "some" free space, make sure to call Normalize() before this + void EnsureFreeSpace() + { + // resize buffer if it's already full + if (GetRemainingSpace() == 0) + _storage.resize(_storage.size() * 3 / 2); + } + + void Write(void const* data, std::size_t size) + { + if (size) + { + memcpy(GetWritePointer(), data, size); + WriteCompleted(size); + } + } + + std::vector<uint8>&& Move() + { + _wpos = 0; + _rpos = 0; + return std::move(_storage); + } + + MessageBuffer& operator=(MessageBuffer const& right) + { + if (this != &right) + { + _wpos = right._wpos; + _rpos = right._rpos; + _storage = right._storage; + } + + return *this; + } + + MessageBuffer& operator=(MessageBuffer&& right) + { + if (this != &right) + { + _wpos = right._wpos; + _rpos = right._rpos; + _storage = right.Move(); + } + + return *this; + } + +private: + size_type _wpos; + size_type _rpos; + std::vector<uint8> _storage; +}; + +#endif /* __MESSAGEBUFFER_H_ */ diff --git a/src/common/Utilities/Random.cpp b/src/common/Utilities/Random.cpp index cc013110b01..31318e8f52d 100644 --- a/src/common/Utilities/Random.cpp +++ b/src/common/Utilities/Random.cpp @@ -61,6 +61,14 @@ float frand(float min, float max) return float(GetRng()->Random() * (max - min) + min); } +Milliseconds randtime(Milliseconds const& min, Milliseconds const& max) +{ + long long diff = max.count() - min.count(); + ASSERT(diff >= 0); + ASSERT(diff <= (uint32)-1); + return min + Milliseconds(urand(0, diff)); +} + uint32 rand32() { return GetRng()->BRandom(); diff --git a/src/common/Utilities/Random.h b/src/common/Utilities/Random.h index 5610651a83b..b3ca00219ef 100644 --- a/src/common/Utilities/Random.h +++ b/src/common/Utilities/Random.h @@ -19,29 +19,33 @@ #define Random_h__ #include "Define.h" +#include "Duration.h" #include <limits> #include <random> /* Return a random number in the range min..max. */ -int32 irand(int32 min, int32 max); +TC_COMMON_API int32 irand(int32 min, int32 max); /* Return a random number in the range min..max (inclusive). */ -uint32 urand(uint32 min, uint32 max); +TC_COMMON_API 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); +TC_COMMON_API uint32 urandms(uint32 min, uint32 max); /* Return a random number in the range 0 .. UINT32_MAX. */ -uint32 rand32(); +TC_COMMON_API uint32 rand32(); + +/* Return a random time in the range min..max (up to millisecond precision). Only works for values where millisecond difference is a valid uint32. */ +TC_COMMON_API Milliseconds randtime(Milliseconds const& min, Milliseconds const& max); /* Return a random number in the range min..max */ -float frand(float min, float max); +TC_COMMON_API float frand(float min, float max); /* Return a random double from 0.0 to 1.0 (exclusive). */ -double rand_norm(); +TC_COMMON_API double rand_norm(); /* Return a random double from 0.0 to 100.0 (exclusive). */ -double rand_chance(); +TC_COMMON_API double rand_chance(); /* Return true if a random roll fits in the specified chance (range 0-100). */ inline bool roll_chance_f(float chance) @@ -58,7 +62,7 @@ inline bool roll_chance_i(int chance) /* * SFMT wrapper satisfying UniformRandomNumberGenerator concept for use in <random> algorithms */ -class SFMTEngine +class TC_COMMON_API SFMTEngine { public: typedef uint32 result_type; diff --git a/src/common/Utilities/StartProcess.cpp b/src/common/Utilities/StartProcess.cpp new file mode 100644 index 00000000000..f35c6de3b5c --- /dev/null +++ b/src/common/Utilities/StartProcess.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "StartProcess.h" + +#include <atomic> +#include <thread> +#include <functional> + +#include <boost/algorithm/string/join.hpp> +#include <boost/iostreams/stream.hpp> +#include <boost/iostreams/copy.hpp> +#include <boost/iostreams/concepts.hpp> +#include <boost/iostreams/device/file_descriptor.hpp> +#include <boost/process.hpp> +#include <boost/system/system_error.hpp> + +#include "Common.h" +#include "Log.h" + +using namespace boost::process; +using namespace boost::process::initializers; +using namespace boost::iostreams; + +namespace Trinity { + +template<typename T> +class TCLogSink +{ + T callback_; + +public: + typedef char char_type; + typedef sink_tag category; + + // Requires a callback type which has a void(std::string) signature + TCLogSink(T callback) + : callback_(std::move(callback)) { } + + std::streamsize write(const char* str, std::streamsize size) + { + callback_(std::string(str, size)); + return size; + } +}; + +template<typename T> +auto MakeTCLogSink(T&& callback) + -> TCLogSink<typename std::decay<T>::type> +{ + return { std::forward<T>(callback) }; +} + +template<typename T> +static int CreateChildProcess(T waiter, std::string const& executable, + std::vector<std::string> const& args, + std::string const& logger, std::string const& input, + bool secure) +{ + auto outPipe = create_pipe(); + auto errPipe = create_pipe(); + + Optional<file_descriptor_source> inputSource; + + if (!secure) + { + TC_LOG_TRACE(logger, "Starting process \"%s\" with arguments: \"%s\".", + executable.c_str(), boost::algorithm::join(args, " ").c_str()); + } + + // Start the child process + child c = [&] + { + if (!input.empty()) + { + inputSource = file_descriptor_source(input); + + // With binding stdin + return execute(run_exe(boost::filesystem::absolute(executable)), + set_args(args), + inherit_env(), + bind_stdin(*inputSource), + bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)), + bind_stderr(file_descriptor_sink(errPipe.sink, close_handle))); + } + else + { + // Without binding stdin + return execute(run_exe(boost::filesystem::absolute(executable)), + set_args(args), + inherit_env(), + bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)), + bind_stderr(file_descriptor_sink(errPipe.sink, close_handle))); + } + }(); + + file_descriptor_source outFd(outPipe.source, close_handle); + file_descriptor_source errFd(errPipe.source, close_handle); + + auto outInfo = MakeTCLogSink([&](std::string msg) + { + TC_LOG_INFO(logger, "%s", msg.c_str()); + }); + + auto outError = MakeTCLogSink([&](std::string msg) + { + TC_LOG_ERROR(logger, "%s", msg.c_str()); + }); + + copy(outFd, outInfo); + copy(errFd, outError); + + // Call the waiter in the current scope to prevent + // the streams from closing too early on leaving the scope. + int const result = waiter(c); + + if (!secure) + { + TC_LOG_TRACE(logger, ">> Process \"%s\" finished with return value %i.", + executable.c_str(), result); + } + + if (inputSource) + inputSource->close(); + + return result; +} + +int StartProcess(std::string const& executable, std::vector<std::string> const& args, + std::string const& logger, std::string input_file, bool secure) +{ + return CreateChildProcess([](child& c) -> int + { + try + { + return wait_for_exit(c); + } + catch (...) + { + return EXIT_FAILURE; + } + }, executable, args, logger, input_file, secure); +} + +class AsyncProcessResultImplementation + : public AsyncProcessResult +{ + std::string const executable; + std::vector<std::string> const args; + std::string const logger; + std::string const input_file; + bool const is_secure; + + std::atomic<bool> was_terminated; + + // Workaround for missing move support in boost < 1.57 + Optional<std::shared_ptr<std::future<int>>> result; + Optional<std::reference_wrapper<child>> my_child; + +public: + explicit AsyncProcessResultImplementation(std::string executable_, std::vector<std::string> args_, + std::string logger_, std::string input_file_, + bool secure) + : executable(std::move(executable_)), args(std::move(args_)), + logger(std::move(logger_)), input_file(input_file_), + is_secure(secure), was_terminated(false) { } + + AsyncProcessResultImplementation(AsyncProcessResultImplementation const&) = delete; + AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation const&) = delete; + AsyncProcessResultImplementation(AsyncProcessResultImplementation&&) = delete; + AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation&&) = delete; + + int StartProcess() + { + ASSERT(!my_child, "Process started already!"); + + return CreateChildProcess([&](child& c) -> int + { + int result; + my_child = std::reference_wrapper<child>(c); + + try + { + result = wait_for_exit(c); + } + catch (...) + { + result = EXIT_FAILURE; + } + + my_child.reset(); + return was_terminated ? EXIT_FAILURE : result; + + }, executable, args, logger, input_file, is_secure); + } + + void SetFuture(std::future<int> result_) + { + result = std::make_shared<std::future<int>>(std::move(result_)); + } + + /// Returns the future which contains the result of the process + /// as soon it is finished. + std::future<int>& GetFutureResult() override + { + ASSERT(*result, "The process wasn't started!"); + return **result; + } + + /// Tries to terminate the process + void Terminate() override + { + if (!my_child) + { + was_terminated = true; + try + { + terminate(my_child->get()); + } + catch(...) + { + // Do nothing + } + } + } +}; + +std::shared_ptr<AsyncProcessResult> + StartAsyncProcess(std::string executable, std::vector<std::string> args, + std::string logger, std::string input_file, bool secure) +{ + auto handle = std::make_shared<AsyncProcessResultImplementation>( + std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure); + + handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); })); + return handle; +} + +Optional<std::string> SearchExecutableInPath(std::string const& filename) +{ + try + { + auto result = search_path(filename); + if (result.empty()) + return boost::none; + else + return result; + } + catch (...) + { + return boost::none; + } +} + +} // namespace Trinity diff --git a/src/common/Utilities/StartProcess.h b/src/common/Utilities/StartProcess.h new file mode 100644 index 00000000000..3b380bd4f4e --- /dev/null +++ b/src/common/Utilities/StartProcess.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef Process_h__ +#define Process_h__ + +#include <future> +#include <memory> +#include "Common.h" + +namespace Trinity { + +/// Starts a process with the given arguments and parameters and will block +/// until the process is finished. +/// When an input path is given, the file will be routed to the processes stdin. +/// When the process is marked as secure no arguments are leaked to logs. +/// Note that most executables expect it's name as the first argument. +TC_COMMON_API int StartProcess(std::string const& executable, std::vector<std::string> const& args, + std::string const& logger, std::string input_file = "", + bool secure = false); + +/// Platform and library independent representation +/// of asynchronous process results +class AsyncProcessResult +{ +public: + virtual ~AsyncProcessResult() { } + + /// Returns the future which contains the result of the process + /// as soon it is finished. + virtual std::future<int>& GetFutureResult() = 0; + + /// Tries to terminate the process + virtual void Terminate() = 0; +}; + +/// Starts a process asynchronously with the given arguments and parameters and +/// returns an AsyncProcessResult immediately which is set, when the process exits. +/// When an input path is given, the file will be routed to the processes stdin. +/// When the process is marked as secure no arguments are leaked to logs. +/// Note that most executables expect it's name as the first argument. +TC_COMMON_API std::shared_ptr<AsyncProcessResult> + StartAsyncProcess(std::string executable, std::vector<std::string> args, + std::string logger, std::string input_file = "", + bool secure = false); + +/// Searches for the given executable in the PATH variable +/// and returns a present optional when it was found. +TC_COMMON_API Optional<std::string> SearchExecutableInPath(std::string const& filename); + +} // namespace Trinity + +#endif // Process_h__ diff --git a/src/common/Utilities/StringFormat.h b/src/common/Utilities/StringFormat.h index d85523bc11f..e21b1024e87 100644 --- a/src/common/Utilities/StringFormat.h +++ b/src/common/Utilities/StringFormat.h @@ -19,7 +19,7 @@ #ifndef TRINITYCORE_STRING_FORMAT_H #define TRINITYCORE_STRING_FORMAT_H -#include "format.h" +#include "cppformat/format.h" namespace Trinity { diff --git a/src/common/Utilities/TaskScheduler.h b/src/common/Utilities/TaskScheduler.h index 8cf5d914128..6784c968683 100644 --- a/src/common/Utilities/TaskScheduler.h +++ b/src/common/Utilities/TaskScheduler.h @@ -46,7 +46,7 @@ class TaskContext; /// 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 +class TC_COMMON_API TaskScheduler { friend class TaskContext; @@ -131,7 +131,7 @@ class TaskScheduler }; }; - class TaskQueue + class TC_COMMON_API TaskQueue { std::multiset<TaskContainer, Compare> container; @@ -401,14 +401,14 @@ private: auto const milli_max = std::chrono::duration_cast<std::chrono::milliseconds>(max); // TC specific: use SFMT URandom - return std::chrono::milliseconds(urand(milli_min.count(), milli_max.count())); + return std::chrono::milliseconds(urand(uint32(milli_min.count()), uint32(milli_max.count()))); } /// Dispatch remaining tasks void Dispatch(success_t const& callback); }; -class TaskContext +class TC_COMMON_API TaskContext { friend class TaskScheduler; diff --git a/src/common/Utilities/Timer.h b/src/common/Utilities/Timer.h index cdce08caaf0..f66bb90c98e 100644 --- a/src/common/Utilities/Timer.h +++ b/src/common/Utilities/Timer.h @@ -25,9 +25,9 @@ inline uint32 getMSTime() { using namespace std::chrono; - static const system_clock::time_point ApplicationStartTime = system_clock::now(); + static const steady_clock::time_point ApplicationStartTime = steady_clock::now(); - return uint32(duration_cast<milliseconds>(system_clock::now() - ApplicationStartTime).count()); + return uint32(duration_cast<milliseconds>(steady_clock::now() - ApplicationStartTime).count()); } inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime) diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp index 1360253294f..3d8cda66d48 100644 --- a/src/common/Utilities/Util.cpp +++ b/src/common/Utilities/Util.cpp @@ -22,6 +22,7 @@ #include "utf8.h" #include "Errors.h" // for ASSERT #include <stdarg.h> +#include <boost/algorithm/string/case_conv.hpp> #if COMPILER == COMPILER_GNU #include <sys/socket.h> @@ -218,22 +219,29 @@ bool IsIPAddress(char const* ipaddress) } /// create PID file -uint32 CreatePIDFile(const std::string& filename) +uint32 CreatePIDFile(std::string const& filename) { - FILE* pid_file = fopen (filename.c_str(), "w" ); + FILE* pid_file = fopen(filename.c_str(), "w"); if (pid_file == NULL) return 0; + uint32 pid = GetPID(); + + fprintf(pid_file, "%u", pid); + fclose(pid_file); + + return pid; +} + +uint32 GetPID() +{ #ifdef _WIN32 DWORD pid = GetCurrentProcessId(); #else pid_t pid = getpid(); #endif - fprintf(pid_file, "%u", pid ); - fclose(pid_file); - - return (uint32)pid; + return uint32(pid); } size_t utf8length(std::string& utf8str) @@ -418,7 +426,7 @@ bool utf8ToConsole(const std::string& utf8str, std::string& conStr) return false; conStr.resize(wstr.size()); - CharToOemBuffW(&wstr[0], &conStr[0], wstr.size()); + CharToOemBuffW(&wstr[0], &conStr[0], uint32(wstr.size())); #else // not implemented yet conStr = utf8str; @@ -432,7 +440,7 @@ bool consoleToUtf8(const std::string& conStr, std::string& utf8str) #if PLATFORM == PLATFORM_WINDOWS std::wstring wstr; wstr.resize(conStr.size()); - OemToCharBuffW(&conStr[0], &wstr[0], conStr.size()); + OemToCharBuffW(&conStr[0], &wstr[0], uint32(conStr.size())); return WStrToUtf8(wstr, utf8str); #else @@ -450,7 +458,7 @@ bool Utf8FitTo(const std::string& str, std::wstring const& search) return false; // converting to lower case - wstrToLower( temp ); + wstrToLower(temp); if (temp.find(search) == std::wstring::npos) return false; @@ -469,10 +477,10 @@ void utf8printf(FILE* out, const char *str, ...) void vutf8printf(FILE* out, const char *str, va_list* ap) { #if PLATFORM == PLATFORM_WINDOWS - char temp_buf[32*1024]; - wchar_t wtemp_buf[32*1024]; + char temp_buf[32 * 1024]; + wchar_t wtemp_buf[32 * 1024]; - size_t temp_len = vsnprintf(temp_buf, 32*1024, str, *ap); + size_t temp_len = vsnprintf(temp_buf, 32 * 1024, str, *ap); //vsnprintf returns -1 if the buffer is too small if (temp_len == size_t(-1)) temp_len = 32*1024-1; @@ -480,13 +488,24 @@ void vutf8printf(FILE* out, const char *str, va_list* ap) size_t wtemp_len = 32*1024-1; Utf8toWStr(temp_buf, temp_len, wtemp_buf, wtemp_len); - CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len+1); + CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], uint32(wtemp_len + 1)); fprintf(out, "%s", temp_buf); #else vfprintf(out, str, *ap); #endif } +bool Utf8ToUpperOnlyLatin(std::string& utf8String) +{ + std::wstring wstr; + if (!Utf8toWStr(utf8String, wstr)) + return false; + + std::transform(wstr.begin(), wstr.end(), wstr.begin(), wcharToUpperOnlyLatin); + + return WStrToUtf8(wstr, utf8String); +} + std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse /* = false */) { int32 init = 0; @@ -510,3 +529,34 @@ std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse return ss.str(); } + +void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= false*/) +{ + // string must have even number of characters + if (str.length() & 1) + return; + + int32 init = 0; + int32 end = int32(str.length()); + int8 op = 1; + + if (reverse) + { + init = int32(str.length() - 2); + end = -2; + op = -1; + } + + uint32 j = 0; + for (int32 i = init; i != end; i += 2 * op) + { + char buffer[3] = { str[i], str[i + 1], '\0' }; + out[j++] = uint8(strtoul(buffer, NULL, 16)); + } +} + +bool StringToBool(std::string const& str) +{ + std::string lowerStr = boost::algorithm::to_lower_copy(str); + return lowerStr == "1" || lowerStr == "true" || lowerStr == "yes"; +} diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index ab5cabca8d2..cc68f3b2237 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -40,7 +40,7 @@ template<typename T, class S> struct Finder bool operator()(const std::pair<int, S> &obj) { return obj.second.*idMember_ == val_; } }; -class Tokenizer +class TC_COMMON_API Tokenizer { public: typedef std::vector<char const*> StorageType; @@ -68,15 +68,15 @@ private: StorageType m_storage; }; -void stripLineInvisibleChars(std::string &src); +TC_COMMON_API void stripLineInvisibleChars(std::string &src); -int32 MoneyStringToMoney(const std::string& moneyString); +TC_COMMON_API int32 MoneyStringToMoney(const std::string& moneyString); -struct tm* localtime_r(const time_t* time, struct tm *result); +TC_COMMON_API 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); +TC_COMMON_API std::string secsToTimeString(uint64 timeInSecs, bool shortText = false, bool hoursOnly = false); +TC_COMMON_API uint32 TimeStringToSecs(const std::string& timestring); +TC_COMMON_API std::string TimeToTimestampStr(time_t t); inline void ApplyPercentModFloatVar(float& var, float val, bool apply) { @@ -111,20 +111,20 @@ inline T RoundToInterval(T& num, T floor, T ceil) } // UTF8 handling -bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr); +TC_COMMON_API 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); +TC_COMMON_API 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); +TC_COMMON_API bool WStrToUtf8(std::wstring const& wstr, std::string& utf8str); // size==real string size -bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str); +TC_COMMON_API 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); +TC_COMMON_API size_t utf8length(std::string& utf8str); // set string to "" if invalid utf8 sequence +TC_COMMON_API void utf8truncate(std::string& utf8str, size_t len); inline bool isBasicLatinCharacter(wchar_t wchar) { @@ -303,19 +303,24 @@ inline void wstrToLower(std::wstring& str) std::transform( str.begin(), str.end(), str.begin(), wcharToLower ); } -std::wstring GetMainPartOfName(std::wstring const& wname, uint32 declension); +TC_COMMON_API 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); +TC_COMMON_API bool utf8ToConsole(const std::string& utf8str, std::string& conStr); +TC_COMMON_API bool consoleToUtf8(const std::string& conStr, std::string& utf8str); +TC_COMMON_API bool Utf8FitTo(const std::string& str, std::wstring const& search); +TC_COMMON_API void utf8printf(FILE* out, const char *str, ...); +TC_COMMON_API void vutf8printf(FILE* out, const char *str, va_list* ap); +TC_COMMON_API bool Utf8ToUpperOnlyLatin(std::string& utf8String); -bool IsIPAddress(char const* ipaddress); +TC_COMMON_API bool IsIPAddress(char const* ipaddress); -uint32 CreatePIDFile(const std::string& filename); +TC_COMMON_API uint32 CreatePIDFile(std::string const& filename); +TC_COMMON_API uint32 GetPID(); -std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false); +TC_COMMON_API std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false); +TC_COMMON_API void HexStrToByteArray(std::string const& str, uint8* out, bool reverse = false); + +TC_COMMON_API bool StringToBool(std::string const& str); // simple class for not-modifyable list template <typename T> |
