diff --git a/stationapi/.gitignore b/stationapi/.gitignore new file mode 100755 index 0000000..3ac8df7 --- /dev/null +++ b/stationapi/.gitignore @@ -0,0 +1 @@ +externals/udplibrary \ No newline at end of file diff --git a/stationapi/.travis.yml b/stationapi/.travis.yml new file mode 100755 index 0000000..07f1d9a --- /dev/null +++ b/stationapi/.travis.yml @@ -0,0 +1,30 @@ +sudo: required +dist: trusty +language: cpp +compiler: + - gcc +script: + - git clone https://bitbucket.org/swgathrawn/udplibrary.git externals/udplibrary + - mkdir build + - cd build + - cmake .. && make + +notifications: + email: + recipients: builds@eb2.co + on_success: change + on_failure: always + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 + - gcc-5 + - libboost-dev + - libboost-program-options-dev + - libsqlite3-dev + +install: + - if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi diff --git a/stationapi/CMakeLists.txt b/stationapi/CMakeLists.txt new file mode 100755 index 0000000..5d6e370 --- /dev/null +++ b/stationapi/CMakeLists.txt @@ -0,0 +1,46 @@ +# don't allow in-source project generation +if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(WARNING "\n\nCMake generation is not allowed within the source directory!\n") + message(STATUS "Cancelling CMake and cleaning up source tree...") + execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_BINARY_DIR}/CMakeFiles") + math(EXPR Crash 0/0) + message(FATAL_ERROR "CMake should have crashed - this is a failsafe in case the call used to trigger the crash gets fixed.") +endif() + +cmake_minimum_required(VERSION 2.8) + +project(stationapi CXX) + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") + +include(GroupSourceByFilesystem) +include(ModernCpp) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +if (EXISTS "${PROJECT_SOURCE_DIR}/externals/udplibrary/CMakeLists.txt") + message(STATUS "Detected udplibrary") + set(HAVE_UDPLIBRARY 1) +else() + message(FATAL_ERROR "udplibrary required... copy from swg source to the externals directory") +endif() + +add_definitions(-DBOOST_ALL_NO_LIB) +set(Boost_USE_STATIC_LIBS ON) +set(Boost_USE_MULTITHREADED ON) + +find_package(Boost COMPONENTS program_options REQUIRED) +find_package(SQLite3 REQUIRED) + +if (NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-write-strings") +endif () + +add_subdirectory(externals) +add_subdirectory(src) +add_subdirectory(tests) + +configure_file(extras/logger.cfg.dist bin/logger.cfg COPYONLY) +configure_file(extras/swgchat.cfg.dist bin/swgchat.cfg COPYONLY) +configure_file(extras/chat.db.dist bin/chat.db COPYONLY) diff --git a/stationapi/LICENSE.txt b/stationapi/LICENSE.txt new file mode 100755 index 0000000..574f072 --- /dev/null +++ b/stationapi/LICENSE.txt @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2016 Eric Barr + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/stationapi/README.md b/stationapi/README.md new file mode 100755 index 0000000..b88c27d --- /dev/null +++ b/stationapi/README.md @@ -0,0 +1,51 @@ +# stationapi [![Build Status](https://travis-ci.org/apathyboy/stationapi.svg?branch=master)](https://travis-ci.com/apathyboy/stationapi) # + +A base library at the core of applications that implement chat and login functionality across galaxies. + +# stationchat + +An open implementation of the chat gateway that SOE based games used to provide various social communication features such as mail, custom chat rooms, friend management, etc. + +Like my work and want to support my free and open-source contributions? + +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8KCAU8HB9J7YU) + +## Implementation ## + +Uses the SOE libraries to implement chat features in a standalone utility. Ideally, the completed implementation would allow for multiple galaxies to connect and allow players to communicate across them. + +## External Dependencies ## + +* c++14 compatible compiler +* boost::program_options +* sqlite3 +* udplibrary - bundled in the Star Wars Galaxies official source + +## Building ## + + chmod 777 build.sh + ./build.sh + +## Database Initialization ## + +By default, a clean database instance is provided and placed with the default configuration files in the **build/bin** directory; therefore, nothing needs to be done for new installations, the db is already created and placed in the appropriate location. + +To create a new, clean database instance, use the following commands: + + sqlite3 chat.db + sqlite> .read /path/to/extras/init_database.sql + +Then update the **database_path** config option with the full path to the database. + +## Running ## + +A default configuration and database is created when building the project. Configure the listen address/ports in **build/bin/stationchat.cfg**. Then run the following commands from the project root: + +### Linux ### + + cd build/bin + ./stationchat + +## Final Notes ## + +It is recommended to copy the **build/bin** directory to another location after building to ensure the configuration files are not overwritten by future changes to the default versions of these files. diff --git a/stationapi/build.sh b/stationapi/build.sh new file mode 100755 index 0000000..2ec11a9 --- /dev/null +++ b/stationapi/build.sh @@ -0,0 +1,10 @@ +# dependencies +sudo apt-get install libboost-dev libboost-program-options-dev sqlite3 libsqlite3-dev + +# copy udp library +cp -a udplibrary externals/udplibrary + +# make +rm -rf build; mkdir build; cd build +cmake .. +cmake --build . diff --git a/stationapi/build/CMakeCache.txt b/stationapi/build/CMakeCache.txt new file mode 100644 index 0000000..afd00c9 --- /dev/null +++ b/stationapi/build/CMakeCache.txt @@ -0,0 +1,372 @@ +# This is the CMakeCache file. +# For build in directory: /root/swg-main/stationapi/build +# It was generated by CMake: /usr/bin/cmake +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//The directory containing a CMake configuration file for Boost. +Boost_DIR:PATH=Boost_DIR-NOTFOUND + +//Path to a file. +Boost_INCLUDE_DIR:PATH=/usr/include + +//Boost library directory DEBUG +Boost_LIBRARY_DIR_DEBUG:PATH=/usr/lib/x86_64-linux-gnu + +//Boost library directory RELEASE +Boost_LIBRARY_DIR_RELEASE:PATH=/usr/lib/x86_64-linux-gnu + +//Boost program_options library (debug) +Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_program_options.a + +//Boost program_options library (release) +Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_program_options.a + +//Path to a program. +CMAKE_AR:FILEPATH=/usr/bin/ar + +//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or +// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel. +CMAKE_BUILD_TYPE:STRING= + +//Enable/Disable color output during build. +CMAKE_COLOR_MAKEFILE:BOOL=ON + +//CXX compiler +CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++ + +//Flags used by the compiler during all build types. +CMAKE_CXX_FLAGS:STRING= + +//Flags used by the compiler during debug builds. +CMAKE_CXX_FLAGS_DEBUG:STRING=-g + +//Flags used by the compiler during release builds for minimum +// size. +CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG + +//Flags used by the compiler during release builds. +CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG + +//Flags used by the compiler during release builds with debug info. +CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG + +//C compiler +CMAKE_C_COMPILER:FILEPATH=/usr/bin/cc + +//Flags used by the compiler during all build types. +CMAKE_C_FLAGS:STRING= + +//Flags used by the compiler during debug builds. +CMAKE_C_FLAGS_DEBUG:STRING=-g + +//Flags used by the compiler during release builds for minimum +// size. +CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG + +//Flags used by the compiler during release builds. +CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG + +//Flags used by the compiler during release builds with debug info. +CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG + +//Flags used by the linker. +CMAKE_EXE_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Enable/Disable output of compile commands during generation. +CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF + +//Install path prefix, prepended onto install directories. +CMAKE_INSTALL_PREFIX:PATH=/usr/local + +//Path to a program. +CMAKE_LINKER:FILEPATH=/usr/bin/ld + +//Path to a program. +CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make + +//Flags used by the linker during the creation of modules. +CMAKE_MODULE_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_NM:FILEPATH=/usr/bin/nm + +//Path to a program. +CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy + +//Path to a program. +CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump + +//Value Computed by CMake +CMAKE_PROJECT_NAME:STATIC=stationapi + +//Path to a program. +CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib + +//Flags used by the linker during the creation of dll's. +CMAKE_SHARED_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//If set, runtime paths are not added when installing shared libraries, +// but are added when building. +CMAKE_SKIP_INSTALL_RPATH:BOOL=NO + +//If set, runtime paths are not added when using shared libraries. +CMAKE_SKIP_RPATH:BOOL=NO + +//Flags used by the linker during the creation of static libraries. +CMAKE_STATIC_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_STRIP:FILEPATH=/usr/bin/strip + +//If this value is on, makefiles will be generated without the +// .SILENT directive, and all commands will be echoed to the console +// during the make. This is useful for debugging only. With Visual +// Studio IDE projects all commands are done without /nologo. +CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE + +//Path to a file. +SQLite3_INCLUDE_DIR:PATH=/usr/include + +//Path to a library. +SQLite3_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libsqlite3.so + +//Value Computed by CMake +stationapi_BINARY_DIR:STATIC=/root/swg-main/stationapi/build + +//Dependencies for target +stationapi_LIB_DEPENDS:STATIC= + +//Value Computed by CMake +stationapi_SOURCE_DIR:STATIC=/root/swg-main/stationapi + +//Value Computed by CMake +udplibrary_BINARY_DIR:STATIC=/root/swg-main/stationapi/build/externals/udplibrary + +//Dependencies for target +udplibrary_LIB_DEPENDS:STATIC= + +//Value Computed by CMake +udplibrary_SOURCE_DIR:STATIC=/root/swg-main/stationapi/externals/udplibrary + + +######################## +# INTERNAL cache entries +######################## + +//ADVANCED property for variable: Boost_DIR +Boost_DIR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: Boost_INCLUDE_DIR +Boost_INCLUDE_DIR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: Boost_LIBRARY_DIR_DEBUG +Boost_LIBRARY_DIR_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: Boost_LIBRARY_DIR_RELEASE +Boost_LIBRARY_DIR_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG +Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE +Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_AR +CMAKE_AR-ADVANCED:INTERNAL=1 +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=/root/swg-main/stationapi/build +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=7 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=2 +//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE +CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=/usr/bin/cmake +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest +//ADVANCED property for variable: CMAKE_CXX_COMPILER +CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS +CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG +CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL +CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE +CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO +CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_COMPILER +CMAKE_C_COMPILER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS +CMAKE_C_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG +CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL +CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE +CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO +CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//Executable file format +CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS +CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG +CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE +CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS +CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1 +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Unix Makefiles +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=/root/swg-main/stationapi +//Install .so files without execute permission. +CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1 +//ADVANCED property for variable: CMAKE_LINKER +CMAKE_LINKER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MAKE_PROGRAM +CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS +CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG +CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE +CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_NM +CMAKE_NM-ADVANCED:INTERNAL=1 +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=7 +//ADVANCED property for variable: CMAKE_OBJCOPY +CMAKE_OBJCOPY-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_OBJDUMP +CMAKE_OBJDUMP-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RANLIB +CMAKE_RANLIB-ADVANCED:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.7 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS +CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG +CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE +CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH +CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_RPATH +CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS +CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG +CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE +CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STRIP +CMAKE_STRIP-ADVANCED:INTERNAL=1 +//uname command +CMAKE_UNAME:INTERNAL=/bin/uname +//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE +CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 +//Details about finding SQLite3 +FIND_PACKAGE_MESSAGE_DETAILS_SQLite3:INTERNAL=[/usr/lib/x86_64-linux-gnu/libsqlite3.so][/usr/include][v()] +//ADVANCED property for variable: SQLite3_INCLUDE_DIR +SQLite3_INCLUDE_DIR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: SQLite3_LIBRARY +SQLite3_LIBRARY-ADVANCED:INTERNAL=1 +//Components requested for this build tree. +_Boost_COMPONENTS_SEARCHED:INTERNAL=program_options +//Last used Boost_INCLUDE_DIR value. +_Boost_INCLUDE_DIR_LAST:INTERNAL=/usr/include +//Last used Boost_LIBRARY_DIR_DEBUG value. +_Boost_LIBRARY_DIR_DEBUG_LAST:INTERNAL=/usr/lib/x86_64-linux-gnu +//Last used Boost_LIBRARY_DIR_RELEASE value. +_Boost_LIBRARY_DIR_RELEASE_LAST:INTERNAL=/usr/lib/x86_64-linux-gnu +//Last used Boost_NAMESPACE value. +_Boost_NAMESPACE_LAST:INTERNAL=boost +//Last used Boost_USE_MULTITHREADED value. +_Boost_USE_MULTITHREADED_LAST:INTERNAL=ON +//Last used Boost_USE_STATIC_LIBS value. +_Boost_USE_STATIC_LIBS_LAST:INTERNAL=ON + diff --git a/stationapi/build/CMakeFiles/3.7.2/CMakeCCompiler.cmake b/stationapi/build/CMakeFiles/3.7.2/CMakeCCompiler.cmake new file mode 100644 index 0000000..d628b11 --- /dev/null +++ b/stationapi/build/CMakeFiles/3.7.2/CMakeCCompiler.cmake @@ -0,0 +1,68 @@ +set(CMAKE_C_COMPILER "/usr/bin/cc") +set(CMAKE_C_COMPILER_ARG1 "") +set(CMAKE_C_COMPILER_ID "GNU") +set(CMAKE_C_COMPILER_VERSION "6.3.0") +set(CMAKE_C_COMPILER_WRAPPER "") +set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "11") +set(CMAKE_C_COMPILE_FEATURES "c_function_prototypes;c_restrict;c_variadic_macros;c_static_assert") +set(CMAKE_C90_COMPILE_FEATURES "c_function_prototypes") +set(CMAKE_C99_COMPILE_FEATURES "c_restrict;c_variadic_macros") +set(CMAKE_C11_COMPILE_FEATURES "c_static_assert") + +set(CMAKE_C_PLATFORM_ID "Linux") +set(CMAKE_C_SIMULATE_ID "") +set(CMAKE_C_SIMULATE_VERSION "") + +set(CMAKE_AR "/usr/bin/ar") +set(CMAKE_RANLIB "/usr/bin/ranlib") +set(CMAKE_LINKER "/usr/bin/ld") +set(CMAKE_COMPILER_IS_GNUCC 1) +set(CMAKE_C_COMPILER_LOADED 1) +set(CMAKE_C_COMPILER_WORKS TRUE) +set(CMAKE_C_ABI_COMPILED TRUE) +set(CMAKE_COMPILER_IS_MINGW ) +set(CMAKE_COMPILER_IS_CYGWIN ) +if(CMAKE_COMPILER_IS_CYGWIN) + set(CYGWIN 1) + set(UNIX 1) +endif() + +set(CMAKE_C_COMPILER_ENV_VAR "CC") + +if(CMAKE_COMPILER_IS_MINGW) + set(MINGW 1) +endif() +set(CMAKE_C_COMPILER_ID_RUN 1) +set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m) +set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC) +set(CMAKE_C_LINKER_PREFERENCE 10) + +# Save compiler ABI information. +set(CMAKE_C_SIZEOF_DATA_PTR "8") +set(CMAKE_C_COMPILER_ABI "ELF") +set(CMAKE_C_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") + +if(CMAKE_C_SIZEOF_DATA_PTR) + set(CMAKE_SIZEOF_VOID_P "${CMAKE_C_SIZEOF_DATA_PTR}") +endif() + +if(CMAKE_C_COMPILER_ABI) + set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_C_COMPILER_ABI}") +endif() + +if(CMAKE_C_LIBRARY_ARCHITECTURE) + set(CMAKE_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") +endif() + +set(CMAKE_C_CL_SHOWINCLUDES_PREFIX "") +if(CMAKE_C_CL_SHOWINCLUDES_PREFIX) + set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_C_CL_SHOWINCLUDES_PREFIX}") +endif() + + + + + +set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "c") +set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/6;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib") +set(CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "") diff --git a/stationapi/build/CMakeFiles/3.7.2/CMakeCXXCompiler.cmake b/stationapi/build/CMakeFiles/3.7.2/CMakeCXXCompiler.cmake new file mode 100644 index 0000000..0018983 --- /dev/null +++ b/stationapi/build/CMakeFiles/3.7.2/CMakeCXXCompiler.cmake @@ -0,0 +1,69 @@ +set(CMAKE_CXX_COMPILER "/usr/bin/c++") +set(CMAKE_CXX_COMPILER_ARG1 "") +set(CMAKE_CXX_COMPILER_ID "GNU") +set(CMAKE_CXX_COMPILER_VERSION "6.3.0") +set(CMAKE_CXX_COMPILER_WRAPPER "") +set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "14") +set(CMAKE_CXX_COMPILE_FEATURES "cxx_template_template_parameters;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") +set(CMAKE_CXX98_COMPILE_FEATURES "cxx_template_template_parameters") +set(CMAKE_CXX11_COMPILE_FEATURES "cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") +set(CMAKE_CXX14_COMPILE_FEATURES "cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") + +set(CMAKE_CXX_PLATFORM_ID "Linux") +set(CMAKE_CXX_SIMULATE_ID "") +set(CMAKE_CXX_SIMULATE_VERSION "") + +set(CMAKE_AR "/usr/bin/ar") +set(CMAKE_RANLIB "/usr/bin/ranlib") +set(CMAKE_LINKER "/usr/bin/ld") +set(CMAKE_COMPILER_IS_GNUCXX 1) +set(CMAKE_CXX_COMPILER_LOADED 1) +set(CMAKE_CXX_COMPILER_WORKS TRUE) +set(CMAKE_CXX_ABI_COMPILED TRUE) +set(CMAKE_COMPILER_IS_MINGW ) +set(CMAKE_COMPILER_IS_CYGWIN ) +if(CMAKE_COMPILER_IS_CYGWIN) + set(CYGWIN 1) + set(UNIX 1) +endif() + +set(CMAKE_CXX_COMPILER_ENV_VAR "CXX") + +if(CMAKE_COMPILER_IS_MINGW) + set(MINGW 1) +endif() +set(CMAKE_CXX_COMPILER_ID_RUN 1) +set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC) +set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;mm;CPP) +set(CMAKE_CXX_LINKER_PREFERENCE 30) +set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1) + +# Save compiler ABI information. +set(CMAKE_CXX_SIZEOF_DATA_PTR "8") +set(CMAKE_CXX_COMPILER_ABI "ELF") +set(CMAKE_CXX_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") + +if(CMAKE_CXX_SIZEOF_DATA_PTR) + set(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}") +endif() + +if(CMAKE_CXX_COMPILER_ABI) + set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}") +endif() + +if(CMAKE_CXX_LIBRARY_ARCHITECTURE) + set(CMAKE_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") +endif() + +set(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX "") +if(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX) + set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_CXX_CL_SHOWINCLUDES_PREFIX}") +endif() + + + + + +set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "stdc++;m;c") +set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/6;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib") +set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "") diff --git a/stationapi/build/CMakeFiles/3.7.2/CMakeDetermineCompilerABI_C.bin b/stationapi/build/CMakeFiles/3.7.2/CMakeDetermineCompilerABI_C.bin new file mode 100755 index 0000000..3ae043e Binary files /dev/null and b/stationapi/build/CMakeFiles/3.7.2/CMakeDetermineCompilerABI_C.bin differ diff --git a/stationapi/build/CMakeFiles/3.7.2/CMakeDetermineCompilerABI_CXX.bin b/stationapi/build/CMakeFiles/3.7.2/CMakeDetermineCompilerABI_CXX.bin new file mode 100755 index 0000000..6f57927 Binary files /dev/null and b/stationapi/build/CMakeFiles/3.7.2/CMakeDetermineCompilerABI_CXX.bin differ diff --git a/stationapi/build/CMakeFiles/3.7.2/CMakeSystem.cmake b/stationapi/build/CMakeFiles/3.7.2/CMakeSystem.cmake new file mode 100644 index 0000000..365ce74 --- /dev/null +++ b/stationapi/build/CMakeFiles/3.7.2/CMakeSystem.cmake @@ -0,0 +1,15 @@ +set(CMAKE_HOST_SYSTEM "Linux-4.9.0-6-amd64") +set(CMAKE_HOST_SYSTEM_NAME "Linux") +set(CMAKE_HOST_SYSTEM_VERSION "4.9.0-6-amd64") +set(CMAKE_HOST_SYSTEM_PROCESSOR "x86_64") + + + +set(CMAKE_SYSTEM "Linux-4.9.0-6-amd64") +set(CMAKE_SYSTEM_NAME "Linux") +set(CMAKE_SYSTEM_VERSION "4.9.0-6-amd64") +set(CMAKE_SYSTEM_PROCESSOR "x86_64") + +set(CMAKE_CROSSCOMPILING "FALSE") + +set(CMAKE_SYSTEM_LOADED 1) diff --git a/stationapi/build/CMakeFiles/3.7.2/CompilerIdC/CMakeCCompilerId.c b/stationapi/build/CMakeFiles/3.7.2/CompilerIdC/CMakeCCompilerId.c new file mode 100644 index 0000000..512e360 --- /dev/null +++ b/stationapi/build/CMakeFiles/3.7.2/CompilerIdC/CMakeCCompilerId.c @@ -0,0 +1,561 @@ +#ifdef __cplusplus +# error "A C++ compiler has been selected for C." +#endif + +#if defined(__18CXX) +# define ID_VOID_MAIN +#endif +#if defined(__CLASSIC_C__) +/* cv-qualifiers did not exist in K&R C */ +# define const +# define volatile +#endif + + +/* Version number components: V=Version, R=Revision, P=Patch + Version date components: YYYY=Year, MM=Month, DD=Day */ + +#if defined(__INTEL_COMPILER) || defined(__ICC) +# define COMPILER_ID "Intel" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif + /* __INTEL_COMPILER = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100) +# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10) +# if defined(__INTEL_COMPILER_UPDATE) +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE) +# else +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10) +# endif +# if defined(__INTEL_COMPILER_BUILD_DATE) + /* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */ +# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE) +# endif +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif + +#elif defined(__PATHCC__) +# define COMPILER_ID "PathScale" +# define COMPILER_VERSION_MAJOR DEC(__PATHCC__) +# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__) +# if defined(__PATHCC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__) +# endif + +#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__) +# define COMPILER_ID "Embarcadero" +# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF) +# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF) +# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF) + +#elif defined(__BORLANDC__) +# define COMPILER_ID "Borland" + /* __BORLANDC__ = 0xVRR */ +# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8) +# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF) + +#elif defined(__WATCOMC__) && __WATCOMC__ < 1200 +# define COMPILER_ID "Watcom" + /* __WATCOMC__ = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__WATCOMC__) +# define COMPILER_ID "OpenWatcom" + /* __WATCOMC__ = VVRP + 1100 */ +# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__SUNPRO_C) +# define COMPILER_ID "SunPro" +# if __SUNPRO_C >= 0x5100 + /* __SUNPRO_C = 0xVRRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>12) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF) +# else + /* __SUNPRO_CC = 0xVRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>8) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF) +# endif + +#elif defined(__HP_cc) +# define COMPILER_ID "HP" + /* __HP_cc = VVRRPP */ +# define COMPILER_VERSION_MAJOR DEC(__HP_cc/10000) +# define COMPILER_VERSION_MINOR DEC(__HP_cc/100 % 100) +# define COMPILER_VERSION_PATCH DEC(__HP_cc % 100) + +#elif defined(__DECC) +# define COMPILER_ID "Compaq" + /* __DECC_VER = VVRRTPPPP */ +# define COMPILER_VERSION_MAJOR DEC(__DECC_VER/10000000) +# define COMPILER_VERSION_MINOR DEC(__DECC_VER/100000 % 100) +# define COMPILER_VERSION_PATCH DEC(__DECC_VER % 10000) + +#elif defined(__IBMC__) && defined(__COMPILER_VER__) +# define COMPILER_ID "zOS" + /* __IBMC__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10) + +#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ >= 800 +# define COMPILER_ID "XL" + /* __IBMC__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10) + +#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ < 800 +# define COMPILER_ID "VisualAge" + /* __IBMC__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10) + +#elif defined(__PGI) +# define COMPILER_ID "PGI" +# define COMPILER_VERSION_MAJOR DEC(__PGIC__) +# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__) +# if defined(__PGIC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__) +# endif + +#elif defined(_CRAYC) +# define COMPILER_ID "Cray" +# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR) +# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR) + +#elif defined(__TI_COMPILER_VERSION__) +# define COMPILER_ID "TI" + /* __TI_COMPILER_VERSION__ = VVVRRRPPP */ +# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000) +# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000) +# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000) + +#elif defined(__FUJITSU) || defined(__FCC_VERSION) || defined(__fcc_version) +# define COMPILER_ID "Fujitsu" + +#elif defined(__TINYC__) +# define COMPILER_ID "TinyCC" + +#elif defined(__BCC__) +# define COMPILER_ID "Bruce" + +#elif defined(__SCO_VERSION__) +# define COMPILER_ID "SCO" + +#elif defined(__clang__) && defined(__apple_build_version__) +# define COMPILER_ID "AppleClang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif +# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__) + +#elif defined(__clang__) +# define COMPILER_ID "Clang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif + +#elif defined(__GNUC__) +# define COMPILER_ID "GNU" +# define COMPILER_VERSION_MAJOR DEC(__GNUC__) +# if defined(__GNUC_MINOR__) +# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__) +# endif +# if defined(__GNUC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__) +# endif + +#elif defined(_MSC_VER) +# define COMPILER_ID "MSVC" + /* _MSC_VER = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100) +# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100) +# if defined(_MSC_FULL_VER) +# if _MSC_VER >= 1400 + /* _MSC_FULL_VER = VVRRPPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000) +# else + /* _MSC_FULL_VER = VVRRPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000) +# endif +# endif +# if defined(_MSC_BUILD) +# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD) +# endif + +#elif defined(__VISUALDSPVERSION__) || defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__) +# define COMPILER_ID "ADSP" +#if defined(__VISUALDSPVERSION__) + /* __VISUALDSPVERSION__ = 0xVVRRPP00 */ +# define COMPILER_VERSION_MAJOR HEX(__VISUALDSPVERSION__>>24) +# define COMPILER_VERSION_MINOR HEX(__VISUALDSPVERSION__>>16 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__VISUALDSPVERSION__>>8 & 0xFF) +#endif + +#elif defined(__IAR_SYSTEMS_ICC__ ) || defined(__IAR_SYSTEMS_ICC) +# define COMPILER_ID "IAR" + +#elif defined(__ARMCC_VERSION) +# define COMPILER_ID "ARMCC" +#if __ARMCC_VERSION >= 1000000 + /* __ARMCC_VERSION = VRRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#else + /* __ARMCC_VERSION = VRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#endif + + +#elif defined(SDCC) +# define COMPILER_ID "SDCC" + /* SDCC = VRP */ +# define COMPILER_VERSION_MAJOR DEC(SDCC/100) +# define COMPILER_VERSION_MINOR DEC(SDCC/10 % 10) +# define COMPILER_VERSION_PATCH DEC(SDCC % 10) + +#elif defined(_SGI_COMPILER_VERSION) || defined(_COMPILER_VERSION) +# define COMPILER_ID "MIPSpro" +# if defined(_SGI_COMPILER_VERSION) + /* _SGI_COMPILER_VERSION = VRP */ +# define COMPILER_VERSION_MAJOR DEC(_SGI_COMPILER_VERSION/100) +# define COMPILER_VERSION_MINOR DEC(_SGI_COMPILER_VERSION/10 % 10) +# define COMPILER_VERSION_PATCH DEC(_SGI_COMPILER_VERSION % 10) +# else + /* _COMPILER_VERSION = VRP */ +# define COMPILER_VERSION_MAJOR DEC(_COMPILER_VERSION/100) +# define COMPILER_VERSION_MINOR DEC(_COMPILER_VERSION/10 % 10) +# define COMPILER_VERSION_PATCH DEC(_COMPILER_VERSION % 10) +# endif + + +/* These compilers are either not known or too old to define an + identification macro. Try to identify the platform and guess that + it is the native compiler. */ +#elif defined(__sgi) +# define COMPILER_ID "MIPSpro" + +#elif defined(__hpux) || defined(__hpua) +# define COMPILER_ID "HP" + +#else /* unknown compiler */ +# define COMPILER_ID "" +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]"; +#ifdef SIMULATE_ID +char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; +#endif + +#ifdef __QNXNTO__ +char const* qnxnto = "INFO" ":" "qnxnto[]"; +#endif + +#if defined(__CRAYXE) || defined(__CRAYXC) +char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]"; +#endif + +#define STRINGIFY_HELPER(X) #X +#define STRINGIFY(X) STRINGIFY_HELPER(X) + +/* Identify known platforms by name. */ +#if defined(__linux) || defined(__linux__) || defined(linux) +# define PLATFORM_ID "Linux" + +#elif defined(__CYGWIN__) +# define PLATFORM_ID "Cygwin" + +#elif defined(__MINGW32__) +# define PLATFORM_ID "MinGW" + +#elif defined(__APPLE__) +# define PLATFORM_ID "Darwin" + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define PLATFORM_ID "Windows" + +#elif defined(__FreeBSD__) || defined(__FreeBSD) +# define PLATFORM_ID "FreeBSD" + +#elif defined(__NetBSD__) || defined(__NetBSD) +# define PLATFORM_ID "NetBSD" + +#elif defined(__OpenBSD__) || defined(__OPENBSD) +# define PLATFORM_ID "OpenBSD" + +#elif defined(__sun) || defined(sun) +# define PLATFORM_ID "SunOS" + +#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__) +# define PLATFORM_ID "AIX" + +#elif defined(__sgi) || defined(__sgi__) || defined(_SGI) +# define PLATFORM_ID "IRIX" + +#elif defined(__hpux) || defined(__hpux__) +# define PLATFORM_ID "HP-UX" + +#elif defined(__HAIKU__) +# define PLATFORM_ID "Haiku" + +#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS) +# define PLATFORM_ID "BeOS" + +#elif defined(__QNX__) || defined(__QNXNTO__) +# define PLATFORM_ID "QNX" + +#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__) +# define PLATFORM_ID "Tru64" + +#elif defined(__riscos) || defined(__riscos__) +# define PLATFORM_ID "RISCos" + +#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__) +# define PLATFORM_ID "SINIX" + +#elif defined(__UNIX_SV__) +# define PLATFORM_ID "UNIX_SV" + +#elif defined(__bsdos__) +# define PLATFORM_ID "BSDOS" + +#elif defined(_MPRAS) || defined(MPRAS) +# define PLATFORM_ID "MP-RAS" + +#elif defined(__osf) || defined(__osf__) +# define PLATFORM_ID "OSF1" + +#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv) +# define PLATFORM_ID "SCO_SV" + +#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX) +# define PLATFORM_ID "ULTRIX" + +#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX) +# define PLATFORM_ID "Xenix" + +#elif defined(__WATCOMC__) +# if defined(__LINUX__) +# define PLATFORM_ID "Linux" + +# elif defined(__DOS__) +# define PLATFORM_ID "DOS" + +# elif defined(__OS2__) +# define PLATFORM_ID "OS2" + +# elif defined(__WINDOWS__) +# define PLATFORM_ID "Windows3x" + +# else /* unknown platform */ +# define PLATFORM_ID +# endif + +#else /* unknown platform */ +# define PLATFORM_ID + +#endif + +/* For windows compilers MSVC and Intel we can determine + the architecture of the compiler being used. This is because + the compilers do not have flags that can change the architecture, + but rather depend on which compiler is being used +*/ +#if defined(_WIN32) && defined(_MSC_VER) +# if defined(_M_IA64) +# define ARCHITECTURE_ID "IA64" + +# elif defined(_M_X64) || defined(_M_AMD64) +# define ARCHITECTURE_ID "x64" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# elif defined(_M_ARM) +# if _M_ARM == 4 +# define ARCHITECTURE_ID "ARMV4I" +# elif _M_ARM == 5 +# define ARCHITECTURE_ID "ARMV5I" +# else +# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM) +# endif + +# elif defined(_M_MIPS) +# define ARCHITECTURE_ID "MIPS" + +# elif defined(_M_SH) +# define ARCHITECTURE_ID "SHx" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__WATCOMC__) +# if defined(_M_I86) +# define ARCHITECTURE_ID "I86" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#else +# define ARCHITECTURE_ID +#endif + +/* Convert integer to decimal digit literals. */ +#define DEC(n) \ + ('0' + (((n) / 10000000)%10)), \ + ('0' + (((n) / 1000000)%10)), \ + ('0' + (((n) / 100000)%10)), \ + ('0' + (((n) / 10000)%10)), \ + ('0' + (((n) / 1000)%10)), \ + ('0' + (((n) / 100)%10)), \ + ('0' + (((n) / 10)%10)), \ + ('0' + ((n) % 10)) + +/* Convert integer to hex digit literals. */ +#define HEX(n) \ + ('0' + ((n)>>28 & 0xF)), \ + ('0' + ((n)>>24 & 0xF)), \ + ('0' + ((n)>>20 & 0xF)), \ + ('0' + ((n)>>16 & 0xF)), \ + ('0' + ((n)>>12 & 0xF)), \ + ('0' + ((n)>>8 & 0xF)), \ + ('0' + ((n)>>4 & 0xF)), \ + ('0' + ((n) & 0xF)) + +/* Construct a string literal encoding the version number components. */ +#ifdef COMPILER_VERSION_MAJOR +char const info_version[] = { + 'I', 'N', 'F', 'O', ':', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[', + COMPILER_VERSION_MAJOR, +# ifdef COMPILER_VERSION_MINOR + '.', COMPILER_VERSION_MINOR, +# ifdef COMPILER_VERSION_PATCH + '.', COMPILER_VERSION_PATCH, +# ifdef COMPILER_VERSION_TWEAK + '.', COMPILER_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct a string literal encoding the version number components. */ +#ifdef SIMULATE_VERSION_MAJOR +char const info_simulate_version[] = { + 'I', 'N', 'F', 'O', ':', + 's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[', + SIMULATE_VERSION_MAJOR, +# ifdef SIMULATE_VERSION_MINOR + '.', SIMULATE_VERSION_MINOR, +# ifdef SIMULATE_VERSION_PATCH + '.', SIMULATE_VERSION_PATCH, +# ifdef SIMULATE_VERSION_TWEAK + '.', SIMULATE_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]"; +char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]"; + + + + +#if !defined(__STDC__) +# if defined(_MSC_VER) && !defined(__clang__) +# define C_DIALECT "90" +# else +# define C_DIALECT +# endif +#elif __STDC_VERSION__ >= 201000L +# define C_DIALECT "11" +#elif __STDC_VERSION__ >= 199901L +# define C_DIALECT "99" +#else +# define C_DIALECT "90" +#endif +const char* info_language_dialect_default = + "INFO" ":" "dialect_default[" C_DIALECT "]"; + +/*--------------------------------------------------------------------------*/ + +#ifdef ID_VOID_MAIN +void main() {} +#else +# if defined(__CLASSIC_C__) +int main(argc, argv) int argc; char *argv[]; +# else +int main(int argc, char* argv[]) +# endif +{ + int require = 0; + require += info_compiler[argc]; + require += info_platform[argc]; + require += info_arch[argc]; +#ifdef COMPILER_VERSION_MAJOR + require += info_version[argc]; +#endif +#ifdef SIMULATE_ID + require += info_simulate[argc]; +#endif +#ifdef SIMULATE_VERSION_MAJOR + require += info_simulate_version[argc]; +#endif +#if defined(__CRAYXE) || defined(__CRAYXC) + require += info_cray[argc]; +#endif + require += info_language_dialect_default[argc]; + (void)argv; + return require; +} +#endif diff --git a/stationapi/build/CMakeFiles/3.7.2/CompilerIdC/a.out b/stationapi/build/CMakeFiles/3.7.2/CompilerIdC/a.out new file mode 100755 index 0000000..7025321 Binary files /dev/null and b/stationapi/build/CMakeFiles/3.7.2/CompilerIdC/a.out differ diff --git a/stationapi/build/CMakeFiles/3.7.2/CompilerIdCXX/CMakeCXXCompilerId.cpp b/stationapi/build/CMakeFiles/3.7.2/CompilerIdCXX/CMakeCXXCompilerId.cpp new file mode 100644 index 0000000..a6e6bed --- /dev/null +++ b/stationapi/build/CMakeFiles/3.7.2/CompilerIdCXX/CMakeCXXCompilerId.cpp @@ -0,0 +1,533 @@ +/* This source file must have a .cpp extension so that all C++ compilers + recognize the extension without flags. Borland does not know .cxx for + example. */ +#ifndef __cplusplus +# error "A C compiler has been selected for C++." +#endif + + +/* Version number components: V=Version, R=Revision, P=Patch + Version date components: YYYY=Year, MM=Month, DD=Day */ + +#if defined(__COMO__) +# define COMPILER_ID "Comeau" + /* __COMO_VERSION__ = VRR */ +# define COMPILER_VERSION_MAJOR DEC(__COMO_VERSION__ / 100) +# define COMPILER_VERSION_MINOR DEC(__COMO_VERSION__ % 100) + +#elif defined(__INTEL_COMPILER) || defined(__ICC) +# define COMPILER_ID "Intel" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif + /* __INTEL_COMPILER = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100) +# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10) +# if defined(__INTEL_COMPILER_UPDATE) +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE) +# else +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10) +# endif +# if defined(__INTEL_COMPILER_BUILD_DATE) + /* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */ +# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE) +# endif +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif + +#elif defined(__PATHCC__) +# define COMPILER_ID "PathScale" +# define COMPILER_VERSION_MAJOR DEC(__PATHCC__) +# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__) +# if defined(__PATHCC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__) +# endif + +#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__) +# define COMPILER_ID "Embarcadero" +# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF) +# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF) +# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF) + +#elif defined(__BORLANDC__) +# define COMPILER_ID "Borland" + /* __BORLANDC__ = 0xVRR */ +# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8) +# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF) + +#elif defined(__WATCOMC__) && __WATCOMC__ < 1200 +# define COMPILER_ID "Watcom" + /* __WATCOMC__ = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__WATCOMC__) +# define COMPILER_ID "OpenWatcom" + /* __WATCOMC__ = VVRP + 1100 */ +# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__SUNPRO_CC) +# define COMPILER_ID "SunPro" +# if __SUNPRO_CC >= 0x5100 + /* __SUNPRO_CC = 0xVRRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>12) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF) +# else + /* __SUNPRO_CC = 0xVRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>8) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF) +# endif + +#elif defined(__HP_aCC) +# define COMPILER_ID "HP" + /* __HP_aCC = VVRRPP */ +# define COMPILER_VERSION_MAJOR DEC(__HP_aCC/10000) +# define COMPILER_VERSION_MINOR DEC(__HP_aCC/100 % 100) +# define COMPILER_VERSION_PATCH DEC(__HP_aCC % 100) + +#elif defined(__DECCXX) +# define COMPILER_ID "Compaq" + /* __DECCXX_VER = VVRRTPPPP */ +# define COMPILER_VERSION_MAJOR DEC(__DECCXX_VER/10000000) +# define COMPILER_VERSION_MINOR DEC(__DECCXX_VER/100000 % 100) +# define COMPILER_VERSION_PATCH DEC(__DECCXX_VER % 10000) + +#elif defined(__IBMCPP__) && defined(__COMPILER_VER__) +# define COMPILER_ID "zOS" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ >= 800 +# define COMPILER_ID "XL" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ < 800 +# define COMPILER_ID "VisualAge" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__PGI) +# define COMPILER_ID "PGI" +# define COMPILER_VERSION_MAJOR DEC(__PGIC__) +# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__) +# if defined(__PGIC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__) +# endif + +#elif defined(_CRAYC) +# define COMPILER_ID "Cray" +# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR) +# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR) + +#elif defined(__TI_COMPILER_VERSION__) +# define COMPILER_ID "TI" + /* __TI_COMPILER_VERSION__ = VVVRRRPPP */ +# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000) +# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000) +# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000) + +#elif defined(__FUJITSU) || defined(__FCC_VERSION) || defined(__fcc_version) +# define COMPILER_ID "Fujitsu" + +#elif defined(__SCO_VERSION__) +# define COMPILER_ID "SCO" + +#elif defined(__clang__) && defined(__apple_build_version__) +# define COMPILER_ID "AppleClang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif +# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__) + +#elif defined(__clang__) +# define COMPILER_ID "Clang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif + +#elif defined(__GNUC__) +# define COMPILER_ID "GNU" +# define COMPILER_VERSION_MAJOR DEC(__GNUC__) +# if defined(__GNUC_MINOR__) +# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__) +# endif +# if defined(__GNUC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__) +# endif + +#elif defined(_MSC_VER) +# define COMPILER_ID "MSVC" + /* _MSC_VER = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100) +# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100) +# if defined(_MSC_FULL_VER) +# if _MSC_VER >= 1400 + /* _MSC_FULL_VER = VVRRPPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000) +# else + /* _MSC_FULL_VER = VVRRPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000) +# endif +# endif +# if defined(_MSC_BUILD) +# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD) +# endif + +#elif defined(__VISUALDSPVERSION__) || defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__) +# define COMPILER_ID "ADSP" +#if defined(__VISUALDSPVERSION__) + /* __VISUALDSPVERSION__ = 0xVVRRPP00 */ +# define COMPILER_VERSION_MAJOR HEX(__VISUALDSPVERSION__>>24) +# define COMPILER_VERSION_MINOR HEX(__VISUALDSPVERSION__>>16 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__VISUALDSPVERSION__>>8 & 0xFF) +#endif + +#elif defined(__IAR_SYSTEMS_ICC__ ) || defined(__IAR_SYSTEMS_ICC) +# define COMPILER_ID "IAR" + +#elif defined(__ARMCC_VERSION) +# define COMPILER_ID "ARMCC" +#if __ARMCC_VERSION >= 1000000 + /* __ARMCC_VERSION = VRRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#else + /* __ARMCC_VERSION = VRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#endif + + +#elif defined(_SGI_COMPILER_VERSION) || defined(_COMPILER_VERSION) +# define COMPILER_ID "MIPSpro" +# if defined(_SGI_COMPILER_VERSION) + /* _SGI_COMPILER_VERSION = VRP */ +# define COMPILER_VERSION_MAJOR DEC(_SGI_COMPILER_VERSION/100) +# define COMPILER_VERSION_MINOR DEC(_SGI_COMPILER_VERSION/10 % 10) +# define COMPILER_VERSION_PATCH DEC(_SGI_COMPILER_VERSION % 10) +# else + /* _COMPILER_VERSION = VRP */ +# define COMPILER_VERSION_MAJOR DEC(_COMPILER_VERSION/100) +# define COMPILER_VERSION_MINOR DEC(_COMPILER_VERSION/10 % 10) +# define COMPILER_VERSION_PATCH DEC(_COMPILER_VERSION % 10) +# endif + + +/* These compilers are either not known or too old to define an + identification macro. Try to identify the platform and guess that + it is the native compiler. */ +#elif defined(__sgi) +# define COMPILER_ID "MIPSpro" + +#elif defined(__hpux) || defined(__hpua) +# define COMPILER_ID "HP" + +#else /* unknown compiler */ +# define COMPILER_ID "" +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]"; +#ifdef SIMULATE_ID +char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; +#endif + +#ifdef __QNXNTO__ +char const* qnxnto = "INFO" ":" "qnxnto[]"; +#endif + +#if defined(__CRAYXE) || defined(__CRAYXC) +char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]"; +#endif + +#define STRINGIFY_HELPER(X) #X +#define STRINGIFY(X) STRINGIFY_HELPER(X) + +/* Identify known platforms by name. */ +#if defined(__linux) || defined(__linux__) || defined(linux) +# define PLATFORM_ID "Linux" + +#elif defined(__CYGWIN__) +# define PLATFORM_ID "Cygwin" + +#elif defined(__MINGW32__) +# define PLATFORM_ID "MinGW" + +#elif defined(__APPLE__) +# define PLATFORM_ID "Darwin" + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define PLATFORM_ID "Windows" + +#elif defined(__FreeBSD__) || defined(__FreeBSD) +# define PLATFORM_ID "FreeBSD" + +#elif defined(__NetBSD__) || defined(__NetBSD) +# define PLATFORM_ID "NetBSD" + +#elif defined(__OpenBSD__) || defined(__OPENBSD) +# define PLATFORM_ID "OpenBSD" + +#elif defined(__sun) || defined(sun) +# define PLATFORM_ID "SunOS" + +#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__) +# define PLATFORM_ID "AIX" + +#elif defined(__sgi) || defined(__sgi__) || defined(_SGI) +# define PLATFORM_ID "IRIX" + +#elif defined(__hpux) || defined(__hpux__) +# define PLATFORM_ID "HP-UX" + +#elif defined(__HAIKU__) +# define PLATFORM_ID "Haiku" + +#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS) +# define PLATFORM_ID "BeOS" + +#elif defined(__QNX__) || defined(__QNXNTO__) +# define PLATFORM_ID "QNX" + +#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__) +# define PLATFORM_ID "Tru64" + +#elif defined(__riscos) || defined(__riscos__) +# define PLATFORM_ID "RISCos" + +#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__) +# define PLATFORM_ID "SINIX" + +#elif defined(__UNIX_SV__) +# define PLATFORM_ID "UNIX_SV" + +#elif defined(__bsdos__) +# define PLATFORM_ID "BSDOS" + +#elif defined(_MPRAS) || defined(MPRAS) +# define PLATFORM_ID "MP-RAS" + +#elif defined(__osf) || defined(__osf__) +# define PLATFORM_ID "OSF1" + +#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv) +# define PLATFORM_ID "SCO_SV" + +#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX) +# define PLATFORM_ID "ULTRIX" + +#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX) +# define PLATFORM_ID "Xenix" + +#elif defined(__WATCOMC__) +# if defined(__LINUX__) +# define PLATFORM_ID "Linux" + +# elif defined(__DOS__) +# define PLATFORM_ID "DOS" + +# elif defined(__OS2__) +# define PLATFORM_ID "OS2" + +# elif defined(__WINDOWS__) +# define PLATFORM_ID "Windows3x" + +# else /* unknown platform */ +# define PLATFORM_ID +# endif + +#else /* unknown platform */ +# define PLATFORM_ID + +#endif + +/* For windows compilers MSVC and Intel we can determine + the architecture of the compiler being used. This is because + the compilers do not have flags that can change the architecture, + but rather depend on which compiler is being used +*/ +#if defined(_WIN32) && defined(_MSC_VER) +# if defined(_M_IA64) +# define ARCHITECTURE_ID "IA64" + +# elif defined(_M_X64) || defined(_M_AMD64) +# define ARCHITECTURE_ID "x64" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# elif defined(_M_ARM) +# if _M_ARM == 4 +# define ARCHITECTURE_ID "ARMV4I" +# elif _M_ARM == 5 +# define ARCHITECTURE_ID "ARMV5I" +# else +# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM) +# endif + +# elif defined(_M_MIPS) +# define ARCHITECTURE_ID "MIPS" + +# elif defined(_M_SH) +# define ARCHITECTURE_ID "SHx" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__WATCOMC__) +# if defined(_M_I86) +# define ARCHITECTURE_ID "I86" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#else +# define ARCHITECTURE_ID +#endif + +/* Convert integer to decimal digit literals. */ +#define DEC(n) \ + ('0' + (((n) / 10000000)%10)), \ + ('0' + (((n) / 1000000)%10)), \ + ('0' + (((n) / 100000)%10)), \ + ('0' + (((n) / 10000)%10)), \ + ('0' + (((n) / 1000)%10)), \ + ('0' + (((n) / 100)%10)), \ + ('0' + (((n) / 10)%10)), \ + ('0' + ((n) % 10)) + +/* Convert integer to hex digit literals. */ +#define HEX(n) \ + ('0' + ((n)>>28 & 0xF)), \ + ('0' + ((n)>>24 & 0xF)), \ + ('0' + ((n)>>20 & 0xF)), \ + ('0' + ((n)>>16 & 0xF)), \ + ('0' + ((n)>>12 & 0xF)), \ + ('0' + ((n)>>8 & 0xF)), \ + ('0' + ((n)>>4 & 0xF)), \ + ('0' + ((n) & 0xF)) + +/* Construct a string literal encoding the version number components. */ +#ifdef COMPILER_VERSION_MAJOR +char const info_version[] = { + 'I', 'N', 'F', 'O', ':', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[', + COMPILER_VERSION_MAJOR, +# ifdef COMPILER_VERSION_MINOR + '.', COMPILER_VERSION_MINOR, +# ifdef COMPILER_VERSION_PATCH + '.', COMPILER_VERSION_PATCH, +# ifdef COMPILER_VERSION_TWEAK + '.', COMPILER_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct a string literal encoding the version number components. */ +#ifdef SIMULATE_VERSION_MAJOR +char const info_simulate_version[] = { + 'I', 'N', 'F', 'O', ':', + 's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[', + SIMULATE_VERSION_MAJOR, +# ifdef SIMULATE_VERSION_MINOR + '.', SIMULATE_VERSION_MINOR, +# ifdef SIMULATE_VERSION_PATCH + '.', SIMULATE_VERSION_PATCH, +# ifdef SIMULATE_VERSION_TWEAK + '.', SIMULATE_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]"; +char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]"; + + + + +const char* info_language_dialect_default = "INFO" ":" "dialect_default[" +#if __cplusplus >= 201402L + "14" +#elif __cplusplus >= 201103L + "11" +#else + "98" +#endif +"]"; + +/*--------------------------------------------------------------------------*/ + +int main(int argc, char* argv[]) +{ + int require = 0; + require += info_compiler[argc]; + require += info_platform[argc]; +#ifdef COMPILER_VERSION_MAJOR + require += info_version[argc]; +#endif +#ifdef SIMULATE_ID + require += info_simulate[argc]; +#endif +#ifdef SIMULATE_VERSION_MAJOR + require += info_simulate_version[argc]; +#endif +#if defined(__CRAYXE) || defined(__CRAYXC) + require += info_cray[argc]; +#endif + require += info_language_dialect_default[argc]; + (void)argv; + return require; +} diff --git a/stationapi/build/CMakeFiles/3.7.2/CompilerIdCXX/a.out b/stationapi/build/CMakeFiles/3.7.2/CompilerIdCXX/a.out new file mode 100755 index 0000000..c4ac972 Binary files /dev/null and b/stationapi/build/CMakeFiles/3.7.2/CompilerIdCXX/a.out differ diff --git a/stationapi/build/CMakeFiles/CMakeDirectoryInformation.cmake b/stationapi/build/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..a19da41 --- /dev/null +++ b/stationapi/build/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/root/swg-main/stationapi") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/root/swg-main/stationapi/build") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/stationapi/build/CMakeFiles/CMakeOutput.log b/stationapi/build/CMakeFiles/CMakeOutput.log new file mode 100644 index 0000000..a597f2b --- /dev/null +++ b/stationapi/build/CMakeFiles/CMakeOutput.log @@ -0,0 +1,554 @@ +The system is: Linux - 4.9.0-6-amd64 - x86_64 +Compiling the CXX compiler identification source file "CMakeCXXCompilerId.cpp" succeeded. +Compiler: /usr/bin/c++ +Build flags: +Id flags: + +The output was: +0 + + +Compilation of the CXX compiler identification source "CMakeCXXCompilerId.cpp" produced "a.out" + +The CXX compiler identification is GNU, found in "/root/swg-main/stationapi/build/CMakeFiles/3.7.2/CompilerIdCXX/a.out" + +Determining if the CXX compiler works passed with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_4560f/fast" +/usr/bin/make -f CMakeFiles/cmTC_4560f.dir/build.make CMakeFiles/cmTC_4560f.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_4560f.dir/testCXXCompiler.cxx.o +/usr/bin/c++ -o CMakeFiles/cmTC_4560f.dir/testCXXCompiler.cxx.o -c /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp/testCXXCompiler.cxx +Linking CXX executable cmTC_4560f +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_4560f.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/cmTC_4560f.dir/testCXXCompiler.cxx.o -o cmTC_4560f -rdynamic +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + +Detecting CXX compiler ABI info compiled with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_4ae58/fast" +/usr/bin/make -f CMakeFiles/cmTC_4ae58.dir/build.make CMakeFiles/cmTC_4ae58.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o +/usr/bin/c++ -o CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o -c /usr/share/cmake-3.7/Modules/CMakeCXXCompilerABI.cpp +Linking CXX executable cmTC_4ae58 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_4ae58.dir/link.txt --verbose=1 +/usr/bin/c++ -v CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_4ae58 -rdynamic +Using built-in specs. +COLLECT_GCC=/usr/bin/c++ +COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-18+deb9u1' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu +Thread model: posix +gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) +COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/ +LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../:/lib/:/usr/lib/ +COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_4ae58' '-rdynamic' '-shared-libgcc' '-mtune=generic' '-march=x86-64' + /usr/lib/gcc/x86_64-linux-gnu/6/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=-fresolution=/tmp/ccV3Mrhx.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_4ae58 /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/6 -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/6/../../.. CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o +COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_4ae58' '-rdynamic' '-shared-libgcc' '-mtune=generic' '-march=x86-64' +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + +Parsed CXX implicit link information from above output: + link line regex: [^( *|.*[/\])(ld|([^/\]+-)?ld|collect2)[^/\]*( |$)] + ignore line: [Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp] + ignore line: [] + ignore line: [Run Build Command:"/usr/bin/make" "cmTC_4ae58/fast"] + ignore line: [/usr/bin/make -f CMakeFiles/cmTC_4ae58.dir/build.make CMakeFiles/cmTC_4ae58.dir/build] + ignore line: [make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp'] + ignore line: [Building CXX object CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o] + ignore line: [/usr/bin/c++ -o CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o -c /usr/share/cmake-3.7/Modules/CMakeCXXCompilerABI.cpp] + ignore line: [Linking CXX executable cmTC_4ae58] + ignore line: [/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_4ae58.dir/link.txt --verbose=1] + ignore line: [/usr/bin/c++ -v CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_4ae58 -rdynamic ] + ignore line: [Using built-in specs.] + ignore line: [COLLECT_GCC=/usr/bin/c++] + ignore line: [COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper] + ignore line: [Target: x86_64-linux-gnu] + ignore line: [Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-18+deb9u1' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu] + ignore line: [Thread model: posix] + ignore line: [gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) ] + ignore line: [COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/] + ignore line: [LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../:/lib/:/usr/lib/] + ignore line: [COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_4ae58' '-rdynamic' '-shared-libgcc' '-mtune=generic' '-march=x86-64'] + link line: [ /usr/lib/gcc/x86_64-linux-gnu/6/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=-fresolution=/tmp/ccV3Mrhx.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_4ae58 /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/6 -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/6/../../.. CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o] + arg [/usr/lib/gcc/x86_64-linux-gnu/6/collect2] ==> ignore + arg [-plugin] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so] ==> ignore + arg [-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper] ==> ignore + arg [-plugin-opt=-fresolution=/tmp/ccV3Mrhx.res] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc] ==> ignore + arg [-plugin-opt=-pass-through=-lc] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc] ==> ignore + arg [--sysroot=/] ==> ignore + arg [--build-id] ==> ignore + arg [--eh-frame-hdr] ==> ignore + arg [-m] ==> ignore + arg [elf_x86_64] ==> ignore + arg [--hash-style=gnu] ==> ignore + arg [-export-dynamic] ==> ignore + arg [-dynamic-linker] ==> ignore + arg [/lib64/ld-linux-x86-64.so.2] ==> ignore + arg [-pie] ==> ignore + arg [-o] ==> ignore + arg [cmTC_4ae58] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o] ==> ignore + arg [-L/usr/lib/gcc/x86_64-linux-gnu/6] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/6] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib] + arg [-L/lib/x86_64-linux-gnu] ==> dir [/lib/x86_64-linux-gnu] + arg [-L/lib/../lib] ==> dir [/lib/../lib] + arg [-L/usr/lib/x86_64-linux-gnu] ==> dir [/usr/lib/x86_64-linux-gnu] + arg [-L/usr/lib/../lib] ==> dir [/usr/lib/../lib] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/6/../../..] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../..] + arg [CMakeFiles/cmTC_4ae58.dir/CMakeCXXCompilerABI.cpp.o] ==> ignore + arg [-lstdc++] ==> lib [stdc++] + arg [-lm] ==> lib [m] + arg [-lgcc_s] ==> lib [gcc_s] + arg [-lgcc] ==> lib [gcc] + arg [-lc] ==> lib [c] + arg [-lgcc_s] ==> lib [gcc_s] + arg [-lgcc] ==> lib [gcc] + arg [/usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o] ==> ignore + remove lib [gcc_s] + remove lib [gcc] + remove lib [gcc_s] + remove lib [gcc] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/6] ==> [/usr/lib/gcc/x86_64-linux-gnu/6] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib] ==> [/usr/lib] + collapse library dir [/lib/x86_64-linux-gnu] ==> [/lib/x86_64-linux-gnu] + collapse library dir [/lib/../lib] ==> [/lib] + collapse library dir [/usr/lib/x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu] + collapse library dir [/usr/lib/../lib] ==> [/usr/lib] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../..] ==> [/usr/lib] + implicit libs: [stdc++;m;c] + implicit dirs: [/usr/lib/gcc/x86_64-linux-gnu/6;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib] + implicit fwks: [] + + + + +Detecting CXX [-std=c++14] compiler features compiled with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_92518/fast" +/usr/bin/make -f CMakeFiles/cmTC_92518.dir/build.make CMakeFiles/cmTC_92518.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_92518.dir/feature_tests.cxx.o +/usr/bin/c++ -std=c++14 -o CMakeFiles/cmTC_92518.dir/feature_tests.cxx.o -c /root/swg-main/stationapi/build/CMakeFiles/feature_tests.cxx +Linking CXX executable cmTC_92518 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_92518.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/cmTC_92518.dir/feature_tests.cxx.o -o cmTC_92518 -rdynamic +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + + Feature record: CXX_FEATURE:1cxx_aggregate_default_initializers + Feature record: CXX_FEATURE:1cxx_alias_templates + Feature record: CXX_FEATURE:1cxx_alignas + Feature record: CXX_FEATURE:1cxx_alignof + Feature record: CXX_FEATURE:1cxx_attributes + Feature record: CXX_FEATURE:1cxx_attribute_deprecated + Feature record: CXX_FEATURE:1cxx_auto_type + Feature record: CXX_FEATURE:1cxx_binary_literals + Feature record: CXX_FEATURE:1cxx_constexpr + Feature record: CXX_FEATURE:1cxx_contextual_conversions + Feature record: CXX_FEATURE:1cxx_decltype + Feature record: CXX_FEATURE:1cxx_decltype_auto + Feature record: CXX_FEATURE:1cxx_decltype_incomplete_return_types + Feature record: CXX_FEATURE:1cxx_default_function_template_args + Feature record: CXX_FEATURE:1cxx_defaulted_functions + Feature record: CXX_FEATURE:1cxx_defaulted_move_initializers + Feature record: CXX_FEATURE:1cxx_delegating_constructors + Feature record: CXX_FEATURE:1cxx_deleted_functions + Feature record: CXX_FEATURE:1cxx_digit_separators + Feature record: CXX_FEATURE:1cxx_enum_forward_declarations + Feature record: CXX_FEATURE:1cxx_explicit_conversions + Feature record: CXX_FEATURE:1cxx_extended_friend_declarations + Feature record: CXX_FEATURE:1cxx_extern_templates + Feature record: CXX_FEATURE:1cxx_final + Feature record: CXX_FEATURE:1cxx_func_identifier + Feature record: CXX_FEATURE:1cxx_generalized_initializers + Feature record: CXX_FEATURE:1cxx_generic_lambdas + Feature record: CXX_FEATURE:1cxx_inheriting_constructors + Feature record: CXX_FEATURE:1cxx_inline_namespaces + Feature record: CXX_FEATURE:1cxx_lambdas + Feature record: CXX_FEATURE:1cxx_lambda_init_captures + Feature record: CXX_FEATURE:1cxx_local_type_template_args + Feature record: CXX_FEATURE:1cxx_long_long_type + Feature record: CXX_FEATURE:1cxx_noexcept + Feature record: CXX_FEATURE:1cxx_nonstatic_member_init + Feature record: CXX_FEATURE:1cxx_nullptr + Feature record: CXX_FEATURE:1cxx_override + Feature record: CXX_FEATURE:1cxx_range_for + Feature record: CXX_FEATURE:1cxx_raw_string_literals + Feature record: CXX_FEATURE:1cxx_reference_qualified_functions + Feature record: CXX_FEATURE:1cxx_relaxed_constexpr + Feature record: CXX_FEATURE:1cxx_return_type_deduction + Feature record: CXX_FEATURE:1cxx_right_angle_brackets + Feature record: CXX_FEATURE:1cxx_rvalue_references + Feature record: CXX_FEATURE:1cxx_sizeof_member + Feature record: CXX_FEATURE:1cxx_static_assert + Feature record: CXX_FEATURE:1cxx_strong_enums + Feature record: CXX_FEATURE:1cxx_template_template_parameters + Feature record: CXX_FEATURE:1cxx_thread_local + Feature record: CXX_FEATURE:1cxx_trailing_return_types + Feature record: CXX_FEATURE:1cxx_unicode_literals + Feature record: CXX_FEATURE:1cxx_uniform_initialization + Feature record: CXX_FEATURE:1cxx_unrestricted_unions + Feature record: CXX_FEATURE:1cxx_user_literals + Feature record: CXX_FEATURE:1cxx_variable_templates + Feature record: CXX_FEATURE:1cxx_variadic_macros + Feature record: CXX_FEATURE:1cxx_variadic_templates + + +Detecting CXX [-std=c++11] compiler features compiled with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_fdf99/fast" +/usr/bin/make -f CMakeFiles/cmTC_fdf99.dir/build.make CMakeFiles/cmTC_fdf99.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_fdf99.dir/feature_tests.cxx.o +/usr/bin/c++ -std=c++11 -o CMakeFiles/cmTC_fdf99.dir/feature_tests.cxx.o -c /root/swg-main/stationapi/build/CMakeFiles/feature_tests.cxx +Linking CXX executable cmTC_fdf99 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_fdf99.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/cmTC_fdf99.dir/feature_tests.cxx.o -o cmTC_fdf99 -rdynamic +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + + Feature record: CXX_FEATURE:0cxx_aggregate_default_initializers + Feature record: CXX_FEATURE:1cxx_alias_templates + Feature record: CXX_FEATURE:1cxx_alignas + Feature record: CXX_FEATURE:1cxx_alignof + Feature record: CXX_FEATURE:1cxx_attributes + Feature record: CXX_FEATURE:0cxx_attribute_deprecated + Feature record: CXX_FEATURE:1cxx_auto_type + Feature record: CXX_FEATURE:0cxx_binary_literals + Feature record: CXX_FEATURE:1cxx_constexpr + Feature record: CXX_FEATURE:0cxx_contextual_conversions + Feature record: CXX_FEATURE:1cxx_decltype + Feature record: CXX_FEATURE:0cxx_decltype_auto + Feature record: CXX_FEATURE:1cxx_decltype_incomplete_return_types + Feature record: CXX_FEATURE:1cxx_default_function_template_args + Feature record: CXX_FEATURE:1cxx_defaulted_functions + Feature record: CXX_FEATURE:1cxx_defaulted_move_initializers + Feature record: CXX_FEATURE:1cxx_delegating_constructors + Feature record: CXX_FEATURE:1cxx_deleted_functions + Feature record: CXX_FEATURE:0cxx_digit_separators + Feature record: CXX_FEATURE:1cxx_enum_forward_declarations + Feature record: CXX_FEATURE:1cxx_explicit_conversions + Feature record: CXX_FEATURE:1cxx_extended_friend_declarations + Feature record: CXX_FEATURE:1cxx_extern_templates + Feature record: CXX_FEATURE:1cxx_final + Feature record: CXX_FEATURE:1cxx_func_identifier + Feature record: CXX_FEATURE:1cxx_generalized_initializers + Feature record: CXX_FEATURE:0cxx_generic_lambdas + Feature record: CXX_FEATURE:1cxx_inheriting_constructors + Feature record: CXX_FEATURE:1cxx_inline_namespaces + Feature record: CXX_FEATURE:1cxx_lambdas + Feature record: CXX_FEATURE:0cxx_lambda_init_captures + Feature record: CXX_FEATURE:1cxx_local_type_template_args + Feature record: CXX_FEATURE:1cxx_long_long_type + Feature record: CXX_FEATURE:1cxx_noexcept + Feature record: CXX_FEATURE:1cxx_nonstatic_member_init + Feature record: CXX_FEATURE:1cxx_nullptr + Feature record: CXX_FEATURE:1cxx_override + Feature record: CXX_FEATURE:1cxx_range_for + Feature record: CXX_FEATURE:1cxx_raw_string_literals + Feature record: CXX_FEATURE:1cxx_reference_qualified_functions + Feature record: CXX_FEATURE:0cxx_relaxed_constexpr + Feature record: CXX_FEATURE:0cxx_return_type_deduction + Feature record: CXX_FEATURE:1cxx_right_angle_brackets + Feature record: CXX_FEATURE:1cxx_rvalue_references + Feature record: CXX_FEATURE:1cxx_sizeof_member + Feature record: CXX_FEATURE:1cxx_static_assert + Feature record: CXX_FEATURE:1cxx_strong_enums + Feature record: CXX_FEATURE:1cxx_template_template_parameters + Feature record: CXX_FEATURE:1cxx_thread_local + Feature record: CXX_FEATURE:1cxx_trailing_return_types + Feature record: CXX_FEATURE:1cxx_unicode_literals + Feature record: CXX_FEATURE:1cxx_uniform_initialization + Feature record: CXX_FEATURE:1cxx_unrestricted_unions + Feature record: CXX_FEATURE:1cxx_user_literals + Feature record: CXX_FEATURE:0cxx_variable_templates + Feature record: CXX_FEATURE:1cxx_variadic_macros + Feature record: CXX_FEATURE:1cxx_variadic_templates + + +Detecting CXX [-std=c++98] compiler features compiled with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_a5f3b/fast" +/usr/bin/make -f CMakeFiles/cmTC_a5f3b.dir/build.make CMakeFiles/cmTC_a5f3b.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_a5f3b.dir/feature_tests.cxx.o +/usr/bin/c++ -std=c++98 -o CMakeFiles/cmTC_a5f3b.dir/feature_tests.cxx.o -c /root/swg-main/stationapi/build/CMakeFiles/feature_tests.cxx +Linking CXX executable cmTC_a5f3b +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_a5f3b.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/cmTC_a5f3b.dir/feature_tests.cxx.o -o cmTC_a5f3b -rdynamic +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + + Feature record: CXX_FEATURE:0cxx_aggregate_default_initializers + Feature record: CXX_FEATURE:0cxx_alias_templates + Feature record: CXX_FEATURE:0cxx_alignas + Feature record: CXX_FEATURE:0cxx_alignof + Feature record: CXX_FEATURE:0cxx_attributes + Feature record: CXX_FEATURE:0cxx_attribute_deprecated + Feature record: CXX_FEATURE:0cxx_auto_type + Feature record: CXX_FEATURE:0cxx_binary_literals + Feature record: CXX_FEATURE:0cxx_constexpr + Feature record: CXX_FEATURE:0cxx_contextual_conversions + Feature record: CXX_FEATURE:0cxx_decltype + Feature record: CXX_FEATURE:0cxx_decltype_auto + Feature record: CXX_FEATURE:0cxx_decltype_incomplete_return_types + Feature record: CXX_FEATURE:0cxx_default_function_template_args + Feature record: CXX_FEATURE:0cxx_defaulted_functions + Feature record: CXX_FEATURE:0cxx_defaulted_move_initializers + Feature record: CXX_FEATURE:0cxx_delegating_constructors + Feature record: CXX_FEATURE:0cxx_deleted_functions + Feature record: CXX_FEATURE:0cxx_digit_separators + Feature record: CXX_FEATURE:0cxx_enum_forward_declarations + Feature record: CXX_FEATURE:0cxx_explicit_conversions + Feature record: CXX_FEATURE:0cxx_extended_friend_declarations + Feature record: CXX_FEATURE:0cxx_extern_templates + Feature record: CXX_FEATURE:0cxx_final + Feature record: CXX_FEATURE:0cxx_func_identifier + Feature record: CXX_FEATURE:0cxx_generalized_initializers + Feature record: CXX_FEATURE:0cxx_generic_lambdas + Feature record: CXX_FEATURE:0cxx_inheriting_constructors + Feature record: CXX_FEATURE:0cxx_inline_namespaces + Feature record: CXX_FEATURE:0cxx_lambdas + Feature record: CXX_FEATURE:0cxx_lambda_init_captures + Feature record: CXX_FEATURE:0cxx_local_type_template_args + Feature record: CXX_FEATURE:0cxx_long_long_type + Feature record: CXX_FEATURE:0cxx_noexcept + Feature record: CXX_FEATURE:0cxx_nonstatic_member_init + Feature record: CXX_FEATURE:0cxx_nullptr + Feature record: CXX_FEATURE:0cxx_override + Feature record: CXX_FEATURE:0cxx_range_for + Feature record: CXX_FEATURE:0cxx_raw_string_literals + Feature record: CXX_FEATURE:0cxx_reference_qualified_functions + Feature record: CXX_FEATURE:0cxx_relaxed_constexpr + Feature record: CXX_FEATURE:0cxx_return_type_deduction + Feature record: CXX_FEATURE:0cxx_right_angle_brackets + Feature record: CXX_FEATURE:0cxx_rvalue_references + Feature record: CXX_FEATURE:0cxx_sizeof_member + Feature record: CXX_FEATURE:0cxx_static_assert + Feature record: CXX_FEATURE:0cxx_strong_enums + Feature record: CXX_FEATURE:1cxx_template_template_parameters + Feature record: CXX_FEATURE:0cxx_thread_local + Feature record: CXX_FEATURE:0cxx_trailing_return_types + Feature record: CXX_FEATURE:0cxx_unicode_literals + Feature record: CXX_FEATURE:0cxx_uniform_initialization + Feature record: CXX_FEATURE:0cxx_unrestricted_unions + Feature record: CXX_FEATURE:0cxx_user_literals + Feature record: CXX_FEATURE:0cxx_variable_templates + Feature record: CXX_FEATURE:0cxx_variadic_macros + Feature record: CXX_FEATURE:0cxx_variadic_templates +Compiling the C compiler identification source file "CMakeCCompilerId.c" succeeded. +Compiler: /usr/bin/cc +Build flags: +Id flags: + +The output was: +0 + + +Compilation of the C compiler identification source "CMakeCCompilerId.c" produced "a.out" + +The C compiler identification is GNU, found in "/root/swg-main/stationapi/build/CMakeFiles/3.7.2/CompilerIdC/a.out" + +Determining if the C compiler works passed with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_a8adf/fast" +/usr/bin/make -f CMakeFiles/cmTC_a8adf.dir/build.make CMakeFiles/cmTC_a8adf.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building C object CMakeFiles/cmTC_a8adf.dir/testCCompiler.c.o +/usr/bin/cc -o CMakeFiles/cmTC_a8adf.dir/testCCompiler.c.o -c /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp/testCCompiler.c +Linking C executable cmTC_a8adf +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_a8adf.dir/link.txt --verbose=1 +/usr/bin/cc CMakeFiles/cmTC_a8adf.dir/testCCompiler.c.o -o cmTC_a8adf -rdynamic +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + +Detecting C compiler ABI info compiled with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_1856f/fast" +/usr/bin/make -f CMakeFiles/cmTC_1856f.dir/build.make CMakeFiles/cmTC_1856f.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building C object CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o +/usr/bin/cc -o CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o -c /usr/share/cmake-3.7/Modules/CMakeCCompilerABI.c +Linking C executable cmTC_1856f +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_1856f.dir/link.txt --verbose=1 +/usr/bin/cc -v CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o -o cmTC_1856f -rdynamic +Using built-in specs. +COLLECT_GCC=/usr/bin/cc +COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-18+deb9u1' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu +Thread model: posix +gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) +COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/ +LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../:/lib/:/usr/lib/ +COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_1856f' '-rdynamic' '-mtune=generic' '-march=x86-64' + /usr/lib/gcc/x86_64-linux-gnu/6/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=-fresolution=/tmp/cc9fweLx.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_1856f /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/6 -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/6/../../.. CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o +COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_1856f' '-rdynamic' '-mtune=generic' '-march=x86-64' +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + +Parsed C implicit link information from above output: + link line regex: [^( *|.*[/\])(ld|([^/\]+-)?ld|collect2)[^/\]*( |$)] + ignore line: [Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp] + ignore line: [] + ignore line: [Run Build Command:"/usr/bin/make" "cmTC_1856f/fast"] + ignore line: [/usr/bin/make -f CMakeFiles/cmTC_1856f.dir/build.make CMakeFiles/cmTC_1856f.dir/build] + ignore line: [make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp'] + ignore line: [Building C object CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o] + ignore line: [/usr/bin/cc -o CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o -c /usr/share/cmake-3.7/Modules/CMakeCCompilerABI.c] + ignore line: [Linking C executable cmTC_1856f] + ignore line: [/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_1856f.dir/link.txt --verbose=1] + ignore line: [/usr/bin/cc -v CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o -o cmTC_1856f -rdynamic ] + ignore line: [Using built-in specs.] + ignore line: [COLLECT_GCC=/usr/bin/cc] + ignore line: [COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper] + ignore line: [Target: x86_64-linux-gnu] + ignore line: [Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-18+deb9u1' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu] + ignore line: [Thread model: posix] + ignore line: [gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) ] + ignore line: [COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/] + ignore line: [LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/6/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/6/../../../:/lib/:/usr/lib/] + ignore line: [COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_1856f' '-rdynamic' '-mtune=generic' '-march=x86-64'] + link line: [ /usr/lib/gcc/x86_64-linux-gnu/6/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=-fresolution=/tmp/cc9fweLx.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_1856f /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/6 -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/6/../../.. CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o] + arg [/usr/lib/gcc/x86_64-linux-gnu/6/collect2] ==> ignore + arg [-plugin] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so] ==> ignore + arg [-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper] ==> ignore + arg [-plugin-opt=-fresolution=/tmp/cc9fweLx.res] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore + arg [-plugin-opt=-pass-through=-lc] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore + arg [--sysroot=/] ==> ignore + arg [--build-id] ==> ignore + arg [--eh-frame-hdr] ==> ignore + arg [-m] ==> ignore + arg [elf_x86_64] ==> ignore + arg [--hash-style=gnu] ==> ignore + arg [-export-dynamic] ==> ignore + arg [-dynamic-linker] ==> ignore + arg [/lib64/ld-linux-x86-64.so.2] ==> ignore + arg [-pie] ==> ignore + arg [-o] ==> ignore + arg [cmTC_1856f] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o] ==> ignore + arg [-L/usr/lib/gcc/x86_64-linux-gnu/6] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/6] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib] + arg [-L/lib/x86_64-linux-gnu] ==> dir [/lib/x86_64-linux-gnu] + arg [-L/lib/../lib] ==> dir [/lib/../lib] + arg [-L/usr/lib/x86_64-linux-gnu] ==> dir [/usr/lib/x86_64-linux-gnu] + arg [-L/usr/lib/../lib] ==> dir [/usr/lib/../lib] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/6/../../..] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../..] + arg [CMakeFiles/cmTC_1856f.dir/CMakeCCompilerABI.c.o] ==> ignore + arg [-lgcc] ==> lib [gcc] + arg [--as-needed] ==> ignore + arg [-lgcc_s] ==> lib [gcc_s] + arg [--no-as-needed] ==> ignore + arg [-lc] ==> lib [c] + arg [-lgcc] ==> lib [gcc] + arg [--as-needed] ==> ignore + arg [-lgcc_s] ==> lib [gcc_s] + arg [--no-as-needed] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o] ==> ignore + remove lib [gcc] + remove lib [gcc_s] + remove lib [gcc] + remove lib [gcc_s] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/6] ==> [/usr/lib/gcc/x86_64-linux-gnu/6] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib] ==> [/usr/lib] + collapse library dir [/lib/x86_64-linux-gnu] ==> [/lib/x86_64-linux-gnu] + collapse library dir [/lib/../lib] ==> [/lib] + collapse library dir [/usr/lib/x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu] + collapse library dir [/usr/lib/../lib] ==> [/usr/lib] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/6/../../..] ==> [/usr/lib] + implicit libs: [c] + implicit dirs: [/usr/lib/gcc/x86_64-linux-gnu/6;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib] + implicit fwks: [] + + + + +Detecting C [-std=c11] compiler features compiled with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_886d0/fast" +/usr/bin/make -f CMakeFiles/cmTC_886d0.dir/build.make CMakeFiles/cmTC_886d0.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building C object CMakeFiles/cmTC_886d0.dir/feature_tests.c.o +/usr/bin/cc -std=c11 -o CMakeFiles/cmTC_886d0.dir/feature_tests.c.o -c /root/swg-main/stationapi/build/CMakeFiles/feature_tests.c +Linking C executable cmTC_886d0 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_886d0.dir/link.txt --verbose=1 +/usr/bin/cc CMakeFiles/cmTC_886d0.dir/feature_tests.c.o -o cmTC_886d0 -rdynamic +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + + Feature record: C_FEATURE:1c_function_prototypes + Feature record: C_FEATURE:1c_restrict + Feature record: C_FEATURE:1c_static_assert + Feature record: C_FEATURE:1c_variadic_macros + + +Detecting C [-std=c99] compiler features compiled with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_87842/fast" +/usr/bin/make -f CMakeFiles/cmTC_87842.dir/build.make CMakeFiles/cmTC_87842.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building C object CMakeFiles/cmTC_87842.dir/feature_tests.c.o +/usr/bin/cc -std=c99 -o CMakeFiles/cmTC_87842.dir/feature_tests.c.o -c /root/swg-main/stationapi/build/CMakeFiles/feature_tests.c +Linking C executable cmTC_87842 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_87842.dir/link.txt --verbose=1 +/usr/bin/cc CMakeFiles/cmTC_87842.dir/feature_tests.c.o -o cmTC_87842 -rdynamic +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + + Feature record: C_FEATURE:1c_function_prototypes + Feature record: C_FEATURE:1c_restrict + Feature record: C_FEATURE:0c_static_assert + Feature record: C_FEATURE:1c_variadic_macros + + +Detecting C [-std=c90] compiler features compiled with the following output: +Change Dir: /root/swg-main/stationapi/build/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_f3612/fast" +/usr/bin/make -f CMakeFiles/cmTC_f3612.dir/build.make CMakeFiles/cmTC_f3612.dir/build +make[1]: Entering directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' +Building C object CMakeFiles/cmTC_f3612.dir/feature_tests.c.o +/usr/bin/cc -std=c90 -o CMakeFiles/cmTC_f3612.dir/feature_tests.c.o -c /root/swg-main/stationapi/build/CMakeFiles/feature_tests.c +Linking C executable cmTC_f3612 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_f3612.dir/link.txt --verbose=1 +/usr/bin/cc CMakeFiles/cmTC_f3612.dir/feature_tests.c.o -o cmTC_f3612 -rdynamic +make[1]: Leaving directory '/root/swg-main/stationapi/build/CMakeFiles/CMakeTmp' + + + Feature record: C_FEATURE:1c_function_prototypes + Feature record: C_FEATURE:0c_restrict + Feature record: C_FEATURE:0c_static_assert + Feature record: C_FEATURE:0c_variadic_macros diff --git a/stationapi/build/CMakeFiles/Makefile.cmake b/stationapi/build/CMakeFiles/Makefile.cmake new file mode 100644 index 0000000..b3c6091 --- /dev/null +++ b/stationapi/build/CMakeFiles/Makefile.cmake @@ -0,0 +1,140 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# The generator used is: +set(CMAKE_DEPENDS_GENERATOR "Unix Makefiles") + +# The top level Makefile was generated from the following files: +set(CMAKE_MAKEFILE_DEPENDS + "CMakeCache.txt" + "../CMakeLists.txt" + "CMakeFiles/3.7.2/CMakeCCompiler.cmake" + "CMakeFiles/3.7.2/CMakeCXXCompiler.cmake" + "CMakeFiles/3.7.2/CMakeSystem.cmake" + "CMakeFiles/feature_tests.c" + "CMakeFiles/feature_tests.cxx" + "../cmake/FindSQLite3.cmake" + "../cmake/GroupSourceByFilesystem.cmake" + "../cmake/ModernCpp.cmake" + "../externals/CMakeLists.txt" + "../externals/udplibrary/CMakeLists.txt" + "../extras/chat.db.dist" + "../extras/logger.cfg.dist" + "../extras/swgchat.cfg.dist" + "../src/CMakeLists.txt" + "../src/stationapi/CMakeLists.txt" + "../src/stationchat/CMakeLists.txt" + "../tests/CMakeLists.txt" + "/usr/share/cmake-3.7/Modules/CMakeCCompiler.cmake.in" + "/usr/share/cmake-3.7/Modules/CMakeCCompilerABI.c" + "/usr/share/cmake-3.7/Modules/CMakeCInformation.cmake" + "/usr/share/cmake-3.7/Modules/CMakeCXXCompiler.cmake.in" + "/usr/share/cmake-3.7/Modules/CMakeCXXCompilerABI.cpp" + "/usr/share/cmake-3.7/Modules/CMakeCXXInformation.cmake" + "/usr/share/cmake-3.7/Modules/CMakeCommonLanguageInclude.cmake" + "/usr/share/cmake-3.7/Modules/CMakeCompilerIdDetection.cmake" + "/usr/share/cmake-3.7/Modules/CMakeDetermineCCompiler.cmake" + "/usr/share/cmake-3.7/Modules/CMakeDetermineCXXCompiler.cmake" + "/usr/share/cmake-3.7/Modules/CMakeDetermineCompileFeatures.cmake" + "/usr/share/cmake-3.7/Modules/CMakeDetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/CMakeDetermineCompilerABI.cmake" + "/usr/share/cmake-3.7/Modules/CMakeDetermineCompilerId.cmake" + "/usr/share/cmake-3.7/Modules/CMakeDetermineSystem.cmake" + "/usr/share/cmake-3.7/Modules/CMakeFindBinUtils.cmake" + "/usr/share/cmake-3.7/Modules/CMakeGenericSystem.cmake" + "/usr/share/cmake-3.7/Modules/CMakeLanguageInformation.cmake" + "/usr/share/cmake-3.7/Modules/CMakeParseArguments.cmake" + "/usr/share/cmake-3.7/Modules/CMakeParseImplicitLinkInfo.cmake" + "/usr/share/cmake-3.7/Modules/CMakeSystem.cmake.in" + "/usr/share/cmake-3.7/Modules/CMakeSystemSpecificInformation.cmake" + "/usr/share/cmake-3.7/Modules/CMakeSystemSpecificInitialize.cmake" + "/usr/share/cmake-3.7/Modules/CMakeTestCCompiler.cmake" + "/usr/share/cmake-3.7/Modules/CMakeTestCXXCompiler.cmake" + "/usr/share/cmake-3.7/Modules/CMakeTestCompilerCommon.cmake" + "/usr/share/cmake-3.7/Modules/CMakeUnixFindMake.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/ADSP-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/ARMCC-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/AppleClang-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Borland-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Bruce-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Clang-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Clang-DetermineCompilerInternal.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Comeau-CXX-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Compaq-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Compaq-CXX-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Cray-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Embarcadero-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Fujitsu-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/GHS-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/GNU-C-FeatureTests.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/GNU-C.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/GNU-CXX-FeatureTests.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/GNU-CXX.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/GNU-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/GNU.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/HP-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/HP-CXX-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/IAR-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/IBMCPP-C-DetermineVersionInternal.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/IBMCPP-CXX-DetermineVersionInternal.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Intel-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/MIPSpro-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/MSVC-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/OpenWatcom-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/PGI-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/PathScale-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/SCO-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/SDCC-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/SunPro-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/SunPro-CXX-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/TI-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/TinyCC-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/VisualAge-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/VisualAge-CXX-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/Watcom-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/XL-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/XL-CXX-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/zOS-C-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/Compiler/zOS-CXX-DetermineCompiler.cmake" + "/usr/share/cmake-3.7/Modules/FindBoost.cmake" + "/usr/share/cmake-3.7/Modules/FindPackageHandleStandardArgs.cmake" + "/usr/share/cmake-3.7/Modules/FindPackageMessage.cmake" + "/usr/share/cmake-3.7/Modules/Internal/FeatureTesting.cmake" + "/usr/share/cmake-3.7/Modules/Platform/Linux-Determine-CXX.cmake" + "/usr/share/cmake-3.7/Modules/Platform/Linux-GNU-C.cmake" + "/usr/share/cmake-3.7/Modules/Platform/Linux-GNU-CXX.cmake" + "/usr/share/cmake-3.7/Modules/Platform/Linux-GNU.cmake" + "/usr/share/cmake-3.7/Modules/Platform/Linux.cmake" + "/usr/share/cmake-3.7/Modules/Platform/UnixPaths.cmake" + ) + +# The corresponding makefile is: +set(CMAKE_MAKEFILE_OUTPUTS + "Makefile" + "CMakeFiles/cmake.check_cache" + ) + +# Byproducts of CMake generate step: +set(CMAKE_MAKEFILE_PRODUCTS + "CMakeFiles/3.7.2/CMakeSystem.cmake" + "CMakeFiles/3.7.2/CMakeCXXCompiler.cmake" + "CMakeFiles/3.7.2/CMakeCXXCompiler.cmake" + "bin/logger.cfg" + "bin/swgchat.cfg" + "bin/chat.db" + "CMakeFiles/CMakeDirectoryInformation.cmake" + "externals/CMakeFiles/CMakeDirectoryInformation.cmake" + "externals/udplibrary/CMakeFiles/CMakeDirectoryInformation.cmake" + "src/CMakeFiles/CMakeDirectoryInformation.cmake" + "src/stationapi/CMakeFiles/CMakeDirectoryInformation.cmake" + "src/stationchat/CMakeFiles/CMakeDirectoryInformation.cmake" + "tests/CMakeFiles/CMakeDirectoryInformation.cmake" + ) + +# Dependency information for all targets: +set(CMAKE_DEPEND_INFO_FILES + "externals/udplibrary/CMakeFiles/udplibrary.dir/DependInfo.cmake" + "src/stationapi/CMakeFiles/stationapi.dir/DependInfo.cmake" + "src/stationchat/CMakeFiles/stationchat.dir/DependInfo.cmake" + "tests/CMakeFiles/stationapi_tests.dir/DependInfo.cmake" + ) diff --git a/stationapi/build/CMakeFiles/Makefile2 b/stationapi/build/CMakeFiles/Makefile2 new file mode 100644 index 0000000..5db4f05 --- /dev/null +++ b/stationapi/build/CMakeFiles/Makefile2 @@ -0,0 +1,331 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# The main recursive all target +all: + +.PHONY : all + +# The main recursive preinstall target +preinstall: + +.PHONY : preinstall + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +#============================================================================= +# Directory level rules for directory externals + +# Convenience name for "all" pass in the directory. +externals/all: externals/udplibrary/all + +.PHONY : externals/all + +# Convenience name for "clean" pass in the directory. +externals/clean: externals/udplibrary/clean + +.PHONY : externals/clean + +# Convenience name for "preinstall" pass in the directory. +externals/preinstall: externals/udplibrary/preinstall + +.PHONY : externals/preinstall + +#============================================================================= +# Directory level rules for directory externals/udplibrary + +# Convenience name for "all" pass in the directory. +externals/udplibrary/all: externals/udplibrary/CMakeFiles/udplibrary.dir/all + +.PHONY : externals/udplibrary/all + +# Convenience name for "clean" pass in the directory. +externals/udplibrary/clean: externals/udplibrary/CMakeFiles/udplibrary.dir/clean + +.PHONY : externals/udplibrary/clean + +# Convenience name for "preinstall" pass in the directory. +externals/udplibrary/preinstall: + +.PHONY : externals/udplibrary/preinstall + +#============================================================================= +# Target rules for target externals/udplibrary/CMakeFiles/udplibrary.dir + +# All Build rule for target. +externals/udplibrary/CMakeFiles/udplibrary.dir/all: + $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/depend + $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/build + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=21,22 "Built target udplibrary" +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/all + +# Include target in all. +all: externals/udplibrary/CMakeFiles/udplibrary.dir/all + +.PHONY : all + +# Build rule for subdir invocation for target. +externals/udplibrary/CMakeFiles/udplibrary.dir/rule: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 2 + $(MAKE) -f CMakeFiles/Makefile2 externals/udplibrary/CMakeFiles/udplibrary.dir/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/rule + +# Convenience name for target. +udplibrary: externals/udplibrary/CMakeFiles/udplibrary.dir/rule + +.PHONY : udplibrary + +# clean rule for target. +externals/udplibrary/CMakeFiles/udplibrary.dir/clean: + $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/clean +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/clean + +# clean rule for target. +clean: externals/udplibrary/CMakeFiles/udplibrary.dir/clean + +.PHONY : clean + +#============================================================================= +# Directory level rules for directory src + +# Convenience name for "all" pass in the directory. +src/all: src/stationapi/all +src/all: src/stationchat/all + +.PHONY : src/all + +# Convenience name for "clean" pass in the directory. +src/clean: src/stationapi/clean +src/clean: src/stationchat/clean + +.PHONY : src/clean + +# Convenience name for "preinstall" pass in the directory. +src/preinstall: src/stationapi/preinstall +src/preinstall: src/stationchat/preinstall + +.PHONY : src/preinstall + +#============================================================================= +# Directory level rules for directory src/stationapi + +# Convenience name for "all" pass in the directory. +src/stationapi/all: src/stationapi/CMakeFiles/stationapi.dir/all + +.PHONY : src/stationapi/all + +# Convenience name for "clean" pass in the directory. +src/stationapi/clean: src/stationapi/CMakeFiles/stationapi.dir/clean + +.PHONY : src/stationapi/clean + +# Convenience name for "preinstall" pass in the directory. +src/stationapi/preinstall: + +.PHONY : src/stationapi/preinstall + +#============================================================================= +# Target rules for target src/stationapi/CMakeFiles/stationapi.dir + +# All Build rule for target. +src/stationapi/CMakeFiles/stationapi.dir/all: + $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/depend + $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/build + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=1,2,3,4 "Built target stationapi" +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/all + +# Include target in all. +all: src/stationapi/CMakeFiles/stationapi.dir/all + +.PHONY : all + +# Build rule for subdir invocation for target. +src/stationapi/CMakeFiles/stationapi.dir/rule: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 4 + $(MAKE) -f CMakeFiles/Makefile2 src/stationapi/CMakeFiles/stationapi.dir/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/rule + +# Convenience name for target. +stationapi: src/stationapi/CMakeFiles/stationapi.dir/rule + +.PHONY : stationapi + +# clean rule for target. +src/stationapi/CMakeFiles/stationapi.dir/clean: + $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/clean +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/clean + +# clean rule for target. +clean: src/stationapi/CMakeFiles/stationapi.dir/clean + +.PHONY : clean + +#============================================================================= +# Directory level rules for directory src/stationchat + +# Convenience name for "all" pass in the directory. +src/stationchat/all: src/stationchat/CMakeFiles/stationchat.dir/all + +.PHONY : src/stationchat/all + +# Convenience name for "clean" pass in the directory. +src/stationchat/clean: src/stationchat/CMakeFiles/stationchat.dir/clean + +.PHONY : src/stationchat/clean + +# Convenience name for "preinstall" pass in the directory. +src/stationchat/preinstall: + +.PHONY : src/stationchat/preinstall + +#============================================================================= +# Target rules for target src/stationchat/CMakeFiles/stationchat.dir + +# All Build rule for target. +src/stationchat/CMakeFiles/stationchat.dir/all: externals/udplibrary/CMakeFiles/udplibrary.dir/all +src/stationchat/CMakeFiles/stationchat.dir/all: src/stationapi/CMakeFiles/stationapi.dir/all + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/depend + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/build + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=7,8,9,10,11,12,13,14,15,16,17,18,19,20 "Built target stationchat" +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/all + +# Include target in all. +all: src/stationchat/CMakeFiles/stationchat.dir/all + +.PHONY : all + +# Build rule for subdir invocation for target. +src/stationchat/CMakeFiles/stationchat.dir/rule: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 20 + $(MAKE) -f CMakeFiles/Makefile2 src/stationchat/CMakeFiles/stationchat.dir/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/rule + +# Convenience name for target. +stationchat: src/stationchat/CMakeFiles/stationchat.dir/rule + +.PHONY : stationchat + +# clean rule for target. +src/stationchat/CMakeFiles/stationchat.dir/clean: + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/clean +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/clean + +# clean rule for target. +clean: src/stationchat/CMakeFiles/stationchat.dir/clean + +.PHONY : clean + +#============================================================================= +# Directory level rules for directory tests + +# Convenience name for "all" pass in the directory. +tests/all: tests/CMakeFiles/stationapi_tests.dir/all + +.PHONY : tests/all + +# Convenience name for "clean" pass in the directory. +tests/clean: tests/CMakeFiles/stationapi_tests.dir/clean + +.PHONY : tests/clean + +# Convenience name for "preinstall" pass in the directory. +tests/preinstall: + +.PHONY : tests/preinstall + +#============================================================================= +# Target rules for target tests/CMakeFiles/stationapi_tests.dir + +# All Build rule for target. +tests/CMakeFiles/stationapi_tests.dir/all: src/stationapi/CMakeFiles/stationapi.dir/all + $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/depend + $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/build + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=5,6 "Built target stationapi_tests" +.PHONY : tests/CMakeFiles/stationapi_tests.dir/all + +# Include target in all. +all: tests/CMakeFiles/stationapi_tests.dir/all + +.PHONY : all + +# Build rule for subdir invocation for target. +tests/CMakeFiles/stationapi_tests.dir/rule: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 6 + $(MAKE) -f CMakeFiles/Makefile2 tests/CMakeFiles/stationapi_tests.dir/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : tests/CMakeFiles/stationapi_tests.dir/rule + +# Convenience name for target. +stationapi_tests: tests/CMakeFiles/stationapi_tests.dir/rule + +.PHONY : stationapi_tests + +# clean rule for target. +tests/CMakeFiles/stationapi_tests.dir/clean: + $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/clean +.PHONY : tests/CMakeFiles/stationapi_tests.dir/clean + +# clean rule for target. +clean: tests/CMakeFiles/stationapi_tests.dir/clean + +.PHONY : clean + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/stationapi/build/CMakeFiles/TargetDirectories.txt b/stationapi/build/CMakeFiles/TargetDirectories.txt new file mode 100644 index 0000000..24c97ad --- /dev/null +++ b/stationapi/build/CMakeFiles/TargetDirectories.txt @@ -0,0 +1,18 @@ +/root/swg-main/stationapi/build/CMakeFiles/rebuild_cache.dir +/root/swg-main/stationapi/build/CMakeFiles/edit_cache.dir +/root/swg-main/stationapi/build/externals/CMakeFiles/rebuild_cache.dir +/root/swg-main/stationapi/build/externals/CMakeFiles/edit_cache.dir +/root/swg-main/stationapi/build/externals/udplibrary/CMakeFiles/rebuild_cache.dir +/root/swg-main/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir +/root/swg-main/stationapi/build/externals/udplibrary/CMakeFiles/edit_cache.dir +/root/swg-main/stationapi/build/src/CMakeFiles/rebuild_cache.dir +/root/swg-main/stationapi/build/src/CMakeFiles/edit_cache.dir +/root/swg-main/stationapi/build/src/stationapi/CMakeFiles/rebuild_cache.dir +/root/swg-main/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir +/root/swg-main/stationapi/build/src/stationapi/CMakeFiles/edit_cache.dir +/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/rebuild_cache.dir +/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir +/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/edit_cache.dir +/root/swg-main/stationapi/build/tests/CMakeFiles/rebuild_cache.dir +/root/swg-main/stationapi/build/tests/CMakeFiles/stationapi_tests.dir +/root/swg-main/stationapi/build/tests/CMakeFiles/edit_cache.dir diff --git a/stationapi/build/CMakeFiles/cmake.check_cache b/stationapi/build/CMakeFiles/cmake.check_cache new file mode 100644 index 0000000..3dccd73 --- /dev/null +++ b/stationapi/build/CMakeFiles/cmake.check_cache @@ -0,0 +1 @@ +# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/stationapi/build/CMakeFiles/feature_tests.bin b/stationapi/build/CMakeFiles/feature_tests.bin new file mode 100755 index 0000000..2273ed5 Binary files /dev/null and b/stationapi/build/CMakeFiles/feature_tests.bin differ diff --git a/stationapi/build/CMakeFiles/feature_tests.c b/stationapi/build/CMakeFiles/feature_tests.c new file mode 100644 index 0000000..6590dde --- /dev/null +++ b/stationapi/build/CMakeFiles/feature_tests.c @@ -0,0 +1,34 @@ + + const char features[] = {"\n" +"C_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 +"1" +#else +"0" +#endif +"c_function_prototypes\n" +"C_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +"1" +#else +"0" +#endif +"c_restrict\n" +"C_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201000L +"1" +#else +"0" +#endif +"c_static_assert\n" +"C_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +"1" +#else +"0" +#endif +"c_variadic_macros\n" + +}; + +int main(int argc, char** argv) { (void)argv; return features[argc]; } diff --git a/stationapi/build/CMakeFiles/feature_tests.cxx b/stationapi/build/CMakeFiles/feature_tests.cxx new file mode 100644 index 0000000..b93418c --- /dev/null +++ b/stationapi/build/CMakeFiles/feature_tests.cxx @@ -0,0 +1,405 @@ + + const char features[] = {"\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L +"1" +#else +"0" +#endif +"cxx_aggregate_default_initializers\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_alias_templates\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_alignas\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_alignof\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_attributes\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_attribute_deprecated\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_auto_type\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_binary_literals\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_constexpr\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_contextual_conversions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_decltype\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_decltype_auto\n" +"CXX_FEATURE:" +#if ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_decltype_incomplete_return_types\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_default_function_template_args\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_defaulted_functions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_defaulted_move_initializers\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_delegating_constructors\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_deleted_functions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_digit_separators\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_enum_forward_declarations\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_explicit_conversions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_extended_friend_declarations\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_extern_templates\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_final\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_func_identifier\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_generalized_initializers\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_generic_lambdas\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_inheriting_constructors\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_inline_namespaces\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_lambdas\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_lambda_init_captures\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_local_type_template_args\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_long_long_type\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_noexcept\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_nonstatic_member_init\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_nullptr\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_override\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_range_for\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_raw_string_literals\n" +"CXX_FEATURE:" +#if ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_reference_qualified_functions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L +"1" +#else +"0" +#endif +"cxx_relaxed_constexpr\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_return_type_deduction\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_right_angle_brackets\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_rvalue_references\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_sizeof_member\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_static_assert\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_strong_enums\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && __cplusplus +"1" +#else +"0" +#endif +"cxx_template_template_parameters\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_thread_local\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_trailing_return_types\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_unicode_literals\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_uniform_initialization\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_unrestricted_unions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_user_literals\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L +"1" +#else +"0" +#endif +"cxx_variable_templates\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_variadic_macros\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_variadic_templates\n" + +}; + +int main(int argc, char** argv) { (void)argv; return features[argc]; } diff --git a/stationapi/build/CMakeFiles/progress.marks b/stationapi/build/CMakeFiles/progress.marks new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/stationapi/build/CMakeFiles/progress.marks @@ -0,0 +1 @@ +22 diff --git a/stationapi/build/Makefile b/stationapi/build/Makefile new file mode 100644 index 0000000..cbd41c1 --- /dev/null +++ b/stationapi/build/Makefile @@ -0,0 +1,190 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles /root/swg-main/stationapi/build/CMakeFiles/progress.marks + $(MAKE) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named udplibrary + +# Build rule for target. +udplibrary: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 udplibrary +.PHONY : udplibrary + +# fast build rule for target. +udplibrary/fast: + $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/build +.PHONY : udplibrary/fast + +#============================================================================= +# Target rules for targets named stationapi + +# Build rule for target. +stationapi: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 stationapi +.PHONY : stationapi + +# fast build rule for target. +stationapi/fast: + $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/build +.PHONY : stationapi/fast + +#============================================================================= +# Target rules for targets named stationchat + +# Build rule for target. +stationchat: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 stationchat +.PHONY : stationchat + +# fast build rule for target. +stationchat/fast: + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/build +.PHONY : stationchat/fast + +#============================================================================= +# Target rules for targets named stationapi_tests + +# Build rule for target. +stationapi_tests: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 stationapi_tests +.PHONY : stationapi_tests + +# fast build rule for target. +stationapi_tests/fast: + $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/build +.PHONY : stationapi_tests/fast + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... edit_cache" + @echo "... udplibrary" + @echo "... stationapi" + @echo "... stationchat" + @echo "... stationapi_tests" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/stationapi/build/cmake_install.cmake b/stationapi/build/cmake_install.cmake new file mode 100644 index 0000000..361619e --- /dev/null +++ b/stationapi/build/cmake_install.cmake @@ -0,0 +1,52 @@ +# Install script for directory: /root/swg-main/stationapi + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +if(NOT CMAKE_INSTALL_LOCAL_ONLY) + # Include the install script for each subdirectory. + include("/root/swg-main/stationapi/build/externals/cmake_install.cmake") + include("/root/swg-main/stationapi/build/src/cmake_install.cmake") + include("/root/swg-main/stationapi/build/tests/cmake_install.cmake") + +endif() + +if(CMAKE_INSTALL_COMPONENT) + set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") +else() + set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") +endif() + +string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT + "${CMAKE_INSTALL_MANIFEST_FILES}") +file(WRITE "/root/swg-main/stationapi/build/${CMAKE_INSTALL_MANIFEST}" + "${CMAKE_INSTALL_MANIFEST_CONTENT}") diff --git a/stationapi/build/externals/CMakeFiles/CMakeDirectoryInformation.cmake b/stationapi/build/externals/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..a19da41 --- /dev/null +++ b/stationapi/build/externals/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/root/swg-main/stationapi") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/root/swg-main/stationapi/build") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/stationapi/build/externals/CMakeFiles/progress.marks b/stationapi/build/externals/CMakeFiles/progress.marks new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/stationapi/build/externals/CMakeFiles/progress.marks @@ -0,0 +1 @@ +2 diff --git a/stationapi/build/externals/Makefile b/stationapi/build/externals/Makefile new file mode 100644 index 0000000..51f2c54 --- /dev/null +++ b/stationapi/build/externals/Makefile @@ -0,0 +1,134 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles /root/swg-main/stationapi/build/externals/CMakeFiles/progress.marks + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... edit_cache" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/stationapi/build/externals/cmake_install.cmake b/stationapi/build/externals/cmake_install.cmake new file mode 100644 index 0000000..5ed978d --- /dev/null +++ b/stationapi/build/externals/cmake_install.cmake @@ -0,0 +1,40 @@ +# Install script for directory: /root/swg-main/stationapi/externals + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +if(NOT CMAKE_INSTALL_LOCAL_ONLY) + # Include the install script for each subdirectory. + include("/root/swg-main/stationapi/build/externals/udplibrary/cmake_install.cmake") + +endif() + diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/CMakeDirectoryInformation.cmake b/stationapi/build/externals/udplibrary/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..a19da41 --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/root/swg-main/stationapi") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/root/swg-main/stationapi/build") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/progress.marks b/stationapi/build/externals/udplibrary/CMakeFiles/progress.marks new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/progress.marks @@ -0,0 +1 @@ +2 diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/CXX.includecache b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/CXX.includecache new file mode 100644 index 0000000..f1c0e13 --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/CXX.includecache @@ -0,0 +1,82 @@ +#IncludeRegexLine: ^[ ]*#[ ]*(include|import)[ ]*[<"]([^">]+)([">]) + +#IncludeRegexScan: ^.*$ + +#IncludeRegexComplain: ^$ + +#IncludeRegexTransform: + +/root/swg-main/stationapi/externals/udplibrary/UdpHandler.hpp + +/root/swg-main/stationapi/externals/udplibrary/UdpLibrary.cpp +assert.h +- +time.h +- +stdlib.h +- +stdio.h +- +string.h +- +UdpLibrary.hpp +/root/swg-main/stationapi/externals/udplibrary/UdpLibrary.hpp +winsock2.h +- +ws2tcpip.h +- +winsock.h +- +arpa/inet.h +- +netdb.h +- +sys/filio.h +- +sys/ioctl.h +- +sys/socket.h +- +sys/time.h +- +sys/types.h +- +unistd.h +- +errno.h +- +arpa/inet.h +- +netdb.h +- +sys/ioctl.h +- +sys/socket.h +- +sys/time.h +- +sys/types.h +- +unistd.h +- +netinet/ip_icmp.h +- + +/root/swg-main/stationapi/externals/udplibrary/UdpLibrary.hpp +stdio.h +- +UdpHandler.hpp +/root/swg-main/stationapi/externals/udplibrary/UdpHandler.hpp +priority.hpp +/root/swg-main/stationapi/externals/udplibrary/priority.hpp +hashtable.hpp +/root/swg-main/stationapi/externals/udplibrary/hashtable.hpp + +/root/swg-main/stationapi/externals/udplibrary/hashtable.hpp +stddef.h +- +memory.h +- + +/root/swg-main/stationapi/externals/udplibrary/priority.hpp + diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/DependInfo.cmake b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/DependInfo.cmake new file mode 100644 index 0000000..e24c645 --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/DependInfo.cmake @@ -0,0 +1,27 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/root/swg-main/stationapi/externals/udplibrary/UdpLibrary.cpp" "/root/swg-main/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BOOST_ALL_NO_LIB" + "REENTRANT" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "../externals/udplibrary" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o new file mode 100644 index 0000000..4f52155 Binary files /dev/null and b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o differ diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/build.make b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/build.make new file mode 100644 index 0000000..2d1b171 --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/build.make @@ -0,0 +1,114 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +# Include any dependencies generated for this target. +include externals/udplibrary/CMakeFiles/udplibrary.dir/depend.make + +# Include the progress variables for this target. +include externals/udplibrary/CMakeFiles/udplibrary.dir/progress.make + +# Include the compile flags for this target's objects. +include externals/udplibrary/CMakeFiles/udplibrary.dir/flags.make + +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o: externals/udplibrary/CMakeFiles/udplibrary.dir/flags.make +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o: ../externals/udplibrary/UdpLibrary.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o" + cd /root/swg-main/stationapi/build/externals/udplibrary && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o -c /root/swg-main/stationapi/externals/udplibrary/UdpLibrary.cpp + +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/udplibrary.dir/UdpLibrary.cpp.i" + cd /root/swg-main/stationapi/build/externals/udplibrary && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/externals/udplibrary/UdpLibrary.cpp > CMakeFiles/udplibrary.dir/UdpLibrary.cpp.i + +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/udplibrary.dir/UdpLibrary.cpp.s" + cd /root/swg-main/stationapi/build/externals/udplibrary && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/externals/udplibrary/UdpLibrary.cpp -o CMakeFiles/udplibrary.dir/UdpLibrary.cpp.s + +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o.requires: + +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o.requires + +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o.provides: externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o.requires + $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o.provides.build +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o.provides + +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o.provides.build: externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o + + +# Object files for target udplibrary +udplibrary_OBJECTS = \ +"CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o" + +# External object files for target udplibrary +udplibrary_EXTERNAL_OBJECTS = + +externals/udplibrary/libudplibrary.a: externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o +externals/udplibrary/libudplibrary.a: externals/udplibrary/CMakeFiles/udplibrary.dir/build.make +externals/udplibrary/libudplibrary.a: externals/udplibrary/CMakeFiles/udplibrary.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX static library libudplibrary.a" + cd /root/swg-main/stationapi/build/externals/udplibrary && $(CMAKE_COMMAND) -P CMakeFiles/udplibrary.dir/cmake_clean_target.cmake + cd /root/swg-main/stationapi/build/externals/udplibrary && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/udplibrary.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +externals/udplibrary/CMakeFiles/udplibrary.dir/build: externals/udplibrary/libudplibrary.a + +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/build + +externals/udplibrary/CMakeFiles/udplibrary.dir/requires: externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o.requires + +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/requires + +externals/udplibrary/CMakeFiles/udplibrary.dir/clean: + cd /root/swg-main/stationapi/build/externals/udplibrary && $(CMAKE_COMMAND) -P CMakeFiles/udplibrary.dir/cmake_clean.cmake +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/clean + +externals/udplibrary/CMakeFiles/udplibrary.dir/depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /root/swg-main/stationapi /root/swg-main/stationapi/externals/udplibrary /root/swg-main/stationapi/build /root/swg-main/stationapi/build/externals/udplibrary /root/swg-main/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/depend + diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/cmake_clean.cmake b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/cmake_clean.cmake new file mode 100644 index 0000000..28d9f0c --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/cmake_clean.cmake @@ -0,0 +1,10 @@ +file(REMOVE_RECURSE + "CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o" + "libudplibrary.pdb" + "libudplibrary.a" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/udplibrary.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/cmake_clean_target.cmake b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/cmake_clean_target.cmake new file mode 100644 index 0000000..87f4b99 --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/cmake_clean_target.cmake @@ -0,0 +1,3 @@ +file(REMOVE_RECURSE + "libudplibrary.a" +) diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/depend.internal b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/depend.internal new file mode 100644 index 0000000..99d4abc --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/depend.internal @@ -0,0 +1,9 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o + /root/swg-main/stationapi/externals/udplibrary/UdpHandler.hpp + /root/swg-main/stationapi/externals/udplibrary/UdpLibrary.cpp + /root/swg-main/stationapi/externals/udplibrary/UdpLibrary.hpp + /root/swg-main/stationapi/externals/udplibrary/hashtable.hpp + /root/swg-main/stationapi/externals/udplibrary/priority.hpp diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/depend.make b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/depend.make new file mode 100644 index 0000000..6b8c43e --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/depend.make @@ -0,0 +1,9 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o: ../externals/udplibrary/UdpHandler.hpp +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o: ../externals/udplibrary/UdpLibrary.cpp +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o: ../externals/udplibrary/hashtable.hpp +externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o: ../externals/udplibrary/priority.hpp + diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/flags.make b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/flags.make new file mode 100644 index 0000000..7cc9535 --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -std=c++14 -Wno-write-strings + +CXX_DEFINES = -DBOOST_ALL_NO_LIB -DREENTRANT + +CXX_INCLUDES = -I/root/swg-main/stationapi/externals/udplibrary + diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/link.txt b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/link.txt new file mode 100644 index 0000000..fcdc9c5 --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/link.txt @@ -0,0 +1,2 @@ +/usr/bin/ar qc libudplibrary.a CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o +/usr/bin/ranlib libudplibrary.a diff --git a/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/progress.make b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/progress.make new file mode 100644 index 0000000..6ec2abf --- /dev/null +++ b/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/progress.make @@ -0,0 +1,3 @@ +CMAKE_PROGRESS_1 = 21 +CMAKE_PROGRESS_2 = 22 + diff --git a/stationapi/build/externals/udplibrary/Makefile b/stationapi/build/externals/udplibrary/Makefile new file mode 100644 index 0000000..8112882 --- /dev/null +++ b/stationapi/build/externals/udplibrary/Makefile @@ -0,0 +1,180 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles /root/swg-main/stationapi/build/externals/udplibrary/CMakeFiles/progress.marks + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/udplibrary/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/udplibrary/clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/udplibrary/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/udplibrary/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Convenience name for target. +externals/udplibrary/CMakeFiles/udplibrary.dir/rule: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 externals/udplibrary/CMakeFiles/udplibrary.dir/rule +.PHONY : externals/udplibrary/CMakeFiles/udplibrary.dir/rule + +# Convenience name for target. +udplibrary: externals/udplibrary/CMakeFiles/udplibrary.dir/rule + +.PHONY : udplibrary + +# fast build rule for target. +udplibrary/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/build +.PHONY : udplibrary/fast + +UdpLibrary.o: UdpLibrary.cpp.o + +.PHONY : UdpLibrary.o + +# target to build an object file +UdpLibrary.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.o +.PHONY : UdpLibrary.cpp.o + +UdpLibrary.i: UdpLibrary.cpp.i + +.PHONY : UdpLibrary.i + +# target to preprocess a source file +UdpLibrary.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.i +.PHONY : UdpLibrary.cpp.i + +UdpLibrary.s: UdpLibrary.cpp.s + +.PHONY : UdpLibrary.s + +# target to generate assembly for a file +UdpLibrary.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f externals/udplibrary/CMakeFiles/udplibrary.dir/build.make externals/udplibrary/CMakeFiles/udplibrary.dir/UdpLibrary.cpp.s +.PHONY : UdpLibrary.cpp.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... udplibrary" + @echo "... edit_cache" + @echo "... UdpLibrary.o" + @echo "... UdpLibrary.i" + @echo "... UdpLibrary.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/stationapi/build/externals/udplibrary/cmake_install.cmake b/stationapi/build/externals/udplibrary/cmake_install.cmake new file mode 100644 index 0000000..c57f339 --- /dev/null +++ b/stationapi/build/externals/udplibrary/cmake_install.cmake @@ -0,0 +1,34 @@ +# Install script for directory: /root/swg-main/stationapi/externals/udplibrary + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + diff --git a/stationapi/build/externals/udplibrary/libudplibrary.a b/stationapi/build/externals/udplibrary/libudplibrary.a new file mode 100644 index 0000000..67c395c Binary files /dev/null and b/stationapi/build/externals/udplibrary/libudplibrary.a differ diff --git a/stationapi/build/src/CMakeFiles/CMakeDirectoryInformation.cmake b/stationapi/build/src/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..a19da41 --- /dev/null +++ b/stationapi/build/src/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/root/swg-main/stationapi") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/root/swg-main/stationapi/build") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/stationapi/build/src/CMakeFiles/progress.marks b/stationapi/build/src/CMakeFiles/progress.marks new file mode 100644 index 0000000..209e3ef --- /dev/null +++ b/stationapi/build/src/CMakeFiles/progress.marks @@ -0,0 +1 @@ +20 diff --git a/stationapi/build/src/Makefile b/stationapi/build/src/Makefile new file mode 100644 index 0000000..4abf2d0 --- /dev/null +++ b/stationapi/build/src/Makefile @@ -0,0 +1,134 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles /root/swg-main/stationapi/build/src/CMakeFiles/progress.marks + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... edit_cache" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/stationapi/build/src/cmake_install.cmake b/stationapi/build/src/cmake_install.cmake new file mode 100644 index 0000000..cf47b65 --- /dev/null +++ b/stationapi/build/src/cmake_install.cmake @@ -0,0 +1,41 @@ +# Install script for directory: /root/swg-main/stationapi/src + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +if(NOT CMAKE_INSTALL_LOCAL_ONLY) + # Include the install script for each subdirectory. + include("/root/swg-main/stationapi/build/src/stationapi/cmake_install.cmake") + include("/root/swg-main/stationapi/build/src/stationchat/cmake_install.cmake") + +endif() + diff --git a/stationapi/build/src/stationapi/CMakeFiles/CMakeDirectoryInformation.cmake b/stationapi/build/src/stationapi/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..a19da41 --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/root/swg-main/stationapi") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/root/swg-main/stationapi/build") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/stationapi/build/src/stationapi/CMakeFiles/progress.marks b/stationapi/build/src/stationapi/CMakeFiles/progress.marks new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/progress.marks @@ -0,0 +1 @@ +4 diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/CXX.includecache b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/CXX.includecache new file mode 100644 index 0000000..43b7088 --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/CXX.includecache @@ -0,0 +1,196 @@ +#IncludeRegexLine: ^[ ]*#[ ]*(include|import)[ ]*[<"]([^">]+)([">]) + +#IncludeRegexScan: ^.*$ + +#IncludeRegexComplain: ^$ + +#IncludeRegexTransform: + +../externals/easyloggingpp/easylogging++.h +syslog.h +- +ctime +- +cstring +- +cstdlib +- +cctype +- +cwchar +- +csignal +- +cerrno +- +cstdarg +- +locale +- +codecvt +- +cxxabi.h +- +execinfo.h +- +sys/system_properties.h +- +sys/stat.h +- +sys/time.h +- +direct.h +- +windows.h +- +winsock2.h +- +winsock.h +- +string +- +vector +- +map +- +utility +- +functional +- +algorithm +- +fstream +- +iostream +- +sstream +- +memory +- +type_traits +- +mutex +- +thread +- +pthread.h +- +thread +- +queue +- +condition_variable +- +list +- +queue +- +deque +- +set +- +bitset +- +stack +- +array +- +unordered_map +- +unordered_set +- +QString +- +QByteArray +- +QVector +- +QList +- +QPair +- +QMap +- +QQueue +- +QSet +- +QLinkedList +- +QHash +- +QMultiHash +- +QStack +- +boost/container/vector.hpp +- +boost/container/stable_vector.hpp +- +boost/container/list.hpp +- +boost/container/deque.hpp +- +boost/container/map.hpp +- +boost/container/flat_map.hpp +- +boost/container/set.hpp +- +boost/container/flat_set.hpp +- +wx/vector.h +- + +../externals/udplibrary/UdpHandler.hpp + +../externals/udplibrary/UdpLibrary.hpp +stdio.h +- +UdpHandler.hpp +../externals/udplibrary/UdpHandler.hpp +priority.hpp +../externals/udplibrary/priority.hpp +hashtable.hpp +../externals/udplibrary/hashtable.hpp + +../externals/udplibrary/hashtable.hpp +stddef.h +- +memory.h +- + +../externals/udplibrary/priority.hpp + +/root/swg-main/stationapi/src/stationapi/NodeClient.cpp +NodeClient.hpp +/root/swg-main/stationapi/src/stationapi/NodeClient.hpp +StreamUtils.hpp +/root/swg-main/stationapi/src/stationapi/StreamUtils.hpp + +/root/swg-main/stationapi/src/stationapi/NodeClient.hpp +UdpLibrary.hpp +/root/swg-main/stationapi/src/stationapi/UdpLibrary.hpp +sstream +- + +/root/swg-main/stationapi/src/stationapi/StreamUtils.cpp +StreamUtils.hpp +/root/swg-main/stationapi/src/stationapi/StreamUtils.hpp +UdpLibrary.hpp +/root/swg-main/stationapi/src/stationapi/UdpLibrary.hpp +easylogging++.h +/root/swg-main/stationapi/src/stationapi/easylogging++.h +iomanip +- + +/root/swg-main/stationapi/src/stationapi/StreamUtils.hpp +ostream +- +string +- + +/root/swg-main/stationapi/src/stationapi/StringUtils.cpp +StreamUtils.hpp +/root/swg-main/stationapi/src/stationapi/StreamUtils.hpp + diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/DependInfo.cmake b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/DependInfo.cmake new file mode 100644 index 0000000..d70d7ab --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/DependInfo.cmake @@ -0,0 +1,30 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/root/swg-main/stationapi/src/stationapi/NodeClient.cpp" "/root/swg-main/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o" + "/root/swg-main/stationapi/src/stationapi/StreamUtils.cpp" "/root/swg-main/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o" + "/root/swg-main/stationapi/src/stationapi/StringUtils.cpp" "/root/swg-main/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BOOST_ALL_NO_LIB" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "../src/stationapi" + "../externals/easyloggingpp" + "../externals/udplibrary" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o new file mode 100644 index 0000000..ff22fa8 Binary files /dev/null and b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o differ diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o new file mode 100644 index 0000000..30e49e2 Binary files /dev/null and b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o differ diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o new file mode 100644 index 0000000..b85f58b Binary files /dev/null and b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o differ diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/build.make b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/build.make new file mode 100644 index 0000000..38c677c --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/build.make @@ -0,0 +1,168 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +# Include any dependencies generated for this target. +include src/stationapi/CMakeFiles/stationapi.dir/depend.make + +# Include the progress variables for this target. +include src/stationapi/CMakeFiles/stationapi.dir/progress.make + +# Include the compile flags for this target's objects. +include src/stationapi/CMakeFiles/stationapi.dir/flags.make + +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: src/stationapi/CMakeFiles/stationapi.dir/flags.make +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: ../src/stationapi/NodeClient.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationapi.dir/NodeClient.cpp.o -c /root/swg-main/stationapi/src/stationapi/NodeClient.cpp + +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationapi.dir/NodeClient.cpp.i" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationapi/NodeClient.cpp > CMakeFiles/stationapi.dir/NodeClient.cpp.i + +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationapi.dir/NodeClient.cpp.s" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationapi/NodeClient.cpp -o CMakeFiles/stationapi.dir/NodeClient.cpp.s + +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o.requires: + +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o.requires + +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o.provides: src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o.requires + $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o.provides.build +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o.provides + +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o.provides.build: src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o + + +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: src/stationapi/CMakeFiles/stationapi.dir/flags.make +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: ../src/stationapi/StreamUtils.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Building CXX object src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationapi.dir/StreamUtils.cpp.o -c /root/swg-main/stationapi/src/stationapi/StreamUtils.cpp + +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationapi.dir/StreamUtils.cpp.i" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationapi/StreamUtils.cpp > CMakeFiles/stationapi.dir/StreamUtils.cpp.i + +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationapi.dir/StreamUtils.cpp.s" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationapi/StreamUtils.cpp -o CMakeFiles/stationapi.dir/StreamUtils.cpp.s + +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o.requires: + +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o.requires + +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o.provides: src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o.requires + $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o.provides.build +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o.provides + +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o.provides.build: src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o + + +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o: src/stationapi/CMakeFiles/stationapi.dir/flags.make +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o: ../src/stationapi/StringUtils.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Building CXX object src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationapi.dir/StringUtils.cpp.o -c /root/swg-main/stationapi/src/stationapi/StringUtils.cpp + +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationapi.dir/StringUtils.cpp.i" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationapi/StringUtils.cpp > CMakeFiles/stationapi.dir/StringUtils.cpp.i + +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationapi.dir/StringUtils.cpp.s" + cd /root/swg-main/stationapi/build/src/stationapi && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationapi/StringUtils.cpp -o CMakeFiles/stationapi.dir/StringUtils.cpp.s + +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o.requires: + +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o.requires + +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o.provides: src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o.requires + $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o.provides.build +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o.provides + +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o.provides.build: src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o + + +# Object files for target stationapi +stationapi_OBJECTS = \ +"CMakeFiles/stationapi.dir/NodeClient.cpp.o" \ +"CMakeFiles/stationapi.dir/StreamUtils.cpp.o" \ +"CMakeFiles/stationapi.dir/StringUtils.cpp.o" + +# External object files for target stationapi +stationapi_EXTERNAL_OBJECTS = + +src/stationapi/libstationapi.a: src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o +src/stationapi/libstationapi.a: src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o +src/stationapi/libstationapi.a: src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o +src/stationapi/libstationapi.a: src/stationapi/CMakeFiles/stationapi.dir/build.make +src/stationapi/libstationapi.a: src/stationapi/CMakeFiles/stationapi.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_4) "Linking CXX static library libstationapi.a" + cd /root/swg-main/stationapi/build/src/stationapi && $(CMAKE_COMMAND) -P CMakeFiles/stationapi.dir/cmake_clean_target.cmake + cd /root/swg-main/stationapi/build/src/stationapi && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/stationapi.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +src/stationapi/CMakeFiles/stationapi.dir/build: src/stationapi/libstationapi.a + +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/build + +src/stationapi/CMakeFiles/stationapi.dir/requires: src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o.requires +src/stationapi/CMakeFiles/stationapi.dir/requires: src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o.requires +src/stationapi/CMakeFiles/stationapi.dir/requires: src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o.requires + +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/requires + +src/stationapi/CMakeFiles/stationapi.dir/clean: + cd /root/swg-main/stationapi/build/src/stationapi && $(CMAKE_COMMAND) -P CMakeFiles/stationapi.dir/cmake_clean.cmake +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/clean + +src/stationapi/CMakeFiles/stationapi.dir/depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /root/swg-main/stationapi /root/swg-main/stationapi/src/stationapi /root/swg-main/stationapi/build /root/swg-main/stationapi/build/src/stationapi /root/swg-main/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/depend + diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/cmake_clean.cmake b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/cmake_clean.cmake new file mode 100644 index 0000000..a03d116 --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/cmake_clean.cmake @@ -0,0 +1,12 @@ +file(REMOVE_RECURSE + "CMakeFiles/stationapi.dir/NodeClient.cpp.o" + "CMakeFiles/stationapi.dir/StreamUtils.cpp.o" + "CMakeFiles/stationapi.dir/StringUtils.cpp.o" + "libstationapi.pdb" + "libstationapi.a" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/stationapi.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/cmake_clean_target.cmake b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/cmake_clean_target.cmake new file mode 100644 index 0000000..cc1a3be --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/cmake_clean_target.cmake @@ -0,0 +1,3 @@ +file(REMOVE_RECURSE + "libstationapi.a" +) diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/depend.internal b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/depend.internal new file mode 100644 index 0000000..c7de575 --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/depend.internal @@ -0,0 +1,22 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + /root/swg-main/stationapi/src/stationapi/NodeClient.cpp + /root/swg-main/stationapi/src/stationapi/NodeClient.hpp + /root/swg-main/stationapi/src/stationapi/StreamUtils.hpp +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + /root/swg-main/stationapi/src/stationapi/StreamUtils.cpp + /root/swg-main/stationapi/src/stationapi/StreamUtils.hpp +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o + /root/swg-main/stationapi/src/stationapi/StreamUtils.hpp + /root/swg-main/stationapi/src/stationapi/StringUtils.cpp diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/depend.make b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/depend.make new file mode 100644 index 0000000..8443e64 --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/depend.make @@ -0,0 +1,22 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: ../externals/udplibrary/priority.hpp +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: ../src/stationapi/NodeClient.cpp +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: ../src/stationapi/NodeClient.hpp +src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o: ../src/stationapi/StreamUtils.hpp + +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: ../externals/udplibrary/priority.hpp +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: ../src/stationapi/StreamUtils.cpp +src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o: ../src/stationapi/StreamUtils.hpp + +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o: ../src/stationapi/StreamUtils.hpp +src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o: ../src/stationapi/StringUtils.cpp + diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/flags.make b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/flags.make new file mode 100644 index 0000000..734817f --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -std=c++14 -Wno-write-strings + +CXX_DEFINES = -DBOOST_ALL_NO_LIB + +CXX_INCLUDES = -I/root/swg-main/stationapi/src/stationapi -I/root/swg-main/stationapi/externals/easyloggingpp -I/root/swg-main/stationapi/externals/udplibrary + diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/link.txt b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/link.txt new file mode 100644 index 0000000..26e7fdd --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/link.txt @@ -0,0 +1,2 @@ +/usr/bin/ar qc libstationapi.a CMakeFiles/stationapi.dir/NodeClient.cpp.o CMakeFiles/stationapi.dir/StreamUtils.cpp.o CMakeFiles/stationapi.dir/StringUtils.cpp.o +/usr/bin/ranlib libstationapi.a diff --git a/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/progress.make b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/progress.make new file mode 100644 index 0000000..a69a57e --- /dev/null +++ b/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/progress.make @@ -0,0 +1,5 @@ +CMAKE_PROGRESS_1 = 1 +CMAKE_PROGRESS_2 = 2 +CMAKE_PROGRESS_3 = 3 +CMAKE_PROGRESS_4 = 4 + diff --git a/stationapi/build/src/stationapi/Makefile b/stationapi/build/src/stationapi/Makefile new file mode 100644 index 0000000..bf6b00b --- /dev/null +++ b/stationapi/build/src/stationapi/Makefile @@ -0,0 +1,240 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles /root/swg-main/stationapi/build/src/stationapi/CMakeFiles/progress.marks + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationapi/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationapi/clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationapi/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationapi/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Convenience name for target. +src/stationapi/CMakeFiles/stationapi.dir/rule: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationapi/CMakeFiles/stationapi.dir/rule +.PHONY : src/stationapi/CMakeFiles/stationapi.dir/rule + +# Convenience name for target. +stationapi: src/stationapi/CMakeFiles/stationapi.dir/rule + +.PHONY : stationapi + +# fast build rule for target. +stationapi/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/build +.PHONY : stationapi/fast + +NodeClient.o: NodeClient.cpp.o + +.PHONY : NodeClient.o + +# target to build an object file +NodeClient.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.o +.PHONY : NodeClient.cpp.o + +NodeClient.i: NodeClient.cpp.i + +.PHONY : NodeClient.i + +# target to preprocess a source file +NodeClient.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.i +.PHONY : NodeClient.cpp.i + +NodeClient.s: NodeClient.cpp.s + +.PHONY : NodeClient.s + +# target to generate assembly for a file +NodeClient.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/NodeClient.cpp.s +.PHONY : NodeClient.cpp.s + +StreamUtils.o: StreamUtils.cpp.o + +.PHONY : StreamUtils.o + +# target to build an object file +StreamUtils.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.o +.PHONY : StreamUtils.cpp.o + +StreamUtils.i: StreamUtils.cpp.i + +.PHONY : StreamUtils.i + +# target to preprocess a source file +StreamUtils.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.i +.PHONY : StreamUtils.cpp.i + +StreamUtils.s: StreamUtils.cpp.s + +.PHONY : StreamUtils.s + +# target to generate assembly for a file +StreamUtils.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/StreamUtils.cpp.s +.PHONY : StreamUtils.cpp.s + +StringUtils.o: StringUtils.cpp.o + +.PHONY : StringUtils.o + +# target to build an object file +StringUtils.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.o +.PHONY : StringUtils.cpp.o + +StringUtils.i: StringUtils.cpp.i + +.PHONY : StringUtils.i + +# target to preprocess a source file +StringUtils.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.i +.PHONY : StringUtils.cpp.i + +StringUtils.s: StringUtils.cpp.s + +.PHONY : StringUtils.s + +# target to generate assembly for a file +StringUtils.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationapi/CMakeFiles/stationapi.dir/build.make src/stationapi/CMakeFiles/stationapi.dir/StringUtils.cpp.s +.PHONY : StringUtils.cpp.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... stationapi" + @echo "... edit_cache" + @echo "... NodeClient.o" + @echo "... NodeClient.i" + @echo "... NodeClient.s" + @echo "... StreamUtils.o" + @echo "... StreamUtils.i" + @echo "... StreamUtils.s" + @echo "... StringUtils.o" + @echo "... StringUtils.i" + @echo "... StringUtils.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/stationapi/build/src/stationapi/cmake_install.cmake b/stationapi/build/src/stationapi/cmake_install.cmake new file mode 100644 index 0000000..6164a9d --- /dev/null +++ b/stationapi/build/src/stationapi/cmake_install.cmake @@ -0,0 +1,34 @@ +# Install script for directory: /root/swg-main/stationapi/src/stationapi + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + diff --git a/stationapi/build/src/stationapi/libstationapi.a b/stationapi/build/src/stationapi/libstationapi.a new file mode 100644 index 0000000..f9005bf Binary files /dev/null and b/stationapi/build/src/stationapi/libstationapi.a differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/CMakeDirectoryInformation.cmake b/stationapi/build/src/stationchat/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..a19da41 --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/root/swg-main/stationapi") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/root/swg-main/stationapi/build") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/stationapi/build/src/stationchat/CMakeFiles/progress.marks b/stationapi/build/src/stationchat/CMakeFiles/progress.marks new file mode 100644 index 0000000..209e3ef --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/progress.marks @@ -0,0 +1 @@ +20 diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/CXX.includecache b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/CXX.includecache new file mode 100644 index 0000000..a5d482b --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/CXX.includecache @@ -0,0 +1,1082 @@ +#IncludeRegexLine: ^[ ]*#[ ]*(include|import)[ ]*[<"]([^">]+)([">]) + +#IncludeRegexScan: ^.*$ + +#IncludeRegexComplain: ^$ + +#IncludeRegexTransform: + +../externals/easyloggingpp/easylogging++.h +syslog.h +- +ctime +- +cstring +- +cstdlib +- +cctype +- +cwchar +- +csignal +- +cerrno +- +cstdarg +- +locale +- +codecvt +- +cxxabi.h +- +execinfo.h +- +sys/system_properties.h +- +sys/stat.h +- +sys/time.h +- +direct.h +- +windows.h +- +winsock2.h +- +winsock.h +- +string +- +vector +- +map +- +utility +- +functional +- +algorithm +- +fstream +- +iostream +- +sstream +- +memory +- +type_traits +- +mutex +- +thread +- +pthread.h +- +thread +- +queue +- +condition_variable +- +list +- +queue +- +deque +- +set +- +bitset +- +stack +- +array +- +unordered_map +- +unordered_set +- +QString +- +QByteArray +- +QVector +- +QList +- +QPair +- +QMap +- +QQueue +- +QSet +- +QLinkedList +- +QHash +- +QMultiHash +- +QStack +- +boost/container/vector.hpp +- +boost/container/stable_vector.hpp +- +boost/container/list.hpp +- +boost/container/deque.hpp +- +boost/container/map.hpp +- +boost/container/flat_map.hpp +- +boost/container/set.hpp +- +boost/container/flat_set.hpp +- +wx/vector.h +- + +../externals/udplibrary/UdpHandler.hpp + +../externals/udplibrary/UdpLibrary.hpp +stdio.h +- +UdpHandler.hpp +../externals/udplibrary/UdpHandler.hpp +priority.hpp +../externals/udplibrary/priority.hpp +hashtable.hpp +../externals/udplibrary/hashtable.hpp + +../externals/udplibrary/hashtable.hpp +stddef.h +- +memory.h +- + +../externals/udplibrary/priority.hpp + +../src/stationapi/Node.hpp +UdpLibrary.hpp +../src/stationapi/UdpLibrary.hpp +algorithm +- +cstdint +- +memory +- +string +- +vector +- + +../src/stationapi/NodeClient.hpp +UdpLibrary.hpp +../src/stationapi/UdpLibrary.hpp +sstream +- + +../src/stationapi/SQLite3.hpp +string +- +sqlite3.h +- + +../src/stationapi/Serialization.hpp +string +- +type_traits +- + +../src/stationapi/StreamUtils.hpp +ostream +- +string +- + +../src/stationapi/StringUtils.hpp +string +- + +../src/stationchat/ChatAvatar.hpp +Serialization.hpp +../src/stationchat/Serialization.hpp +cstdint +- +string +- +vector +- + +../src/stationchat/ChatAvatarService.hpp +ChatAvatar.hpp +../src/stationchat/ChatAvatar.hpp +ChatEnums.hpp +../src/stationchat/ChatEnums.hpp +boost/optional.hpp +- +memory +- +string +- +unordered_map +- + +../src/stationchat/ChatEnums.hpp +cstdint +- +string +- + +../src/stationchat/ChatRoom.hpp +ChatEnums.hpp +../src/stationchat/ChatEnums.hpp +string +- +vector +- + +../src/stationchat/ChatRoomService.hpp +ChatEnums.hpp +../src/stationchat/ChatEnums.hpp +ChatRoom.hpp +../src/stationchat/ChatRoom.hpp +boost/optional.hpp +- +cstdint +- +memory +- +set +- +vector +- + +../src/stationchat/GatewayClient.hpp +ChatEnums.hpp +../src/stationchat/ChatEnums.hpp +NodeClient.hpp +../src/stationchat/NodeClient.hpp +SQLite3.hpp +../src/stationchat/SQLite3.hpp +easylogging++.h +../src/stationchat/easylogging++.h + +../src/stationchat/GatewayNode.hpp +Node.hpp +../src/stationchat/Node.hpp +GatewayClient.hpp +../src/stationchat/GatewayClient.hpp +map +- +memory +- + +../src/stationchat/PersistentMessage.hpp +cstdint +- +string +- + +../src/stationchat/PersistentMessageService.hpp +ChatEnums.hpp +../src/stationchat/ChatEnums.hpp +PersistentMessage.hpp +../src/stationchat/PersistentMessage.hpp +boost/optional.hpp +- +cstdint +- +vector +- + +../src/stationchat/RegistrarClient.hpp +NodeClient.hpp +../src/stationchat/NodeClient.hpp + +../src/stationchat/RegistrarNode.hpp +Node.hpp +../src/stationchat/Node.hpp +RegistrarClient.hpp +../src/stationchat/RegistrarClient.hpp + +../src/stationchat/StationChatConfig.hpp +cstdint +- +string +- + +../src/stationchat/protocol/AddBan.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/AddFriend.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/AddIgnore.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/AddInvite.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/AddModerator.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/CreateRoom.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +ChatRoom.hpp +../src/stationchat/protocol/ChatRoom.hpp +cstdint +- +string +- +vector +- + +../src/stationchat/protocol/DestroyAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/DestroyRoom.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/EnterRoom.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +ChatRoom.hpp +../src/stationchat/protocol/ChatRoom.hpp + +../src/stationchat/protocol/FailoverReLoginAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +cstdint +- +string +- + +../src/stationchat/protocol/FriendStatus.hpp +ChatAvatar.hpp +../src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +string +- + +../src/stationchat/protocol/GetAnyAvatar.hpp +ChatAvatar.hpp +../src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/GetPersistentHeaders.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +PersistentMessage.hpp +../src/stationchat/protocol/PersistentMessage.hpp +vector +- + +../src/stationchat/protocol/GetPersistentMessage.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +PersistentMessage.hpp +../src/stationchat/protocol/PersistentMessage.hpp + +../src/stationchat/protocol/GetRoom.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +ChatRoom.hpp +../src/stationchat/protocol/ChatRoom.hpp + +../src/stationchat/protocol/GetRoomSummaries.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +ChatRoom.hpp +../src/stationchat/protocol/ChatRoom.hpp +vector +- + +../src/stationchat/protocol/IgnoreStatus.hpp +ChatAvatar.hpp +../src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/KickAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/LeaveRoom.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/LoginAvatar.hpp +ChatAvatar.hpp +../src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/LogoutAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/RegistrarGetChatServer.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/RemoveBan.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/RemoveFriend.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/RemoveIgnore.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/RemoveInvite.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/RemoveModerator.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/SendInstantMessage.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/SendPersistentMessage.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/SendRoomMessage.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/SetApiVersion.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/SetAvatarAttributes.hpp +ChatAvatar.hpp +../src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp + +../src/stationchat/protocol/UpdatePersistentMessage.hpp +ChatEnums.hpp +../src/stationchat/protocol/ChatEnums.hpp +PersistentMessage.hpp +../src/stationchat/protocol/PersistentMessage.hpp + +/root/swg-main/stationapi/src/stationchat/ChatAvatar.cpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp +ChatAvatarService.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp +ChatRoom.hpp +/root/swg-main/stationapi/src/stationchat/ChatRoom.hpp +algorithm +- + +/root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp +Serialization.hpp +/root/swg-main/stationapi/src/stationchat/Serialization.hpp +cstdint +- +string +- +vector +- + +/root/swg-main/stationapi/src/stationchat/ChatAvatarService.cpp +ChatAvatarService.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp +SQLite3.hpp +/root/swg-main/stationapi/src/stationchat/SQLite3.hpp +StringUtils.hpp +/root/swg-main/stationapi/src/stationchat/StringUtils.hpp +easylogging++.h +- + +/root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +boost/optional.hpp +- +memory +- +string +- +unordered_map +- + +/root/swg-main/stationapi/src/stationchat/ChatEnums.cpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +cstdint +- +string +- + +/root/swg-main/stationapi/src/stationchat/ChatRoom.cpp +ChatRoom.hpp +/root/swg-main/stationapi/src/stationchat/ChatRoom.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp +ChatRoomService.hpp +/root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp +algorithm +- + +/root/swg-main/stationapi/src/stationchat/ChatRoom.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +string +- +vector +- + +/root/swg-main/stationapi/src/stationchat/ChatRoomService.cpp +ChatRoomService.hpp +/root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp +ChatAvatarService.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp +SQLite3.hpp +/root/swg-main/stationapi/src/stationchat/SQLite3.hpp +StreamUtils.hpp +/root/swg-main/stationapi/src/stationchat/StreamUtils.hpp +StringUtils.hpp +/root/swg-main/stationapi/src/stationchat/StringUtils.hpp +easylogging++.h +/root/swg-main/stationapi/src/stationchat/easylogging++.h + +/root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +ChatRoom.hpp +/root/swg-main/stationapi/src/stationchat/ChatRoom.hpp +boost/optional.hpp +- +cstdint +- +memory +- +set +- +vector +- + +/root/swg-main/stationapi/src/stationchat/GatewayClient.cpp +GatewayClient.hpp +/root/swg-main/stationapi/src/stationchat/GatewayClient.hpp +ChatAvatarService.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +ChatRoomService.hpp +/root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp +GatewayNode.hpp +/root/swg-main/stationapi/src/stationchat/GatewayNode.hpp +Message.hpp +/root/swg-main/stationapi/src/stationchat/Message.hpp +PersistentMessageService.hpp +/root/swg-main/stationapi/src/stationchat/PersistentMessageService.hpp +SQLite3.hpp +/root/swg-main/stationapi/src/stationchat/SQLite3.hpp +StationChatConfig.hpp +/root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp +UdpLibrary.hpp +/root/swg-main/stationapi/src/stationchat/UdpLibrary.hpp +protocol/AddBan.hpp +/root/swg-main/stationapi/src/stationchat/protocol/AddBan.hpp +protocol/AddFriend.hpp +/root/swg-main/stationapi/src/stationchat/protocol/AddFriend.hpp +protocol/AddIgnore.hpp +/root/swg-main/stationapi/src/stationchat/protocol/AddIgnore.hpp +protocol/AddInvite.hpp +/root/swg-main/stationapi/src/stationchat/protocol/AddInvite.hpp +protocol/AddModerator.hpp +/root/swg-main/stationapi/src/stationchat/protocol/AddModerator.hpp +protocol/CreateRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/CreateRoom.hpp +protocol/DestroyRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/DestroyRoom.hpp +protocol/EnterRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/EnterRoom.hpp +protocol/FailoverReLoginAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/FailoverReLoginAvatar.hpp +protocol/FriendStatus.hpp +/root/swg-main/stationapi/src/stationchat/protocol/FriendStatus.hpp +protocol/GetAnyAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/GetAnyAvatar.hpp +protocol/GetPersistentHeaders.hpp +/root/swg-main/stationapi/src/stationchat/protocol/GetPersistentHeaders.hpp +protocol/GetPersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/GetPersistentMessage.hpp +protocol/GetRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/GetRoom.hpp +protocol/GetRoomSummaries.hpp +/root/swg-main/stationapi/src/stationchat/protocol/GetRoomSummaries.hpp +protocol/IgnoreStatus.hpp +/root/swg-main/stationapi/src/stationchat/protocol/IgnoreStatus.hpp +protocol/KickAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/KickAvatar.hpp +protocol/LeaveRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/LeaveRoom.hpp +protocol/LoginAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/LoginAvatar.hpp +protocol/LogoutAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/LogoutAvatar.hpp +protocol/RemoveBan.hpp +/root/swg-main/stationapi/src/stationchat/protocol/RemoveBan.hpp +protocol/RemoveFriend.hpp +/root/swg-main/stationapi/src/stationchat/protocol/RemoveFriend.hpp +protocol/RemoveIgnore.hpp +/root/swg-main/stationapi/src/stationchat/protocol/RemoveIgnore.hpp +protocol/RemoveInvite.hpp +/root/swg-main/stationapi/src/stationchat/protocol/RemoveInvite.hpp +protocol/RemoveModerator.hpp +/root/swg-main/stationapi/src/stationchat/protocol/RemoveModerator.hpp +protocol/SendInstantMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/SendInstantMessage.hpp +protocol/SendPersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/SendPersistentMessage.hpp +protocol/SendRoomMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/SendRoomMessage.hpp +protocol/SetApiVersion.hpp +/root/swg-main/stationapi/src/stationchat/protocol/SetApiVersion.hpp +protocol/SetAvatarAttributes.hpp +/root/swg-main/stationapi/src/stationchat/protocol/SetAvatarAttributes.hpp +protocol/UpdatePersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/UpdatePersistentMessage.hpp +easylogging++.h +/root/swg-main/stationapi/src/stationchat/easylogging++.h + +/root/swg-main/stationapi/src/stationchat/GatewayClient.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +NodeClient.hpp +/root/swg-main/stationapi/src/stationchat/NodeClient.hpp +SQLite3.hpp +/root/swg-main/stationapi/src/stationchat/SQLite3.hpp +easylogging++.h +/root/swg-main/stationapi/src/stationchat/easylogging++.h + +/root/swg-main/stationapi/src/stationchat/GatewayNode.cpp +GatewayNode.hpp +/root/swg-main/stationapi/src/stationchat/GatewayNode.hpp +ChatAvatarService.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp +ChatRoomService.hpp +/root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp +PersistentMessageService.hpp +/root/swg-main/stationapi/src/stationchat/PersistentMessageService.hpp +StationChatConfig.hpp +/root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp +sqlite3.h +- + +/root/swg-main/stationapi/src/stationchat/GatewayNode.hpp +Node.hpp +/root/swg-main/stationapi/src/stationchat/Node.hpp +GatewayClient.hpp +/root/swg-main/stationapi/src/stationchat/GatewayClient.hpp +map +- +memory +- + +/root/swg-main/stationapi/src/stationchat/Message.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp +PersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/PersistentMessage.hpp +cstdint +- +string +- + +/root/swg-main/stationapi/src/stationchat/PersistentMessage.hpp +cstdint +- +string +- + +/root/swg-main/stationapi/src/stationchat/PersistentMessageService.cpp +PersistentMessageService.hpp +/root/swg-main/stationapi/src/stationchat/PersistentMessageService.hpp +SQLite3.hpp +/root/swg-main/stationapi/src/stationchat/SQLite3.hpp +StringUtils.hpp +/root/swg-main/stationapi/src/stationchat/StringUtils.hpp + +/root/swg-main/stationapi/src/stationchat/PersistentMessageService.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +PersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/PersistentMessage.hpp +boost/optional.hpp +- +cstdint +- +vector +- + +/root/swg-main/stationapi/src/stationchat/RegistrarClient.cpp +RegistrarClient.hpp +/root/swg-main/stationapi/src/stationchat/RegistrarClient.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +RegistrarNode.hpp +/root/swg-main/stationapi/src/stationchat/RegistrarNode.hpp +Serialization.hpp +/root/swg-main/stationapi/src/stationchat/Serialization.hpp +StationChatConfig.hpp +/root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp +StringUtils.hpp +/root/swg-main/stationapi/src/stationchat/StringUtils.hpp +protocol/RegistrarGetChatServer.hpp +/root/swg-main/stationapi/src/stationchat/protocol/RegistrarGetChatServer.hpp +easylogging++.h +/root/swg-main/stationapi/src/stationchat/easylogging++.h + +/root/swg-main/stationapi/src/stationchat/RegistrarClient.hpp +NodeClient.hpp +/root/swg-main/stationapi/src/stationchat/NodeClient.hpp + +/root/swg-main/stationapi/src/stationchat/RegistrarNode.cpp +RegistrarNode.hpp +/root/swg-main/stationapi/src/stationchat/RegistrarNode.hpp +StationChatConfig.hpp +/root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp + +/root/swg-main/stationapi/src/stationchat/RegistrarNode.hpp +Node.hpp +/root/swg-main/stationapi/src/stationchat/Node.hpp +RegistrarClient.hpp +/root/swg-main/stationapi/src/stationchat/RegistrarClient.hpp + +/root/swg-main/stationapi/src/stationchat/StationChatApp.cpp +StationChatApp.hpp +/root/swg-main/stationapi/src/stationchat/StationChatApp.hpp +easylogging++.h +/root/swg-main/stationapi/src/stationchat/easylogging++.h + +/root/swg-main/stationapi/src/stationchat/StationChatApp.hpp +GatewayNode.hpp +/root/swg-main/stationapi/src/stationchat/GatewayNode.hpp +RegistrarNode.hpp +/root/swg-main/stationapi/src/stationchat/RegistrarNode.hpp +StationChatConfig.hpp +/root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp +cstdint +- +memory +- +string +- + +/root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp +cstdint +- +string +- + +/root/swg-main/stationapi/src/stationchat/main.cpp +easylogging++.h +/root/swg-main/stationapi/src/stationchat/easylogging++.h +StationChatApp.hpp +/root/swg-main/stationapi/src/stationchat/StationChatApp.hpp +boost/program_options.hpp +- +chrono +- +fstream +- +iostream +- +thread +- +execinfo.h +- + +/root/swg-main/stationapi/src/stationchat/protocol/AddBan.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/AddFriend.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/AddIgnore.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/AddInvite.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/AddModerator.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/CreateRoom.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +ChatRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatRoom.hpp +cstdint +- +string +- +vector +- + +/root/swg-main/stationapi/src/stationchat/protocol/DestroyRoom.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/EnterRoom.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +ChatRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatRoom.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/FailoverReLoginAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +cstdint +- +string +- + +/root/swg-main/stationapi/src/stationchat/protocol/FriendStatus.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +string +- + +/root/swg-main/stationapi/src/stationchat/protocol/GetAnyAvatar.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/GetPersistentHeaders.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +PersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/PersistentMessage.hpp +vector +- + +/root/swg-main/stationapi/src/stationchat/protocol/GetPersistentMessage.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +PersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/PersistentMessage.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/GetRoom.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +ChatRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatRoom.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/GetRoomSummaries.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +ChatRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatRoom.hpp +vector +- + +/root/swg-main/stationapi/src/stationchat/protocol/IgnoreStatus.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/KickAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/LeaveRoom.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/LoginAvatar.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/LogoutAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/Protocol.cpp +AddBan.hpp +/root/swg-main/stationapi/src/stationchat/protocol/AddBan.hpp +ChatAvatarService.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatAvatarService.hpp +ChatRoomService.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatRoomService.hpp +GatewayClient.hpp +/root/swg-main/stationapi/src/stationchat/protocol/GatewayClient.hpp +GatewayNode.hpp +/root/swg-main/stationapi/src/stationchat/protocol/GatewayNode.hpp +PersistentMessageService.hpp +/root/swg-main/stationapi/src/stationchat/protocol/PersistentMessageService.hpp +RegistrarClient.hpp +/root/swg-main/stationapi/src/stationchat/protocol/RegistrarClient.hpp +RegistrarNode.hpp +/root/swg-main/stationapi/src/stationchat/protocol/RegistrarNode.hpp +StringUtils.hpp +/root/swg-main/stationapi/src/stationchat/protocol/StringUtils.hpp +StationChatConfig.hpp +/root/swg-main/stationapi/src/stationchat/protocol/StationChatConfig.hpp +protocol/AddBan.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/AddBan.hpp +protocol/AddFriend.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/AddFriend.hpp +protocol/AddIgnore.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/AddIgnore.hpp +protocol/AddInvite.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/AddInvite.hpp +protocol/AddModerator.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/AddModerator.hpp +protocol/CreateRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/CreateRoom.hpp +protocol/DestroyAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/DestroyAvatar.hpp +protocol/DestroyRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/DestroyRoom.hpp +protocol/EnterRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/EnterRoom.hpp +protocol/FailoverReLoginAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/FailoverReLoginAvatar.hpp +protocol/FriendStatus.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/FriendStatus.hpp +protocol/GetAnyAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/GetAnyAvatar.hpp +protocol/GetPersistentHeaders.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/GetPersistentHeaders.hpp +protocol/GetPersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/GetPersistentMessage.hpp +protocol/GetRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/GetRoom.hpp +protocol/GetRoomSummaries.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/GetRoomSummaries.hpp +protocol/IgnoreStatus.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/IgnoreStatus.hpp +protocol/KickAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/KickAvatar.hpp +protocol/LeaveRoom.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/LeaveRoom.hpp +protocol/LoginAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/LoginAvatar.hpp +protocol/LogoutAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/LogoutAvatar.hpp +protocol/RegistrarGetChatServer.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/RegistrarGetChatServer.hpp +protocol/RemoveBan.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/RemoveBan.hpp +protocol/RemoveFriend.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/RemoveFriend.hpp +protocol/RemoveIgnore.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/RemoveIgnore.hpp +protocol/RemoveInvite.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/RemoveInvite.hpp +protocol/RemoveModerator.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/RemoveModerator.hpp +protocol/SendInstantMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/SendInstantMessage.hpp +protocol/SendPersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/SendPersistentMessage.hpp +protocol/SendRoomMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/SendRoomMessage.hpp +protocol/SetApiVersion.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/SetApiVersion.hpp +protocol/SetAvatarAttributes.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/SetAvatarAttributes.hpp +protocol/UpdatePersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/protocol/UpdatePersistentMessage.hpp +easylogging++.h +/root/swg-main/stationapi/src/stationchat/protocol/easylogging++.h + +/root/swg-main/stationapi/src/stationchat/protocol/RegistrarGetChatServer.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/RemoveBan.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/RemoveFriend.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/RemoveIgnore.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/RemoveInvite.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/RemoveModerator.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/SendInstantMessage.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/SendPersistentMessage.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/SendRoomMessage.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/SetApiVersion.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/SetAvatarAttributes.hpp +ChatAvatar.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatAvatar.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp + +/root/swg-main/stationapi/src/stationchat/protocol/UpdatePersistentMessage.hpp +ChatEnums.hpp +/root/swg-main/stationapi/src/stationchat/protocol/ChatEnums.hpp +PersistentMessage.hpp +/root/swg-main/stationapi/src/stationchat/protocol/PersistentMessage.hpp + diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o new file mode 100644 index 0000000..8a635fd Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o new file mode 100644 index 0000000..45b8a92 Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o new file mode 100644 index 0000000..e04244e Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o new file mode 100644 index 0000000..cdf324d Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o new file mode 100644 index 0000000..3dbc762 Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/DependInfo.cmake b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/DependInfo.cmake new file mode 100644 index 0000000..848605a --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/DependInfo.cmake @@ -0,0 +1,43 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/root/swg-main/stationapi/src/stationchat/ChatAvatar.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o" + "/root/swg-main/stationapi/src/stationchat/ChatAvatarService.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o" + "/root/swg-main/stationapi/src/stationchat/ChatEnums.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o" + "/root/swg-main/stationapi/src/stationchat/ChatRoom.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o" + "/root/swg-main/stationapi/src/stationchat/ChatRoomService.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o" + "/root/swg-main/stationapi/src/stationchat/GatewayClient.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o" + "/root/swg-main/stationapi/src/stationchat/GatewayNode.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o" + "/root/swg-main/stationapi/src/stationchat/PersistentMessageService.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o" + "/root/swg-main/stationapi/src/stationchat/RegistrarClient.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o" + "/root/swg-main/stationapi/src/stationchat/RegistrarNode.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o" + "/root/swg-main/stationapi/src/stationchat/StationChatApp.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o" + "/root/swg-main/stationapi/src/stationchat/main.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o" + "/root/swg-main/stationapi/src/stationchat/protocol/Protocol.cpp" "/root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BOOST_ALL_NO_LIB" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "../src/stationchat" + "../src/stationapi" + "../externals/easyloggingpp" + "../externals/udplibrary" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + "/root/swg-main/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/DependInfo.cmake" + "/root/swg-main/stationapi/build/externals/udplibrary/CMakeFiles/udplibrary.dir/DependInfo.cmake" + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o new file mode 100644 index 0000000..b14ef58 Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o new file mode 100644 index 0000000..4ae5f09 Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o new file mode 100644 index 0000000..6ba6e82 Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o new file mode 100644 index 0000000..035a5db Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o new file mode 100644 index 0000000..b61d5ba Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o new file mode 100644 index 0000000..17d44d6 Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/build.make b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/build.make new file mode 100644 index 0000000..186fd7e --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/build.make @@ -0,0 +1,441 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +# Include any dependencies generated for this target. +include src/stationchat/CMakeFiles/stationchat.dir/depend.make + +# Include the progress variables for this target. +include src/stationchat/CMakeFiles/stationchat.dir/progress.make + +# Include the compile flags for this target's objects. +include src/stationchat/CMakeFiles/stationchat.dir/flags.make + +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/Protocol.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o -c /root/swg-main/stationapi/src/stationchat/protocol/Protocol.cpp + +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/protocol/Protocol.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/protocol/Protocol.cpp > CMakeFiles/stationchat.dir/protocol/Protocol.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/protocol/Protocol.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/protocol/Protocol.cpp -o CMakeFiles/stationchat.dir/protocol/Protocol.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o: ../src/stationchat/ChatAvatar.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/ChatAvatar.cpp.o -c /root/swg-main/stationapi/src/stationchat/ChatAvatar.cpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/ChatAvatar.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/ChatAvatar.cpp > CMakeFiles/stationchat.dir/ChatAvatar.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/ChatAvatar.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/ChatAvatar.cpp -o CMakeFiles/stationchat.dir/ChatAvatar.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../src/stationchat/ChatAvatarService.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o -c /root/swg-main/stationapi/src/stationchat/ChatAvatarService.cpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/ChatAvatarService.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/ChatAvatarService.cpp > CMakeFiles/stationchat.dir/ChatAvatarService.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/ChatAvatarService.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/ChatAvatarService.cpp -o CMakeFiles/stationchat.dir/ChatAvatarService.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o: ../src/stationchat/ChatEnums.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_4) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/ChatEnums.cpp.o -c /root/swg-main/stationapi/src/stationchat/ChatEnums.cpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/ChatEnums.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/ChatEnums.cpp > CMakeFiles/stationchat.dir/ChatEnums.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/ChatEnums.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/ChatEnums.cpp -o CMakeFiles/stationchat.dir/ChatEnums.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o: ../src/stationchat/ChatRoom.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_5) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/ChatRoom.cpp.o -c /root/swg-main/stationapi/src/stationchat/ChatRoom.cpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/ChatRoom.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/ChatRoom.cpp > CMakeFiles/stationchat.dir/ChatRoom.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/ChatRoom.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/ChatRoom.cpp -o CMakeFiles/stationchat.dir/ChatRoom.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationchat/ChatRoomService.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_6) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/ChatRoomService.cpp.o -c /root/swg-main/stationapi/src/stationchat/ChatRoomService.cpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/ChatRoomService.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/ChatRoomService.cpp > CMakeFiles/stationchat.dir/ChatRoomService.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/ChatRoomService.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/ChatRoomService.cpp -o CMakeFiles/stationchat.dir/ChatRoomService.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/GatewayClient.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_7) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/GatewayClient.cpp.o -c /root/swg-main/stationapi/src/stationchat/GatewayClient.cpp + +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/GatewayClient.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/GatewayClient.cpp > CMakeFiles/stationchat.dir/GatewayClient.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/GatewayClient.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/GatewayClient.cpp -o CMakeFiles/stationchat.dir/GatewayClient.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/GatewayNode.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_8) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/GatewayNode.cpp.o -c /root/swg-main/stationapi/src/stationchat/GatewayNode.cpp + +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/GatewayNode.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/GatewayNode.cpp > CMakeFiles/stationchat.dir/GatewayNode.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/GatewayNode.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/GatewayNode.cpp -o CMakeFiles/stationchat.dir/GatewayNode.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/main.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_9) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/main.cpp.o -c /root/swg-main/stationapi/src/stationchat/main.cpp + +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/main.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/main.cpp > CMakeFiles/stationchat.dir/main.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/main.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/main.cpp -o CMakeFiles/stationchat.dir/main.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o: ../src/stationchat/PersistentMessageService.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_10) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o -c /root/swg-main/stationapi/src/stationchat/PersistentMessageService.cpp + +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/PersistentMessageService.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/PersistentMessageService.cpp > CMakeFiles/stationchat.dir/PersistentMessageService.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/PersistentMessageService.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/PersistentMessageService.cpp -o CMakeFiles/stationchat.dir/PersistentMessageService.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationchat/RegistrarClient.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_11) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/RegistrarClient.cpp.o -c /root/swg-main/stationapi/src/stationchat/RegistrarClient.cpp + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/RegistrarClient.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/RegistrarClient.cpp > CMakeFiles/stationchat.dir/RegistrarClient.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/RegistrarClient.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/RegistrarClient.cpp -o CMakeFiles/stationchat.dir/RegistrarClient.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../src/stationchat/RegistrarNode.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_12) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/RegistrarNode.cpp.o -c /root/swg-main/stationapi/src/stationchat/RegistrarNode.cpp + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/RegistrarNode.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/RegistrarNode.cpp > CMakeFiles/stationchat.dir/RegistrarNode.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/RegistrarNode.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/RegistrarNode.cpp -o CMakeFiles/stationchat.dir/RegistrarNode.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o + + +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: src/stationchat/CMakeFiles/stationchat.dir/flags.make +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/StationChatApp.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_13) "Building CXX object src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationchat.dir/StationChatApp.cpp.o -c /root/swg-main/stationapi/src/stationchat/StationChatApp.cpp + +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationchat.dir/StationChatApp.cpp.i" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/src/stationchat/StationChatApp.cpp > CMakeFiles/stationchat.dir/StationChatApp.cpp.i + +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationchat.dir/StationChatApp.cpp.s" + cd /root/swg-main/stationapi/build/src/stationchat && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/src/stationchat/StationChatApp.cpp -o CMakeFiles/stationchat.dir/StationChatApp.cpp.s + +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o.requires: + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o.requires + +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o.provides: src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o.requires + $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o.provides.build +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o.provides + +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o.provides.build: src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o + + +# Object files for target stationchat +stationchat_OBJECTS = \ +"CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o" \ +"CMakeFiles/stationchat.dir/ChatAvatar.cpp.o" \ +"CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o" \ +"CMakeFiles/stationchat.dir/ChatEnums.cpp.o" \ +"CMakeFiles/stationchat.dir/ChatRoom.cpp.o" \ +"CMakeFiles/stationchat.dir/ChatRoomService.cpp.o" \ +"CMakeFiles/stationchat.dir/GatewayClient.cpp.o" \ +"CMakeFiles/stationchat.dir/GatewayNode.cpp.o" \ +"CMakeFiles/stationchat.dir/main.cpp.o" \ +"CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o" \ +"CMakeFiles/stationchat.dir/RegistrarClient.cpp.o" \ +"CMakeFiles/stationchat.dir/RegistrarNode.cpp.o" \ +"CMakeFiles/stationchat.dir/StationChatApp.cpp.o" + +# External object files for target stationchat +stationchat_EXTERNAL_OBJECTS = + +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/build.make +bin/stationchat: src/stationapi/libstationapi.a +bin/stationchat: externals/udplibrary/libudplibrary.a +bin/stationchat: /usr/lib/x86_64-linux-gnu/libboost_program_options.a +bin/stationchat: /usr/lib/x86_64-linux-gnu/libsqlite3.so +bin/stationchat: src/stationchat/CMakeFiles/stationchat.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_14) "Linking CXX executable ../../bin/stationchat" + cd /root/swg-main/stationapi/build/src/stationchat && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/stationchat.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +src/stationchat/CMakeFiles/stationchat.dir/build: bin/stationchat + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/build + +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o.requires +src/stationchat/CMakeFiles/stationchat.dir/requires: src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o.requires + +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/requires + +src/stationchat/CMakeFiles/stationchat.dir/clean: + cd /root/swg-main/stationapi/build/src/stationchat && $(CMAKE_COMMAND) -P CMakeFiles/stationchat.dir/cmake_clean.cmake +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/clean + +src/stationchat/CMakeFiles/stationchat.dir/depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /root/swg-main/stationapi /root/swg-main/stationapi/src/stationchat /root/swg-main/stationapi/build /root/swg-main/stationapi/build/src/stationchat /root/swg-main/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/depend + diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/cmake_clean.cmake b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/cmake_clean.cmake new file mode 100644 index 0000000..5b258a6 --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/cmake_clean.cmake @@ -0,0 +1,22 @@ +file(REMOVE_RECURSE + "CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o" + "CMakeFiles/stationchat.dir/ChatAvatar.cpp.o" + "CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o" + "CMakeFiles/stationchat.dir/ChatEnums.cpp.o" + "CMakeFiles/stationchat.dir/ChatRoom.cpp.o" + "CMakeFiles/stationchat.dir/ChatRoomService.cpp.o" + "CMakeFiles/stationchat.dir/GatewayClient.cpp.o" + "CMakeFiles/stationchat.dir/GatewayNode.cpp.o" + "CMakeFiles/stationchat.dir/main.cpp.o" + "CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o" + "CMakeFiles/stationchat.dir/RegistrarClient.cpp.o" + "CMakeFiles/stationchat.dir/RegistrarNode.cpp.o" + "CMakeFiles/stationchat.dir/StationChatApp.cpp.o" + "../../bin/stationchat.pdb" + "../../bin/stationchat" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/stationchat.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/depend.internal b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/depend.internal new file mode 100644 index 0000000..d431982 --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/depend.internal @@ -0,0 +1,241 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o + ../src/stationapi/Serialization.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatar.cpp + /root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../src/stationapi/SQLite3.hpp + ../src/stationapi/Serialization.hpp + ../src/stationapi/StringUtils.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatarService.cpp + /root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o + /root/swg-main/stationapi/src/stationchat/ChatEnums.cpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o + ../src/stationapi/Serialization.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoom.cpp + /root/swg-main/stationapi/src/stationchat/ChatRoom.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../src/stationapi/SQLite3.hpp + ../src/stationapi/Serialization.hpp + ../src/stationapi/StreamUtils.hpp + ../src/stationapi/StringUtils.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoom.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoomService.cpp + /root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + ../src/stationapi/Node.hpp + ../src/stationapi/NodeClient.hpp + ../src/stationapi/SQLite3.hpp + ../src/stationapi/Serialization.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoom.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp + /root/swg-main/stationapi/src/stationchat/GatewayClient.cpp + /root/swg-main/stationapi/src/stationchat/GatewayClient.hpp + /root/swg-main/stationapi/src/stationchat/GatewayNode.hpp + /root/swg-main/stationapi/src/stationchat/Message.hpp + /root/swg-main/stationapi/src/stationchat/PersistentMessage.hpp + /root/swg-main/stationapi/src/stationchat/PersistentMessageService.hpp + /root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp + /root/swg-main/stationapi/src/stationchat/protocol/AddBan.hpp + /root/swg-main/stationapi/src/stationchat/protocol/AddFriend.hpp + /root/swg-main/stationapi/src/stationchat/protocol/AddIgnore.hpp + /root/swg-main/stationapi/src/stationchat/protocol/AddInvite.hpp + /root/swg-main/stationapi/src/stationchat/protocol/AddModerator.hpp + /root/swg-main/stationapi/src/stationchat/protocol/CreateRoom.hpp + /root/swg-main/stationapi/src/stationchat/protocol/DestroyRoom.hpp + /root/swg-main/stationapi/src/stationchat/protocol/EnterRoom.hpp + /root/swg-main/stationapi/src/stationchat/protocol/FailoverReLoginAvatar.hpp + /root/swg-main/stationapi/src/stationchat/protocol/FriendStatus.hpp + /root/swg-main/stationapi/src/stationchat/protocol/GetAnyAvatar.hpp + /root/swg-main/stationapi/src/stationchat/protocol/GetPersistentHeaders.hpp + /root/swg-main/stationapi/src/stationchat/protocol/GetPersistentMessage.hpp + /root/swg-main/stationapi/src/stationchat/protocol/GetRoom.hpp + /root/swg-main/stationapi/src/stationchat/protocol/GetRoomSummaries.hpp + /root/swg-main/stationapi/src/stationchat/protocol/IgnoreStatus.hpp + /root/swg-main/stationapi/src/stationchat/protocol/KickAvatar.hpp + /root/swg-main/stationapi/src/stationchat/protocol/LeaveRoom.hpp + /root/swg-main/stationapi/src/stationchat/protocol/LoginAvatar.hpp + /root/swg-main/stationapi/src/stationchat/protocol/LogoutAvatar.hpp + /root/swg-main/stationapi/src/stationchat/protocol/RemoveBan.hpp + /root/swg-main/stationapi/src/stationchat/protocol/RemoveFriend.hpp + /root/swg-main/stationapi/src/stationchat/protocol/RemoveIgnore.hpp + /root/swg-main/stationapi/src/stationchat/protocol/RemoveInvite.hpp + /root/swg-main/stationapi/src/stationchat/protocol/RemoveModerator.hpp + /root/swg-main/stationapi/src/stationchat/protocol/SendInstantMessage.hpp + /root/swg-main/stationapi/src/stationchat/protocol/SendPersistentMessage.hpp + /root/swg-main/stationapi/src/stationchat/protocol/SendRoomMessage.hpp + /root/swg-main/stationapi/src/stationchat/protocol/SetApiVersion.hpp + /root/swg-main/stationapi/src/stationchat/protocol/SetAvatarAttributes.hpp + /root/swg-main/stationapi/src/stationchat/protocol/UpdatePersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + ../src/stationapi/Node.hpp + ../src/stationapi/NodeClient.hpp + ../src/stationapi/SQLite3.hpp + ../src/stationapi/Serialization.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatar.hpp + /root/swg-main/stationapi/src/stationchat/ChatAvatarService.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoom.hpp + /root/swg-main/stationapi/src/stationchat/ChatRoomService.hpp + /root/swg-main/stationapi/src/stationchat/GatewayClient.hpp + /root/swg-main/stationapi/src/stationchat/GatewayNode.cpp + /root/swg-main/stationapi/src/stationchat/GatewayNode.hpp + /root/swg-main/stationapi/src/stationchat/PersistentMessage.hpp + /root/swg-main/stationapi/src/stationchat/PersistentMessageService.hpp + /root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o + ../src/stationapi/SQLite3.hpp + ../src/stationapi/StringUtils.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/PersistentMessage.hpp + /root/swg-main/stationapi/src/stationchat/PersistentMessageService.cpp + /root/swg-main/stationapi/src/stationchat/PersistentMessageService.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + ../src/stationapi/Node.hpp + ../src/stationapi/NodeClient.hpp + ../src/stationapi/Serialization.hpp + ../src/stationapi/StringUtils.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/RegistrarClient.cpp + /root/swg-main/stationapi/src/stationchat/RegistrarClient.hpp + /root/swg-main/stationapi/src/stationchat/RegistrarNode.hpp + /root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp + /root/swg-main/stationapi/src/stationchat/protocol/RegistrarGetChatServer.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + ../src/stationapi/Node.hpp + ../src/stationapi/NodeClient.hpp + /root/swg-main/stationapi/src/stationchat/RegistrarClient.hpp + /root/swg-main/stationapi/src/stationchat/RegistrarNode.cpp + /root/swg-main/stationapi/src/stationchat/RegistrarNode.hpp + /root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + ../src/stationapi/Node.hpp + ../src/stationapi/NodeClient.hpp + ../src/stationapi/SQLite3.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/GatewayClient.hpp + /root/swg-main/stationapi/src/stationchat/GatewayNode.hpp + /root/swg-main/stationapi/src/stationchat/RegistrarClient.hpp + /root/swg-main/stationapi/src/stationchat/RegistrarNode.hpp + /root/swg-main/stationapi/src/stationchat/StationChatApp.cpp + /root/swg-main/stationapi/src/stationchat/StationChatApp.hpp + /root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + ../src/stationapi/Node.hpp + ../src/stationapi/NodeClient.hpp + ../src/stationapi/SQLite3.hpp + /root/swg-main/stationapi/src/stationchat/ChatEnums.hpp + /root/swg-main/stationapi/src/stationchat/GatewayClient.hpp + /root/swg-main/stationapi/src/stationchat/GatewayNode.hpp + /root/swg-main/stationapi/src/stationchat/RegistrarClient.hpp + /root/swg-main/stationapi/src/stationchat/RegistrarNode.hpp + /root/swg-main/stationapi/src/stationchat/StationChatApp.hpp + /root/swg-main/stationapi/src/stationchat/StationChatConfig.hpp + /root/swg-main/stationapi/src/stationchat/main.cpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o + ../externals/easyloggingpp/easylogging++.h + ../externals/udplibrary/UdpHandler.hpp + ../externals/udplibrary/UdpLibrary.hpp + ../externals/udplibrary/hashtable.hpp + ../externals/udplibrary/priority.hpp + ../src/stationapi/Node.hpp + ../src/stationapi/NodeClient.hpp + ../src/stationapi/SQLite3.hpp + ../src/stationapi/Serialization.hpp + ../src/stationapi/StringUtils.hpp + ../src/stationchat/ChatAvatar.hpp + ../src/stationchat/ChatAvatarService.hpp + ../src/stationchat/ChatEnums.hpp + ../src/stationchat/ChatRoom.hpp + ../src/stationchat/ChatRoomService.hpp + ../src/stationchat/GatewayClient.hpp + ../src/stationchat/GatewayNode.hpp + ../src/stationchat/PersistentMessage.hpp + ../src/stationchat/PersistentMessageService.hpp + ../src/stationchat/RegistrarClient.hpp + ../src/stationchat/RegistrarNode.hpp + ../src/stationchat/StationChatConfig.hpp + ../src/stationchat/protocol/AddBan.hpp + ../src/stationchat/protocol/AddFriend.hpp + ../src/stationchat/protocol/AddIgnore.hpp + ../src/stationchat/protocol/AddInvite.hpp + ../src/stationchat/protocol/AddModerator.hpp + ../src/stationchat/protocol/CreateRoom.hpp + ../src/stationchat/protocol/DestroyAvatar.hpp + ../src/stationchat/protocol/DestroyRoom.hpp + ../src/stationchat/protocol/EnterRoom.hpp + ../src/stationchat/protocol/FailoverReLoginAvatar.hpp + ../src/stationchat/protocol/FriendStatus.hpp + ../src/stationchat/protocol/GetAnyAvatar.hpp + ../src/stationchat/protocol/GetPersistentHeaders.hpp + ../src/stationchat/protocol/GetPersistentMessage.hpp + ../src/stationchat/protocol/GetRoom.hpp + ../src/stationchat/protocol/GetRoomSummaries.hpp + ../src/stationchat/protocol/IgnoreStatus.hpp + ../src/stationchat/protocol/KickAvatar.hpp + ../src/stationchat/protocol/LeaveRoom.hpp + ../src/stationchat/protocol/LoginAvatar.hpp + ../src/stationchat/protocol/LogoutAvatar.hpp + ../src/stationchat/protocol/RegistrarGetChatServer.hpp + ../src/stationchat/protocol/RemoveBan.hpp + ../src/stationchat/protocol/RemoveFriend.hpp + ../src/stationchat/protocol/RemoveIgnore.hpp + ../src/stationchat/protocol/RemoveInvite.hpp + ../src/stationchat/protocol/RemoveModerator.hpp + ../src/stationchat/protocol/SendInstantMessage.hpp + ../src/stationchat/protocol/SendPersistentMessage.hpp + ../src/stationchat/protocol/SendRoomMessage.hpp + ../src/stationchat/protocol/SetApiVersion.hpp + ../src/stationchat/protocol/SetAvatarAttributes.hpp + ../src/stationchat/protocol/UpdatePersistentMessage.hpp + /root/swg-main/stationapi/src/stationchat/protocol/AddBan.hpp + /root/swg-main/stationapi/src/stationchat/protocol/Protocol.cpp diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/depend.make b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/depend.make new file mode 100644 index 0000000..a8b1ebb --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/depend.make @@ -0,0 +1,241 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o: ../src/stationapi/Serialization.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o: ../src/stationchat/ChatAvatar.cpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o: ../src/stationchat/ChatAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o: ../src/stationchat/ChatAvatarService.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o: ../src/stationchat/ChatRoom.hpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../src/stationapi/SQLite3.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../src/stationapi/Serialization.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../src/stationapi/StringUtils.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../src/stationchat/ChatAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../src/stationchat/ChatAvatarService.cpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../src/stationchat/ChatAvatarService.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o: ../src/stationchat/ChatEnums.hpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o: ../src/stationchat/ChatEnums.cpp +src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o: ../src/stationchat/ChatEnums.hpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o: ../src/stationapi/Serialization.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o: ../src/stationchat/ChatAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o: ../src/stationchat/ChatRoom.cpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o: ../src/stationchat/ChatRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o: ../src/stationchat/ChatRoomService.hpp + +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationapi/SQLite3.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationapi/Serialization.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationapi/StreamUtils.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationapi/StringUtils.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationchat/ChatAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationchat/ChatAvatarService.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationchat/ChatRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationchat/ChatRoomService.cpp +src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o: ../src/stationchat/ChatRoomService.hpp + +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../externals/udplibrary/priority.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationapi/Node.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationapi/NodeClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationapi/SQLite3.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationapi/Serialization.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/ChatAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/ChatAvatarService.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/ChatRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/ChatRoomService.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/GatewayClient.cpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/GatewayClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/GatewayNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/Message.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/PersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/PersistentMessageService.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/StationChatConfig.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/AddBan.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/AddFriend.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/AddIgnore.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/AddInvite.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/AddModerator.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/CreateRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/DestroyRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/EnterRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/FailoverReLoginAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/FriendStatus.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/GetAnyAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/GetPersistentHeaders.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/GetPersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/GetRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/GetRoomSummaries.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/IgnoreStatus.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/KickAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/LeaveRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/LoginAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/LogoutAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/RemoveBan.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/RemoveFriend.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/RemoveIgnore.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/RemoveInvite.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/RemoveModerator.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/SendInstantMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/SendPersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/SendRoomMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/SetApiVersion.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/SetAvatarAttributes.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o: ../src/stationchat/protocol/UpdatePersistentMessage.hpp + +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../externals/udplibrary/priority.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationapi/Node.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationapi/NodeClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationapi/SQLite3.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationapi/Serialization.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/ChatAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/ChatAvatarService.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/ChatRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/ChatRoomService.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/GatewayClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/GatewayNode.cpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/GatewayNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/PersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/PersistentMessageService.hpp +src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o: ../src/stationchat/StationChatConfig.hpp + +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o: ../src/stationapi/SQLite3.hpp +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o: ../src/stationapi/StringUtils.hpp +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o: ../src/stationchat/PersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o: ../src/stationchat/PersistentMessageService.cpp +src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o: ../src/stationchat/PersistentMessageService.hpp + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../externals/udplibrary/priority.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationapi/Node.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationapi/NodeClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationapi/Serialization.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationapi/StringUtils.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationchat/RegistrarClient.cpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationchat/RegistrarClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationchat/RegistrarNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationchat/StationChatConfig.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o: ../src/stationchat/protocol/RegistrarGetChatServer.hpp + +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../externals/udplibrary/priority.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../src/stationapi/Node.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../src/stationapi/NodeClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../src/stationchat/RegistrarClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../src/stationchat/RegistrarNode.cpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../src/stationchat/RegistrarNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o: ../src/stationchat/StationChatConfig.hpp + +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../externals/udplibrary/priority.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationapi/Node.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationapi/NodeClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationapi/SQLite3.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/GatewayClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/GatewayNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/RegistrarClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/RegistrarNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/StationChatApp.cpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/StationChatApp.hpp +src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o: ../src/stationchat/StationChatConfig.hpp + +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../externals/udplibrary/priority.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationapi/Node.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationapi/NodeClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationapi/SQLite3.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/GatewayClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/GatewayNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/RegistrarClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/RegistrarNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/StationChatApp.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/StationChatConfig.hpp +src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o: ../src/stationchat/main.cpp + +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../externals/easyloggingpp/easylogging++.h +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../externals/udplibrary/UdpHandler.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../externals/udplibrary/UdpLibrary.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../externals/udplibrary/hashtable.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../externals/udplibrary/priority.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationapi/Node.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationapi/NodeClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationapi/SQLite3.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationapi/Serialization.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationapi/StringUtils.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/ChatAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/ChatAvatarService.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/ChatEnums.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/ChatRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/ChatRoomService.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/GatewayClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/GatewayNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/PersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/PersistentMessageService.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/RegistrarClient.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/RegistrarNode.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/StationChatConfig.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/AddBan.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/AddFriend.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/AddIgnore.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/AddInvite.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/AddModerator.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/CreateRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/DestroyAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/DestroyRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/EnterRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/FailoverReLoginAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/FriendStatus.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/GetAnyAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/GetPersistentHeaders.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/GetPersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/GetRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/GetRoomSummaries.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/IgnoreStatus.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/KickAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/LeaveRoom.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/LoginAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/LogoutAvatar.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/RegistrarGetChatServer.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/RemoveBan.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/RemoveFriend.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/RemoveIgnore.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/RemoveInvite.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/RemoveModerator.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/SendInstantMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/SendPersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/SendRoomMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/SetApiVersion.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/SetAvatarAttributes.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/UpdatePersistentMessage.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/AddBan.hpp +src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o: ../src/stationchat/protocol/Protocol.cpp + diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/flags.make b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/flags.make new file mode 100644 index 0000000..61bb27e --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -std=c++14 -Wno-write-strings + +CXX_DEFINES = -DBOOST_ALL_NO_LIB + +CXX_INCLUDES = -I/root/swg-main/stationapi/src/stationchat -I/root/swg-main/stationapi/src/stationapi -I/root/swg-main/stationapi/externals/easyloggingpp -I/root/swg-main/stationapi/externals/udplibrary + diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/link.txt b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/link.txt new file mode 100644 index 0000000..998fb5f --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/link.txt @@ -0,0 +1 @@ +/usr/bin/c++ -std=c++14 -Wno-write-strings CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o CMakeFiles/stationchat.dir/ChatAvatar.cpp.o CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o CMakeFiles/stationchat.dir/ChatEnums.cpp.o CMakeFiles/stationchat.dir/ChatRoom.cpp.o CMakeFiles/stationchat.dir/ChatRoomService.cpp.o CMakeFiles/stationchat.dir/GatewayClient.cpp.o CMakeFiles/stationchat.dir/GatewayNode.cpp.o CMakeFiles/stationchat.dir/main.cpp.o CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o CMakeFiles/stationchat.dir/RegistrarClient.cpp.o CMakeFiles/stationchat.dir/RegistrarNode.cpp.o CMakeFiles/stationchat.dir/StationChatApp.cpp.o -o ../../bin/stationchat -rdynamic ../stationapi/libstationapi.a ../../externals/udplibrary/libudplibrary.a -Wl,-Bstatic -lboost_program_options -Wl,-Bdynamic -lsqlite3 diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o new file mode 100644 index 0000000..1e9d3fe Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o differ diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/progress.make b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/progress.make new file mode 100644 index 0000000..0561625 --- /dev/null +++ b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/progress.make @@ -0,0 +1,15 @@ +CMAKE_PROGRESS_1 = 7 +CMAKE_PROGRESS_2 = 8 +CMAKE_PROGRESS_3 = 9 +CMAKE_PROGRESS_4 = 10 +CMAKE_PROGRESS_5 = 11 +CMAKE_PROGRESS_6 = 12 +CMAKE_PROGRESS_7 = 13 +CMAKE_PROGRESS_8 = 14 +CMAKE_PROGRESS_9 = 15 +CMAKE_PROGRESS_10 = 16 +CMAKE_PROGRESS_11 = 17 +CMAKE_PROGRESS_12 = 18 +CMAKE_PROGRESS_13 = 19 +CMAKE_PROGRESS_14 = 20 + diff --git a/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o new file mode 100644 index 0000000..2ef9112 Binary files /dev/null and b/stationapi/build/src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o differ diff --git a/stationapi/build/src/stationchat/Makefile b/stationapi/build/src/stationchat/Makefile new file mode 100644 index 0000000..a0a9fa8 --- /dev/null +++ b/stationapi/build/src/stationchat/Makefile @@ -0,0 +1,540 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles /root/swg-main/stationapi/build/src/stationchat/CMakeFiles/progress.marks + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationchat/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationchat/clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationchat/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationchat/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Convenience name for target. +src/stationchat/CMakeFiles/stationchat.dir/rule: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 src/stationchat/CMakeFiles/stationchat.dir/rule +.PHONY : src/stationchat/CMakeFiles/stationchat.dir/rule + +# Convenience name for target. +stationchat: src/stationchat/CMakeFiles/stationchat.dir/rule + +.PHONY : stationchat + +# fast build rule for target. +stationchat/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/build +.PHONY : stationchat/fast + +ChatAvatar.o: ChatAvatar.cpp.o + +.PHONY : ChatAvatar.o + +# target to build an object file +ChatAvatar.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.o +.PHONY : ChatAvatar.cpp.o + +ChatAvatar.i: ChatAvatar.cpp.i + +.PHONY : ChatAvatar.i + +# target to preprocess a source file +ChatAvatar.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.i +.PHONY : ChatAvatar.cpp.i + +ChatAvatar.s: ChatAvatar.cpp.s + +.PHONY : ChatAvatar.s + +# target to generate assembly for a file +ChatAvatar.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatAvatar.cpp.s +.PHONY : ChatAvatar.cpp.s + +ChatAvatarService.o: ChatAvatarService.cpp.o + +.PHONY : ChatAvatarService.o + +# target to build an object file +ChatAvatarService.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.o +.PHONY : ChatAvatarService.cpp.o + +ChatAvatarService.i: ChatAvatarService.cpp.i + +.PHONY : ChatAvatarService.i + +# target to preprocess a source file +ChatAvatarService.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.i +.PHONY : ChatAvatarService.cpp.i + +ChatAvatarService.s: ChatAvatarService.cpp.s + +.PHONY : ChatAvatarService.s + +# target to generate assembly for a file +ChatAvatarService.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatAvatarService.cpp.s +.PHONY : ChatAvatarService.cpp.s + +ChatEnums.o: ChatEnums.cpp.o + +.PHONY : ChatEnums.o + +# target to build an object file +ChatEnums.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.o +.PHONY : ChatEnums.cpp.o + +ChatEnums.i: ChatEnums.cpp.i + +.PHONY : ChatEnums.i + +# target to preprocess a source file +ChatEnums.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.i +.PHONY : ChatEnums.cpp.i + +ChatEnums.s: ChatEnums.cpp.s + +.PHONY : ChatEnums.s + +# target to generate assembly for a file +ChatEnums.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatEnums.cpp.s +.PHONY : ChatEnums.cpp.s + +ChatRoom.o: ChatRoom.cpp.o + +.PHONY : ChatRoom.o + +# target to build an object file +ChatRoom.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.o +.PHONY : ChatRoom.cpp.o + +ChatRoom.i: ChatRoom.cpp.i + +.PHONY : ChatRoom.i + +# target to preprocess a source file +ChatRoom.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.i +.PHONY : ChatRoom.cpp.i + +ChatRoom.s: ChatRoom.cpp.s + +.PHONY : ChatRoom.s + +# target to generate assembly for a file +ChatRoom.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatRoom.cpp.s +.PHONY : ChatRoom.cpp.s + +ChatRoomService.o: ChatRoomService.cpp.o + +.PHONY : ChatRoomService.o + +# target to build an object file +ChatRoomService.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.o +.PHONY : ChatRoomService.cpp.o + +ChatRoomService.i: ChatRoomService.cpp.i + +.PHONY : ChatRoomService.i + +# target to preprocess a source file +ChatRoomService.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.i +.PHONY : ChatRoomService.cpp.i + +ChatRoomService.s: ChatRoomService.cpp.s + +.PHONY : ChatRoomService.s + +# target to generate assembly for a file +ChatRoomService.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/ChatRoomService.cpp.s +.PHONY : ChatRoomService.cpp.s + +GatewayClient.o: GatewayClient.cpp.o + +.PHONY : GatewayClient.o + +# target to build an object file +GatewayClient.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.o +.PHONY : GatewayClient.cpp.o + +GatewayClient.i: GatewayClient.cpp.i + +.PHONY : GatewayClient.i + +# target to preprocess a source file +GatewayClient.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.i +.PHONY : GatewayClient.cpp.i + +GatewayClient.s: GatewayClient.cpp.s + +.PHONY : GatewayClient.s + +# target to generate assembly for a file +GatewayClient.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/GatewayClient.cpp.s +.PHONY : GatewayClient.cpp.s + +GatewayNode.o: GatewayNode.cpp.o + +.PHONY : GatewayNode.o + +# target to build an object file +GatewayNode.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.o +.PHONY : GatewayNode.cpp.o + +GatewayNode.i: GatewayNode.cpp.i + +.PHONY : GatewayNode.i + +# target to preprocess a source file +GatewayNode.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.i +.PHONY : GatewayNode.cpp.i + +GatewayNode.s: GatewayNode.cpp.s + +.PHONY : GatewayNode.s + +# target to generate assembly for a file +GatewayNode.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/GatewayNode.cpp.s +.PHONY : GatewayNode.cpp.s + +PersistentMessageService.o: PersistentMessageService.cpp.o + +.PHONY : PersistentMessageService.o + +# target to build an object file +PersistentMessageService.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.o +.PHONY : PersistentMessageService.cpp.o + +PersistentMessageService.i: PersistentMessageService.cpp.i + +.PHONY : PersistentMessageService.i + +# target to preprocess a source file +PersistentMessageService.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.i +.PHONY : PersistentMessageService.cpp.i + +PersistentMessageService.s: PersistentMessageService.cpp.s + +.PHONY : PersistentMessageService.s + +# target to generate assembly for a file +PersistentMessageService.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/PersistentMessageService.cpp.s +.PHONY : PersistentMessageService.cpp.s + +RegistrarClient.o: RegistrarClient.cpp.o + +.PHONY : RegistrarClient.o + +# target to build an object file +RegistrarClient.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.o +.PHONY : RegistrarClient.cpp.o + +RegistrarClient.i: RegistrarClient.cpp.i + +.PHONY : RegistrarClient.i + +# target to preprocess a source file +RegistrarClient.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.i +.PHONY : RegistrarClient.cpp.i + +RegistrarClient.s: RegistrarClient.cpp.s + +.PHONY : RegistrarClient.s + +# target to generate assembly for a file +RegistrarClient.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/RegistrarClient.cpp.s +.PHONY : RegistrarClient.cpp.s + +RegistrarNode.o: RegistrarNode.cpp.o + +.PHONY : RegistrarNode.o + +# target to build an object file +RegistrarNode.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.o +.PHONY : RegistrarNode.cpp.o + +RegistrarNode.i: RegistrarNode.cpp.i + +.PHONY : RegistrarNode.i + +# target to preprocess a source file +RegistrarNode.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.i +.PHONY : RegistrarNode.cpp.i + +RegistrarNode.s: RegistrarNode.cpp.s + +.PHONY : RegistrarNode.s + +# target to generate assembly for a file +RegistrarNode.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/RegistrarNode.cpp.s +.PHONY : RegistrarNode.cpp.s + +StationChatApp.o: StationChatApp.cpp.o + +.PHONY : StationChatApp.o + +# target to build an object file +StationChatApp.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.o +.PHONY : StationChatApp.cpp.o + +StationChatApp.i: StationChatApp.cpp.i + +.PHONY : StationChatApp.i + +# target to preprocess a source file +StationChatApp.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.i +.PHONY : StationChatApp.cpp.i + +StationChatApp.s: StationChatApp.cpp.s + +.PHONY : StationChatApp.s + +# target to generate assembly for a file +StationChatApp.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/StationChatApp.cpp.s +.PHONY : StationChatApp.cpp.s + +main.o: main.cpp.o + +.PHONY : main.o + +# target to build an object file +main.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/main.cpp.o +.PHONY : main.cpp.o + +main.i: main.cpp.i + +.PHONY : main.i + +# target to preprocess a source file +main.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/main.cpp.i +.PHONY : main.cpp.i + +main.s: main.cpp.s + +.PHONY : main.s + +# target to generate assembly for a file +main.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/main.cpp.s +.PHONY : main.cpp.s + +protocol/Protocol.o: protocol/Protocol.cpp.o + +.PHONY : protocol/Protocol.o + +# target to build an object file +protocol/Protocol.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.o +.PHONY : protocol/Protocol.cpp.o + +protocol/Protocol.i: protocol/Protocol.cpp.i + +.PHONY : protocol/Protocol.i + +# target to preprocess a source file +protocol/Protocol.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.i +.PHONY : protocol/Protocol.cpp.i + +protocol/Protocol.s: protocol/Protocol.cpp.s + +.PHONY : protocol/Protocol.s + +# target to generate assembly for a file +protocol/Protocol.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f src/stationchat/CMakeFiles/stationchat.dir/build.make src/stationchat/CMakeFiles/stationchat.dir/protocol/Protocol.cpp.s +.PHONY : protocol/Protocol.cpp.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... stationchat" + @echo "... edit_cache" + @echo "... ChatAvatar.o" + @echo "... ChatAvatar.i" + @echo "... ChatAvatar.s" + @echo "... ChatAvatarService.o" + @echo "... ChatAvatarService.i" + @echo "... ChatAvatarService.s" + @echo "... ChatEnums.o" + @echo "... ChatEnums.i" + @echo "... ChatEnums.s" + @echo "... ChatRoom.o" + @echo "... ChatRoom.i" + @echo "... ChatRoom.s" + @echo "... ChatRoomService.o" + @echo "... ChatRoomService.i" + @echo "... ChatRoomService.s" + @echo "... GatewayClient.o" + @echo "... GatewayClient.i" + @echo "... GatewayClient.s" + @echo "... GatewayNode.o" + @echo "... GatewayNode.i" + @echo "... GatewayNode.s" + @echo "... PersistentMessageService.o" + @echo "... PersistentMessageService.i" + @echo "... PersistentMessageService.s" + @echo "... RegistrarClient.o" + @echo "... RegistrarClient.i" + @echo "... RegistrarClient.s" + @echo "... RegistrarNode.o" + @echo "... RegistrarNode.i" + @echo "... RegistrarNode.s" + @echo "... StationChatApp.o" + @echo "... StationChatApp.i" + @echo "... StationChatApp.s" + @echo "... main.o" + @echo "... main.i" + @echo "... main.s" + @echo "... protocol/Protocol.o" + @echo "... protocol/Protocol.i" + @echo "... protocol/Protocol.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/stationapi/build/src/stationchat/cmake_install.cmake b/stationapi/build/src/stationchat/cmake_install.cmake new file mode 100644 index 0000000..9a7ec77 --- /dev/null +++ b/stationapi/build/src/stationchat/cmake_install.cmake @@ -0,0 +1,34 @@ +# Install script for directory: /root/swg-main/stationapi/src/stationchat + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + diff --git a/stationapi/build/tests/CMakeFiles/CMakeDirectoryInformation.cmake b/stationapi/build/tests/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..a19da41 --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/root/swg-main/stationapi") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/root/swg-main/stationapi/build") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/stationapi/build/tests/CMakeFiles/progress.marks b/stationapi/build/tests/CMakeFiles/progress.marks new file mode 100644 index 0000000..1e8b314 --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/progress.marks @@ -0,0 +1 @@ +6 diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/CXX.includecache b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/CXX.includecache new file mode 100644 index 0000000..0a3678a --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/CXX.includecache @@ -0,0 +1,244 @@ +#IncludeRegexLine: ^[ ]*#[ ]*(include|import)[ ]*[<"]([^">]+)([">]) + +#IncludeRegexScan: ^.*$ + +#IncludeRegexComplain: ^$ + +#IncludeRegexTransform: + +../externals/catch/catch.hpp +sstream +- +stdexcept +- +algorithm +- +ostream +- +string +- +memory +- +vector +- +stdlib.h +- +vector +- +string +- +cstddef +- +sstream +- +iomanip +- +limits +- +vector +- +cstddef +- +Foundation/Foundation.h +- +tuple +- +type_traits +- +string +- +string +- +string +- +cstddef +- +stdint.h +- +string +- +iterator +- +vector +- +string +- +stdlib.h +- +string +- +vector +- +string +- +cmath +- +limits +- +string +- +string +- +set +- +objc/runtime.h +- +string +- +string +- +vector +- +iostream +- +string +- +vector +- +streambuf +- +streambuf +- +ostream +- +fstream +- +memory +- +vector +- +string +- +iostream +- +ctime +- +string +- +vector +- +sstream +- +algorithm +- +map +- +stdexcept +- +memory +- +fstream +- +string +- +vector +- +sstream +- +string +- +ostream +- +map +- +assert.h +- +limits +- +algorithm +- +map +- +string +- +assert.h +- +vector +- +signal.h +- +set +- +string +- +fstream +- +stdlib.h +- +limits +- +vector +- +set +- +sstream +- +iostream +- +algorithm +- +random +- +map +- +Foundation/Foundation.h +../externals/catch/Foundation/Foundation.h +ostream +- +stdexcept +- +cstdio +- +iostream +- +AfxWin.h +- +windows.h +- +unistd.h +- +vector +- +string +- +map +- +windows.h +- +sys/time.h +- +iostream +- +assert.h +- +stdbool.h +- +sys/types.h +- +unistd.h +- +sys/sysctl.h +- +map +- +map +- +iostream +- +cstring +- +sstream +- +string +- +vector +- +iomanip +- +assert.h +- + +/root/swg-main/stationapi/tests/main.cpp +catch.hpp +/root/swg-main/stationapi/tests/catch.hpp + diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/DependInfo.cmake b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/DependInfo.cmake new file mode 100644 index 0000000..5e862a3 --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/DependInfo.cmake @@ -0,0 +1,28 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/root/swg-main/stationapi/tests/main.cpp" "/root/swg-main/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/main.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BOOST_ALL_NO_LIB" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "../externals/catch" + "../src" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + "/root/swg-main/stationapi/build/src/stationapi/CMakeFiles/stationapi.dir/DependInfo.cmake" + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/build.make b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/build.make new file mode 100644 index 0000000..5cf84b7 --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/build.make @@ -0,0 +1,114 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +# Include any dependencies generated for this target. +include tests/CMakeFiles/stationapi_tests.dir/depend.make + +# Include the progress variables for this target. +include tests/CMakeFiles/stationapi_tests.dir/progress.make + +# Include the compile flags for this target's objects. +include tests/CMakeFiles/stationapi_tests.dir/flags.make + +tests/CMakeFiles/stationapi_tests.dir/main.cpp.o: tests/CMakeFiles/stationapi_tests.dir/flags.make +tests/CMakeFiles/stationapi_tests.dir/main.cpp.o: ../tests/main.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object tests/CMakeFiles/stationapi_tests.dir/main.cpp.o" + cd /root/swg-main/stationapi/build/tests && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/stationapi_tests.dir/main.cpp.o -c /root/swg-main/stationapi/tests/main.cpp + +tests/CMakeFiles/stationapi_tests.dir/main.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/stationapi_tests.dir/main.cpp.i" + cd /root/swg-main/stationapi/build/tests && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /root/swg-main/stationapi/tests/main.cpp > CMakeFiles/stationapi_tests.dir/main.cpp.i + +tests/CMakeFiles/stationapi_tests.dir/main.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/stationapi_tests.dir/main.cpp.s" + cd /root/swg-main/stationapi/build/tests && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /root/swg-main/stationapi/tests/main.cpp -o CMakeFiles/stationapi_tests.dir/main.cpp.s + +tests/CMakeFiles/stationapi_tests.dir/main.cpp.o.requires: + +.PHONY : tests/CMakeFiles/stationapi_tests.dir/main.cpp.o.requires + +tests/CMakeFiles/stationapi_tests.dir/main.cpp.o.provides: tests/CMakeFiles/stationapi_tests.dir/main.cpp.o.requires + $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/main.cpp.o.provides.build +.PHONY : tests/CMakeFiles/stationapi_tests.dir/main.cpp.o.provides + +tests/CMakeFiles/stationapi_tests.dir/main.cpp.o.provides.build: tests/CMakeFiles/stationapi_tests.dir/main.cpp.o + + +# Object files for target stationapi_tests +stationapi_tests_OBJECTS = \ +"CMakeFiles/stationapi_tests.dir/main.cpp.o" + +# External object files for target stationapi_tests +stationapi_tests_EXTERNAL_OBJECTS = + +bin/stationapi_tests: tests/CMakeFiles/stationapi_tests.dir/main.cpp.o +bin/stationapi_tests: tests/CMakeFiles/stationapi_tests.dir/build.make +bin/stationapi_tests: src/stationapi/libstationapi.a +bin/stationapi_tests: tests/CMakeFiles/stationapi_tests.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/root/swg-main/stationapi/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX executable ../bin/stationapi_tests" + cd /root/swg-main/stationapi/build/tests && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/stationapi_tests.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +tests/CMakeFiles/stationapi_tests.dir/build: bin/stationapi_tests + +.PHONY : tests/CMakeFiles/stationapi_tests.dir/build + +tests/CMakeFiles/stationapi_tests.dir/requires: tests/CMakeFiles/stationapi_tests.dir/main.cpp.o.requires + +.PHONY : tests/CMakeFiles/stationapi_tests.dir/requires + +tests/CMakeFiles/stationapi_tests.dir/clean: + cd /root/swg-main/stationapi/build/tests && $(CMAKE_COMMAND) -P CMakeFiles/stationapi_tests.dir/cmake_clean.cmake +.PHONY : tests/CMakeFiles/stationapi_tests.dir/clean + +tests/CMakeFiles/stationapi_tests.dir/depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /root/swg-main/stationapi /root/swg-main/stationapi/tests /root/swg-main/stationapi/build /root/swg-main/stationapi/build/tests /root/swg-main/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : tests/CMakeFiles/stationapi_tests.dir/depend + diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/cmake_clean.cmake b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/cmake_clean.cmake new file mode 100644 index 0000000..bc742dd --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/cmake_clean.cmake @@ -0,0 +1,10 @@ +file(REMOVE_RECURSE + "CMakeFiles/stationapi_tests.dir/main.cpp.o" + "../bin/stationapi_tests.pdb" + "../bin/stationapi_tests" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/stationapi_tests.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/depend.internal b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/depend.internal new file mode 100644 index 0000000..6bf7109 --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/depend.internal @@ -0,0 +1,6 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +tests/CMakeFiles/stationapi_tests.dir/main.cpp.o + ../externals/catch/catch.hpp + /root/swg-main/stationapi/tests/main.cpp diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/depend.make b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/depend.make new file mode 100644 index 0000000..3e02bce --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/depend.make @@ -0,0 +1,6 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +tests/CMakeFiles/stationapi_tests.dir/main.cpp.o: ../externals/catch/catch.hpp +tests/CMakeFiles/stationapi_tests.dir/main.cpp.o: ../tests/main.cpp + diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/flags.make b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/flags.make new file mode 100644 index 0000000..c1fdf8d --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -std=c++14 -Wno-write-strings + +CXX_DEFINES = -DBOOST_ALL_NO_LIB + +CXX_INCLUDES = -I/root/swg-main/stationapi/externals/catch -I/root/swg-main/stationapi/src + diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/link.txt b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/link.txt new file mode 100644 index 0000000..bbd9e37 --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/link.txt @@ -0,0 +1 @@ +/usr/bin/c++ -std=c++14 -Wno-write-strings CMakeFiles/stationapi_tests.dir/main.cpp.o -o ../bin/stationapi_tests -rdynamic ../src/stationapi/libstationapi.a diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/main.cpp.o b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/main.cpp.o new file mode 100644 index 0000000..1c017ea Binary files /dev/null and b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/main.cpp.o differ diff --git a/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/progress.make b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/progress.make new file mode 100644 index 0000000..3a86673 --- /dev/null +++ b/stationapi/build/tests/CMakeFiles/stationapi_tests.dir/progress.make @@ -0,0 +1,3 @@ +CMAKE_PROGRESS_1 = 5 +CMAKE_PROGRESS_2 = 6 + diff --git a/stationapi/build/tests/Makefile b/stationapi/build/tests/Makefile new file mode 100644 index 0000000..283a6fd --- /dev/null +++ b/stationapi/build/tests/Makefile @@ -0,0 +1,180 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.7 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /root/swg-main/stationapi + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /root/swg-main/stationapi/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles /root/swg-main/stationapi/build/tests/CMakeFiles/progress.marks + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 tests/all + $(CMAKE_COMMAND) -E cmake_progress_start /root/swg-main/stationapi/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 tests/clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 tests/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 tests/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Convenience name for target. +tests/CMakeFiles/stationapi_tests.dir/rule: + cd /root/swg-main/stationapi/build && $(MAKE) -f CMakeFiles/Makefile2 tests/CMakeFiles/stationapi_tests.dir/rule +.PHONY : tests/CMakeFiles/stationapi_tests.dir/rule + +# Convenience name for target. +stationapi_tests: tests/CMakeFiles/stationapi_tests.dir/rule + +.PHONY : stationapi_tests + +# fast build rule for target. +stationapi_tests/fast: + cd /root/swg-main/stationapi/build && $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/build +.PHONY : stationapi_tests/fast + +main.o: main.cpp.o + +.PHONY : main.o + +# target to build an object file +main.cpp.o: + cd /root/swg-main/stationapi/build && $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/main.cpp.o +.PHONY : main.cpp.o + +main.i: main.cpp.i + +.PHONY : main.i + +# target to preprocess a source file +main.cpp.i: + cd /root/swg-main/stationapi/build && $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/main.cpp.i +.PHONY : main.cpp.i + +main.s: main.cpp.s + +.PHONY : main.s + +# target to generate assembly for a file +main.cpp.s: + cd /root/swg-main/stationapi/build && $(MAKE) -f tests/CMakeFiles/stationapi_tests.dir/build.make tests/CMakeFiles/stationapi_tests.dir/main.cpp.s +.PHONY : main.cpp.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... stationapi_tests" + @echo "... edit_cache" + @echo "... main.o" + @echo "... main.i" + @echo "... main.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /root/swg-main/stationapi/build && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/stationapi/build/tests/cmake_install.cmake b/stationapi/build/tests/cmake_install.cmake new file mode 100644 index 0000000..893cc47 --- /dev/null +++ b/stationapi/build/tests/cmake_install.cmake @@ -0,0 +1,34 @@ +# Install script for directory: /root/swg-main/stationapi/tests + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + diff --git a/stationapi/cmake/FindSQLite3.cmake b/stationapi/cmake/FindSQLite3.cmake new file mode 100755 index 0000000..fdd93f4 --- /dev/null +++ b/stationapi/cmake/FindSQLite3.cmake @@ -0,0 +1,25 @@ + +find_path(SQLite3_INCLUDE_DIR sqlite3.h + HINTS + $ENV{SQLite3_ROOT} + PATH_SUFFIXES include + PATHS + ${SQLite3_ROOT} + ${SQLite3_INCLUDEDIR} +) + +find_library(SQLite3_LIBRARY + NAMES sqlite3 + PATH_SUFFIXES lib + HINTS + $ENV{SQLite3_ROOT} + ${SQLite3_ROOT} + ${SQLite3_LIBRARYDIR} +) + +# handle the QUIETLY and REQUIRED arguments and set OPENAL_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SQLite3 DEFAULT_MSG SQLite3_LIBRARY SQLite3_INCLUDE_DIR) + +mark_as_advanced(SQLite3_ROOT SQLite3_INCLUDE_DIR SQLite3_LIBRARY) diff --git a/stationapi/cmake/GroupSourceByFilesystem.cmake b/stationapi/cmake/GroupSourceByFilesystem.cmake new file mode 100755 index 0000000..5fb0d75 --- /dev/null +++ b/stationapi/cmake/GroupSourceByFilesystem.cmake @@ -0,0 +1,16 @@ +function(GroupSourceByFilesystem SOURCES) + foreach(FILE ${SOURCES}) + get_filename_component(PARENT_DIR "${FILE}" DIRECTORY) + string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" GROUP "${PARENT_DIR}") + string(REPLACE "/" "\\" GROUP "${GROUP}") + + # Group into "Source Files" and "Header Files" + if ("${FILE}" MATCHES ".*\\.cpp") + set(GROUP "Source Files\\${GROUP}") + elseif("${FILE}" MATCHES ".*\\.h") + set(GROUP "Header Files\\${GROUP}") + endif() + + source_group("${GROUP}" FILES "${FILE}") + endforeach() +endfunction(GroupSourceByFilesystem) \ No newline at end of file diff --git a/stationapi/cmake/ModernCpp.cmake b/stationapi/cmake/ModernCpp.cmake new file mode 100755 index 0000000..b3499ef --- /dev/null +++ b/stationapi/cmake/ModernCpp.cmake @@ -0,0 +1,10 @@ + +if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") + if (APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -stdlib=libc++") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++") + endif () +elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") +endif () diff --git a/stationapi/externals/CMakeLists.txt b/stationapi/externals/CMakeLists.txt new file mode 100755 index 0000000..a4477d6 --- /dev/null +++ b/stationapi/externals/CMakeLists.txt @@ -0,0 +1,4 @@ + +if (HAVE_UDPLIBRARY) + add_subdirectory(udplibrary) +endif() diff --git a/stationapi/externals/catch/catch.hpp b/stationapi/externals/catch/catch.hpp new file mode 100755 index 0000000..c8df33d --- /dev/null +++ b/stationapi/externals/catch/catch.hpp @@ -0,0 +1,10593 @@ +/* + * Catch v1.5.6 + * Generated: 2016-06-09 19:20:41.460328 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + +#define TWOBLUECUBES_CATCH_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang system_header +#elif defined __GNUC__ +#pragma GCC system_header +#endif + +// #included from: internal/catch_suppress_warnings.h + +#ifdef __clang__ +#ifdef __ICC // icpc defines the __clang__ macro +#pragma warning(push) +#pragma warning(disable : 161 1682) +#else // __ICC +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc99-extensions" +#pragma clang diagnostic ignored "-Wunused-variable" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif +#elif defined __GNUC__ +#pragma GCC diagnostic ignored "-Wvariadic-macros" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpadded" +#endif +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +#define CATCH_IMPL +#endif + +#ifdef CATCH_IMPL +#ifndef CLARA_CONFIG_MAIN +#define CLARA_CONFIG_MAIN_NOT_DEFINED +#define CLARA_CONFIG_MAIN +#endif +#endif + +// #included from: internal/catch_notimplemented_exception.h +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED + +// #included from: catch_common.h +#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED + +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE(name, line) INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) +#ifdef CATCH_CONFIG_COUNTER +#define INTERNAL_CATCH_UNIQUE_NAME(name) INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __COUNTER__) +#else +#define INTERNAL_CATCH_UNIQUE_NAME(name) INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __LINE__) +#endif + +#define INTERNAL_CATCH_STRINGIFY2(expr) #expr +#define INTERNAL_CATCH_STRINGIFY(expr) INTERNAL_CATCH_STRINGIFY2(expr) + +#include +#include +#include + +// #included from: catch_compiler_capabilities.h +#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? +// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported +// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? +// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? +// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 + +#ifdef __cplusplus + +#if __cplusplus >= 201103L +#define CATCH_CPP11_OR_GREATER +#endif + +#if __cplusplus >= 201402L +#define CATCH_CPP14_OR_GREATER +#endif + +#endif + +#ifdef __clang__ + +#if __has_feature(cxx_nullptr) +#define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#if __has_feature(cxx_noexcept) +#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#if defined(CATCH_CPP11_OR_GREATER) +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma("clang diagnostic ignored \"-Wparentheses\"") +#endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Borland +#ifdef __BORLANDC__ + +#endif // __BORLANDC__ + +//////////////////////////////////////////////////////////////////////////////// +// EDG +#ifdef __EDG_VERSION__ + +#endif // __EDG_VERSION__ + +//////////////////////////////////////////////////////////////////////////////// +// Digital Mars +#ifdef __DMC__ + +#endif // __DMC__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma("GCC diagnostic ignored \"-Wparentheses\"") +#endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +#define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +#define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900) // (VC++ 13 (VS2015)) +#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// + +// Use variadic macros if the compiler supports them +#if (defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || (defined __WAVE__ && __WAVE_HAS_VARIADICS) || (defined __GNUC__ && __GNUC__ >= 3) || (!defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L) + +#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS + +#endif + +// Use __COUNTER__ if the compiler supports it +#if (defined _MSC_VER && _MSC_VER >= 1300) || (defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3) || (defined __clang__ && __clang_major__ >= 3) + +#define CATCH_INTERNAL_CONFIG_COUNTER + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(CATCH_CPP11_OR_GREATER) + +#if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) +#define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +#define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +#endif + +#ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE +#define CATCH_INTERNAL_CONFIG_CPP11_TUPLE +#endif + +#ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) +#define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) +#define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE +#endif +#if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +#define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) +#define CATCH_CONFIG_CPP11_NULLPTR +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) +#define CATCH_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) +#define CATCH_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) +#define CATCH_CONFIG_CPP11_IS_ENUM +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) +#define CATCH_CONFIG_CPP11_TUPLE +#endif +#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) +#define CATCH_CONFIG_VARIADIC_MACROS +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) +#define CATCH_CONFIG_CPP11_LONG_LONG +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) +#define CATCH_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) +#define CATCH_CONFIG_CPP11_UNIQUE_PTR +#endif +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +#define CATCH_CONFIG_COUNTER +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif + +// noexcept support: +#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) +#define CATCH_NOEXCEPT noexcept +#define CATCH_NOEXCEPT_IS(x) noexcept(x) +#else +#define CATCH_NOEXCEPT throw() +#define CATCH_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CATCH_CONFIG_CPP11_NULLPTR +#define CATCH_NULL nullptr +#else +#define CATCH_NULL NULL +#endif + +// override support +#ifdef CATCH_CONFIG_CPP11_OVERRIDE +#define CATCH_OVERRIDE override +#else +#define CATCH_OVERRIDE +#endif + +// unique_ptr support +#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR +#define CATCH_AUTO_PTR(T) std::unique_ptr +#else +#define CATCH_AUTO_PTR(T) std::auto_ptr +#endif + +namespace Catch { + +struct IConfig; + +struct CaseSensitive { + enum Choice { + Yes, + No + }; +}; + +class NonCopyable { +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + NonCopyable(NonCopyable const&) = delete; + NonCopyable(NonCopyable&&) = delete; + NonCopyable& operator=(NonCopyable const&) = delete; + NonCopyable& operator=(NonCopyable&&) = delete; +#else + NonCopyable(NonCopyable const& info); + NonCopyable& operator=(NonCopyable const&); +#endif + +protected: + NonCopyable() {} + virtual ~NonCopyable(); +}; + +class SafeBool { +public: + typedef void (SafeBool::*type)() const; + + static type makeSafe(bool value) { + return value ? &SafeBool::trueValue : 0; + } + +private: + void trueValue() const {} +}; + +template +inline void deleteAll(ContainerT& container) { + typename ContainerT::const_iterator it = container.begin(); + typename ContainerT::const_iterator itEnd = container.end(); + for (; it != itEnd; ++it) + delete *it; +} +template +inline void deleteAllValues(AssociativeContainerT& container) { + typename AssociativeContainerT::const_iterator it = container.begin(); + typename AssociativeContainerT::const_iterator itEnd = container.end(); + for (; it != itEnd; ++it) + delete it->second; +} + +bool startsWith(std::string const& s, std::string const& prefix); +bool endsWith(std::string const& s, std::string const& suffix); +bool contains(std::string const& s, std::string const& infix); +void toLowerInPlace(std::string& s); +std::string toLower(std::string const& s); +std::string trim(std::string const& str); +bool replaceInPlace(std::string& str, std::string const& replaceThis, std::string const& withThis); + +struct pluralise { + pluralise(std::size_t count, std::string const& label); + + friend std::ostream& operator<<(std::ostream& os, pluralise const& pluraliser); + + std::size_t m_count; + std::string m_label; +}; + +struct SourceLineInfo { + + SourceLineInfo(); + SourceLineInfo(char const* _file, std::size_t _line); + SourceLineInfo(SourceLineInfo const& other); +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo(SourceLineInfo&&) = default; + SourceLineInfo& operator=(SourceLineInfo const&) = default; + SourceLineInfo& operator=(SourceLineInfo&&) = default; +#endif + bool empty() const; + bool operator==(SourceLineInfo const& other) const; + bool operator<(SourceLineInfo const& other) const; + + std::string file; + std::size_t line; +}; + +std::ostream& operator<<(std::ostream& os, SourceLineInfo const& info); + +// This is just here to avoid compiler warnings with macro constants and boolean literals +inline bool isTrue(bool value) { return value; } +inline bool alwaysTrue() { return true; } +inline bool alwaysFalse() { return false; } + +void throwLogicError(std::string const& message, SourceLineInfo const& locationInfo); + +void seedRng(IConfig const& config); +unsigned int rngSeed(); + +// Use this in variadic streaming macros to allow +// >> +StreamEndStop +// as well as +// >> stuff +StreamEndStop +struct StreamEndStop { + std::string operator+() { + return std::string(); + } +}; +template +T const& operator+(T const& value, StreamEndStop) { + return value; +} +} + +#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo(__FILE__, static_cast(__LINE__)) +#define CATCH_INTERNAL_ERROR(msg) ::Catch::throwLogicError(msg, CATCH_INTERNAL_LINEINFO); + +#include + +namespace Catch { + +class NotImplementedException : public std::exception { +public: + NotImplementedException(SourceLineInfo const& lineInfo); + NotImplementedException(NotImplementedException const&) {} + + virtual ~NotImplementedException() CATCH_NOEXCEPT {} + + virtual const char* what() const CATCH_NOEXCEPT; + +private: + std::string m_what; + SourceLineInfo m_lineInfo; +}; + +} // end namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException(CATCH_INTERNAL_LINEINFO) + +// #included from: internal/catch_context.h +#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED + +// #included from: catch_interfaces_generators.h +#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED + +#include + +namespace Catch { + +struct IGeneratorInfo { + virtual ~IGeneratorInfo(); + virtual bool moveNext() = 0; + virtual std::size_t getCurrentIndex() const = 0; +}; + +struct IGeneratorsForTest { + virtual ~IGeneratorsForTest(); + + virtual IGeneratorInfo& getGeneratorInfo(std::string const& fileInfo, std::size_t size) = 0; + virtual bool moveNext() = 0; +}; + +IGeneratorsForTest* createGeneratorsForTest(); + +} // end namespace Catch + +// #included from: catch_ptr.hpp +#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + +// An intrusive reference counting smart pointer. +// T must implement addRef() and release() methods +// typically implementing the IShared interface +template +class Ptr { +public: + Ptr() + : m_p(CATCH_NULL) {} + Ptr(T* p) + : m_p(p) { + if (m_p) + m_p->addRef(); + } + Ptr(Ptr const& other) + : m_p(other.m_p) { + if (m_p) + m_p->addRef(); + } + ~Ptr() { + if (m_p) + m_p->release(); + } + void reset() { + if (m_p) + m_p->release(); + m_p = CATCH_NULL; + } + Ptr& operator=(T* p) { + Ptr temp(p); + swap(temp); + return *this; + } + Ptr& operator=(Ptr const& other) { + Ptr temp(other); + swap(temp); + return *this; + } + void swap(Ptr& other) { std::swap(m_p, other.m_p); } + T* get() const { return m_p; } + T& operator*() const { return *m_p; } + T* operator->() const { return m_p; } + bool operator!() const { return m_p == CATCH_NULL; } + operator SafeBool::type() const { return SafeBool::makeSafe(m_p != CATCH_NULL); } + +private: + T* m_p; +}; + +struct IShared : NonCopyable { + virtual ~IShared(); + virtual void addRef() const = 0; + virtual void release() const = 0; +}; + +template +struct SharedImpl : T { + + SharedImpl() + : m_rc(0) {} + + virtual void addRef() const { + ++m_rc; + } + virtual void release() const { + if (--m_rc == 0) + delete this; + } + + mutable unsigned int m_rc; +}; + +} // end namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include +#include +#include + +namespace Catch { + +class TestCase; +class Stream; +struct IResultCapture; +struct IRunner; +struct IGeneratorsForTest; +struct IConfig; + +struct IContext { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual size_t getGeneratorIndex(std::string const& fileInfo, size_t totalSize) = 0; + virtual bool advanceGeneratorsForCurrentTest() = 0; + virtual Ptr getConfig() const = 0; +}; + +struct IMutableContext : IContext { + virtual ~IMutableContext(); + virtual void setResultCapture(IResultCapture* resultCapture) = 0; + virtual void setRunner(IRunner* runner) = 0; + virtual void setConfig(Ptr const& config) = 0; +}; + +IContext& getCurrentContext(); +IMutableContext& getCurrentMutableContext(); +void cleanUpContext(); +Stream createStream(std::string const& streamName); +} + +// #included from: internal/catch_test_registry.hpp +#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED + +// #included from: catch_interfaces_testcase.h +#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED + +#include + +namespace Catch { + +class TestSpec; + +struct ITestCase : IShared { + virtual void invoke() const = 0; + +protected: + virtual ~ITestCase(); +}; + +class TestCase; +struct IConfig; + +struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted(IConfig const& config) const = 0; +}; + +bool matchTest(TestCase const& testCase, TestSpec const& testSpec, IConfig const& config); +std::vector filterTests(std::vector const& testCases, TestSpec const& testSpec, IConfig const& config); +std::vector const& getAllTestCasesSorted(IConfig const& config); +} + +namespace Catch { + +template +class MethodTestCase : public SharedImpl { + +public: + MethodTestCase(void (C::*method)()) + : m_method(method) {} + + virtual void invoke() const { + C obj; + (obj.*m_method)(); + } + +private: + virtual ~MethodTestCase() {} + + void (C::*m_method)(); +}; + +typedef void (*TestFunction)(); + +struct NameAndDesc { + NameAndDesc(const char* _name = "", const char* _description = "") + : name(_name) + , description(_description) {} + + const char* name; + const char* description; +}; + +void registerTestCase(ITestCase* testCase, + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo); + +struct AutoReg { + + AutoReg(TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc); + + template + AutoReg(void (C::*method)(), + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo) { + + registerTestCase(new MethodTestCase(method), + className, + nameAndDesc, + lineInfo); + } + + ~AutoReg(); + +private: + AutoReg(AutoReg const&); + void operator=(AutoReg const&); +}; + +void registerTestCaseFunction(TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc); + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TESTCASE2(TestName, ...) \ + static void TestName(); \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(__VA_ARGS__)); \ + } \ + static void TestName() +#define INTERNAL_CATCH_TESTCASE(...) \ + INTERNAL_CATCH_TESTCASE2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), __VA_ARGS__) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, ...) \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc(__VA_ARGS__), CATCH_INTERNAL_LINEINFO); \ + } + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST_CASE_METHOD2(TestName, ClassName, ...) \ + namespace { \ + struct TestName : ClassName { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&TestName::test, #ClassName, Catch::NameAndDesc(__VA_ARGS__), CATCH_INTERNAL_LINEINFO); \ + } \ + void TestName::test() +#define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, ...) \ + INTERNAL_CATCH_TEST_CASE_METHOD2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), ClassName, __VA_ARGS__) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_REGISTER_TESTCASE(Function, ...) \ + Catch::AutoReg(Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(__VA_ARGS__)); + +#else +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TESTCASE2(TestName, Name, Desc) \ + static void TestName(); \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(Name, Desc)); \ + } \ + static void TestName() +#define INTERNAL_CATCH_TESTCASE(Name, Desc) \ + INTERNAL_CATCH_TESTCASE2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), Name, Desc) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, Name, Desc) \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc(Name, Desc), CATCH_INTERNAL_LINEINFO); \ + } + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST_CASE_METHOD2(TestCaseName, ClassName, TestName, Desc) \ + namespace { \ + struct TestCaseName : ClassName { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&TestCaseName::test, #ClassName, Catch::NameAndDesc(TestName, Desc), CATCH_INTERNAL_LINEINFO); \ + } \ + void TestCaseName::test() +#define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, TestName, Desc) \ + INTERNAL_CATCH_TEST_CASE_METHOD2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), ClassName, TestName, Desc) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_REGISTER_TESTCASE(Function, Name, Desc) \ + Catch::AutoReg(Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(Name, Desc)); +#endif + +// #included from: internal/catch_capture.hpp +#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED + +// #included from: catch_result_builder.h +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED + +// #included from: catch_result_type.h +#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED + +namespace Catch { + +// ResultWas::OfType enum +struct ResultWas { + enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; +}; + +inline bool isOk(ResultWas::OfType resultType) { + return (resultType & ResultWas::FailureBit) == 0; +} +inline bool isJustInfo(int flags) { + return flags == ResultWas::Info; +} + +// ResultDisposition::Flags enum +struct ResultDisposition { + enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; +}; + +inline ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs) { + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +inline bool shouldContinueOnFailure(int flags) { return (flags & ResultDisposition::ContinueOnFailure) != 0; } +inline bool isFalseTest(int flags) { return (flags & ResultDisposition::FalseTest) != 0; } +inline bool shouldSuppressFailure(int flags) { return (flags & ResultDisposition::SuppressFail) != 0; } + +} // end namespace Catch + +// #included from: catch_assertionresult.h +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED + +#include + +namespace Catch { + +struct AssertionInfo { + AssertionInfo() {} + AssertionInfo(std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition); + + std::string macroName; + SourceLineInfo lineInfo; + std::string capturedExpression; + ResultDisposition::Flags resultDisposition; +}; + +struct AssertionResultData { + AssertionResultData() + : resultType(ResultWas::Unknown) {} + + std::string reconstructedExpression; + std::string message; + ResultWas::OfType resultType; +}; + +class AssertionResult { +public: + AssertionResult(); + AssertionResult(AssertionInfo const& info, AssertionResultData const& data); + ~AssertionResult(); +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionResult(AssertionResult const&) = default; + AssertionResult(AssertionResult&&) = default; + AssertionResult& operator=(AssertionResult const&) = default; + AssertionResult& operator=(AssertionResult&&) = default; +#endif + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + std::string getTestMacroName() const; + +protected: + AssertionInfo m_info; + AssertionResultData m_resultData; +}; + +} // end namespace Catch + +// #included from: catch_matchers.hpp +#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED + +namespace Catch { +namespace Matchers { + namespace Impl { + + namespace Generic { + template + class AllOf; + template + class AnyOf; + template + class Not; + } + + template + struct Matcher : SharedImpl { + typedef ExpressionT ExpressionType; + + virtual ~Matcher() {} + virtual Ptr clone() const = 0; + virtual bool match(ExpressionT const& expr) const = 0; + virtual std::string toString() const = 0; + + Generic::AllOf operator&&(Matcher const& other) const; + Generic::AnyOf operator||(Matcher const& other) const; + Generic::Not operator!() const; + }; + + template + struct MatcherImpl : Matcher { + + virtual Ptr> clone() const { + return Ptr>(new DerivedT(static_cast(*this))); + } + }; + + namespace Generic { + template + class Not : public MatcherImpl, ExpressionT> { + public: + explicit Not(Matcher const& matcher) + : m_matcher(matcher.clone()) {} + Not(Not const& other) + : m_matcher(other.m_matcher) {} + + virtual bool match(ExpressionT const& expr) const CATCH_OVERRIDE { + return !m_matcher->match(expr); + } + + virtual std::string toString() const CATCH_OVERRIDE { + return "not " + m_matcher->toString(); + } + + private: + Ptr> m_matcher; + }; + + template + class AllOf : public MatcherImpl, ExpressionT> { + public: + AllOf() {} + AllOf(AllOf const& other) + : m_matchers(other.m_matchers) {} + + AllOf& add(Matcher const& matcher) { + m_matchers.push_back(matcher.clone()); + return *this; + } + virtual bool match(ExpressionT const& expr) const { + for (std::size_t i = 0; i < m_matchers.size(); ++i) + if (!m_matchers[i]->match(expr)) + return false; + return true; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for (std::size_t i = 0; i < m_matchers.size(); ++i) { + if (i != 0) + oss << " and "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + AllOf operator&&(Matcher const& other) const { + AllOf allOfExpr(*this); + allOfExpr.add(other); + return allOfExpr; + } + + private: + std::vector>> m_matchers; + }; + + template + class AnyOf : public MatcherImpl, ExpressionT> { + public: + AnyOf() {} + AnyOf(AnyOf const& other) + : m_matchers(other.m_matchers) {} + + AnyOf& add(Matcher const& matcher) { + m_matchers.push_back(matcher.clone()); + return *this; + } + virtual bool match(ExpressionT const& expr) const { + for (std::size_t i = 0; i < m_matchers.size(); ++i) + if (m_matchers[i]->match(expr)) + return true; + return false; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for (std::size_t i = 0; i < m_matchers.size(); ++i) { + if (i != 0) + oss << " or "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + AnyOf operator||(Matcher const& other) const { + AnyOf anyOfExpr(*this); + anyOfExpr.add(other); + return anyOfExpr; + } + + private: + std::vector>> m_matchers; + }; + + } // namespace Generic + + template + Generic::AllOf Matcher::operator&&(Matcher const& other) const { + Generic::AllOf allOfExpr; + allOfExpr.add(*this); + allOfExpr.add(other); + return allOfExpr; + } + + template + Generic::AnyOf Matcher::operator||(Matcher const& other) const { + Generic::AnyOf anyOfExpr; + anyOfExpr.add(*this); + anyOfExpr.add(other); + return anyOfExpr; + } + + template + Generic::Not Matcher::operator!() const { + return Generic::Not(*this); + } + + namespace StdString { + + inline std::string makeString(std::string const& str) { return str; } + inline std::string makeString(const char* str) { return str ? std::string(str) : std::string(); } + + struct CasedString { + CasedString(std::string const& str, CaseSensitive::Choice caseSensitivity) + : m_caseSensitivity(caseSensitivity) + , m_str(adjustString(str)) {} + std::string adjustString(std::string const& str) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower(str) + : str; + } + std::string toStringSuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : ""; + } + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct Equals : MatcherImpl { + Equals(std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) + : m_data(str, caseSensitivity) {} + Equals(Equals const& other) + : m_data(other.m_data) {} + + virtual ~Equals(); + + virtual bool match(std::string const& expr) const { + return m_data.m_str == m_data.adjustString(expr); + ; + } + virtual std::string toString() const { + return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct Contains : MatcherImpl { + Contains(std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) + : m_data(substr, caseSensitivity) {} + Contains(Contains const& other) + : m_data(other.m_data) {} + + virtual ~Contains(); + + virtual bool match(std::string const& expr) const { + return m_data.adjustString(expr).find(m_data.m_str) != std::string::npos; + } + virtual std::string toString() const { + return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct StartsWith : MatcherImpl { + StartsWith(std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) + : m_data(substr, caseSensitivity) {} + + StartsWith(StartsWith const& other) + : m_data(other.m_data) {} + + virtual ~StartsWith(); + + virtual bool match(std::string const& expr) const { + return startsWith(m_data.adjustString(expr), m_data.m_str); + } + virtual std::string toString() const { + return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct EndsWith : MatcherImpl { + EndsWith(std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) + : m_data(substr, caseSensitivity) {} + EndsWith(EndsWith const& other) + : m_data(other.m_data) {} + + virtual ~EndsWith(); + + virtual bool match(std::string const& expr) const { + return endsWith(m_data.adjustString(expr), m_data.m_str); + } + virtual std::string toString() const { + return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + } // namespace StdString + } // namespace Impl + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + template + inline Impl::Generic::Not Not(Impl::Matcher const& m) { + return Impl::Generic::Not(m); + } + + template + inline Impl::Generic::AllOf AllOf(Impl::Matcher const& m1, + Impl::Matcher const& m2) { + return Impl::Generic::AllOf().add(m1).add(m2); + } + template + inline Impl::Generic::AllOf AllOf(Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3) { + return Impl::Generic::AllOf().add(m1).add(m2).add(m3); + } + template + inline Impl::Generic::AnyOf AnyOf(Impl::Matcher const& m1, + Impl::Matcher const& m2) { + return Impl::Generic::AnyOf().add(m1).add(m2); + } + template + inline Impl::Generic::AnyOf AnyOf(Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3) { + return Impl::Generic::AnyOf().add(m1).add(m2).add(m3); + } + + inline Impl::StdString::Equals Equals(std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { + return Impl::StdString::Equals(str, caseSensitivity); + } + inline Impl::StdString::Equals Equals(const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { + return Impl::StdString::Equals(Impl::StdString::makeString(str), caseSensitivity); + } + inline Impl::StdString::Contains Contains(std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { + return Impl::StdString::Contains(substr, caseSensitivity); + } + inline Impl::StdString::Contains Contains(const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { + return Impl::StdString::Contains(Impl::StdString::makeString(substr), caseSensitivity); + } + inline Impl::StdString::StartsWith StartsWith(std::string const& substr) { + return Impl::StdString::StartsWith(substr); + } + inline Impl::StdString::StartsWith StartsWith(const char* substr) { + return Impl::StdString::StartsWith(Impl::StdString::makeString(substr)); + } + inline Impl::StdString::EndsWith EndsWith(std::string const& substr) { + return Impl::StdString::EndsWith(substr); + } + inline Impl::StdString::EndsWith EndsWith(const char* substr) { + return Impl::StdString::EndsWith(Impl::StdString::makeString(substr)); + } + +} // namespace Matchers + +using namespace Matchers; + +} // namespace Catch + +namespace Catch { + +struct TestFailureException {}; + +template +class ExpressionLhs; + +struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + +struct CopyableStream { + CopyableStream() {} + CopyableStream(CopyableStream const& other) { + oss << other.oss.str(); + } + CopyableStream& operator=(CopyableStream const& other) { + oss.str(""); + oss << other.oss.str(); + return *this; + } + std::ostringstream oss; +}; + +class ResultBuilder { +public: + ResultBuilder(char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg = ""); + + template + ExpressionLhs operator<=(T const& operand); + ExpressionLhs operator<=(bool value); + + template + ResultBuilder& operator<<(T const& value) { + m_stream.oss << value; + return *this; + } + + template + STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator&&(RhsT const&); + template + STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator||(RhsT const&); + + ResultBuilder& setResultType(ResultWas::OfType result); + ResultBuilder& setResultType(bool result); + ResultBuilder& setLhs(std::string const& lhs); + ResultBuilder& setRhs(std::string const& rhs); + ResultBuilder& setOp(std::string const& op); + + void endExpression(); + + std::string reconstructExpression() const; + AssertionResult build() const; + + void useActiveException(ResultDisposition::Flags resultDisposition = ResultDisposition::Normal); + void captureResult(ResultWas::OfType resultType); + void captureExpression(); + void captureExpectedException(std::string const& expectedMessage); + void captureExpectedException(Matchers::Impl::Matcher const& matcher); + void handleResult(AssertionResult const& result); + void react(); + bool shouldDebugBreak() const; + bool allowThrows() const; + +private: + AssertionInfo m_assertionInfo; + AssertionResultData m_data; + struct ExprComponents { + ExprComponents() + : testFalse(false) {} + bool testFalse; + std::string lhs, rhs, op; + } m_exprComponents; + CopyableStream m_stream; + + bool m_shouldDebugBreak; + bool m_shouldThrow; +}; + +} // namespace Catch + +// Include after due to circular dependency: +// #included from: catch_expression_lhs.hpp +#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED + +// #included from: catch_evaluate.hpp +#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4389) // '==' : signed/unsigned mismatch +#endif + +#include + +namespace Catch { +namespace Internal { + + enum Operator { + IsEqualTo, + IsNotEqualTo, + IsLessThan, + IsGreaterThan, + IsLessThanOrEqualTo, + IsGreaterThanOrEqualTo + }; + + template + struct OperatorTraits { + static const char* getName() { return "*error*"; } + }; + template <> + struct OperatorTraits { + static const char* getName() { return "=="; } + }; + template <> + struct OperatorTraits { + static const char* getName() { return "!="; } + }; + template <> + struct OperatorTraits { + static const char* getName() { return "<"; } + }; + template <> + struct OperatorTraits { + static const char* getName() { return ">"; } + }; + template <> + struct OperatorTraits { + static const char* getName() { return "<="; } + }; + template <> + struct OperatorTraits { + static const char* getName() { return ">="; } + }; + + template + inline T& opCast(T const& t) { return const_cast(t); } + +// nullptr_t support based on pull request #154 from Konstantin Baumann +#ifdef CATCH_CONFIG_CPP11_NULLPTR + inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } +#endif // CATCH_CONFIG_CPP11_NULLPTR + + // So the compare overloads can be operator agnostic we convey the operator as a template + // enum, which is used to specialise an Evaluator for doing the comparison. + template + class Evaluator {}; + + template + struct Evaluator { + static bool evaluate(T1 const& lhs, T2 const& rhs) { + return bool(opCast(lhs) == opCast(rhs)); + } + }; + template + struct Evaluator { + static bool evaluate(T1 const& lhs, T2 const& rhs) { + return bool(opCast(lhs) != opCast(rhs)); + } + }; + template + struct Evaluator { + static bool evaluate(T1 const& lhs, T2 const& rhs) { + return bool(opCast(lhs) < opCast(rhs)); + } + }; + template + struct Evaluator { + static bool evaluate(T1 const& lhs, T2 const& rhs) { + return bool(opCast(lhs) > opCast(rhs)); + } + }; + template + struct Evaluator { + static bool evaluate(T1 const& lhs, T2 const& rhs) { + return bool(opCast(lhs) >= opCast(rhs)); + } + }; + template + struct Evaluator { + static bool evaluate(T1 const& lhs, T2 const& rhs) { + return bool(opCast(lhs) <= opCast(rhs)); + } + }; + + template + bool applyEvaluator(T1 const& lhs, T2 const& rhs) { + return Evaluator::evaluate(lhs, rhs); + } + + // This level of indirection allows us to specialise for integer types + // to avoid signed/ unsigned warnings + + // "base" overload + template + bool compare(T1 const& lhs, T2 const& rhs) { + return Evaluator::evaluate(lhs, rhs); + } + + // unsigned X to int + template + bool compare(unsigned int lhs, int rhs) { + return applyEvaluator(lhs, static_cast(rhs)); + } + template + bool compare(unsigned long lhs, int rhs) { + return applyEvaluator(lhs, static_cast(rhs)); + } + template + bool compare(unsigned char lhs, int rhs) { + return applyEvaluator(lhs, static_cast(rhs)); + } + + // unsigned X to long + template + bool compare(unsigned int lhs, long rhs) { + return applyEvaluator(lhs, static_cast(rhs)); + } + template + bool compare(unsigned long lhs, long rhs) { + return applyEvaluator(lhs, static_cast(rhs)); + } + template + bool compare(unsigned char lhs, long rhs) { + return applyEvaluator(lhs, static_cast(rhs)); + } + + // int to unsigned X + template + bool compare(int lhs, unsigned int rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(int lhs, unsigned long rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(int lhs, unsigned char rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + + // long to unsigned X + template + bool compare(long lhs, unsigned int rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(long lhs, unsigned long rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(long lhs, unsigned char rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + + // pointer to long (when comparing against NULL) + template + bool compare(long lhs, T* rhs) { + return Evaluator::evaluate(reinterpret_cast(lhs), rhs); + } + template + bool compare(T* lhs, long rhs) { + return Evaluator::evaluate(lhs, reinterpret_cast(rhs)); + } + + // pointer to int (when comparing against NULL) + template + bool compare(int lhs, T* rhs) { + return Evaluator::evaluate(reinterpret_cast(lhs), rhs); + } + template + bool compare(T* lhs, int rhs) { + return Evaluator::evaluate(lhs, reinterpret_cast(rhs)); + } + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG + // long long to unsigned X + template + bool compare(long long lhs, unsigned int rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(long long lhs, unsigned long rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(long long lhs, unsigned long long rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(long long lhs, unsigned char rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + + // unsigned long long to X + template + bool compare(unsigned long long lhs, int rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(unsigned long long lhs, long rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(unsigned long long lhs, long long rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + template + bool compare(unsigned long long lhs, char rhs) { + return applyEvaluator(static_cast(lhs), rhs); + } + + // pointer to long long (when comparing against NULL) + template + bool compare(long long lhs, T* rhs) { + return Evaluator::evaluate(reinterpret_cast(lhs), rhs); + } + template + bool compare(T* lhs, long long rhs) { + return Evaluator::evaluate(lhs, reinterpret_cast(rhs)); + } +#endif // CATCH_CONFIG_CPP11_LONG_LONG + +#ifdef CATCH_CONFIG_CPP11_NULLPTR + // pointer to nullptr_t (when comparing against nullptr) + template + bool compare(std::nullptr_t, T* rhs) { + return Evaluator::evaluate(nullptr, rhs); + } + template + bool compare(T* lhs, std::nullptr_t) { + return Evaluator::evaluate(lhs, nullptr); + } +#endif // CATCH_CONFIG_CPP11_NULLPTR + +} // end of namespace Internal +} // end of namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// #included from: catch_tostring.h +#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +// #included from: catch_objc_arc.hpp +#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease(NSObject* obj); +id performOptionalSelector(id obj, SEL sel); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease(NSObject* obj) { + [obj release]; +} +inline id performOptionalSelector(id obj, SEL sel) { + if ([obj respondsToSelector:sel]) + return [obj performSelector:sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease(NSObject*) {} +inline id performOptionalSelector(id obj, SEL sel) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if ([obj respondsToSelector:sel]) + return [obj performSelector:sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +#endif + +#ifdef CATCH_CONFIG_CPP11_TUPLE +#include +#endif + +#ifdef CATCH_CONFIG_CPP11_IS_ENUM +#include +#endif + +namespace Catch { + +// Why we're here. +template +std::string toString(T const& value); + +// Built in overloads + +std::string toString(std::string const& value); +std::string toString(std::wstring const& value); +std::string toString(const char* const value); +std::string toString(char* const value); +std::string toString(const wchar_t* const value); +std::string toString(wchar_t* const value); +std::string toString(int value); +std::string toString(unsigned long value); +std::string toString(unsigned int value); +std::string toString(const double value); +std::string toString(const float value); +std::string toString(bool value); +std::string toString(char value); +std::string toString(signed char value); +std::string toString(unsigned char value); + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG +std::string toString(long long value); +std::string toString(unsigned long long value); +#endif + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString(std::nullptr_t); +#endif + +#ifdef __OBJC__ +std::string toString(NSString const* const& nsstring); +std::string toString(NSString* CATCH_ARC_STRONG const& nsstring); +std::string toString(NSObject* const& nsObject); +#endif + +namespace Detail { + + extern const std::string unprintableString; + + struct BorgType { + template + BorgType(T const&); + }; + + struct TrueType { + char sizer[1]; + }; + struct FalseType { + char sizer[2]; + }; + + TrueType& testStreamable(std::ostream&); + FalseType testStreamable(FalseType); + + FalseType operator<<(std::ostream const&, BorgType const&); + + template + struct IsStreamInsertable { + static std::ostream& s; + static T const& t; + enum { value = sizeof(testStreamable(s << t)) == sizeof(TrueType) }; + }; + +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template ::value> + struct EnumStringMaker { + static std::string convert(T const&) { return unprintableString; } + }; + + template + struct EnumStringMaker { + static std::string convert(T const& v) { + return ::Catch::toString( + static_cast::type>(v)); + } + }; +#endif + template + struct StringMakerBase { +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template + static std::string convert(T const& v) { + return EnumStringMaker::convert(v); + } +#else + template + static std::string convert(T const&) { return unprintableString; } +#endif + }; + + template <> + struct StringMakerBase { + template + static std::string convert(T const& _value) { + std::ostringstream oss; + oss << _value; + return oss.str(); + } + }; + + std::string rawMemoryToString(const void* object, std::size_t size); + + template + inline std::string rawMemoryToString(const T& object) { + return rawMemoryToString(&object, sizeof(object)); + } + +} // end namespace Detail + +template +struct StringMaker : Detail::StringMakerBase::value> {}; + +template +struct StringMaker { + template + static std::string convert(U* p) { + if (!p) + return "NULL"; + else + return Detail::rawMemoryToString(p); + } +}; + +template +struct StringMaker { + static std::string convert(R C::*p) { + if (!p) + return "NULL"; + else + return Detail::rawMemoryToString(p); + } +}; + +namespace Detail { + template + std::string rangeToString(InputIterator first, InputIterator last); +} + +//template +//struct StringMaker > { +// static std::string convert( std::vector const& v ) { +// return Detail::rangeToString( v.begin(), v.end() ); +// } +//}; + +template +std::string toString(std::vector const& v) { + return Detail::rangeToString(v.begin(), v.end()); +} + +#ifdef CATCH_CONFIG_CPP11_TUPLE + +// toString for tuples +namespace TupleDetail { + template < + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value)> + struct ElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << Catch::toString(std::get(tuple)); + ElementPrinter::print(tuple, os); + } + }; + + template < + typename Tuple, + std::size_t N> + struct ElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; +} + +template +struct StringMaker> { + + static std::string convert(const std::tuple& tuple) { + std::ostringstream os; + os << '{'; + TupleDetail::ElementPrinter>::print(tuple, os); + os << " }"; + return os.str(); + } +}; +#endif // CATCH_CONFIG_CPP11_TUPLE + +namespace Detail { + template + std::string makeString(T const& value) { + return StringMaker::convert(value); + } +} // end namespace Detail + +/// \brief converts any type to a string +/// +/// The default template forwards on to ostringstream - except when an +/// ostringstream overload does not exist - in which case it attempts to detect +/// that and writes {?}. +/// Overload (not specialise) this template for custom typs that you don't want +/// to provide an ostream overload for. +template +std::string toString(T const& value) { + return StringMaker::convert(value); +} + +namespace Detail { + template + std::string rangeToString(InputIterator first, InputIterator last) { + std::ostringstream oss; + oss << "{ "; + if (first != last) { + oss << Catch::toString(*first); + for (++first; first != last; ++first) + oss << ", " << Catch::toString(*first); + } + oss << " }"; + return oss.str(); + } +} + +} // end namespace Catch + +namespace Catch { + +// Wraps the LHS of an expression and captures the operator and RHS (if any) - +// wrapping them all in a ResultBuilder object +template +class ExpressionLhs { + ExpressionLhs& operator=(ExpressionLhs const&); +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + ExpressionLhs& operator=(ExpressionLhs&&) = delete; +#endif + +public: + ExpressionLhs(ResultBuilder& rb, T lhs) + : m_rb(rb) + , m_lhs(lhs) {} +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + ExpressionLhs(ExpressionLhs const&) = default; + ExpressionLhs(ExpressionLhs&&) = default; +#endif + + template + ResultBuilder& operator==(RhsT const& rhs) { + return captureExpression(rhs); + } + + template + ResultBuilder& operator!=(RhsT const& rhs) { + return captureExpression(rhs); + } + + template + ResultBuilder& operator<(RhsT const& rhs) { + return captureExpression(rhs); + } + + template + ResultBuilder& operator>(RhsT const& rhs) { + return captureExpression(rhs); + } + + template + ResultBuilder& operator<=(RhsT const& rhs) { + return captureExpression(rhs); + } + + template + ResultBuilder& operator>=(RhsT const& rhs) { + return captureExpression(rhs); + } + + ResultBuilder& operator==(bool rhs) { + return captureExpression(rhs); + } + + ResultBuilder& operator!=(bool rhs) { + return captureExpression(rhs); + } + + void endExpression() { + bool value = m_lhs ? true : false; + m_rb + .setLhs(Catch::toString(value)) + .setResultType(value) + .endExpression(); + } + + // Only simple binary expressions are allowed on the LHS. + // If more complex compositions are required then place the sub expression in parentheses + template + STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator+(RhsT const&); + template + STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator-(RhsT const&); + template + STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator/(RhsT const&); + template + STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator*(RhsT const&); + template + STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator&&(RhsT const&); + template + STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator||(RhsT const&); + +private: + template + ResultBuilder& captureExpression(RhsT const& rhs) { + return m_rb + .setResultType(Internal::compare(m_lhs, rhs)) + .setLhs(Catch::toString(m_lhs)) + .setRhs(Catch::toString(rhs)) + .setOp(Internal::OperatorTraits::getName()); + } + +private: + ResultBuilder& m_rb; + T m_lhs; +}; + +} // end namespace Catch + +namespace Catch { + +template +inline ExpressionLhs ResultBuilder::operator<=(T const& operand) { + return ExpressionLhs(*this, operand); +} + +inline ExpressionLhs ResultBuilder::operator<=(bool value) { + return ExpressionLhs(*this, value); +} + +} // namespace Catch + +// #included from: catch_message.h +#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED + +#include + +namespace Catch { + +struct MessageInfo { + MessageInfo(std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type); + + std::string macroName; + SourceLineInfo lineInfo; + ResultWas::OfType type; + std::string message; + unsigned int sequence; + + bool operator==(MessageInfo const& other) const { + return sequence == other.sequence; + } + bool operator<(MessageInfo const& other) const { + return sequence < other.sequence; + } + +private: + static unsigned int globalCount; +}; + +struct MessageBuilder { + MessageBuilder(std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type) + : m_info(macroName, lineInfo, type) {} + + template + MessageBuilder& operator<<(T const& value) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + std::ostringstream m_stream; +}; + +class ScopedMessage { +public: + ScopedMessage(MessageBuilder const& builder); + ScopedMessage(ScopedMessage const& other); + ~ScopedMessage(); + + MessageInfo m_info; +}; + +} // end namespace Catch + +// #included from: catch_interfaces_capture.h +#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED + +#include + +namespace Catch { + +class TestCase; +class AssertionResult; +struct AssertionInfo; +struct SectionInfo; +struct SectionEndInfo; +struct MessageInfo; +class ScopedMessageBuilder; +struct Counts; + +struct IResultCapture { + + virtual ~IResultCapture(); + + virtual void assertionEnded(AssertionResult const& result) = 0; + virtual bool sectionStarted(SectionInfo const& sectionInfo, + Counts& assertions) + = 0; + virtual void sectionEnded(SectionEndInfo const& endInfo) = 0; + virtual void sectionEndedEarly(SectionEndInfo const& endInfo) = 0; + virtual void pushScopedMessage(MessageInfo const& message) = 0; + virtual void popScopedMessage(MessageInfo const& message) = 0; + + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + + virtual void handleFatalErrorCondition(std::string const& message) = 0; +}; + +IResultCapture& getResultCapture(); +} + +// #included from: catch_debugger.h +#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED + +// #included from: catch_platform.h +#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED + +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_IPHONE +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CATCH_PLATFORM_WINDOWS +#endif + +#include + +namespace Catch { + +bool isDebuggerActive(); +void writeToDebugConsole(std::string const& text); +} + +#ifdef CATCH_PLATFORM_MAC + +// The following code snippet based on: +// http://cocoawithlove.com/2008/03/break-into-debugger.html +#ifdef DEBUG +#if defined(__ppc64__) || defined(__ppc__) +#define CATCH_BREAK_INTO_DEBUGGER() \ + if (Catch::isDebuggerActive()) { \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : \ + : \ + : "memory", "r0", "r3", "r4"); \ + } +#else +#define CATCH_BREAK_INTO_DEBUGGER() \ + if (Catch::isDebuggerActive()) { \ + __asm__("int $3\n" \ + : \ + :); \ + } +#endif +#endif + +#elif defined(_MSC_VER) +#define CATCH_BREAK_INTO_DEBUGGER() \ + if (Catch::isDebuggerActive()) { \ + __debugbreak(); \ + } +#elif defined(__MINGW32__) +extern "C" __declspec(dllimport) void __stdcall DebugBreak(); +#define CATCH_BREAK_INTO_DEBUGGER() \ + if (Catch::isDebuggerActive()) { \ + DebugBreak(); \ + } +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER +#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#endif + +// #included from: catch_interfaces_runner.h +#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED + +namespace Catch { +class TestCase; + +struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; +}; +} + +/////////////////////////////////////////////////////////////////////////////// +// In the event of a failure works out if the debugger needs to be invoked +// and/or an exception thrown and takes appropriate action. +// This needs to be done as a macro so the debugger will stop in the user +// source code rather than in Catch library code +#define INTERNAL_CATCH_REACT(resultBuilder) \ + if (resultBuilder.shouldDebugBreak()) \ + CATCH_BREAK_INTO_DEBUGGER(); \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST(expr, resultDisposition, macroName) \ + do { \ + Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition); \ + try { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS(__catchResult <= expr) \ + .endExpression(); \ + } catch (...) { \ + __catchResult.useActiveException(Catch::ResultDisposition::Normal); \ + } \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::isTrue(false && !!(expr))) // expr here is never evaluated at runtime but it forces the compiler to give it a look + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF(expr, resultDisposition, macroName) \ + INTERNAL_CATCH_TEST(expr, resultDisposition, macroName); \ + if (Catch::getResultCapture().getLastResult()->succeeded()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE(expr, resultDisposition, macroName) \ + INTERNAL_CATCH_TEST(expr, resultDisposition, macroName); \ + if (!Catch::getResultCapture().getLastResult()->succeeded()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW(expr, resultDisposition, macroName) \ + do { \ + Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition); \ + try { \ + expr; \ + __catchResult.captureResult(Catch::ResultWas::Ok); \ + } catch (...) { \ + __catchResult.useActiveException(resultDisposition); \ + } \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::alwaysFalse()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS(expr, resultDisposition, matcher, macroName) \ + do { \ + Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher); \ + if (__catchResult.allowThrows()) \ + try { \ + expr; \ + __catchResult.captureResult(Catch::ResultWas::DidntThrowException); \ + } catch (...) { \ + __catchResult.captureExpectedException(matcher); \ + } \ + else \ + __catchResult.captureResult(Catch::ResultWas::Ok); \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::alwaysFalse()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS(expr, exceptionType, resultDisposition, macroName) \ + do { \ + Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition); \ + if (__catchResult.allowThrows()) \ + try { \ + expr; \ + __catchResult.captureResult(Catch::ResultWas::DidntThrowException); \ + } catch (exceptionType) { \ + __catchResult.captureResult(Catch::ResultWas::Ok); \ + } catch (...) { \ + __catchResult.useActiveException(resultDisposition); \ + } \ + else \ + __catchResult.captureResult(Catch::ResultWas::Ok); \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::alwaysFalse()) + +/////////////////////////////////////////////////////////////////////////////// +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define INTERNAL_CATCH_MSG(messageType, resultDisposition, macroName, ...) \ + do { \ + Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition); \ + __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ + __catchResult.captureResult(messageType); \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::alwaysFalse()) +#else +#define INTERNAL_CATCH_MSG(messageType, resultDisposition, macroName, log) \ + do { \ + Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition); \ + __catchResult << log + ::Catch::StreamEndStop(); \ + __catchResult.captureResult(messageType); \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::alwaysFalse()) +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO(log, macroName) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME(scopedMessage) = Catch::MessageBuilder(macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info) << log; + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT(arg, matcher, resultDisposition, macroName) \ + do { \ + Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition); \ + try { \ + std::string matcherAsString = (matcher).toString(); \ + __catchResult \ + .setLhs(Catch::toString(arg)) \ + .setRhs(matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString) \ + .setOp("matches") \ + .setResultType((matcher).match(arg)); \ + __catchResult.captureExpression(); \ + } catch (...) { \ + __catchResult.useActiveException(resultDisposition | Catch::ResultDisposition::ContinueOnFailure); \ + } \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::alwaysFalse()) + +// #included from: internal/catch_section.h +#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED + +// #included from: catch_section_info.h +#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED + +// #included from: catch_totals.hpp +#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + +struct Counts { + Counts() + : passed(0) + , failed(0) + , failedButOk(0) {} + + Counts operator-(Counts const& other) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + Counts& operator+=(Counts const& other) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t total() const { + return passed + failed + failedButOk; + } + bool allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool allOk() const { + return failed == 0; + } + + std::size_t passed; + std::size_t failed; + std::size_t failedButOk; +}; + +struct Totals { + + Totals operator-(Totals const& other) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals delta(Totals const& prevTotals) const { + Totals diff = *this - prevTotals; + if (diff.assertions.failed > 0) + ++diff.testCases.failed; + else if (diff.assertions.failedButOk > 0) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + + Totals& operator+=(Totals const& other) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Counts assertions; + Counts testCases; +}; +} + +namespace Catch { + +struct SectionInfo { + SectionInfo(SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description = std::string()); + + std::string name; + std::string description; + SourceLineInfo lineInfo; +}; + +struct SectionEndInfo { + SectionEndInfo(SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds) + : sectionInfo(_sectionInfo) + , prevAssertions(_prevAssertions) + , durationInSeconds(_durationInSeconds) {} + + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; +}; + +} // end namespace Catch + +// #included from: catch_timer.h +#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED + +#ifdef CATCH_PLATFORM_WINDOWS +typedef unsigned long long uint64_t; +#else +#include +#endif + +namespace Catch { + +class Timer { +public: + Timer() + : m_ticks(0) {} + void start(); + unsigned int getElapsedMicroseconds() const; + unsigned int getElapsedMilliseconds() const; + double getElapsedSeconds() const; + +private: + uint64_t m_ticks; +}; + +} // namespace Catch + +#include + +namespace Catch { + +class Section : NonCopyable { +public: + Section(SectionInfo const& info); + ~Section(); + + // This indicates whether the section should be executed or not + operator bool() const; + +private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; +}; + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define INTERNAL_CATCH_SECTION(...) \ + if (Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME(catch_internal_Section) = Catch::SectionInfo(CATCH_INTERNAL_LINEINFO, __VA_ARGS__)) +#else +#define INTERNAL_CATCH_SECTION(name, desc) \ + if (Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME(catch_internal_Section) = Catch::SectionInfo(CATCH_INTERNAL_LINEINFO, name, desc)) +#endif + +// #included from: internal/catch_generators.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + +template +struct IGenerator { + virtual ~IGenerator() {} + virtual T getValue(std::size_t index) const = 0; + virtual std::size_t size() const = 0; +}; + +template +class BetweenGenerator : public IGenerator { +public: + BetweenGenerator(T from, T to) + : m_from(from) + , m_to(to) {} + + virtual T getValue(std::size_t index) const { + return m_from + static_cast(index); + } + + virtual std::size_t size() const { + return static_cast(1 + m_to - m_from); + } + +private: + T m_from; + T m_to; +}; + +template +class ValuesGenerator : public IGenerator { +public: + ValuesGenerator() {} + + void add(T value) { + m_values.push_back(value); + } + + virtual T getValue(std::size_t index) const { + return m_values[index]; + } + + virtual std::size_t size() const { + return m_values.size(); + } + +private: + std::vector m_values; +}; + +template +class CompositeGenerator { +public: + CompositeGenerator() + : m_totalSize(0) {} + + // *** Move semantics, similar to auto_ptr *** + CompositeGenerator(CompositeGenerator& other) + : m_fileInfo(other.m_fileInfo) + , m_totalSize(0) { + move(other); + } + + CompositeGenerator& setFileInfo(const char* fileInfo) { + m_fileInfo = fileInfo; + return *this; + } + + ~CompositeGenerator() { + deleteAll(m_composed); + } + + operator T() const { + size_t overallIndex = getCurrentContext().getGeneratorIndex(m_fileInfo, m_totalSize); + + typename std::vector*>::const_iterator it = m_composed.begin(); + typename std::vector*>::const_iterator itEnd = m_composed.end(); + for (size_t index = 0; it != itEnd; ++it) { + const IGenerator* generator = *it; + if (overallIndex >= index && overallIndex < index + generator->size()) { + return generator->getValue(overallIndex - index); + } + index += generator->size(); + } + CATCH_INTERNAL_ERROR("Indexed past end of generated range"); + return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so + } + + void add(const IGenerator* generator) { + m_totalSize += generator->size(); + m_composed.push_back(generator); + } + + CompositeGenerator& then(CompositeGenerator& other) { + move(other); + return *this; + } + + CompositeGenerator& then(T value) { + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add(value); + add(valuesGen); + return *this; + } + +private: + void move(CompositeGenerator& other) { + std::copy(other.m_composed.begin(), other.m_composed.end(), std::back_inserter(m_composed)); + m_totalSize += other.m_totalSize; + other.m_composed.clear(); + } + + std::vector*> m_composed; + std::string m_fileInfo; + size_t m_totalSize; +}; + +namespace Generators { + template + CompositeGenerator between(T from, T to) { + CompositeGenerator generators; + generators.add(new BetweenGenerator(from, to)); + return generators; + } + + template + CompositeGenerator values(T val1, T val2) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add(val1); + valuesGen->add(val2); + generators.add(valuesGen); + return generators; + } + + template + CompositeGenerator values(T val1, T val2, T val3) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add(val1); + valuesGen->add(val2); + valuesGen->add(val3); + generators.add(valuesGen); + return generators; + } + + template + CompositeGenerator values(T val1, T val2, T val3, T val4) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add(val1); + valuesGen->add(val2); + valuesGen->add(val3); + valuesGen->add(val4); + generators.add(valuesGen); + return generators; + } + +} // end namespace Generators + +using namespace Generators; + +} // end namespace Catch + +#define INTERNAL_CATCH_LINESTR2(line) #line +#define INTERNAL_CATCH_LINESTR(line) INTERNAL_CATCH_LINESTR2(line) + +#define INTERNAL_CATCH_GENERATE(expr) expr.setFileInfo(__FILE__ "(" INTERNAL_CATCH_LINESTR(__LINE__) ")") + +// #included from: internal/catch_interfaces_exception.h +#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED + +#include +#include + +// #included from: catch_interfaces_registry_hub.h +#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED + +#include + +namespace Catch { + +class TestCase; +struct ITestCaseRegistry; +struct IExceptionTranslatorRegistry; +struct IExceptionTranslator; +struct IReporterRegistry; +struct IReporterFactory; + +struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; +}; + +struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter(std::string const& name, Ptr const& factory) = 0; + virtual void registerListener(Ptr const& factory) = 0; + virtual void registerTest(TestCase const& testInfo) = 0; + virtual void registerTranslator(const IExceptionTranslator* translator) = 0; +}; + +IRegistryHub& getRegistryHub(); +IMutableRegistryHub& getMutableRegistryHub(); +void cleanUp(); +std::string translateActiveException(); +} + +namespace Catch { + +typedef std::string (*exceptionTranslateFunction)(); + +struct IExceptionTranslator; +typedef std::vector ExceptionTranslators; + +struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate(ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd) const = 0; +}; + +struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; +}; + +class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + ExceptionTranslator(std::string (*translateFunction)(T&)) + : m_translateFunction(translateFunction) {} + + virtual std::string translate(ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd) const CATCH_OVERRIDE { + try { + if (it == itEnd) + throw; + else + return (*it)->translate(it + 1, itEnd); + } catch (T& ex) { + return m_translateFunction(ex); + } + } + + protected: + std::string (*m_translateFunction)(T&); + }; + +public: + template + ExceptionTranslatorRegistrar(std::string (*translateFunction)(T&)) { + getMutableRegistryHub().registerTranslator(new ExceptionTranslator(translateFunction)); + } +}; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2(translatorName, signature) \ + static std::string translatorName(signature); \ + namespace { \ + Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionRegistrar)(&translatorName); \ + } \ + static std::string translatorName(signature) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION(signature) INTERNAL_CATCH_TRANSLATE_EXCEPTION2(INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator), signature) + +// #included from: internal/catch_approx.hpp +#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED + +#include +#include + +namespace Catch { +namespace Detail { + + class Approx { + public: + explicit Approx(double value) + : m_epsilon(std::numeric_limits::epsilon() * 100) + , m_scale(1.0) + , m_value(value) {} + + Approx(Approx const& other) + : m_epsilon(other.m_epsilon) + , m_scale(other.m_scale) + , m_value(other.m_value) {} + + static Approx custom() { + return Approx(0); + } + + Approx operator()(double value) { + Approx approx(value); + approx.epsilon(m_epsilon); + approx.scale(m_scale); + return approx; + } + + friend bool operator==(double lhs, Approx const& rhs) { + // Thanks to Richard Harris for his help refining this formula + return fabs(lhs - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(fabs(lhs), fabs(rhs.m_value))); + } + + friend bool operator==(Approx const& lhs, double rhs) { + return operator==(rhs, lhs); + } + + friend bool operator!=(double lhs, Approx const& rhs) { + return !operator==(lhs, rhs); + } + + friend bool operator!=(Approx const& lhs, double rhs) { + return !operator==(rhs, lhs); + } + + Approx& epsilon(double newEpsilon) { + m_epsilon = newEpsilon; + return *this; + } + + Approx& scale(double newScale) { + m_scale = newScale; + return *this; + } + + std::string toString() const { + std::ostringstream oss; + oss << "Approx( " << Catch::toString(m_value) << " )"; + return oss.str(); + } + + private: + double m_epsilon; + double m_scale; + double m_value; + }; +} + +template <> +inline std::string toString(Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch + +// #included from: internal/catch_interfaces_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED + +// #included from: catch_tag_alias.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED + +#include + +namespace Catch { + +struct TagAlias { + TagAlias(std::string _tag, SourceLineInfo _lineInfo) + : tag(_tag) + , lineInfo(_lineInfo) {} + + std::string tag; + SourceLineInfo lineInfo; +}; + +struct RegistrarForTagAliases { + RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo); +}; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS(alias, spec) \ + namespace { \ + Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME(AutoRegisterTagAlias)(alias, spec, CATCH_INTERNAL_LINEINFO); \ + } +// #included from: catch_option.hpp +#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED + +namespace Catch { + +// An optional type +template +class Option { +public: + Option() + : nullableValue(CATCH_NULL) {} + Option(T const& _value) + : nullableValue(new (storage) T(_value)) {} + Option(Option const& _other) + : nullableValue(_other ? new (storage) T(*_other) : CATCH_NULL) {} + + ~Option() { + reset(); + } + + Option& operator=(Option const& _other) { + if (&_other != this) { + reset(); + if (_other) + nullableValue = new (storage) T(*_other); + } + return *this; + } + Option& operator=(T const& _value) { + reset(); + nullableValue = new (storage) T(_value); + return *this; + } + + void reset() { + if (nullableValue) + nullableValue->~T(); + nullableValue = CATCH_NULL; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr(T const& defaultValue) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != CATCH_NULL; } + bool none() const { return nullableValue == CATCH_NULL; } + + bool operator!() const { return nullableValue == CATCH_NULL; } + operator SafeBool::type() const { + return SafeBool::makeSafe(some()); + } + +private: + T* nullableValue; + char storage[sizeof(T)]; +}; + +} // end namespace Catch + +namespace Catch { + +struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + virtual Option find(std::string const& alias) const = 0; + virtual std::string expandAliases(std::string const& unexpandedTestSpec) const = 0; + + static ITagAliasRegistry const& get(); +}; + +} // end namespace Catch + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// #included from: internal/catch_test_case_info.h +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED + +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + +struct ITestCase; + +struct TestCaseInfo { + enum SpecialProperties { + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4 + }; + + TestCaseInfo(std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo); + + TestCaseInfo(TestCaseInfo const& other); + + friend void setTags(TestCaseInfo& testCaseInfo, std::set const& tags); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string name; + std::string className; + std::string description; + std::set tags; + std::set lcaseTags; + std::string tagsAsString; + SourceLineInfo lineInfo; + SpecialProperties properties; +}; + +class TestCase : public TestCaseInfo { +public: + TestCase(ITestCase* testCase, TestCaseInfo const& info); + TestCase(TestCase const& other); + + TestCase withName(std::string const& _newName) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + void swap(TestCase& other); + bool operator==(TestCase const& other) const; + bool operator<(TestCase const& other) const; + TestCase& operator=(TestCase const& other); + +private: + Ptr test; +}; + +TestCase makeTestCase(ITestCase* testCase, + std::string const& className, + std::string const& name, + std::string const& description, + SourceLineInfo const& lineInfo); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __OBJC__ +// #included from: internal/catch_objc.hpp +#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +- (void)setUp; +- (void)tearDown; + +@end + +namespace Catch { + +class OcMethod : public SharedImpl { + +public: + OcMethod(Class cls, SEL sel) + : m_cls(cls) + , m_sel(sel) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector(obj, @selector(setUp)); + performOptionalSelector(obj, m_sel); + performOptionalSelector(obj, @selector(tearDown)); + + arcSafeRelease(obj); + } + +private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; +}; + +namespace Detail { + + inline std::string getAnnotation(Class cls, + std::string const& annotationName, + std::string const& testCaseName) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString(selStr); + arcSafeRelease(selStr); + id value = performOptionalSelector(cls, sel); + if (value) + return [(NSString*)value UTF8String]; + return ""; + } +} + +inline size_t registerTestMethods() { + size_t noTestMethods = 0; + int noClasses = objc_getClassList(CATCH_NULL, 0); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class*)malloc(sizeof(Class) * noClasses); + objc_getClassList(classes, noClasses); + + for (int c = 0; c < noClasses; c++) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList(cls, &count); + for (u_int m = 0; m < count; m++) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if (startsWith(methodName, "Catch_TestCase_")) { + std::string testCaseName = methodName.substr(15); + std::string name = Detail::getAnnotation(cls, "Name", testCaseName); + std::string desc = Detail::getAnnotation(cls, "Description", testCaseName); + const char* className = class_getName(cls); + + getMutableRegistryHub().registerTest(makeTestCase(new OcMethod(cls, selector), className, name.c_str(), desc.c_str(), SourceLineInfo())); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; +} + +namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + template + struct StringHolder : MatcherImpl { + StringHolder(NSString* substr) + : m_substr([substr copy]) {} + StringHolder(StringHolder const& other) + : m_substr([other.m_substr copy]) {} + StringHolder() { + arcSafeRelease(m_substr); + } + + NSString* m_substr; + }; + + struct Equals : StringHolder { + Equals(NSString* substr) + : StringHolder(substr) {} + + virtual bool match(ExpressionType const& str) const { + return (str != nil || m_substr == nil) && + [str isEqualToString:m_substr]; + } + + virtual std::string toString() const { + return "equals string: " + Catch::toString(m_substr); + } + }; + + struct Contains : StringHolder { + Contains(NSString* substr) + : StringHolder(substr) {} + + virtual bool match(ExpressionType const& str) const { + return (str != nil || m_substr == nil) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + virtual std::string toString() const { + return "contains string: " + Catch::toString(m_substr); + } + }; + + struct StartsWith : StringHolder { + StartsWith(NSString* substr) + : StringHolder(substr) {} + + virtual bool match(ExpressionType const& str) const { + return (str != nil || m_substr == nil) && + [str rangeOfString:m_substr].location == 0; + } + + virtual std::string toString() const { + return "starts with: " + Catch::toString(m_substr); + } + }; + struct EndsWith : StringHolder { + EndsWith(NSString* substr) + : StringHolder(substr) {} + + virtual bool match(ExpressionType const& str) const { + return (str != nil || m_substr == nil) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + virtual std::string toString() const { + return "ends with: " + Catch::toString(m_substr); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals(NSString* substr) { return Impl::NSStringMatchers::Equals(substr); } + + inline Impl::NSStringMatchers::Contains + Contains(NSString* substr) { return Impl::NSStringMatchers::Contains(substr); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith(NSString* substr) { return Impl::NSStringMatchers::StartsWith(substr); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith(NSString* substr) { return Impl::NSStringMatchers::EndsWith(substr); } + +} // namespace Matchers + +using namespace Matchers; + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_TEST_CASE(name, desc) \ + +(NSString*)INTERNAL_CATCH_UNIQUE_NAME(Catch_Name_test) { \ + return @name; \ + } \ + +(NSString*)INTERNAL_CATCH_UNIQUE_NAME(Catch_Description_test) { \ + return @desc; \ + } \ + -(void)INTERNAL_CATCH_UNIQUE_NAME(Catch_TestCase_test) + +#endif + +#ifdef CATCH_IMPL +// #included from: internal/catch_impl.hpp +#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED + +// Collect all the implementation files together here +// These are the equivalent of what would usually be cpp files + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// #included from: ../catch_session.hpp +#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED + +// #included from: internal/catch_commandline.hpp +#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED + +// #included from: catch_config.hpp +#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED + +// #included from: catch_test_spec_parser.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_test_spec.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_wildcard_pattern.hpp +#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED + +namespace Catch { +class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + +public: + WildcardPattern(std::string const& pattern, CaseSensitive::Choice caseSensitivity) + : m_caseSensitivity(caseSensitivity) + , m_wildcard(NoWildcard) + , m_pattern(adjustCase(pattern)) { + if (startsWith(m_pattern, "*")) { + m_pattern = m_pattern.substr(1); + m_wildcard = WildcardAtStart; + } + if (endsWith(m_pattern, "*")) { + m_pattern = m_pattern.substr(0, m_pattern.size() - 1); + m_wildcard = static_cast(m_wildcard | WildcardAtEnd); + } + } + virtual ~WildcardPattern(); + virtual bool matches(std::string const& str) const { + switch (m_wildcard) { + case NoWildcard: + return m_pattern == adjustCase(str); + case WildcardAtStart: + return endsWith(adjustCase(str), m_pattern); + case WildcardAtEnd: + return startsWith(adjustCase(str), m_pattern); + case WildcardAtBothEnds: + return contains(adjustCase(str), m_pattern); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + throw std::logic_error("Unknown enum"); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + +private: + std::string adjustCase(std::string const& str) const { + return m_caseSensitivity == CaseSensitive::No ? toLower(str) : str; + } + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard; + std::string m_pattern; +}; +} + +#include +#include + +namespace Catch { + +class TestSpec { + struct Pattern : SharedImpl<> { + virtual ~Pattern(); + virtual bool matches(TestCaseInfo const& testCase) const = 0; + }; + class NamePattern : public Pattern { + public: + NamePattern(std::string const& name) + : m_wildcardPattern(toLower(name), CaseSensitive::No) {} + virtual ~NamePattern(); + virtual bool matches(TestCaseInfo const& testCase) const { + return m_wildcardPattern.matches(toLower(testCase.name)); + } + + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern(std::string const& tag) + : m_tag(toLower(tag)) {} + virtual ~TagPattern(); + virtual bool matches(TestCaseInfo const& testCase) const { + return testCase.lcaseTags.find(m_tag) != testCase.lcaseTags.end(); + } + + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern(Ptr const& underlyingPattern) + : m_underlyingPattern(underlyingPattern) {} + virtual ~ExcludedPattern(); + virtual bool matches(TestCaseInfo const& testCase) const { return !m_underlyingPattern->matches(testCase); } + + private: + Ptr m_underlyingPattern; + }; + + struct Filter { + std::vector> m_patterns; + + bool matches(TestCaseInfo const& testCase) const { + // All patterns in a filter must match for the filter to be a match + for (std::vector>::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it) + if (!(*it)->matches(testCase)) + return false; + return true; + } + }; + +public: + bool hasFilters() const { + return !m_filters.empty(); + } + bool matches(TestCaseInfo const& testCase) const { + // A TestSpec matches if any filter matches + for (std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it) + if (it->matches(testCase)) + return true; + return false; + } + +private: + std::vector m_filters; + + friend class TestSpecParser; +}; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +namespace Catch { + +class TestSpecParser { + enum Mode { None, + Name, + QuotedName, + Tag }; + Mode m_mode; + bool m_exclusion; + std::size_t m_start, m_pos; + std::string m_arg; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases; + +public: + TestSpecParser(ITagAliasRegistry const& tagAliases) + : m_tagAliases(&tagAliases) {} + + TestSpecParser& parse(std::string const& arg) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases(arg); + for (m_pos = 0; m_pos < m_arg.size(); ++m_pos) + visitChar(m_arg[m_pos]); + if (m_mode == Name) + addPattern(); + return *this; + } + TestSpec testSpec() { + addFilter(); + return m_testSpec; + } + +private: + void visitChar(char c) { + if (m_mode == None) { + switch (c) { + case ' ': + return; + case '~': + m_exclusion = true; + return; + case '[': + return startNewMode(Tag, ++m_pos); + case '"': + return startNewMode(QuotedName, ++m_pos); + default: + startNewMode(Name, m_pos); + break; + } + } + if (m_mode == Name) { + if (c == ',') { + addPattern(); + addFilter(); + } else if (c == '[') { + if (subString() == "exclude:") + m_exclusion = true; + else + addPattern(); + startNewMode(Tag, ++m_pos); + } + } else if (m_mode == QuotedName && c == '"') + addPattern(); + else if (m_mode == Tag && c == ']') + addPattern(); + } + void startNewMode(Mode mode, std::size_t start) { + m_mode = mode; + m_start = start; + } + std::string subString() const { return m_arg.substr(m_start, m_pos - m_start); } + template + void addPattern() { + std::string token = subString(); + if (startsWith(token, "exclude:")) { + m_exclusion = true; + token = token.substr(8); + } + if (!token.empty()) { + Ptr pattern = new T(token); + if (m_exclusion) + pattern = new TestSpec::ExcludedPattern(pattern); + m_currentFilter.m_patterns.push_back(pattern); + } + m_exclusion = false; + m_mode = None; + } + void addFilter() { + if (!m_currentFilter.m_patterns.empty()) { + m_testSpec.m_filters.push_back(m_currentFilter); + m_currentFilter = TestSpec::Filter(); + } + } +}; +inline TestSpec parseTestSpec(std::string const& arg) { + return TestSpecParser(ITagAliasRegistry::get()).parse(arg).testSpec(); +} + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// #included from: catch_interfaces_config.h +#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED + +#include +#include +#include + +namespace Catch { + +struct Verbosity { + enum Level { + NoOutput = 0, + Quiet, + Normal + }; +}; + +struct WarnAbout { + enum What { + Nothing = 0x00, + NoAssertions = 0x01 + }; +}; + +struct ShowDurations { + enum OrNot { + DefaultForReporter, + Always, + Never + }; +}; +struct RunTests { + enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; +}; +struct UseColour { + enum YesOrNo { + Auto, + Yes, + No + }; +}; + +class TestSpec; + +struct IConfig : IShared { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; +}; +} + +// #included from: catch_stream.h +#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED + +// #included from: catch_streambuf.h +#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED + +#include + +namespace Catch { + +class StreamBufBase : public std::streambuf { +public: + virtual ~StreamBufBase() CATCH_NOEXCEPT; +}; +} + +#include +#include +#include + +namespace Catch { + +std::ostream& cout(); +std::ostream& cerr(); + +struct IStream { + virtual ~IStream() CATCH_NOEXCEPT; + virtual std::ostream& stream() const = 0; +}; + +class FileStream : public IStream { + mutable std::ofstream m_ofs; + +public: + FileStream(std::string const& filename); + virtual ~FileStream() CATCH_NOEXCEPT; + +public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; +}; + +class CoutStream : public IStream { + mutable std::ostream m_os; + +public: + CoutStream(); + virtual ~CoutStream() CATCH_NOEXCEPT; + +public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; +}; + +class DebugOutStream : public IStream { + CATCH_AUTO_PTR(StreamBufBase) + m_streamBuf; + mutable std::ostream m_os; + +public: + DebugOutStream(); + virtual ~DebugOutStream() CATCH_NOEXCEPT; + +public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; +}; +} + +#include +#include +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + +struct ConfigData { + + ConfigData() + : listTests(false) + , listTags(false) + , listReporters(false) + , listTestNamesOnly(false) + , showSuccessfulTests(false) + , shouldDebugBreak(false) + , noThrow(false) + , showHelp(false) + , showInvisibles(false) + , filenamesAsTags(false) + , abortAfter(-1) + , rngSeed(0) + , verbosity(Verbosity::Normal) + , warnings(WarnAbout::Nothing) + , showDurations(ShowDurations::DefaultForReporter) + , runOrder(RunTests::InDeclarationOrder) + , useColour(UseColour::Auto) {} + + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; + + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + bool showInvisibles; + bool filenamesAsTags; + + int abortAfter; + unsigned int rngSeed; + + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + RunTests::InWhatOrder runOrder; + UseColour::YesOrNo useColour; + + std::string outputFilename; + std::string name; + std::string processName; + + std::vector reporterNames; + std::vector testsOrTags; +}; + +class Config : public SharedImpl { +private: + Config(Config const& other); + Config& operator=(Config const& other); + virtual void dummy(); + +public: + Config() {} + + Config(ConfigData const& data) + : m_data(data) + , m_stream(openStream()) { + if (!data.testsOrTags.empty()) { + TestSpecParser parser(ITagAliasRegistry::get()); + for (std::size_t i = 0; i < data.testsOrTags.size(); ++i) + parser.parse(data.testsOrTags[i]); + m_testSpec = parser.testSpec(); + } + } + + virtual ~Config() { + } + + std::string const& getFilename() const { + return m_data.outputFilename; + } + + bool listTests() const { return m_data.listTests; } + bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool listTags() const { return m_data.listTags; } + bool listReporters() const { return m_data.listReporters; } + + std::string getProcessName() const { return m_data.processName; } + + bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } + + std::vector getReporterNames() const { return m_data.reporterNames; } + + int abortAfter() const { return m_data.abortAfter; } + + TestSpec const& testSpec() const { return m_testSpec; } + + bool showHelp() const { return m_data.showHelp; } + bool showInvisibles() const { return m_data.showInvisibles; } + + // IConfig interface + virtual bool allowThrows() const { return !m_data.noThrow; } + virtual std::ostream& stream() const { return m_stream->stream(); } + virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } + virtual unsigned int rngSeed() const { return m_data.rngSeed; } + virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } + +private: + IStream const* openStream() { + if (m_data.outputFilename.empty()) + return new CoutStream(); + else if (m_data.outputFilename[0] == '%') { + if (m_data.outputFilename == "%debug") + return new DebugOutStream(); + else + throw std::domain_error("Unrecognised stream: " + m_data.outputFilename); + } else + return new FileStream(m_data.outputFilename); + } + ConfigData m_data; + + CATCH_AUTO_PTR(IStream const) + m_stream; + TestSpec m_testSpec; +}; + +} // end namespace Catch + +// #included from: catch_clara.h +#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#undef CLARA_CONFIG_CONSOLE_WIDTH +#endif +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +// Declare Clara inside the Catch namespace +#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { +// #included from: ../external/clara.h + +// Version 0.0.2.4 + +// Only use header guard if we are not using an outer namespace +#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + +#ifndef STITCH_CLARA_OPEN_NAMESPACE +#define TWOBLUECUBES_CLARA_H_INCLUDED +#define STITCH_CLARA_OPEN_NAMESPACE +#define STITCH_CLARA_CLOSE_NAMESPACE +#else +#define STITCH_CLARA_CLOSE_NAMESPACE } +#endif + +#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + +// ----------- #included from tbc_text_format.h ----------- + +// Only use header guard if we are not using an outer namespace +#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) +#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#define TBC_TEXT_FORMAT_H_INCLUDED +#endif + +#include +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else +const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent(std::string::npos) + , indent(0) + , width(consoleWidth - 1) + , tabChar('\t') {} + + TextAttributes& setInitialIndent(std::size_t _value) { + initialIndent = _value; + return *this; + } + TextAttributes& setIndent(std::size_t _value) { + indent = _value; + return *this; + } + TextAttributes& setWidth(std::size_t _value) { + width = _value; + return *this; + } + TextAttributes& setTabChar(char _value) { + tabChar = _value; + return *this; + } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text(std::string const& _str, TextAttributes const& _attr = TextAttributes()) + : attr(_attr) { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while (!remainder.empty()) { + if (lines.size() >= 1000) { + lines.push_back("... message truncated due to excessive size"); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)(remainder.size(), _attr.width - indent); + std::size_t pos = remainder.find_first_of('\n'); + if (pos <= width) { + width = pos; + } + pos = remainder.find_last_of(_attr.tabChar, width); + if (pos != std::string::npos) { + tabPos = pos; + if (remainder[width] == '\n') + width--; + remainder = remainder.substr(0, tabPos) + remainder.substr(tabPos + 1); + } + + if (width == remainder.size()) { + spliceLine(indent, remainder, width); + } else if (remainder[width] == '\n') { + spliceLine(indent, remainder, width); + if (width <= 1 || remainder.size() != 1) + remainder = remainder.substr(1); + indent = _attr.indent; + } else { + pos = remainder.find_last_of(wrappableChars, width); + if (pos != std::string::npos && pos > 0) { + spliceLine(indent, remainder, pos); + if (remainder[0] == ' ') + remainder = remainder.substr(1); + } else { + spliceLine(indent, remainder, width - 1); + lines.back() += "-"; + } + if (lines.size() == 1) + indent = _attr.indent; + if (tabPos != std::string::npos) + indent += tabPos; + } + } + } + + void spliceLine(std::size_t _indent, std::string& _remainder, std::size_t _pos) { + lines.push_back(std::string(_indent, ' ') + _remainder.substr(0, _pos)); + _remainder = _remainder.substr(_pos); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[](std::size_t _index) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator<<(std::ostream& _stream, Text const& _text) { + for (Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it) { + if (it != _text.begin()) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TBC_TEXT_FORMAT_H_INCLUDED + +// ----------- end of #include from tbc_text_format.h ----------- +// ........... back in clara.h + +#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE + +// ----------- #included from clara_compilers.h ----------- + +#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED +#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? +// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? + +// In general each macro has a _NO_ form +// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 + +#ifdef __clang__ + +#if __has_feature(cxx_nullptr) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#if __has_feature(cxx_noexcept) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900) // (VC++ 13 (VS2015)) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(__cplusplus) && __cplusplus >= 201103L + +#define CLARA_CPP11_OR_GREATER + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) +#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE +#endif +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NULLPTR +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_UNIQUE_PTR +#endif + +// noexcept support: +#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) +#define CLARA_NOEXCEPT noexcept +#define CLARA_NOEXCEPT_IS(x) noexcept(x) +#else +#define CLARA_NOEXCEPT throw() +#define CLARA_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CLARA_CONFIG_CPP11_NULLPTR +#define CLARA_NULL nullptr +#else +#define CLARA_NULL NULL +#endif + +// override support +#ifdef CLARA_CONFIG_CPP11_OVERRIDE +#define CLARA_OVERRIDE override +#else +#define CLARA_OVERRIDE +#endif + +// unique_ptr support +#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR +#define CLARA_AUTO_PTR(T) std::unique_ptr +#else +#define CLARA_AUTO_PTR(T) std::auto_ptr +#endif + +#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// ----------- end of #include from clara_compilers.h ----------- +// ........... back in clara.h + +#include +#include +#include + +#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CLARA_PLATFORM_WINDOWS +#endif + +// Use optional outer namespace +#ifdef STITCH_CLARA_OPEN_NAMESPACE +STITCH_CLARA_OPEN_NAMESPACE +#endif + +namespace Clara { + +struct UnpositionalTag {}; + +extern UnpositionalTag _; + +#ifdef CLARA_CONFIG_MAIN +UnpositionalTag _; +#endif + +namespace Detail { + +#ifdef CLARA_CONSOLE_WIDTH + const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + using namespace Tbc; + + inline bool startsWith(std::string const& str, std::string const& prefix) { + return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix; + } + + template + struct RemoveConstRef { typedef T type; }; + template + struct RemoveConstRef { typedef T type; }; + template + struct RemoveConstRef { typedef T type; }; + template + struct RemoveConstRef { typedef T type; }; + + template + struct IsBool { static const bool value = false; }; + template <> + struct IsBool { static const bool value = true; }; + + template + void convertInto(std::string const& _source, T& _dest) { + std::stringstream ss; + ss << _source; + ss >> _dest; + if (ss.fail()) + throw std::runtime_error("Unable to convert " + _source + " to destination type"); + } + inline void convertInto(std::string const& _source, std::string& _dest) { + _dest = _source; + } + inline void convertInto(std::string const& _source, bool& _dest) { + std::string sourceLC = _source; + std::transform(sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower); + if (sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on") + _dest = true; + else if (sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off") + _dest = false; + else + throw std::runtime_error("Expected a boolean value but did not recognise:\n '" + _source + "'"); + } + + template + struct IArgFunction { + virtual ~IArgFunction() {} +#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS + IArgFunction() = default; + IArgFunction(IArgFunction const&) = default; +#endif + virtual void set(ConfigT& config, std::string const& value) const = 0; + virtual bool takesArg() const = 0; + virtual IArgFunction* clone() const = 0; + }; + + template + class BoundArgFunction { + public: + BoundArgFunction() + : functionObj(CLARA_NULL) {} + BoundArgFunction(IArgFunction* _functionObj) + : functionObj(_functionObj) {} + BoundArgFunction(BoundArgFunction const& other) + : functionObj(other.functionObj ? other.functionObj->clone() : CLARA_NULL) {} + BoundArgFunction& operator=(BoundArgFunction const& other) { + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; + delete functionObj; + functionObj = newFunctionObj; + return *this; + } + ~BoundArgFunction() { delete functionObj; } + + void set(ConfigT& config, std::string const& value) const { + functionObj->set(config, value); + } + bool takesArg() const { return functionObj->takesArg(); } + + bool isSet() const { + return functionObj != CLARA_NULL; + } + + private: + IArgFunction* functionObj; + }; + + template + struct NullBinder : IArgFunction { + virtual void set(C&, std::string const&) const {} + virtual bool takesArg() const { return true; } + virtual IArgFunction* clone() const { return new NullBinder(*this); } + }; + + template + struct BoundDataMember : IArgFunction { + BoundDataMember(M C::*_member) + : member(_member) {} + virtual void set(C& p, std::string const& stringValue) const { + convertInto(stringValue, p.*member); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundDataMember(*this); } + M C::*member; + }; + template + struct BoundUnaryMethod : IArgFunction { + BoundUnaryMethod(void (C::*_member)(M)) + : member(_member) {} + virtual void set(C& p, std::string const& stringValue) const { + typename RemoveConstRef::type value; + convertInto(stringValue, value); + (p.*member)(value); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundUnaryMethod(*this); } + void (C::*member)(M); + }; + template + struct BoundNullaryMethod : IArgFunction { + BoundNullaryMethod(void (C::*_member)()) + : member(_member) {} + virtual void set(C& p, std::string const& stringValue) const { + bool value; + convertInto(stringValue, value); + if (value) + (p.*member)(); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundNullaryMethod(*this); } + void (C::*member)(); + }; + + template + struct BoundUnaryFunction : IArgFunction { + BoundUnaryFunction(void (*_function)(C&)) + : function(_function) {} + virtual void set(C& obj, std::string const& stringValue) const { + bool value; + convertInto(stringValue, value); + if (value) + function(obj); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundUnaryFunction(*this); } + void (*function)(C&); + }; + + template + struct BoundBinaryFunction : IArgFunction { + BoundBinaryFunction(void (*_function)(C&, T)) + : function(_function) {} + virtual void set(C& obj, std::string const& stringValue) const { + typename RemoveConstRef::type value; + convertInto(stringValue, value); + function(obj, value); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundBinaryFunction(*this); } + void (*function)(C&, T); + }; + +} // namespace Detail + +inline std::vector argsToVector(int argc, char const* const* const argv) { + std::vector args(static_cast(argc)); + for (std::size_t i = 0; i < static_cast(argc); ++i) + args[i] = argv[i]; + + return args; +} + +class Parser { + enum Mode { None, + MaybeShortOpt, + SlashOpt, + ShortOpt, + LongOpt, + Positional }; + Mode mode; + std::size_t from; + bool inQuotes; + +public: + struct Token { + enum Type { Positional, + ShortOpt, + LongOpt }; + Token(Type _type, std::string const& _data) + : type(_type) + , data(_data) {} + Type type; + std::string data; + }; + + Parser() + : mode(None) + , from(0) + , inQuotes(false) {} + + void parseIntoTokens(std::vector const& args, std::vector& tokens) { + const std::string doubleDash = "--"; + for (std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i) + parseIntoTokens(args[i], tokens); + } + + void parseIntoTokens(std::string const& arg, std::vector& tokens) { + for (std::size_t i = 0; i <= arg.size(); ++i) { + char c = arg[i]; + if (c == '"') + inQuotes = !inQuotes; + mode = handleMode(i, c, arg, tokens); + } + } + Mode handleMode(std::size_t i, char c, std::string const& arg, std::vector& tokens) { + switch (mode) { + case None: + return handleNone(i, c); + case MaybeShortOpt: + return handleMaybeShortOpt(i, c); + case ShortOpt: + case LongOpt: + case SlashOpt: + return handleOpt(i, c, arg, tokens); + case Positional: + return handlePositional(i, c, arg, tokens); + default: + throw std::logic_error("Unknown mode"); + } + } + + Mode handleNone(std::size_t i, char c) { + if (inQuotes) { + from = i; + return Positional; + } + switch (c) { + case '-': + return MaybeShortOpt; +#ifdef CLARA_PLATFORM_WINDOWS + case '/': + from = i + 1; + return SlashOpt; +#endif + default: + from = i; + return Positional; + } + } + Mode handleMaybeShortOpt(std::size_t i, char c) { + switch (c) { + case '-': + from = i + 1; + return LongOpt; + default: + from = i; + return ShortOpt; + } + } + Mode handleOpt(std::size_t i, char c, std::string const& arg, std::vector& tokens) { + if (std::string(":=\0", 3).find(c) == std::string::npos) + return mode; + + std::string optName = arg.substr(from, i - from); + if (mode == ShortOpt) + for (std::size_t j = 0; j < optName.size(); ++j) + tokens.push_back(Token(Token::ShortOpt, optName.substr(j, 1))); + else if (mode == SlashOpt && optName.size() == 1) + tokens.push_back(Token(Token::ShortOpt, optName)); + else + tokens.push_back(Token(Token::LongOpt, optName)); + return None; + } + Mode handlePositional(std::size_t i, char c, std::string const& arg, std::vector& tokens) { + if (inQuotes || std::string("\0", 1).find(c) == std::string::npos) + return mode; + + std::string data = arg.substr(from, i - from); + tokens.push_back(Token(Token::Positional, data)); + return None; + } +}; + +template +struct CommonArgProperties { + CommonArgProperties() {} + CommonArgProperties(Detail::BoundArgFunction const& _boundField) + : boundField(_boundField) {} + + Detail::BoundArgFunction boundField; + std::string description; + std::string detail; + std::string placeholder; // Only value if boundField takes an arg + + bool takesArg() const { + return !placeholder.empty(); + } + void validate() const { + if (!boundField.isSet()) + throw std::logic_error("option not bound"); + } +}; +struct OptionArgProperties { + std::vector shortNames; + std::string longName; + + bool hasShortName(std::string const& shortName) const { + return std::find(shortNames.begin(), shortNames.end(), shortName) != shortNames.end(); + } + bool hasLongName(std::string const& _longName) const { + return _longName == longName; + } +}; +struct PositionalArgProperties { + PositionalArgProperties() + : position(-1) {} + int position; // -1 means non-positional (floating) + + bool isFixedPositional() const { + return position != -1; + } +}; + +template +class CommandLine { + + struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { + Arg() {} + Arg(Detail::BoundArgFunction const& _boundField) + : CommonArgProperties(_boundField) {} + + using CommonArgProperties::placeholder; // !TBD + + std::string dbgName() const { + if (!longName.empty()) + return "--" + longName; + if (!shortNames.empty()) + return "-" + shortNames[0]; + return "positional args"; + } + std::string commands() const { + std::ostringstream oss; + bool first = true; + std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); + for (; it != itEnd; ++it) { + if (first) + first = false; + else + oss << ", "; + oss << "-" << *it; + } + if (!longName.empty()) { + if (!first) + oss << ", "; + oss << "--" << longName; + } + if (!placeholder.empty()) + oss << " <" << placeholder << ">"; + return oss.str(); + } + }; + + typedef CLARA_AUTO_PTR(Arg) ArgAutoPtr; + + friend void addOptName(Arg& arg, std::string const& optName) { + if (optName.empty()) + return; + if (Detail::startsWith(optName, "--")) { + if (!arg.longName.empty()) + throw std::logic_error("Only one long opt may be specified. '" + + arg.longName + + "' already specified, now attempting to add '" + + optName + "'"); + arg.longName = optName.substr(2); + } else if (Detail::startsWith(optName, "-")) + arg.shortNames.push_back(optName.substr(1)); + else + throw std::logic_error("option must begin with - or --. Option was: '" + optName + "'"); + } + friend void setPositionalArg(Arg& arg, int position) { + arg.position = position; + } + + class ArgBuilder { + public: + ArgBuilder(Arg* arg) + : m_arg(arg) {} + + // Bind a non-boolean data member (requires placeholder string) + template + void bind(M C::*field, std::string const& placeholder) { + m_arg->boundField = new Detail::BoundDataMember(field); + m_arg->placeholder = placeholder; + } + // Bind a boolean data member (no placeholder required) + template + void bind(bool C::*field) { + m_arg->boundField = new Detail::BoundDataMember(field); + } + + // Bind a method taking a single, non-boolean argument (requires a placeholder string) + template + void bind(void (C::*unaryMethod)(M), std::string const& placeholder) { + m_arg->boundField = new Detail::BoundUnaryMethod(unaryMethod); + m_arg->placeholder = placeholder; + } + + // Bind a method taking a single, boolean argument (no placeholder string required) + template + void bind(void (C::*unaryMethod)(bool)) { + m_arg->boundField = new Detail::BoundUnaryMethod(unaryMethod); + } + + // Bind a method that takes no arguments (will be called if opt is present) + template + void bind(void (C::*nullaryMethod)()) { + m_arg->boundField = new Detail::BoundNullaryMethod(nullaryMethod); + } + + // Bind a free function taking a single argument - the object to operate on (no placeholder string required) + template + void bind(void (*unaryFunction)(C&)) { + m_arg->boundField = new Detail::BoundUnaryFunction(unaryFunction); + } + + // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) + template + void bind(void (*binaryFunction)(C&, T), std::string const& placeholder) { + m_arg->boundField = new Detail::BoundBinaryFunction(binaryFunction); + m_arg->placeholder = placeholder; + } + + ArgBuilder& describe(std::string const& description) { + m_arg->description = description; + return *this; + } + ArgBuilder& detail(std::string const& detail) { + m_arg->detail = detail; + return *this; + } + + protected: + Arg* m_arg; + }; + + class OptBuilder : public ArgBuilder { + public: + OptBuilder(Arg* arg) + : ArgBuilder(arg) {} + OptBuilder(OptBuilder& other) + : ArgBuilder(other) {} + + OptBuilder& operator[](std::string const& optName) { + addOptName(*ArgBuilder::m_arg, optName); + return *this; + } + }; + +public: + CommandLine() + : m_boundProcessName(new Detail::NullBinder()) + , m_highestSpecifiedArgPosition(0) + , m_throwOnUnrecognisedTokens(false) {} + CommandLine(CommandLine const& other) + : m_boundProcessName(other.m_boundProcessName) + , m_options(other.m_options) + , m_positionalArgs(other.m_positionalArgs) + , m_highestSpecifiedArgPosition(other.m_highestSpecifiedArgPosition) + , m_throwOnUnrecognisedTokens(other.m_throwOnUnrecognisedTokens) { + if (other.m_floatingArg.get()) + m_floatingArg.reset(new Arg(*other.m_floatingArg)); + } + + CommandLine& setThrowOnUnrecognisedTokens(bool shouldThrow = true) { + m_throwOnUnrecognisedTokens = shouldThrow; + return *this; + } + + OptBuilder operator[](std::string const& optName) { + m_options.push_back(Arg()); + addOptName(m_options.back(), optName); + OptBuilder builder(&m_options.back()); + return builder; + } + + ArgBuilder operator[](int position) { + m_positionalArgs.insert(std::make_pair(position, Arg())); + if (position > m_highestSpecifiedArgPosition) + m_highestSpecifiedArgPosition = position; + setPositionalArg(m_positionalArgs[position], position); + ArgBuilder builder(&m_positionalArgs[position]); + return builder; + } + + // Invoke this with the _ instance + ArgBuilder operator[](UnpositionalTag) { + if (m_floatingArg.get()) + throw std::logic_error("Only one unpositional argument can be added"); + m_floatingArg.reset(new Arg()); + ArgBuilder builder(m_floatingArg.get()); + return builder; + } + + template + void bindProcessName(M C::*field) { + m_boundProcessName = new Detail::BoundDataMember(field); + } + template + void bindProcessName(void (C::*_unaryMethod)(M)) { + m_boundProcessName = new Detail::BoundUnaryMethod(_unaryMethod); + } + + void optUsage(std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth) const { + typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; + std::size_t maxWidth = 0; + for (it = itBegin; it != itEnd; ++it) + maxWidth = (std::max)(maxWidth, it->commands().size()); + + for (it = itBegin; it != itEnd; ++it) { + Detail::Text usage(it->commands(), Detail::TextAttributes() + .setWidth(maxWidth + indent) + .setIndent(indent)); + Detail::Text desc(it->description, Detail::TextAttributes() + .setWidth(width - maxWidth - 3)); + + for (std::size_t i = 0; i < (std::max)(usage.size(), desc.size()); ++i) { + std::string usageCol = i < usage.size() ? usage[i] : ""; + os << usageCol; + + if (i < desc.size() && !desc[i].empty()) + os << std::string(indent + 2 + maxWidth - usageCol.size(), ' ') + << desc[i]; + os << "\n"; + } + } + } + std::string optUsage() const { + std::ostringstream oss; + optUsage(oss); + return oss.str(); + } + + void argSynopsis(std::ostream& os) const { + for (int i = 1; i <= m_highestSpecifiedArgPosition; ++i) { + if (i > 1) + os << " "; + typename std::map::const_iterator it = m_positionalArgs.find(i); + if (it != m_positionalArgs.end()) + os << "<" << it->second.placeholder << ">"; + else if (m_floatingArg.get()) + os << "<" << m_floatingArg->placeholder << ">"; + else + throw std::logic_error("non consecutive positional arguments with no floating args"); + } + // !TBD No indication of mandatory args + if (m_floatingArg.get()) { + if (m_highestSpecifiedArgPosition > 1) + os << " "; + os << "[<" << m_floatingArg->placeholder << "> ...]"; + } + } + std::string argSynopsis() const { + std::ostringstream oss; + argSynopsis(oss); + return oss.str(); + } + + void usage(std::ostream& os, std::string const& procName) const { + validate(); + os << "usage:\n " << procName << " "; + argSynopsis(os); + if (!m_options.empty()) { + os << " [options]\n\nwhere options are: \n"; + optUsage(os, 2); + } + os << "\n"; + } + std::string usage(std::string const& procName) const { + std::ostringstream oss; + usage(oss, procName); + return oss.str(); + } + + ConfigT parse(std::vector const& args) const { + ConfigT config; + parseInto(args, config); + return config; + } + + std::vector parseInto(std::vector const& args, ConfigT& config) const { + std::string processName = args[0]; + std::size_t lastSlash = processName.find_last_of("/\\"); + if (lastSlash != std::string::npos) + processName = processName.substr(lastSlash + 1); + m_boundProcessName.set(config, processName); + std::vector tokens; + Parser parser; + parser.parseIntoTokens(args, tokens); + return populate(tokens, config); + } + + std::vector populate(std::vector const& tokens, ConfigT& config) const { + validate(); + std::vector unusedTokens = populateOptions(tokens, config); + unusedTokens = populateFixedArgs(unusedTokens, config); + unusedTokens = populateFloatingArgs(unusedTokens, config); + return unusedTokens; + } + + std::vector populateOptions(std::vector const& tokens, ConfigT& config) const { + std::vector unusedTokens; + std::vector errors; + for (std::size_t i = 0; i < tokens.size(); ++i) { + Parser::Token const& token = tokens[i]; + typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); + for (; it != itEnd; ++it) { + Arg const& arg = *it; + + try { + if ((token.type == Parser::Token::ShortOpt && arg.hasShortName(token.data)) || (token.type == Parser::Token::LongOpt && arg.hasLongName(token.data))) { + if (arg.takesArg()) { + if (i == tokens.size() - 1 || tokens[i + 1].type != Parser::Token::Positional) + errors.push_back("Expected argument to option: " + token.data); + else + arg.boundField.set(config, tokens[++i].data); + } else { + arg.boundField.set(config, "true"); + } + break; + } + } catch (std::exception& ex) { + errors.push_back(std::string(ex.what()) + "\n- while parsing: (" + arg.commands() + ")"); + } + } + if (it == itEnd) { + if (token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens) + unusedTokens.push_back(token); + else if (errors.empty() && m_throwOnUnrecognisedTokens) + errors.push_back("unrecognised option: " + token.data); + } + } + if (!errors.empty()) { + std::ostringstream oss; + for (std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it) { + if (it != errors.begin()) + oss << "\n"; + oss << *it; + } + throw std::runtime_error(oss.str()); + } + return unusedTokens; + } + std::vector populateFixedArgs(std::vector const& tokens, ConfigT& config) const { + std::vector unusedTokens; + int position = 1; + for (std::size_t i = 0; i < tokens.size(); ++i) { + Parser::Token const& token = tokens[i]; + typename std::map::const_iterator it = m_positionalArgs.find(position); + if (it != m_positionalArgs.end()) + it->second.boundField.set(config, token.data); + else + unusedTokens.push_back(token); + if (token.type == Parser::Token::Positional) + position++; + } + return unusedTokens; + } + std::vector populateFloatingArgs(std::vector const& tokens, ConfigT& config) const { + if (!m_floatingArg.get()) + return tokens; + std::vector unusedTokens; + for (std::size_t i = 0; i < tokens.size(); ++i) { + Parser::Token const& token = tokens[i]; + if (token.type == Parser::Token::Positional) + m_floatingArg->boundField.set(config, token.data); + else + unusedTokens.push_back(token); + } + return unusedTokens; + } + + void validate() const { + if (m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get()) + throw std::logic_error("No options or arguments specified"); + + for (typename std::vector::const_iterator it = m_options.begin(), + itEnd = m_options.end(); + it != itEnd; ++it) + it->validate(); + } + +private: + Detail::BoundArgFunction m_boundProcessName; + std::vector m_options; + std::map m_positionalArgs; + ArgAutoPtr m_floatingArg; + int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; +}; + +} // end namespace Clara + +STITCH_CLARA_CLOSE_NAMESPACE +#undef STITCH_CLARA_OPEN_NAMESPACE +#undef STITCH_CLARA_CLOSE_NAMESPACE + +#endif // TWOBLUECUBES_CLARA_H_INCLUDED +#undef STITCH_CLARA_OPEN_NAMESPACE + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#include + +namespace Catch { + +inline void abortAfterFirst(ConfigData& config) { config.abortAfter = 1; } +inline void abortAfterX(ConfigData& config, int x) { + if (x < 1) + throw std::runtime_error("Value after -x or --abortAfter must be greater than zero"); + config.abortAfter = x; +} +inline void addTestOrTags(ConfigData& config, std::string const& _testSpec) { config.testsOrTags.push_back(_testSpec); } +inline void addReporterName(ConfigData& config, std::string const& _reporterName) { config.reporterNames.push_back(_reporterName); } + +inline void addWarning(ConfigData& config, std::string const& _warning) { + if (_warning == "NoAssertions") + config.warnings = static_cast(config.warnings | WarnAbout::NoAssertions); + else + throw std::runtime_error("Unrecognised warning: '" + _warning + "'"); +} +inline void setOrder(ConfigData& config, std::string const& order) { + if (startsWith("declared", order)) + config.runOrder = RunTests::InDeclarationOrder; + else if (startsWith("lexical", order)) + config.runOrder = RunTests::InLexicographicalOrder; + else if (startsWith("random", order)) + config.runOrder = RunTests::InRandomOrder; + else + throw std::runtime_error("Unrecognised ordering: '" + order + "'"); +} +inline void setRngSeed(ConfigData& config, std::string const& seed) { + if (seed == "time") { + config.rngSeed = static_cast(std::time(0)); + } else { + std::stringstream ss; + ss << seed; + ss >> config.rngSeed; + if (ss.fail()) + throw std::runtime_error("Argment to --rng-seed should be the word 'time' or a number"); + } +} +inline void setVerbosity(ConfigData& config, int level) { + // !TBD: accept strings? + config.verbosity = static_cast(level); +} +inline void setShowDurations(ConfigData& config, bool _showDurations) { + config.showDurations = _showDurations + ? ShowDurations::Always + : ShowDurations::Never; +} +inline void setUseColour(ConfigData& config, std::string const& value) { + std::string mode = toLower(value); + + if (mode == "yes") + config.useColour = UseColour::Yes; + else if (mode == "no") + config.useColour = UseColour::No; + else if (mode == "auto") + config.useColour = UseColour::Auto; + else + throw std::runtime_error("colour mode must be one of: auto, yes or no"); +} +inline void forceColour(ConfigData& config) { + config.useColour = UseColour::Yes; +} +inline void loadTestNamesFromFile(ConfigData& config, std::string const& _filename) { + std::ifstream f(_filename.c_str()); + if (!f.is_open()) + throw std::domain_error("Unable to load input file: " + _filename); + + std::string line; + while (std::getline(f, line)) { + line = trim(line); + if (!line.empty() && !startsWith(line, "#")) + addTestOrTags(config, "\"" + line + "\","); + } +} + +inline Clara::CommandLine makeCommandLineParser() { + + using namespace Clara; + CommandLine cli; + + cli.bindProcessName(&ConfigData::processName); + + cli["-?"]["-h"]["--help"] + .describe("display usage information") + .bind(&ConfigData::showHelp); + + cli["-l"]["--list-tests"] + .describe("list all/matching test cases") + .bind(&ConfigData::listTests); + + cli["-t"]["--list-tags"] + .describe("list all/matching tags") + .bind(&ConfigData::listTags); + + cli["-s"]["--success"] + .describe("include successful tests in output") + .bind(&ConfigData::showSuccessfulTests); + + cli["-b"]["--break"] + .describe("break into debugger on failure") + .bind(&ConfigData::shouldDebugBreak); + + cli["-e"]["--nothrow"] + .describe("skip exception tests") + .bind(&ConfigData::noThrow); + + cli["-i"]["--invisibles"] + .describe("show invisibles (tabs, newlines)") + .bind(&ConfigData::showInvisibles); + + cli["-o"]["--out"] + .describe("output filename") + .bind(&ConfigData::outputFilename, "filename"); + + cli["-r"]["--reporter"] + // .placeholder( "name[:filename]" ) + .describe("reporter to use (defaults to console)") + .bind(&addReporterName, "name"); + + cli["-n"]["--name"] + .describe("suite name") + .bind(&ConfigData::name, "name"); + + cli["-a"]["--abort"] + .describe("abort at first failure") + .bind(&abortAfterFirst); + + cli["-x"]["--abortx"] + .describe("abort after x failures") + .bind(&abortAfterX, "no. failures"); + + cli["-w"]["--warn"] + .describe("enable warnings") + .bind(&addWarning, "warning name"); + + // - needs updating if reinstated + // cli.into( &setVerbosity ) + // .describe( "level of verbosity (0=no output)" ) + // .shortOpt( "v") + // .longOpt( "verbosity" ) + // .placeholder( "level" ); + + cli[_] + .describe("which test or tests to use") + .bind(&addTestOrTags, "test name, pattern or tags"); + + cli["-d"]["--durations"] + .describe("show test durations") + .bind(&setShowDurations, "yes|no"); + + cli["-f"]["--input-file"] + .describe("load test names to run from a file") + .bind(&loadTestNamesFromFile, "filename"); + + cli["-#"]["--filenames-as-tags"] + .describe("adds a tag for the filename") + .bind(&ConfigData::filenamesAsTags); + + // Less common commands which don't have a short form + cli["--list-test-names-only"] + .describe("list all/matching test cases names only") + .bind(&ConfigData::listTestNamesOnly); + + cli["--list-reporters"] + .describe("list all reporters") + .bind(&ConfigData::listReporters); + + cli["--order"] + .describe("test case order (defaults to decl)") + .bind(&setOrder, "decl|lex|rand"); + + cli["--rng-seed"] + .describe("set a specific seed for random numbers") + .bind(&setRngSeed, "'time'|number"); + + cli["--force-colour"] + .describe("force colourised output (deprecated)") + .bind(&forceColour); + + cli["--use-colour"] + .describe("should output be colourised") + .bind(&setUseColour, "yes|no"); + + return cli; +} + +} // end namespace Catch + +// #included from: internal/catch_list.hpp +#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED + +// #included from: catch_text.h +#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + +#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch +// #included from: ../external/tbc_text_format.h +// Only use header guard if we are not using an outer namespace +#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#endif +#else +#define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +#endif +#endif +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#include +#include +#include + +// Use optional outer namespace +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else +const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent(std::string::npos) + , indent(0) + , width(consoleWidth - 1) + , tabChar('\t') {} + + TextAttributes& setInitialIndent(std::size_t _value) { + initialIndent = _value; + return *this; + } + TextAttributes& setIndent(std::size_t _value) { + indent = _value; + return *this; + } + TextAttributes& setWidth(std::size_t _value) { + width = _value; + return *this; + } + TextAttributes& setTabChar(char _value) { + tabChar = _value; + return *this; + } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text(std::string const& _str, TextAttributes const& _attr = TextAttributes()) + : attr(_attr) { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while (!remainder.empty()) { + if (lines.size() >= 1000) { + lines.push_back("... message truncated due to excessive size"); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)(remainder.size(), _attr.width - indent); + std::size_t pos = remainder.find_first_of('\n'); + if (pos <= width) { + width = pos; + } + pos = remainder.find_last_of(_attr.tabChar, width); + if (pos != std::string::npos) { + tabPos = pos; + if (remainder[width] == '\n') + width--; + remainder = remainder.substr(0, tabPos) + remainder.substr(tabPos + 1); + } + + if (width == remainder.size()) { + spliceLine(indent, remainder, width); + } else if (remainder[width] == '\n') { + spliceLine(indent, remainder, width); + if (width <= 1 || remainder.size() != 1) + remainder = remainder.substr(1); + indent = _attr.indent; + } else { + pos = remainder.find_last_of(wrappableChars, width); + if (pos != std::string::npos && pos > 0) { + spliceLine(indent, remainder, pos); + if (remainder[0] == ' ') + remainder = remainder.substr(1); + } else { + spliceLine(indent, remainder, width - 1); + lines.back() += "-"; + } + if (lines.size() == 1) + indent = _attr.indent; + if (tabPos != std::string::npos) + indent += tabPos; + } + } + } + + void spliceLine(std::size_t _indent, std::string& _remainder, std::size_t _pos) { + lines.push_back(std::string(_indent, ' ') + _remainder.substr(0, _pos)); + _remainder = _remainder.substr(_pos); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[](std::size_t _index) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator<<(std::ostream& _stream, Text const& _text) { + for (Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it) { + if (it != _text.begin()) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE + +namespace Catch { +using Tbc::Text; +using Tbc::TextAttributes; +} + +// #included from: catch_console_colour.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + +namespace Catch { + +struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + + // By intention + FileName = LightGrey, + Warning = Yellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour(Code _colourCode); + Colour(Colour const& other); + ~Colour(); + + // Use static method for one-shot changes + static void use(Code _colourCode); + +private: + bool m_moved; +}; + +inline std::ostream& operator<<(std::ostream& os, Colour const&) { return os; } + +} // end namespace Catch + +// #included from: catch_interfaces_reporter.h +#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED + +#include +#include +#include +#include + +namespace Catch { +struct ReporterConfig { + explicit ReporterConfig(Ptr const& _fullConfig) + : m_stream(&_fullConfig->stream()) + , m_fullConfig(_fullConfig) {} + + ReporterConfig(Ptr const& _fullConfig, std::ostream& _stream) + : m_stream(&_stream) + , m_fullConfig(_fullConfig) {} + + std::ostream& stream() const { return *m_stream; } + Ptr fullConfig() const { return m_fullConfig; } + +private: + std::ostream* m_stream; + Ptr m_fullConfig; +}; + +struct ReporterPreferences { + ReporterPreferences() + : shouldRedirectStdOut(false) {} + + bool shouldRedirectStdOut; +}; + +template +struct LazyStat : Option { + LazyStat() + : used(false) {} + LazyStat& operator=(T const& _value) { + Option::operator=(_value); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used; +}; + +struct TestRunInfo { + TestRunInfo(std::string const& _name) + : name(_name) {} + std::string name; +}; +struct GroupInfo { + GroupInfo(std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount) + : name(_name) + , groupIndex(_groupIndex) + , groupsCounts(_groupsCount) {} + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; +}; + +struct AssertionStats { + AssertionStats(AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals) + : assertionResult(_assertionResult) + , infoMessages(_infoMessages) + , totals(_totals) { + if (assertionResult.hasMessage()) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder(assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType()); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back(builder.m_info); + } + } + virtual ~AssertionStats(); + +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionStats(AssertionStats const&) = default; + AssertionStats(AssertionStats&&) = default; + AssertionStats& operator=(AssertionStats const&) = default; + AssertionStats& operator=(AssertionStats&&) = default; +#endif + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; +}; + +struct SectionStats { + SectionStats(SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions) + : sectionInfo(_sectionInfo) + , assertions(_assertions) + , durationInSeconds(_durationInSeconds) + , missingAssertions(_missingAssertions) {} + virtual ~SectionStats(); +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SectionStats(SectionStats const&) = default; + SectionStats(SectionStats&&) = default; + SectionStats& operator=(SectionStats const&) = default; + SectionStats& operator=(SectionStats&&) = default; +#endif + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; +}; + +struct TestCaseStats { + TestCaseStats(TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting) + : testInfo(_testInfo) + , totals(_totals) + , stdOut(_stdOut) + , stdErr(_stdErr) + , aborting(_aborting) {} + virtual ~TestCaseStats(); + +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestCaseStats(TestCaseStats const&) = default; + TestCaseStats(TestCaseStats&&) = default; + TestCaseStats& operator=(TestCaseStats const&) = default; + TestCaseStats& operator=(TestCaseStats&&) = default; +#endif + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; +}; + +struct TestGroupStats { + TestGroupStats(GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting) + : groupInfo(_groupInfo) + , totals(_totals) + , aborting(_aborting) {} + TestGroupStats(GroupInfo const& _groupInfo) + : groupInfo(_groupInfo) + , aborting(false) {} + virtual ~TestGroupStats(); + +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestGroupStats(TestGroupStats const&) = default; + TestGroupStats(TestGroupStats&&) = default; + TestGroupStats& operator=(TestGroupStats const&) = default; + TestGroupStats& operator=(TestGroupStats&&) = default; +#endif + + GroupInfo groupInfo; + Totals totals; + bool aborting; +}; + +struct TestRunStats { + TestRunStats(TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting) + : runInfo(_runInfo) + , totals(_totals) + , aborting(_aborting) {} + virtual ~TestRunStats(); + +#ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestRunStats(TestRunStats const& _other) + : runInfo(_other.runInfo) + , totals(_other.totals) + , aborting(_other.aborting) {} +#else + TestRunStats(TestRunStats const&) = default; + TestRunStats(TestRunStats&&) = default; + TestRunStats& operator=(TestRunStats const&) = default; + TestRunStats& operator=(TestRunStats&&) = default; +#endif + + TestRunInfo runInfo; + Totals totals; + bool aborting; +}; + +class MultipleReporters; + +struct IStreamingReporter : IShared { + virtual ~IStreamingReporter(); + + // Implementing class must also provide the following static method: + // static std::string getDescription(); + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases(std::string const& spec) = 0; + + virtual void testRunStarting(TestRunInfo const& testRunInfo) = 0; + virtual void testGroupStarting(GroupInfo const& groupInfo) = 0; + + virtual void testCaseStarting(TestCaseInfo const& testInfo) = 0; + virtual void sectionStarting(SectionInfo const& sectionInfo) = 0; + + virtual void assertionStarting(AssertionInfo const& assertionInfo) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded(AssertionStats const& assertionStats) = 0; + + virtual void sectionEnded(SectionStats const& sectionStats) = 0; + virtual void testCaseEnded(TestCaseStats const& testCaseStats) = 0; + virtual void testGroupEnded(TestGroupStats const& testGroupStats) = 0; + virtual void testRunEnded(TestRunStats const& testRunStats) = 0; + + virtual void skipTest(TestCaseInfo const& testInfo) = 0; + + virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } +}; + +struct IReporterFactory : IShared { + virtual ~IReporterFactory(); + virtual IStreamingReporter* create(ReporterConfig const& config) const = 0; + virtual std::string getDescription() const = 0; +}; + +struct IReporterRegistry { + typedef std::map> FactoryMap; + typedef std::vector> Listeners; + + virtual ~IReporterRegistry(); + virtual IStreamingReporter* create(std::string const& name, Ptr const& config) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; +}; + +Ptr addReporter(Ptr const& existingReporter, Ptr const& additionalReporter); +} + +#include +#include + +namespace Catch { + +inline std::size_t listTests(Config const& config) { + + TestSpec testSpec = config.testSpec(); + if (config.testSpec().hasFilters()) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("*").testSpec(); + } + + std::size_t matchedTests = 0; + TextAttributes nameAttr, tagsAttr; + nameAttr.setInitialIndent(2).setIndent(4); + tagsAttr.setIndent(6); + + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard(colour); + + Catch::cout() << Text(testCaseInfo.name, nameAttr) << std::endl; + if (!testCaseInfo.tags.empty()) + Catch::cout() << Text(testCaseInfo.tagsAsString, tagsAttr) << std::endl; + } + + if (!config.testSpec().hasFilters()) + Catch::cout() << pluralise(matchedTests, "test case") << "\n" << std::endl; + else + Catch::cout() << pluralise(matchedTests, "matching test case") << "\n" << std::endl; + return matchedTests; +} + +inline std::size_t listTestsNamesOnly(Config const& config) { + TestSpec testSpec = config.testSpec(); + if (!config.testSpec().hasFilters()) + testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("*").testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Catch::cout() << testCaseInfo.name << std::endl; + } + return matchedTests; +} + +struct TagInfo { + TagInfo() + : count(0) {} + void add(std::string const& spelling) { + ++count; + spellings.insert(spelling); + } + std::string all() const { + std::string out; + for (std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); + it != itEnd; + ++it) + out += "[" + *it + "]"; + return out; + } + std::set spellings; + std::size_t count; +}; + +inline std::size_t listTags(Config const& config) { + TestSpec testSpec = config.testSpec(); + if (config.testSpec().hasFilters()) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("*").testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it) { + for (std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt) { + std::string tagName = *tagIt; + std::string lcaseTagName = toLower(tagName); + std::map::iterator countIt = tagCounts.find(lcaseTagName); + if (countIt == tagCounts.end()) + countIt = tagCounts.insert(std::make_pair(lcaseTagName, TagInfo())).first; + countIt->second.add(tagName); + } + } + + for (std::map::const_iterator countIt = tagCounts.begin(), + countItEnd = tagCounts.end(); + countIt != countItEnd; + ++countIt) { + std::ostringstream oss; + oss << " " << std::setw(2) << countIt->second.count << " "; + Text wrapper(countIt->second.all(), TextAttributes() + .setInitialIndent(0) + .setIndent(oss.str().size()) + .setWidth(CATCH_CONFIG_CONSOLE_WIDTH - 10)); + Catch::cout() << oss.str() << wrapper << "\n"; + } + Catch::cout() << pluralise(tagCounts.size(), "tag") << "\n" << std::endl; + return tagCounts.size(); +} + +inline std::size_t listReporters(Config const& /*config*/) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; + std::size_t maxNameLen = 0; + for (it = itBegin; it != itEnd; ++it) + maxNameLen = (std::max)(maxNameLen, it->first.size()); + + for (it = itBegin; it != itEnd; ++it) { + Text wrapper(it->second->getDescription(), TextAttributes() + .setInitialIndent(0) + .setIndent(7 + maxNameLen) + .setWidth(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8)); + Catch::cout() << " " + << it->first + << ":" + << std::string(maxNameLen - it->first.size() + 2, ' ') + << wrapper << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); +} + +inline Option list(Config const& config) { + Option listedCount; + if (config.listTests()) + listedCount = listedCount.valueOr(0) + listTests(config); + if (config.listTestNamesOnly()) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly(config); + if (config.listTags()) + listedCount = listedCount.valueOr(0) + listTags(config); + if (config.listReporters()) + listedCount = listedCount.valueOr(0) + listReporters(config); + return listedCount; +} + +} // end namespace Catch + +// #included from: internal/catch_run_context.hpp +#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED + +// #included from: catch_test_case_tracker.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct ITracker : SharedImpl<> { + virtual ~ITracker(); + + // static queries + virtual std::string name() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild(Ptr const& child) = 0; + virtual ITracker* findChild(std::string const& name) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + Ptr m_rootTracker; + ITracker* m_currentTracker; + RunState m_runState; + + public: + static TrackerContext& instance() { + static TrackerContext s_instance; + return s_instance; + } + + TrackerContext() + : m_currentTracker(CATCH_NULL) + , m_runState(NotStarted) {} + + ITracker& startRun(); + + void endRun() { + m_rootTracker.reset(); + m_currentTracker = CATCH_NULL; + m_runState = NotStarted; + } + + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void completeCycle() { + m_runState = CompletedCycle; + } + + bool completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& currentTracker() { + return *m_currentTracker; + } + void setCurrentTracker(ITracker* tracker) { + m_currentTracker = tracker; + } + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + class TrackerHasName { + std::string m_name; + + public: + TrackerHasName(std::string const& name) + : m_name(name) {} + bool operator()(Ptr const& tracker) { + return tracker->name() == m_name; + } + }; + typedef std::vector> Children; + std::string m_name; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState; + + public: + TrackerBase(std::string const& name, TrackerContext& ctx, ITracker* parent) + : m_name(name) + , m_ctx(ctx) + , m_parent(parent) + , m_runState(NotStarted) {} + virtual ~TrackerBase(); + + virtual std::string name() const CATCH_OVERRIDE { + return m_name; + } + virtual bool isComplete() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully; + } + virtual bool isOpen() const CATCH_OVERRIDE { + return m_runState != NotStarted && !isComplete(); + } + virtual bool hasChildren() const CATCH_OVERRIDE { + return !m_children.empty(); + } + + virtual void addChild(Ptr const& child) CATCH_OVERRIDE { + m_children.push_back(child); + } + + virtual ITracker* findChild(std::string const& name) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if(m_children.begin(), m_children.end(), TrackerHasName(name)); + return (it != m_children.end()) + ? it->get() + : CATCH_NULL; + } + virtual ITracker& parent() CATCH_OVERRIDE { + assert(m_parent); // Should always be non-null except for root + return *m_parent; + } + + virtual void openChild() CATCH_OVERRIDE { + if (m_runState != ExecutingChildren) { + m_runState = ExecutingChildren; + if (m_parent) + m_parent->openChild(); + } + } + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } + virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + + void open() { + m_runState = Executing; + moveToThis(); + if (m_parent) + m_parent->openChild(); + } + + virtual void close() CATCH_OVERRIDE { + + // Close any still open children (e.g. generators) + while (&m_ctx.currentTracker() != this) + m_ctx.currentTracker().close(); + + switch (m_runState) { + case NotStarted: + case CompletedSuccessfully: + case Failed: + throw std::logic_error("Illogical state"); + + case NeedsAnotherRun: + break; + ; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if (m_children.empty() || m_children.back()->isComplete()) + m_runState = CompletedSuccessfully; + break; + + default: + throw std::logic_error("Unexpected state"); + } + moveToParent(); + m_ctx.completeCycle(); + } + virtual void fail() CATCH_OVERRIDE { + m_runState = Failed; + if (m_parent) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { + m_runState = NeedsAnotherRun; + } + + private: + void moveToParent() { + assert(m_parent); + m_ctx.setCurrentTracker(m_parent); + } + void moveToThis() { + m_ctx.setCurrentTracker(this); + } + }; + + class SectionTracker : public TrackerBase { + public: + SectionTracker(std::string const& name, TrackerContext& ctx, ITracker* parent) + : TrackerBase(name, ctx, parent) {} + virtual ~SectionTracker(); + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + + static SectionTracker& acquire(TrackerContext& ctx, std::string const& name) { + SectionTracker* section = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if (ITracker* childTracker = currentTracker.findChild(name)) { + assert(childTracker); + assert(childTracker->isSectionTracker()); + section = static_cast(childTracker); + } else { + section = new SectionTracker(name, ctx, ¤tTracker); + currentTracker.addChild(section); + } + if (!ctx.completedCycle() && !section->isComplete()) { + + section->open(); + } + return *section; + } + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index; + + public: + IndexTracker(std::string const& name, TrackerContext& ctx, ITracker* parent, int size) + : TrackerBase(name, ctx, parent) + , m_size(size) + , m_index(-1) {} + virtual ~IndexTracker(); + + virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } + + static IndexTracker& acquire(TrackerContext& ctx, std::string const& name, int size) { + IndexTracker* tracker = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if (ITracker* childTracker = currentTracker.findChild(name)) { + assert(childTracker); + assert(childTracker->isIndexTracker()); + tracker = static_cast(childTracker); + } else { + tracker = new IndexTracker(name, ctx, ¤tTracker, size); + currentTracker.addChild(tracker); + } + + if (!ctx.completedCycle() && !tracker->isComplete()) { + if (tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int index() const { return m_index; } + + void moveNext() { + m_index++; + m_children.clear(); + } + + virtual void close() CATCH_OVERRIDE { + TrackerBase::close(); + if (m_runState == CompletedSuccessfully && m_index < m_size - 1) + m_runState = Executing; + } + }; + + inline ITracker& TrackerContext::startRun() { + m_rootTracker = new SectionTracker("{root}", *this, CATCH_NULL); + m_currentTracker = CATCH_NULL; + m_runState = Executing; + return *m_rootTracker; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +// #included from: catch_fatal_condition.hpp +#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED + +namespace Catch { + +// Report the error condition then exit the process +inline void fatal(std::string const& message, int exitCode) { + IContext& context = Catch::getCurrentContext(); + IResultCapture* resultCapture = context.getResultCapture(); + resultCapture->handleFatalErrorCondition(message); + + if (Catch::alwaysTrue()) // avoids "no return" warnings + exit(exitCode); +} + +} // namespace Catch + +#if defined(CATCH_PLATFORM_WINDOWS) ///////////////////////////////////////// + +namespace Catch { + +struct FatalConditionHandler { + void reset() {} +}; + +} // namespace Catch + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +#include + +namespace Catch { + +struct SignalDefs { + int id; + const char* name; +}; +extern SignalDefs signalDefs[]; +SignalDefs signalDefs[] = { + {SIGINT, "SIGINT - Terminal interrupt signal"}, + {SIGILL, "SIGILL - Illegal instruction signal"}, + {SIGFPE, "SIGFPE - Floating point error signal"}, + {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, + {SIGTERM, "SIGTERM - Termination request signal"}, + {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; + +struct FatalConditionHandler { + + static void handleSignal(int sig) { + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) + if (sig == signalDefs[i].id) + fatal(signalDefs[i].name, -sig); + fatal("", -sig); + } + + FatalConditionHandler() + : m_isSet(true) { + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) + signal(signalDefs[i].id, handleSignal); + } + ~FatalConditionHandler() { + reset(); + } + void reset() { + if (m_isSet) { + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) + signal(signalDefs[i].id, SIG_DFL); + m_isSet = false; + } + } + + bool m_isSet; +}; + +} // namespace Catch + +#endif // not Windows + +#include +#include + +namespace Catch { + +class StreamRedirect { + +public: + StreamRedirect(std::ostream& stream, std::string& targetString) + : m_stream(stream) + , m_prevBuf(stream.rdbuf()) + , m_targetString(targetString) { + stream.rdbuf(m_oss.rdbuf()); + } + + ~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf(m_prevBuf); + } + +private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream m_oss; + std::string& m_targetString; +}; + +/////////////////////////////////////////////////////////////////////////// + +class RunContext : public IResultCapture, public IRunner { + + RunContext(RunContext const&); + void operator=(RunContext const&); + +public: + explicit RunContext(Ptr const& _config, Ptr const& reporter) + : m_runInfo(_config->name()) + , m_context(getCurrentMutableContext()) + , m_activeTestCase(CATCH_NULL) + , m_config(_config) + , m_reporter(reporter) { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + virtual ~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + void testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + do { + m_trackerContext.startRun(); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, testInfo.name); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + } + // !TBD: deprecated - this will be replaced by indexed trackers + while (getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = CATCH_NULL; + m_testCaseTracker = CATCH_NULL; + + return deltaTotals; + } + + Ptr config() const { + return m_config; + } + +private: // IResultCapture + virtual void assertionEnded(AssertionResult const& result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + } else if (!result.isOk()) { + m_totals.assertions.failed++; + } + + if (m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))) + m_messages.clear(); + + // Reset working state + m_lastAssertionInfo = AssertionInfo("", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}", m_lastAssertionInfo.resultDisposition); + m_lastResult = result; + } + + virtual bool sectionStarted( + SectionInfo const& sectionInfo, + Counts& assertions) { + std::ostringstream oss; + oss << sectionInfo.name << "@" << sectionInfo.lineInfo; + + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, oss.str()); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + bool testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + virtual void sectionEnded(SectionEndInfo const& endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + } + + virtual void sectionEndedEarly(SectionEndInfo const& endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + + virtual void pushScopedMessage(MessageInfo const& message) { + m_messages.push_back(message); + } + + virtual void popScopedMessage(MessageInfo const& message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + virtual std::string getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : ""; + } + + virtual const AssertionResult* getLastResult() const { + return &m_lastResult; + } + + virtual void handleFatalErrorCondition(std::string const& message) { + ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); + resultBuilder.setResultType(ResultWas::FatalErrorCondition); + resultBuilder << message; + resultBuilder.captureExpression(); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + "", + "", + false)); + m_totals.testCases.failed++; + testGroupEnded("", m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + +public: + // !TBD We need to do this another way! + bool aborting() const { + return m_totals.assertions.failed == static_cast(m_config->abortAfter()); + } + +private: + void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr) { + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + try { + m_lastAssertionInfo = AssertionInfo("TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal); + + seedRng(*m_config); + + Timer timer; + timer.start(); + if (m_reporter->getPreferences().shouldRedirectStdOut) { + StreamRedirect coutRedir(Catch::cout(), redirectedCout); + StreamRedirect cerrRedir(Catch::cerr(), redirectedCerr); + invokeActiveTestCase(); + } else { + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } catch (TestFailureException&) { + // This just means the test was aborted due to failure + } catch (...) { + makeUnexpectedResultBuilder().useActiveException(); + } + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (testCaseInfo.okToFail()) { + std::swap(assertions.failedButOk, assertions.failed); + m_totals.assertions.failed -= assertions.failedButOk; + m_totals.assertions.failedButOk += assertions.failedButOk; + } + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + +private: + ResultBuilder makeUnexpectedResultBuilder() const { + return ResultBuilder(m_lastAssertionInfo.macroName.c_str(), + m_lastAssertionInfo.lineInfo, + m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.resultDisposition); + } + + void handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase; + ITracker* m_testCaseTracker; + ITracker* m_currentSectionTracker; + AssertionResult m_lastResult; + + Ptr m_config; + Totals m_totals; + Ptr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; +}; + +IResultCapture& getResultCapture() { + if (IResultCapture* capture = getCurrentContext().getResultCapture()) + return *capture; + else + throw std::logic_error("No result capture instance"); +} + +} // end namespace Catch + +// #included from: internal/catch_version.h +#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED + +namespace Catch { + +// Versioning information +struct Version { + Version(unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + std::string const& _branchName, + unsigned int _buildNumber); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + std::string const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator<<(std::ostream& os, Version const& version); + +private: + void operator=(Version const&); +}; + +extern Version libraryVersion; +} + +#include +#include +#include + +namespace Catch { + +Ptr createReporter(std::string const& reporterName, Ptr const& config) { + Ptr reporter = getRegistryHub().getReporterRegistry().create(reporterName, config.get()); + if (!reporter) { + std::ostringstream oss; + oss << "No reporter registered with name: '" << reporterName << "'"; + throw std::domain_error(oss.str()); + } + return reporter; +} + +Ptr makeReporter(Ptr const& config) { + std::vector reporters = config->getReporterNames(); + if (reporters.empty()) + reporters.push_back("console"); + + Ptr reporter; + for (std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); + it != itEnd; + ++it) + reporter = addReporter(reporter, createReporter(*it, config)); + return reporter; +} +Ptr addListeners(Ptr const& config, Ptr reporters) { + IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); + for (IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); + it != itEnd; + ++it) + reporters = addReporter(reporters, (*it)->create(ReporterConfig(config))); + return reporters; +} + +Totals runTests(Ptr const& config) { + + Ptr iconfig = config.get(); + + Ptr reporter = makeReporter(config); + reporter = addListeners(iconfig, reporter); + + RunContext context(iconfig, reporter); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + if (!testSpec.hasFilters()) + testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests + + std::vector const& allTestCases = getAllTestCasesSorted(*iconfig); + for (std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); + it != itEnd; + ++it) { + if (!context.aborting() && matchTest(*it, testSpec, *iconfig)) + totals += context.runTest(*it); + else + reporter->skipTest(*it); + } + + context.testGroupEnded(iconfig->name(), totals, 1, 1); + return totals; +} + +void applyFilenamesAsTags(IConfig const& config) { + std::vector const& tests = getAllTestCasesSorted(config); + for (std::size_t i = 0; i < tests.size(); ++i) { + TestCase& test = const_cast(tests[i]); + std::set tags = test.tags; + + std::string filename = test.lineInfo.file; + std::string::size_type lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) + filename = filename.substr(lastSlash + 1); + + std::string::size_type lastDot = filename.find_last_of("."); + if (lastDot != std::string::npos) + filename = filename.substr(0, lastDot); + + tags.insert("#" + filename); + setTags(test, tags); + } +} + +class Session : NonCopyable { + static bool alreadyInstantiated; + +public: + struct OnUnusedOptions { + enum DoWhat { Ignore, + Fail }; + }; + + Session() + : m_cli(makeCommandLineParser()) { + if (alreadyInstantiated) { + std::string msg = "Only one instance of Catch::Session can ever be used"; + Catch::cerr() << msg << std::endl; + throw std::logic_error(msg); + } + alreadyInstantiated = true; + } + ~Session() { + Catch::cleanUp(); + } + + void showHelp(std::string const& processName) { + Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + + m_cli.usage(Catch::cout(), processName); + Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; + } + + int applyCommandLine(int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail) { + try { + m_cli.setThrowOnUnrecognisedTokens(unusedOptionBehaviour == OnUnusedOptions::Fail); + m_unusedTokens = m_cli.parseInto(Clara::argsToVector(argc, argv), m_configData); + if (m_configData.showHelp) + showHelp(m_configData.processName); + m_config.reset(); + } catch (std::exception& ex) { + { + Colour colourGuard(Colour::Red); + Catch::cerr() + << "\nError(s) in input:\n" + << Text(ex.what(), TextAttributes().setIndent(2)) + << "\n\n"; + } + m_cli.usage(Catch::cout(), m_configData.processName); + return (std::numeric_limits::max)(); + } + return 0; + } + + void useConfigData(ConfigData const& _configData) { + m_configData = _configData; + m_config.reset(); + } + + int run(int argc, char const* const* const argv) { + + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } + + int run() { + if (m_configData.showHelp) + return 0; + + try { + config(); // Force config to be constructed + + seedRng(*m_config); + + if (m_configData.filenamesAsTags) + applyFilenamesAsTags(*m_config); + + // Handle list request + if (Option listed = list(config())) + return static_cast(*listed); + + return static_cast(runTests(m_config).assertions.failed); + } catch (std::exception& ex) { + Catch::cerr() << ex.what() << std::endl; + return (std::numeric_limits::max)(); + } + } + + Clara::CommandLine const& cli() const { + return m_cli; + } + std::vector const& unusedTokens() const { + return m_unusedTokens; + } + ConfigData& configData() { + return m_configData; + } + Config& config() { + if (!m_config) + m_config = new Config(m_configData); + return *m_config; + } + +private: + Clara::CommandLine m_cli; + std::vector m_unusedTokens; + ConfigData m_configData; + Ptr m_config; +}; + +bool Session::alreadyInstantiated = false; + +} // end namespace Catch + +// #included from: catch_registry_hub.hpp +#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED + +// #included from: catch_test_case_registry_impl.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef CATCH_CPP14_OR_GREATER +#include +#endif + +namespace Catch { + +struct RandomNumberGenerator { + typedef int result_type; + + result_type operator()(result_type n) const { return std::rand() % n; } + +#ifdef CATCH_CPP14_OR_GREATER + static constexpr result_type min() { return 0; } + static constexpr result_type max() { return 1000000; } + result_type operator()() const { return std::rand() % max(); } +#endif + template + static void shuffle(V& vector) { + RandomNumberGenerator rng; +#ifdef CATCH_CPP14_OR_GREATER + std::shuffle(vector.begin(), vector.end(), rng); +#else + std::random_shuffle(vector.begin(), vector.end(), rng); +#endif + } +}; + +inline std::vector sortTests(IConfig const& config, std::vector const& unsortedTestCases) { + + std::vector sorted = unsortedTestCases; + + switch (config.runOrder()) { + case RunTests::InLexicographicalOrder: + std::sort(sorted.begin(), sorted.end()); + break; + case RunTests::InRandomOrder: { + seedRng(config); + RandomNumberGenerator::shuffle(sorted); + } break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; +} +bool matchTest(TestCase const& testCase, TestSpec const& testSpec, IConfig const& config) { + return testSpec.matches(testCase) && (config.allowThrows() || !testCase.throws()); +} + +void enforceNoDuplicateTestCases(std::vector const& functions) { + std::set seenFunctions; + for (std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); + it != itEnd; + ++it) { + std::pair::const_iterator, bool> prev = seenFunctions.insert(*it); + if (!prev.second) { + std::ostringstream ss; + + ss << Colour(Colour::Red) + << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + + throw std::runtime_error(ss.str()); + } + } +} + +std::vector filterTests(std::vector const& testCases, TestSpec const& testSpec, IConfig const& config) { + std::vector filtered; + filtered.reserve(testCases.size()); + for (std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); + it != itEnd; + ++it) + if (matchTest(*it, testSpec, config)) + filtered.push_back(*it); + return filtered; +} +std::vector const& getAllTestCasesSorted(IConfig const& config) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted(config); +} + +class TestRegistry : public ITestCaseRegistry { +public: + TestRegistry() + : m_currentSortOrder(RunTests::InDeclarationOrder) + , m_unnamedCount(0) {} + virtual ~TestRegistry(); + + virtual void registerTest(TestCase const& testCase) { + std::string name = testCase.getTestCaseInfo().name; + if (name == "") { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest(testCase.withName(oss.str())); + } + m_functions.push_back(testCase); + } + + virtual std::vector const& getAllTests() const { + return m_functions; + } + virtual std::vector const& getAllTestsSorted(IConfig const& config) const { + if (m_sortedFunctions.empty()) + enforceNoDuplicateTestCases(m_functions); + + if (m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty()) { + m_sortedFunctions = sortTests(config, m_functions); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + +private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder; + mutable std::vector m_sortedFunctions; + size_t m_unnamedCount; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised +}; + +/////////////////////////////////////////////////////////////////////////// + +class FreeFunctionTestCase : public SharedImpl { +public: + FreeFunctionTestCase(TestFunction fun) + : m_fun(fun) {} + + virtual void invoke() const { + m_fun(); + } + +private: + virtual ~FreeFunctionTestCase(); + + TestFunction m_fun; +}; + +inline std::string extractClassName(std::string const& classOrQualifiedMethodName) { + std::string className = classOrQualifiedMethodName; + if (startsWith(className, "&")) { + std::size_t lastColons = className.rfind("::"); + std::size_t penultimateColons = className.rfind("::", lastColons - 1); + if (penultimateColons == std::string::npos) + penultimateColons = 1; + className = className.substr(penultimateColons, lastColons - penultimateColons); + } + return className; +} + +void registerTestCase(ITestCase* testCase, + char const* classOrQualifiedMethodName, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo) { + + getMutableRegistryHub().registerTest(makeTestCase(testCase, + extractClassName(classOrQualifiedMethodName), + nameAndDesc.name, + nameAndDesc.description, + lineInfo)); +} +void registerTestCaseFunction(TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc) { + registerTestCase(new FreeFunctionTestCase(function), "", nameAndDesc, lineInfo); +} + +/////////////////////////////////////////////////////////////////////////// + +AutoReg::AutoReg(TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc) { + registerTestCaseFunction(function, lineInfo, nameAndDesc); +} + +AutoReg::~AutoReg() {} + +} // end namespace Catch + +// #included from: catch_reporter_registry.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED + +#include + +namespace Catch { + +class ReporterRegistry : public IReporterRegistry { + +public: + virtual ~ReporterRegistry() CATCH_OVERRIDE {} + + virtual IStreamingReporter* create(std::string const& name, Ptr const& config) const CATCH_OVERRIDE { + FactoryMap::const_iterator it = m_factories.find(name); + if (it == m_factories.end()) + return CATCH_NULL; + return it->second->create(ReporterConfig(config)); + } + + void registerReporter(std::string const& name, Ptr const& factory) { + m_factories.insert(std::make_pair(name, factory)); + } + void registerListener(Ptr const& factory) { + m_listeners.push_back(factory); + } + + virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { + return m_factories; + } + virtual Listeners const& getListeners() const CATCH_OVERRIDE { + return m_listeners; + } + +private: + FactoryMap m_factories; + Listeners m_listeners; +}; +} + +// #included from: catch_exception_translator_registry.hpp +#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED + +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + +class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { +public: + ~ExceptionTranslatorRegistry() { + deleteAll(m_translators); + } + + virtual void registerTranslator(const IExceptionTranslator* translator) { + m_translators.push_back(translator); + } + + virtual std::string translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException* exception) { + return Catch::toString([exception description]); + } +#else + return tryTranslators(); +#endif + } catch (TestFailureException&) { + throw; + } catch (std::exception& ex) { + return ex.what(); + } catch (std::string& msg) { + return msg; + } catch (const char* msg) { + return msg; + } catch (...) { + return "Unknown exception"; + } + } + + std::string tryTranslators() const { + if (m_translators.empty()) + throw; + else + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); + } + +private: + std::vector m_translators; +}; +} + +namespace Catch { + +namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + + RegistryHub(RegistryHub const&); + void operator=(RegistryHub const&); + + public: // IRegistryHub + RegistryHub() { + } + virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { + return m_reporterRegistry; + } + virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { + return m_testCaseRegistry; + } + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { + return m_exceptionTranslatorRegistry; + } + + public: // IMutableRegistryHub + virtual void registerReporter(std::string const& name, Ptr const& factory) CATCH_OVERRIDE { + m_reporterRegistry.registerReporter(name, factory); + } + virtual void registerListener(Ptr const& factory) CATCH_OVERRIDE { + m_reporterRegistry.registerListener(factory); + } + virtual void registerTest(TestCase const& testInfo) CATCH_OVERRIDE { + m_testCaseRegistry.registerTest(testInfo); + } + virtual void registerTranslator(const IExceptionTranslator* translator) CATCH_OVERRIDE { + m_exceptionTranslatorRegistry.registerTranslator(translator); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + }; + + // Single, global, instance + inline RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = CATCH_NULL; + if (!theRegistryHub) + theRegistryHub = new RegistryHub(); + return theRegistryHub; + } +} + +IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); +} +IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); +} +void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = CATCH_NULL; + cleanUpContext(); +} +std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); +} + +} // end namespace Catch + +// #included from: catch_notimplemented_exception.hpp +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED + +#include + +namespace Catch { + +NotImplementedException::NotImplementedException(SourceLineInfo const& lineInfo) + : m_lineInfo(lineInfo) { + std::ostringstream oss; + oss << lineInfo << ": function "; + oss << "not implemented"; + m_what = oss.str(); +} + +const char* NotImplementedException::what() const CATCH_NOEXCEPT { + return m_what.c_str(); +} + +} // end namespace Catch + +// #included from: catch_context_impl.hpp +#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED + +// #included from: catch_stream.hpp +#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + +template +class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; + +public: + StreamBufImpl() { + setp(data, data + sizeof(data)); + } + + ~StreamBufImpl() CATCH_NOEXCEPT { + sync(); + } + +private: + int overflow(int c) { + sync(); + + if (c != EOF) { + if (pbase() == epptr()) + m_writer(std::string(1, static_cast(c))); + else + sputc(static_cast(c)); + } + return 0; + } + + int sync() { + if (pbase() != pptr()) { + m_writer(std::string(pbase(), static_cast(pptr() - pbase()))); + setp(pbase(), epptr()); + } + return 0; + } +}; + +/////////////////////////////////////////////////////////////////////////// + +FileStream::FileStream(std::string const& filename) { + m_ofs.open(filename.c_str()); + if (m_ofs.fail()) { + std::ostringstream oss; + oss << "Unable to open file: '" << filename << "'"; + throw std::domain_error(oss.str()); + } +} + +std::ostream& FileStream::stream() const { + return m_ofs; +} + +struct OutputDebugWriter { + + void operator()(std::string const& str) { + writeToDebugConsole(str); + } +}; + +DebugOutStream::DebugOutStream() + : m_streamBuf(new StreamBufImpl()) + , m_os(m_streamBuf.get()) {} + +std::ostream& DebugOutStream::stream() const { + return m_os; +} + +// Store the streambuf from cout up-front because +// cout may get redirected when running tests +CoutStream::CoutStream() + : m_os(Catch::cout().rdbuf()) {} + +std::ostream& CoutStream::stream() const { + return m_os; +} + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions +std::ostream& cout() { + return std::cout; +} +std::ostream& cerr() { + return std::cerr; +} +#endif +} + +namespace Catch { + +class Context : public IMutableContext { + + Context() + : m_config(CATCH_NULL) + , m_runner(CATCH_NULL) + , m_resultCapture(CATCH_NULL) {} + Context(Context const&); + void operator=(Context const&); + +public: // IContext + virtual IResultCapture* getResultCapture() { + return m_resultCapture; + } + virtual IRunner* getRunner() { + return m_runner; + } + virtual size_t getGeneratorIndex(std::string const& fileInfo, size_t totalSize) { + return getGeneratorsForCurrentTest() + .getGeneratorInfo(fileInfo, totalSize) + .getCurrentIndex(); + } + virtual bool advanceGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + return generators && generators->moveNext(); + } + + virtual Ptr getConfig() const { + return m_config; + } + +public: // IMutableContext + virtual void setResultCapture(IResultCapture* resultCapture) { + m_resultCapture = resultCapture; + } + virtual void setRunner(IRunner* runner) { + m_runner = runner; + } + virtual void setConfig(Ptr const& config) { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + +private: + IGeneratorsForTest* findGeneratorsForCurrentTest() { + std::string testName = getResultCapture()->getCurrentTestName(); + + std::map::const_iterator it = m_generatorsByTestName.find(testName); + return it != m_generatorsByTestName.end() + ? it->second + : CATCH_NULL; + } + + IGeneratorsForTest& getGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + if (!generators) { + std::string testName = getResultCapture()->getCurrentTestName(); + generators = createGeneratorsForTest(); + m_generatorsByTestName.insert(std::make_pair(testName, generators)); + } + return *generators; + } + +private: + Ptr m_config; + IRunner* m_runner; + IResultCapture* m_resultCapture; + std::map m_generatorsByTestName; +}; + +namespace { + Context* currentContext = CATCH_NULL; +} +IMutableContext& getCurrentMutableContext() { + if (!currentContext) + currentContext = new Context(); + return *currentContext; +} +IContext& getCurrentContext() { + return getCurrentMutableContext(); +} + +void cleanUpContext() { + delete currentContext; + currentContext = CATCH_NULL; +} +} + +// #included from: catch_console_colour_impl.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED + +namespace Catch { +namespace { + + struct IColourImpl { + virtual ~IColourImpl() {} + virtual void use(Colour::Code _colourCode) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use(Colour::Code) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + +} // anon namespace +} // namespace Catch + +#if !defined(CATCH_CONFIG_COLOUR_NONE) && !defined(CATCH_CONFIG_COLOUR_WINDOWS) && !defined(CATCH_CONFIG_COLOUR_ANSI) +#ifdef CATCH_PLATFORM_WINDOWS +#define CATCH_CONFIG_COLOUR_WINDOWS +#else +#define CATCH_CONFIG_COLOUR_ANSI +#endif +#endif + +#if defined(CATCH_CONFIG_COLOUR_WINDOWS) ///////////////////////////////////////// + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() + : stdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE)) { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); + originalForegroundAttributes = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); + originalBackgroundAttributes = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + } + + virtual void use(Colour::Code _colourCode) { + switch (_colourCode) { + case Colour::None: + return setTextAttribute(originalForegroundAttributes); + case Colour::White: + return setTextAttribute(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); + case Colour::Red: + return setTextAttribute(FOREGROUND_RED); + case Colour::Green: + return setTextAttribute(FOREGROUND_GREEN); + case Colour::Blue: + return setTextAttribute(FOREGROUND_BLUE); + case Colour::Cyan: + return setTextAttribute(FOREGROUND_BLUE | FOREGROUND_GREEN); + case Colour::Yellow: + return setTextAttribute(FOREGROUND_RED | FOREGROUND_GREEN); + case Colour::Grey: + return setTextAttribute(0); + + case Colour::LightGrey: + return setTextAttribute(FOREGROUND_INTENSITY); + case Colour::BrightRed: + return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_RED); + case Colour::BrightGreen: + return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_GREEN); + case Colour::BrightWhite: + return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); + + case Colour::Bright: + throw std::logic_error("not a colour"); + } + } + + private: + void setTextAttribute(WORD _textAttribute) { + SetConsoleTextAttribute(stdoutHandle, _textAttribute | originalBackgroundAttributes); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if (colourMode == UseColour::Auto) + colourMode = !isDebuggerActive() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined(CATCH_CONFIG_COLOUR_ANSI) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use(Colour::Code _colourCode) { + switch (_colourCode) { + case Colour::None: + case Colour::White: + return setColour("[0m"); + case Colour::Red: + return setColour("[0;31m"); + case Colour::Green: + return setColour("[0;32m"); + case Colour::Blue: + return setColour("[0:34m"); + case Colour::Cyan: + return setColour("[0;36m"); + case Colour::Yellow: + return setColour("[0;33m"); + case Colour::Grey: + return setColour("[1;30m"); + + case Colour::LightGrey: + return setColour("[0;37m"); + case Colour::BrightRed: + return setColour("[1;31m"); + case Colour::BrightGreen: + return setColour("[1;32m"); + case Colour::BrightWhite: + return setColour("[1;37m"); + + case Colour::Bright: + throw std::logic_error("not a colour"); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour(const char* _escapeCode) { + Catch::cout() << '\033' << _escapeCode; + } + }; + + IColourImpl* platformColourInstance() { + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if (colourMode == UseColour::Auto) + colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO)) + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + +static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + +Colour::Colour(Code _colourCode) + : m_moved(false) { use(_colourCode); } +Colour::Colour(Colour const& _other) + : m_moved(false) { const_cast(_other).m_moved = true; } +Colour::~Colour() { + if (!m_moved) + use(None); +} + +void Colour::use(Code _colourCode) { + static IColourImpl* impl = platformColourInstance(); + impl->use(_colourCode); +} + +} // end namespace Catch + +// #included from: catch_generators_impl.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + +struct GeneratorInfo : IGeneratorInfo { + + GeneratorInfo(std::size_t size) + : m_size(size) + , m_currentIndex(0) {} + + bool moveNext() { + if (++m_currentIndex == m_size) { + m_currentIndex = 0; + return false; + } + return true; + } + + std::size_t getCurrentIndex() const { + return m_currentIndex; + } + + std::size_t m_size; + std::size_t m_currentIndex; +}; + +/////////////////////////////////////////////////////////////////////////// + +class GeneratorsForTest : public IGeneratorsForTest { + +public: + ~GeneratorsForTest() { + deleteAll(m_generatorsInOrder); + } + + IGeneratorInfo& getGeneratorInfo(std::string const& fileInfo, std::size_t size) { + std::map::const_iterator it = m_generatorsByName.find(fileInfo); + if (it == m_generatorsByName.end()) { + IGeneratorInfo* info = new GeneratorInfo(size); + m_generatorsByName.insert(std::make_pair(fileInfo, info)); + m_generatorsInOrder.push_back(info); + return *info; + } + return *it->second; + } + + bool moveNext() { + std::vector::const_iterator it = m_generatorsInOrder.begin(); + std::vector::const_iterator itEnd = m_generatorsInOrder.end(); + for (; it != itEnd; ++it) { + if ((*it)->moveNext()) + return true; + } + return false; + } + +private: + std::map m_generatorsByName; + std::vector m_generatorsInOrder; +}; + +IGeneratorsForTest* createGeneratorsForTest() { + return new GeneratorsForTest(); +} + +} // end namespace Catch + +// #included from: catch_assertionresult.hpp +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED + +namespace Catch { + +AssertionInfo::AssertionInfo(std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition) + : macroName(_macroName) + , lineInfo(_lineInfo) + , capturedExpression(_capturedExpression) + , resultDisposition(_resultDisposition) {} + +AssertionResult::AssertionResult() {} + +AssertionResult::AssertionResult(AssertionInfo const& info, AssertionResultData const& data) + : m_info(info) + , m_resultData(data) {} + +AssertionResult::~AssertionResult() {} + +// Result was a success +bool AssertionResult::succeeded() const { + return Catch::isOk(m_resultData.resultType); +} + +// Result was a success, or failure is suppressed +bool AssertionResult::isOk() const { + return Catch::isOk(m_resultData.resultType) || shouldSuppressFailure(m_info.resultDisposition); +} + +ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; +} + +bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); +} + +bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); +} + +std::string AssertionResult::getExpression() const { + if (isFalseTest(m_info.resultDisposition)) + return "!" + m_info.capturedExpression; + else + return m_info.capturedExpression; +} +std::string AssertionResult::getExpressionInMacro() const { + if (m_info.macroName.empty()) + return m_info.capturedExpression; + else + return m_info.macroName + "( " + m_info.capturedExpression + " )"; +} + +bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); +} + +std::string AssertionResult::getExpandedExpression() const { + return m_resultData.reconstructedExpression; +} + +std::string AssertionResult::getMessage() const { + return m_resultData.message; +} +SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; +} + +std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; +} + +} // end namespace Catch + +// #included from: catch_test_case_info.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED + +namespace Catch { + +inline TestCaseInfo::SpecialProperties parseSpecialTag(std::string const& tag) { + if (startsWith(tag, ".") || tag == "hide" || tag == "!hide") + return TestCaseInfo::IsHidden; + else if (tag == "!throws") + return TestCaseInfo::Throws; + else if (tag == "!shouldfail") + return TestCaseInfo::ShouldFail; + else if (tag == "!mayfail") + return TestCaseInfo::MayFail; + else + return TestCaseInfo::None; +} +inline bool isReservedTag(std::string const& tag) { + return parseSpecialTag(tag) == TestCaseInfo::None && tag.size() > 0 && !isalnum(tag[0]); +} +inline void enforceNotReservedTag(std::string const& tag, SourceLineInfo const& _lineInfo) { + if (isReservedTag(tag)) { + { + Colour colourGuard(Colour::Red); + Catch::cerr() + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n"; + } + { + Colour colourGuard(Colour::FileName); + Catch::cerr() << _lineInfo << std::endl; + } + exit(1); + } +} + +TestCase makeTestCase(ITestCase* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo) { + bool isHidden(startsWith(_name, "./")); // Legacy support + + // Parse out tags + std::set tags; + std::string desc, tag; + bool inTag = false; + for (std::size_t i = 0; i < _descOrTags.size(); ++i) { + char c = _descOrTags[i]; + if (!inTag) { + if (c == '[') + inTag = true; + else + desc += c; + } else { + if (c == ']') { + TestCaseInfo::SpecialProperties prop = parseSpecialTag(tag); + if (prop == TestCaseInfo::IsHidden) + isHidden = true; + else if (prop == TestCaseInfo::None) + enforceNotReservedTag(tag, _lineInfo); + + tags.insert(tag); + tag.clear(); + inTag = false; + } else + tag += c; + } + } + if (isHidden) { + tags.insert("hide"); + tags.insert("."); + } + + TestCaseInfo info(_name, _className, desc, tags, _lineInfo); + return TestCase(_testCase, info); +} + +void setTags(TestCaseInfo& testCaseInfo, std::set const& tags) { + testCaseInfo.tags = tags; + testCaseInfo.lcaseTags.clear(); + + std::ostringstream oss; + for (std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it) { + oss << "[" << *it << "]"; + std::string lcaseTag = toLower(*it); + testCaseInfo.properties = static_cast(testCaseInfo.properties | parseSpecialTag(lcaseTag)); + testCaseInfo.lcaseTags.insert(lcaseTag); + } + testCaseInfo.tagsAsString = oss.str(); +} + +TestCaseInfo::TestCaseInfo(std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo) + : name(_name) + , className(_className) + , description(_description) + , lineInfo(_lineInfo) + , properties(None) { + setTags(*this, _tags); +} + +TestCaseInfo::TestCaseInfo(TestCaseInfo const& other) + : name(other.name) + , className(other.className) + , description(other.description) + , tags(other.tags) + , lcaseTags(other.lcaseTags) + , tagsAsString(other.tagsAsString) + , lineInfo(other.lineInfo) + , properties(other.properties) {} + +bool TestCaseInfo::isHidden() const { + return (properties & IsHidden) != 0; +} +bool TestCaseInfo::throws() const { + return (properties & Throws) != 0; +} +bool TestCaseInfo::okToFail() const { + return (properties & (ShouldFail | MayFail)) != 0; +} +bool TestCaseInfo::expectedToFail() const { + return (properties & (ShouldFail)) != 0; +} + +TestCase::TestCase(ITestCase* testCase, TestCaseInfo const& info) + : TestCaseInfo(info) + , test(testCase) {} + +TestCase::TestCase(TestCase const& other) + : TestCaseInfo(other) + , test(other.test) {} + +TestCase TestCase::withName(std::string const& _newName) const { + TestCase other(*this); + other.name = _newName; + return other; +} + +void TestCase::swap(TestCase& other) { + test.swap(other.test); + name.swap(other.name); + className.swap(other.className); + description.swap(other.description); + tags.swap(other.tags); + lcaseTags.swap(other.lcaseTags); + tagsAsString.swap(other.tagsAsString); + std::swap(TestCaseInfo::properties, static_cast(other).properties); + std::swap(lineInfo, other.lineInfo); +} + +void TestCase::invoke() const { + test->invoke(); +} + +bool TestCase::operator==(TestCase const& other) const { + return test.get() == other.test.get() && name == other.name && className == other.className; +} + +bool TestCase::operator<(TestCase const& other) const { + return name < other.name; +} +TestCase& TestCase::operator=(TestCase const& other) { + TestCase temp(other); + swap(temp); + return *this; +} + +TestCaseInfo const& TestCase::getTestCaseInfo() const { + return *this; +} + +} // end namespace Catch + +// #included from: catch_version.hpp +#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED + +namespace Catch { + +Version::Version(unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + std::string const& _branchName, + unsigned int _buildNumber) + : majorVersion(_majorVersion) + , minorVersion(_minorVersion) + , patchNumber(_patchNumber) + , branchName(_branchName) + , buildNumber(_buildNumber) {} + +std::ostream& operator<<(std::ostream& os, Version const& version) { + os << version.majorVersion << "." + << version.minorVersion << "." + << version.patchNumber; + + if (!version.branchName.empty()) { + os << "-" << version.branchName + << "." << version.buildNumber; + } + return os; +} + +Version libraryVersion(1, 5, 6, "", 0); +} + +// #included from: catch_message.hpp +#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED + +namespace Catch { + +MessageInfo::MessageInfo(std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type) + : macroName(_macroName) + , lineInfo(_lineInfo) + , type(_type) + , sequence(++globalCount) {} + +// This may need protecting if threading support is added +unsigned int MessageInfo::globalCount = 0; + +//////////////////////////////////////////////////////////////////////////// + +ScopedMessage::ScopedMessage(MessageBuilder const& builder) + : m_info(builder.m_info) { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage(m_info); +} +ScopedMessage::ScopedMessage(ScopedMessage const& other) + : m_info(other.m_info) {} + +ScopedMessage::~ScopedMessage() { + getResultCapture().popScopedMessage(m_info); +} + +} // end namespace Catch + +// #included from: catch_legacy_reporter_adapter.hpp +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED + +// #included from: catch_legacy_reporter_adapter.h +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED + +namespace Catch { +// Deprecated +struct IReporter : IShared { + virtual ~IReporter(); + + virtual bool shouldRedirectStdout() const = 0; + + virtual void StartTesting() = 0; + virtual void EndTesting(Totals const& totals) = 0; + virtual void StartGroup(std::string const& groupName) = 0; + virtual void EndGroup(std::string const& groupName, Totals const& totals) = 0; + virtual void StartTestCase(TestCaseInfo const& testInfo) = 0; + virtual void EndTestCase(TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr) = 0; + virtual void StartSection(std::string const& sectionName, std::string const& description) = 0; + virtual void EndSection(std::string const& sectionName, Counts const& assertions) = 0; + virtual void NoAssertionsInSection(std::string const& sectionName) = 0; + virtual void NoAssertionsInTestCase(std::string const& testName) = 0; + virtual void Aborted() = 0; + virtual void Result(AssertionResult const& result) = 0; +}; + +class LegacyReporterAdapter : public SharedImpl { +public: + LegacyReporterAdapter(Ptr const& legacyReporter); + virtual ~LegacyReporterAdapter(); + + virtual ReporterPreferences getPreferences() const; + virtual void noMatchingTestCases(std::string const&); + virtual void testRunStarting(TestRunInfo const&); + virtual void testGroupStarting(GroupInfo const& groupInfo); + virtual void testCaseStarting(TestCaseInfo const& testInfo); + virtual void sectionStarting(SectionInfo const& sectionInfo); + virtual void assertionStarting(AssertionInfo const&); + virtual bool assertionEnded(AssertionStats const& assertionStats); + virtual void sectionEnded(SectionStats const& sectionStats); + virtual void testCaseEnded(TestCaseStats const& testCaseStats); + virtual void testGroupEnded(TestGroupStats const& testGroupStats); + virtual void testRunEnded(TestRunStats const& testRunStats); + virtual void skipTest(TestCaseInfo const&); + +private: + Ptr m_legacyReporter; +}; +} + +namespace Catch { +LegacyReporterAdapter::LegacyReporterAdapter(Ptr const& legacyReporter) + : m_legacyReporter(legacyReporter) {} +LegacyReporterAdapter::~LegacyReporterAdapter() {} + +ReporterPreferences LegacyReporterAdapter::getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); + return prefs; +} + +void LegacyReporterAdapter::noMatchingTestCases(std::string const&) {} +void LegacyReporterAdapter::testRunStarting(TestRunInfo const&) { + m_legacyReporter->StartTesting(); +} +void LegacyReporterAdapter::testGroupStarting(GroupInfo const& groupInfo) { + m_legacyReporter->StartGroup(groupInfo.name); +} +void LegacyReporterAdapter::testCaseStarting(TestCaseInfo const& testInfo) { + m_legacyReporter->StartTestCase(testInfo); +} +void LegacyReporterAdapter::sectionStarting(SectionInfo const& sectionInfo) { + m_legacyReporter->StartSection(sectionInfo.name, sectionInfo.description); +} +void LegacyReporterAdapter::assertionStarting(AssertionInfo const&) { + // Not on legacy interface +} + +bool LegacyReporterAdapter::assertionEnded(AssertionStats const& assertionStats) { + if (assertionStats.assertionResult.getResultType() != ResultWas::Ok) { + for (std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it) { + if (it->type == ResultWas::Info) { + ResultBuilder rb(it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal); + rb << it->message; + rb.setResultType(ResultWas::Info); + AssertionResult result = rb.build(); + m_legacyReporter->Result(result); + } + } + } + m_legacyReporter->Result(assertionStats.assertionResult); + return true; +} +void LegacyReporterAdapter::sectionEnded(SectionStats const& sectionStats) { + if (sectionStats.missingAssertions) + m_legacyReporter->NoAssertionsInSection(sectionStats.sectionInfo.name); + m_legacyReporter->EndSection(sectionStats.sectionInfo.name, sectionStats.assertions); +} +void LegacyReporterAdapter::testCaseEnded(TestCaseStats const& testCaseStats) { + m_legacyReporter->EndTestCase(testCaseStats.testInfo, + testCaseStats.totals, + testCaseStats.stdOut, + testCaseStats.stdErr); +} +void LegacyReporterAdapter::testGroupEnded(TestGroupStats const& testGroupStats) { + if (testGroupStats.aborting) + m_legacyReporter->Aborted(); + m_legacyReporter->EndGroup(testGroupStats.groupInfo.name, testGroupStats.totals); +} +void LegacyReporterAdapter::testRunEnded(TestRunStats const& testRunStats) { + m_legacyReporter->EndTesting(testRunStats.totals); +} +void LegacyReporterAdapter::skipTest(TestCaseInfo const&) { +} +} + +// #included from: catch_timer.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#endif + +#ifdef CATCH_PLATFORM_WINDOWS +#include +#else +#include +#endif + +namespace Catch { + +namespace { +#ifdef CATCH_PLATFORM_WINDOWS + uint64_t getCurrentTicks() { + static uint64_t hz = 0, hzo = 0; + if (!hz) { + QueryPerformanceFrequency(reinterpret_cast(&hz)); + QueryPerformanceCounter(reinterpret_cast(&hzo)); + } + uint64_t t; + QueryPerformanceCounter(reinterpret_cast(&t)); + return ((t - hzo) * 1000000) / hz; + } +#else + uint64_t getCurrentTicks() { + timeval t; + gettimeofday(&t, CATCH_NULL); + return static_cast(t.tv_sec) * 1000000ull + static_cast(t.tv_usec); + } +#endif +} + +void Timer::start() { + m_ticks = getCurrentTicks(); +} +unsigned int Timer::getElapsedMicroseconds() const { + return static_cast(getCurrentTicks() - m_ticks); +} +unsigned int Timer::getElapsedMilliseconds() const { + return static_cast(getElapsedMicroseconds() / 1000); +} +double Timer::getElapsedSeconds() const { + return getElapsedMicroseconds() / 1000000.0; +} + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +// #included from: catch_common.hpp +#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED + +namespace Catch { + +bool startsWith(std::string const& s, std::string const& prefix) { + return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix; +} +bool endsWith(std::string const& s, std::string const& suffix) { + return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix; +} +bool contains(std::string const& s, std::string const& infix) { + return s.find(infix) != std::string::npos; +} +void toLowerInPlace(std::string& s) { + std::transform(s.begin(), s.end(), s.begin(), ::tolower); +} +std::string toLower(std::string const& s) { + std::string lc = s; + toLowerInPlace(lc); + return lc; +} +std::string trim(std::string const& str) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of(whitespaceChars); + std::string::size_type end = str.find_last_not_of(whitespaceChars); + + return start != std::string::npos ? str.substr(start, 1 + end - start) : ""; +} + +bool replaceInPlace(std::string& str, std::string const& replaceThis, std::string const& withThis) { + bool replaced = false; + std::size_t i = str.find(replaceThis); + while (i != std::string::npos) { + replaced = true; + str = str.substr(0, i) + withThis + str.substr(i + replaceThis.size()); + if (i < str.size() - withThis.size()) + i = str.find(replaceThis, i + withThis.size()); + else + i = std::string::npos; + } + return replaced; +} + +pluralise::pluralise(std::size_t count, std::string const& label) + : m_count(count) + , m_label(label) {} + +std::ostream& operator<<(std::ostream& os, pluralise const& pluraliser) { + os << pluraliser.m_count << " " << pluraliser.m_label; + if (pluraliser.m_count != 1) + os << "s"; + return os; +} + +SourceLineInfo::SourceLineInfo() + : line(0) {} +SourceLineInfo::SourceLineInfo(char const* _file, std::size_t _line) + : file(_file) + , line(_line) {} +SourceLineInfo::SourceLineInfo(SourceLineInfo const& other) + : file(other.file) + , line(other.line) {} +bool SourceLineInfo::empty() const { + return file.empty(); +} +bool SourceLineInfo::operator==(SourceLineInfo const& other) const { + return line == other.line && file == other.file; +} +bool SourceLineInfo::operator<(SourceLineInfo const& other) const { + return line < other.line || (line == other.line && file < other.file); +} + +void seedRng(IConfig const& config) { + if (config.rngSeed() != 0) + std::srand(config.rngSeed()); +} +unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); +} + +std::ostream& operator<<(std::ostream& os, SourceLineInfo const& info) { +#ifndef __GNUG__ + os << info.file << "(" << info.line << ")"; +#else + os << info.file << ":" << info.line; +#endif + return os; +} + +void throwLogicError(std::string const& message, SourceLineInfo const& locationInfo) { + std::ostringstream oss; + oss << locationInfo << ": Internal Catch error: '" << message << "'"; + if (alwaysTrue()) + throw std::logic_error(oss.str()); +} +} + +// #included from: catch_section.hpp +#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED + +namespace Catch { + +SectionInfo::SectionInfo(SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description) + : name(_name) + , description(_description) + , lineInfo(_lineInfo) {} + +Section::Section(SectionInfo const& info) + : m_info(info) + , m_sectionIncluded(getResultCapture().sectionStarted(m_info, m_assertions)) { + m_timer.start(); +} + +Section::~Section() { + if (m_sectionIncluded) { + SectionEndInfo endInfo(m_info, m_assertions, m_timer.getElapsedSeconds()); + if (std::uncaught_exception()) + getResultCapture().sectionEndedEarly(endInfo); + else + getResultCapture().sectionEnded(endInfo); + } +} + +// This indicates whether the section should be executed or not +Section::operator bool() const { + return m_sectionIncluded; +} + +} // end namespace Catch + +// #included from: catch_debugger.hpp +#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED + +#include + +#ifdef CATCH_PLATFORM_MAC + +#include +#include +#include +#include +#include + +namespace Catch { + +// The following function is taken directly from the following technical note: +// http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + +// Returns true if the current process is being debugged (either +// running under the debugger or has a debugger attached post facto). +bool isDebuggerActive() { + + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if (sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ((info.kp_proc.p_flag & P_TRACED) != 0); +} +} // namespace Catch + +#elif defined(_MSC_VER) +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { +bool isDebuggerActive() { + return IsDebuggerPresent() != 0; +} +} +#elif defined(__MINGW32__) +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { +bool isDebuggerActive() { + return IsDebuggerPresent() != 0; +} +} +#else +namespace Catch { +inline bool isDebuggerActive() { return false; } +} +#endif // Platform + +#ifdef CATCH_PLATFORM_WINDOWS +extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char*); +namespace Catch { +void writeToDebugConsole(std::string const& text) { + ::OutputDebugStringA(text.c_str()); +} +} +#else +namespace Catch { +void writeToDebugConsole(std::string const& text) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; +} +} +#endif // Platform + +// #included from: catch_tostring.hpp +#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, + Little }; + + static Arch which() { + union _ { + int asInt; + char asChar[sizeof(int)]; + } u; + + u.asInt = 1; + return (u.asChar[sizeof(int) - 1] == 1) ? Big : Little; + } + }; + } + + std::string rawMemoryToString(const void* object, std::size_t size) { + // Reverse order for little endian architectures + int i = 0, end = static_cast(size), inc = 1; + if (Endianness::which() == Endianness::Little) { + i = end - 1; + end = inc = -1; + } + + unsigned char const* bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for (; i != end; i += inc) + os << std::setw(2) << static_cast(bytes[i]); + return os.str(); + } +} + +std::string toString(std::string const& value) { + std::string s = value; + if (getCurrentContext().getConfig()->showInvisibles()) { + for (size_t i = 0; i < s.size(); ++i) { + std::string subs; + switch (s[i]) { + case '\n': + subs = "\\n"; + break; + case '\t': + subs = "\\t"; + break; + default: + break; + } + if (!subs.empty()) { + s = s.substr(0, i) + subs + s.substr(i + 1); + ++i; + } + } + } + return "\"" + s + "\""; +} +std::string toString(std::wstring const& value) { + + std::string s; + s.reserve(value.size()); + for (size_t i = 0; i < value.size(); ++i) + s += value[i] <= 0xff ? static_cast(value[i]) : '?'; + return Catch::toString(s); +} + +std::string toString(const char* const value) { + return value ? Catch::toString(std::string(value)) : std::string("{null string}"); +} + +std::string toString(char* const value) { + return Catch::toString(static_cast(value)); +} + +std::string toString(const wchar_t* const value) { + return value ? Catch::toString(std::wstring(value)) : std::string("{null string}"); +} + +std::string toString(wchar_t* const value) { + return Catch::toString(static_cast(value)); +} + +std::string toString(int value) { + std::ostringstream oss; + oss << value; + if (value > Detail::hexThreshold) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} + +std::string toString(unsigned long value) { + std::ostringstream oss; + oss << value; + if (value > Detail::hexThreshold) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} + +std::string toString(unsigned int value) { + return Catch::toString(static_cast(value)); +} + +template +std::string fpToString(T value, int precision) { + std::ostringstream oss; + oss << std::setprecision(precision) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of('0'); + if (i != std::string::npos && i != d.size() - 1) { + if (d[i] == '.') + i++; + d = d.substr(0, i + 1); + } + return d; +} + +std::string toString(const double value) { + return fpToString(value, 10); +} +std::string toString(const float value) { + return fpToString(value, 5) + "f"; +} + +std::string toString(bool value) { + return value ? "true" : "false"; +} + +std::string toString(char value) { + return value < ' ' + ? toString(static_cast(value)) + : Detail::makeString(value); +} + +std::string toString(signed char value) { + return toString(static_cast(value)); +} + +std::string toString(unsigned char value) { + return toString(static_cast(value)); +} + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG +std::string toString(long long value) { + std::ostringstream oss; + oss << value; + if (value > Detail::hexThreshold) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} +std::string toString(unsigned long long value) { + std::ostringstream oss; + oss << value; + if (value > Detail::hexThreshold) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} +#endif + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString(std::nullptr_t) { + return "nullptr"; +} +#endif + +#ifdef __OBJC__ +std::string toString(NSString const* const& nsstring) { + if (!nsstring) + return "nil"; + return "@" + toString([nsstring UTF8String]); +} +std::string toString(NSString* CATCH_ARC_STRONG const& nsstring) { + if (!nsstring) + return "nil"; + return "@" + toString([nsstring UTF8String]); +} +std::string toString(NSObject* const& nsObject) { + return toString([nsObject description]); +} +#endif + +} // end namespace Catch + +// #included from: catch_result_builder.hpp +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED + +namespace Catch { + +std::string capturedExpressionWithSecondArgument(std::string const& capturedExpression, std::string const& secondArg) { + return secondArg.empty() || secondArg == "\"\"" + ? capturedExpression + : capturedExpression + ", " + secondArg; +} +ResultBuilder::ResultBuilder(char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg) + : m_assertionInfo(macroName, lineInfo, capturedExpressionWithSecondArgument(capturedExpression, secondArg), resultDisposition) + , m_shouldDebugBreak(false) + , m_shouldThrow(false) {} + +ResultBuilder& ResultBuilder::setResultType(ResultWas::OfType result) { + m_data.resultType = result; + return *this; +} +ResultBuilder& ResultBuilder::setResultType(bool result) { + m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; + return *this; +} +ResultBuilder& ResultBuilder::setLhs(std::string const& lhs) { + m_exprComponents.lhs = lhs; + return *this; +} +ResultBuilder& ResultBuilder::setRhs(std::string const& rhs) { + m_exprComponents.rhs = rhs; + return *this; +} +ResultBuilder& ResultBuilder::setOp(std::string const& op) { + m_exprComponents.op = op; + return *this; +} + +void ResultBuilder::endExpression() { + m_exprComponents.testFalse = isFalseTest(m_assertionInfo.resultDisposition); + captureExpression(); +} + +void ResultBuilder::useActiveException(ResultDisposition::Flags resultDisposition) { + m_assertionInfo.resultDisposition = resultDisposition; + m_stream.oss << Catch::translateActiveException(); + captureResult(ResultWas::ThrewException); +} + +void ResultBuilder::captureResult(ResultWas::OfType resultType) { + setResultType(resultType); + captureExpression(); +} +void ResultBuilder::captureExpectedException(std::string const& expectedMessage) { + if (expectedMessage.empty()) + captureExpectedException(Matchers::Impl::Generic::AllOf()); + else + captureExpectedException(Matchers::Equals(expectedMessage)); +} + +void ResultBuilder::captureExpectedException(Matchers::Impl::Matcher const& matcher) { + + assert(m_exprComponents.testFalse == false); + AssertionResultData data = m_data; + data.resultType = ResultWas::Ok; + data.reconstructedExpression = m_assertionInfo.capturedExpression; + + std::string actualMessage = Catch::translateActiveException(); + if (!matcher.match(actualMessage)) { + data.resultType = ResultWas::ExpressionFailed; + data.reconstructedExpression = actualMessage; + } + AssertionResult result(m_assertionInfo, data); + handleResult(result); +} + +void ResultBuilder::captureExpression() { + AssertionResult result = build(); + handleResult(result); +} +void ResultBuilder::handleResult(AssertionResult const& result) { + getResultCapture().assertionEnded(result); + + if (!result.isOk()) { + if (getCurrentContext().getConfig()->shouldDebugBreak()) + m_shouldDebugBreak = true; + if (getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal)) + m_shouldThrow = true; + } +} +void ResultBuilder::react() { + if (m_shouldThrow) + throw Catch::TestFailureException(); +} + +bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } +bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } + +AssertionResult ResultBuilder::build() const { + assert(m_data.resultType != ResultWas::Unknown); + + AssertionResultData data = m_data; + + // Flip bool results if testFalse is set + if (m_exprComponents.testFalse) { + if (data.resultType == ResultWas::Ok) + data.resultType = ResultWas::ExpressionFailed; + else if (data.resultType == ResultWas::ExpressionFailed) + data.resultType = ResultWas::Ok; + } + + data.message = m_stream.oss.str(); + data.reconstructedExpression = reconstructExpression(); + if (m_exprComponents.testFalse) { + if (m_exprComponents.op == "") + data.reconstructedExpression = "!" + data.reconstructedExpression; + else + data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; + } + return AssertionResult(m_assertionInfo, data); +} +std::string ResultBuilder::reconstructExpression() const { + if (m_exprComponents.op == "") + return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; + else if (m_exprComponents.op == "matches") + return m_exprComponents.lhs + " " + m_exprComponents.rhs; + else if (m_exprComponents.op != "!") { + if (m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && m_exprComponents.lhs.find("\n") == std::string::npos && m_exprComponents.rhs.find("\n") == std::string::npos) + return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; + else + return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; + } else + return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; +} + +} // end namespace Catch + +// #included from: catch_tag_alias_registry.hpp +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED + +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + +namespace Catch { + +class TagAliasRegistry : public ITagAliasRegistry { +public: + virtual ~TagAliasRegistry(); + virtual Option find(std::string const& alias) const; + virtual std::string expandAliases(std::string const& unexpandedTestSpec) const; + void add(char const* alias, char const* tag, SourceLineInfo const& lineInfo); + static TagAliasRegistry& get(); + +private: + std::map m_registry; +}; + +} // end namespace Catch + +#include +#include + +namespace Catch { + +TagAliasRegistry::~TagAliasRegistry() {} + +Option TagAliasRegistry::find(std::string const& alias) const { + std::map::const_iterator it = m_registry.find(alias); + if (it != m_registry.end()) + return it->second; + else + return Option(); +} + +std::string TagAliasRegistry::expandAliases(std::string const& unexpandedTestSpec) const { + std::string expandedTestSpec = unexpandedTestSpec; + for (std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); + it != itEnd; + ++it) { + std::size_t pos = expandedTestSpec.find(it->first); + if (pos != std::string::npos) { + expandedTestSpec = expandedTestSpec.substr(0, pos) + it->second.tag + expandedTestSpec.substr(pos + it->first.size()); + } + } + return expandedTestSpec; +} + +void TagAliasRegistry::add(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + + if (!startsWith(alias, "[@") || !endsWith(alias, "]")) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + throw std::domain_error(oss.str().c_str()); + } + if (!m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " << find(alias)->lineInfo << "\n" + << "\tRedefined at " << lineInfo; + throw std::domain_error(oss.str().c_str()); + } +} + +TagAliasRegistry& TagAliasRegistry::get() { + static TagAliasRegistry instance; + return instance; +} + +ITagAliasRegistry::~ITagAliasRegistry() {} +ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } + +RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + try { + TagAliasRegistry::get().add(alias, tag, lineInfo); + } catch (std::exception& ex) { + Colour colourGuard(Colour::Red); + Catch::cerr() << ex.what() << std::endl; + exit(1); + } +} + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_multi.hpp +#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED + +namespace Catch { + +class MultipleReporters : public SharedImpl { + typedef std::vector> Reporters; + Reporters m_reporters; + +public: + void add(Ptr const& reporter) { + m_reporters.push_back(reporter); + } + +public: // IStreamingReporter + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporters[0]->getPreferences(); + } + + virtual void noMatchingTestCases(std::string const& spec) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->noMatchingTestCases(spec); + } + + virtual void testRunStarting(TestRunInfo const& testRunInfo) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->testRunStarting(testRunInfo); + } + + virtual void testGroupStarting(GroupInfo const& groupInfo) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->testGroupStarting(groupInfo); + } + + virtual void testCaseStarting(TestCaseInfo const& testInfo) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->testCaseStarting(testInfo); + } + + virtual void sectionStarting(SectionInfo const& sectionInfo) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->sectionStarting(sectionInfo); + } + + virtual void assertionStarting(AssertionInfo const& assertionInfo) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->assertionStarting(assertionInfo); + } + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded(AssertionStats const& assertionStats) CATCH_OVERRIDE { + bool clearBuffer = false; + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + clearBuffer |= (*it)->assertionEnded(assertionStats); + return clearBuffer; + } + + virtual void sectionEnded(SectionStats const& sectionStats) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->sectionEnded(sectionStats); + } + + virtual void testCaseEnded(TestCaseStats const& testCaseStats) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->testCaseEnded(testCaseStats); + } + + virtual void testGroupEnded(TestGroupStats const& testGroupStats) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->testGroupEnded(testGroupStats); + } + + virtual void testRunEnded(TestRunStats const& testRunStats) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->testRunEnded(testRunStats); + } + + virtual void skipTest(TestCaseInfo const& testInfo) CATCH_OVERRIDE { + for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it) + (*it)->skipTest(testInfo); + } + + virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { + return this; + } +}; + +Ptr addReporter(Ptr const& existingReporter, Ptr const& additionalReporter) { + Ptr resultingReporter; + + if (existingReporter) { + MultipleReporters* multi = existingReporter->tryAsMulti(); + if (!multi) { + multi = new MultipleReporters; + resultingReporter = Ptr(multi); + if (existingReporter) + multi->add(existingReporter); + } else + resultingReporter = existingReporter; + multi->add(additionalReporter); + } else + resultingReporter = additionalReporter; + + return resultingReporter; +} + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_xml.hpp +#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED + +// #included from: catch_reporter_bases.hpp +#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED + +#include + +namespace Catch { + +struct StreamingReporterBase : SharedImpl { + + StreamingReporterBase(ReporterConfig const& _config) + : m_config(_config.fullConfig()) + , stream(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = false; + } + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual ~StreamingReporterBase() CATCH_OVERRIDE; + + virtual void noMatchingTestCases(std::string const&) CATCH_OVERRIDE {} + + virtual void testRunStarting(TestRunInfo const& _testRunInfo) CATCH_OVERRIDE { + currentTestRunInfo = _testRunInfo; + } + virtual void testGroupStarting(GroupInfo const& _groupInfo) CATCH_OVERRIDE { + currentGroupInfo = _groupInfo; + } + + virtual void testCaseStarting(TestCaseInfo const& _testInfo) CATCH_OVERRIDE { + currentTestCaseInfo = _testInfo; + } + virtual void sectionStarting(SectionInfo const& _sectionInfo) CATCH_OVERRIDE { + m_sectionStack.push_back(_sectionInfo); + } + + virtual void sectionEnded(SectionStats const& /* _sectionStats */) CATCH_OVERRIDE { + m_sectionStack.pop_back(); + } + virtual void testCaseEnded(TestCaseStats const& /* _testCaseStats */) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + } + virtual void testGroupEnded(TestGroupStats const& /* _testGroupStats */) CATCH_OVERRIDE { + currentGroupInfo.reset(); + } + virtual void testRunEnded(TestRunStats const& /* _testRunStats */) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + virtual void skipTest(TestCaseInfo const&) CATCH_OVERRIDE { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + Ptr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; +}; + +struct CumulativeReporterBase : SharedImpl { + template + struct Node : SharedImpl<> { + explicit Node(T const& _value) + : value(_value) {} + virtual ~Node() {} + + typedef std::vector> ChildNodes; + T value; + ChildNodes children; + }; + struct SectionNode : SharedImpl<> { + explicit SectionNode(SectionStats const& _stats) + : stats(_stats) {} + virtual ~SectionNode(); + + bool operator==(SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator==(Ptr const& other) const { + return operator==(*other); + } + + SectionStats stats; + typedef std::vector> ChildSections; + typedef std::vector Assertions; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo(SectionInfo const& other) + : m_other(other) {} + BySectionInfo(BySectionInfo const& other) + : m_other(other.m_other) {} + bool operator()(Ptr const& node) const { + return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + } + + private: + void operator=(BySectionInfo const&); + SectionInfo const& m_other; + }; + + typedef Node TestCaseNode; + typedef Node TestGroupNode; + typedef Node TestRunNode; + + CumulativeReporterBase(ReporterConfig const& _config) + : m_config(_config.fullConfig()) + , stream(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = false; + } + ~CumulativeReporterBase(); + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual void testRunStarting(TestRunInfo const&) CATCH_OVERRIDE {} + virtual void testGroupStarting(GroupInfo const&) CATCH_OVERRIDE {} + + virtual void testCaseStarting(TestCaseInfo const&) CATCH_OVERRIDE {} + + virtual void sectionStarting(SectionInfo const& sectionInfo) CATCH_OVERRIDE { + SectionStats incompleteStats(sectionInfo, Counts(), 0, false); + Ptr node; + if (m_sectionStack.empty()) { + if (!m_rootSection) + m_rootSection = new SectionNode(incompleteStats); + node = m_rootSection; + } else { + SectionNode& parentNode = *m_sectionStack.back(); + SectionNode::ChildSections::const_iterator it = std::find_if(parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo(sectionInfo)); + if (it == parentNode.childSections.end()) { + node = new SectionNode(incompleteStats); + parentNode.childSections.push_back(node); + } else + node = *it; + } + m_sectionStack.push_back(node); + m_deepestSection = node; + } + + virtual void assertionStarting(AssertionInfo const&) CATCH_OVERRIDE {} + + virtual bool assertionEnded(AssertionStats const& assertionStats) CATCH_OVERRIDE { + assert(!m_sectionStack.empty()); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + virtual void sectionEnded(SectionStats const& sectionStats) CATCH_OVERRIDE { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + virtual void testCaseEnded(TestCaseStats const& testCaseStats) CATCH_OVERRIDE { + Ptr node = new TestCaseNode(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + virtual void testGroupEnded(TestGroupStats const& testGroupStats) CATCH_OVERRIDE { + Ptr node = new TestGroupNode(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + virtual void testRunEnded(TestRunStats const& testRunStats) CATCH_OVERRIDE { + Ptr node = new TestRunNode(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + virtual void skipTest(TestCaseInfo const&) CATCH_OVERRIDE {} + + Ptr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + Ptr m_rootSection; + Ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; +}; + +template +char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if (!*line) { + memset(line, C, CATCH_CONFIG_CONSOLE_WIDTH - 1); + line[CATCH_CONFIG_CONSOLE_WIDTH - 1] = 0; + } + return line; +} + +struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase(ReporterConfig const& _config) + : StreamingReporterBase(_config) {} + + virtual void assertionStarting(AssertionInfo const&) CATCH_OVERRIDE {} + virtual bool assertionEnded(AssertionStats const&) CATCH_OVERRIDE { + return false; + } +}; + +} // end namespace Catch + +// #included from: ../internal/catch_reporter_registrars.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED + +namespace Catch { + +template +class LegacyReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + virtual IStreamingReporter* create(ReporterConfig const& config) const { + return new LegacyReporterAdapter(new T(config)); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + +public: + LegacyReporterRegistrar(std::string const& name) { + getMutableRegistryHub().registerReporter(name, new ReporterFactory()); + } +}; + +template +class ReporterRegistrar { + + class ReporterFactory : public SharedImpl { + + // *** Please Note ***: + // - If you end up here looking at a compiler error because it's trying to register + // your custom reporter class be aware that the native reporter interface has changed + // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via + // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. + // However please consider updating to the new interface as the old one is now + // deprecated and will probably be removed quite soon! + // Please contact me via github if you have any questions at all about this. + // In fact, ideally, please contact me anyway to let me know you've hit this - as I have + // no idea who is actually using custom reporters at all (possibly no-one!). + // The new interface is designed to minimise exposure to interface changes in the future. + virtual IStreamingReporter* create(ReporterConfig const& config) const { + return new T(config); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + +public: + ReporterRegistrar(std::string const& name) { + getMutableRegistryHub().registerReporter(name, new ReporterFactory()); + } +}; + +template +class ListenerRegistrar { + + class ListenerFactory : public SharedImpl { + + virtual IStreamingReporter* create(ReporterConfig const& config) const { + return new T(config); + } + virtual std::string getDescription() const { + return ""; + } + }; + +public: + ListenerRegistrar() { + getMutableRegistryHub().registerListener(new ListenerFactory()); + } +}; +} + +#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER(name, reporterType) \ + namespace { \ + Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType(name); \ + } + +#define INTERNAL_CATCH_REGISTER_REPORTER(name, reporterType) \ + namespace { \ + Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType(name); \ + } + +#define INTERNAL_CATCH_REGISTER_LISTENER(listenerType) \ + namespace { \ + Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; \ + } + +// #included from: ../internal/catch_xmlwriter.hpp +#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + +class XmlEncode { +public: + enum ForWhat { ForTextNodes, + ForAttributes }; + + XmlEncode(std::string const& str, ForWhat forWhat = ForTextNodes) + : m_str(str) + , m_forWhat(forWhat) {} + + void encodeTo(std::ostream& os) const { + + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for (std::size_t i = 0; i < m_str.size(); ++i) { + char c = m_str[i]; + switch (c) { + case '<': + os << "<"; + break; + case '&': + os << "&"; + break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (i > 2 && m_str[i - 1] == ']' && m_str[i - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Escape control chars - based on contribution by @espenalb in PR #465 + if ((c < '\x09') || (c > '\x0D' && c < '\x20') || c == '\x7F') + os << "&#x" << std::uppercase << std::hex << static_cast(c); + else + os << c; + } + } + } + + friend std::ostream& operator<<(std::ostream& os, XmlEncode const& xmlEncode) { + xmlEncode.encodeTo(os); + return os; + } + +private: + std::string m_str; + ForWhat m_forWhat; +}; + +class XmlWriter { +public: + class ScopedElement { + public: + ScopedElement(XmlWriter* writer) + : m_writer(writer) {} + + ScopedElement(ScopedElement const& other) + : m_writer(other.m_writer) { + other.m_writer = CATCH_NULL; + } + + ~ScopedElement() { + if (m_writer) + m_writer->endElement(); + } + + ScopedElement& writeText(std::string const& text, bool indent = true) { + m_writer->writeText(text, indent); + return *this; + } + + template + ScopedElement& writeAttribute(std::string const& name, T const& attribute) { + m_writer->writeAttribute(name, attribute); + return *this; + } + + private: + mutable XmlWriter* m_writer; + }; + + XmlWriter() + : m_tagIsOpen(false) + , m_needsNewline(false) + , m_os(&Catch::cout()) {} + + XmlWriter(std::ostream& os) + : m_tagIsOpen(false) + , m_needsNewline(false) + , m_os(&os) {} + + ~XmlWriter() { + while (!m_tags.empty()) + endElement(); + } + + XmlWriter& startElement(std::string const& name) { + ensureTagClosed(); + newlineIfNecessary(); + stream() << m_indent << "<" << name; + m_tags.push_back(name); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + ScopedElement scopedElement(std::string const& name) { + ScopedElement scoped(this); + startElement(name); + return scoped; + } + + XmlWriter& endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr(0, m_indent.size() - 2); + if (m_tagIsOpen) { + stream() << "/>\n"; + m_tagIsOpen = false; + } else { + stream() << m_indent << "\n"; + } + m_tags.pop_back(); + return *this; + } + + XmlWriter& writeAttribute(std::string const& name, std::string const& attribute) { + if (!name.empty() && !attribute.empty()) + stream() << " " << name << "=\"" << XmlEncode(attribute, XmlEncode::ForAttributes) << "\""; + return *this; + } + + XmlWriter& writeAttribute(std::string const& name, bool attribute) { + stream() << " " << name << "=\"" << (attribute ? "true" : "false") << "\""; + return *this; + } + + template + XmlWriter& writeAttribute(std::string const& name, T const& attribute) { + std::ostringstream oss; + oss << attribute; + return writeAttribute(name, oss.str()); + } + + XmlWriter& writeText(std::string const& text, bool indent = true) { + if (!text.empty()) { + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if (tagWasOpen && indent) + stream() << m_indent; + stream() << XmlEncode(text); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& writeComment(std::string const& text) { + ensureTagClosed(); + stream() << m_indent << ""; + m_needsNewline = true; + return *this; + } + + XmlWriter& writeBlankLine() { + ensureTagClosed(); + stream() << "\n"; + return *this; + } + + void setStream(std::ostream& os) { + m_os = &os; + } + +private: + XmlWriter(XmlWriter const&); + void operator=(XmlWriter const&); + + std::ostream& stream() { + return *m_os; + } + + void ensureTagClosed() { + if (m_tagIsOpen) { + stream() << ">\n"; + m_tagIsOpen = false; + } + } + + void newlineIfNecessary() { + if (m_needsNewline) { + stream() << "\n"; + m_needsNewline = false; + } + } + + bool m_tagIsOpen; + bool m_needsNewline; + std::vector m_tags; + std::string m_indent; + std::ostream* m_os; +}; +} +// #included from: catch_reenable_warnings.h + +#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED + +#ifdef __clang__ +#ifdef __ICC // icpc defines the __clang__ macro +#pragma warning(pop) +#else +#pragma clang diagnostic pop +#endif +#elif defined __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace Catch { +class XmlReporter : public StreamingReporterBase { +public: + XmlReporter(ReporterConfig const& _config) + : StreamingReporterBase(_config) + , m_sectionDepth(0) { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~XmlReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results as an XML document"; + } + +public: // StreamingReporterBase + virtual void noMatchingTestCases(std::string const& s) CATCH_OVERRIDE { + StreamingReporterBase::noMatchingTestCases(s); + } + + virtual void testRunStarting(TestRunInfo const& testInfo) CATCH_OVERRIDE { + StreamingReporterBase::testRunStarting(testInfo); + m_xml.setStream(stream); + m_xml.startElement("Catch"); + if (!m_config->name().empty()) + m_xml.writeAttribute("name", m_config->name()); + } + + virtual void testGroupStarting(GroupInfo const& groupInfo) CATCH_OVERRIDE { + StreamingReporterBase::testGroupStarting(groupInfo); + m_xml.startElement("Group") + .writeAttribute("name", groupInfo.name); + } + + virtual void testCaseStarting(TestCaseInfo const& testInfo) CATCH_OVERRIDE { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement("TestCase").writeAttribute("name", trim(testInfo.name)); + + if (m_config->showDurations() == ShowDurations::Always) + m_testCaseTimer.start(); + } + + virtual void sectionStarting(SectionInfo const& sectionInfo) CATCH_OVERRIDE { + StreamingReporterBase::sectionStarting(sectionInfo); + if (m_sectionDepth++ > 0) { + m_xml.startElement("Section") + .writeAttribute("name", trim(sectionInfo.name)) + .writeAttribute("description", sectionInfo.description); + } + } + + virtual void assertionStarting(AssertionInfo const&) CATCH_OVERRIDE {} + + virtual bool assertionEnded(AssertionStats const& assertionStats) CATCH_OVERRIDE { + const AssertionResult& assertionResult = assertionStats.assertionResult; + + // Print any info messages in tags. + if (assertionStats.assertionResult.getResultType() != ResultWas::Ok) { + for (std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it) { + if (it->type == ResultWas::Info) { + m_xml.scopedElement("Info") + .writeText(it->message); + } else if (it->type == ResultWas::Warning) { + m_xml.scopedElement("Warning") + .writeText(it->message); + } + } + } + + // Drop out if result was successful but we're not printing them. + if (!m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType())) + return true; + + // Print the expression if there is one. + if (assertionResult.hasExpression()) { + m_xml.startElement("Expression") + .writeAttribute("success", assertionResult.succeeded()) + .writeAttribute("type", assertionResult.getTestMacroName()) + .writeAttribute("filename", assertionResult.getSourceInfo().file) + .writeAttribute("line", assertionResult.getSourceInfo().line); + + m_xml.scopedElement("Original") + .writeText(assertionResult.getExpression()); + m_xml.scopedElement("Expanded") + .writeText(assertionResult.getExpandedExpression()); + } + + // And... Print a result applicable to each result type. + switch (assertionResult.getResultType()) { + case ResultWas::ThrewException: + m_xml.scopedElement("Exception") + .writeAttribute("filename", assertionResult.getSourceInfo().file) + .writeAttribute("line", assertionResult.getSourceInfo().line) + .writeText(assertionResult.getMessage()); + break; + case ResultWas::FatalErrorCondition: + m_xml.scopedElement("Fatal Error Condition") + .writeAttribute("filename", assertionResult.getSourceInfo().file) + .writeAttribute("line", assertionResult.getSourceInfo().line) + .writeText(assertionResult.getMessage()); + break; + case ResultWas::Info: + m_xml.scopedElement("Info") + .writeText(assertionResult.getMessage()); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.scopedElement("Failure") + .writeText(assertionResult.getMessage()); + break; + default: + break; + } + + if (assertionResult.hasExpression()) + m_xml.endElement(); + + return true; + } + + virtual void sectionEnded(SectionStats const& sectionStats) CATCH_OVERRIDE { + StreamingReporterBase::sectionEnded(sectionStats); + if (--m_sectionDepth > 0) { + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResults"); + e.writeAttribute("successes", sectionStats.assertions.passed); + e.writeAttribute("failures", sectionStats.assertions.failed); + e.writeAttribute("expectedFailures", sectionStats.assertions.failedButOk); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds", sectionStats.durationInSeconds); + + m_xml.endElement(); + } + } + + virtual void testCaseEnded(TestCaseStats const& testCaseStats) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded(testCaseStats); + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResult"); + e.writeAttribute("success", testCaseStats.totals.assertions.allOk()); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds", m_testCaseTimer.getElapsedSeconds()); + + m_xml.endElement(); + } + + virtual void testGroupEnded(TestGroupStats const& testGroupStats) CATCH_OVERRIDE { + StreamingReporterBase::testGroupEnded(testGroupStats); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement("OverallResults") + .writeAttribute("successes", testGroupStats.totals.assertions.passed) + .writeAttribute("failures", testGroupStats.totals.assertions.failed) + .writeAttribute("expectedFailures", testGroupStats.totals.assertions.failedButOk); + m_xml.endElement(); + } + + virtual void testRunEnded(TestRunStats const& testRunStats) CATCH_OVERRIDE { + StreamingReporterBase::testRunEnded(testRunStats); + m_xml.scopedElement("OverallResults") + .writeAttribute("successes", testRunStats.totals.assertions.passed) + .writeAttribute("failures", testRunStats.totals.assertions.failed) + .writeAttribute("expectedFailures", testRunStats.totals.assertions.failedButOk); + m_xml.endElement(); + } + +private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth; +}; + +INTERNAL_CATCH_REGISTER_REPORTER("xml", XmlReporter) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_junit.hpp +#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED + +#include + +namespace Catch { + +class JunitReporter : public CumulativeReporterBase { +public: + JunitReporter(ReporterConfig const& _config) + : CumulativeReporterBase(_config) + , xml(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~JunitReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + virtual void noMatchingTestCases(std::string const& /*spec*/) CATCH_OVERRIDE {} + + virtual void testRunStarting(TestRunInfo const& runInfo) CATCH_OVERRIDE { + CumulativeReporterBase::testRunStarting(runInfo); + xml.startElement("testsuites"); + } + + virtual void testGroupStarting(GroupInfo const& groupInfo) CATCH_OVERRIDE { + suiteTimer.start(); + stdOutForSuite.str(""); + stdErrForSuite.str(""); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting(groupInfo); + } + + virtual bool assertionEnded(AssertionStats const& assertionStats) CATCH_OVERRIDE { + if (assertionStats.assertionResult.getResultType() == ResultWas::ThrewException) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded(assertionStats); + } + + virtual void testCaseEnded(TestCaseStats const& testCaseStats) CATCH_OVERRIDE { + stdOutForSuite << testCaseStats.stdOut; + stdErrForSuite << testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded(testCaseStats); + } + + virtual void testGroupEnded(TestGroupStats const& testGroupStats) CATCH_OVERRIDE { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded(testGroupStats); + writeGroup(*m_testGroups.back(), suiteTime); + } + + virtual void testRunEndedCumulative() CATCH_OVERRIDE { + xml.endElement(); + } + + void writeGroup(TestGroupNode const& groupNode, double suiteTime) { + XmlWriter::ScopedElement e = xml.scopedElement("testsuite"); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute("name", stats.groupInfo.name); + xml.writeAttribute("errors", unexpectedExceptions); + xml.writeAttribute("failures", stats.totals.assertions.failed - unexpectedExceptions); + xml.writeAttribute("tests", stats.totals.assertions.total()); + xml.writeAttribute("hostname", "tbd"); // !TBD + if (m_config->showDurations() == ShowDurations::Never) + xml.writeAttribute("time", ""); + else + xml.writeAttribute("time", suiteTime); + xml.writeAttribute("timestamp", "tbd"); // !TBD + + // Write test cases + for (TestGroupNode::ChildNodes::const_iterator + it + = groupNode.children.begin(), + itEnd = groupNode.children.end(); + it != itEnd; + ++it) + writeTestCase(**it); + + xml.scopedElement("system-out").writeText(trim(stdOutForSuite.str()), false); + xml.scopedElement("system-err").writeText(trim(stdErrForSuite.str()), false); + } + + void writeTestCase(TestCaseNode const& testCaseNode) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert(testCaseNode.children.size() == 1); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if (className.empty()) { + if (rootSection.childSections.empty()) + className = "global"; + } + writeSection(className, "", rootSection); + } + + void writeSection(std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode) { + std::string name = trim(sectionNode.stats.sectionInfo.name); + if (!rootName.empty()) + name = rootName + "/" + name; + + if (!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) { + XmlWriter::ScopedElement e = xml.scopedElement("testcase"); + if (className.empty()) { + xml.writeAttribute("classname", name); + xml.writeAttribute("name", "root"); + } else { + xml.writeAttribute("classname", className); + xml.writeAttribute("name", name); + } + xml.writeAttribute("time", Catch::toString(sectionNode.stats.durationInSeconds)); + + writeAssertions(sectionNode); + + if (!sectionNode.stdOut.empty()) + xml.scopedElement("system-out").writeText(trim(sectionNode.stdOut), false); + if (!sectionNode.stdErr.empty()) + xml.scopedElement("system-err").writeText(trim(sectionNode.stdErr), false); + } + for (SectionNode::ChildSections::const_iterator + it + = sectionNode.childSections.begin(), + itEnd = sectionNode.childSections.end(); + it != itEnd; + ++it) + if (className.empty()) + writeSection(name, "", **it); + else + writeSection(className, name, **it); + } + + void writeAssertions(SectionNode const& sectionNode) { + for (SectionNode::Assertions::const_iterator + it + = sectionNode.assertions.begin(), + itEnd = sectionNode.assertions.end(); + it != itEnd; + ++it) + writeAssertion(*it); + } + void writeAssertion(AssertionStats const& stats) { + AssertionResult const& result = stats.assertionResult; + if (!result.isOk()) { + std::string elementName; + switch (result.getResultType()) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement(elementName); + + xml.writeAttribute("message", result.getExpandedExpression()); + xml.writeAttribute("type", result.getTestMacroName()); + + std::ostringstream oss; + if (!result.getMessage().empty()) + oss << result.getMessage() << "\n"; + for (std::vector::const_iterator + it + = stats.infoMessages.begin(), + itEnd = stats.infoMessages.end(); + it != itEnd; + ++it) + if (it->type == ResultWas::Info) + oss << it->message << "\n"; + + oss << "at " << result.getSourceInfo(); + xml.writeText(oss.str(), false); + } + } + + XmlWriter xml; + Timer suiteTimer; + std::ostringstream stdOutForSuite; + std::ostringstream stdErrForSuite; + unsigned int unexpectedExceptions; +}; + +INTERNAL_CATCH_REGISTER_REPORTER("junit", JunitReporter) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_console.hpp +#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED + +namespace Catch { + +struct ConsoleReporter : StreamingReporterBase { + ConsoleReporter(ReporterConfig const& _config) + : StreamingReporterBase(_config) + , m_headerPrinted(false) {} + + virtual ~ConsoleReporter() CATCH_OVERRIDE; + static std::string getDescription() { + return "Reports test results as plain lines of text"; + } + + virtual void noMatchingTestCases(std::string const& spec) CATCH_OVERRIDE { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting(AssertionInfo const&) CATCH_OVERRIDE { + } + + virtual bool assertionEnded(AssertionStats const& _assertionStats) CATCH_OVERRIDE { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if (!m_config->includeSuccessfulResults() && result.isOk()) { + if (result.getResultType() != ResultWas::Warning) + return false; + printInfoMessages = false; + } + + lazyPrint(); + + AssertionPrinter printer(stream, _assertionStats, printInfoMessages); + printer.print(); + stream << std::endl; + return true; + } + + virtual void sectionStarting(SectionInfo const& _sectionInfo) CATCH_OVERRIDE { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); + } + virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if (m_headerPrinted) { + if (m_config->showDurations() == ShowDurations::Always) + stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + m_headerPrinted = false; + } else { + if (m_config->showDurations() == ShowDurations::Always) + stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + } + StreamingReporterBase::sectionEnded(_sectionStats); + } + + virtual void testCaseEnded(TestCaseStats const& _testCaseStats) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; + } + virtual void testGroupEnded(TestGroupStats const& _testGroupStats) CATCH_OVERRIDE { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << "\n" << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); + } + virtual void testRunEnded(TestRunStats const& _testRunStats) CATCH_OVERRIDE { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); + } + +private: + class AssertionPrinter { + void operator=(AssertionPrinter const&); + + public: + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , stats(_stats) + , result(_stats.assertionResult) + , colour(Colour::None) + , message(result.getMessage()) + , messages(_stats.infoMessages) + , printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with message"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + if (result.isOk()) + stream << "\n"; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << "\n"; + } + printMessage(); + } + + private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << "\n"; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Text(result.getExpandedExpression(), TextAttributes().setIndent(2)) << "\n"; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ":" + << "\n"; + for (std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); + it != itEnd; + ++it) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || it->type != ResultWas::Info) + stream << Text(it->message, TextAttributes().setIndent(2)) << "\n"; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; + }; + + void lazyPrint() { + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } + } + void lazyPrintRunInfo() { + stream << "\n" << getLineOfChars<'~'>() << "\n"; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; + } + void lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } + } + void printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + std::vector::const_iterator + it + = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + + if (!lineInfo.empty()) { + stream << getLineOfChars<'-'>() << "\n"; + Colour colourGuard(Colour::FileName); + stream << lineInfo << "\n"; + } + stream << getLineOfChars<'.'>() << "\n" << std::endl; + } + + void printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << "\n"; + } + void printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << "\n"; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } + } + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const& _string, std::size_t indent = 0) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Text(_string, TextAttributes() + .setIndent(indent + i) + .setInitialIndent(indent)) + << "\n"; + } + + struct SummaryColumn { + + SummaryColumn(std::string const& _label, Colour::Code _colour) + : label(_label) + , colour(_colour) {} + SummaryColumn addRow(std::size_t count) { + std::ostringstream oss; + oss << count; + std::string row = oss.str(); + for (std::vector::iterator it = rows.begin(); it != rows.end(); ++it) { + while (it->size() < row.size()) + *it = " " + *it; + while (it->size() > row.size()) + row = " " + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + }; + + void printTotals(Totals const& totals) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ")" + << "\n"; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } + } + void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) { + for (std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it) { + std::string value = it->rows[row]; + if (it->label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(it->colour) + << value << " " << it->label; + } + } + stream << "\n"; + } + + static std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; + } + static std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; + } + + void printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << "\n"; + } + void printSummaryDivider() { + stream << getLineOfChars<'-'>() << "\n"; + } + +private: + bool m_headerPrinted; +}; + +INTERNAL_CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_compact.hpp +#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED + +namespace Catch { + +struct CompactReporter : StreamingReporterBase { + + CompactReporter(ReporterConfig const& _config) + : StreamingReporterBase(_config) {} + + virtual ~CompactReporter(); + + static std::string getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting(AssertionInfo const&) { + } + + virtual bool assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if (!m_config->includeSuccessfulResults() && result.isOk()) { + if (result.getResultType() != ResultWas::Warning) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer(stream, _assertionStats, printInfoMessages); + printer.print(); + + stream << std::endl; + return true; + } + + virtual void testRunEnded(TestRunStats const& _testRunStats) { + printTotals(_testRunStats.totals); + stream << "\n" << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); + } + +private: + class AssertionPrinter { + void operator=(AssertionPrinter const&); + + public: + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , stats(_stats) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + + private: + // Colour::LightGrey + + static Colour::Code dimColour() { return Colour::FileName; } + +#ifdef CATCH_PLATFORM_MAC + static const char* failedString() { return "FAILED"; } + static const char* passedString() { return "PASSED"; } +#else + static const char* failedString() { return "failed"; } + static const char* passedString() { return "passed"; } +#endif + + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ":"; + } + + void printResultType(Colour::Code colour, std::string passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << " " << passOrFail; + } + stream << ":"; + } + } + + void printIssue(std::string issue) const { + stream << " " << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ";"; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << " " << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << "'"; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; + + // using messages.end() directly yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ":"; + } + + for (; itMessage != itEnd;) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << "'"; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + } + } + } + + private: + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; + + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + + std::string bothOrAll(std::size_t count) const { + return count == 1 ? "" : count == 2 ? "both " : "all "; + } + + void printTotals(const Totals& totals) const { + if (totals.testCases.total() == 0) { + stream << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? bothOrAll(totals.assertions.failed) : ""; + stream << "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " + << qualify_assertions_failed << pluralise(totals.assertions.failed, "assertion") << "."; + } else if (totals.assertions.total() == 0) { + stream << "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + stream << "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " + << pluralise(totals.assertions.failed, "assertion") << "."; + } else { + Colour colour(Colour::ResultSuccess); + stream << "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << " with " << pluralise(totals.assertions.passed, "assertion") << "."; + } + } +}; + +INTERNAL_CATCH_REGISTER_REPORTER("compact", CompactReporter) + +} // end namespace Catch + +namespace Catch { +// These are all here to avoid warnings about not having any out of line +// virtual methods +NonCopyable::~NonCopyable() {} +IShared::~IShared() {} +IStream::~IStream() CATCH_NOEXCEPT {} +FileStream::~FileStream() CATCH_NOEXCEPT {} +CoutStream::~CoutStream() CATCH_NOEXCEPT {} +DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} +StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} +IContext::~IContext() {} +IResultCapture::~IResultCapture() {} +ITestCase::~ITestCase() {} +ITestCaseRegistry::~ITestCaseRegistry() {} +IRegistryHub::~IRegistryHub() {} +IMutableRegistryHub::~IMutableRegistryHub() {} +IExceptionTranslator::~IExceptionTranslator() {} +IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} +IReporter::~IReporter() {} +IReporterFactory::~IReporterFactory() {} +IReporterRegistry::~IReporterRegistry() {} +IStreamingReporter::~IStreamingReporter() {} +AssertionStats::~AssertionStats() {} +SectionStats::~SectionStats() {} +TestCaseStats::~TestCaseStats() {} +TestGroupStats::~TestGroupStats() {} +TestRunStats::~TestRunStats() {} +CumulativeReporterBase::SectionNode::~SectionNode() {} +CumulativeReporterBase::~CumulativeReporterBase() {} + +StreamingReporterBase::~StreamingReporterBase() {} +ConsoleReporter::~ConsoleReporter() {} +CompactReporter::~CompactReporter() {} +IRunner::~IRunner() {} +IMutableContext::~IMutableContext() {} +IConfig::~IConfig() {} +XmlReporter::~XmlReporter() {} +JunitReporter::~JunitReporter() {} +TestRegistry::~TestRegistry() {} +FreeFunctionTestCase::~FreeFunctionTestCase() {} +IGeneratorInfo::~IGeneratorInfo() {} +IGeneratorsForTest::~IGeneratorsForTest() {} +WildcardPattern::~WildcardPattern() {} +TestSpec::Pattern::~Pattern() {} +TestSpec::NamePattern::~NamePattern() {} +TestSpec::TagPattern::~TagPattern() {} +TestSpec::ExcludedPattern::~ExcludedPattern() {} + +Matchers::Impl::StdString::Equals::~Equals() {} +Matchers::Impl::StdString::Contains::~Contains() {} +Matchers::Impl::StdString::StartsWith::~StartsWith() {} +Matchers::Impl::StdString::EndsWith::~EndsWith() {} + +void Config::dummy() {} + +namespace TestCaseTracking { + ITracker::~ITracker() {} + TrackerBase::~TrackerBase() {} + SectionTracker::~SectionTracker() {} + IndexTracker::~IndexTracker() {} +} +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +#ifdef CATCH_CONFIG_MAIN +// #included from: internal/catch_default_main.hpp +#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED + +#ifndef __OBJC__ + +// Standard C/C++ main entry point +int main(int argc, char* argv[]) { + return Catch::Session().run(argc, argv); +} + +#else // __OBJC__ + +// Objective-C entry point +int main(int argc, char* const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run(argc, (char* const*)argv); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +#endif + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +#undef CLARA_CONFIG_MAIN +#endif + +////// + +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE") +#define CATCH_REQUIRE_FALSE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE") + +#define CATCH_REQUIRE_THROWS(expr) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS") +#define CATCH_REQUIRE_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS(expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS") +#define CATCH_REQUIRE_THROWS_WITH(expr, matcher) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH") +#define CATCH_REQUIRE_NOTHROW(expr) INTERNAL_CATCH_NO_THROW(expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW") + +#define CATCH_CHECK(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK") +#define CATCH_CHECK_FALSE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE") +#define CATCH_CHECKED_IF(expr) INTERNAL_CATCH_IF(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF") +#define CATCH_CHECKED_ELSE(expr) INTERNAL_CATCH_ELSE(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE") +#define CATCH_CHECK_NOFAIL(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL") + +#define CATCH_CHECK_THROWS(expr) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS") +#define CATCH_CHECK_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS(expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS") +#define CATCH_CHECK_THROWS_WITH(expr, matcher) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH") +#define CATCH_CHECK_NOTHROW(expr) INTERNAL_CATCH_NO_THROW(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW") + +#define CHECK_THAT(arg, matcher) INTERNAL_CHECK_THAT(arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT") +#define CATCH_REQUIRE_THAT(arg, matcher) INTERNAL_CHECK_THAT(arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT") + +#define CATCH_INFO(msg) INTERNAL_CATCH_INFO(msg, "CATCH_INFO") +#define CATCH_WARN(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg) +#define CATCH_SCOPED_INFO(msg) INTERNAL_CATCH_INFO(msg, "CATCH_INFO") +#define CATCH_CAPTURE(msg) INTERNAL_CATCH_INFO(#msg " := " << msg, "CATCH_CAPTURE") +#define CATCH_SCOPED_CAPTURE(msg) INTERNAL_CATCH_INFO(#msg " := " << msg, "CATCH_CAPTURE") + +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define CATCH_TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) +#define CATCH_TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_METHOD_AS_TEST_CASE(method, ...) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) +#define CATCH_REGISTER_TEST_CASE(Function, ...) INTERNAL_CATCH_REGISTER_TESTCASE(Function, __VA_ARGS__) +#define CATCH_SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) +#define CATCH_FAIL(...) INTERNAL_CATCH_MSG(Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__) +#define CATCH_SUCCEED(...) INTERNAL_CATCH_MSG(Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__) +#else +#define CATCH_TEST_CASE(name, description) INTERNAL_CATCH_TESTCASE(name, description) +#define CATCH_TEST_CASE_METHOD(className, name, description) INTERNAL_CATCH_TEST_CASE_METHOD(className, name, description) +#define CATCH_METHOD_AS_TEST_CASE(method, name, description) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, name, description) +#define CATCH_REGISTER_TEST_CASE(function, name, description) INTERNAL_CATCH_REGISTER_TESTCASE(function, name, description) +#define CATCH_SECTION(name, description) INTERNAL_CATCH_SECTION(name, description) +#define CATCH_FAIL(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg) +#define CATCH_SUCCEED(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg) +#endif +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE("", "") + +#define CATCH_REGISTER_REPORTER(name, reporterType) INTERNAL_CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LEGACY_REPORTER(name, reporterType) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER(name, reporterType) + +#define CATCH_GENERATE(expr) INTERNAL_CATCH_GENERATE(expr) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define CATCH_SCENARIO(...) CATCH_TEST_CASE("Scenario: " __VA_ARGS__) +#define CATCH_SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) +#else +#define CATCH_SCENARIO(name, tags) CATCH_TEST_CASE("Scenario: " name, tags) +#define CATCH_SCENARIO_METHOD(className, name, tags) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " name, tags) +#endif +#define CATCH_GIVEN(desc) CATCH_SECTION(std::string("Given: ") + desc, "") +#define CATCH_WHEN(desc) CATCH_SECTION(std::string(" When: ") + desc, "") +#define CATCH_AND_WHEN(desc) CATCH_SECTION(std::string(" And: ") + desc, "") +#define CATCH_THEN(desc) CATCH_SECTION(std::string(" Then: ") + desc, "") +#define CATCH_AND_THEN(desc) CATCH_SECTION(std::string(" And: ") + desc, "") + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::Normal, "REQUIRE") +#define REQUIRE_FALSE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE") + +#define REQUIRE_THROWS(expr) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS") +#define REQUIRE_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS(expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS") +#define REQUIRE_THROWS_WITH(expr, matcher) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH") +#define REQUIRE_NOTHROW(expr) INTERNAL_CATCH_NO_THROW(expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW") + +#define CHECK(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK") +#define CHECK_FALSE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE") +#define CHECKED_IF(expr) INTERNAL_CATCH_IF(expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF") +#define CHECKED_ELSE(expr) INTERNAL_CATCH_ELSE(expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE") +#define CHECK_NOFAIL(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL") + +#define CHECK_THROWS(expr) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS") +#define CHECK_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS(expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS") +#define CHECK_THROWS_WITH(expr, matcher) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH") +#define CHECK_NOTHROW(expr) INTERNAL_CATCH_NO_THROW(expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW") + +#define CHECK_THAT(arg, matcher) INTERNAL_CHECK_THAT(arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT") +#define REQUIRE_THAT(arg, matcher) INTERNAL_CHECK_THAT(arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT") + +#define INFO(msg) INTERNAL_CATCH_INFO(msg, "INFO") +#define WARN(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg) +#define SCOPED_INFO(msg) INTERNAL_CATCH_INFO(msg, "INFO") +#define CAPTURE(msg) INTERNAL_CATCH_INFO(#msg " := " << msg, "CAPTURE") +#define SCOPED_CAPTURE(msg) INTERNAL_CATCH_INFO(#msg " := " << msg, "CAPTURE") + +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) +#define TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) +#define METHOD_AS_TEST_CASE(method, ...) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) +#define REGISTER_TEST_CASE(Function, ...) INTERNAL_CATCH_REGISTER_TESTCASE(Function, __VA_ARGS__) +#define SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) +#define FAIL(...) INTERNAL_CATCH_MSG(Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__) +#define SUCCEED(...) INTERNAL_CATCH_MSG(Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__) +#else +#define TEST_CASE(name, description) INTERNAL_CATCH_TESTCASE(name, description) +#define TEST_CASE_METHOD(className, name, description) INTERNAL_CATCH_TEST_CASE_METHOD(className, name, description) +#define METHOD_AS_TEST_CASE(method, name, description) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, name, description) +#define REGISTER_TEST_CASE(method, name, description) INTERNAL_CATCH_REGISTER_TESTCASE(method, name, description) +#define SECTION(name, description) INTERNAL_CATCH_SECTION(name, description) +#define FAIL(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg) +#define SUCCEED(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg) +#endif +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE("", "") + +#define REGISTER_REPORTER(name, reporterType) INTERNAL_CATCH_REGISTER_REPORTER(name, reporterType) +#define REGISTER_LEGACY_REPORTER(name, reporterType) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER(name, reporterType) + +#define GENERATE(expr) INTERNAL_CATCH_GENERATE(expr) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION(signature) INTERNAL_CATCH_TRANSLATE_EXCEPTION(signature) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define SCENARIO(...) TEST_CASE("Scenario: " __VA_ARGS__) +#define SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) +#else +#define SCENARIO(name, tags) TEST_CASE("Scenario: " name, tags) +#define SCENARIO_METHOD(className, name, tags) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " name, tags) +#endif +#define GIVEN(desc) SECTION(std::string(" Given: ") + desc, "") +#define WHEN(desc) SECTION(std::string(" When: ") + desc, "") +#define AND_WHEN(desc) SECTION(std::string("And when: ") + desc, "") +#define THEN(desc) SECTION(std::string(" Then: ") + desc, "") +#define AND_THEN(desc) SECTION(std::string(" And: ") + desc, "") + +using Catch::Detail::Approx; + +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/stationapi/externals/easyloggingpp/LICENCE b/stationapi/externals/easyloggingpp/LICENCE new file mode 100755 index 0000000..6baa0e2 --- /dev/null +++ b/stationapi/externals/easyloggingpp/LICENCE @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2015 muflihun.com + +http://github.com/easylogging/ +http://easylogging.muflihun.com +http://muflihun.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/stationapi/externals/easyloggingpp/easylogging++.h b/stationapi/externals/easyloggingpp/easylogging++.h new file mode 100755 index 0000000..f9a67fe --- /dev/null +++ b/stationapi/externals/easyloggingpp/easylogging++.h @@ -0,0 +1,6663 @@ +// +// Easylogging++ v9.80 +// Single-header only, cross-platform logging library for C++ applications +// +// Copyright (c) 2015 muflihun.com +// +// This library is released under the MIT Licence. +// http://easylogging.muflihun.com/licence.php +// +// easylogging@muflihun.com +// +// https://github.com/easylogging/easyloggingpp +// http://easylogging.muflihun.com +// http://muflihun.com +// +#ifndef EASYLOGGINGPP_H +#define EASYLOGGINGPP_H +// Compilers and C++0x/C++11 Evaluation +#if defined(__GNUC__) +# define ELPP_COMPILER_GCC 1 +# define ELPP_GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# elif(ELPP_GCC_VERSION >= 40801) +# define ELPP_CXX11 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +#endif // defined(__GNUC__) +// Visual C++ +#if defined(_MSC_VER) +# define ELPP_COMPILER_MSVC 1 +# define ELPP_CRT_DBG_WARNINGS 1 +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif // (_MSC_VER == 1600) +#endif // defined(_MSC_VER) +// Clang++ +#if defined(__clang__) && (__clang__ == 1) +# define ELPP_COMPILER_CLANG 1 +# define ELPP_CLANG_VERSION (__clang_major__ * 10000 \ + + __clang_minor__ * 100 \ + + __clang_patchlevel__) +# if (ELPP_CLANG_VERSION >= 30300) +# define ELPP_CXX11 1 +# endif // (ELPP_CLANG_VERSION >= 30300) +#endif // defined(__clang__) && (__clang__ == 1) +// MinGW +#if defined(__MINGW32__) || defined(__MINGW64__) +# define ELPP_MINGW 1 +#endif // defined(__MINGW32__) || defined(__MINGW64__) +// Cygwin +#if defined(__CYGWIN__) && (__CYGWIN__ == 1) +# define ELPP_CYGWIN 1 +#endif // defined(__CYGWIN__) && (__CYGWIN__ == 1) +// Intel C++ +#if defined(__INTEL_COMPILER) +# define ELPP_COMPILER_INTEL 1 +#endif +// Operating System Evaluation +// Windows +#if defined(_WIN32) || defined(_WIN64) +# define ELPP_OS_WINDOWS 1 +#endif // defined(_WIN32) || defined(_WIN64) +// Linux +#if (defined(__linux) || defined(__linux__)) +# define ELPP_OS_LINUX 1 +#endif // (defined(__linux) || defined(__linux__)) +// Mac +#if defined(__APPLE__) +# define ELPP_OS_MAC 1 +#endif // defined(__APPLE__) +// FreeBSD +#if defined(__FreeBSD__) +# define ELPP_OS_FREEBSD 1 +#endif +// Solaris +#if defined(__sun) +# define ELPP_OS_SOLARIS 1 +#endif +// Unix +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) +# define ELPP_OS_UNIX 1 +#endif // ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) +// Android +#if defined(__ANDROID__) +# define ELPP_OS_ANDROID 1 +#endif // defined(__ANDROID__) +// Evaluating Cygwin as *nix OS +#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 +#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_MSG) +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +// Internal Assertions and errors +#if !defined(ELPP_DISABLE_ASSERT) +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ + std::stringstream internalInfoStream; internalInfoStream << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ + << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ + << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ + "ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ + std::stringstream internalInfoStream; internalInfoStream << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ + << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ + << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ + << ELPP_INTERNAL_DEBUGGING_ENDL; } +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +#else +# define ELPP_ASSERT(x, y) +#endif //(!defined(ELPP_DISABLE_ASSERT) +#if ELPP_COMPILER_MSVC +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ + { char buff[256]; strerror_s(buff, 256, errno); \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 +#else +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 +#endif // ELPP_COMPILER_MSVC +#if defined(ELPP_DEBUG_ERRORS) +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ + std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ + << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ + << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ + if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) +#endif // defined(ELPP_DEBUG_ERRORS) +#if (defined(ELPP_DEBUG_INFO)) +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ + std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ + << ELPP_INTERNAL_DEBUGGING_ENDL; }} +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) +#endif // (defined(ELPP_DEBUG_INFO)) +#if defined(ELPP_STACKTRACE_ON_CRASH) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW) +# define ELPP_STACKTRACE 1 +# else +# if ELPP_COMPILER_MSVC +# pragma message("Stack trace not available for this compiler") +# else +# warning "Stack trace not available for this compiler"; +# endif // ELPP_COMPILER_MSVC +# endif // ELPP_COMPILER_GCC +#endif // (defined(ELPP_STACKTRACE_ON_CRASH)) +// Miscellaneous macros +#define ELPP_UNUSED(x) (void)x +#if ELPP_OS_UNIX +// Log file permissions for unix-based systems +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +#endif // ELPP_OS_UNIX +#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) +#else +# define ELPP_EXPORT +#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +// Some special functions that are VC++ specific +#undef STRTOK +#undef STRERROR +#undef STRCAT +#undef STRCPY +#if ELPP_CRT_DBG_WARNINGS +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) +#else +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) +#endif +// Compiler specific support evaluations +#if (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD) +# define ELPP_USE_STD_THREADING 1 +#endif // (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD) +#undef ELPP_FINAL +#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +# define ELPP_FINAL +#else +# define ELPP_FINAL final +#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +#if defined(ELPP_EXPERIMENTAL_ASYNC) +# define ELPP_ASYNC_LOGGING 1 +#else +# define ELPP_ASYNC_LOGGING 0 +#endif // defined(ELPP_EXPERIMENTAL_ASYNC) +#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +# define ELPP_THREADING_ENABLED 1 +#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +// Function macro ELPP_FUNC +#undef ELPP_FUNC +#if ELPP_COMPILER_MSVC // Visual C++ +# define ELPP_FUNC __FUNCSIG__ +#elif ELPP_COMPILER_GCC // GCC +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_INTEL // Intel C++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_CLANG // Clang++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#else +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) +#endif // defined(_MSC_VER) +#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED +// Keep following line commented until features are fixed +#if ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800) +# define ELPP_VARIADIC_TEMPLATES_SUPPORTED 1 +#endif // ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800) +// Logging Enable/Disable macros +#if (!defined(ELPP_DISABLE_LOGS)) +# define ELPP_LOGGING_ENABLED 1 +#endif // (!defined(ELPP_DISABLE_LOGS)) +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) +# define ELPP_DEBUG_LOG 1 +#else +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) +#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_INFO_LOG 1 +#else +# define ELPP_INFO_LOG 0 +#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_WARNING_LOG 1 +#else +# define ELPP_WARNING_LOG 0 +#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_ERROR_LOG 1 +#else +# define ELPP_ERROR_LOG 0 +#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_FATAL_LOG 1 +#else +# define ELPP_FATAL_LOG 0 +#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_TRACE_LOG 1 +#else +# define ELPP_TRACE_LOG 0 +#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_VERBOSE_LOG 1 +#else +# define ELPP_VERBOSE_LOG 0 +#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!(ELPP_CXX0X || ELPP_CXX11)) +# error "Easylogging++ 9.0+ is only compatible with C++0x (or higher) compliant compiler" +#endif // (!(ELPP_CXX0X || ELPP_CXX11)) +// Headers +#if defined(ELPP_SYSLOG) +# include +#endif // defined(ELPP_SYSLOG) +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(ELPP_UNICODE) +# include +# if ELPP_OS_WINDOWS +# include +# endif // ELPP_OS_WINDOWS +#endif // defined(ELPP_UNICODE) +#if ELPP_STACKTRACE +# include +# include +#endif // ELPP_STACKTRACE +#if ELPP_OS_ANDROID +# include +#endif // ELPP_OS_ANDROID +#if ELPP_OS_UNIX +# include +# include +#elif ELPP_OS_WINDOWS +# include +# include +# if defined(WIN32_LEAN_AND_MEAN) +# if defined(ELPP_WINSOCK2) +# include +# else +# include +# endif // defined(ELPP_WINSOCK2) +# endif // defined(WIN32_LEAN_AND_MEAN) +#endif // ELPP_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# include +# include +# else +# if ELPP_OS_UNIX +# include +# endif // ELPP_OS_UNIX +# endif // ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED +#if ELPP_ASYNC_LOGGING +# include +# include +# include +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_STL_LOGGING) +// For logging STL based templates +# include +# include +# include +# include +# include +# include +# if defined(ELPP_LOG_STD_ARRAY) +# include +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) +# include +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) +# include +# endif // defined(ELPP_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) +// For logging Qt based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) +// For logging boost based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_BOOST_LOGGING) +#if defined(ELPP_WXWIDGETS_LOGGING) +// For logging wxWidgets based classes & templates +# include +#endif // defined(ELPP_WXWIDGETS_LOGGING) +// Forward declarations +namespace el { +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING +class DefaultPerformanceTrackingCallback; +} // namespace base +} // namespace el +/// @brief Easylogging++ entry namespace +namespace el { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { +#undef ELPP_LITERAL +#undef ELPP_STRLEN +#undef ELPP_COUT +#if defined(ELPP_UNICODE) +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; +#else +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; +#endif // defined(ELPP_UNICODE) +#if defined(ELPP_CUSTOM_COUT_LINE) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +#else +# define ELPP_COUT_LINE(logLine) logLine << std::flush +#endif // defined(ELPP_CUSTOM_COUT_LINE) +typedef unsigned short EnumType; +typedef std::shared_ptr StoragePointer; +typedef int VerboseLevel; +typedef std::shared_ptr LogDispatchCallbackPtr; +typedef std::shared_ptr PerformanceTrackingCallbackPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { +protected: + NoCopy(void) {} +private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { +private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situtaions + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { +public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (level == Level::Global) return "GLOBAL"; + if (level == Level::Debug) return "DEBUG"; + if (level == Level::Info) return "INFO"; + if (level == Level::Warning) return "WARNING"; + if (level == Level::Error) return "ERROR"; + if (level == Level::Fatal) return "FATAL"; + if (level == Level::Verbose) return "VERBOSE"; + if (level == Level::Trace) return "TRACE"; + return "UNKNOWN"; + } + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr) { + if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0)) + return Level::Global; + if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0)) + return Level::Debug; + if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0)) + return Level::Info; + if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0)) + return Level::Warning; + if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0)) + return Level::Error; + if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0)) + return Level::Fatal; + if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0)) + return Level::Verbose; + if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0)) + return Level::Trace; + return Level::Unknown; + } + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static inline void forEachLevel(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType lIndexMax = LevelHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= lIndexMax); + } +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for correponding level and logger + Filename = 16, + /// @brief Specifies milliseconds width. Width can be within range (1-6) + MillisecondsWidth = 32, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { +public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast(configurationType); + } + /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (configurationType == ConfigurationType::Enabled) return "ENABLED"; + if (configurationType == ConfigurationType::Filename) return "FILENAME"; + if (configurationType == ConfigurationType::Format) return "FORMAT"; + if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; + if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; + if (configurationType == ConfigurationType::MillisecondsWidth) return "MILLISECONDS_WIDTH"; + if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; + if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; + if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; + return "UNKNOWN"; + } + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr) { + if ((strcmp(configStr, "ENABLED") == 0) || (strcmp(configStr, "enabled") == 0)) + return ConfigurationType::Enabled; + if ((strcmp(configStr, "TO_FILE") == 0) || (strcmp(configStr, "to_file") == 0)) + return ConfigurationType::ToFile; + if ((strcmp(configStr, "TO_STANDARD_OUTPUT") == 0) || (strcmp(configStr, "to_standard_output") == 0)) + return ConfigurationType::ToStandardOutput; + if ((strcmp(configStr, "FORMAT") == 0) || (strcmp(configStr, "format") == 0)) + return ConfigurationType::Format; + if ((strcmp(configStr, "FILENAME") == 0) || (strcmp(configStr, "filename") == 0)) + return ConfigurationType::Filename; + if ((strcmp(configStr, "MILLISECONDS_WIDTH") == 0) || (strcmp(configStr, "milliseconds_width") == 0)) + return ConfigurationType::MillisecondsWidth; + if ((strcmp(configStr, "PERFORMANCE_TRACKING") == 0) || (strcmp(configStr, "performance_tracking") == 0)) + return ConfigurationType::PerformanceTracking; + if ((strcmp(configStr, "MAX_LOG_FILE_SIZE") == 0) || (strcmp(configStr, "max_log_file_size") == 0)) + return ConfigurationType::MaxLogFileSize; + if ((strcmp(configStr, "LOG_FLUSH_THRESHOLD") == 0) || (strcmp(configStr, "log_flush_threshold") == 0)) + return ConfigurationType::LogFlushThreshold; + return ConfigurationType::Unknown; + } + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= cIndexMax); + } +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384 +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { + // Level log values - These are values that are replaced in place of %level format specifier + static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO "); + static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); + static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARN "); + static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); + static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); + static const base::type::char_t* kVerboseLevelLogValue = ELPP_LITERAL("VER"); + static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); + static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); + static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); + static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); + static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); + static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); + static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); + static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); + // Format specifiers - These are used to define log format + static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); + static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); + static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); + static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); + static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); + static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); + static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); + static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); + static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); + static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); + static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); + static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); + static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); + static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); + static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); + static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; + // Date/time + static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", + "September", "October", "November", "December" }; + static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; + static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; + static const int kYearBase = 1900; + static const char* kAm = "AM"; + static const char* kPm = "PM"; + // Miscellaneous constants + static const char* kDefaultLoggerId = "default"; + static const char* kPerformanceLoggerId = "performance"; + static const char* kSysLogLoggerId = "syslog"; + static const char* kNullPointer = "nullptr"; + static const char kFormatSpecifierChar = '%'; +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + static const char kFormatSpecifierCharValue = 'v'; +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + static const unsigned int kMaxLogPerContainer = 100; + static const unsigned int kMaxLogPerCounter = 100000; + static const unsigned int kDefaultMillisecondsWidth = 3; + static const base::type::VerboseLevel kMaxVerboseLevel = 9; + static const char* kUnknownUser = "user"; + static const char* kUnknownHost = "unknown-host"; +#if defined(ELPP_DEFAULT_LOG_FILE) + static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +#else +# if ELPP_OS_UNIX +# if ELPP_OS_ANDROID + static const char* kDefaultLogFile = "logs/myeasylog.log"; +# else + static const char* kDefaultLogFile = "logs/myeasylog.log"; +# endif // ELPP_OS_ANDROID +# elif ELPP_OS_WINDOWS + static const char* kDefaultLogFile = "logs\\myeasylog.log"; +# endif // ELPP_OS_UNIX +#endif // defined(ELPP_DEFAULT_LOG_FILE) +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + static const char* kDefaultLogFileParam = "--default-log-file"; +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + static const char* kLoggingFlagsParam = "--logging-flags"; +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +#if ELPP_OS_WINDOWS + static const char* kFilePathSeperator = "\\"; +#else + static const char* kFilePathSeperator = "/"; +#endif // ELPP_OS_WINDOWS + static const char* kValidLoggerIdSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; + static const char* kConfigurationComment = "##"; + static const char* kConfigurationLevel = "*"; + static const char* kConfigurationLoggerId = "--"; + static const std::size_t kSourceFilenameMaxLength = 100; + static const std::size_t kSourceLineMaxLength = 10; + static const Level kPerformanceTrackerDefaultLevel = Level::Info; + const struct { + double value; + const base::type::char_t* unit; + } kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("mis") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } + }; + static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); + const struct { + int numb; + const char* name; + const char* brief; + const char* detail; + } kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." }, + { SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithemetic operation issue such as division by zero or operation resulting in overflow." }, + { SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data."}, + { SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." }, + { SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." }, + }; + static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +} // namespace consts +} // namespace base +typedef std::function PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1<<1, LoggerId = 1<<2, File = 1<<3, Line = 1<<4, Location = 1<<5, Function = 1<<6, + User = 1<<7, Host = 1<<8, LogMessage = 1<<9, VerboseLevel = 1<<10, AppName = 1<<11, ThreadId = 1<<12, + Level = 1<<13, FileBase = 1<<14, LevelShort = 1<<15 +}; +/// @brief A milliseconds width class containing actual width and offset for date/time +class MillisecondsWidth { +public: + MillisecondsWidth(void) { init(base::consts::kDefaultMillisecondsWidth); } + explicit MillisecondsWidth(int width) { init(width); } + bool operator==(const MillisecondsWidth& msWidth) { return m_width == msWidth.m_width && m_offset == msWidth.m_offset; } + int m_width; unsigned int m_offset; +private: + void init(int width) { + if (width < 1 || width > 6) { + width = base::consts::kDefaultMillisecondsWidth; + } + m_width = width; + switch (m_width) { + case 3: m_offset = 1000; break; + case 4: m_offset = 100; break; + case 5: m_offset = 10; break; + case 6: m_offset = 1; break; + default: m_offset = 1000; break; + } + } +}; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template +static inline +typename std::enable_if::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned +static inline const char* charPtrVal(const char* pointer) { + return pointer == nullptr ? base::consts::kNullPointer : pointer; +} +/// @brief Aborts application due with user-defined status +static inline void abort(int status, const std::string& reason = std::string()) { + // Both status and reason params are there for debugging with tools like gdb etc + ELPP_UNUSED(status); + ELPP_UNUSED(reason); +#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) + // Ignore msvc critical error dialog - break instead (on debug mode) + _asm int 3 +#else + ::abort(); +#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as
flag = bitwise::Or(MyEnum::val1, flag);
+namespace bitwise { +template +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast(flag) & static_cast(e); +} +template +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast(flag) & ~(static_cast(e)); +} +template +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast(flag) | static_cast(e); +} +} // namespace bitwise +template +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or(e, *flag); +} +template +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not(e, *flag); +} +template +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::mutex +class Mutex : base::NoCopy { +public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_init(&m_underlyingMutex, nullptr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + +private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template +class ScopedLock : base::NoCopy { +public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } +private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static inline std::string getCurrentThreadId(void) { + std::stringstream ss; +# if (ELPP_OS_WINDOWS) + ss << GetCurrentThreadId(); +# endif // (ELPP_OS_WINDOWS) + return ss.str(); +} +static inline void msleep(int) { + // No implementation for non std::thread version +} +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock ScopedLock; +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static inline std::string getCurrentThreadId(void) { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} +static inline void msleep(int ms) { + // Only when async logging enabled - this is because async is strict on compiler +#if ELPP_ASYNC_LOGGING + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +#endif // ELPP_ASYNC_LOGGING +} +typedef std::mutex Mutex; +typedef std::lock_guard ScopedLock; +# endif // !ELPP_USE_STD_THREADING +#else +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { +public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { return true; } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template +class NoScopedLock : base::NoCopy { +public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } +private: + NoScopedLock(void); +}; +} // namespace internal +static inline std::string getCurrentThreadId(void) { + return std::string(); +} +static inline void msleep(int) { + // No custom implementation +} +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { +public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } +protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} +private: + base::threading::Mutex m_mutex; +}; +} // namespace threading +namespace utils { +class File : base::StaticClass { +public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename) { + base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), + base::type::fstream_t::out | base::type::fstream_t::app); +#if defined(ELPP_UNICODE) + std::locale elppUnicodeLocale(""); +#if ELPP_OS_WINDOWS + std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16); + elppUnicodeLocale = elppUnicodeLocaleWindows; +#endif + fs->imbue(elppUnicodeLocale); +#endif // defined(ELPP_UNICODE) + if (fs->is_open()) { + fs->flush(); + } else { + base::utils::safeDelete(fs); + ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); + } + return fs; + } + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs) { + if (fs == nullptr) { + return 0; + } + std::streampos currPos = fs->tellg(); + fs->seekg(0, fs->end); + std::size_t size = static_cast(fs->tellg()); + fs->seekg(currPos); + return size; + } + + /// @brief Determines whether or not provided path exist in current file system + static inline bool pathExists(const char* path, bool considerFile = false) { + if (path == nullptr) { + return false; + } +#if ELPP_OS_UNIX + ELPP_UNUSED(considerFile); + struct stat st; + return (stat(path, &st) == 0); +#elif ELPP_OS_WINDOWS + DWORD fileType = GetFileAttributesA(path); + if (fileType == INVALID_FILE_ATTRIBUTES) { + return false; + } + return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); +#endif // ELPP_OS_UNIX + } + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path) { + if (path.empty()) { + return false; + } + if (base::utils::File::pathExists(path.c_str())) { + return true; + } + int status = -1; + + char* currPath = const_cast(path.c_str()); + std::string builtPath = std::string(); +#if ELPP_OS_UNIX + if (path[0] == '/') { + builtPath = "/"; + } + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + // Use secure functions API + char* nextTok_ = nullptr; + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_); + ELPP_UNUSED(nextTok_); +#endif // ELPP_OS_UNIX + while (currPath != nullptr) { + builtPath.append(currPath); + builtPath.append(base::consts::kFilePathSeperator); +#if ELPP_OS_UNIX + status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + status = _mkdir(builtPath.c_str()); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_); +#endif // ELPP_OS_UNIX + } + if (status == -1) { + ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); + return false; + } + return true; + } + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* seperator = base::consts::kFilePathSeperator) { + if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) { + return fullPath; + } + std::size_t lastSlashAt = fullPath.find_last_of(seperator); + if (lastSlashAt == 0) { + return std::string(seperator); + } + return fullPath.substr(0, lastSlashAt + 1); + } + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength) { + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); + } + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* seperator = base::consts::kFilePathSeperator) { + const char *filename = fullPath.c_str(); + std::size_t lastSlashAt = fullPath.find_last_of(seperator); + filename += lastSlashAt ? lastSlashAt+1 : 0; + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); + } +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { +public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern) { + while (*pattern) { + switch (*pattern) { + case '?': + if (!*str) + return false; + ++str; + ++pattern; + break; + case '*': + if (wildCardMatch(str, pattern + 1)) + return true; + if (*str && wildCardMatch(str + 1, pattern)) + return true; + return false; + break; + default: + if (*str++ != *pattern++) + return false; + break; + } + } + return !*str && !*pattern; + } + + /// @brief Trims string from start + /// @param [in,out] str String to trim + static inline std::string& ltrim(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun(&std::isspace)))); + return str; + } + + /// @brief Trim string from end + /// @param [in,out] str String to trim + static inline std::string& rtrim(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun(&std::isspace))).base(), str.end()); + return str; + } + + /// @brief Trims string from left and right + /// @param [in,out] str String to trim + static inline std::string& trim(std::string& str) { + return ltrim(rtrim(str)); + } + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static inline bool startsWith(const std::string& str, const std::string& start) { + return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); + } + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static inline bool endsWith(const std::string& str, const std::string& end) { + return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); + } + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) { + std::replace(str.begin(), str.end(), replaceWhat, replaceWith); + return str; + } + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT + const std::string& replaceWith) { + if (replaceWhat == replaceWith) + return str; + std::size_t foundAt = std::string::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { + str.replace(foundAt, replaceWhat.length(), replaceWith); + } + return str; + } + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT + const base::type::string_t& replaceWith) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { + str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } else { + str.replace(foundAt, replaceWhat.length(), replaceWith); + return; + } + } + } +#if defined(ELPP_UNICODE) + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT + const std::string& replaceWith) { + replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); + } +#endif // defined(ELPP_UNICODE) + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static inline std::string& toUpper(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), ::toupper); + return str; + } + + /// @brief Compares cstring equality - uses strcmp + static inline bool cStringEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + return strcmp(s1, s2) == 0; + } + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + if (strlen(s1) != strlen(s2)) return false; + while (*s1 != '\0' && *s2 != '\0') { + if (::toupper(*s1) != ::toupper(*s2)) return false; + ++s1; + ++s2; + } + return true; + } + + /// @brief Returns true if c exist in str + static inline bool contains(const char* str, char c) { + for (; *str; ++str) { + if (*str == c) + return true; + } + return false; + } + + static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) { + char localBuff[10] = ""; + char* p = localBuff + sizeof(localBuff) - 2; + if (n > 0) { + for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) + *--p = static_cast(n % 10 + '0'); + } else { + *--p = '0'; + --len; + } + if (zeroPadded) + while (p > localBuff && len-- > 0) *--p = static_cast('0'); + return addToBuff(p, buf, bufLim); + } + + static inline char* addToBuff(const char* str, char* buf, const char* bufLim) { + while ((buf < bufLim) && ((*buf = *str++) != '\0')) + ++buf; + return buf; + } + + static inline char* clearBuff(char buff[], std::size_t lim) { + STRCPY(buff, "", lim); + ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro + return buff; + } + + /// @brief Converst wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line) { + std::size_t len_ = wcslen(line) + 1; + char* buff_ = static_cast(malloc(len_ + 1)); +# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + std::wcstombs(buff_, line, len_); +# elif ELPP_OS_WINDOWS + std::size_t convCount_ = 0; + mbstate_t mbState_; + ::memset(static_cast(&mbState_), 0, sizeof(mbState_)); + wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); +# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + return buff_; + } +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { +public: +#if ELPP_OS_WINDOWS + /// @brief Gets environment variables for Windows based OS. + /// We are not using getenv(const char*) because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname) { + const DWORD bufferLen = 50; + static char buffer[bufferLen]; + if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { + return buffer; + } + return nullptr; + } +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID + /// @brief Reads android property value + static inline std::string getProperty(const char* prop) { + char propVal[PROP_VALUE_MAX + 1]; + int ret = __system_property_get(prop, propVal); + return ret == 0 ? std::string() : std::string(propVal); + } + + /// @brief Reads android device name + static std::string getDeviceName(void) { + std::stringstream ss; + std::string manufacturer = getProperty("ro.product.manufacturer"); + std::string model = getProperty("ro.product.model"); + if (manufacturer.empty() || model.empty()) { + return std::string(); + } + ss << manufacturer << "-" << model; + return ss.str(); + } +#endif // ELPP_OS_ANDROID + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command) { +#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + if (command == nullptr) { + return std::string(); + } + FILE* proc = nullptr; + if ((proc = popen(command, "r")) == nullptr) { + ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); + return std::string(); + } + char hBuff[4096]; + if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { + pclose(proc); + if (hBuff[strlen(hBuff) - 1] == '\n') { + hBuff[strlen(hBuff) - 1] = '\0'; + } + return std::string(hBuff); + } + return std::string(); +#else + ELPP_UNUSED(command); + return std::string(); +#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + } + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) { +#if ELPP_OS_UNIX + const char* val = getenv(variableName); +#elif ELPP_OS_WINDOWS + const char* val = getWindowsEnvironmentVariable(variableName); +#endif // ELPP_OS_UNIX + if ((val == nullptr) || ((strcmp(val, "") == 0))) { +#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + // Try harder on unix-based systems + std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); + if (valBash.empty()) { + return std::string(defaultVal); + } else { + return valBash; + } +#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX + ELPP_UNUSED(alternativeBashCommand); + return std::string(defaultVal); +#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + } + return std::string(val); + } + /// @brief Gets current username. + static inline std::string currentUser(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownUser); + return std::string("android"); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID + } + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen + static inline std::string currentHost(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownHost); + return getDeviceName(); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID + } + /// @brief Whether or not terminal supports colors + static inline bool termSupportsColor(void) { + std::string term = getEnvironmentVariable("TERM", ""); + return term == "xterm" || term == "xterm-color" || term == "xterm-256color" || + term == "screen" || term == "linux" || term == "cygwin"; + } +}; +extern std::string s_currentUser; +extern std::string s_currentHost; +extern bool s_termSupportsColor; +#define ELPP_INITI_BASIC_DECLR \ + namespace el {\ + namespace base {\ + namespace utils {\ + std::string s_currentUser = el::base::utils::OS::currentUser(); \ + std::string s_currentHost = el::base::utils::OS::currentHost(); \ + bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \ + }\ + }\ + } +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { +public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv) { +#if ELPP_OS_WINDOWS + if (tv != nullptr) { +# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const unsigned __int64 delta_ = 11644473600000000Ui64; +# else + const unsigned __int64 delta_ = 11644473600000000ULL; +# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const double secOffSet = 0.000001; + const unsigned long usecOffSet = 1000000; + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec + // Subtract the difference + present -= delta_; + tv->tv_sec = static_cast(present * secOffSet); + tv->tv_usec = static_cast(present % usecOffSet); + } +#else + ::gettimeofday(tv, nullptr); +#endif // ELPP_OS_WINDOWS + } + + /// @brief Gets current date and time with milliseconds. + /// @param format User provided date/time format + /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null) + /// @returns string based date time in specified format. + static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) { + struct timeval currTime; + gettimeofday(&currTime); + struct ::tm timeInfo; + buildTimeInfo(&currTime, &timeInfo); + const int kBuffSize = 30; + char buff_[kBuffSize] = ""; + parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(currTime.tv_usec / msWidth->m_offset), msWidth); + return std::string(buff_); + } + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { + double result = static_cast(time); + base::type::EnumType start = static_cast(timestampUnit); + const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; + for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { + if (result <= base::consts::kTimeFormats[i].value) { + break; + } + result /= base::consts::kTimeFormats[i].value; + unit = base::consts::kTimeFormats[i + 1].unit; + } + base::type::stringstream_t ss; + ss << result << " " << unit; + return ss.str(); + } + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) { + if (timestampUnit == base::TimestampUnit::Microsecond) { + return static_cast(static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) - + static_cast(1000000 * startTime.tv_sec + startTime.tv_usec)); + } else { + return static_cast((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000); + } + } + +private: + static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { +#if ELPP_OS_UNIX + time_t rawTime = currTime->tv_sec; + ::localtime_r(&rawTime, timeInfo); + return timeInfo; +#else +# if ELPP_COMPILER_MSVC + ELPP_UNUSED(currTime); + time_t t; + _time64(&t); + localtime_s(timeInfo, &t); + return timeInfo; +# else + // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method + time_t rawTime = currTime->tv_sec; + struct tm* tmInf = localtime(&rawTime); + *timeInfo = *tmInf; + return timeInfo; +# endif // ELPP_COMPILER_MSVC +#endif // ELPP_OS_UNIX + } + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::MillisecondsWidth* msWidth) { + const char* bufLim = buf + bufSz; + for (; *format; ++format) { + if (*format == base::consts::kFormatSpecifierChar) { + switch (*++format) { + case base::consts::kFormatSpecifierChar: // Escape + break; + case '\0': // End + --format; + break; + case 'd': // Day + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); + continue; + case 'a': // Day of week (short) + buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); + continue; + case 'A': // Day of week (long) + buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); + continue; + case 'M': // month + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); + continue; + case 'b': // month (short) + buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); + continue; + case 'B': // month (long) + buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); + continue; + case 'y': // year (two digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); + continue; + case 'Y': // year (four digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); + continue; + case 'h': // hour (12-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); + continue; + case 'H': // hour (24-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); + continue; + case 'm': // minute + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); + continue; + case 's': // second + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); + continue; + case 'z': // milliseconds + case 'g': + buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->m_width, buf, bufLim); + continue; + case 'F': // AM/PM + buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); + continue; + default: + continue; + } + } + if (buf == bufLim) break; + *buf++ = *format; + } + return buf; + } +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { +public: + CommandLineArgs(void) { + setArgs(0, static_cast(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast(argv)); + } + /// @brief Sets arguments and parses them + inline void setArgs(int argc, char** argv) { + m_params.clear(); + m_paramsWithValue.clear(); + if (argc == 0 || argv == nullptr) { + return; + } + m_argc = argc; + m_argv = argv; + for (int i = 1; i < m_argc; ++i) { + const char* v = (strstr(m_argv[i], "=")); + if (v != nullptr && strlen(v) > 0) { + std::string key = std::string(m_argv[i]); + key = key.substr(0, key.find_first_of('=')); + if (hasParamWithValue(key.c_str())) { + ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" + << getParamValue(key.c_str()) << "]"); + } else { + m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); + } + } + if (v == nullptr) { + if (hasParam(m_argv[i])) { + ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); + } else { + m_params.push_back(std::string(m_argv[i])); + } + } + } + } + /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') + inline bool hasParamWithValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); + } + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + inline const char* getParamValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey))->second.c_str(); + } + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + inline bool hasParam(const char* paramKey) const { + return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); + } + /// @brief Returns true if no params available. This exclude argv[0] + inline bool empty(void) const { + return m_params.empty() && m_paramsWithValue.empty(); + } + /// @brief Returns total number of arguments. This exclude argv[0] + inline std::size_t size(void) const { + return m_params.size() + m_paramsWithValue.size(); + } + inline friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { + for (int i = 1; i < c.m_argc; ++i) { + os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); + if (i < c.m_argc - 1) { + os << ELPP_LITERAL(" "); + } + } + return os; + } + +private: + int m_argc; + char** m_argv; + std::map m_paramsWithValue; + std::vector m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template +class AbstractRegistry : public base::threading::ThreadSafe { +public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; + } + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } + } + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; + } + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + +protected: + virtual void deepCopy(const AbstractRegistry&) = 0; + void reinitDeepCopy(const AbstractRegistry& sr) { + unregisterAll(); + deepCopy(sr); + } + +private: + Container m_list; +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions) +/// of AbstractRegistry. Any implementation of this class should be +/// explicitly (by using lock functions) +template +class Registry : public AbstractRegistry> { +public: + typedef typename Registry::iterator iterator; + typedef typename Registry::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + +protected: + virtual inline void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); + } + } + + /// @brief Registers new registry to repository. + virtual inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } + + /// @brief Unregisters single entry mapped to specified unique key + inline void unregister(const T_Key& uniqKey) { + T_Ptr* existing = get(uniqKey); + if (existing != nullptr) { + base::utils::safeDelete(existing); + this->list().erase(uniqKey); + } + } + + /// @brief Gets pointer from repository. If none found, nullptr is returned. + inline T_Ptr* get(const T_Key& uniqKey) { + iterator it = this->list().find(uniqKey); + return it == this->list().end() + ? nullptr + : it->second; + } + +private: + virtual inline void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry. Any implementation of this class +/// should be made thread-safe explicitly +template +class RegistryWithPred : public AbstractRegistry> { +public: + typedef typename RegistryWithPred::iterator iterator; + typedef typename RegistryWithPred::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + friend inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); + } + return os; + } + +protected: + virtual inline void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); + } + } + + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { + if (ptr == *iter) { + break; + } + } + if (iter != this->end() && *iter != nullptr) { + this->list().erase(iter); + base::utils::safeDelete(*iter); + } + } + } + + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } + + /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate + /// in order to validate pointer. + template + inline T_Ptr* get(const T& arg1, const T2 arg2) { + iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); + if (iter != this->list().end() && *iter != nullptr) { + return *iter; + } + return nullptr; + } + +private: + virtual inline void deepCopy(const AbstractRegistry>& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + registerNew(new T_Ptr(**it)); + } + } +}; + +} // namespace utils +} // namespace base +/// @brief Base of Easylogging++ friendly class +/// +/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` +class Loggable { +public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; +private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } +}; +namespace base { +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { +public: + LogFormat(void) : + m_level(Level::Unknown), + m_userFormat(base::type::string_t()), + m_format(base::type::string_t()), + m_dateTimeFormat(std::string()), + m_flags(0x0) { + } + + LogFormat(Level level, const base::type::string_t& format) + : m_level(level), m_userFormat(format) { + parseFromFormat(m_userFormat); + } + + LogFormat(const LogFormat& logFormat) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_format = logFormat.m_format; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + } + + LogFormat(LogFormat&& logFormat) { + m_level = std::move(logFormat.m_level); + m_userFormat = std::move(logFormat.m_userFormat); + m_format = std::move(logFormat.m_format); + m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); + m_flags = std::move(logFormat.m_flags); + } + + LogFormat& operator=(const LogFormat& logFormat) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + return *this; + } + + virtual ~LogFormat(void) { + } + + inline bool operator==(const LogFormat& other) { + return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && + m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; + } + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat) { + // We make copy because we will be changing the format + // i.e, removing user provided date format from original format + // and then storing it. + base::type::string_t formatCopy = userFormat; + m_flags = 0x0; + auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){ + if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { + if (hasFlag(flag)) { + // If we already have flag we remove the escape chars so that '%%' is turned to '%' + // even after specifier resolution - this is because we only replaceFirst specifier + formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } + } else { + if (!hasFlag(flag)) addFlag(flag); + } + } + }; + conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); + conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); + conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); + conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); + conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); + conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); + conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); + conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); + conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); + conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); + conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); + conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); + conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); + conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); + // For date/time we need to extract user's date format first + std::size_t dateIndex = std::string::npos; + if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { + while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); + } + if (dateIndex != std::string::npos) { + addFlag(base::FormatFlags::DateTime); + updateDateFormat(dateIndex, formatCopy); + } + } + m_format = formatCopy; + updateFormatSpec(); + } + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + +protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL { + if (hasFlag(base::FormatFlags::DateTime)) { + index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); + } + const base::type::char_t* ptr = currFormat.c_str() + index; + if ((currFormat.size() > index) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + currFormat.erase(index, count); + m_dateTimeFormat = ss.str(); + } else { + // No format provided, use default + if (hasFlag(base::FormatFlags::DateTime)) { + m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); + } + } + } + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (m_level == Level::Debug) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kDebugLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kDebugLevelShortLogValue); + } else if (m_level == Level::Info) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kInfoLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kInfoLevelShortLogValue); + } else if (m_level == Level::Warning) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kWarningLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kWarningLevelShortLogValue); + } else if (m_level == Level::Error) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kErrorLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kErrorLevelShortLogValue); + } else if (m_level == Level::Fatal) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kFatalLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kFatalLevelShortLogValue); + } else if (m_level == Level::Verbose) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kVerboseLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kVerboseLevelShortLogValue); + } else if (m_level == Level::Trace) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kTraceLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kTraceLevelShortLogValue); + } + if (hasFlag(base::FormatFlags::User)) { + std::string s = base::utils::s_currentUser; + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, + base::utils::s_currentUser); + } + if (hasFlag(base::FormatFlags::Host)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, + base::utils::s_currentHost); + } + // Ignore Level::Global and Level::Unknown + } + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + +private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + friend class el::Logger; // To resolve loggerId format specifier easily +}; +} // namespace base +/// @brief Resolving function for format specifier +typedef std::function FormatSpecifierValueResolver; +/// @brief User-provided custom format specifier +/// @see el::Helpers::installCustomFormatSpecifier +/// @see FormatSpecifierValueResolver +class CustomFormatSpecifier { +public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} + inline const char* formatSpecifier(void) const { return m_formatSpecifier; } + inline const FormatSpecifierValueResolver& resolver(void) const { return m_resolver; } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + +private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; +}; +/// @brief Represents single configuration that has representing level, configuration type and a string based value. +/// +/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes +/// and will be parsed later. +/// +/// Consider some examples below: +/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true"); +/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); +/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); +class Configuration : public Loggable { +public: + Configuration(const Configuration& c) : + m_level(c.m_level), + m_configurationType(c.m_configurationType), + m_value(c.m_value) { + } + + Configuration& operator=(const Configuration& c) { + m_level = c.m_level; + m_configurationType = c.m_configurationType; + m_value = c.m_value; + return *this; + } + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value) : + m_level(level), + m_configurationType(configurationType), + m_value(value) { + } + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << LevelHelper::convertToString(m_level) + << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) + << ELPP_LITERAL(" = ") << m_value.c_str(); + } + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType) : + m_level(level), + m_configurationType(configurationType) { + } + + inline bool operator()(const Configuration* conf) const { + return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); + } + + private: + Level m_level; + ConfigurationType m_configurationType; + }; + +private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; +}; + +/// @brief Thread-safe Configuration repository +/// +/// @detail This repository represents configurations for all the levels and configuration type mapped to a value. +class Configurations : public base::utils::RegistryWithPred { +public: + /// @brief Default constructor with empty repository + Configurations(void) : + m_configurationFile(std::string()), + m_isFromFile(false) { + } + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr) : + m_configurationFile(configurationFile), + m_isFromFile(false) { + parseFromFile(configurationFile, base); + if (useDefaultsForRemaining) { + setRemainingToDefault(); + } + } + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + inline bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr) { + // We initial assertion with true because if we have assertion diabled, we want to pass this + // check and if assertion is enabled we will have values re-assigned any way. + bool assertionPassed = true; + ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)), + "Configuration file [" << configurationFile << "] does not exist!"); + if (!assertionPassed) { + return false; + } + bool success = Parser::parseFromFile(configurationFile, this, base); + m_isFromFile = success; + return success; + } + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + inline bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr) { + bool success = Parser::parseFromText(configurationsString, this, base); + if (success) { + m_isFromFile = false; + } + return success; + } + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + inline void setFromBase(Configurations* base) { + if (base == nullptr || base == this) { + return; + } + base::threading::ScopedLock scopedLock(base->lock()); + for (Configuration*& conf : base->list()) { + set(conf); + } + } + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType) { + base::type::EnumType lIndex = LevelHelper::kMinValid; + bool result = false; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { + result = true; + } + return result; + }); + return result; + } + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + inline bool hasConfiguration(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); +#if ELPP_COMPILER_INTEL + // We cant specify template types here, Intel C++ throws compilation error + // "error: type name is not allowed" + return RegistryWithPred::get(level, configurationType) != nullptr; +#else + return RegistryWithPred::get(level, configurationType) != nullptr; +#endif // ELPP_COMPILER_INTEL + } + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::MillisecondsWidth and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + inline void set(Level level, ConfigurationType configurationType, const std::string& value) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either + } + } + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + inline void set(Configuration* conf) { + if (conf == nullptr) { + return; + } + set(conf->level(), conf->configurationType(), conf->value()); + } + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void) { + setGlobally(ConfigurationType::Enabled, std::string("true"), true); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); +#else + ELPP_UNUSED(base::consts::kDefaultLogFile); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + setGlobally(ConfigurationType::ToFile, std::string("true"), true); + setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); + setGlobally(ConfigurationType::MillisecondsWidth, std::string("3"), true); + setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); + setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); + setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); + + setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); + set(Level::Debug, ConfigurationType::Format, std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); + } + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitley (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToFile, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MillisecondsWidth, std::string("3")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); + } + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse. + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr) { + sender->setFromBase(base); + std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); + ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); + bool parsedSuccessfully = false; + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (fileStream_.good()) { + std::getline(fileStream_, line); + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; + } + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr) { + sender->setFromBase(base); + bool parsedSuccessfully = false; + std::stringstream ss(configurationsString); + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (std::getline(ss, line)) { + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; + } + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line) { + std::size_t foundAt = 0; + std::size_t quotesStart = line->find("\""); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = line->find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { + // Do not erase slash yet - we will erase it in parseLine(..) while loop + quotesEnd = line->find("\"", quotesEnd + 2); + } + } + if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { + if (foundAt < quotesEnd) { + foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); + } + *line = line->substr(0, foundAt); + } + } + static inline bool isLevel(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); + } + + static inline bool isComment(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); + } + + static inline bool isConfig(const std::string& line) { + std::size_t assignment = line.find('='); + return line != "" && + (line[0] >= 65 || line[0] <= 90 || line[0] >= 97 || line[0] <= 122) && + (assignment != std::string::npos) && + (line.size() > assignment); + } + + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, Configurations* conf) { + ConfigurationType currConfig = ConfigurationType::Unknown; + std::string currValue = std::string(); + *line = base::utils::Str::trim(*line); + if (isComment(*line)) return true; + ignoreComments(line); + *line = base::utils::Str::trim(*line); + if (line->empty()) { + // Comment ignored + return true; + } + if (isLevel(*line)) { + if (line->size() <= 2) { + return true; + } + *currLevelStr = line->substr(1, line->size() - 2); + *currLevelStr = base::utils::Str::toUpper(*currLevelStr); + *currLevelStr = base::utils::Str::trim(*currLevelStr); + *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); + return true; + } + if (isConfig(*line)) { + std::size_t assignment = line->find('='); + *currConfigStr = line->substr(0, assignment); + *currConfigStr = base::utils::Str::toUpper(*currConfigStr); + *currConfigStr = base::utils::Str::trim(*currConfigStr); + currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); + currValue = line->substr(assignment + 1); + currValue = base::utils::Str::trim(currValue); + std::size_t quotesStart = currValue.find("\"", 0); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = currValue.find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { + currValue = currValue.erase(quotesEnd - 1, 1); + quotesEnd = currValue.find("\"", quotesEnd + 2); + } + } + if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { + // Quote provided - check and strip if valid + ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" + << currConfigStr << "]"); + ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); + if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { + // Explicit check in case if assertion is disabled + currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); + } + } + } + ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); + ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); + if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { + return false; // unrecognizable level or config + } + conf->set(*currLevel, currConfig, currValue); + return true; + } + }; + +private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + unsafeSet(level, configurationType, value); + } + } + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + registerNew(new Configuration(level, configurationType, value)); + } else { + conf->setValue(value); + } + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); + } + } + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { + if (includeGlobalLevel) { + set(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + set(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); + } + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { + if (includeGlobalLevel) { + unsafeSet(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); + } +}; + +namespace base { +typedef std::shared_ptr FileStreamPtr; +typedef std::map LogStreamsReferenceMap; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { +public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference) { + m_configurations = configurations; + m_logStreamsReference = logStreamsReference; + build(m_configurations); + } + + TypedConfigurations(const TypedConfigurations& other) { + this->m_configurations = other.m_configurations; + this->m_logStreamsReference = other.m_logStreamsReference; + build(m_configurations); + } + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + inline bool enabled(Level level) { + return getConfigByVal(level, &m_enabledMap, "enabled"); + } + + inline bool toFile(Level level) { + return getConfigByVal(level, &m_toFileMap, "toFile"); + } + + inline const std::string& filename(Level level) { + return getConfigByRef(level, &m_filenameMap, "filename"); + } + + inline bool toStandardOutput(Level level) { + return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput"); + } + + inline const base::LogFormat& logFormat(Level level) { + return getConfigByRef(level, &m_logFormatMap, "logFormat"); + } + + inline const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global) { + return getConfigByRef(level, &m_millisecondsWidthMap, "millisecondsWidth"); + } + + inline bool performanceTracking(Level level = Level::Global) { + return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking"); + } + + inline base::type::fstream_t* fileStream(Level level) { + return getConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + } + + inline std::size_t maxLogFileSize(Level level) { + return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + } + + inline std::size_t logFlushThreshold(Level level) { + return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold"); + } + +private: + Configurations* m_configurations; + std::map m_enabledMap; + std::map m_toFileMap; + std::map m_filenameMap; + std::map m_toStandardOutputMap; + std::map m_logFormatMap; + std::map m_millisecondsWidthMap; + std::map m_performanceTrackingMap; + std::map m_fileStreamMap; + std::map m_maxLogFileSizeMap; + std::map m_logFlushThresholdMap; + base::LogStreamsReferenceMap* m_logStreamsReference; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template + inline Conf_T getConfigByVal(Level level, const std::map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T& getConfigByRef(Level level, std::map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T unsafeGetConfigByVal(Level level, const std::map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } + } + return it->second; + } + + template + inline Conf_T& unsafeGetConfigByRef(Level level, std::map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } + } + return it->second; + } + + template + void setValue(Level level, const Conf_T& value, std::map* confMap, bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; + } + // If same value exist in generic level already, dont add it to explicit level + typename std::map::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; + } + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; + } + } + + void build(Configurations* configurations) { + base::threading::ScopedLock scopedLock(lock()); + auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming + base::utils::Str::trim(boolStr); + return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); + }; + std::vector withFileSizeLimit; + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + // We cannot use switch on strong enums because Intel C++ dont support them yet + if (conf->configurationType() == ConfigurationType::Enabled) { + setValue(conf->level(), getBool(conf->value()), &m_enabledMap); + } else if (conf->configurationType() == ConfigurationType::ToFile) { + setValue(conf->level(), getBool(conf->value()), &m_toFileMap); + } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { + setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); + } else if (conf->configurationType() == ConfigurationType::Filename) { + // We do not yet configure filename but we will configure in another + // loop. This is because if file cannot be created, we will force ToFile + // to be false. Because configuring logger is not necessarily performance + // sensative operation, we can live with another loop; (by the way this loop + // is not very heavy either) + } else if (conf->configurationType() == ConfigurationType::Format) { + setValue(conf->level(), base::LogFormat(conf->level(), + base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); + } else if (conf->configurationType() == ConfigurationType::MillisecondsWidth) { + setValue(Level::Global, + base::MillisecondsWidth(static_cast(getULong(conf->value()))), &m_millisecondsWidthMap); + } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { + setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); + } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_maxLogFileSizeMap); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + withFileSizeLimit.push_back(conf); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); + } + } + // As mentioned early, we will now set filename configuration in separate loop to deal with non-existent files + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + if (conf->configurationType() == ConfigurationType::Filename) { + insertFile(conf->level(), conf->value()); + } + } + for (std::vector::iterator conf = withFileSizeLimit.begin(); + conf != withFileSizeLimit.end(); ++conf) { + // This is not unsafe as mutex is locked in currect scope + unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); + } + } + + unsigned long getULong(std::string confVal) { + bool valid = true; + base::utils::Str::trim(confVal); + valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), + [](char c) { return !base::utils::Str::isDigit(c); }) == confVal.end(); + if (!valid) { + valid = false; + ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); + return 0; + } + return atol(confVal.c_str()); + } + + std::string resolveFilename(const std::string& filename) { + std::string resultingFilename = filename; + std::size_t dateIndex = std::string::npos; + std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); + if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { + while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); + } + if (dateIndex != std::string::npos) { + const char* ptr = resultingFilename.c_str() + dateIndex; + // Goto end of specifier + ptr += dateTimeFormatSpecifierStr.size(); + std::string fmt; + if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); + fmt = ss.str(); + } else { + fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); + } + base::MillisecondsWidth msWidth(3); + std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &msWidth); + base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename + base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); + } + } + return resultingFilename; + } + + void insertFile(Level level, const std::string& fullFilename) { + std::string resolvedFilename = resolveFilename(fullFilename); + if (resolvedFilename.empty()) { + std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" + << LevelHelper::convertToString(level) << "]"; + } + std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator); + if (filePath.size() < resolvedFilename.size()) { + base::utils::File::createPath(filePath); + } + auto create = [&](Level level) { + base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); + base::type::fstream_t* fs = nullptr; + if (filestreamIter == m_logStreamsReference->end()) { + // We need a completely new stream, nothing to share with + fs = base::utils::File::newFileStream(resolvedFilename); + m_filenameMap.insert(std::make_pair(level, resolvedFilename)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); + m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); + } else { + // Woops! we have an existing one, share it! + m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); + fs = filestreamIter->second.get(); + } + if (fs == nullptr) { + // We display bad file error from newFileStream() + ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" + << LevelHelper::convertToString(level) << "] to FALSE", false); + setValue(level, false, &m_toFileMap); + } + }; + // If we dont have file conf for any level, create it for Level::Global first + // otherwise create for specified level + create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); + } + + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { + base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + if (fs == nullptr) { + return true; + } + std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); + if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { + std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); + ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" + << LevelHelper::convertToString(level) << "]"); + fs->close(); + PreRollOutCallback(fname.c_str(), currFileSize); + fs->open(fname, std::fstream::out | std::fstream::trunc); + return true; + } + return false; + } + + bool validateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, PreRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { +public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, unsigned long int lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, unsigned long int lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); + } + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline unsigned long int lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, unsigned long int lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { + } + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); + } + + private: + const char* m_filename; + unsigned long int m_lineNumber; + }; + +private: + const char* m_filename; + unsigned long int m_lineNumber; + std::size_t m_hitCounts; +}; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred { +public: + /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateEveryN(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->validateHitCounts(n); + bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); + return result; + } + + /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateAfterN(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + // Do not use validateHitCounts here since we do not want to reset counter here + // Note the >= instead of > because we are incrementing + // after this check + if (counter->hitCounts() >= n) + return true; + counter->increment(); + return false; + } + + /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateNTimes(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->increment(); + // Do not use validateHitCounts here since we do not want to reset counter here + if (counter->hitCounts() <= n) + return true; + return false; + } + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, unsigned long int lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4 +}; +} // namespace base +template +class Callback : protected base::threading::ThreadSafe { +public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { return m_enabled; } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } +protected: + virtual void handle(const T* handlePtr) = 0; +private: + bool m_enabled; +}; +class LogDispatchData { +public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { return m_logMessage; } + inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; } +private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + + inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; } + inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; } +}; +class LogDispatchCallback : public Callback { +private: + friend class base::LogDispatcher; +}; +class PerformanceTrackingCallback : public Callback { +private: + friend class base::PerformanceTracker; +}; +class LogBuilder : base::NoCopy { +public: + virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...")} + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level) { + if (!base::utils::s_termSupportsColor) return; + const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); + if (level == Level::Error || level == Level::Fatal) + *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; + else if (level == Level::Warning) + *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; + } +private: + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configuations from. +class Logger : public base::threading::ThreadSafe, public Loggable { +public: + Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + } + + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + configure(configurations); + } + + Logger(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + } + + Logger& operator=(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + return *this; + } + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations) { + m_isConfigured = false; // we set it to false in case if we fail + initUnflushedCount(); + if (m_typedConfigurations != nullptr) { + Configurations* c = const_cast(m_typedConfigurations->configurations()); + if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { + // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE + flush(); + } + } + base::threading::ScopedLock scopedLock(lock()); + if (m_configurations != configurations) { + m_configurations.setFromBase(const_cast(&configurations)); + } + base::utils::safeDelete(m_typedConfigurations); + m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); + resolveLoggerFormatSpec(); + m_isConfigured = true; + } + + /// @brief Reconfigures logger using existing configurations + inline void reconfigure(void) { + ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); + configure(m_configurations); + } + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static inline bool isValidId(const std::string& id) { + for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { + if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { + return false; + } + } + return true; + } + /// @brief Flushes logger to sync all log files for all levels + inline void flush(void) { + ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); + base::threading::ScopedLock scopedLock(lock()); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + flush(LevelHelper::castFromInt(lIndex), nullptr); + return false; + }); + } + + inline void flush(Level level, base::type::fstream_t* fs) { + if (fs == nullptr && m_typedConfigurations->toFile(level)) { + fs = m_typedConfigurations->fileStream(level); + } + if (fs != nullptr) { + fs->flush(); + m_unflushedCount.find(level)->second = 0; + } + } + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ + template \ + inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ + template \ + inline void FUNCTION_NAME(const T&); + + template + inline void verbose(int, const char*, const T&, const Args&...); + + template + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::map m_unflushedCount; + base::LogStreamsReferenceMap* m_logStreamsReference; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void log_(Level, int, const char*, const T&, const Args&...); + + template + inline void log_(Level, int, const T&); + + template + void log(Level, const char*, const T&, const Args&...); + + template + inline void log(Level, const T&); +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + + void initUnflushedCount(void) { + m_unflushedCount.clear(); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); + return false; + }); + } + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const { + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + base::LogFormat* logFormat = + const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); + base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); + return false; + }); + } +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry { +public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : + m_defaultLogBuilder(defaultLogBuilder) { + m_defaultConfigurations.setToDefault(); + } + + virtual ~RegisteredLoggers(void) { + flushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true) { + base::threading::ScopedLock scopedLock(lock()); + Logger* logger_ = base::utils::Registry::get(id); + if (logger_ == nullptr && forceCreation) { + bool validId = Logger::isValidId(id); + if (!validId) { + ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); + return nullptr; + } + logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference); + logger_->m_logBuilder = m_defaultLogBuilder; + registerNew(id, logger_); + } + return logger_; + } + + bool remove(const std::string& id) { + if (id == "default") { + return false; + } + Logger* logger = base::utils::Registry::get(id); + if (logger != nullptr) { + unregister(logger); + } + return true; + } + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry::unregister(logger->id()); + } + + inline base::LogStreamsReferenceMap* logStreamsReference(void) { + return &m_logStreamsReference; + } + + inline void flushAll(void) { + ELPP_INTERNAL_INFO(1, "Flushing all log files"); + base::threading::ScopedLock scopedLock(lock()); + for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin(); + it != m_logStreamsReference.end(); ++it) { + if (it->second.get() == nullptr) continue; + it->second->flush(); + } + } + +private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMap m_logStreamsReference; + friend class el::base::Storage; +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { +public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { + } + + /// @brief Sets verbose level. Accepted range is 0-9 + inline void setLevel(base::type::VerboseLevel level) { + base::threading::ScopedLock scopedLock(lock()); + if (level < 0) + m_level = 0; + else if (level > 9) + m_level = base::consts::kMaxVerboseLevel; + else + m_level = level; + } + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setModules(const char* modules) { + base::threading::ScopedLock scopedLock(lock()); + auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { + if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); + ss.str(std::string("")); + ss << chr; + } + if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); + ss.str(std::string("")); + ss << chr; + } + ss << sfx; + }; + auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { + if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { + addSuffix(ss, ".h", nullptr); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".c", ".h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cpp", ".c"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cc", ".cpp"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cxx", ".cc"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".-inl.h", ".cxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hxx", ".-inl.h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hpp", ".hxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hh", ".hpp"); + } + m_modules.insert(std::make_pair(ss.str(), level)); + }; + bool isMod = true; + bool isLevel = false; + std::stringstream ss; + int level = -1; + for (; *modules; ++modules) { + switch (*modules) { + case '=': + isLevel = true; + isMod = false; + break; + case ',': + isLevel = false; + isMod = true; + if (!ss.str().empty() && level != -1) { + insert(ss, level); + ss.str(std::string("")); + level = -1; + } + break; + default: + if (isMod) { + ss << *modules; + } else if (isLevel) { + if (isdigit(*modules)) { + level = static_cast(*modules) - 48; + } + } + break; + } + } + if (!ss.str().empty() && level != -1) { + insert(ss, level); + } + } + + bool allowed(base::type::VerboseLevel vlevel, const char* file) { + base::threading::ScopedLock scopedLock(lock()); + if (m_modules.empty() || file == nullptr) { + return vlevel <= m_level; + } else { + std::map::iterator it = m_modules.begin(); + for (; it != m_modules.end(); ++it) { + if (base::utils::Str::wildCardMatch(file, it->first.c_str())) { + return vlevel <= it->second; + } + } + if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { + return true; + } + return false; + } + } + + inline const std::map& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { + if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || + commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { + setLevel(base::consts::kMaxVerboseLevel); + } else if (commandLineArgs->hasParamWithValue("--v")) { + setLevel(atoi(commandLineArgs->getParamValue("--v"))); + } else if (commandLineArgs->hasParamWithValue("--V")) { + setLevel(atoi(commandLineArgs->getParamValue("--V"))); + } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-vmodule")); + } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-VMODULE")); + } + } + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + +private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::map m_modules; +}; +} // namespace base +class LogMessage { +public: + LogMessage(Level level, const std::string& file, unsigned long int line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(std::move(logger->stream().str())) { + } + inline Level level(void) const { return m_level; } + inline const std::string& file(void) const { return m_file; } + inline unsigned long int line(void) const { return m_line; } // NOLINT + inline const std::string& func(void) const { return m_func; } + inline base::type::VerboseLevel verboseLevel(void) const { return m_verboseLevel; } + inline Logger* logger(void) const { return m_logger; } + inline const base::type::string_t& message(void) const { return m_message; } +private: + Level m_level; + std::string m_file; + unsigned long int m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { +#if ELPP_ASYNC_LOGGING +class AsyncLogItem { +public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { return &m_logMessage; } + inline LogDispatchData* data(void) { return &m_dispatchData; } + inline base::type::string_t logLine(void) { return m_logLine; } +private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { +public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } +private: + std::queue m_queue; +}; +class IWorker { +public: + virtual ~IWorker() {} + virtual void start() = 0; +}; +#endif // ELPP_ASYNC_LOGGING +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { +public: +#if ELPP_ASYNC_LOGGING + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : +#else + explicit Storage(const LogBuilderPtr& defaultLogBuilder) : +#endif // ELPP_ASYNC_LOGGING + m_registeredHitCounters(new base::RegisteredHitCounters()), + m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), + m_flags(0x0), + m_vRegistry(new base::VRegistry(0, &m_flags)), +#if ELPP_ASYNC_LOGGING + m_asyncLogQueue(new base::AsyncLogQueue()), + m_asyncDispatchWorker(asyncDispatchWorker), +#endif // ELPP_ASYNC_LOGGING + m_preRollOutCallback(base::defaultPreRollOutCallback) { + // Register default logger + m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); + // Register performance logger and reconfigure format + Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); + performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); + performanceLogger->reconfigure(); +#if defined(ELPP_SYSLOG) + // Register syslog logger and reconfigure format + Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); + sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); + sysLogLogger->reconfigure(); +#else + ELPP_UNUSED(base::consts::kSysLogLoggerId); +#endif // defined(ELPP_SYSLOG) + addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); +#if ELPP_ASYNC_LOGGING + installLogDispatchCallback(std::string("AsyncLogDispatchCallback")); +#else + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); +#endif // ELPP_ASYNC_LOGGING + installPerformanceTrackingCallback(std::string("DefaultPerformanceTrackingCallback")); + ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); +#if ELPP_ASYNC_LOGGING + m_asyncDispatchWorker->start(); +#endif // ELPP_ASYNC_LOGGING + } + + virtual ~Storage(void) { + ELPP_INTERNAL_INFO(4, "Destroying storage"); +#if ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); + uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); + ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); + base::utils::safeDelete(m_asyncDispatchWorker); + ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); + base::utils::safeDelete(m_asyncLogQueue); +#endif // ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); + base::utils::safeDelete(m_registeredHitCounters); + ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); + base::utils::safeDelete(m_registeredLoggers); + ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); + base::utils::safeDelete(m_vRegistry); + } + + inline bool validateEveryNCounter(const char* filename, unsigned long int lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + +#if ELPP_ASYNC_LOGGING + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } +#endif // ELPP_ASYNC_LOGGING + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), + formatSpecifier) != m_customFormatSpecifiers.end(); + } + + inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { + return; + } + base::threading::ScopedLock scopedLock(lock()); + m_customFormatSpecifiers.push_back(customFormatSpecifier); + } + + inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(), + m_customFormatSpecifiers.end(), formatSpecifier); + if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { + m_customFormatSpecifiers.erase(it); + return true; + } + return false; + } + + const std::vector* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template + inline bool installLogDispatchCallback(const std::string& id) { + return installCallback(id, &m_logDispatchCallbacks); + } + + template + inline void uninstallLogDispatchCallback(const std::string& id) { + uninstallCallback(id, &m_logDispatchCallbacks); + } + template + inline T* logDispatchCallback(const std::string& id) { + return callback(id, &m_logDispatchCallbacks); + } + + template + inline bool installPerformanceTrackingCallback(const std::string& id) { + return installCallback(id, &m_performanceTrackingCallbacks); + } + + template + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + uninstallCallback(id, &m_performanceTrackingCallbacks); + } + + template + inline T* performanceTrackingCallback(const std::string& id) { + return callback(id, &m_performanceTrackingCallbacks); + } +private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; +#if ELPP_ASYNC_LOGGING + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::map m_logDispatchCallbacks; + std::map m_performanceTrackingCallbacks; + std::vector m_customFormatSpecifiers; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv) { + m_commandLineArgs.setArgs(argc, argv); + m_vRegistry->setFromArgs(commandLineArgs()); + // default log file +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { + Configurations c; + c.setGlobally(ConfigurationType::Filename, std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); + registeredLoggers()->setDefaultConfigurations(c); + for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); + it != registeredLoggers()->end(); ++it) { + it->second->configure(c); + } + } +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { + m_flags = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); + } +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) + } + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast(argv)); + } + + template + inline bool installCallback(const std::string& id, std::map* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + + template + inline void uninstallCallback(const std::string& id, std::map* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + + template + inline T* callback(const std::string& id, std::map* mapT) { + typename std::map::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast(iter->second.get()); + } + return nullptr; + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; +#define ELPP el::base::elStorage +class DefaultLogDispatchCallback : public LogDispatchCallback { +protected: + void handle(const LogDispatchData* data) { + m_data = data; + dispatch(std::move(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + m_data->dispatchAction() == base::DispatchAction::NormalLog))); + } +private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine) { + if (m_data->dispatchAction() == base::DispatchAction::NormalLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { + base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(m_data->logMessage()->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { + m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " + << m_data->logMessage()->logger()->id() << "]", false); + } + } + if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + } +#if defined(ELPP_SYSLOG) + else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (m_data->logMessage()->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (m_data->logMessage()->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (m_data->logMessage()->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (m_data->logMessage()->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (m_data->logMessage()->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +#endif // defined(ELPP_SYSLOG) + } +}; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback : public LogDispatchCallback { +protected: + void handle(const LogDispatchData* data) { + base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), data->dispatchAction() == base::DispatchAction::NormalLog); + if (data->dispatchAction() == base::DispatchAction::NormalLog && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + // Save resources and only queue if we want to write to file otherwise just ignore handler + if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { + ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); + } + } +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { +public: + AsyncDispatchWorker() { + setContinueRunning(false); + } + + virtual ~AsyncDispatchWorker() { + setContinueRunning(false); + ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); + clean(); + ELPP_INTERNAL_INFO(6, "Log queue cleaned"); + } + + inline bool clean() { + std::mutex m; + std::unique_lock lk(m); + cv.wait(lk, []{ return !ELPP->asyncLogQueue()->empty(); }); + emptyQueue(); + lk.unlock(); + cv.notify_one(); + return ELPP->asyncLogQueue()->empty(); + } + + inline void emptyQueue() { + while (!ELPP->asyncLogQueue()->empty()) { + AsyncLogItem data = ELPP->asyncLogQueue()->next(); + handle(&data); + base::threading::msleep(100); + } + } + + virtual inline void start() { + base::threading::msleep(5000); // Wait extra few seconds + setContinueRunning(true); + std::thread t1(&AsyncDispatchWorker::runner, this); + t1.join(); + } + + void handle(AsyncLogItem* logItem) { + LogDispatchData* data = logItem->data(); + LogMessage* logMessage = logItem->logMessage(); + Logger* logger = logMessage->logger(); + base::TypedConfigurations* conf = logger->typedConfigurations(); + base::type::string_t logLine = logItem->logLine(); + if (data->dispatchAction() == base::DispatchAction::NormalLog) { + if (conf->toFile(logMessage->level())) { + base::type::fstream_t* fs = conf->fileStream(logMessage->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << conf->filename(logMessage->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { + logger->flush(logMessage->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); + } + } + } +# if defined(ELPP_SYSLOG) + else if (data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (logMessage->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (logMessage->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (logMessage->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (logMessage->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (logMessage->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +# endif // defined(ELPP_SYSLOG) + } + + void run() { + while (continueRunning()) { + emptyQueue(); + base::threading::msleep(10); // 10ms + } + } + + static void* runner(void *context) { + static_cast(context)->run(); + return NULL; + } + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningMutex); + m_continueRunning = value; + } + bool continueRunning(void) { + return m_continueRunning; + } +private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningMutex; +}; +#endif // ELPP_ASYNC_LOGGING +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { +public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const { + base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); + base::type::string_t logLine = logFormat->format(); + char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; + const char* bufLim = buff + sizeof(buff); + if (logFormat->hasFlag(base::FormatFlags::AppName)) { + // App name + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, + logMessage->logger()->parentApplicationName()); + } + if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { + // Thread ID + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, + base::threading::getCurrentThreadId()); + } + if (logFormat->hasFlag(base::FormatFlags::DateTime)) { + // DateTime + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, + base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), + &tc->millisecondsWidth(logMessage->level()))); + } + if (logFormat->hasFlag(base::FormatFlags::Function)) { + // Function + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); + } + if (logFormat->hasFlag(base::FormatFlags::File)) { + // File + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::FileBase)) { + // FileBase + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildBaseFilename(logMessage->file(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Line)) { + // Line + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), + base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Location)) { + // Location + char* buf = base::utils::Str::clearBuff(buff, + base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + buf = base::utils::Str::addToBuff(":", buf, bufLim); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), + base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); + } + if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { + // Verbose level + char* buf = base::utils::Str::clearBuff(buff, 1); + buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { + // Log message + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); + } +#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); + it != ELPP->customFormatSpecifiers()->end(); ++it) { + std::string fs(it->formatSpecifier()); + base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); + base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, std::string(it->resolver()())); + } +#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + if (appendNewLine) logLine += ELPP_LITERAL("\n"); + return logLine; + } +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { +public: + LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(std::move(logMessage)), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void) { + if (m_proceed && m_dispatchAction == base::DispatchAction::None) { + m_proceed = false; + } + if (!m_proceed) { + return; + } + // We minimize the time of ELPP's lock - this lock is released after log is written + base::threading::ScopedLock scopedLock(ELPP->lock()); + base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; + if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { + tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); + } + LogDispatchCallback* callback = nullptr; + LogDispatchData data; + for (const std::pair& h + : ELPP->m_logDispatchCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + data.setLogMessage(&m_logMessage); + data.setDispatchAction(m_dispatchAction); + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + } + +private: + bool m_proceed; + LogMessage m_logMessage; + base::DispatchAction m_dispatchAction; +}; +#if defined(ELPP_STL_LOGGING) +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template +class IterableContainer { +public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { return getContainer().begin(); } + iterator end(void) { return getContainer().end(); } +private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template, typename Comparator = std::less> +class IterablePriorityQueue : public IterableContainer, public std::priority_queue { +public: + IterablePriorityQueue(std::priority_queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template> +class IterableQueue : public IterableContainer, public std::queue { +public: + IterableQueue(std::queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template> +class IterableStack : public IterableContainer, public std::stack { +public: + IterableStack(std::stack stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds +#endif // defined(ELPP_STL_LOGGING) +// Log message builder +class MessageBuilder { +public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {} + void initialize(Logger* logger) { + m_logger = logger; + m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? + ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); + } + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ + inline MessageBuilder& operator<<(LOG_TYPE msg) {\ + m_logger->stream() << msg;\ + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ + m_logger->stream() << " ";\ + }\ + return *this;\ + } + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + inline MessageBuilder& operator<<(const wchar_t* msg) { + if (msg == nullptr) { + m_logger->stream() << base::consts::kNullPointer; + return *this; + } +# if defined(ELPP_UNICODE) + m_logger->stream() << msg; +# else + char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); + m_logger->stream() << buff_; + free(buff_); +# endif + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } +#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } + +#if defined(ELPP_STL_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template + inline MessageBuilder& operator<<(const std::queue& queue_) { + base::workarounds::IterableQueue iterableQueue_ = + static_cast >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::stack& stack_) { + base::workarounds::IterableStack iterableStack_ = + static_cast >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template + inline MessageBuilder& operator<<(const std::priority_queue& priorityQueue_) { + base::workarounds::IterablePriorityQueue iterablePriorityQueue_ = + static_cast >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::pair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + inline MessageBuilder& operator<<(const std::bitset& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template + inline MessageBuilder& operator<<(const std::array& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template + inline MessageBuilder& operator<<(const QPair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMap& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = map_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiMap& map_) { + operator << (static_cast>(map_)); + return *this; + } + template + inline MessageBuilder& operator<<(const QHash& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = hash_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiHash& multiHash_) { + operator << (static_cast>(multiHash_)); + return *this; + } +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) +#endif // defined(ELPP_BOOST_LOGGING) + +/// @brief Macro used internally that can be used externally to make containers easylogging++ friendly +/// +/// @detail This macro expands to write an ostream& operator<< for container. This container is expected to +/// have begin() and end() methods that return respective iterators +/// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets +/// @param SizeMethod Method used to get size of container. +/// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro +/// for an example usage +#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ + el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ + const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ + ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");\ + ContainerType::const_iterator elem = container.begin();\ + ContainerType::const_iterator endElem = container.end();\ + std::size_t size_ = container.SizeMethod; \ + ss << ELPP_LITERAL("[");\ + for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \ + ss << ElementInstance;\ + ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\ + }\ + if (elem != endElem) {\ + ss << ELPP_LITERAL("...");\ + }\ + ss << ELPP_LITERAL("]");\ + return ss;\ + } +#if defined(ELPP_WXWIDGETS_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ + ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") +#else +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +#endif // defined(ELPP_WXWIDGETS_LOGGING) + // Other classes + template + ELPP_SIMPLE_LOG(const Class&) +#undef ELPP_SIMPLE_LOG +#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG +private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeperator; + + template + inline MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { +public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template + inline NullWriter& operator<<(const T&) { + return *this; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { +public: + Writer(Level level, const char* file, unsigned long int line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template + inline Writer& operator<<(const T& log) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + Writer& construct(Logger* logger, bool needLock = true) { + m_logger = logger; + initializeLogger(logger->id(), false, needLock); + m_messageBuilder.initialize(m_logger); + return *this; + } + + Writer& construct(int count, const char* loggerIds, ...) { + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + va_list loggersList; + va_start(loggersList, loggerIds); + const char* id = loggerIds; + for (int i = 0; i < count; ++i) { + m_loggerIds.push_back(std::string(id)); + id = va_arg(loggersList, const char*); + } + va_end(loggersList); + initializeLogger(m_loggerIds.at(0)); + } else { + initializeLogger(std::string(loggerIds)); + } + m_messageBuilder.initialize(m_logger); + return *this; + } +protected: + Level m_level; + const char* m_file; + const unsigned long int m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true) { + if (lookup) { + m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); + } + if (m_logger == nullptr) { + ELPP->acquireLock(); + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } + ELPP->releaseLock(); // Need to unlock it for next writer + Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Logger [" << loggerId << "] is not registered yet!"; + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { + m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : + LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel); + } else { + m_proceed = m_logger->enabled(m_level); + } + } + } + + void processDispatch() { +#if ELPP_LOGGING_ENABLED + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + bool firstDispatched = false; + base::type::string_t logMessage; + std::size_t i = 0; + do { + if (m_proceed) { + if (firstDispatched) { + m_logger->stream() << logMessage; + } else { + firstDispatched = true; + if (m_loggerIds.size() > 1) { + logMessage = m_logger->stream().str(); + } + } + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (i + 1 < m_loggerIds.size()) { + initializeLogger(m_loggerIds.at(i + 1)); + } + } while (++i < m_loggerIds.size()); + } else { + if (m_proceed) { + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + } +#else + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } +#endif // ELPP_LOGGING_ENABLED + } + + void triggerDispatch(void) { + if (m_proceed) { + base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger), m_dispatchAction).dispatch(); + } + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (m_proceed && m_level == Level::Fatal + && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { + base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; + std::stringstream reasonStream; + reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" + << " If you wish to disable 'abort on fatal log' please use " + << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + base::utils::abort(1, reasonStream.str()); + } + m_proceed = false; + } +}; +class PErrorWriter : public base::Writer { +public: + PErrorWriter(Level level, const char* file, unsigned long int line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void) { + if (m_proceed) { +#if ELPP_COMPILER_MSVC + char buff[256]; + strerror_s(buff, 256, errno); + m_logger->stream() << ": " << buff << " [" << errno << "]"; +#else + m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; +#endif + } + } +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } + } + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); + } + template + inline void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); + } + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } + } + template + void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, s, value, args...); + } + template + inline void Logger::log(Level level, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, log); + } +# if ELPP_VERBOSE_LOG + template + inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, s, value, args...); + } + template + inline void Logger::verbose(int vlevel, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, log); + } +# else + template + inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; + } + template + inline void Logger::verbose(int, const T&) { + return; + } +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ + template \ + inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ + log(LOG_LEVEL, s, value, args...);\ + }\ + template \ + inline void Logger::FUNCTION_NAME(const T& value) {\ + log(LOG_LEVEL, value);\ + } +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ + template \ + inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ + return;\ + }\ + template \ + inline void Logger::FUNCTION_NAME(const T&) {\ + return;\ + } + +# if ELPP_INFO_LOG + LOGGER_LEVEL_WRITERS(info, Level::Info) +# else + LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG + LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else + LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG + LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else + LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG + LOGGER_LEVEL_WRITERS(error, Level::Error) +# else + LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG + LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else + LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG + LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else + LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +#if ELPP_COMPILER_MSVC +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#else +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG +#endif // ELPP_COMPILER_MSVC +#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ + if (ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ + if (ELPP->validateAfterNCounter(__FILE__, __LINE__, n)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ + if (ELPP->validateNTimesCounter(__FILE__, __LINE__, n)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#undef ELPP_CURR_FILE_PERFORMANCE_LOGGER +#if defined(ELPP_PERFORMANCE_LOGGER) +# define ELPP_CURR_FILE_PERFORMANCE_LOGGER ELPP_PERFORMANCE_LOGGER +#else +# define ELPP_CURR_FILE_PERFORMANCE_LOGGER el::base::consts::kPerformanceLoggerId +#endif +class PerformanceTrackingData { +public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { return m_performanceTracker; } + inline PerformanceTrackingData::DataType dataType(void) const { return m_dataType; } + inline bool firstCheckpoint(void) const { return m_firstCheckpoint; } + inline std::string checkpointId(void) const { return m_checkpointId; } + inline const char* file(void) const { return m_file; } + inline unsigned long int line(void) const { return m_line; } + inline const char* func(void) const { return m_func; } + inline const base::type::string_t* formattedTimeTaken() const { return &m_formattedTimeTaken; } + inline const std::string& loggerId(void) const; +private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + unsigned long int m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { +public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(ELPP_CURR_FILE_PERFORMANCE_LOGGER), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel) : + m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), + m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + // We store it locally so that if user happen to change configuration by the end of scope + // or before calling checkpoint, we still depend on state of configuraton at time of construction + el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); + m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); + if (m_enabled) { + base::utils::DateTime::gettimeofday(&m_startTime); + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + } + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + if (m_scopedLog) { + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = getFormattedTimeTaken(); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); + data.init(this); + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + } + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) + } + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, unsigned long int line = __LINE__, const char* func = "") { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); + data.init(this); + data.m_checkpointId = id; + data.m_file = file; + data.m_line = line; + data.m_func = func; + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); + m_hasChecked = true; + m_lastCheckpointId = id; + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + ELPP_UNUSED(id); + ELPP_UNUSED(file); + ELPP_UNUSED(line); + ELPP_UNUSED(func); + } + inline Level level(void) const { return m_level; } +private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const { + if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { + base::type::stringstream_t ss; + ss << base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast(m_timestampUnit)].unit; + return ss.str(); + } + return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit), m_timestampUnit); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { +protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); + } + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } + } + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, m_data->loggerId().c_str()) << ss.str(); + } +private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { return m_performanceTracker->m_loggerId; } +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +class StackTrace : base::NoCopy { +public: + static const std::size_t kMaxStack = 64; + static const std::size_t kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr) { + m_index = index; + m_location = std::string(loc); + m_demangled = std::string(demang); + m_hex = std::string(hex); + m_addr = std::string(addr); + } + StackTraceEntry(std::size_t index, char* loc) { + m_index = index; + m_location = std::string(loc); + } + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si) { + ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled + << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; + return ss; + } + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector& getLatestStack(void) { + return m_stack; + } + + friend inline std::ostream& operator<<(std::ostream& os, const StackTrace& st) { + std::vector::const_iterator it = st.m_stack.begin(); + while (it != st.m_stack.end()) { + os << " " << *it++ << "\n"; + } + return os; + } + +private: + std::vector m_stack; + + void generateNew(void) { +#if ELPP_STACKTRACE + m_stack.clear(); + void* stack[kMaxStack]; + std::size_t size = backtrace(stack, kMaxStack); + char** strings = backtrace_symbols(stack, size); + if (size > kStackStart) { // Skip StackTrace c'tor and generateNew + for (std::size_t i = kStackStart; i < size; ++i) { + char* mangName = nullptr; + char* hex = nullptr; + char* addr = nullptr; + for (char* c = strings[i]; *c; ++c) { + switch (*c) { + case '(': + mangName = c; + break; + case '+': + hex = c; + break; + case ')': + addr = c; + break; + } + } + // Perform demangling if parsed properly + if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { + *mangName++ = '\0'; + *hex++ = '\0'; + *addr++ = '\0'; + int status = 0; + char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) + StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); + m_stack.push_back(entry); + } else { + // Not successful - we will use mangled name + StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); + m_stack.push_back(entry); + } + free(demangName); + } else { + StackTraceEntry entry(i - 1, strings[i]); + m_stack.push_back(entry); + } + } + } + free(strings); +#else + ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); +#endif // ELPP_STACKTRACE + } +}; +static std::string crashReason(int sig) { + std::stringstream ss; + bool foundReason = false; + for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { + if (base::consts::kCrashSignals[i].numb == sig) { + ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; + if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { + ss << std::endl << + " " << base::consts::kCrashSignals[i].brief << std::endl << + " " << base::consts::kCrashSignals[i].detail; + } + foundReason = true; + } + } + if (!foundReason) { + ss << "Application has crashed due to unknown signal [" << sig << "]"; + } + return ss.str(); +} +/// @brief Logs reason of crash from sig +static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + std::stringstream ss; + ss << "CRASH HANDLED; "; + ss << crashReason(sig); +#if ELPP_STACKTRACE + if (stackTraceIfAvailable) { + ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); + } +#else + ELPP_UNUSED(stackTraceIfAvailable); +#endif // ELPP_STACKTRACE + ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); +} +static inline void crashAbort(int sig) { + base::utils::abort(sig); +} +/// @brief Default application crash handler +/// +/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. +static inline void defaultCrashHandler(int sig) { + base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); + base::debug::crashAbort(sig); +} +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { +public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault) { + if (useDefault) { + setHandler(defaultCrashHandler); + } + } + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler) { + m_handler = cHandler; +#if defined(ELPP_HANDLE_SIGABRT) + int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] +#else + int i = 1; +#endif // defined(ELPP_HANDLE_SIGABRT) + for (; i < base::consts::kCrashSignalsCount; ++i) { + m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); + } + } + +private: + Handler m_handler; +}; +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; +#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ + el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { +public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +#if defined(ELPP_SYSLOG) + openlog(processIdent, options, facility); +#else + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); +#endif // defined(ELPP_SYSLOG) + } + virtual ~SysLogInitializer(void) { +#if defined(ELPP_SYSLOG) + closelog(); +#endif // defined(ELPP_SYSLOG) + } +}; +#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) +/// @brief Static helpers for developers +class Helpers : base::StaticClass { +public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast(argv)); + } + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static inline void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0) { + std::stringstream ss; + ss << base::debug::crashReason(sig).c_str(); + ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; + if (sourceFile != nullptr && strlen(sourceFile) > 0) { + ss << " - Source: " << sourceFile; + if (line > 0) + ss << ":" << line; + else + ss << " (line number not specified)"; + } + base::utils::abort(sig, ss.str()); + } + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static inline void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId) { + el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); + } + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback(id); + } + template + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback(id); + } + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback(id); + } + /// @brief Uninstalls post performance tracking handler + template + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback(id); + } + template + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback(id); + } + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; +#if defined(ELPP_UNICODE) + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); +#else + std::string s = logger->stream().str(); +#endif // defined(ELPP_UNICODE) + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { +public: + /// @brief Gets existing or registers new logger + static inline Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static inline bool unregisterLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->remove(identity); + } + /// @brief Whether or not logger with id is registered + static inline bool hasLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->has(identity); + } + /// @brief Reconfigures specified logger with new configurations + static inline Logger* reconfigureLogger(Logger* logger, const Configurations& configurations) { + if (!logger) return nullptr; + logger->configure(configurations); + return logger; + } + /// @brief Reconfigures logger with new configurations after looking it up using identity + static inline Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations) { + return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); + } + /// @brief Reconfigures logger's single configuration + static inline Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value) { + Logger* logger = Loggers::getLogger(identity); + if (logger == nullptr) { + return nullptr; + } + logger->configurations()->set(Level::Global, configurationType, value); + logger->reconfigure(); + return logger; + } + /// @brief Reconfigures all the existing loggers with new configurations + static inline void reconfigureAllLoggers(const Configurations& configurations) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Loggers::reconfigureLogger(it->second, configurations); + } + } + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static inline void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Logger* logger = it->second; + logger->configurations()->set(level, configurationType, value); + logger->reconfigure(); + } + } + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static inline void setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers = false) { + ELPP->registeredLoggers()->setDefaultConfigurations(configurations); + if (reconfigureExistingLoggers) { + Loggers::reconfigureAllLoggers(configurations); + } + } + /// @brief Returns current default + static inline const Configurations* defaultConfigurations(void) { + return ELPP->registeredLoggers()->defaultConfigurations(); + } + /// @brief Returns log stream reference pointer if needed by user + static inline const base::LogStreamsReferenceMap* logStreamsReference(void) { + return ELPP->registeredLoggers()->logStreamsReference(); + } + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void) { + return base::TypedConfigurations( + ELPP->registeredLoggers()->defaultConfigurations(), + ELPP->registeredLoggers()->logStreamsReference()); + } + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static inline std::vector* populateAllLoggerIds(std::vector* targetList) { + targetList->clear(); + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); + it != ELPP->registeredLoggers()->list().end(); ++it) { + targetList->push_back(it->first); + } + return targetList; + } + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath) { + std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); + ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath + << "] for parsing."); + std::string line = std::string(); + std::stringstream ss; + Logger* logger = nullptr; + auto configure = [&](void) { + ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() + << "\n--------------"); + Configurations c; + c.parseFromText(ss.str()); + logger->configure(c); + }; + while (gcfStream.good()) { + std::getline(gcfStream, line); + ELPP_INTERNAL_INFO(1, "Parsing line: " << line); + base::utils::Str::trim(line); + if (Configurations::Parser::isComment(line)) continue; + Configurations::Parser::ignoreComments(&line); + base::utils::Str::trim(line); + if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + ss.str(std::string("")); + line = line.substr(2); + base::utils::Str::trim(line); + if (line.size() > 1) { + ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); + logger = getLogger(line); + } + } else { + ss << line << "\n"; + } + } + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + } + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempeted to configure logger using configuration file provided in argument + static inline bool configureFromArg(const char* argKey) { +#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + ELPP_UNUSED(argKey); +#else + if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { + return false; + } + configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); +#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + return true; + } + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static inline void flushAll(void) { + ELPP->registeredLoggers()->flushAll(); + } + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { Loggers::addFlag(m_flag); } + ~ScopedAddFlag(void) { Loggers::removeFlag(m_flag); } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { Loggers::removeFlag(m_flag); } + ~ScopedRemoveFlag(void) { Loggers::addFlag(m_flag); } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static inline void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static inline void setVerboseLevel(base::type::VerboseLevel level) { + ELPP->vRegistry()->setLevel(level); + } + /// @brief Gets current verbose level + static inline base::type::VerboseLevel verboseLevel(void) { + return ELPP->vRegistry()->level(); + } + /// @brief Sets vmodules as specified (on the fly) + static inline void setVModules(const char* modules) { + if (ELPP->vRegistry()->vModulesEnabled()) { + ELPP->vRegistry()->setModules(modules); + } + } + /// @brief Clears vmodules + static inline void clearVModules(void) { + ELPP->vRegistry()->clearModules(); + } +}; +class VersionInfo : base::StaticClass { +public: + /// @brief Current version number + static inline const std::string version(void) { return std::string("9.80"); } + /// @brief Release date of current version + static inline const std::string releaseDate(void) { return std::string("08-01-2015 0850hrs"); } +}; +} // namespace el +#undef VLOG_IS_ON +/// @brief Determines whether verbose logging is on for specified level current file. +#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) +#undef TIMED_BLOCK +#undef TIMED_SCOPE +#undef TIMED_FUNC +#undef ELPP_MIN_UNIT +#if defined(ELPP_PERFORMANCE_MICROSECONDS) +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +#else +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) +/// @brief Performance tracked scope. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +// Note: Do not surround this definition with null macro because of obj instance +#define TIMED_SCOPE(obj, blockname) el::base::PerformanceTracker obj(blockname, ELPP_MIN_UNIT) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::PerformanceTracker timer; } obj = { 0, \ + el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i) +/// @brief Performance tracked function. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) +#undef PERFORMANCE_CHECKPOINT +#undef PERFORMANCE_CHECKPOINT_WITH_ID +#define PERFORMANCE_CHECKPOINT(obj) obj.checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj.checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#undef ELPP_COUNTER +#undef ELPP_COUNTER_POS +/// @brief Gets hit counter for file/line +#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__)) +/// @brief Gets hit counter position for file/line, -1 if not registered yet +#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts()) +// Undef levels to support LOG(LEVEL) +#undef INFO +#undef WARNING +#undef DEBUG +#undef ERROR +#undef FATAL +#undef TRACE +#undef VERBOSE +// Undef existing +#undef CINFO +#undef CWARNING +#undef CDEBUG +#undef CFATAL +#undef CERROR +#undef CTRACE +#undef CVERBOSE +#undef CINFO_IF +#undef CWARNING_IF +#undef CDEBUG_IF +#undef CERROR_IF +#undef CFATAL_IF +#undef CTRACE_IF +#undef CVERBOSE_IF +#undef CINFO_EVERY_N +#undef CWARNING_EVERY_N +#undef CDEBUG_EVERY_N +#undef CERROR_EVERY_N +#undef CFATAL_EVERY_N +#undef CTRACE_EVERY_N +#undef CVERBOSE_EVERY_N +#undef CINFO_AFTER_N +#undef CWARNING_AFTER_N +#undef CDEBUG_AFTER_N +#undef CERROR_AFTER_N +#undef CFATAL_AFTER_N +#undef CTRACE_AFTER_N +#undef CVERBOSE_AFTER_N +#undef CINFO_N_TIMES +#undef CWARNING_N_TIMES +#undef CDEBUG_N_TIMES +#undef CERROR_N_TIMES +#undef CFATAL_N_TIMES +#undef CTRACE_N_TIMES +#undef CVERBOSE_N_TIMES +// Normal logs +#if ELPP_INFO_LOG +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ + el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Conditional logs +#if ELPP_INFO_LOG +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ + el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Occasional logs +#if ELPP_INFO_LOG +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// After N logs +#if ELPP_INFO_LOG +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// N Times logs +#if ELPP_INFO_LOG +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// +// Custom Loggers - Requires (level, dispatchAction, loggerId/s) +// +// undef existing +#undef CLOG +#undef CLOG_VERBOSE +#undef CVLOG +#undef CLOG_IF +#undef CLOG_VERBOSE_IF +#undef CVLOG_IF +#undef CLOG_EVERY_N +#undef CVLOG_EVERY_N +#undef CLOG_AFTER_N +#undef CVLOG_AFTER_N +#undef CLOG_N_TIMES +#undef CVLOG_N_TIMES +// Normal logs +#define CLOG(LEVEL, ...)\ + C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Conditional logs +#define CLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_IF(condition, vlevel, ...)\ + CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Hit counts based logs +#define CLOG_EVERY_N(n, LEVEL, ...)\ + C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_EVERY_N(n, vlevel, ...)\ + CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_AFTER_N(n, LEVEL, ...)\ + C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_AFTER_N(n, vlevel, ...)\ + CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_N_TIMES(n, LEVEL, ...)\ + C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_N_TIMES(n, vlevel, ...)\ + CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// +// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef LOG +#undef VLOG +#undef LOG_IF +#undef VLOG_IF +#undef LOG_EVERY_N +#undef VLOG_EVERY_N +#undef LOG_AFTER_N +#undef VLOG_AFTER_N +#undef LOG_N_TIMES +#undef VLOG_N_TIMES +#undef ELPP_CURR_FILE_LOGGER_ID +#if defined(ELPP_DEFAULT_LOGGER) +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +#else +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +#endif +#undef ELPP_TRACE +#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) +// Normal logs +#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Generic PLOG() +#undef CPLOG +#undef CPLOG_IF +#undef PLOG +#undef PLOG_IF +#undef DCPLOG +#undef DCPLOG_IF +#undef DPLOG +#undef DPLOG_IF +#define CPLOG(LEVEL, ...)\ + C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CPLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG(LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +// Generic SYSLOG() +#undef CSYSLOG +#undef CSYSLOG_IF +#undef CSYSLOG_EVERY_N +#undef CSYSLOG_AFTER_N +#undef CSYSLOG_N_TIMES +#undef SYSLOG +#undef SYSLOG_IF +#undef SYSLOG_EVERY_N +#undef SYSLOG_AFTER_N +#undef SYSLOG_N_TIMES +#undef DCSYSLOG +#undef DCSYSLOG_IF +#undef DCSYSLOG_EVERY_N +#undef DCSYSLOG_AFTER_N +#undef DCSYSLOG_N_TIMES +#undef DSYSLOG +#undef DSYSLOG_IF +#undef DSYSLOG_EVERY_N +#undef DSYSLOG_AFTER_N +#undef DSYSLOG_N_TIMES +#if defined(ELPP_SYSLOG) +# define CSYSLOG(LEVEL, ...)\ + C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +#else +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +#endif // defined(ELPP_SYSLOG) +// +// Custom Debug Only Loggers - Requires (level, loggerId/s) +// +// undef existing +#undef DCLOG +#undef DCVLOG +#undef DCLOG_IF +#undef DCVLOG_IF +#undef DCLOG_EVERY_N +#undef DCVLOG_EVERY_N +#undef DCLOG_AFTER_N +#undef DCVLOG_AFTER_N +#undef DCLOG_N_TIMES +#undef DCVLOG_N_TIMES +// Normal logs +#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__) +#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__) +#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__) +// Conditional logs +#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__) +#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__) +// Hit counts based logs +#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__) +#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__) +#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__) +#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__) +// +// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef DLOG +#undef DVLOG +#undef DLOG_IF +#undef DVLOG_IF +#undef DLOG_EVERY_N +#undef DVLOG_EVERY_N +#undef DLOG_AFTER_N +#undef DVLOG_AFTER_N +#undef DLOG_N_TIMES +#undef DVLOG_N_TIMES +// Normal logs +#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Check macros +#undef CCHECK +#undef CPCHECK +#undef CCHECK_EQ +#undef CCHECK_NE +#undef CCHECK_LT +#undef CCHECK_GT +#undef CCHECK_LE +#undef CCHECK_GE +#undef CCHECK_BOUNDS +#undef CCHECK_NOTNULL +#undef CCHECK_STRCASEEQ +#undef CCHECK_STRCASENE +#undef CHECK +#undef PCHECK +#undef CHECK_EQ +#undef CHECK_NE +#undef CHECK_LT +#undef CHECK_GT +#undef CHECK_LE +#undef CHECK_GE +#undef CHECK_BOUNDS +#undef CHECK_NOTNULL +#undef CHECK_STRCASEEQ +#undef CHECK_STRCASENE +#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__) +#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__) +#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__) +#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__) +#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__) +#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__) +#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__) +#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +namespace el { +namespace base { +namespace utils { +template +static T* checkNotNull(T* ptr, const char* name, const char* loggers, ...) { + CLOG_IF(ptr == nullptr, FATAL, loggers) << "Check failed: [" << name << " != nullptr]"; + return ptr; +} +} // namespace utils +} // namespace base +} // namespace el +#define CCHECK_NOTNULL(ptr, ...) el::base::utils::checkNotNull(ptr, #ptr, __VA_ARGS__) +#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#undef DCCHECK +#undef DCCHECK_EQ +#undef DCCHECK_NE +#undef DCCHECK_LT +#undef DCCHECK_GT +#undef DCCHECK_LE +#undef DCCHECK_GE +#undef DCCHECK_BOUNDS +#undef DCCHECK_NOTNULL +#undef DCCHECK_STRCASEEQ +#undef DCCHECK_STRCASENE +#undef DCPCHECK +#undef DCHECK +#undef DCHECK_EQ +#undef DCHECK_NE +#undef DCHECK_LT +#undef DCHECK_GT +#undef DCHECK_LE +#undef DCHECK_GE +#undef DCHECK_BOUNDS_ +#undef DCHECK_NOTNULL +#undef DCHECK_STRCASEEQ +#undef DCHECK_STRCASENE +#undef DPCHECK +#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__) +#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__) +#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__) +#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__) +#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__) +#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__) +#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__) +#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__) +#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL(ptr, __VA_ARGS__) +#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__) +#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__) +#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +# define ELPP_USE_DEF_CRASH_HANDLER false +#else +# define ELPP_USE_DEF_CRASH_HANDLER true +#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +#define ELPP_CRASH_HANDLER_INIT +#define ELPP_INIT_EASYLOGGINGPP(val)\ + ELPP_INITI_BASIC_DECLR\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage(val);\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } + +#if ELPP_ASYNC_LOGGING +# define INITIALIZE_EASYLOGGINGPP\ + ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ + new el::base::AsyncDispatchWorker()))\ + +#else +# define INITIALIZE_EASYLOGGINGPP\ + ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +#endif // ELPP_ASYNC_LOGGING +#define INITIALIZE_NULL_EASYLOGGINGPP\ + ELPP_INITI_BASIC_DECLR\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage;\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } +// NOTE: no ELPP_INITI_BASIC_DECLR when sharing - causes double free corruption on external symbols +#define SHARE_EASYLOGGINGPP(initializedStorage)\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage(initializedStorage);\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } + +#if defined(ELPP_UNICODE) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +#else +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +#endif // defined(ELPP_UNICODE) +#endif // EASYLOGGINGPP_H diff --git a/stationapi/extras/chat.db.dist b/stationapi/extras/chat.db.dist new file mode 100755 index 0000000..28dd84c Binary files /dev/null and b/stationapi/extras/chat.db.dist differ diff --git a/stationapi/extras/init_database.sql b/stationapi/extras/init_database.sql new file mode 100755 index 0000000..a98b064 --- /dev/null +++ b/stationapi/extras/init_database.sql @@ -0,0 +1,73 @@ +CREATE TABLE avatar (id INTEGER PRIMARY KEY, + user_id INTEGER, + name TEXT, + address TEXT, + attributes INTEGER, + UNIQUE(name, address)); + +CREATE TABLE room (id INTEGER PRIMARY KEY, + creator_id INTEGER, + creator_name TEXT, + creator_address TEXT, + room_name TEXT, + room_topic TEXT, + room_password TEXT, + room_prefix TEXT, + room_address TEXT, + room_attributes INTEGER, + room_max_size INTEGER, + room_message_id INTEGER, + created_at INTEGER, + node_level INTEGER, + UNIQUE(room_name, room_address), + FOREIGN KEY(creator_id) REFERENCES avatar(id)); + +CREATE TABLE room_administrator (admin_avatar_id INTEGER, + room_id INTEGER, + PRIMARY KEY(admin_avatar_id, room_id), + FOREIGN KEY(admin_avatar_id) REFERENCES avatar(id) ON DELETE CASCADE, + FOREIGN KEY(room_id) REFERENCES room(id) ON DELETE CASCADE); + +CREATE TABLE room_moderator (moderator_avatar_id INTEGER, + room_id INTEGER, + PRIMARY KEY(moderator_avatar_id, room_id), + FOREIGN KEY(moderator_avatar_id) REFERENCES avatar(id) ON DELETE CASCADE, + FOREIGN KEY(room_id) REFERENCES room(id) ON DELETE CASCADE); + +CREATE TABLE room_ban (banned_avatar_id INTEGER, + room_id INTEGER, + PRIMARY KEY(banned_avatar_id, room_id), + FOREIGN KEY(banned_avatar_id) REFERENCES avatar(id) ON DELETE CASCADE, + FOREIGN KEY(room_id) REFERENCES room(id) ON DELETE CASCADE); + +CREATE TABLE room_invite (invited_avatar_id INTEGER, + room_id INTEGER, + PRIMARY KEY(invited_avatar_id, room_id), + FOREIGN KEY(invited_avatar_id) REFERENCES avatar(id) ON DELETE CASCADE, + FOREIGN KEY(room_id) REFERENCES room(id) ON DELETE CASCADE); + +CREATE TABLE persistent_message (id INTEGER PRIMARY KEY, + avatar_id INTEGER, + from_name TEXT, + from_address TEXT, + subject TEXT, + sent_time INTEGER, + status INTEGER, + folder TEXT, + category TEXT, + message TEXT, + oob BLOB, + FOREIGN KEY(avatar_id) REFERENCES avatar(id) ON DELETE CASCADE); + +CREATE TABLE friend (avatar_id INTEGER, + friend_avatar_id INTEGER, + comment TEXT, + PRIMARY KEY(avatar_id, friend_avatar_id), + FOREIGN KEY(avatar_id) REFERENCES avatar(id) ON DELETE CASCADE, + FOREIGN KEY(friend_avatar_id) REFERENCES avatar(id) ON DELETE CASCADE); + +CREATE TABLE ignore (avatar_id INTEGER, + ignore_avatar_id INTEGER, + PRIMARY KEY(avatar_id, ignore_avatar_id) + FOREIGN KEY(avatar_id) REFERENCES avatar(id) ON DELETE CASCADE, + FOREIGN KEY(ignore_avatar_id) REFERENCES avatar(id) ON DELETE CASCADE); diff --git a/stationapi/extras/logger.cfg.dist b/stationapi/extras/logger.cfg.dist new file mode 100755 index 0000000..75648b9 --- /dev/null +++ b/stationapi/extras/logger.cfg.dist @@ -0,0 +1,18 @@ +* GLOBAL: + FILENAME = "logs/swgchat.log" + TO_STANDARD_OUTPUT = true +* TRACE: + FILENAME = "logs/swgchat.log" +* DEBUG: + FILENAME = "logs/swgchat.log" +* FATAL: + FILENAME = "logs/swgchat.log" +* ERROR: + FILENAME = "logs/swgchat.log" +* WARNING: + FILENAME = "logs/swgchat.log" +* INFO: + FILENAME = "logs/swgchat.log" +* VERBOSE: + FILENAME = "logs/verbose.%datetime{%Y%M%d_%H%m%s}.log" + TO_STANDARD_OUTPUT = false diff --git a/stationapi/extras/swgchat.cfg.dist b/stationapi/extras/swgchat.cfg.dist new file mode 100755 index 0000000..a018b1e --- /dev/null +++ b/stationapi/extras/swgchat.cfg.dist @@ -0,0 +1,17 @@ +# Address that the gateway server will listen for connections on +gateway_address = 127.0.0.1 + +# Port the gateway server will listen for connections on +gateway_port = 5001 + +# Address that the registrar server will listen for connections on +registrar_address = 127.0.0.1 + +# Port the registrar server will listen for connections on +registrar_port = 5000 + +# Path to the application database +database_path = chat.db + +# When set to true, binds to the config address; otherwise, binds on any interface +bind_to_ip = false diff --git a/stationapi/src/CMakeLists.txt b/stationapi/src/CMakeLists.txt new file mode 100755 index 0000000..79583ca --- /dev/null +++ b/stationapi/src/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(stationapi) +add_subdirectory(stationchat) diff --git a/stationapi/src/stationapi/CMakeLists.txt b/stationapi/src/stationapi/CMakeLists.txt new file mode 100755 index 0000000..61670ef --- /dev/null +++ b/stationapi/src/stationapi/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/externals/easyloggingpp + ${PROJECT_SOURCE_DIR}/externals/udplibrary + ${Boost_INCLUDE_DIRS} + ${SQLite3_INCLUDE_DIR}) + +set(SOURCES + Node.hpp + NodeClient.cpp + NodeClient.hpp + Serialization.hpp + SQLite3.hpp + StreamUtils.cpp + StreamUtils.hpp + StringUtils.cpp + StringUtils.hpp) + +add_library(stationapi ${SOURCES}) + +GroupSourceByFilesystem("${SOURCES}") diff --git a/stationapi/src/stationapi/Node.hpp b/stationapi/src/stationapi/Node.hpp new file mode 100755 index 0000000..6ce98a3 --- /dev/null +++ b/stationapi/src/stationapi/Node.hpp @@ -0,0 +1,61 @@ + +#pragma once + +#include "UdpLibrary.hpp" + +#include +#include +#include +#include +#include + +template +class Node : public UdpManagerHandler { +public: + explicit Node(NodeT* node, const std::string& listenAddress, uint16_t listenPort, bool bindToIp = false) + : node_{node} { + + UdpManager::Params params; + params.handler = this; + params.port = listenPort; + + if (bindToIp) { + if (listenAddress.length() > sizeof(params.bindIpAddress)) { + throw std::runtime_error{"Invalid bind ip specified: " + listenAddress}; + } + + std::copy(std::begin(listenAddress), std::end(listenAddress), params.bindIpAddress); + } + + udpManager_ = new UdpManager(¶ms); + } + + virtual ~Node() { udpManager_->Release(); } + + void Tick() { + udpManager_->GiveTime(); + + auto remove_iter + = std::remove_if(std::begin(clients_), std::end(clients_), [](auto& client) { + return client->GetConnection()->GetStatus() == UdpConnection::cStatusDisconnected; + }); + + if (remove_iter != std::end(clients_)) + clients_.erase(remove_iter); + + OnTick(); + } + +private: + virtual void OnTick() = 0; + + void OnConnectRequest(UdpConnection* connection) override { + AddClient(std::make_unique(connection, node_)); + } + + void AddClient(std::unique_ptr client) { clients_.push_back(std::move(client)); } + + std::vector> clients_; + NodeT* node_; + UdpManager* udpManager_; +}; diff --git a/stationapi/src/stationapi/NodeClient.cpp b/stationapi/src/stationapi/NodeClient.cpp new file mode 100755 index 0000000..e8d3fd0 --- /dev/null +++ b/stationapi/src/stationapi/NodeClient.cpp @@ -0,0 +1,30 @@ + +#include "NodeClient.hpp" +#include "StreamUtils.hpp" + +NodeClient::NodeClient(UdpConnection* connection) + : connection_{connection} + , ostream_{std::stringstream::out | std::stringstream::binary} + , istream_{std::stringstream::in | std::stringstream::binary} { + connection_->AddRef(); +} + +NodeClient::~NodeClient() { + connection_->SetHandler(nullptr); + connection_->Disconnect(); + connection_->Release(); +} + +void NodeClient::Send(const char* data, uint32_t length) { + logNetworkMessage( + connection_, "Message To ->", reinterpret_cast(data), length); + connection_->Send(cUdpChannelReliable1, data, length); +} + +void NodeClient::OnRoutePacket(UdpConnection* connection, const uchar* data, int length) { + logNetworkMessage(connection, "Message From <-", data, length); + + istream_.clear(); + istream_.str({reinterpret_cast(data), static_cast(length)}); + OnIncoming(istream_); +} diff --git a/stationapi/src/stationapi/NodeClient.hpp b/stationapi/src/stationapi/NodeClient.hpp new file mode 100755 index 0000000..c779266 --- /dev/null +++ b/stationapi/src/stationapi/NodeClient.hpp @@ -0,0 +1,35 @@ + +#pragma once + +#include "UdpLibrary.hpp" + +#include + +class NodeClient : public UdpConnectionHandler { +public: + explicit NodeClient(UdpConnection* connection); + + virtual ~NodeClient(); + + template + void Send(const T& message) { + ostream_.clear(); + ostream_.str(""); + write(ostream_, message); + auto data = ostream_.str(); + Send(data.c_str(), data.length()); + } + + UdpConnection* GetConnection() { return connection_; } + +private: + void Send(const char* data, uint32_t length); + + virtual void OnIncoming(std::istringstream& istream) = 0; + + void OnRoutePacket(UdpConnection* connection, const uchar* data, int length) override; + + std::ostringstream ostream_; + std::istringstream istream_; + UdpConnection* connection_; +}; diff --git a/stationapi/src/stationapi/SQLite3.hpp b/stationapi/src/stationapi/SQLite3.hpp new file mode 100755 index 0000000..924ee73 --- /dev/null +++ b/stationapi/src/stationapi/SQLite3.hpp @@ -0,0 +1,14 @@ + +#pragma once + +#include +#include + +struct SQLite3Exception { + int code; + std::string message; + + SQLite3Exception(const int result, char const * text) + : code{result} + , message{text} {} +}; diff --git a/stationapi/src/stationapi/Serialization.hpp b/stationapi/src/stationapi/Serialization.hpp new file mode 100755 index 0000000..7158fbd --- /dev/null +++ b/stationapi/src/stationapi/Serialization.hpp @@ -0,0 +1,119 @@ + +#pragma once + +#include +#include + +// integral types + +template ::value, int> = 0> +void read(StreamT& istream, T& value) { + istream.read(reinterpret_cast(&value), sizeof(T)); +} + +template ::value, int> = 0> +void write(StreamT& ostream, const T& value) { + ostream.write(reinterpret_cast(&value), sizeof(T)); +} + +// enumeration types with integral underlying types + +template ::value, int> = 0> + void read(StreamT& istream, T& value) { + istream.read(reinterpret_cast(&value), sizeof(T)); +} + +template ::value, int> = 0> + void write(StreamT& ostream, const T& value) { + ostream.write(reinterpret_cast(&value), sizeof(T)); +} + +// boolean types + +template +void read(StreamT& istream, bool& value) { + uint8_t boolAsInt; + read(istream, boolAsInt); + value = (boolAsInt != 0); +} + +template +void write(StreamT& ostream, const bool& value) { + uint8_t boolAsInt = value ? 1 : 0; + write(ostream, boolAsInt); +} + +// std::string types + +template +void read(StreamT& istream, std::string& value) { + uint16_t length; + read(istream, length); + + value.resize(length); + + istream.read(&value[0], length); +} + +template +void write(StreamT& ostream, const std::string& value) { + uint16_t length = static_cast(value.length()); + write(ostream, length); + + ostream.write(&value[0], length); +} + +// std::u16string types + +template +void read(StreamT& istream, std::u16string& value) { + uint32_t length; + read(istream, length); + + value.resize(length); + uint16_t tmp; + for (uint32_t i = 0; i < length; ++i) { + istream.read(reinterpret_cast(&tmp), sizeof(uint16_t)); + value[i] = tmp; + } +} + +template +void write(StreamT& ostream, const std::u16string& value) { + uint32_t length = static_cast(value.length()); + write(ostream, length); + + uint16_t tmp; + for (uint32_t i = 0; i < length; ++i) { + tmp = static_cast(value[i]); + ostream.write(reinterpret_cast(&tmp), sizeof(uint16_t)); + } +} + +// Specialized Read Types + +template +T read(StreamT& istream) { + T tmp; + read(istream, tmp); + return tmp; +} + +template +T readAt(StreamT& istream, size_t offset) { + istream.seekg(offset); + return read(istream); +} + +// Similar to readAt, but preserves the read position of the stream +template +T peekAt(StreamT& istream, size_t offset) { + auto pos = istream.tellg(); + T val = readAt(istream, offset); + istream.seekg(pos); + return val; +} diff --git a/stationapi/src/stationapi/StreamUtils.cpp b/stationapi/src/stationapi/StreamUtils.cpp new file mode 100755 index 0000000..912995c --- /dev/null +++ b/stationapi/src/stationapi/StreamUtils.cpp @@ -0,0 +1,75 @@ + +#include "StreamUtils.hpp" +#include "UdpLibrary.hpp" + +#include "easylogging++.h" + +#include + +std::ostream& operator<<(std::ostream& os, const BinaryData& bd) { + auto length = bd.length(); + auto data = bd.data(); + + if (length == 0) + return os; + + // Calculate the number of lines and extra bits. + auto lines = static_cast(length / 16); + auto extra = static_cast(length % 16); + + // Save the formatting state of the stream. + auto flags = os.flags(os.hex); + auto fill = os.fill('0'); + auto width = os.width(2); + + // The byte buffer should be printed out in lines of 16 characters and display + // both hex and ascii values for each character, see most hex editors for + // reference. + char ascii[17] = {0}; + unsigned char c; + for (int16_t i = 0; i <= lines; i++) { + // Print out a line number. + os << std::setw(4) << (i * 16) << ": "; + + for (int16_t j = 0; j < 16; ++j) { // Loop through the characters of this line (max 16) + // For the last line there may not be 16 characters. In this case filler + // whitespace should be added to keep column widths consistent. + if (i == lines && j >= extra) { + os << " "; + ascii[j] = ' '; + } else { + c = data[(i * 16) + j]; + + os << std::setw(2) << static_cast(c) << " "; + + // If an ascii char print it, otherwise print a . instead of gibberish + ascii[j] = (c < ' ' || c > '~') ? '.' : c; + } + } + + os << " " << ascii << "\n"; + } + + // Return formatting of stream to its previous state. + os.flags(flags); + os.fill(fill); + os.width(width); + + return os; +} + +void logNetworkMessage( + UdpConnection* connection, std::string message, const unsigned char* data, int length) { + char hold[256]; + + VLOG(1) << "\n" + << message << " " << connection->GetDestinationIp().GetAddress(hold) << ":" + << connection->GetDestinationPort() << " length: " << length << "\n" + << BinaryData{data, length}; +} + +std::ostream& operator<<(std::ostream& os, const std::u16string& data) { + os << std::string{std::begin(data), std::end(data)}; + + return os; +} diff --git a/stationapi/src/stationapi/StreamUtils.hpp b/stationapi/src/stationapi/StreamUtils.hpp new file mode 100755 index 0000000..7cda2f8 --- /dev/null +++ b/stationapi/src/stationapi/StreamUtils.hpp @@ -0,0 +1,28 @@ + +#pragma once + +#include +#include + +class UdpConnection; + +class BinaryData { +public: + BinaryData(const unsigned char* data, int length) + : data_{ data } + , length_{ length } + {} + + const unsigned char* data() const { return data_; } + int length() const { return length_; } + +private: + const unsigned char* data_; + int length_; +}; + +std::ostream& operator<<(std::ostream& os, const BinaryData& data); + +std::ostream& operator<<(std::ostream& os, const std::u16string& data); + +void logNetworkMessage(UdpConnection* connection, std::string message, const unsigned char* data, int length); diff --git a/stationapi/src/stationapi/StringUtils.cpp b/stationapi/src/stationapi/StringUtils.cpp new file mode 100755 index 0000000..4bf260f --- /dev/null +++ b/stationapi/src/stationapi/StringUtils.cpp @@ -0,0 +1,10 @@ + +#include "StreamUtils.hpp" + +std::string FromWideString(const std::u16string& str) { + return std::string{std::begin(str), std::end(str)}; +} + +std::u16string ToWideString(const std::string& str) { + return std::u16string{std::begin(str), std::end(str)}; +} diff --git a/stationapi/src/stationapi/StringUtils.hpp b/stationapi/src/stationapi/StringUtils.hpp new file mode 100755 index 0000000..94e3a99 --- /dev/null +++ b/stationapi/src/stationapi/StringUtils.hpp @@ -0,0 +1,8 @@ + +#pragma once + +#include + +std::string FromWideString(const std::u16string& str); + +std::u16string ToWideString(const std::string& str); diff --git a/stationapi/src/stationchat/CMakeLists.txt b/stationapi/src/stationchat/CMakeLists.txt new file mode 100755 index 0000000..68af2a0 --- /dev/null +++ b/stationapi/src/stationchat/CMakeLists.txt @@ -0,0 +1,84 @@ +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/src/stationapi + ${PROJECT_SOURCE_DIR}/externals/easyloggingpp + ${PROJECT_SOURCE_DIR}/externals/udplibrary + ${Boost_INCLUDE_DIRS} + ${SQLite3_INCLUDE_DIR}) + +set(SOURCES + protocol/AddBan.hpp + protocol/AddFriend.hpp + protocol/AddIgnore.hpp + protocol/AddInvite.hpp + protocol/AddModerator.hpp + protocol/CreateRoom.hpp + protocol/DestroyAvatar.hpp + protocol/DestroyRoom.hpp + protocol/EnterRoom.hpp + protocol/FailoverReLoginAvatar.hpp + protocol/FriendStatus.hpp + protocol/GetAnyAvatar.hpp + protocol/GetPersistentHeaders.hpp + protocol/GetPersistentMessage.hpp + protocol/GetRoom.hpp + protocol/GetRoomSummaries.hpp + protocol/IgnoreStatus.hpp + protocol/KickAvatar.hpp + protocol/LeaveRoom.hpp + protocol/LoginAvatar.hpp + protocol/LogoutAvatar.hpp + protocol/Protocol.cpp + protocol/RegistrarGetChatServer.hpp + protocol/RemoveBan.hpp + protocol/RemoveFriend.hpp + protocol/RemoveIgnore.hpp + protocol/RemoveInvite.hpp + protocol/RemoveModerator.hpp + protocol/SendInstantMessage.hpp + protocol/SendPersistentMessage.hpp + protocol/SendRoomMessage.hpp + protocol/SetApiVersion.hpp + protocol/SetAvatarAttributes.hpp + protocol/UpdatePersistentMessage.hpp + + ChatAvatar.cpp + ChatAvatar.hpp + ChatAvatarService.cpp + ChatAvatarService.hpp + ChatEnums.cpp + ChatEnums.hpp + ChatRoom.cpp + ChatRoom.hpp + ChatRoomService.cpp + ChatRoomService.hpp + GatewayClient.cpp + GatewayClient.hpp + GatewayNode.cpp + GatewayNode.hpp + main.cpp + Message.hpp + PersistentMessage.hpp + PersistentMessageService.cpp + PersistentMessageService.hpp + RegistrarClient.cpp + RegistrarClient.hpp + RegistrarNode.cpp + RegistrarNode.hpp + StationChatApp.cpp + StationChatApp.hpp + StationChatConfig.hpp) + +add_executable(stationchat ${SOURCES}) + +GroupSourceByFilesystem("${SOURCES}") + +target_link_libraries(stationchat + stationapi + udplibrary + ${Boost_LIBRARIES} + ${SQLite3_LIBRARY}) + +if(WIN32) + target_link_libraries(stationchat ws2_32) +endif() diff --git a/stationapi/src/stationchat/ChatAvatar.cpp b/stationapi/src/stationchat/ChatAvatar.cpp new file mode 100755 index 0000000..4da889a --- /dev/null +++ b/stationapi/src/stationchat/ChatAvatar.cpp @@ -0,0 +1,91 @@ +#include "ChatAvatar.hpp" +#include "ChatAvatarService.hpp" +#include "ChatRoom.hpp" + +#include + +ChatAvatar::ChatAvatar(ChatAvatarService * avatarService) + : avatarService_{avatarService} {} + +ChatAvatar::ChatAvatar(ChatAvatarService* avatarService, const std::u16string& name, const std::u16string& address, uint32_t userId, + uint32_t attributes, const std::u16string& loginLocation) + : avatarService_{avatarService} + , userId_{userId} + , name_{name} + , address_{address} + , attributes_{attributes} + , loginLocation_{loginLocation} {} + +void ChatAvatar::SetAttributes(const uint32_t attributes) { attributes_ = attributes; } + +void ChatAvatar::AddFriend(ChatAvatar* avatar, const std::u16string& comment) { + if (IsFriend(avatar)) return; + if (IsIgnored(avatar)) RemoveIgnore(avatar); + + friendList_.push_back(FriendContact{avatar, comment}); + + avatarService_->PersistFriend(avatarId_, avatar->avatarId_, comment); +} + +void ChatAvatar::RemoveFriend(const ChatAvatar* avatar) { + auto del_iter = std::remove_if(std::begin(friendList_), std::end(friendList_), + [avatar](auto& frnd) { return frnd.frnd->GetAvatarId() == avatar->GetAvatarId(); }); + + if (del_iter != std::end(friendList_)) { + friendList_.erase(del_iter); + + avatarService_->RemoveFriend(avatarId_, avatar->avatarId_); + } +} + +void ChatAvatar::UpdateFriendComment(const ChatAvatar* avatar, const std::u16string& comment) { + auto find_iter = std::find_if(std::begin(friendList_), std::end(friendList_), + [avatar](auto& frnd) { return frnd.frnd->GetAvatarId() == avatar->GetAvatarId(); }); + + if (find_iter != std::end(friendList_)) { + find_iter->comment = comment; + avatarService_->UpdateFriendComment(avatarId_, avatar->avatarId_, comment); + } +} + +bool ChatAvatar::IsFriend(const ChatAvatar* avatar) { + auto find_iter = std::find_if(std::begin(friendList_), std::end(friendList_), + [avatar](auto& frnd) { return frnd.frnd->GetAvatarId() == avatar->GetAvatarId(); }); + + if (find_iter != std::end(friendList_)) { + return true; + } + + return false; +} + +void ChatAvatar::AddIgnore(ChatAvatar* avatar) { + if (IsIgnored(avatar)) return; + if (IsFriend(avatar)) RemoveFriend(avatar); + + ignoreList_.push_back(IgnoreContact{avatar}); + + avatarService_->PersistIgnore(avatarId_, avatar->avatarId_); +} + +void ChatAvatar::RemoveIgnore(const ChatAvatar* avatar) { + auto del_iter = std::remove_if(std::begin(ignoreList_), std::end(ignoreList_), + [avatar](auto& ignored) { return ignored.ignored->GetAvatarId() == avatar->GetAvatarId(); }); + + if (del_iter != std::end(ignoreList_)) { + ignoreList_.erase(del_iter); + + avatarService_->RemoveIgnore(avatarId_, avatar->avatarId_); + } +} + +bool ChatAvatar::IsIgnored(const ChatAvatar* avatar) { + auto find_iter = std::find_if(std::begin(ignoreList_), std::end(ignoreList_), + [avatar](auto& ignored) { return ignored.ignored->GetAvatarId() == avatar->GetAvatarId(); }); + + if (find_iter != std::end(ignoreList_)) { + return true; + } + + return false; +} diff --git a/stationapi/src/stationchat/ChatAvatar.hpp b/stationapi/src/stationchat/ChatAvatar.hpp new file mode 100755 index 0000000..06a2f17 --- /dev/null +++ b/stationapi/src/stationchat/ChatAvatar.hpp @@ -0,0 +1,131 @@ + +#pragma once + +#include "Serialization.hpp" + +#include +#include +#include + +class ChatAvatar; +class ChatAvatarService; +class ChatRoom; + +enum class AvatarAttribute : uint32_t { + INVISIBLE = 1 << 0, + GM = 1 << 1, + SUPERGM = 1 << 2, + SUPERSNOOP = 1 << 3, + EXTENDED = 1 << 4 +}; + +struct FriendContact { + FriendContact(const ChatAvatar* frnd_, const std::u16string& comment_) + : frnd{frnd_} + , comment{comment_} {} + + const ChatAvatar* frnd; + std::u16string comment = u""; +}; + +struct IgnoreContact { + IgnoreContact(const ChatAvatar* ignored_) + : ignored{ignored_} {} + + const ChatAvatar* ignored; +}; + +class ChatAvatar { +public: + explicit ChatAvatar(ChatAvatarService* avatarService); + ChatAvatar(ChatAvatarService* avatarService, const std::u16string& name, const std::u16string& address, uint32_t userId, + uint32_t attributes, const std::u16string& loginLocation); + + bool IsInvisible() const { return (attributes_ & static_cast(AvatarAttribute::INVISIBLE)) != 0; } + bool IsGm() const { return (attributes_ & static_cast(AvatarAttribute::GM)) != 0; } + bool IsSuperGm() const { return (attributes_ & static_cast(AvatarAttribute::SUPERGM)) != 0; } + bool IsSuperSnoop() const { return (attributes_ & static_cast(AvatarAttribute::SUPERSNOOP)) != 0; } + + const uint32_t GetAvatarId() const { return avatarId_; } + const uint32_t GetUserId() const { return userId_; } + const std::u16string& GetName() const { return name_; } + const std::u16string& GetAddress() const { return address_; } + const uint32_t GetAttributes() const { return attributes_; } + void SetAttributes(const uint32_t attributes); + const std::u16string& GetLoginLocation() const { return loginLocation_; } + const std::u16string& GetServer() const { return server_; } + const std::u16string& GetGateway() const { return gateway_; } + const uint32_t GetServerId() const { return serverId_; } + const uint32_t GetGatewayId() const { return gatewayId_; } + const std::u16string& GetEmail() const { return email_; } + const uint32_t GetInboxLimit() const { return inboxLimit_; } + const std::u16string& GetStatusMessage() const { return statusMessage_; } + const bool IsOnline() const { return isOnline_; } + + void AddFriend(ChatAvatar* avatar, const std::u16string& comment = u""); + void RemoveFriend(const ChatAvatar* avatar); + void UpdateFriendComment(const ChatAvatar* avatar, const std::u16string& comment); + bool IsFriend(const ChatAvatar* avatar); + + const std::vector GetFriendList() const { return friendList_; } + + void AddIgnore(ChatAvatar* avatar); + void RemoveIgnore(const ChatAvatar* avatar); + bool IsIgnored(const ChatAvatar* avatar); + + const std::vector GetIgnoreList() const { return ignoreList_; } + +private: + friend class ChatAvatarService; + + ChatAvatarService* avatarService_; + + uint32_t avatarId_ = 0; + uint32_t userId_ = 0; + std::u16string name_ = u""; + std::u16string address_ = u""; + uint32_t attributes_ = 0; + std::u16string loginLocation_ = u""; + std::u16string server_ = u""; + std::u16string gateway_ = u""; + uint32_t serverId_ = 0; + uint32_t gatewayId_ = 0; + std::u16string email_ = u""; + uint32_t inboxLimit_ = 0; + std::u16string statusMessage_ = u""; + bool isOnline_ = false; + + std::vector friendList_; + std::vector ignoreList_; + + std::vector rooms_; +}; + +template +void write(StreamT& ar, const ChatAvatar* data) { + write(ar, data->GetAvatarId()); + write(ar, data->GetUserId()); + write(ar, data->GetName()); + write(ar, data->GetAddress()); + write(ar, data->GetAttributes()); + write(ar, data->GetLoginLocation()); + write(ar, data->GetServer()); + write(ar, data->GetGateway()); + write(ar, data->GetServerId()); + write(ar, data->GetGatewayId()); +} + + +template +void write(StreamT& ar, const FriendContact& data) { + write(ar, data.frnd->GetName()); + write(ar, data.frnd->GetAddress()); + write(ar, data.comment); + write(ar, static_cast(data.frnd->IsOnline() ? 1 : 0)); +} + +template +void write(StreamT& ar, const IgnoreContact& data) { + write(ar, data.ignored->GetName()); + write(ar, data.ignored->GetAddress()); +} diff --git a/stationapi/src/stationchat/ChatAvatarService.cpp b/stationapi/src/stationchat/ChatAvatarService.cpp new file mode 100755 index 0000000..0de85a3 --- /dev/null +++ b/stationapi/src/stationchat/ChatAvatarService.cpp @@ -0,0 +1,488 @@ +#include "ChatAvatarService.hpp" +#include "ChatAvatar.hpp" +#include "SQLite3.hpp" +#include "StringUtils.hpp" + +#include + +ChatAvatarService::ChatAvatarService(sqlite3* db) + : db_{db} {} + +ChatAvatarService::~ChatAvatarService() {} + +ChatAvatar* ChatAvatarService::GetAvatar(const std::u16string& name, const std::u16string& address) { + ChatAvatar* avatar = GetCachedAvatar(name, address); + + if (!avatar) { + auto loadedAvatar = LoadStoredAvatar(name, address); + if (loadedAvatar != nullptr) { + avatar = loadedAvatar.get(); + avatarCache_.emplace_back(std::move(loadedAvatar)); + + LoadFriendList(avatar); + LoadIgnoreList(avatar); + } + } + + return avatar; +} + +ChatAvatar* ChatAvatarService::GetAvatar(uint32_t avatarId) { + ChatAvatar* avatar = GetCachedAvatar(avatarId); + + if (!avatar) { + auto loadedAvatar = LoadStoredAvatar(avatarId); + if (loadedAvatar != nullptr) { + avatar = loadedAvatar.get(); + avatarCache_.emplace_back(std::move(loadedAvatar)); + + LoadFriendList(avatar); + LoadIgnoreList(avatar); + } + } + + return avatar; +} + +ChatAvatar* ChatAvatarService::CreateAvatar(const std::u16string& name, const std::u16string& address, + uint32_t userId, uint32_t loginAttributes, const std::u16string& loginLocation) { + auto tmp + = std::make_unique(this, name, address, userId, loginAttributes, loginLocation); + auto avatar = tmp.get(); + + InsertAvatar(avatar); + + avatarCache_.emplace_back(std::move(tmp)); + + return avatar; +} + +void ChatAvatarService::DestroyAvatar(ChatAvatar* avatar) { + DeleteAvatar(avatar); + LogoutAvatar(avatar); + RemoveCachedAvatar(avatar->GetAvatarId()); +} + +void ChatAvatarService::LoginAvatar(ChatAvatar* avatar) { + avatar->isOnline_ = true; + + if (!IsOnline(avatar)) { + onlineAvatars_.push_back(avatar); + } +} + +void ChatAvatarService::LogoutAvatar(ChatAvatar* avatar) { + avatar->isOnline_ = false; + + onlineAvatars_.erase(std::remove_if( + std::begin(onlineAvatars_), std::end(onlineAvatars_), [avatar](auto onlineAvatar) { + return onlineAvatar->GetAvatarId() == avatar->GetAvatarId(); + })); +} + +void ChatAvatarService::PersistAvatar(const ChatAvatar* avatar) { UpdateAvatar(avatar); } + +void ChatAvatarService::PersistFriend( + uint32_t srcAvatarId, uint32_t destAvatarId, const std::u16string& comment) { + sqlite3_stmt* stmt; + char sql[] = "INSERT INTO friend (avatar_id, friend_avatar_id, comment) VALUES (@avatar_id, " + "@friend_avatar_id, @comment)"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + int friendAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@friend_avatar_id"); + int commentIdx = sqlite3_bind_parameter_index(stmt, "@comment"); + + std::string commentStr = FromWideString(comment); + + sqlite3_bind_int(stmt, avatarIdIdx, srcAvatarId); + sqlite3_bind_int(stmt, friendAvatarIdIdx, destAvatarId); + sqlite3_bind_text(stmt, commentIdx, commentStr.c_str(), -1, 0); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatAvatarService::PersistIgnore(uint32_t srcAvatarId, uint32_t destAvatarId) { + sqlite3_stmt* stmt; + char sql[] = "INSERT INTO ignore (avatar_id, ignore_avatar_id, comment) VALUES (@avatar_id, " + "@ignore_avatar_id)"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + int ignoreAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@ignore_avatar_id"); + + sqlite3_bind_int(stmt, avatarIdIdx, srcAvatarId); + sqlite3_bind_int(stmt, ignoreAvatarIdIdx, destAvatarId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatAvatarService::RemoveFriend(uint32_t srcAvatarId, uint32_t destAvatarId) { + sqlite3_stmt* stmt; + + char sql[] = "DELETE FROM friend WHERE avatar_id = @avatar_id AND friend_avatar_id = " + "@friend_avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + int friendAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@friend_avatar_id"); + + sqlite3_bind_int(stmt, avatarIdIdx, srcAvatarId); + sqlite3_bind_int(stmt, friendAvatarIdIdx, destAvatarId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatAvatarService::RemoveIgnore(uint32_t srcAvatarId, uint32_t destAvatarId) { + sqlite3_stmt* stmt; + + char sql[] = "DELETE FROM ignore WHERE avatar_id = @avatar_id AND ignore_avatar_id = " + "@ignore_avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + int ignoreAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@ignore_avatar_id"); + + sqlite3_bind_int(stmt, avatarIdIdx, srcAvatarId); + sqlite3_bind_int(stmt, ignoreAvatarIdIdx, destAvatarId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatAvatarService::UpdateFriendComment( + uint32_t srcAvatarId, uint32_t destAvatarId, const std::u16string& comment) { + sqlite3_stmt* stmt; + char sql[] = "UDPATE friend SET comment = @comment WHERE avatar_id = @avatar_id AND " + "friend_avatar_id = @friend_avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int commentIdx = sqlite3_bind_parameter_index(stmt, "@comment"); + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + int friendAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@friend_avatar_id"); + + std::string commentStr = FromWideString(comment); + + sqlite3_bind_text(stmt, commentIdx, commentStr.c_str(), -1, 0); + sqlite3_bind_int(stmt, avatarIdIdx, srcAvatarId); + sqlite3_bind_int(stmt, friendAvatarIdIdx, destAvatarId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +ChatAvatar* ChatAvatarService::GetCachedAvatar( + const std::u16string& name, const std::u16string& address) { + ChatAvatar* avatar = nullptr; + + // First look for the avatar in the cache + auto find_iter = std::find_if( + std::begin(avatarCache_), std::end(avatarCache_), [name, address](const auto& avatar) { + return avatar->name_.compare(name) == 0 && avatar->address_.compare(address) == 0; + }); + + if (find_iter != std::end(avatarCache_)) { + avatar = find_iter->get(); + } + + return avatar; +} + +ChatAvatar* ChatAvatarService::GetCachedAvatar(uint32_t avatarId) { + ChatAvatar* avatar = nullptr; + + // First look for the avatar in the cache + auto find_iter = std::find_if(std::begin(avatarCache_), std::end(avatarCache_), + [avatarId](const auto& avatar) { return avatar->avatarId_ == avatarId; }); + + if (find_iter != std::end(avatarCache_)) { + avatar = find_iter->get(); + } + + return avatar; +} + +void ChatAvatarService::RemoveCachedAvatar(uint32_t avatarId) { + auto remove_iter = std::remove_if(std::begin(avatarCache_), std::end(avatarCache_), + [avatarId](const auto& avatar) { return avatar->avatarId_ == avatarId; }); + + if (remove_iter != std::end(avatarCache_)) { + avatarCache_.erase(remove_iter); + } +} + +void ChatAvatarService::RemoveAsFriendOrIgnoreFromAll(const ChatAvatar* avatar) { + for (auto& cachedAvatar : avatarCache_) { + if (cachedAvatar->IsFriend(avatar)) { + cachedAvatar->RemoveFriend(avatar); + } + + if (cachedAvatar->IsIgnored(avatar)) { + cachedAvatar->RemoveIgnore(avatar); + } + } +} + +std::unique_ptr ChatAvatarService::LoadStoredAvatar( + const std::u16string& name, const std::u16string& address) { + std::unique_ptr avatar{nullptr}; + + sqlite3_stmt* stmt; + + char sql[] = "SELECT id, user_id, name, address, attributes FROM avatar WHERE name = @name AND " + "address = @address"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + std::string nameStr = FromWideString(name); + std::string addressStr = FromWideString(address); + + int nameIdx = sqlite3_bind_parameter_index(stmt, "@name"); + int addressIdx = sqlite3_bind_parameter_index(stmt, "@address"); + + sqlite3_bind_text(stmt, nameIdx, nameStr.c_str(), -1, 0); + sqlite3_bind_text(stmt, addressIdx, addressStr.c_str(), -1, 0); + + if (sqlite3_step(stmt) == SQLITE_ROW) { + avatar = std::make_unique(this); + avatar->avatarId_ = sqlite3_column_int(stmt, 0); + avatar->userId_ = sqlite3_column_int(stmt, 1); + + auto tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 2))); + avatar->name_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 3))); + avatar->address_ = std::u16string(std::begin(tmp), std::end(tmp)); + + avatar->attributes_ = sqlite3_column_int(stmt, 4); + } + + sqlite3_finalize(stmt); + + return avatar; +} + +std::unique_ptr ChatAvatarService::LoadStoredAvatar(uint32_t avatarId) { + std::unique_ptr avatar{nullptr}; + + sqlite3_stmt* stmt; + + char sql[] = "SELECT id, user_id, name, address, attributes FROM avatar WHERE id = @avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + + sqlite3_bind_int(stmt, avatarIdIdx, avatarId); + + if (sqlite3_step(stmt) == SQLITE_ROW) { + avatar = std::make_unique(this); + avatar->avatarId_ = sqlite3_column_int(stmt, 0); + avatar->userId_ = sqlite3_column_int(stmt, 1); + + auto tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 2))); + avatar->name_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 3))); + avatar->address_ = std::u16string(std::begin(tmp), std::end(tmp)); + + avatar->attributes_ = sqlite3_column_int(stmt, 4); + } + + sqlite3_finalize(stmt); + + return avatar; +} + +void ChatAvatarService::InsertAvatar(ChatAvatar* avatar) { + CHECK_NOTNULL(avatar); + sqlite3_stmt* stmt; + + char sql[] = "INSERT INTO avatar (user_id, name, address, attributes) VALUES (@user_id, @name, " + "@address, @attributes)"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + std::string nameStr = FromWideString(avatar->name_); + std::string addressStr = FromWideString(avatar->address_); + + int userIdIdx = sqlite3_bind_parameter_index(stmt, "@user_id"); + int nameIdx = sqlite3_bind_parameter_index(stmt, "@name"); + int addressIdx = sqlite3_bind_parameter_index(stmt, "@address"); + int attributesIdx = sqlite3_bind_parameter_index(stmt, "@attributes"); + + sqlite3_bind_int(stmt, userIdIdx, avatar->userId_); + sqlite3_bind_text(stmt, nameIdx, nameStr.c_str(), -1, 0); + sqlite3_bind_text(stmt, addressIdx, addressStr.c_str(), -1, 0); + sqlite3_bind_int(stmt, attributesIdx, avatar->attributes_); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + avatar->avatarId_ = static_cast(sqlite3_last_insert_rowid(db_)); + + sqlite3_finalize(stmt); +} + +void ChatAvatarService::UpdateAvatar(const ChatAvatar* avatar) { + CHECK_NOTNULL(avatar); + sqlite3_stmt* stmt; + + char sql[] = "UPDATE avatar SET user_id = @user_id, name = @name, address = @address, " + "attributes = @attributes " + "WHERE id = @avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + std::string nameStr = FromWideString(avatar->name_); + std::string addressStr = FromWideString(avatar->address_); + + int userIdIdx = sqlite3_bind_parameter_index(stmt, "@user_id"); + int nameIdx = sqlite3_bind_parameter_index(stmt, "@name"); + int addressIdx = sqlite3_bind_parameter_index(stmt, "@address"); + int attributesIdx = sqlite3_bind_parameter_index(stmt, "@attributes"); + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + + sqlite3_bind_int(stmt, userIdIdx, avatar->userId_); + sqlite3_bind_text(stmt, nameIdx, nameStr.c_str(), -1, 0); + sqlite3_bind_text(stmt, addressIdx, addressStr.c_str(), -1, 0); + sqlite3_bind_int(stmt, attributesIdx, avatar->attributes_); + sqlite3_bind_int(stmt, avatarIdIdx, avatar->avatarId_); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + sqlite3_finalize(stmt); +} + +void ChatAvatarService::DeleteAvatar(ChatAvatar* avatar) { + CHECK_NOTNULL(avatar); + sqlite3_stmt* stmt; + + char sql[] = "DELETE FROM avatar WHERE id = @avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + + sqlite3_bind_int(stmt, avatarIdIdx, avatar->avatarId_); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + sqlite3_finalize(stmt); +} + +void ChatAvatarService::LoadFriendList(ChatAvatar* avatar) { + sqlite3_stmt* stmt; + + char sql[] = "SELECT friend_avatar_id, comment FROM friend WHERE avatar_id = @avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + + sqlite3_bind_int(stmt, avatarIdIdx, avatar->avatarId_); + + uint32_t tmpFriendId; + std::string tmpComment; + while (sqlite3_step(stmt) == SQLITE_ROW) { + tmpFriendId = sqlite3_column_int(stmt, 0); + tmpComment = reinterpret_cast(sqlite3_column_text(stmt, 1)); + + auto friendAvatar = GetAvatar(tmpFriendId); + + avatar->friendList_.emplace_back(friendAvatar, ToWideString(tmpComment)); + } +} + +void ChatAvatarService::LoadIgnoreList(ChatAvatar* avatar) { + sqlite3_stmt* stmt; + + char sql[] = "SELECT ignore_avatar_id FROM ignore WHERE avatar_id = @avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + + sqlite3_bind_int(stmt, avatarIdIdx, avatar->avatarId_); + + uint32_t tmpIgnoreId; + while (sqlite3_step(stmt) == SQLITE_ROW) { + tmpIgnoreId = sqlite3_column_int(stmt, 0); + + auto ignoreAvatar = GetAvatar(tmpIgnoreId); + + avatar->ignoreList_.emplace_back(ignoreAvatar); + } +} + +bool ChatAvatarService::IsOnline(const ChatAvatar * avatar) const { + for (auto onlineAvatar : onlineAvatars_) { + if (onlineAvatar->GetAvatarId() == avatar->GetAvatarId()) { + return true; + } + } + + return false; +} diff --git a/stationapi/src/stationchat/ChatAvatarService.hpp b/stationapi/src/stationchat/ChatAvatarService.hpp new file mode 100755 index 0000000..8c47b03 --- /dev/null +++ b/stationapi/src/stationchat/ChatAvatarService.hpp @@ -0,0 +1,65 @@ + +#pragma once + +#include "ChatAvatar.hpp" +#include "ChatEnums.hpp" + +#include + +#include +#include +#include + +struct sqlite3; + +class ChatAvatarService { +public: + explicit ChatAvatarService(sqlite3* db); + ~ChatAvatarService(); + + ChatAvatar* GetAvatar(const std::u16string& name, const std::u16string& address); + ChatAvatar* GetAvatar(uint32_t avatarId); + + ChatAvatar* CreateAvatar(const std::u16string& name, + const std::u16string& address, uint32_t userId, uint32_t loginAttributes, + const std::u16string& loginLocation); + + void DestroyAvatar(ChatAvatar* avatar); + + void LoginAvatar(ChatAvatar* avatar); + void LogoutAvatar(ChatAvatar* avatar); + + void PersistAvatar(const ChatAvatar* avatar); + void PersistFriend(uint32_t srcAvatarId, uint32_t destAvatarId, const std::u16string& comment); + void PersistIgnore(uint32_t srcAvatarId, uint32_t destAvatarId); + + void RemoveFriend(uint32_t srcAvatarId, uint32_t destAvatarId); + void RemoveIgnore(uint32_t srcAvatarId, uint32_t destAvatarId); + + void UpdateFriendComment(uint32_t srcAvatarId, uint32_t destAvatarId, const std::u16string& comment); + + const std::vector& GetOnlineAvatars() const { return onlineAvatars_; } + +private: + ChatAvatar* GetCachedAvatar(const std::u16string& name, const std::u16string& address); + ChatAvatar* GetCachedAvatar(uint32_t avatarId); + + void RemoveCachedAvatar(uint32_t avatarId); + void RemoveAsFriendOrIgnoreFromAll(const ChatAvatar* avatar); + + std::unique_ptr LoadStoredAvatar(const std::u16string& name, const std::u16string& address); + std::unique_ptr LoadStoredAvatar(uint32_t avatarId); + + void InsertAvatar(ChatAvatar* avatar); + void UpdateAvatar(const ChatAvatar* avatar); + void DeleteAvatar(ChatAvatar* avatar); + + void LoadFriendList(ChatAvatar* avatar); + void LoadIgnoreList(ChatAvatar* avatar); + + bool IsOnline(const ChatAvatar* avatar) const; + + std::vector> avatarCache_; + std::vector onlineAvatars_; + sqlite3* db_; +}; diff --git a/stationapi/src/stationchat/ChatEnums.cpp b/stationapi/src/stationchat/ChatEnums.cpp new file mode 100755 index 0000000..49abb05 --- /dev/null +++ b/stationapi/src/stationchat/ChatEnums.cpp @@ -0,0 +1,154 @@ +#include "ChatEnums.hpp" + +const char* ToString(ChatResultCode code) { + switch (code) { + case ChatResultCode::SUCCESS: + return "SUCCESS"; + case ChatResultCode::TIMEOUT: + return "TIMEOUT"; + case ChatResultCode::DUPLICATELOGIN: + return "DUPLICATELOGIN"; + case ChatResultCode::SRCAVATARDOESNTEXIST: + return "SRCAVATARDOESNTEXIST"; + case ChatResultCode::DESTAVATARDOESNTEXIST: + return "DESTAVATARDOESNTEXIST"; + case ChatResultCode::ADDRESSDOESNTEXIST: + return "ADDRESSDOESNTEXIST"; + case ChatResultCode::ADDRESSNOTROOM: + return "ADDRESSNOTROOM"; + case ChatResultCode::ADDRESSNOTAID: + return "ADDRESSNOTAID"; + case ChatResultCode::FRIENDNOTFOUND: + return "FRIENDNOTFOUND"; + case ChatResultCode::ROOM_UNKNOWNFAILURE: + return "ROOM_UNKNOWNFAILURE"; + case ChatResultCode::ROOM_SRCNOTINROOM: + return "ROOM_SRCNOTINROOM"; + case ChatResultCode::ROOM_DESTNOTINROOM: + return "ROOM_DESTNOTINROOM"; + case ChatResultCode::ROOM_BANNEDAVATAR: + return "ROOM_BANNEDAVATAR"; + case ChatResultCode::ROOM_PRIVATEROOM: + return "ROOM_PRIVATEROOM"; + case ChatResultCode::ROOM_MODERATEDROOM: + return "ROOM_MODERATEDROOM"; + case ChatResultCode::ROOM_NOTINROOM: + return "ROOM_NOTINROOM"; + case ChatResultCode::ROOM_NOPRIVILEGES: + return "ROOM_NOPRIVILEGES"; + case ChatResultCode::DATABASE: + return "DATABASE"; + case ChatResultCode::CANNOTGETAVATARID: + return "CANNOTGETAVATARID"; + case ChatResultCode::CANNOTGETNODEID: + return "CANNOTGETNODEID"; + case ChatResultCode::CANNOTGETPMSGID: + return "CANNOTGETPMSGID"; + case ChatResultCode::PMSGNOTFOUND: + return "PMSGNOTFOUND"; + case ChatResultCode::ROOMMAXAVATARSREACHED: + return "ROOMMAXAVATARSREACHED"; + case ChatResultCode::IGNORING: + return "IGNORING"; + case ChatResultCode::ROOM_ALREADYEXISTS: + return "ROOM_ALREADYEXISTS"; + case ChatResultCode::NOTHINGTOCONFIRM: + return "NOTHINGTOCONFIRM"; + case ChatResultCode::DUPLICATEFRIEND: + return "DUPLICATEFRIEND"; + case ChatResultCode::IGNORENOTFOUND: + return "IGNORENOTFOUND"; + case ChatResultCode::DUPLICATEIGNORE: + return "DUPLICATEIGNORE"; + case ChatResultCode::DBFAIL: + return "DBFAIL"; + case ChatResultCode::ROOM_DESTAVATARNOTMODERATOR: + return "ROOM_DESTAVATARNOTMODERATOR"; + case ChatResultCode::ROOM_DESTAVATARNOTINVITED: + return "ROOM_DESTAVATARNOTINVITED"; + case ChatResultCode::ROOM_DESTAVATARNOTBANNED: + return "ROOM_DESTAVATARNOTBANNED"; + case ChatResultCode::ROOM_DUPLICATEBAN: + return "ROOM_DUPLICATEBAN"; + case ChatResultCode::ROOM_DUPLICATEMODERATOR: + return "ROOM_DUPLICATEMODERATOR"; + case ChatResultCode::ROOM_DUPLICATEINVITE: + return "ROOM_DUPLICATEINVITE"; + case ChatResultCode::ROOM_ALREADYINROOM: + return "ROOM_ALREADYINROOM"; + case ChatResultCode::ROOM_PARENTNONPERSISTENT: + return "ROOM_PARENTNONPERSISTENT"; + case ChatResultCode::ROOM_PARENTBADNODETYPE: + return "ROOM_PARENTBADNODETYPE"; + case ChatResultCode::NOFANCLUBHANDLE: + return "NOFANCLUBHANDLE"; + case ChatResultCode::AIDALREADYEXISTS: + return "AIDALREADYEXISTS"; + case ChatResultCode::UIDALREADYEXISTS: + return "UIDALREADYEXISTS"; + case ChatResultCode::WRONGCHATSERVERFORREQUEST: + return "WRONGCHATSERVERFORREQUEST"; + case ChatResultCode::SUCCESSBADDATA: + return "SUCCESSBADDATA"; + case ChatResultCode::NULLNAMELOGIN: + return "NULLNAMELOGIN"; + case ChatResultCode::SERVER_IDENTITY_EMPTY: + return "SERVER_IDENTITY_EMPTY"; + case ChatResultCode::SERVER_IDENTITY_TAKEN: + return "SERVER_IDENTITY_TAKEN"; + case ChatResultCode::REMOTESERVERDOWN: + return "REMOTESERVERDOWN"; + case ChatResultCode::NODEIDCONFLICT: + return "NODEIDCONFLICT"; + case ChatResultCode::INVALIDNODENAME: + return "INVALIDNODENAME"; + case ChatResultCode::INSUFFICIENTGMPRIVS: + return "INSUFFICIENTGMPRIVS"; + case ChatResultCode::SNOOPALREADYADDED: + return "SNOOPALREADYADDED"; + case ChatResultCode::NOTSNOOPING: + return "NOTSNOOPING"; + case ChatResultCode::ROOM_DESTAVATARNOTTEMPORARYMODERATOR: + return "ROOM_DESTAVATARNOTTEMPORARYMODERATOR"; + case ChatResultCode::ROOM_DESTAVATARNOTVOICE: + return "ROOM_DESTAVATARNOTVOICE"; + case ChatResultCode::ROOM_DUPLICATETEMPORARYMODERATOR: + return "ROOM_DUPLICATETEMPORARYMODERATOR"; + case ChatResultCode::ROOM_DUPLICATEVOICE: + return "ROOM_DUPLICATEVOICE"; + case ChatResultCode::AVATARMUSTBELOGGEDOUT: + return "AVATARMUSTBELOGGEDOUT"; + case ChatResultCode::NOTHINGTODO: + return "NOTHINGTODO"; + case ChatResultCode::TRANSFERNAMENULL: + return "TRANSFERNAMENULL"; + case ChatResultCode::TRANSFERUSERIDZERO: + return "TRANSFERUSERIDZERO"; + case ChatResultCode::TRANSFERADDRESSNULL: + return "TRANSFERADDRESSNULL"; + case ChatResultCode::OUTOFIDS: + return "OUTOFIDS"; + case ChatResultCode::ROOM_LOCALROOM: + return "ROOM_LOCALROOM"; + case ChatResultCode::ROOM_GAMEROOM: + return "ROOM_GAMEROOM"; + case ChatResultCode::ROOM_DESTAVATARNOTENTERING: + return "ROOM_DESTAVATARNOTENTERING"; + case ChatResultCode::INSUFFICIENTPRIORITY: + return "INSUFFICIENTPRIORITY"; + case ChatResultCode::ROOM_WAITINGFORENTRY: + return "ROOM_WAITINGFORENTRY"; + case ChatResultCode::INBOXLIMITEXCEEDED: + return "INBOXLIMITEXCEEDED"; + case ChatResultCode::DUPLICATEDESTINATION: + return "DUPLICATEDESTINATION"; + case ChatResultCode::CATEGORYLIMITEXCEEDED: + return "CATEGORYLIMITEXCEEDED"; + case ChatResultCode::MESSAGE_FILTER_FAILURE: + return "MESSAGE_FILTER_FAILURE"; + case ChatResultCode::INVALID_INPUT: + return "INVALID_INPUT"; + }; + + return ""; +} diff --git a/stationapi/src/stationchat/ChatEnums.hpp b/stationapi/src/stationchat/ChatEnums.hpp new file mode 100755 index 0000000..1804bd0 --- /dev/null +++ b/stationapi/src/stationchat/ChatEnums.hpp @@ -0,0 +1,252 @@ + +#pragma once + +#include +#include + +enum class ChatRequestType : uint16_t { + LOGINAVATAR = 0, + LOGOUTAVATAR, + DESTROYAVATAR, + GETAVATAR, + CREATEROOM, + DESTROYROOM, // 5 + SENDINSTANTMESSAGE, + SENDROOMMESSAGE, + SENDBROADCASTMESSAGE, + ADDFRIEND, + REMOVEFRIEND, // 10 + FRIENDSTATUS, + ADDIGNORE, + REMOVEIGNORE, + ENTERROOM, + LEAVEROOM, // 15 + ADDMODERATOR, + REMOVEMODERATOR, + ADDBAN, + REMOVEBAN, + ADDINVITE, // 20 + REMOVEINVITE, + KICKAVATAR, + SETROOMPARAMS, + GETROOM, + GETROOMSUMMARIES, // 25 + SENDPERSISTENTMESSAGE, + GETPERSISTENTHEADERS, + GETPERSISTENTMESSAGE, + UPDATEPERSISTENTMESSAGE, + UNREGISTERROOM, // 30 + IGNORESTATUS, + FAILOVER_RELOGINAVATAR, + FAILOVER_RECREATEROOM, + CONFIRMFRIEND, + GETAVATARKEYWORDS, // 35 + SETAVATARKEYWORDS, + SEARCHAVATARKEYWORDS, + GETFANCLUBHANDLE, + UPDATEPERSISTENTMESSAGES, + FINDAVATARBYUID, // 40 + CHANGEROOMOWNER, + SETAPIVERSION, + ADDTEMPORARYMODERATOR, + REMOVETEMPORARYMODERATOR, + GRANTVOICE, // 45 + REVOKEVOICE, + SETAVATARATTRIBUTES, + ADDSNOOPAVATAR, + REMOVESNOOPAVATAR, + ADDSNOOPROOM, // 50 + REMOVESNOOPROOM, + GETSNOOPLIST, + PARTIALPERSISTENTHEADERS, + COUNTPERSISTENTMESSAGES, + PURGEPERSISTENTMESSAGES, // 55 + SETFRIENDCOMMENT, + TRANSFERAVATAR, + CHANGEPERSISTENTFOLDER, + ALLOWROOMENTRY, + SETAVATAREMAIL, // 60 + SETAVATARINBOXLIMIT, + SENDMULTIPLEPERSISTENTMESSAGES, + GETMULTIPLEPERSISTENTMESSAGES, + ALTERPERISTENTMESSAGE, + GETANYAVATAR, // 65 + TEMPORARYAVATAR, + AVATARLIST, + SETAVATARSTATUSMESSAGE, + CONFIRMFRIEND_RECIPROCATE, + ADDFRIEND_RECIPROCATE, // 70 + REMOVEFRIEND_RECIPROCATE, + FILTERMESSAGE, + FILTERMESSAGE_EX, + REGISTRAR_GETCHATSERVER = 20001, +}; + +enum class ChatResponseType : uint16_t { + LOGINAVATAR = 0, + LOGOUTAVATAR, + DESTROYAVATAR, + GETAVATAR, + CREATEROOM, + DESTROYROOM, + SENDINSTANTMESSAGE, + SENDROOMMESSAGE, + SENDBROADCASTMESSAGE, + ADDFRIEND, + REMOVEFRIEND, + FRIENDSTATUS, + ADDIGNORE, + REMOVEIGNORE, + ENTERROOM, + LEAVEROOM, + ADDMODERATOR, + REMOVEMODERATOR, + ADDBAN, + REMOVEBAN, + ADDINVITE, + REMOVEINVITE, + KICKAVATAR, + SETROOMPARAMS, + GETROOM, + GETROOMSUMMARIES, + SENDPERSISTENTMESSAGE, + GETPERSISTENTHEADERS, + GETPERSISTENTMESSAGE, + UPDATEPERSISTENTMESSAGE, + UNREGISTERROOM, + IGNORESTATUS, + FAILOVER_RELOGINAVATAR, + FAILOVER_RECREATEROOM, + CONFIRMFRIEND, + GETAVATARKEYWORDS, + SETAVATARKEYWORDS, + SEARCHAVATARKEYWORDS, + GETFANCLUBHANDLE, + UPDATEPERSISTENTMESSAGES, + FINDAVATARBYUID, + CHANGEROOMOWNER, + SETAPIVERSION, + ADDTEMPORARYMODERATOR, + REMOVETEMPORARYMODERATOR, + GRANTVOICE, + REVOKEVOICE, + SETAVATARATTRIBUTES, + ADDSNOOPAVATAR, + REMOVESNOOPAVATAR, + ADDSNOOPROOM, + REMOVESNOOPROOM, + GETSNOOPLIST, + PARTIALPERSISTENTHEADERS, + COUNTPERSISTENTMESSAGES, + PURGEPERSISTENTMESSAGES, + SETFRIENDCOMMENT, + TRANSFERAVATAR, + CHANGEPERSISTENTFOLDER, + ALLOWROOMENTRY, + SETAVATAREMAIL, + SETAVATARINBOXLIMIT, + SENDMULTIPLEPERSISTENTMESSAGES, + GETMULTIPLEPERSISTENTMESSAGES, + ALTERPERSISTENTMESSAGE, + GETANYAVATAR, + TEMPORARYAVATAR, + AVATARLIST, + SETSTATUSMESSAGE, + CONFIRMFRIEND_RECIPROCATE, + ADDFRIEND_RECIPROCATE, + REMOVEFRIEND_RECIPROCATE, + FILTERMESSAGE, + FILTERMESSAGE_EX, + + REGISTRAR_GETCHATSERVER = 20001, +}; + +enum class ChatResultCode : uint32_t { + SUCCESS = 0, + TIMEOUT = 1, + DUPLICATELOGIN, + SRCAVATARDOESNTEXIST, + DESTAVATARDOESNTEXIST, + ADDRESSDOESNTEXIST, // 5 + ADDRESSNOTROOM, + ADDRESSNOTAID, + FRIENDNOTFOUND, + ROOM_UNKNOWNFAILURE, + ROOM_SRCNOTINROOM, // 10 + ROOM_DESTNOTINROOM, + ROOM_BANNEDAVATAR, + ROOM_PRIVATEROOM, + ROOM_MODERATEDROOM, + ROOM_NOTINROOM, // 15 + ROOM_NOPRIVILEGES, + DATABASE, + CANNOTGETAVATARID, + CANNOTGETNODEID, + CANNOTGETPMSGID, // 20 + PMSGNOTFOUND, + ROOMMAXAVATARSREACHED, + IGNORING, + ROOM_ALREADYEXISTS, + NOTHINGTOCONFIRM, // 25 + DUPLICATEFRIEND, + IGNORENOTFOUND, + DUPLICATEIGNORE, + DBFAIL, + ROOM_DESTAVATARNOTMODERATOR, // 30 + ROOM_DESTAVATARNOTINVITED, + ROOM_DESTAVATARNOTBANNED, + ROOM_DUPLICATEBAN, + ROOM_DUPLICATEMODERATOR, + ROOM_DUPLICATEINVITE, // 35 + ROOM_ALREADYINROOM, + ROOM_PARENTNONPERSISTENT, + ROOM_PARENTBADNODETYPE, + NOFANCLUBHANDLE, + AIDALREADYEXISTS, // 40 + UIDALREADYEXISTS, + WRONGCHATSERVERFORREQUEST, + SUCCESSBADDATA, + NULLNAMELOGIN, + SERVER_IDENTITY_EMPTY, // 45 + SERVER_IDENTITY_TAKEN, + REMOTESERVERDOWN, + NODEIDCONFLICT, + INVALIDNODENAME, + INSUFFICIENTGMPRIVS, // 50 + SNOOPALREADYADDED, + NOTSNOOPING, + ROOM_DESTAVATARNOTTEMPORARYMODERATOR, + ROOM_DESTAVATARNOTVOICE, + ROOM_DUPLICATETEMPORARYMODERATOR, // 55 + ROOM_DUPLICATEVOICE, + AVATARMUSTBELOGGEDOUT, + NOTHINGTODO, + TRANSFERNAMENULL, + TRANSFERUSERIDZERO, // 60 + TRANSFERADDRESSNULL, + OUTOFIDS, + ROOM_LOCALROOM, + ROOM_GAMEROOM, + ROOM_DESTAVATARNOTENTERING, // 65 + INSUFFICIENTPRIORITY, + ROOM_WAITINGFORENTRY, + INBOXLIMITEXCEEDED, + DUPLICATEDESTINATION, + CATEGORYLIMITEXCEEDED, // 70 + MESSAGE_FILTER_FAILURE, + INVALID_INPUT, +}; + +struct ChatResultException { + ChatResultCode code; + std::string message; + + explicit ChatResultException(const ChatResultCode result) + : code{result} {} + + ChatResultException(const ChatResultCode result, char const * text) + : code{result} + , message{text} {} +}; + +const char* ToString(ChatResultCode code); \ No newline at end of file diff --git a/stationapi/src/stationchat/ChatRoom.cpp b/stationapi/src/stationchat/ChatRoom.cpp new file mode 100755 index 0000000..0fdca5d --- /dev/null +++ b/stationapi/src/stationchat/ChatRoom.cpp @@ -0,0 +1,307 @@ + +#include "ChatRoom.hpp" +#include "ChatAvatar.hpp" +#include "ChatRoomService.hpp" + +#include + +inline unsigned IS_SET(unsigned var, unsigned bit) { return (var & bit); } + +ChatRoom::ChatRoom(ChatRoomService* roomService, uint32_t roomId, const ChatAvatar* creator, + const std::u16string& roomName, const std::u16string& roomTopic, const std::u16string& roomPassword, + uint32_t roomAttributes, uint32_t maxRoomSize, const std::u16string& roomAddress, + const std::u16string& srcAddress) + : roomService_{roomService} + , roomId_{roomId} + , creatorName_{creator->GetName()} + , creatorAddress_{creator->GetAddress()} + , roomName_{roomName} + , roomTopic_{roomTopic} + , roomPassword_{roomPassword} + , roomAddress_{roomAddress + u"+" + roomName} + , creatorId_{creator->GetAvatarId()} + , roomAttributes_{roomAttributes} + , maxRoomSize_{maxRoomSize} { + administrators_.push_back(creator); + moderators_.push_back(creator); +} + +bool ChatRoom::IsPrivate() const { + return (roomAttributes_ & static_cast(RoomAttributes::PRIVATE)) != 0; +} + +bool ChatRoom::IsModerated() const { + return (roomAttributes_ & static_cast(RoomAttributes::MODERATED)) != 0; +} + +bool ChatRoom::IsPersistent() const { + return (roomAttributes_ & static_cast(RoomAttributes::PERSISTENT)) != 0; +} + +bool ChatRoom::IsLocalWorld() const { + return (roomAttributes_ & static_cast(RoomAttributes::LOCAL_WORLD)) != 0; +} + +bool ChatRoom::IsLocalGame() const { + return (roomAttributes_ & static_cast(RoomAttributes::LOCAL_GAME)) != 0; +} + +void ChatRoom::EnterRoom(ChatAvatar* avatar, const std::u16string& password) { + if (roomPassword_.length() > 0 && roomPassword_.compare(password) != 0) { + throw ChatResultException{ChatResultCode::ROOM_PRIVATEROOM}; + } + + if (IsBanned(avatar->GetAvatarId())) { + throw ChatResultException{ChatResultCode::ROOM_BANNEDAVATAR}; + } + + if (IsInRoom(avatar)) { + throw ChatResultException{ChatResultCode::ROOM_ALREADYINROOM}; + } + + if (IsPrivate() && !IsCreator(avatar->GetAvatarId()) && !IsInvited(avatar->GetAvatarId())) { + throw ChatResultException{ChatResultCode::ROOM_PRIVATEROOM}; + } + + avatars_.push_back(avatar); +} + +bool ChatRoom::IsInRoom(ChatAvatar* avatar) const { return IsInRoom(avatar->GetAvatarId()); } + +bool ChatRoom::IsInRoom(uint32_t avatarId) const { + return std::find_if(std::begin(avatars_), std::end(avatars_), + [avatarId](ChatAvatar* roomAvatar) { return roomAvatar->GetAvatarId() == avatarId; }) + != std::end(avatars_); +} + +void ChatRoom::LeaveRoom(ChatAvatar* avatar) { + auto avatarsIter = std::remove_if(std::begin(avatars_), std::end(avatars_), + [avatar](auto roomAvatar) { return roomAvatar->GetAvatarId() == avatar->GetAvatarId(); }); + + if (avatarsIter != std::end(avatars_)) { + avatars_.erase(avatarsIter); + } +} + +std::vector ChatRoom::GetAvatarIds(const ChatAvatar * srcAvatar) const { + std::vector avatarIds; + + for (auto roomAvatar : avatars_) { + if (!roomAvatar->IsIgnored(srcAvatar)) { + avatarIds.push_back(roomAvatar->GetAvatarId()); + } + } + + return avatarIds; +} + +std::vector ChatRoom::GetConnectedAddresses() const { + std::vector connectedAddresses; + + std::u16string address; + for (auto avatar : avatars_) { + address = avatar->GetAddress(); + if (connectedAddresses.empty() + || std::none_of(std::begin(connectedAddresses), std::end(connectedAddresses), + [&address](const auto& connectedAddress) { + return connectedAddress.compare(address) == 0; + })) { + connectedAddresses.push_back(address); + } + } + + return connectedAddresses; +} + +std::vector ChatRoom::GetRemoteAddresses() const { + std::vector connectedAddresses; + + std::u16string address; + for (auto avatar : avatars_) { + address = avatar->GetAddress(); + if (creatorAddress_.compare(address) != 0 && (connectedAddresses.empty() + || std::none_of(std::begin(connectedAddresses), std::end(connectedAddresses), + [&address](const auto& connectedAddress) { + return connectedAddress.compare(address) == 0; + }))) { + connectedAddresses.push_back(address); + } + } + + return connectedAddresses; +} + +bool ChatRoom::IsCreator(uint32_t avatarId) const { return avatarId == creatorId_; } + +bool ChatRoom::IsModerator(uint32_t avatarId) const { + if (moderators_.empty()) + return false; + + auto find_iter = std::find_if(std::begin(moderators_), std::end(moderators_), + [avatarId](const auto& moderator) { return moderator->GetAvatarId() == avatarId; }); + + return find_iter != std::end(moderators_); +} + +bool ChatRoom::IsAdministrator(uint32_t avatarId) const { + if (administrators_.empty()) + return false; + + auto find_iter = std::find_if(std::begin(administrators_), std::end(administrators_), + [avatarId](const auto& administrator) { return administrator->GetAvatarId() == avatarId; }); + + return find_iter != std::end(administrators_); +} + +bool ChatRoom::IsBanned(uint32_t avatarId) const { + if (banned_.empty()) + return false; + + auto find_iter = std::find_if(std::begin(banned_), std::end(banned_), + [avatarId](const auto& banned) { return banned->GetAvatarId() == avatarId; }); + + return find_iter != std::end(banned_); +} + +bool ChatRoom::IsInvited(uint32_t avatarId) const { + if (invited_.empty()) + return false; + + auto find_iter = std::find_if(std::begin(invited_), std::end(invited_), + [avatarId](const auto& invited) { return invited->GetAvatarId() == avatarId; }); + + return find_iter != std::end(invited_); +} + +void ChatRoom::KickAvatar(uint32_t srcAvatarId, ChatAvatar* destAvatar) { + if (!IsModerator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (!IsInRoom(destAvatar)) { + throw ChatResultException{ChatResultCode::ROOM_NOTINROOM}; + } + + LeaveRoom(destAvatar); +} + +void ChatRoom::AddAdministrator(uint32_t srcAvatarId, ChatAvatar* administrator) { + if (!IsCreator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (!IsAdministrator(administrator->GetAvatarId())) { + administrators_.push_back(administrator); + + if (IsPersistent()) { + roomService_->PersistBanned(administrator->GetAvatarId(), roomId_); + } + } +} + +void ChatRoom::AddModerator(uint32_t srcAvatarId, ChatAvatar* moderator) { + if (!IsCreator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (IsModerator(moderator->GetAvatarId())) { + throw ChatResultException{ChatResultCode::ROOM_DUPLICATEMODERATOR}; + } + + moderators_.push_back(moderator); + + if (IsPersistent()) { + roomService_->PersistBanned(moderator->GetAvatarId(), roomId_); + } +} + +void ChatRoom::AddBanned(uint32_t srcAvatarId, ChatAvatar* banned) { + if (!IsModerator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (IsBanned(banned->GetAvatarId())) { + throw ChatResultException{ChatResultCode::ROOM_DUPLICATEBAN}; + } + + banned_.push_back(banned); + + if (IsPersistent()) { + roomService_->PersistBanned(banned->GetAvatarId(), roomId_); + } +} + +void ChatRoom::AddInvite(uint32_t srcAvatarId, ChatAvatar* invited) { + if (!IsModerator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (IsInvited(invited->GetAvatarId())) { + throw ChatResultException{ChatResultCode::ROOM_DUPLICATEINVITE}; + } + + invited_.push_back(invited); +} + +void ChatRoom::RemoveAdministrator(uint32_t srcAvatarId, uint32_t avatarId) { + if (!IsCreator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (administrators_.empty()) + return; + + administrators_.erase(std::remove_if(std::begin(administrators_), std::end(administrators_), + [avatarId](auto administrator) { return administrator->GetAvatarId() == avatarId; })); + + if (IsPersistent()) { + roomService_->DeleteAdministrator(avatarId, roomId_); + } +} + +void ChatRoom::RemoveModerator(uint32_t srcAvatarId, uint32_t avatarId) { + if (!IsCreator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (moderators_.empty() || !IsModerator(avatarId)) { + throw ChatResultException{ChatResultCode::ROOM_DESTAVATARNOTMODERATOR}; + } + + moderators_.erase(std::remove_if(std::begin(moderators_), std::end(moderators_), + [avatarId](auto moderator) { return moderator->GetAvatarId() == avatarId; })); + + if (IsPersistent()) { + roomService_->DeleteModerator(avatarId, roomId_); + } +} + +void ChatRoom::RemoveBanned(uint32_t srcAvatarId, uint32_t avatarId) { + if (!IsModerator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (banned_.empty() || !IsBanned(avatarId)) { + throw ChatResultException{ChatResultCode::ROOM_DESTAVATARNOTBANNED}; + } + + banned_.erase(std::remove_if(std::begin(banned_), std::end(banned_), + [avatarId](auto banned) { return banned->GetAvatarId() == avatarId; })); + + if (IsPersistent()) { + roomService_->DeleteBanned(avatarId, roomId_); + } +} + +void ChatRoom::RemoveInvite(uint32_t srcAvatarId, uint32_t avatarId) { + if (!IsModerator(srcAvatarId)) { + throw ChatResultException{ChatResultCode::ROOM_NOPRIVILEGES}; + } + + if (invited_.empty() || !IsInvited(avatarId)) { + throw ChatResultException{ChatResultCode::ROOM_DESTAVATARNOTINVITED}; + } + + invited_.erase(std::remove_if(std::begin(invited_), std::end(invited_), + [avatarId](auto invited) { return invited->GetAvatarId() == avatarId; })); +} diff --git a/stationapi/src/stationchat/ChatRoom.hpp b/stationapi/src/stationchat/ChatRoom.hpp new file mode 100755 index 0000000..5558e30 --- /dev/null +++ b/stationapi/src/stationchat/ChatRoom.hpp @@ -0,0 +1,170 @@ + +#pragma once + +#include "ChatEnums.hpp" + +#include +#include + +class ChatAvatar; +class ChatRoomService; + +enum class RoomAttributes : uint32_t { + PRIVATE = 1 << 0, + MODERATED = 1 << 1, + PERSISTENT = 1 << 2, + LOCAL_WORLD = 1 << 4, + LOCAL_GAME = 1 << 5 +}; + +class ChatRoom { +public: + ChatRoom() = default; + ChatRoom(ChatRoomService* roomService, uint32_t roomId, const ChatAvatar* creator, + const std::u16string& roomName, const std::u16string& roomTopic, const std::u16string& roomPassword, + uint32_t roomAttributes, uint32_t maxRoomSize, const std::u16string& roomAddress, + const std::u16string& srcAddress); + + bool IsPrivate() const; + bool IsModerated() const; + bool IsPersistent() const; + bool IsLocalWorld() const; + bool IsLocalGame() const; + + void EnterRoom(ChatAvatar* avatar, const std::u16string& password); + bool IsInRoom(ChatAvatar* avatar) const; + bool IsInRoom(uint32_t avatarId) const; + void LeaveRoom(ChatAvatar* avatar); + + uint32_t GetCreatorId() const { return creatorId_; } + const std::u16string& GetCreatorName() const { return creatorName_; } + const std::u16string& GetCreatorAddress() const { return creatorAddress_; } + const std::u16string& GetRoomName() const { return roomName_; } + const std::u16string& GetRoomAddress() const { return roomAddress_; } + const std::u16string& GetRoomTopic() const { return roomTopic_; } + const std::u16string& GetRoomPassword() const { return roomPassword_; } + const std::u16string& GetRoomPrefix() const { return roomPrefix_; } + uint32_t GetRoomAttributes() const { return roomAttributes_; } + uint32_t GetCurrentRoomSize() const { return avatars_.size(); } + uint32_t GetMaxRoomSize() const { return maxRoomSize_; } + uint32_t GetRoomId() const { return roomId_; } + uint32_t GetCreateTime() const { return createTime_; } + uint32_t GetNodeLevel() const { return nodeLevel_; } + + const std::vector GetAvatars() const { return avatars_; } + /** Returns a list of id's in the room that are not ignoring the srcAvatar. + */ + std::vector GetAvatarIds(const ChatAvatar* srcAvatar) const; + const std::vector GetAdminstrators() const { return administrators_; } + const std::vector GetModerators() const { return moderators_; } + const std::vector GetTempModerators() const { return tempModerators_; } + const std::vector GetBanned() const { return banned_; } + const std::vector GetInvited() const { return invited_; } + const std::vector GetVoice() const { return voice_; } + + /* Returns the addresses of the different game servers currently with avatars + * connected to this room. + */ + std::vector GetConnectedAddresses() const; + std::vector GetRemoteAddresses() const; + + bool IsCreator(uint32_t avatarId) const; + bool IsModerator(uint32_t avatarId) const; + bool IsAdministrator(uint32_t avatarId) const; + bool IsBanned(uint32_t avatarId) const; + bool IsInvited(uint32_t avatarId) const; + + void KickAvatar(uint32_t srcAvatarId, ChatAvatar* destAvatar); + + void AddAdministrator(uint32_t srcAvatarId, ChatAvatar* administrator); + void AddModerator(uint32_t srcAvatarId, ChatAvatar* moderator); + void AddBanned(uint32_t srcAvatarId, ChatAvatar* banned); + void AddInvite(uint32_t srcAvatarId, ChatAvatar* invited); + + void RemoveAdministrator(uint32_t srcAvatarId, uint32_t avatarId); + void RemoveModerator(uint32_t srcAvatarId, uint32_t avatarId); + void RemoveBanned(uint32_t srcAvatarId, uint32_t avatarId); + void RemoveInvite(uint32_t srcAvatarId, uint32_t avatarId); + + uint32_t GetNextMessageId() { return roomMessageId_++; } + +private: + friend class ChatRoomService; + ChatRoomService* roomService_; + std::u16string creatorName_; + std::u16string creatorAddress_; + std::u16string roomName_; + std::u16string roomTopic_; + std::u16string roomPassword_; + std::u16string roomPrefix_ = u""; + std::u16string roomAddress_; + + uint32_t creatorId_; + uint32_t roomAttributes_; + uint32_t maxRoomSize_; + uint32_t roomId_ = 0; + uint32_t createTime_ = 0; + uint32_t nodeLevel_ = 0; + uint32_t roomMessageId_ = 1; + int32_t dbId_ = -1; + + std::vector avatars_; + std::vector administrators_; + std::vector moderators_; + std::vector tempModerators_; + std::vector banned_; + std::vector invited_; + std::vector voice_; +}; + +template +void write(StreamT& ar, const ChatRoom& data) { + write(ar, data.GetCreatorName()); + write(ar, data.GetCreatorAddress()); + write(ar, data.GetCreatorId()); + write(ar, data.GetRoomName()); + write(ar, data.GetRoomTopic()); + write(ar, data.GetRoomPrefix()); + write(ar, data.GetRoomAddress()); + write(ar, data.GetRoomPassword()); + write(ar, data.GetRoomAttributes()); + write(ar, data.GetMaxRoomSize()); + write(ar, data.GetRoomId()); + write(ar, data.GetCreateTime()); + write(ar, data.GetNodeLevel()); + + auto& avatars = data.GetAvatars(); + write(ar, static_cast(avatars.size())); + for (auto& avatar : avatars) + write(ar, avatar); + + auto& administrators = data.GetAdminstrators(); + write(ar, static_cast(administrators.size())); + for (auto& avatar : administrators) + write(ar, avatar); + + auto& moderators = data.GetModerators(); + write(ar, static_cast(moderators.size())); + for (auto& avatar : moderators) + write(ar, avatar); + + auto& tempModerators = data.GetTempModerators(); + write(ar, static_cast(tempModerators.size())); + for (auto& avatar : tempModerators) + write(ar, avatar); + + auto& banned = data.GetBanned(); + write(ar, static_cast(banned.size())); + for (auto& avatar : banned) + write(ar, avatar); + + auto& invited = data.GetInvited(); + write(ar, static_cast(invited.size())); + for (auto& avatar : invited) + write(ar, avatar); + + auto& voice = data.GetVoice(); + write(ar, static_cast(voice.size())); + for (auto& avatar : voice) + write(ar, avatar); +} diff --git a/stationapi/src/stationchat/ChatRoomService.cpp b/stationapi/src/stationchat/ChatRoomService.cpp new file mode 100755 index 0000000..ef43632 --- /dev/null +++ b/stationapi/src/stationchat/ChatRoomService.cpp @@ -0,0 +1,419 @@ +#include "ChatRoomService.hpp" +#include "ChatAvatarService.hpp" +#include "SQLite3.hpp" +#include "StreamUtils.hpp" +#include "StringUtils.hpp" + +#include "easylogging++.h" + +ChatRoomService::ChatRoomService(ChatAvatarService* avatarService, sqlite3* db) + : avatarService_{avatarService} + , db_{db} {} + +ChatRoomService::~ChatRoomService() {} + +void ChatRoomService::LoadRoomsFromStorage(const std::u16string& baseAddress) { + rooms_.clear(); + + sqlite3_stmt* stmt; + + char sql[] = "SELECT id, creator_id, creator_name, creator_address, room_name, room_topic, " + "room_password, room_prefix, room_address, room_attributes, room_max_size, " + "room_message_id, created_at, node_level FROM room WHERE room_address LIKE @baseAddress||'%'"; + + if (sqlite3_prepare_v2(db_, sql, -1, &stmt, 0) != SQLITE_OK) { + throw std::runtime_error("Error preparing SQL statement"); + } + + int baseAddressIdx = sqlite3_bind_parameter_index(stmt, "@baseAddress"); + + auto baseAddressStr = FromWideString(baseAddress); + LOG(INFO) << "Loading rooms for base address: " << baseAddressStr; + sqlite3_bind_text(stmt, baseAddressIdx, baseAddressStr.c_str(), -1, 0); + + while (sqlite3_step(stmt) == SQLITE_ROW) { + auto room = std::make_unique(); + std::string tmp; + room->roomId_ = nextRoomId_++; + room->dbId_ = sqlite3_column_int(stmt, 0); + room->creatorId_ = sqlite3_column_int(stmt, 1); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 2))); + room->creatorName_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 3))); + room->creatorAddress_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 4))); + room->roomName_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 5))); + room->roomTopic_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 6))); + room->roomPassword_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 7))); + room->roomPrefix_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 8))); + room->roomAddress_ = std::u16string{std::begin(tmp), std::end(tmp)}; + + room->roomAttributes_ = sqlite3_column_int(stmt, 9); + room->maxRoomSize_ = sqlite3_column_int(stmt, 10); + room->roomMessageId_ = sqlite3_column_int(stmt, 11); + room->createTime_ = sqlite3_column_int(stmt, 12); + room->nodeLevel_ = sqlite3_column_int(stmt, 13); + + if (!RoomExists(room->GetRoomAddress())) { + rooms_.emplace_back(std::move(room)); + } + } + + LOG(INFO) << "Rooms currently loaded: " << rooms_.size(); +} + +ChatRoom* ChatRoomService::CreateRoom(const ChatAvatar* creator, + const std::u16string& roomName, const std::u16string& roomTopic, const std::u16string& roomPassword, + uint32_t roomAttributes, uint32_t maxRoomSize, const std::u16string& roomAddress, + const std::u16string& srcAddress) { + ChatRoom* roomPtr = nullptr; + + if (RoomExists(roomAddress + u"+" + roomName)) { + throw ChatResultException(ChatResultCode::ROOM_ALREADYEXISTS, "ChatRoom already exists"); + } + + LOG(INFO) << "Creating room " << FromWideString(roomName) << "@" << FromWideString(roomAddress) << " with attributes " + << roomAttributes; + + rooms_.emplace_back(std::make_unique(this, nextRoomId_++, creator, roomName, + roomTopic, roomPassword, roomAttributes, maxRoomSize, roomAddress, srcAddress)); + roomPtr = rooms_.back().get(); + + if (roomPtr->IsPersistent()) { + PersistNewRoom(*roomPtr); + } + + return roomPtr; +} + +void ChatRoomService::DestroyRoom(ChatRoom* room) { + if (room->IsPersistent()) { + DeleteRoom(room); + } + + rooms_.erase(std::remove_if(std::begin(rooms_), std::end(rooms_), + [room](const auto& trackedRoom) { return trackedRoom->GetRoomId() == room->GetRoomId(); })); +} + +ChatResultCode ChatRoomService::PersistNewRoom(ChatRoom& room) { + ChatResultCode result = ChatResultCode::SUCCESS; + sqlite3_stmt* stmt; + + char sql[] = "INSERT INTO room (creator_id, creator_name, creator_address, room_name, " + "room_topic, room_password, room_prefix, room_address, room_attributes, " + "room_max_size, room_message_id, created_at, node_level) VALUES (@creator_id, " + "@creator_name, @creator_address, @room_name, @room_topic, @room_password, " + "@room_prefix, @room_address, @room_attributes, @room_max_size, @room_message_id, " + "@created_at, @node_level)"; + + if (sqlite3_prepare_v2(db_, sql, -1, &stmt, 0) != SQLITE_OK) { + result = ChatResultCode::DBFAIL; + } else { + int creatorIdIdx = sqlite3_bind_parameter_index(stmt, "@creator_id"); + int creatorNameIdx = sqlite3_bind_parameter_index(stmt, "@creator_name"); + int creatorAddressIdx = sqlite3_bind_parameter_index(stmt, "@creator_address"); + int roomNameIdx = sqlite3_bind_parameter_index(stmt, "@room_name"); + int roomTopicIdx = sqlite3_bind_parameter_index(stmt, "@room_topic"); + int roomPasswordIdx = sqlite3_bind_parameter_index(stmt, "@room_password"); + int roomPrefixIdx = sqlite3_bind_parameter_index(stmt, "@room_prefix"); + int roomAddressIdx = sqlite3_bind_parameter_index(stmt, "@room_address"); + int roomAttributesIdx = sqlite3_bind_parameter_index(stmt, "@room_attributes"); + int roomMaxSizeIdx = sqlite3_bind_parameter_index(stmt, "@room_max_size"); + int roomMessageIdIdx = sqlite3_bind_parameter_index(stmt, "@room_message_id"); + int createdAtIdx = sqlite3_bind_parameter_index(stmt, "@created_at"); + int nodeLevelIdx = sqlite3_bind_parameter_index(stmt, "@node_level"); + + sqlite3_bind_int(stmt, creatorIdIdx, room.creatorId_); + + auto creatorName = FromWideString(room.creatorName_); + sqlite3_bind_text(stmt, creatorNameIdx, creatorName.c_str(), -1, 0); + + auto creatorAddress = FromWideString(room.creatorAddress_); + sqlite3_bind_text(stmt, creatorAddressIdx, creatorAddress.c_str(), -1, 0); + + auto roomName = FromWideString(room.roomName_); + sqlite3_bind_text(stmt, roomNameIdx, roomName.c_str(), -1, 0); + + auto roomTopic = FromWideString(room.roomTopic_); + sqlite3_bind_text(stmt, roomTopicIdx, roomTopic.c_str(), -1, 0); + + auto roomPassword = FromWideString(room.roomPassword_); + sqlite3_bind_text(stmt, roomPasswordIdx, roomPassword.c_str(), -1, 0); + + auto roomPrefix = FromWideString(room.roomPrefix_); + sqlite3_bind_text(stmt, roomPrefixIdx, roomPrefix.c_str(), -1, 0); + + auto roomAddress = FromWideString(room.roomAddress_); + sqlite3_bind_text(stmt, roomAddressIdx, roomAddress.c_str(), -1, 0); + + sqlite3_bind_int(stmt, roomAttributesIdx, room.roomAttributes_); + sqlite3_bind_int(stmt, roomMaxSizeIdx, room.maxRoomSize_); + sqlite3_bind_int(stmt, roomMessageIdIdx, room.roomMessageId_); + sqlite3_bind_int(stmt, createdAtIdx, room.createTime_); + sqlite3_bind_int(stmt, nodeLevelIdx, room.nodeLevel_); + + if (sqlite3_step(stmt) != SQLITE_DONE) { + result = ChatResultCode::DBFAIL; + } else { + room.dbId_ = static_cast(sqlite3_last_insert_rowid(db_)); + } + } + + return result; +} + +std::vector ChatRoomService::GetRoomSummaries( + const std::u16string& startNode, const std::u16string& filter) { + std::vector rooms; + + for (auto& room : rooms_) { + auto& roomAddress = room->GetRoomAddress(); + if (roomAddress.compare(0, startNode.length(), startNode) == 0) { + if (!room->IsPrivate()) { + rooms.push_back(room.get()); + } + } + } + + return rooms; +} + +bool ChatRoomService::RoomExists(const std::u16string& roomAddress) const { + return std::find_if(std::begin(rooms_), std::end(rooms_), [roomAddress](auto& room) { + return roomAddress.compare(room->GetRoomAddress()) == 0; + }) != std::end(rooms_); +} + +ChatRoom* ChatRoomService::GetRoom(const std::u16string& roomAddress) { + ChatRoom* room = nullptr; + + auto find_iter = std::find_if(std::begin(rooms_), std::end(rooms_), + [roomAddress](auto& room) { return roomAddress.compare(room->GetRoomAddress()) == 0; }); + + if (find_iter != std::end(rooms_)) { + room = find_iter->get(); + } + + return room; +} + +std::vector ChatRoomService::GetJoinedRooms(const ChatAvatar * avatar) { + std::vector rooms; + + for (auto& room : rooms_) { + if (room->IsInRoom(avatar->GetAvatarId())) { + rooms.push_back(room.get()); + } + } + + return rooms; +} + +void ChatRoomService::DeleteRoom(ChatRoom* room) { + sqlite3_stmt* stmt; + char sql[] = "DELETE FROM room WHERE id = @id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int idIdx = sqlite3_bind_parameter_index(stmt, "@id"); + sqlite3_bind_int(stmt, idIdx, room->dbId_); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatRoomService::LoadModerators(ChatRoom * room) { + sqlite3_stmt* stmt; + char sql[] = "SELECT moderator_avatar_id FROM room_moderator WHERE room_id = @room_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + sqlite3_bind_int(stmt, roomIdIdx, room->GetRoomId()); + + while (sqlite3_step(stmt) == SQLITE_ROW) { + uint32_t moderatorId = sqlite3_column_int(stmt, 0); + room->moderators_.push_back(avatarService_->GetAvatar(moderatorId)); + } +} + +void ChatRoomService::PersistModerator(uint32_t moderatorId, uint32_t roomId) { + sqlite3_stmt* stmt; + char sql[] = "INSERT OR IGNORE INTO room_moderator (moderator_avatar_id, room_id) VALUES (@moderator_avatar_id, @room_id)"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int moderatorAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@moderator_avatar_id"); + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + + sqlite3_bind_int(stmt, moderatorAvatarIdIdx, moderatorId); + sqlite3_bind_int(stmt, roomIdIdx, roomId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatRoomService::DeleteModerator(uint32_t moderatorId, uint32_t roomId) { + sqlite3_stmt* stmt; + char sql[] = "DELETE FROM room_moderator WHERE moderator_avatar_id = @moderator_avatar_id AND room_id = @room_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int moderatorAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@moderator_avatar_id"); + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + + sqlite3_bind_int(stmt, moderatorAvatarIdIdx, moderatorId); + sqlite3_bind_int(stmt, roomIdIdx, roomId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatRoomService::LoadAdministrators(ChatRoom * room) { + sqlite3_stmt* stmt; + char sql[] = "SELECT administrator_avatar_id FROM room_administrator WHERE room_id = @room_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + sqlite3_bind_int(stmt, roomIdIdx, room->GetRoomId()); + + while (sqlite3_step(stmt) == SQLITE_ROW) { + uint32_t administratorId = sqlite3_column_int(stmt, 0); + room->administrators_.push_back(avatarService_->GetAvatar(administratorId)); + } +} + +void ChatRoomService::PersistAdministrator(uint32_t administratorId, uint32_t roomId) { + sqlite3_stmt* stmt; + char sql[] = "INSERT OR IGNORE INTO room_administrator (administrator_avatar_id, room_id) VALUES (@administrator_avatar_id, @room_id)"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int administratorAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@administrator_avatar_id"); + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + + sqlite3_bind_int(stmt, administratorAvatarIdIdx, administratorId); + sqlite3_bind_int(stmt, roomIdIdx, roomId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatRoomService::DeleteAdministrator(uint32_t administratorId, uint32_t roomId) { + sqlite3_stmt* stmt; + char sql[] = "DELETE FROM room_administrator WHERE administrator_avatar_id = @administrator_avatar_id AND room_id = @room_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int administratorAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@administrator_avatar_id"); + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + + sqlite3_bind_int(stmt, administratorAvatarIdIdx, administratorId); + sqlite3_bind_int(stmt, roomIdIdx, roomId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatRoomService::LoadBanned(ChatRoom * room) { + sqlite3_stmt* stmt; + char sql[] = "SELECT banned_avatar_id FROM room_ban WHERE room_id = @room_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + sqlite3_bind_int(stmt, roomIdIdx, room->GetRoomId()); + + while (sqlite3_step(stmt) == SQLITE_ROW) { + uint32_t bannedId = sqlite3_column_int(stmt, 0); + room->banned_.push_back(avatarService_->GetAvatar(bannedId)); + } +} + +void ChatRoomService::PersistBanned(uint32_t bannedId, uint32_t roomId) { + sqlite3_stmt* stmt; + char sql[] = "INSERT OR IGNORE INTO room_ban (banned_avatar_id, room_id) VALUES (@banned_avatar_id, @room_id)"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int bannedAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@moderator_avatar_id"); + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + + sqlite3_bind_int(stmt, bannedAvatarIdIdx, bannedId); + sqlite3_bind_int(stmt, roomIdIdx, roomId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} + +void ChatRoomService::DeleteBanned(uint32_t bannedId, uint32_t roomId) { + sqlite3_stmt* stmt; + char sql[] = "DELETE FROM room_ban WHERE banned_avatar_id = @banned_avatar_id AND room_id = @room_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int bannedAvatarIdIdx = sqlite3_bind_parameter_index(stmt, "@banned_avatar_id"); + int roomIdIdx = sqlite3_bind_parameter_index(stmt, "@room_id"); + + sqlite3_bind_int(stmt, bannedAvatarIdIdx, bannedId); + sqlite3_bind_int(stmt, roomIdIdx, roomId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } +} diff --git a/stationapi/src/stationchat/ChatRoomService.hpp b/stationapi/src/stationchat/ChatRoomService.hpp new file mode 100755 index 0000000..e5afa46 --- /dev/null +++ b/stationapi/src/stationchat/ChatRoomService.hpp @@ -0,0 +1,59 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "ChatRoom.hpp" + +#include + +#include +#include +#include +#include + +struct sqlite3; + +class ChatAvatarService; + +class ChatRoomService { +public: + ChatRoomService(ChatAvatarService* avatarService, sqlite3* db); + ~ChatRoomService(); + + void LoadRoomsFromStorage(const std::u16string& baseAddress); + + ChatRoom* CreateRoom(const ChatAvatar* creator, + const std::u16string& roomName, const std::u16string& roomTopic, + const std::u16string& roomPassword, uint32_t roomAttributes, uint32_t maxRoomSize, + const std::u16string& roomAddress, const std::u16string& srcAddress); + + void DestroyRoom(ChatRoom* room); + + ChatResultCode PersistNewRoom(ChatRoom& avatar); + + std::vector GetRoomSummaries( + const std::u16string& startNode, const std::u16string& filter = u""); + + bool RoomExists(const std::u16string& roomAddress) const; + ChatRoom* GetRoom(const std::u16string& roomAddress); + + std::vector GetJoinedRooms(const ChatAvatar* avatar); + +private: + friend class ChatRoom; + void DeleteRoom(ChatRoom* room); + void LoadModerators(ChatRoom* room); + void PersistModerator(uint32_t moderatorId, uint32_t roomId); + void DeleteModerator(uint32_t moderatorId, uint32_t roomId); + void LoadAdministrators(ChatRoom* room); + void PersistAdministrator(uint32_t administratorId, uint32_t roomId); + void DeleteAdministrator(uint32_t administratorId, uint32_t roomId); + void LoadBanned(ChatRoom* room); + void PersistBanned(uint32_t bannedId, uint32_t roomId); + void DeleteBanned(uint32_t bannedId, uint32_t roomId); + + uint32_t nextRoomId_ = 0; + std::vector> rooms_; + ChatAvatarService* avatarService_; + sqlite3* db_; +}; diff --git a/stationapi/src/stationchat/Contact.hpp b/stationapi/src/stationchat/Contact.hpp new file mode 100755 index 0000000..18c2789 --- /dev/null +++ b/stationapi/src/stationchat/Contact.hpp @@ -0,0 +1,38 @@ + +#pragma once + +#include "Serialization.hpp" + +#include + +class ChatAvatar; + +struct FriendContact { + FriendContact(const ChatAvatar* frnd_, const std::wstring& comment_) + : frnd{frnd_} + , comment{comment_} {} + + const ChatAvatar* frnd; + std::wstring comment = L""; +}; + +template +void write(StreamT& ar, const FriendContact& data) { + write(ar, data.frnd->GetName()); + write(ar, data.frnd->GetAddress()); + write(ar, data.comment); + write(ar, static_cast(data.frnd->IsOnline() ? 1 : 0)); +} + +struct IgnoreContact { + IgnoreContact(const ChatAvatar* ignored_) + : ignored{ignored_} {} + + const ChatAvatar* ignored; +}; + +template +void write(StreamT& ar, const IgnoreContact& data) { + write(ar, data.ignored->GetName()); + write(ar, data.ignored->GetAddress()); +} diff --git a/stationapi/src/stationchat/GatewayClient.cpp b/stationapi/src/stationchat/GatewayClient.cpp new file mode 100755 index 0000000..8a6d521 --- /dev/null +++ b/stationapi/src/stationchat/GatewayClient.cpp @@ -0,0 +1,245 @@ + +#include "GatewayClient.hpp" + +#include "ChatAvatarService.hpp" +#include "ChatEnums.hpp" +#include "ChatRoomService.hpp" +#include "GatewayNode.hpp" +#include "Message.hpp" +#include "PersistentMessageService.hpp" +#include "SQLite3.hpp" +#include "StationChatConfig.hpp" +#include "UdpLibrary.hpp" + +#include "protocol/AddBan.hpp" +#include "protocol/AddFriend.hpp" +#include "protocol/AddIgnore.hpp" +#include "protocol/AddInvite.hpp" +#include "protocol/AddModerator.hpp" +#include "protocol/CreateRoom.hpp" +#include "protocol/DestroyRoom.hpp" +#include "protocol/EnterRoom.hpp" +#include "protocol/FailoverReLoginAvatar.hpp" +#include "protocol/FriendStatus.hpp" +#include "protocol/GetAnyAvatar.hpp" +#include "protocol/GetPersistentHeaders.hpp" +#include "protocol/GetPersistentMessage.hpp" +#include "protocol/GetRoom.hpp" +#include "protocol/GetRoomSummaries.hpp" +#include "protocol/IgnoreStatus.hpp" +#include "protocol/KickAvatar.hpp" +#include "protocol/LeaveRoom.hpp" +#include "protocol/LoginAvatar.hpp" +#include "protocol/LogoutAvatar.hpp" +#include "protocol/RemoveBan.hpp" +#include "protocol/RemoveFriend.hpp" +#include "protocol/RemoveIgnore.hpp" +#include "protocol/RemoveInvite.hpp" +#include "protocol/RemoveModerator.hpp" +#include "protocol/SendInstantMessage.hpp" +#include "protocol/SendPersistentMessage.hpp" +#include "protocol/SendRoomMessage.hpp" +#include "protocol/SetApiVersion.hpp" +#include "protocol/SetAvatarAttributes.hpp" +#include "protocol/UpdatePersistentMessage.hpp" + +#include "easylogging++.h" + +GatewayClient::GatewayClient(UdpConnection* connection, GatewayNode* node) + : NodeClient(connection) + , node_{node} + , avatarService_{node->GetAvatarService()} + , roomService_{node->GetRoomService()} + , messageService_{node->GetMessageService()} { + connection->SetHandler(this); +} + +GatewayClient::~GatewayClient() {} + +void GatewayClient::OnIncoming(std::istringstream& istream) { + ChatRequestType request_type = ::read(istream); + + switch (request_type) { + case ChatRequestType::LOGINAVATAR: + HandleIncomingMessage(istream); + break; + case ChatRequestType::LOGOUTAVATAR: + HandleIncomingMessage(istream); + break; + case ChatRequestType::CREATEROOM: + HandleIncomingMessage(istream); + break; + case ChatRequestType::DESTROYROOM: + HandleIncomingMessage(istream); + break; + case ChatRequestType::SENDINSTANTMESSAGE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::SENDROOMMESSAGE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::ADDFRIEND: + HandleIncomingMessage(istream); + break; + case ChatRequestType::REMOVEFRIEND: + HandleIncomingMessage(istream); + break; + case ChatRequestType::FRIENDSTATUS: + HandleIncomingMessage(istream); + break; + case ChatRequestType::ADDIGNORE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::REMOVEIGNORE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::ENTERROOM: + HandleIncomingMessage(istream); + break; + case ChatRequestType::LEAVEROOM: + HandleIncomingMessage(istream); + break; + case ChatRequestType::ADDMODERATOR: + HandleIncomingMessage(istream); + break; + case ChatRequestType::REMOVEMODERATOR: + HandleIncomingMessage(istream); + break; + case ChatRequestType::ADDBAN: + HandleIncomingMessage(istream); + break; + case ChatRequestType::REMOVEBAN: + HandleIncomingMessage(istream); + break; + case ChatRequestType::ADDINVITE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::REMOVEINVITE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::KICKAVATAR: + HandleIncomingMessage(istream); + break; + case ChatRequestType::GETROOM: + HandleIncomingMessage(istream); + break; + case ChatRequestType::GETROOMSUMMARIES: + HandleIncomingMessage(istream); + break; + case ChatRequestType::SENDPERSISTENTMESSAGE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::GETPERSISTENTHEADERS: + HandleIncomingMessage(istream); + break; + case ChatRequestType::GETPERSISTENTMESSAGE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::UPDATEPERSISTENTMESSAGE: + HandleIncomingMessage(istream); + break; + case ChatRequestType::IGNORESTATUS: + HandleIncomingMessage(istream); + break; + case ChatRequestType::FAILOVER_RELOGINAVATAR: + HandleIncomingMessage(istream); + break; + case ChatRequestType::SETAPIVERSION: + HandleIncomingMessage(istream); + break; + case ChatRequestType::SETAVATARATTRIBUTES: + HandleIncomingMessage(istream); + break; + case ChatRequestType::GETANYAVATAR: + HandleIncomingMessage(istream); + break; + default: + LOG(INFO) << "Unknown request type received: " << static_cast(request_type); + break; + } +} + +void GatewayClient::SendFriendLoginUpdate( + const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar) { + node_->SendTo( + srcAvatar->GetAddress(), MFriendLogin{destAvatar, destAvatar->GetAddress(), + srcAvatar->GetAvatarId(), destAvatar->GetStatusMessage()}); +} + +void GatewayClient::SendFriendLoginUpdates(const ChatAvatar* avatar) { + auto as = node_->GetAvatarService(); + auto& onlineAvatars = as->GetOnlineAvatars(); + for (auto onlineAvatar : onlineAvatars) { + if (onlineAvatar->IsFriend(avatar)) { + SendFriendLoginUpdate(onlineAvatar, avatar); + } + } + + for (auto& contact : avatar->GetFriendList()) { + if (contact.frnd->IsOnline()) { + Send(MFriendLogin{contact.frnd, contact.frnd->GetAddress(), avatar->GetAvatarId(), + contact.frnd->GetStatusMessage()}); + } + } +} + +void GatewayClient::SendFriendLogoutUpdates(const ChatAvatar* avatar) { + auto& onlineAvatars = avatarService_->GetOnlineAvatars(); + for (auto onlineAvatar : onlineAvatars) { + if (onlineAvatar->IsFriend(avatar)) { + node_->SendTo(onlineAvatar->GetAddress(), + MFriendLogout{avatar, avatar->GetAddress(), onlineAvatar->GetAvatarId()}); + } + } +} + +void GatewayClient::SendDestroyRoomUpdate( + const ChatAvatar* srcAvatar, uint32_t roomId, std::vector targets) { + for (auto& address : targets) { + node_->SendTo(address, MDestroyRoom{srcAvatar, roomId}); + } +} + +void GatewayClient::SendInstantMessageUpdate(const ChatAvatar* srcAvatar, + const ChatAvatar* destAvatar, const std::u16string& message, const std::u16string& oob) { + node_->SendTo(destAvatar->GetAddress(), + MInstantMessage{srcAvatar, destAvatar->GetAvatarId(), message, oob}); +} + +void GatewayClient::SendRoomMessageUpdate(const ChatAvatar* srcAvatar, const ChatRoom* room, + uint32_t messageId, const std::u16string& message, const std::u16string& oob) { + auto connectedAddresses = room->GetConnectedAddresses(); + for (auto& address : connectedAddresses) { + node_->SendTo(address, MRoomMessage{srcAvatar, room->GetRoomId(), room->GetAvatarIds(srcAvatar), + message, oob, messageId}); + } +} + +void GatewayClient::SendEnterRoomUpdate(const ChatAvatar* srcAvatar, const ChatRoom* room) { + for (const auto& address : room->GetConnectedAddresses()) { + node_->SendTo(address, MEnterRoom{srcAvatar, room->GetRoomId()}); + } +} + +void GatewayClient::SendLeaveRoomUpdate( + const std::vector& addresses, uint32_t srcAvatarId, uint32_t roomId) { + for (const auto& address : addresses) { + node_->SendTo(address, MLeaveRoom{srcAvatarId, roomId}); + } +} + +void GatewayClient::SendPersistentMessageUpdate( + const ChatAvatar* destAvatar, const PersistentHeader& header) { + if (destAvatar) { + node_->SendTo( + destAvatar->GetAddress(), MPersistentMessage{destAvatar->GetAvatarId(), header}); + } +} + +void GatewayClient::SendKickAvatarUpdate(const std::vector& addresses, + const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar, const ChatRoom* room) { + for (const auto& address : addresses) { + node_->SendTo(address, + MKickAvatar{srcAvatar, destAvatar, room->GetRoomName(), room->GetRoomAddress()}); + } +} diff --git a/stationapi/src/stationchat/GatewayClient.hpp b/stationapi/src/stationchat/GatewayClient.hpp new file mode 100755 index 0000000..fb50ffb --- /dev/null +++ b/stationapi/src/stationchat/GatewayClient.hpp @@ -0,0 +1,69 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "NodeClient.hpp" +#include "SQLite3.hpp" +#include "easylogging++.h" + +class ChatAvatar; +class ChatAvatarService; +class ChatRoom; +class ChatRoomService; +class GatewayNode; +class PersistentMessageService; +class UdpConnection; + +struct PersistentHeader; + +struct ReqSetAvatarAttributes; +struct ReqGetAnyAvatar; + +class GatewayClient : public NodeClient { +public: + GatewayClient(UdpConnection* connection, GatewayNode* node); + virtual ~GatewayClient(); + + GatewayNode* GetNode() { return node_; } + + void SendFriendLoginUpdate(const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar); + void SendFriendLoginUpdates(const ChatAvatar* avatar); + void SendFriendLogoutUpdates(const ChatAvatar* avatar); + void SendDestroyRoomUpdate(const ChatAvatar* srcAvatar, uint32_t roomId, std::vector targets); + void SendInstantMessageUpdate(const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar, const std::u16string& message, const std::u16string& oob); + void SendRoomMessageUpdate(const ChatAvatar* srcAvatar, const ChatRoom* room, uint32_t messageId, const std::u16string& message, const std::u16string& oob); + void SendEnterRoomUpdate(const ChatAvatar* srcAvatar, const ChatRoom* room); + void SendLeaveRoomUpdate(const std::vector& addresses, uint32_t srcAvatarId, uint32_t roomId); + void SendPersistentMessageUpdate(const ChatAvatar* destAvatar, const PersistentHeader& header); + void SendKickAvatarUpdate(const std::vector& addresses, const ChatAvatar* srcAvatar, const ChatAvatar* destAvatar, const ChatRoom* room); + +private: + void OnIncoming(std::istringstream& istream) override; + + template + void HandleIncomingMessage(StreamT& istream) { + typedef typename HandlerT::RequestType RequestT; + typedef typename HandlerT::ResponseType ResponseT; + + RequestT request; + read(istream, request); + ResponseT response(request.track); + + try { + HandlerT(this, request, response); + } catch (const ChatResultException& e) { + response.result = e.code; + LOG(ERROR) << "ChatAPI Result Exception: [" << ToString(e.code) << "] " << e.message; + } catch (const SQLite3Exception& e) { + response.result = ChatResultCode::DATABASE; + LOG(ERROR) << "Database Error: [" << e.code << "] " << e.message; + } + + Send(response); + } + + GatewayNode* node_; + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; + PersistentMessageService* messageService_; +}; diff --git a/stationapi/src/stationchat/GatewayNode.cpp b/stationapi/src/stationchat/GatewayNode.cpp new file mode 100755 index 0000000..8e06f1d --- /dev/null +++ b/stationapi/src/stationchat/GatewayNode.cpp @@ -0,0 +1,39 @@ + +#include "GatewayNode.hpp" + +#include "ChatAvatarService.hpp" +#include "ChatRoomService.hpp" +#include "PersistentMessageService.hpp" +#include "StationChatConfig.hpp" + +#include + +GatewayNode::GatewayNode(StationChatConfig& config) + : Node(this, config.gatewayAddress, config.gatewayPort, config.bindToIp) + , config_{config} { + if (sqlite3_open(config.chatDatabasePath.c_str(), &db_) != SQLITE_OK) { + throw std::runtime_error("Can't open database: " + std::string{sqlite3_errmsg(db_)}); + } + + avatarService_ = std::make_unique(db_); + roomService_ = std::make_unique(avatarService_.get(), db_); + messageService_ = std::make_unique(db_); +} + +GatewayNode::~GatewayNode() { sqlite3_close(db_); } + +ChatAvatarService* GatewayNode::GetAvatarService() { return avatarService_.get(); } + +ChatRoomService* GatewayNode::GetRoomService() { return roomService_.get(); } + +PersistentMessageService* GatewayNode::GetMessageService() { + return messageService_.get(); +} + +StationChatConfig& GatewayNode::GetConfig() { return config_; } + +void GatewayNode::RegisterClientAddress(const std::u16string & address, GatewayClient * client) { + clientAddressMap_[address] = client; +} + +void GatewayNode::OnTick() {} diff --git a/stationapi/src/stationchat/GatewayNode.hpp b/stationapi/src/stationchat/GatewayNode.hpp new file mode 100755 index 0000000..c53f32b --- /dev/null +++ b/stationapi/src/stationchat/GatewayNode.hpp @@ -0,0 +1,45 @@ + +#pragma once + +#include "Node.hpp" +#include "GatewayClient.hpp" + +#include +#include + +class ChatAvatarService; +class ChatRoomService; +class PersistentMessageService; +struct StationChatConfig; +struct sqlite3; + +class GatewayNode : public Node { +public: + explicit GatewayNode(StationChatConfig& config); + ~GatewayNode(); + + ChatAvatarService* GetAvatarService(); + ChatRoomService* GetRoomService(); + PersistentMessageService* GetMessageService(); + StationChatConfig& GetConfig(); + + void RegisterClientAddress(const std::u16string& address, GatewayClient* client); + + template + void SendTo(const std::u16string& address, const MessageT& message) { + auto find_iter = clientAddressMap_.find(address); + if (find_iter != std::end(clientAddressMap_)) { + find_iter->second->Send(message); + } + } + +private: + void OnTick() override; + + std::unique_ptr avatarService_; + std::unique_ptr roomService_; + std::unique_ptr messageService_; + std::map clientAddressMap_; + StationChatConfig& config_; + sqlite3* db_; +}; diff --git a/stationapi/src/stationchat/Message.hpp b/stationapi/src/stationchat/Message.hpp new file mode 100755 index 0000000..052647d --- /dev/null +++ b/stationapi/src/stationchat/Message.hpp @@ -0,0 +1,298 @@ + +#pragma once + +#include "ChatAvatar.hpp" +#include "PersistentMessage.hpp" + +#include +#include + +enum class ChatMessageType : uint16_t { + // ChatAvatar message types + INSTANTMESSAGE = 0, // 0 + ROOMMESSAGE, + BROADCASTMESSAGE, + FRIENDLOGIN, + FRIENDLOGOUT, + KICKROOM, // 5 + + // ChatRoom message types + ADDMODERATORROOM, + REMOVEMODERATORROOM, + REMOVEMODERATORAVATAR, + ADDBANROOM, + REMOVEBANROOM, // 10 + REMOVEBANAVATAR, + ADDINVITEROOM, + ADDINVITEAVATAR, + REMOVEINVITEROOM, + REMOVEINVITEAVATAR, // 15 + ENTERROOM, + LEAVEROOM, + DESTROYROOM, + SETROOMPARAMS, + PERSISTENTMESSAGE, // 20 + FORCEDLOGOUT, + UNREGISTERROOMREADY, + KICKAVATAR, + ADDMODERATORAVATAR, + ADDBANAVATAR, // 25 + ADDADMIN, + REMOVEADMIN, + FRIENDCONFIRMREQUEST, + FRIENDCONFIRMRESPONSE, + CHANGEROOMOWNER, // 30 + FORCEROOMFAILOVER, + ADDTEMPORARYMODERATORROOM, + ADDTEMPORARYMODERATORAVATAR, + REMOVETEMPORARYMODERATORROOM, + REMOVETEMPORARYMODERATORAVATAR, // 35 + GRANTVOICEROOM, + GRANTVOICEAVATAR, + REVOKEVOICEROOM, + REVOKEVOICEAVATAR, + SNOOP, // 40 + UIDLIST, + REQUESTROOMENTRY, + DELAYEDROOMENTRY, + DENIEDROOMENTRY, + FRIENDSTATUS, // 45 + FRIENDCONFIRMRECIPROCATE_REQUEST, + FRIENDCONFIRMRECIPROCATE_RESPONSE, + FILTERMESSAGE, + FAILOVER_AVATAR_LIST, + NOTIFY_FRIENDS_LIST_CHANGE, // 50 + NOTIFY_FRIEND_IS_REMOVED +}; + +/** Begin INSTANTMESSAGE */ + +struct MInstantMessage { + MInstantMessage(const ChatAvatar* srcAvatar_, uint32_t destAvatarId_, + const std::u16string& message_, const std::u16string& oob_) + : srcAvatar{srcAvatar_} + , destAvatarId{destAvatarId_} + , message{message_} + , oob{oob_} {} + + const ChatMessageType type = ChatMessageType::INSTANTMESSAGE; + const uint32_t track = 0; + const ChatAvatar* srcAvatar; + uint32_t destAvatarId; + std::u16string message; + std::u16string oob; +}; + +template +void write(StreamT& ar, const MInstantMessage& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.srcAvatar); + write(ar, data.destAvatarId); + write(ar, data.message); + write(ar, data.oob); +} + +/** Begin ROOMMESSAGE */ + +struct MRoomMessage { + MRoomMessage(const ChatAvatar* srcAvatar_, uint32_t roomId_, std::vector destList_, + const std::u16string& message_, const std::u16string& oob_, uint32_t messageId_) + : srcAvatar{srcAvatar_} + , roomId{roomId_} + , destList{destList_} + , message{message_} + , oob{oob_} + , messageId{messageId_} {} + + const ChatMessageType type = ChatMessageType::ROOMMESSAGE; + const uint32_t track = 0; + const ChatAvatar* srcAvatar; + uint32_t roomId; + std::vector destList; // list of destination avatars to see the message + std::u16string message; + std::u16string oob; + uint32_t messageId = 0; +}; + +template +void write(StreamT& ar, const MRoomMessage& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.srcAvatar); + write(ar, data.roomId); + + write(ar, static_cast(data.destList.size())); + for (auto destAvatarId : data.destList) { + write(ar, destAvatarId); + } + + write(ar, data.message); + write(ar, data.oob); + write(ar, data.messageId); +} + +/** Begin FRIENDLOGIN */ + +struct MFriendLogin { + MFriendLogin(const ChatAvatar* avatar_, const std::u16string& friendAddress_, + uint32_t destAvatarId_, const std::u16string& friendStatus_) + : avatar{avatar_} + , friendAddress{friendAddress_} + , destAvatarId{destAvatarId_} + , friendStatus{friendStatus_} {} + + const ChatMessageType type = ChatMessageType::FRIENDLOGIN; + const uint32_t track = 0; + const ChatAvatar* avatar; + std::u16string friendAddress; + uint32_t destAvatarId; + std::u16string friendStatus; +}; + +template +void write(StreamT& ar, const MFriendLogin& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.avatar); + write(ar, data.friendAddress); + write(ar, data.destAvatarId); + write(ar, data.friendStatus); +} + +/** Begin FRIENDLOGOUT */ + +struct MFriendLogout { + MFriendLogout( + const ChatAvatar* avatar_, const std::u16string& friendAddress_, uint32_t destAvatarId_) + : avatar{avatar_} + , friendAddress{friendAddress_} + , destAvatarId{destAvatarId_} {} + + const ChatMessageType type = ChatMessageType::FRIENDLOGOUT; + const uint32_t track = 0; + const ChatAvatar* avatar; + uint32_t destAvatarId; + std::u16string friendAddress; +}; + +template +void write(StreamT& ar, const MFriendLogout& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.avatar); + write(ar, data.friendAddress); + write(ar, data.destAvatarId); +} + +/** Begin ENTERROOM */ + +struct MEnterRoom { + MEnterRoom(const ChatAvatar* srcAvatar_, uint32_t roomId_) + : srcAvatar{srcAvatar_} + , roomId{roomId_} {} + + const ChatMessageType type = ChatMessageType::ENTERROOM; + const uint32_t track = 0; + const ChatAvatar* srcAvatar; + uint32_t roomId; +}; + +template +void write(StreamT& ar, const MEnterRoom& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.srcAvatar); + write(ar, data.roomId); +} + +/** Begin LEAVEROOM */ + +struct MLeaveRoom { + MLeaveRoom(uint32_t avatarId_, uint32_t roomId_) + : avatarId{avatarId_} + , roomId{roomId_} {} + + const ChatMessageType type = ChatMessageType::LEAVEROOM; + const uint32_t track = 0; + uint32_t avatarId; + uint32_t roomId; +}; + +template +void write(StreamT& ar, const MLeaveRoom& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.avatarId); + write(ar, data.roomId); +} + +/** Begin DESTROYROOM */ + +struct MDestroyRoom { + MDestroyRoom(const ChatAvatar* srcAvatar_, uint32_t roomId_) + : srcAvatar{srcAvatar_} + , roomId{roomId_} {} + + const ChatMessageType type = ChatMessageType::DESTROYROOM; + const uint32_t track = 0; + const ChatAvatar* srcAvatar; + uint32_t roomId; +}; + +template +void write(StreamT& ar, const MDestroyRoom& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.srcAvatar); + write(ar, data.roomId); +} + +/** Begin PERSISTENTMESSAGE */ + +struct MPersistentMessage { + MPersistentMessage(uint32_t destAvatarId_, PersistentHeader header_) + : destAvatarId{destAvatarId_} + , header{header_} {} + + const ChatMessageType type = ChatMessageType::PERSISTENTMESSAGE; + const uint32_t track = 0; + uint32_t destAvatarId; + PersistentHeader header; +}; + +template +void write(StreamT& ar, const MPersistentMessage& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.destAvatarId); + write(ar, data.header); +} + +/** Begin KICKAVATAR */ + +struct MKickAvatar { + MKickAvatar(const ChatAvatar* srcAvatar_, const ChatAvatar* destAvatar_, + const std::u16string& roomName_, const std::u16string& roomAddress_) + : srcAvatar{srcAvatar_} + , destAvatar{destAvatar_} + , roomName{roomName_} + , roomAddress{roomAddress_} {} + + const ChatMessageType type = ChatMessageType::KICKAVATAR; + const uint32_t track = 0; + const ChatAvatar* srcAvatar; + const ChatAvatar* destAvatar; + std::u16string roomName; + std::u16string roomAddress; +}; + +template +void write(StreamT& ar, const MKickAvatar& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.srcAvatar); + write(ar, data.destAvatar); + write(ar, data.roomName); + write(ar, data.roomAddress); +} diff --git a/stationapi/src/stationchat/PersistentMessage.hpp b/stationapi/src/stationchat/PersistentMessage.hpp new file mode 100755 index 0000000..2e5d0f2 --- /dev/null +++ b/stationapi/src/stationchat/PersistentMessage.hpp @@ -0,0 +1,51 @@ + +#pragma once + +#include +#include + +enum class PersistentState : uint32_t { + NEW = 1, + UNREAD, + READ, + TRASH, + DELETED +}; + +struct PersistentHeader { + uint32_t messageId = 0; + uint32_t avatarId; + std::u16string fromName; + std::u16string fromAddress; + std::u16string subject; + uint32_t sentTime = 0; + PersistentState status = PersistentState::NEW; + std::u16string folder = u""; + std::u16string category = u""; +}; + +struct PersistentMessage { + PersistentHeader header; + std::u16string message; + std::u16string oob; +}; + + +template +void write(StreamT& ar, const PersistentHeader& data) { + write(ar, data.messageId); + write(ar, data.avatarId); + write(ar, data.fromName); + write(ar, data.fromAddress); + write(ar, data.subject); + write(ar, data.sentTime); + write(ar, data.status); +} + + +template +void write(StreamT& ar, const PersistentMessage& data) { + write(ar, data.header); + write(ar, data.message); + write(ar, data.oob); +} diff --git a/stationapi/src/stationchat/PersistentMessageService.cpp b/stationapi/src/stationchat/PersistentMessageService.cpp new file mode 100755 index 0000000..3f22d3d --- /dev/null +++ b/stationapi/src/stationchat/PersistentMessageService.cpp @@ -0,0 +1,210 @@ +#include "PersistentMessageService.hpp" + +#include "SQLite3.hpp" +#include "StringUtils.hpp" + + +PersistentMessageService::PersistentMessageService(sqlite3* db) + : db_{db} {} + +PersistentMessageService::~PersistentMessageService() {} + +void PersistentMessageService::StoreMessage(PersistentMessage& message) { + sqlite3_stmt* stmt; + + char sql[] = "INSERT INTO persistent_message (avatar_id, from_name, from_address, subject, " + "sent_time, status, " + "folder, category, message, oob) VALUES (@avatar_id, @from_name, @from_address, " + "@subject, @sent_time, @status, @folder, @category, @message, @oob)"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + int fromNameIdx = sqlite3_bind_parameter_index(stmt, "@from_name"); + int fromAddressIdx = sqlite3_bind_parameter_index(stmt, "@from_address"); + int subjectIdx = sqlite3_bind_parameter_index(stmt, "@subject"); + int sentTimeIdx = sqlite3_bind_parameter_index(stmt, "@sent_time"); + int statusIdx = sqlite3_bind_parameter_index(stmt, "@status"); + int folderIdx = sqlite3_bind_parameter_index(stmt, "@folder"); + int categoryIdx = sqlite3_bind_parameter_index(stmt, "@category"); + int messageIdx = sqlite3_bind_parameter_index(stmt, "@message"); + int oobIdx = sqlite3_bind_parameter_index(stmt, "@oob"); + + sqlite3_bind_int(stmt, avatarIdIdx, message.header.avatarId); + + std::string fromName = FromWideString(message.header.fromName); + sqlite3_bind_text(stmt, fromNameIdx, fromName.c_str(), -1, 0); + + std::string fromAddress = FromWideString(message.header.fromAddress); + sqlite3_bind_text(stmt, fromAddressIdx, fromAddress.c_str(), -1, 0); + + std::string subject = FromWideString(message.header.subject); + sqlite3_bind_text(stmt, subjectIdx, subject.c_str(), -1, 0); + + sqlite3_bind_int(stmt, sentTimeIdx, message.header.sentTime); + sqlite3_bind_int(stmt, statusIdx, static_cast(message.header.status)); + + std::string folder = FromWideString(message.header.folder); + sqlite3_bind_text(stmt, folderIdx, folder.c_str(), -1, 0); + + std::string category = FromWideString(message.header.category); + sqlite3_bind_text(stmt, categoryIdx, category.c_str(), -1, 0); + + std::string msg = FromWideString(message.message); + sqlite3_bind_text(stmt, messageIdx, msg.c_str(), -1, 0); + + sqlite3_bind_blob(stmt, oobIdx, reinterpret_cast(message.oob.data()), message.oob.size() * 2, SQLITE_STATIC); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + message.header.messageId = static_cast(sqlite3_last_insert_rowid(db_)); +} + +std::vector PersistentMessageService::GetMessageHeaders(uint32_t avatarId) { + std::vector headers; + sqlite3_stmt* stmt; + + char sql[] = "SELECT id, avatar_id, from_name, from_address, subject, sent_time, status, " + "folder, category, message, oob FROM persistent_message WHERE avatar_id = " + "@avatar_id AND status IN (1, 2, 3)"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + + sqlite3_bind_int(stmt, avatarIdIdx, avatarId); + + while (sqlite3_step(stmt) == SQLITE_ROW) { + PersistentHeader header; + + header.messageId = sqlite3_column_int(stmt, 0); + header.avatarId = sqlite3_column_int(stmt, 1); + + auto tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 2))); + header.fromName = std::u16string(std::begin(tmp), std::end(tmp)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 3))); + header.fromAddress = std::u16string(std::begin(tmp), std::end(tmp)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 4))); + header.subject = std::u16string(std::begin(tmp), std::end(tmp)); + + header.sentTime = sqlite3_column_int(stmt, 5); + header.status = static_cast(sqlite3_column_int(stmt, 6)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 7))); + header.folder = std::u16string(std::begin(tmp), std::end(tmp)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 8))); + header.category = std::u16string(std::begin(tmp), std::end(tmp)); + + headers.push_back(std::move(header)); + } + + return headers; +} + +PersistentMessage PersistentMessageService::GetPersistentMessage( + uint32_t avatarId, uint32_t messageId) { + sqlite3_stmt* stmt; + + char sql[] = "SELECT id, avatar_id, from_name, from_address, subject, sent_time, status, " + "folder, category, message, oob FROM persistent_message WHERE id = @message_id " + "AND avatar_id = @avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int messageIdIdx = sqlite3_bind_parameter_index(stmt, "@message_id"); + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + + sqlite3_bind_int(stmt, messageIdIdx, messageId); + sqlite3_bind_int(stmt, avatarIdIdx, avatarId); + + if (sqlite3_step(stmt) != SQLITE_ROW) { + throw ChatResultException{ChatResultCode::PMSGNOTFOUND}; + } + + std::string tmp; + + PersistentMessage message; + message.header.messageId = messageId; + message.header.avatarId = avatarId; + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 2))); + message.header.fromName = std::u16string(std::begin(tmp), std::end(tmp)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 3))); + message.header.fromAddress = std::u16string(std::begin(tmp), std::end(tmp)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 4))); + message.header.subject = std::u16string(std::begin(tmp), std::end(tmp)); + + message.header.sentTime = sqlite3_column_int(stmt, 5); + message.header.status = static_cast(sqlite3_column_int(stmt, 6)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 7))); + message.header.folder = std::u16string(std::begin(tmp), std::end(tmp)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 8))); + message.header.category = std::u16string(std::begin(tmp), std::end(tmp)); + + tmp = std::string(reinterpret_cast(sqlite3_column_text(stmt, 9))); + message.message = std::u16string(std::begin(tmp), std::end(tmp)); + + int size = sqlite3_column_bytes(stmt, 10); + const uint8_t* data = reinterpret_cast(sqlite3_column_blob(stmt, 10)); + + message.oob.resize(size / 2); + for (int i = 0; i < size/2; ++i) { + message.oob[i] = *reinterpret_cast(data + i*2); + } + + sqlite3_finalize(stmt); + + if (message.header.status == PersistentState::NEW) { + UpdateMessageStatus( + message.header.avatarId, message.header.messageId, PersistentState::READ); + } + + return message; +} + +void PersistentMessageService::UpdateMessageStatus( + uint32_t avatarId, uint32_t messageId, PersistentState status) { + sqlite3_stmt* stmt; + + char sql[] = "UPDATE persistent_message SET status = @status WHERE id = @message_id AND " + "avatar_id = @avatar_id"; + + auto result = sqlite3_prepare_v2(db_, sql, -1, &stmt, 0); + if (result != SQLITE_OK) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + int statusIdx = sqlite3_bind_parameter_index(stmt, "@status"); + int messageIdIdx = sqlite3_bind_parameter_index(stmt, "@message_id"); + int avatarIdIdx = sqlite3_bind_parameter_index(stmt, "@avatar_id"); + + sqlite3_bind_int(stmt, statusIdx, static_cast(status)); + sqlite3_bind_int(stmt, messageIdIdx, messageId); + sqlite3_bind_int(stmt, avatarIdIdx, avatarId); + + result = sqlite3_step(stmt); + if (result != SQLITE_DONE) { + throw SQLite3Exception{result, sqlite3_errmsg(db_)}; + } + + sqlite3_finalize(stmt); +} diff --git a/stationapi/src/stationchat/PersistentMessageService.hpp b/stationapi/src/stationchat/PersistentMessageService.hpp new file mode 100755 index 0000000..a61a812 --- /dev/null +++ b/stationapi/src/stationchat/PersistentMessageService.hpp @@ -0,0 +1,30 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "PersistentMessage.hpp" + +#include + +#include +#include + +struct sqlite3; + +class PersistentMessageService { +public: + explicit PersistentMessageService(sqlite3* db); + ~PersistentMessageService(); + + void StoreMessage(PersistentMessage& message); + + std::vector GetMessageHeaders(uint32_t avatarId); + + PersistentMessage GetPersistentMessage(uint32_t avatarId, uint32_t messageId); + + void UpdateMessageStatus( + uint32_t avatarId, uint32_t messageId, PersistentState status); + +private: + sqlite3* db_; +}; diff --git a/stationapi/src/stationchat/RegistrarClient.cpp b/stationapi/src/stationchat/RegistrarClient.cpp new file mode 100755 index 0000000..6ed137c --- /dev/null +++ b/stationapi/src/stationchat/RegistrarClient.cpp @@ -0,0 +1,45 @@ +#include "RegistrarClient.hpp" + +#include "ChatEnums.hpp" +#include "RegistrarNode.hpp" +#include "Serialization.hpp" +#include "StationChatConfig.hpp" +#include "StringUtils.hpp" + +#include "protocol/RegistrarGetChatServer.hpp" + +#include "easylogging++.h" + +RegistrarClient::RegistrarClient(UdpConnection* connection, RegistrarNode* node) + : NodeClient(connection) + , node_{node} { + connection->SetHandler(this); +} + +RegistrarClient::~RegistrarClient() {} + +RegistrarNode* RegistrarClient::GetNode() { return node_; } + +void RegistrarClient::OnIncoming(std::istringstream& istream) { + ChatRequestType request_type = ::read(istream); + + switch (request_type) { + case ChatRequestType::REGISTRAR_GETCHATSERVER: { + auto request = ::read(istream); + RegistrarGetChatServer::ResponseType response{request.track}; + + try { + RegistrarGetChatServer(this, request, response); + } catch (const ChatResultException& e) { + response.result = e.code; + LOG(ERROR) << "ChatAPI Error: [" << static_cast(e.code) << "] " << e.message; + } + + Send(response); + } break; + default: + LOG(ERROR) << "Invalid registrar message type received: " + << static_cast(request_type); + break; + } +} diff --git a/stationapi/src/stationchat/RegistrarClient.hpp b/stationapi/src/stationchat/RegistrarClient.hpp new file mode 100755 index 0000000..3a259c2 --- /dev/null +++ b/stationapi/src/stationchat/RegistrarClient.hpp @@ -0,0 +1,22 @@ + +#pragma once + +#include "NodeClient.hpp" + +class RegistrarNode; +class UdpConnection; + +struct ReqRegistrarGetChatServer; + +class RegistrarClient : public NodeClient { +public: + RegistrarClient(UdpConnection* connection, RegistrarNode* node); + virtual ~RegistrarClient(); + + RegistrarNode* GetNode(); + +private: + void OnIncoming(std::istringstream& istream) override; + + RegistrarNode* node_; +}; diff --git a/stationapi/src/stationchat/RegistrarNode.cpp b/stationapi/src/stationchat/RegistrarNode.cpp new file mode 100755 index 0000000..8bef619 --- /dev/null +++ b/stationapi/src/stationchat/RegistrarNode.cpp @@ -0,0 +1,17 @@ + +#include "RegistrarNode.hpp" + +#include "StationChatConfig.hpp" + +RegistrarNode::RegistrarNode(StationChatConfig& config) + : Node(this, config.registrarAddress, config.registrarPort, config.bindToIp) + , config_{config} {} + +RegistrarNode::~RegistrarNode() {} + +StationChatConfig& RegistrarNode::GetConfig() { + return config_; +} + +void RegistrarNode::OnTick() {} + diff --git a/stationapi/src/stationchat/RegistrarNode.hpp b/stationapi/src/stationchat/RegistrarNode.hpp new file mode 100755 index 0000000..10d35b8 --- /dev/null +++ b/stationapi/src/stationchat/RegistrarNode.hpp @@ -0,0 +1,20 @@ + +#pragma once + +#include "Node.hpp" +#include "RegistrarClient.hpp" + +struct StationChatConfig; + +class RegistrarNode : public Node { +public: + explicit RegistrarNode(StationChatConfig& config); + ~RegistrarNode(); + + StationChatConfig& GetConfig(); + +private: + void OnTick() override; + + StationChatConfig& config_; +}; diff --git a/stationapi/src/stationchat/StationChatApp.cpp b/stationapi/src/stationchat/StationChatApp.cpp new file mode 100755 index 0000000..8fe36f0 --- /dev/null +++ b/stationapi/src/stationchat/StationChatApp.cpp @@ -0,0 +1,17 @@ +#include "StationChatApp.hpp" + +#include "easylogging++.h" + +StationChatApp::StationChatApp(StationChatConfig config) + : config_{std::move(config)} { + registrarNode_ = std::make_unique(config_); + LOG(INFO) << "Registrar listening @" << config_.registrarAddress << ":" << config_.registrarPort; + + gatewayNode_ = std::make_unique(config_); + LOG(INFO) << "Gateway listening @" << config_.gatewayAddress << ":" << config_.gatewayPort; +} + +void StationChatApp::Tick() { + registrarNode_->Tick(); + gatewayNode_->Tick(); +} diff --git a/stationapi/src/stationchat/StationChatApp.hpp b/stationapi/src/stationchat/StationChatApp.hpp new file mode 100755 index 0000000..88c6d51 --- /dev/null +++ b/stationapi/src/stationchat/StationChatApp.hpp @@ -0,0 +1,25 @@ + +#pragma once + +#include "GatewayNode.hpp" +#include "RegistrarNode.hpp" +#include "StationChatConfig.hpp" + +#include +#include +#include + +class StationChatApp { +public: + explicit StationChatApp(StationChatConfig config); + + bool IsRunning() const { return isRunning_; } + + void Tick(); + +private: + StationChatConfig config_; + bool isRunning_ = true; + std::unique_ptr gatewayNode_; + std::unique_ptr registrarNode_; +}; diff --git a/stationapi/src/stationchat/StationChatConfig.hpp b/stationapi/src/stationchat/StationChatConfig.hpp new file mode 100755 index 0000000..aaa0157 --- /dev/null +++ b/stationapi/src/stationchat/StationChatConfig.hpp @@ -0,0 +1,27 @@ + +#pragma once + +#include +#include + +struct StationChatConfig { + StationChatConfig() = default; + StationChatConfig(const std::string& gatewayAddress_, uint16_t gatewayPort_, + const std::string& registrarAddress_, uint16_t registrarPort_, const std::string& chatDatabasePath_, + bool bindToIp_) + : gatewayAddress{gatewayAddress_} + , gatewayPort{gatewayPort_} + , registrarAddress{registrarAddress_} + , registrarPort{registrarPort_} + , chatDatabasePath{chatDatabasePath_} + , bindToIp{bindToIp_} {} + + const uint32_t version = 2; + std::string gatewayAddress; + uint16_t gatewayPort; + std::string registrarAddress; + uint16_t registrarPort; + std::string chatDatabasePath; + std::string loggerConfig; + bool bindToIp; +}; diff --git a/stationapi/src/stationchat/main.cpp b/stationapi/src/stationchat/main.cpp new file mode 100755 index 0000000..1fb3fe9 --- /dev/null +++ b/stationapi/src/stationchat/main.cpp @@ -0,0 +1,117 @@ + + +#define ELPP_DISABLE_DEFAULT_CRASH_HANDLING 1 +#define ELPP_DEFAULT_LOG_FILE "logs/swgchat.log" + +#include "easylogging++.h" + +#include "StationChatApp.hpp" + +#include + +#include +#include +#include +#include + +#ifdef __GNUC__ +#include +#endif + +INITIALIZE_EASYLOGGINGPP + +StationChatConfig BuildConfiguration(int argc, const char* argv[]); + +#ifdef __GNUC__ +void SignalHandler(int sig); +#endif + +int main(int argc, const char* argv[]) { +#ifdef __GNUC__ + signal(SIGSEGV, SignalHandler); +#endif + + auto config = BuildConfiguration(argc, argv); + + el::Loggers::setDefaultConfigurations(config.loggerConfig, true); + START_EASYLOGGINGPP(argc, argv); + + StationChatApp app{config}; + + while (app.IsRunning()) { + app.Tick(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + return 0; +} + +StationChatConfig BuildConfiguration(int argc, const char* argv[]) { + namespace po = boost::program_options; + StationChatConfig config; + std::string configFile; + + // Declare a group of options that will be + // allowed only on command line + po::options_description generic("Generic options"); + generic.add_options() + ("help,h", "produces help message") + ("config,c", po::value(&configFile)->default_value("swgchat.cfg"), + "sets path to the configuration file") + ("logger_config", po::value(&config.loggerConfig)->default_value("logger.cfg"), + "sets path to the logger configuration file") + ; + + po::options_description options("Configuration"); + options.add_options() + ("gateway_address", po::value(&config.gatewayAddress)->default_value("127.0.0.1"), + "address for gateway connections") + ("gateway_port", po::value(&config.gatewayPort)->default_value(5001), + "port for gateway connections") + ("registrar_address", po::value(&config.registrarAddress)->default_value("127.0.0.1"), + "address for registrar connections") + ("registrar_port", po::value(&config.registrarPort)->default_value(5000), + "port for registrar connections") + ("bind_to_ip", po::value(&config.bindToIp)->default_value(false), + "when set to true, binds to the config address; otherwise, binds on any interface") + ("database_path", po::value(&config.chatDatabasePath)->default_value("chat.db"), + "path to the sqlite3 database file") + ; + + po::options_description cmdline_options; + cmdline_options.add(generic).add(options); + + po::options_description config_file_options; + config_file_options.add(options); + + po::variables_map vm; + po::store(po::command_line_parser(argc, argv).options(cmdline_options).allow_unregistered().run(), vm); + po::notify(vm); + + std::ifstream ifs(configFile.c_str()); + if (!ifs) { + throw std::runtime_error("Cannot open configuration file: " + configFile); + } + + po::store(po::parse_config_file(ifs, config_file_options), vm); + po::notify(vm); + + if (vm.count("help")) { + std::cout << cmdline_options << "\n"; + exit(EXIT_SUCCESS); + } + + return config; +} + +#ifdef __GNUC__ +void SignalHandler(int sig) { + const int BACKTRACE_LIMIT = 10; + void *arr[BACKTRACE_LIMIT]; + auto size = backtrace(arr, BACKTRACE_LIMIT); + + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(arr, size, STDERR_FILENO); + exit(1); +} +#endif diff --git a/stationapi/src/stationchat/protocol/AddBan.hpp b/stationapi/src/stationchat/protocol/AddBan.hpp new file mode 100755 index 0000000..8d4991f --- /dev/null +++ b/stationapi/src/stationchat/protocol/AddBan.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin ADDBAN */ + +struct ReqAddBan { + const ChatRequestType type = ChatRequestType::ADDBAN; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destAvatarName; + std::u16string destAvatarAddress; + std::u16string destRoomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqAddBan& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destAvatarName); + read(ar, data.destAvatarAddress); + read(ar, data.destRoomAddress); + read(ar, data.srcAddress); +} + +/** Begin ADDBAN */ + +struct ResAddBan { + explicit ResAddBan(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::ADDBAN; + uint32_t track; + ChatResultCode result; + uint32_t destRoomId; +}; + +template +void write(StreamT& ar, const ResAddBan& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.destRoomId); +} + +class AddBan { +public: + using RequestType = ReqAddBan; + using ResponseType = ResAddBan; + + AddBan(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/AddFriend.hpp b/stationapi/src/stationchat/protocol/AddFriend.hpp new file mode 100755 index 0000000..903dbae --- /dev/null +++ b/stationapi/src/stationchat/protocol/AddFriend.hpp @@ -0,0 +1,62 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin ADDFRIEND */ + +struct ReqAddFriend { + const ChatRequestType type = ChatRequestType::ADDFRIEND; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destName; + std::u16string destAddress; + std::u16string comment; + bool confirm; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqAddFriend& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destName); + read(ar, data.destAddress); + read(ar, data.comment); + read(ar, data.confirm); + read(ar, data.srcAddress); +} + +/** Begin ADDFRIEND */ + +struct ResAddFriend { + ResAddFriend(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::ADDFRIEND; + uint32_t track; + ChatResultCode result; +}; + +template +void write(StreamT& ar, const ResAddFriend& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} + +class AddFriend { +public: + using RequestType = ReqAddFriend; + using ResponseType = ResAddFriend; + + AddFriend(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; +}; diff --git a/stationapi/src/stationchat/protocol/AddIgnore.hpp b/stationapi/src/stationchat/protocol/AddIgnore.hpp new file mode 100755 index 0000000..6c0abcb --- /dev/null +++ b/stationapi/src/stationchat/protocol/AddIgnore.hpp @@ -0,0 +1,58 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin ADDIGNORE */ + +struct ReqAddIgnore { + const ChatRequestType type = ChatRequestType::ADDIGNORE; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destName; + std::u16string destAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqAddIgnore& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destName); + read(ar, data.destAddress); + read(ar, data.srcAddress); +} + +/** Begin ADDIGNORE */ + +struct ResAddIgnore { + ResAddIgnore(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::ADDIGNORE; + uint32_t track; + ChatResultCode result; +}; + +template +void write(StreamT& ar, const ResAddIgnore& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} + +class AddIgnore { +public: + using RequestType = ReqAddIgnore; + using ResponseType = ResAddIgnore; + + AddIgnore(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; +}; diff --git a/stationapi/src/stationchat/protocol/AddInvite.hpp b/stationapi/src/stationchat/protocol/AddInvite.hpp new file mode 100755 index 0000000..efa7234 --- /dev/null +++ b/stationapi/src/stationchat/protocol/AddInvite.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin ADDINVITE */ + +struct ReqAddInvite { + const ChatRequestType type = ChatRequestType::ADDINVITE; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destAvatarName; + std::u16string destAvatarAddress; + std::u16string destRoomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqAddInvite& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destAvatarName); + read(ar, data.destAvatarAddress); + read(ar, data.destRoomAddress); + read(ar, data.srcAddress); +} + +/** Begin ADDINVITE */ + +struct ResAddInvite { + explicit ResAddInvite(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::ADDINVITE; + uint32_t track; + ChatResultCode result; + uint32_t destRoomId; +}; + +template +void write(StreamT& ar, const ResAddInvite& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.destRoomId); +} + +class AddInvite { +public: + using RequestType = ReqAddInvite; + using ResponseType = ResAddInvite; + + AddInvite(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/AddModerator.hpp b/stationapi/src/stationchat/protocol/AddModerator.hpp new file mode 100755 index 0000000..3dbc03f --- /dev/null +++ b/stationapi/src/stationchat/protocol/AddModerator.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin ADDMODERATOR */ + +struct ReqAddModerator { + const ChatRequestType type = ChatRequestType::ADDMODERATOR; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destAvatarName; + std::u16string destAvatarAddress; + std::u16string destRoomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqAddModerator& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destAvatarName); + read(ar, data.destAvatarAddress); + read(ar, data.destRoomAddress); + read(ar, data.srcAddress); +} + +/** Begin ADDMODERATOR */ + +struct ResAddModerator { + ResAddModerator(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::ADDMODERATOR; + uint32_t track; + ChatResultCode result; + uint32_t destRoomId; +}; + +template +void write(StreamT& ar, const ResAddModerator& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.destRoomId); +} + +class AddModerator { +public: + using RequestType = ReqAddModerator; + using ResponseType = ResAddModerator; + + AddModerator(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/CreateRoom.hpp b/stationapi/src/stationchat/protocol/CreateRoom.hpp new file mode 100755 index 0000000..1f0ffde --- /dev/null +++ b/stationapi/src/stationchat/protocol/CreateRoom.hpp @@ -0,0 +1,83 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "ChatRoom.hpp" + +#include +#include +#include + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin CREATEROOM */ + +struct ReqCreateRoom { + const ChatRequestType type = ChatRequestType::CREATEROOM; + uint32_t track; + uint32_t creatorId; + std::u16string roomName; + std::u16string roomTopic; + std::u16string roomPassword; + uint32_t roomAttributes; + uint32_t roomMaxSize; + std::u16string roomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqCreateRoom& data) { + read(ar, data.track); + read(ar, data.creatorId); + read(ar, data.roomName); + read(ar, data.roomTopic); + read(ar, data.roomPassword); + read(ar, data.roomAttributes); + read(ar, data.roomMaxSize); + read(ar, data.roomAddress); + read(ar, data.srcAddress); +} + +/** Begin CREATEROOM */ + +struct ResCreateRoom { + explicit ResCreateRoom(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::CREATEROOM; + uint32_t track; + ChatResultCode result; + ChatRoom* room; + std::vector extraRooms; +}; + +template +void write(StreamT& ar, const ResCreateRoom& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + if (data.result == ChatResultCode::SUCCESS) { + write(ar, *data.room); + + write(ar, static_cast(data.extraRooms.size())); + for (auto room : data.extraRooms) { + write(ar, *room); + } + } +} + +class CreateRoom { +public: + using RequestType = ReqCreateRoom; + using ResponseType = ResCreateRoom; + + CreateRoom(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/DestroyAvatar.hpp b/stationapi/src/stationchat/protocol/DestroyAvatar.hpp new file mode 100755 index 0000000..83ac28d --- /dev/null +++ b/stationapi/src/stationchat/protocol/DestroyAvatar.hpp @@ -0,0 +1,55 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin DESTROYAVATAR */ + +struct ReqDestroyAvatar { + const ChatRequestType type = ChatRequestType::LOGOUTAVATAR; + uint32_t track; + uint32_t avatarId; + std::u16string address; +}; + +template +void read(StreamT& ar, ReqDestroyAvatar& data) { + read(ar, data.track); + read(ar, data.avatarId); + read(ar, data.address); +} + +/** Begin DESTROYAVATAR */ + +struct ResDestroyAvatar { + explicit ResDestroyAvatar(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::DESTROYAVATAR; + uint32_t track; + ChatResultCode result; +}; + +template +void write(StreamT& ar, const ResDestroyAvatar& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} + +class DestroyAvatar { +public: + using RequestType = ReqDestroyAvatar; + using ResponseType = ResDestroyAvatar; + + DestroyAvatar(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/DestroyRoom.hpp b/stationapi/src/stationchat/protocol/DestroyRoom.hpp new file mode 100755 index 0000000..cc88e83 --- /dev/null +++ b/stationapi/src/stationchat/protocol/DestroyRoom.hpp @@ -0,0 +1,59 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin DESTROYROOM */ + +struct ReqDestroyRoom { + const ChatRequestType type = ChatRequestType::DESTROYROOM; + uint32_t track; + uint32_t srcAvatarId; + std::u16string roomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqDestroyRoom& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.roomAddress); + read(ar, data.srcAddress); +} + +/** Begin DESTROYROOM */ + +struct ResDestroyRoom { + explicit ResDestroyRoom(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::DESTROYROOM; + uint32_t track; + ChatResultCode result; + uint32_t roomId; +}; + +template +void write(StreamT& ar, const ResDestroyRoom& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.roomId); +} + +class DestroyRoom { +public: + using RequestType = ReqDestroyRoom; + using ResponseType = ResDestroyRoom; + + DestroyRoom(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/EnterRoom.hpp b/stationapi/src/stationchat/protocol/EnterRoom.hpp new file mode 100755 index 0000000..fc0120b --- /dev/null +++ b/stationapi/src/stationchat/protocol/EnterRoom.hpp @@ -0,0 +1,89 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "ChatRoom.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin ENTERROOM */ + +struct ReqEnterRoom { + const ChatRequestType type = ChatRequestType::ENTERROOM; + uint32_t track; + uint32_t srcAvatarId; + std::u16string roomAddress; + std::u16string roomPassword; + bool passiveCreate; + std::u16string paramRoomTopic; + uint32_t paramRoomAttributes; + uint32_t paramRoomMaxSize; + bool requestingEntry; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqEnterRoom& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.roomAddress); + read(ar, data.roomPassword); + read(ar, data.passiveCreate); + + if (data.passiveCreate) { + read(ar, data.paramRoomTopic); + read(ar, data.paramRoomAttributes); + read(ar, data.paramRoomMaxSize); + } + + read(ar, data.requestingEntry); + read(ar, data.srcAddress); +} + +/** Begin ENTERROOM */ + +struct ResEnterRoom { + ResEnterRoom(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::ENTERROOM; + uint32_t track; + ChatResultCode result; + uint32_t roomId; + bool gotRoomObj = false; + ChatRoom* room = nullptr; + std::vector extraRooms; +}; + +template +void write(StreamT& ar, const ResEnterRoom& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.roomId); + write(ar, data.gotRoomObj); + + if (data.gotRoomObj) { + write(ar, *data.room); + + write(ar, static_cast(data.extraRooms.size())); + for (auto room : data.extraRooms) { + write(ar, *room); + } + } +} + +class EnterRoom { +public: + using RequestType = ReqEnterRoom; + using ResponseType = ResEnterRoom; + + EnterRoom(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/FailoverReLoginAvatar.hpp b/stationapi/src/stationchat/protocol/FailoverReLoginAvatar.hpp new file mode 100755 index 0000000..a4629ea --- /dev/null +++ b/stationapi/src/stationchat/protocol/FailoverReLoginAvatar.hpp @@ -0,0 +1,64 @@ + +#pragma once + +#include "ChatEnums.hpp" + +#include +#include + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +struct ReqFailoverReLoginAvatar{ + const ChatRequestType type = ChatRequestType::FAILOVER_RELOGINAVATAR; + uint32_t track; + uint32_t avatarId; + uint32_t userId; + std::u16string name; + std::u16string address; + std::u16string loginLocation; + int32_t loginPriority; + uint32_t attributes; +}; + +struct ResFailoverReLoginAvatar { + ResFailoverReLoginAvatar(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::FAILOVER_RELOGINAVATAR; + uint32_t track; + ChatResultCode result; +}; + +class FailoverReLoginAvatar { +public: + using RequestType = ReqFailoverReLoginAvatar; + using ResponseType = ResFailoverReLoginAvatar; + + FailoverReLoginAvatar(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; + +template +void read(StreamT& ar, ReqFailoverReLoginAvatar& data) { + read(ar, data.track); + read(ar, data.avatarId); + read(ar, data.userId); + read(ar, data.name); + read(ar, data.address); + read(ar, data.loginLocation); + read(ar, data.loginPriority); + read(ar, data.attributes); +} + +template +void write(StreamT& ar, const ResFailoverReLoginAvatar& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} \ No newline at end of file diff --git a/stationapi/src/stationchat/protocol/FriendStatus.hpp b/stationapi/src/stationchat/protocol/FriendStatus.hpp new file mode 100755 index 0000000..ac6c8f0 --- /dev/null +++ b/stationapi/src/stationchat/protocol/FriendStatus.hpp @@ -0,0 +1,70 @@ + +#pragma once + +#include "ChatAvatar.hpp" +#include "ChatEnums.hpp" + +#include + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin FRIENDSTATUS */ + +struct ReqFriendStatus { + const ChatRequestType type = ChatRequestType::FRIENDSTATUS; + uint32_t track; + uint32_t srcAvatarId; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqFriendStatus& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.srcAddress); +} + +/** Begin FRIENDSTATUS */ + +struct ResFriendStatus { + ResFriendStatus(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::FRIENDSTATUS; + uint32_t track; + ChatResultCode result; + const ChatAvatar* srcAvatar; +}; + +template +void write(StreamT& ar, const ResFriendStatus& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + if (data.result == ChatResultCode::SUCCESS && data.srcAvatar) { + const auto& friends = data.srcAvatar->GetFriendList(); + + write(ar, static_cast(friends.size())); + for (auto& friendContact : friends) { + write(ar, friendContact); + } + } else { + write(ar, static_cast(0)); + } +} + +class FriendStatus { +public: + using RequestType = ReqFriendStatus; + using ResponseType = ResFriendStatus; + + FriendStatus(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/GetAnyAvatar.hpp b/stationapi/src/stationchat/protocol/GetAnyAvatar.hpp new file mode 100755 index 0000000..6e31e15 --- /dev/null +++ b/stationapi/src/stationchat/protocol/GetAnyAvatar.hpp @@ -0,0 +1,68 @@ + +#pragma once + +#include "ChatAvatar.hpp" +#include "ChatEnums.hpp" + +class ChatAvatarService; +class GatewayClient; + +/** Begin GETANYAVATAR */ + +struct ReqGetAnyAvatar { + const ChatRequestType type = ChatRequestType::GETANYAVATAR; + uint32_t track; + std::u16string name; + std::u16string address; +}; + +template +void read(StreamT& ar, ReqGetAnyAvatar& data) { + read(ar, data.track); + read(ar, data.name); + read(ar, data.address); +} + +/** Begin GETANYAVATAR */ + +struct ResGetAnyAvatar { + explicit ResGetAnyAvatar(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + ResGetAnyAvatar(uint32_t track_, ChatResultCode result_, bool isOnline_, + const ChatAvatar* avatar_) + : track{track_} + , result{result_} + , isOnline{isOnline_} + , avatar{avatar_} {} + + const ChatResponseType type = ChatResponseType::GETANYAVATAR; + uint32_t track; + ChatResultCode result; + bool isOnline; + const ChatAvatar* avatar; +}; + +template +void write(StreamT& ar, const ResGetAnyAvatar& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.isOnline); + + if (data.result == ChatResultCode::SUCCESS) { + write(ar, data.avatar); + } +} + +class GetAnyAvatar { +public: + using RequestType = ReqGetAnyAvatar; + using ResponseType = ResGetAnyAvatar; + + GetAnyAvatar(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; +}; diff --git a/stationapi/src/stationchat/protocol/GetPersistentHeaders.hpp b/stationapi/src/stationchat/protocol/GetPersistentHeaders.hpp new file mode 100755 index 0000000..35a1952 --- /dev/null +++ b/stationapi/src/stationchat/protocol/GetPersistentHeaders.hpp @@ -0,0 +1,62 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "PersistentMessage.hpp" + +#include + +class PersistentMessageService; +class GatewayClient; + +/** Begin GETPERSISTENTHEADERS */ + +struct ReqGetPersistentHeaders { + const ChatRequestType type = ChatRequestType::GETPERSISTENTHEADERS; + uint32_t track; + uint32_t avatarId; + std::u16string category; +}; + +template +void read(StreamT& ar, ReqGetPersistentHeaders& data) { + read(ar, data.track); + read(ar, data.avatarId); + read(ar, data.category); +} + +/** Begin GETPERSISTENTHEADERS */ + +struct ResGetPersistentHeaders { + ResGetPersistentHeaders(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::GETPERSISTENTHEADERS; + uint32_t track; + ChatResultCode result; + std::vector headers; +}; + +template +void write(StreamT& ar, const ResGetPersistentHeaders& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, static_cast(data.headers.size())); + + for (auto& header : data.headers) { + write(ar, header); + } +} + +class GetPersistentHeaders { +public: + using RequestType = ReqGetPersistentHeaders; + using ResponseType = ResGetPersistentHeaders; + + GetPersistentHeaders(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + PersistentMessageService* messageService_; +}; diff --git a/stationapi/src/stationchat/protocol/GetPersistentMessage.hpp b/stationapi/src/stationchat/protocol/GetPersistentMessage.hpp new file mode 100755 index 0000000..a5f3101 --- /dev/null +++ b/stationapi/src/stationchat/protocol/GetPersistentMessage.hpp @@ -0,0 +1,60 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "PersistentMessage.hpp" + +class PersistentMessageService; +class GatewayClient; + +/** Begin GETPERSISTENTMESSAGE */ + +struct ReqGetPersistentMessage { + const ChatRequestType type = ChatRequestType::GETPERSISTENTMESSAGE; + uint32_t track; + uint32_t srcAvatarId; + uint32_t messageId; +}; + +template +void read(StreamT& ar, ReqGetPersistentMessage& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.messageId); +} + +/** Begin GETPERSISTENTMESSAGE */ + +struct ResGetPersistentMessage { + ResGetPersistentMessage( + uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::GETPERSISTENTMESSAGE; + uint32_t track; + ChatResultCode result; + PersistentMessage message; +}; + +template +void write(StreamT& ar, const ResGetPersistentMessage& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + if (data.result == ChatResultCode::SUCCESS) { + write(ar, data.message); + } +} + +class GetPersistentMessage { +public: + using RequestType = ReqGetPersistentMessage; + using ResponseType = ResGetPersistentMessage; + + GetPersistentMessage(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + PersistentMessageService* messageService_; +}; diff --git a/stationapi/src/stationchat/protocol/GetRoom.hpp b/stationapi/src/stationchat/protocol/GetRoom.hpp new file mode 100755 index 0000000..0d97baf --- /dev/null +++ b/stationapi/src/stationchat/protocol/GetRoom.hpp @@ -0,0 +1,65 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "ChatRoom.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin GETROOM */ + +struct ReqGetRoom { + const ChatRequestType type = ChatRequestType::GETROOMSUMMARIES; + uint32_t track; + std::u16string roomAddress; +}; + +template +void read(StreamT& ar, ReqGetRoom& data) { + read(ar, data.track); + read(ar, data.roomAddress); +} + +/** Begin GETROOM */ + +struct ResGetRoom { + ResGetRoom(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} + , room{nullptr} {} + + const ChatResponseType type = ChatResponseType::GETROOM; + uint32_t track; + ChatResultCode result; + ChatRoom* room; + std::vector extraRooms; +}; + +template +void write(StreamT& ar, const ResGetRoom& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + if (data.result == ChatResultCode::SUCCESS) { + write(ar, *data.room); + + write(ar, static_cast(data.extraRooms.size())); + for (auto room : data.extraRooms) { + write(ar, *room); + } + } +} + +class GetRoom { +public: + using RequestType = ReqGetRoom; + using ResponseType = ResGetRoom; + + GetRoom(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/GetRoomSummaries.hpp b/stationapi/src/stationchat/protocol/GetRoomSummaries.hpp new file mode 100755 index 0000000..96727e3 --- /dev/null +++ b/stationapi/src/stationchat/protocol/GetRoomSummaries.hpp @@ -0,0 +1,67 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "ChatRoom.hpp" + +#include + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin GETROOMSUMMARIES */ + +struct ReqGetRoomSummaries { + const ChatRequestType type = ChatRequestType::GETROOMSUMMARIES; + uint32_t track; + std::u16string startNodeAddress; + std::u16string roomFilter; +}; + +template +void read(StreamT& ar, ReqGetRoomSummaries& data) { + read(ar, data.track); + read(ar, data.startNodeAddress); + read(ar, data.roomFilter); +} + +/** Begin GETROOMSUMMARIES */ + +struct ResGetRoomSummaries { + ResGetRoomSummaries(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::GETROOMSUMMARIES; + uint32_t track; + ChatResultCode result; + std::vector rooms; +}; + +template +void write(StreamT& ar, const ResGetRoomSummaries& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + write(ar, static_cast(data.rooms.size())); + for (auto room : data.rooms) { + write(ar, room->GetRoomAddress()); + write(ar, room->GetRoomTopic()); + write(ar, room->GetRoomAttributes()); + write(ar, room->GetCurrentRoomSize()); + write(ar, room->GetMaxRoomSize()); + } +} + +class GetRoomSummaries { +public: + using RequestType = ReqGetRoomSummaries; + using ResponseType = ResGetRoomSummaries; + + GetRoomSummaries(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/IgnoreStatus.hpp b/stationapi/src/stationchat/protocol/IgnoreStatus.hpp new file mode 100755 index 0000000..4e14fa5 --- /dev/null +++ b/stationapi/src/stationchat/protocol/IgnoreStatus.hpp @@ -0,0 +1,67 @@ + +#pragma once + +#include "ChatAvatar.hpp" +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin IGNORESTATUS */ + +struct ReqIgnoreStatus { + const ChatRequestType type = ChatRequestType::IGNORESTATUS; + uint32_t track; + uint32_t srcAvatarId; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqIgnoreStatus& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.srcAddress); +} + +/** Begin IGNORESTATUS */ + +struct ResIgnoreStatus { + ResIgnoreStatus(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::IGNORESTATUS; + uint32_t track; + ChatResultCode result; + const ChatAvatar* srcAvatar; +}; + +template +void write(StreamT& ar, const ResIgnoreStatus& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + if (data.result == ChatResultCode::SUCCESS && data.srcAvatar) { + const auto& ignored = data.srcAvatar->GetIgnoreList(); + + write(ar, static_cast(ignored.size())); + for (auto& ignoredContact : ignored) { + write(ar, ignoredContact); + } + } else { + write(ar, static_cast(0)); + } +} + +class IgnoreStatus { +public: + using RequestType = ReqIgnoreStatus; + using ResponseType = ResIgnoreStatus; + + IgnoreStatus(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; +}; diff --git a/stationapi/src/stationchat/protocol/KickAvatar.hpp b/stationapi/src/stationchat/protocol/KickAvatar.hpp new file mode 100755 index 0000000..e4f3812 --- /dev/null +++ b/stationapi/src/stationchat/protocol/KickAvatar.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin KICKAVATAR */ + +struct ReqKickAvatar { + const ChatRequestType type = ChatRequestType::KICKAVATAR; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destAvatarName; + std::u16string destAvatarAddress; + std::u16string destRoomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqKickAvatar& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destAvatarName); + read(ar, data.destAvatarAddress); + read(ar, data.destRoomAddress); + read(ar, data.srcAddress); +} + +/** Begin KICKAVATAR */ + +struct ResKickAvatar { + ResKickAvatar(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::KICKAVATAR; + uint32_t track; + ChatResultCode result; + uint32_t destRoomId; +}; + +template +void write(StreamT& ar, const ResKickAvatar& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.destRoomId); +} + +class KickAvatar { +public: + using RequestType = ReqKickAvatar; + using ResponseType = ResKickAvatar; + + KickAvatar(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/LeaveRoom.hpp b/stationapi/src/stationchat/protocol/LeaveRoom.hpp new file mode 100755 index 0000000..a278e90 --- /dev/null +++ b/stationapi/src/stationchat/protocol/LeaveRoom.hpp @@ -0,0 +1,59 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin LEAVEROOM */ + +struct ReqLeaveRoom { + const ChatRequestType type = ChatRequestType::LEAVEROOM; + uint32_t track; + uint32_t srcAvatarId; + std::u16string roomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqLeaveRoom& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.roomAddress); + read(ar, data.srcAddress); +} + +/** Begin LEAVEROOM */ + +struct ResLeaveRoom { + ResLeaveRoom(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::LEAVEROOM; + uint32_t track; + ChatResultCode result; + uint32_t roomId; +}; + +template +void write(StreamT& ar, const ResLeaveRoom& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.roomId); +} + +class LeaveRoom { +public: + using RequestType = ReqLeaveRoom; + using ResponseType = ResLeaveRoom; + + LeaveRoom(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/LoginAvatar.hpp b/stationapi/src/stationchat/protocol/LoginAvatar.hpp new file mode 100755 index 0000000..e9b5d59 --- /dev/null +++ b/stationapi/src/stationchat/protocol/LoginAvatar.hpp @@ -0,0 +1,69 @@ + +#pragma once + +#include "ChatAvatar.hpp" +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin LOGINAVATAR */ + +struct ReqLoginAvatar { + const ChatRequestType type = ChatRequestType::LOGINAVATAR; + uint32_t track; + uint32_t userId; + std::u16string name; + std::u16string address; + std::u16string loginLocation; + int32_t loginPriority; + int32_t loginAttributes; +}; + +template +void read(StreamT& ar, ReqLoginAvatar& data) { + read(ar, data.track); + read(ar, data.userId); + read(ar, data.name); + read(ar, data.address); + read(ar, data.loginLocation); + read(ar, data.loginPriority); + read(ar, data.loginAttributes); +} + +/** Begin LOGINAVATAR */ + +struct ResLoginAvatar { + explicit ResLoginAvatar(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::LOGINAVATAR; + uint32_t track; + ChatResultCode result; + const ChatAvatar* avatar; +}; + +template +void write(StreamT& ar, const ResLoginAvatar& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + if (data.result == ChatResultCode::SUCCESS) { + write(ar, data.avatar); + } +} + +class LoginAvatar { +public: + using RequestType = ReqLoginAvatar; + using ResponseType = ResLoginAvatar; + + LoginAvatar(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/LogoutAvatar.hpp b/stationapi/src/stationchat/protocol/LogoutAvatar.hpp new file mode 100755 index 0000000..eb763bc --- /dev/null +++ b/stationapi/src/stationchat/protocol/LogoutAvatar.hpp @@ -0,0 +1,54 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin LOGOUTAVATAR */ + +struct ReqLogoutAvatar { + const ChatRequestType type = ChatRequestType::LOGOUTAVATAR; + uint32_t track; + uint32_t avatarId; +}; + +template +void read(StreamT& ar, ReqLogoutAvatar& data) { + read(ar, data.track); + read(ar, data.avatarId); +} + +/** Begin LOGOUTAVATAR */ + +struct ResLogoutAvatar { + explicit ResLogoutAvatar(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::LOGOUTAVATAR; + uint32_t track; + ChatResultCode result; +}; + +template +void write(StreamT& ar, const ResLogoutAvatar& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} + + +class LogoutAvatar { +public: + using RequestType = ReqLogoutAvatar; + using ResponseType = ResLogoutAvatar; + + LogoutAvatar(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/Protocol.cpp b/stationapi/src/stationchat/protocol/Protocol.cpp new file mode 100755 index 0000000..ef64e81 --- /dev/null +++ b/stationapi/src/stationchat/protocol/Protocol.cpp @@ -0,0 +1,727 @@ +#include "AddBan.hpp" + +#include "ChatAvatarService.hpp" +#include "ChatRoomService.hpp" +#include "GatewayClient.hpp" +#include "GatewayNode.hpp" +#include "PersistentMessageService.hpp" +#include "RegistrarClient.hpp" +#include "RegistrarNode.hpp" +#include "StringUtils.hpp" +#include "StationChatConfig.hpp" + +#include "protocol/AddBan.hpp" +#include "protocol/AddFriend.hpp" +#include "protocol/AddIgnore.hpp" +#include "protocol/AddInvite.hpp" +#include "protocol/AddModerator.hpp" +#include "protocol/CreateRoom.hpp" +#include "protocol/DestroyAvatar.hpp" +#include "protocol/DestroyRoom.hpp" +#include "protocol/EnterRoom.hpp" +#include "protocol/FailoverReLoginAvatar.hpp" +#include "protocol/FriendStatus.hpp" +#include "protocol/GetAnyAvatar.hpp" +#include "protocol/GetPersistentHeaders.hpp" +#include "protocol/GetPersistentMessage.hpp" +#include "protocol/GetRoom.hpp" +#include "protocol/GetRoomSummaries.hpp" +#include "protocol/IgnoreStatus.hpp" +#include "protocol/KickAvatar.hpp" +#include "protocol/LeaveRoom.hpp" +#include "protocol/LoginAvatar.hpp" +#include "protocol/LogoutAvatar.hpp" +#include "protocol/RegistrarGetChatServer.hpp" +#include "protocol/RemoveBan.hpp" +#include "protocol/RemoveFriend.hpp" +#include "protocol/RemoveIgnore.hpp" +#include "protocol/RemoveInvite.hpp" +#include "protocol/RemoveModerator.hpp" +#include "protocol/SendInstantMessage.hpp" +#include "protocol/SendPersistentMessage.hpp" +#include "protocol/SendRoomMessage.hpp" +#include "protocol/SetApiVersion.hpp" +#include "protocol/SetAvatarAttributes.hpp" +#include "protocol/UpdatePersistentMessage.hpp" + +#include "easylogging++.h" + +AddBan::AddBan(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "ADDBAN request received - adding ban for: " + << FromWideString(request.destAvatarName) << "@" + << FromWideString(request.destAvatarAddress) << " to " + << FromWideString(request.destRoomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto bannedAvatar + = avatarService_->GetAvatar(request.destAvatarName, request.destAvatarAddress); + if (!bannedAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + auto room = roomService_->GetRoom(request.destRoomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.destRoomId = room->GetRoomId(); + + room->AddBanned(srcAvatar->GetAvatarId(), bannedAvatar); +} + +AddFriend::AddFriend(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "ADDFRIEND request received - adding: " << FromWideString(request.destName) << "@" + << FromWideString(request.destAddress) << " to " << request.srcAvatarId << "@" + << FromWideString(request.srcAddress); + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto destAvatar = avatarService_->GetAvatar(request.destName, request.destAddress); + if (!destAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + srcAvatar->AddFriend(destAvatar); + + if (destAvatar->IsOnline()) { + client->SendFriendLoginUpdate(srcAvatar, destAvatar); + } +} + +AddIgnore::AddIgnore(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "ADDIGNORE request received - adding: " << FromWideString(request.destName) << "@" + << FromWideString(request.destAddress) << " to " << request.srcAvatarId << "@" + << FromWideString(request.srcAddress); + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto destAvatar = avatarService_->GetAvatar(request.destName, request.destAddress); + if (!destAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + srcAvatar->AddIgnore(destAvatar); +} + +AddInvite::AddInvite(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "ADDINVITE request received - adding invitation for: " + << FromWideString(request.destAvatarName) << "@" + << FromWideString(request.destAvatarAddress) << " to " + << FromWideString(request.destRoomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto invitedAvatar + = avatarService_->GetAvatar(request.destAvatarName, request.destAvatarAddress); + if (!invitedAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + auto room = roomService_->GetRoom(request.destRoomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.destRoomId = room->GetRoomId(); + + room->AddInvite(srcAvatar->GetAvatarId(), invitedAvatar); +} + +AddModerator::AddModerator( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "ADDMODERATOR request recieved - adding: " + << FromWideString(request.destAvatarName) << "@" + << FromWideString(request.destAvatarAddress) << " to " + << FromWideString(request.destRoomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto moderatorAvatar + = avatarService_->GetAvatar(request.destAvatarName, request.destAvatarAddress); + if (!moderatorAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + auto room = roomService_->GetRoom(request.destRoomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.destRoomId = room->GetRoomId(); + + room->AddModerator(srcAvatar->GetAvatarId(), moderatorAvatar); +} + +CreateRoom::CreateRoom(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "CREATEROOM request received - creator: " << request.creatorId << "@" + << FromWideString(request.srcAddress) + << " room: " << FromWideString(request.roomAddress); + + response.room = roomService_->CreateRoom(avatarService_->GetAvatar(request.creatorId), + request.roomName, request.roomTopic, request.roomPassword, request.roomAttributes, + request.roomMaxSize, request.roomAddress, request.srcAddress); +} + +DestroyAvatar::DestroyAvatar( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + auto avatar = avatarService_->GetAvatar(request.avatarId); + if (!avatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + // Remove From All Rooms + for (auto room : roomService_->GetJoinedRooms(avatar)) { + auto addresses = room->GetConnectedAddresses(); + room->LeaveRoom(avatar); + + client->SendLeaveRoomUpdate(addresses, avatar->GetAvatarId(), room->GetRoomId()); + } + + // Destroy avatar + avatarService_->DestroyAvatar(avatar); +} + +DestroyRoom::DestroyRoom(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "DESTROYROOM request received " << request.srcAvatarId << "@" + << FromWideString(request.srcAddress) + << " room: " << FromWideString(request.roomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException(ChatResultCode::SRCAVATARDOESNTEXIST); + } + + auto room = roomService_->GetRoom(request.roomAddress); + if (!room) { + throw ChatResultException(ChatResultCode::ADDRESSNOTROOM); + } + + auto addresses = room->GetRemoteAddresses(); + auto roomId = room->GetRoomId(); + + response.roomId = roomId; + + roomService_->DestroyRoom(room); + + client->SendDestroyRoomUpdate(srcAvatar, roomId, addresses); +} + +EnterRoom::EnterRoom(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "ENTERROOM request received - avatar: " << request.srcAvatarId << "@" + << FromWideString(request.srcAddress) + << " room: " << FromWideString(request.roomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + response.room = roomService_->GetRoom(request.roomAddress); + if (!response.room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.roomId = response.room->GetRoomId(); + response.room->EnterRoom(srcAvatar, request.roomPassword); + + client->SendEnterRoomUpdate(srcAvatar, response.room); +} + +FailoverReLoginAvatar::FailoverReLoginAvatar( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "FAILOVER_RELOGINAVATAR request received " << FromWideString(request.name) << "@" + << FromWideString(request.address); + + auto avatar = avatarService_->GetAvatar(request.name, request.address); + if (!avatar) { + LOG(INFO) << "Login avatar does not exist, creating a new one " << FromWideString(request.name) << "@" + << FromWideString(request.address); + avatar = avatarService_->CreateAvatar(request.name, request.address, request.userId, + request.attributes, request.loginLocation); + } + + avatarService_->LoginAvatar(CHECK_NOTNULL(avatar)); + + if (avatar->GetName().compare(u"SYSTEM") == 0) { + client->GetNode()->RegisterClientAddress(avatar->GetAddress(), client); + roomService_->LoadRoomsFromStorage(request.address); + } else { + client->SendFriendLoginUpdates(avatar); + } + + for (auto room : roomService_->GetJoinedRooms(avatar)) { + client->SendEnterRoomUpdate(avatar, room); + } +} + +FriendStatus::FriendStatus( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "FRIENDSTATUS request received - for " << request.srcAvatarId << "@" + << FromWideString(request.srcAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + response.srcAvatar = srcAvatar; +} + +GetAnyAvatar::GetAnyAvatar( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "GETANYAVATAR request received - avatar: " << FromWideString(request.name) << "@" + << FromWideString(request.address); + + auto avatar = avatarService_->GetAvatar(request.name, request.address); + if (!avatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + response.isOnline = avatar->IsOnline(); + response.avatar = avatar; +} + +GetPersistentHeaders::GetPersistentHeaders( + GatewayClient* client, const RequestType& request, ResponseType& response) + : messageService_{client->GetNode()->GetMessageService()} { + LOG(INFO) << "GETPERSISTENTHEADERS request recieved - avatar: " << request.avatarId + << " category: " << FromWideString(request.category); + + response.headers = messageService_->GetMessageHeaders(request.avatarId); +} + +GetPersistentMessage::GetPersistentMessage( + GatewayClient* client, const RequestType& request, ResponseType& response) + : messageService_{client->GetNode()->GetMessageService()} { + LOG(INFO) << "GETPERSISTENTMESSAGE request received - avatar: " << request.srcAvatarId + << " message: " << request.messageId; + + response.message + = messageService_->GetPersistentMessage(request.srcAvatarId, request.messageId); +} + +GetRoom::GetRoom(GatewayClient* client, const RequestType& request, ResponseType& response) + : roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "GETROOM request received - room: " << FromWideString(request.roomAddress); + + auto room = roomService_->GetRoom(request.roomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSDOESNTEXIST}; + } + + response.room = room; +} + +GetRoomSummaries::GetRoomSummaries( + GatewayClient* client, const RequestType& request, ResponseType& response) + : roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "GETROOMSUMMARIES request received - start node: " + << FromWideString(request.startNodeAddress) + << " filter: " << FromWideString(request.roomFilter); + + response.rooms = roomService_->GetRoomSummaries(request.startNodeAddress, request.roomFilter); +} + +IgnoreStatus::IgnoreStatus( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "IGNORESTATUS request received - for " << request.srcAvatarId << "@" + << FromWideString(request.srcAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + response.srcAvatar = srcAvatar; +} + +KickAvatar::KickAvatar(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "KICKAVATAR request received - kicking: " << FromWideString(request.destAvatarName) + << "@" << FromWideString(request.destAvatarAddress) << " from " + << FromWideString(request.destRoomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto destAvatar = avatarService_->GetAvatar(request.destAvatarName, request.destAvatarAddress); + if (!destAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + auto room = roomService_->GetRoom(request.destRoomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.destRoomId = room->GetRoomId(); + + auto addresses = room->GetConnectedAddresses(); + room->KickAvatar(srcAvatar->GetAvatarId(), destAvatar); + + client->SendKickAvatarUpdate(addresses, srcAvatar, destAvatar, room); +} + +LeaveRoom::LeaveRoom(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto room = roomService_->GetRoom(request.roomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.roomId = room->GetRoomId(); + + // Cache the addresses before leaving the room in case this avatar was the + // last on their server, to ensure the update messages goes out. + auto addresses = room->GetConnectedAddresses(); + room->LeaveRoom(srcAvatar); + + client->SendLeaveRoomUpdate(addresses, srcAvatar->GetAvatarId(), room->GetRoomId()); +} + +LoginAvatar::LoginAvatar(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "LOGINAVATAR request received " << FromWideString(request.name) << "@" + << FromWideString(request.address); + + auto avatar = avatarService_->GetAvatar(request.name, request.address); + if (!avatar) { + LOG(INFO) << "Login avatar does not exist, creating a new one " + << FromWideString(request.name) << "@" << FromWideString(request.address); + avatar = avatarService_->CreateAvatar(request.name, request.address, request.userId, + request.loginAttributes, request.loginLocation); + } + + avatarService_->LoginAvatar(CHECK_NOTNULL(avatar)); + + if (avatar->GetName().compare(u"SYSTEM") == 0) { + client->GetNode()->RegisterClientAddress(avatar->GetAddress(), client); + roomService_->LoadRoomsFromStorage(request.address); + } else { + client->SendFriendLoginUpdates(avatar); + } + + response.avatar = avatar; +} + +LogoutAvatar::LogoutAvatar(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "LOGOUTAVATAR request received - avatar id:" << request.avatarId; + + auto avatar = avatarService_->GetAvatar(request.avatarId); + + for (auto room : roomService_->GetJoinedRooms(avatar)) { + auto addresses = room->GetConnectedAddresses(); + room->LeaveRoom(avatar); + + client->SendLeaveRoomUpdate(addresses, avatar->GetAvatarId(), room->GetRoomId()); + } + + client->SendFriendLogoutUpdates(avatar); + + avatarService_->LogoutAvatar(avatar); +} + +RegistrarGetChatServer::RegistrarGetChatServer(RegistrarClient* client, const RequestType& request, ResponseType& response) { + auto& config = client->GetNode()->GetConfig(); + + response.hostname = ToWideString(config.gatewayAddress); + response.port = config.gatewayPort; +} + +RemoveBan::RemoveBan(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "REMOVEBAN request received - removing ban for: " + << FromWideString(request.destAvatarName) << "@" + << FromWideString(request.destAvatarAddress) << " from " + << FromWideString(request.destRoomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto bannedAvatar + = avatarService_->GetAvatar(request.destAvatarName, request.destAvatarAddress); + if (!bannedAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + auto room = roomService_->GetRoom(request.destRoomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.destRoomId = room->GetRoomId(); + + room->RemoveBanned(srcAvatar->GetAvatarId(), bannedAvatar->GetAvatarId()); +} + +RemoveFriend::RemoveFriend( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "REMOVEFRIEND request received - removing: " << FromWideString(request.destName) + << "@" << FromWideString(request.destAddress) << " from " << request.srcAvatarId + << "@" << FromWideString(request.srcAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto destAvatar = avatarService_->GetAvatar(request.destName, request.destAddress); + if (!destAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + srcAvatar->RemoveFriend(destAvatar); +} + +RemoveIgnore::RemoveIgnore(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "REMOVEIGNORE request received - removing: " << FromWideString(request.destName) << "@" + << FromWideString(request.destAddress) << " from " << request.srcAvatarId << "@" + << FromWideString(request.srcAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto destAvatar = avatarService_->GetAvatar(request.destName, request.destAddress); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + srcAvatar->RemoveIgnore(destAvatar); +} + +RemoveInvite::RemoveInvite( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "REMOVEINVITE request received - removing invitation for: " + << FromWideString(request.destAvatarName) << "@" + << FromWideString(request.destAvatarAddress) << " to " + << FromWideString(request.destRoomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto invitedAvatar + = avatarService_->GetAvatar(request.destAvatarName, request.destAvatarAddress); + if (!invitedAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + auto room = roomService_->GetRoom(request.destRoomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.destRoomId = room->GetRoomId(); + + room->RemoveInvite(srcAvatar->GetAvatarId(), invitedAvatar->GetAvatarId()); +} + +RemoveModerator::RemoveModerator(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "REMOVEMODERATOR request recieved - removing: " << FromWideString(request.destAvatarName) << "@" + << FromWideString(request.destAvatarAddress) << " from " << FromWideString(request.destRoomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + auto moderatorAvatar = avatarService_->GetAvatar(request.destAvatarName, request.destAvatarAddress); + if (!moderatorAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + auto room = roomService_->GetRoom(request.destRoomAddress); + if (!room) { + throw ChatResultException{ChatResultCode::ADDRESSNOTROOM}; + } + + response.destRoomId = room->GetRoomId(); + + room->RemoveModerator(srcAvatar->GetAvatarId(), moderatorAvatar->GetAvatarId()); +} + +SendInstantMessage::SendInstantMessage( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "SENDINSTANTMESSAGE request received " + << " - from " << request.srcAvatarId << "@" << FromWideString(request.srcAddress) << " to " + << FromWideString(request.destName) << "@" << FromWideString(request.destAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException(ChatResultCode::SRCAVATARDOESNTEXIST); + } + + auto destAvatar = avatarService_->GetAvatar(request.destName, request.destAddress); + if (!destAvatar) { + throw ChatResultException(ChatResultCode::DESTAVATARDOESNTEXIST); + } + + if (destAvatar->IsIgnored(srcAvatar)) { + throw ChatResultException(ChatResultCode::IGNORING); + } + + client->SendInstantMessageUpdate(srcAvatar, destAvatar, request.message, request.oob); +} + +SendPersistentMessage::SendPersistentMessage(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , messageService_{client->GetNode()->GetMessageService()} { + LOG(INFO) << "SENDPERSISTENTMESSAGE request received:"; + + auto destAvatar = avatarService_->GetAvatar(request.destName, request.destAddress); + if (!destAvatar) { + throw ChatResultException{ChatResultCode::DESTAVATARDOESNTEXIST}; + } + + PersistentMessage message; + + if (request.avatarPresence) { + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + if (destAvatar->IsIgnored(srcAvatar)) { + throw ChatResultException(ChatResultCode::IGNORING); + } + + message.header.fromName = srcAvatar->GetName(); + message.header.fromAddress = srcAvatar->GetAddress(); + } else { + message.header.fromName = request.srcName; + message.header.fromAddress = destAvatar->GetAddress(); + } + + message.header.sentTime = static_cast(std::time(nullptr)); + message.header.avatarId = destAvatar->GetAvatarId(); + message.header.subject = request.subject; + message.header.category = request.category; + message.message = request.msg; + message.oob = request.oob; + + messageService_->StoreMessage(message); + + response.messageId = message.header.messageId; + + client->SendPersistentMessageUpdate(destAvatar, message.header); +} + +SendRoomMessage::SendRoomMessage( + GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} + , roomService_{client->GetNode()->GetRoomService()} { + LOG(INFO) << "SENDROOMMESSAGE request received " + << " - from " << request.srcAvatarId << "@" << FromWideString(request.srcAddress) + << " to " << FromWideString(request.destRoomAddress); + + auto srcAvatar = avatarService_->GetAvatar(request.srcAvatarId); + if (!srcAvatar) { + throw ChatResultException(ChatResultCode::SRCAVATARDOESNTEXIST); + } + + auto room = roomService_->GetRoom(request.destRoomAddress); + if (!room) { + throw ChatResultException(ChatResultCode::ADDRESSNOTROOM); + } + + response.roomId = room->GetRoomId(); + + client->SendRoomMessageUpdate( + srcAvatar, room, room->GetNextMessageId(), request.message, request.oob); +} + +SetApiVersion::SetApiVersion( + GatewayClient* client, const RequestType& request, ResponseType& response) { + LOG(INFO) << "SETAPIVERSION request received - version: " << request.version; + response.version = client->GetNode()->GetConfig().version; + response.result = (response.version == request.version) + ? ChatResultCode::SUCCESS + : ChatResultCode::WRONGCHATSERVERFORREQUEST; +} + +SetAvatarAttributes::SetAvatarAttributes(GatewayClient* client, const RequestType& request, ResponseType& response) + : avatarService_{client->GetNode()->GetAvatarService()} { + LOG(INFO) << "SETAVATARATTRIBUTES request received - avatar: " << request.avatarId; + + auto avatar = avatarService_->GetAvatar(request.avatarId); + if (!avatar) { + throw ChatResultException{ChatResultCode::SRCAVATARDOESNTEXIST}; + } + + response.avatar = avatar; + + if (avatar->GetAttributes() != request.avatarAttributes) { + avatar->SetAttributes(request.avatarAttributes); + + if (request.persistent != 0) { + avatarService_->PersistAvatar(avatar); + } + } +} + +UpdatePersistentMessage::UpdatePersistentMessage(GatewayClient* client, const RequestType& request, ResponseType& response) + : messageService_{client->GetNode()->GetMessageService()} { + LOG(INFO) << "UPDATEPERSISTENTMESSAGE request received"; + messageService_->UpdateMessageStatus( + request.srcAvatarId, request.messageId, request.status); +} diff --git a/stationapi/src/stationchat/protocol/RegistrarGetChatServer.hpp b/stationapi/src/stationchat/protocol/RegistrarGetChatServer.hpp new file mode 100755 index 0000000..87a96e7 --- /dev/null +++ b/stationapi/src/stationchat/protocol/RegistrarGetChatServer.hpp @@ -0,0 +1,54 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class RegistrarClient; + +/** Begin REGISTRAR_GETCHATSERVER */ + +struct ReqRegistrarGetChatServer { + const ChatRequestType type = ChatRequestType::REGISTRAR_GETCHATSERVER; + uint32_t track; + std::u16string hostname; + uint16_t port; +}; + +template +void read(StreamT& ar, ReqRegistrarGetChatServer& data) { + read(ar, data.track); + read(ar, data.hostname); + read(ar, data.port); +} + +/** Begin REGISTRAR_GETCHATSERVER */ + +struct ResRegistrarGetChatServer { + ResRegistrarGetChatServer( + uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::REGISTRAR_GETCHATSERVER; + uint32_t track; + ChatResultCode result; + std::u16string hostname; + uint16_t port; +}; + +template +void write(StreamT& ar, const ResRegistrarGetChatServer& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.hostname); + write(ar, data.port); +} + +class RegistrarGetChatServer { +public: + using RequestType = ReqRegistrarGetChatServer; + using ResponseType = ResRegistrarGetChatServer; + + RegistrarGetChatServer(RegistrarClient* client, const RequestType& request, ResponseType& response); +}; diff --git a/stationapi/src/stationchat/protocol/RemoveBan.hpp b/stationapi/src/stationchat/protocol/RemoveBan.hpp new file mode 100755 index 0000000..451db10 --- /dev/null +++ b/stationapi/src/stationchat/protocol/RemoveBan.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin REMOVEBAN */ + +struct ReqRemoveBan { + const ChatRequestType type = ChatRequestType::REMOVEBAN; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destAvatarName; + std::u16string destAvatarAddress; + std::u16string destRoomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqRemoveBan& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destAvatarName); + read(ar, data.destAvatarAddress); + read(ar, data.destRoomAddress); + read(ar, data.srcAddress); +} + +/** Begin REMOVEBAN */ + +struct ResRemoveBan { + explicit ResRemoveBan(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::REMOVEBAN; + uint32_t track; + ChatResultCode result; + uint32_t destRoomId; +}; + +template +void write(StreamT& ar, const ResRemoveBan& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.destRoomId); +} + +class RemoveBan { +public: + using RequestType = ReqRemoveBan; + using ResponseType = ResRemoveBan; + + RemoveBan(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/RemoveFriend.hpp b/stationapi/src/stationchat/protocol/RemoveFriend.hpp new file mode 100755 index 0000000..e5e7ccb --- /dev/null +++ b/stationapi/src/stationchat/protocol/RemoveFriend.hpp @@ -0,0 +1,58 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin REMOVEFRIEND */ + +struct ReqRemoveFriend { + const ChatRequestType type = ChatRequestType::REMOVEFRIEND; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destName; + std::u16string destAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqRemoveFriend& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destName); + read(ar, data.destAddress); + read(ar, data.srcAddress); +} + +/** Begin REMOVEFRIEND */ + +struct ResRemoveFriend { + ResRemoveFriend(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::REMOVEFRIEND; + uint32_t track; + ChatResultCode result; +}; + +template +void write(StreamT& ar, const ResRemoveFriend& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} + +class RemoveFriend { +public: + using RequestType = ReqRemoveFriend; + using ResponseType = ResRemoveFriend; + + RemoveFriend(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; +}; diff --git a/stationapi/src/stationchat/protocol/RemoveIgnore.hpp b/stationapi/src/stationchat/protocol/RemoveIgnore.hpp new file mode 100755 index 0000000..252dc6e --- /dev/null +++ b/stationapi/src/stationchat/protocol/RemoveIgnore.hpp @@ -0,0 +1,58 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin REMOVEIGNORE */ + +struct ReqRemoveIgnore { + const ChatRequestType type = ChatRequestType::REMOVEIGNORE; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destName; + std::u16string destAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqRemoveIgnore& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destName); + read(ar, data.destAddress); + read(ar, data.srcAddress); +} + +/** Begin REMOVEIGNORE */ + +struct ResRemoveIgnore { + ResRemoveIgnore(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::REMOVEIGNORE; + uint32_t track; + ChatResultCode result; +}; + +template +void write(StreamT& ar, const ResRemoveIgnore& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} + +class RemoveIgnore { +public: + using RequestType = ReqRemoveIgnore; + using ResponseType = ResRemoveIgnore; + + RemoveIgnore(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; +}; diff --git a/stationapi/src/stationchat/protocol/RemoveInvite.hpp b/stationapi/src/stationchat/protocol/RemoveInvite.hpp new file mode 100755 index 0000000..d4c789c --- /dev/null +++ b/stationapi/src/stationchat/protocol/RemoveInvite.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin REMOVEINVITE */ + +struct ReqRemoveInvite { + const ChatRequestType type = ChatRequestType::REMOVEINVITE; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destAvatarName; + std::u16string destAvatarAddress; + std::u16string destRoomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqRemoveInvite& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destAvatarName); + read(ar, data.destAvatarAddress); + read(ar, data.destRoomAddress); + read(ar, data.srcAddress); +} + +/** Begin REMOVEINVITE */ + +struct ResRemoveInvite { + explicit ResRemoveInvite(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::REMOVEINVITE; + uint32_t track; + ChatResultCode result; + uint32_t destRoomId; +}; + +template +void write(StreamT& ar, const ResRemoveInvite& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.destRoomId); +} + +class RemoveInvite { +public: + using RequestType = ReqRemoveInvite; + using ResponseType = ResRemoveInvite; + + RemoveInvite(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/RemoveModerator.hpp b/stationapi/src/stationchat/protocol/RemoveModerator.hpp new file mode 100755 index 0000000..1cfc7dc --- /dev/null +++ b/stationapi/src/stationchat/protocol/RemoveModerator.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin REMOVEMODERATOR */ + +struct ReqRemoveModerator { + const ChatRequestType type = ChatRequestType::REMOVEMODERATOR; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destAvatarName; + std::u16string destAvatarAddress; + std::u16string destRoomAddress; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqRemoveModerator& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destAvatarName); + read(ar, data.destAvatarAddress); + read(ar, data.destRoomAddress); + read(ar, data.srcAddress); +} + +/** Begin REMOVEMODERATOR */ + +struct ResRemoveModerator { + ResRemoveModerator(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::REMOVEMODERATOR; + uint32_t track; + ChatResultCode result; + uint32_t destRoomId; +}; + +template +void write(StreamT& ar, const ResRemoveModerator& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.destRoomId); +} + +class RemoveModerator { +public: + using RequestType = ReqRemoveModerator; + using ResponseType = ResRemoveModerator; + + RemoveModerator(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/SendInstantMessage.hpp b/stationapi/src/stationchat/protocol/SendInstantMessage.hpp new file mode 100755 index 0000000..48fb132 --- /dev/null +++ b/stationapi/src/stationchat/protocol/SendInstantMessage.hpp @@ -0,0 +1,62 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin SENDINSTANTMESSAGE */ + +struct ReqSendInstantMessage { + const ChatRequestType type = ChatRequestType::SENDINSTANTMESSAGE; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destName; + std::u16string destAddress; + std::u16string message; + std::u16string oob; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqSendInstantMessage& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destName); + read(ar, data.destAddress); + read(ar, data.message); + read(ar, data.oob); + read(ar, data.srcAddress); +} + +/** Begin SENDINSTANTMESSAGE */ + +struct ResSendInstantMessage { + ResSendInstantMessage(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::SENDINSTANTMESSAGE; + uint32_t track; + ChatResultCode result; +}; + +template +void write(StreamT& ar, const ResSendInstantMessage& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} + +class SendInstantMessage { +public: + using RequestType = ReqSendInstantMessage; + using ResponseType = ResSendInstantMessage; + + SendInstantMessage(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; +}; diff --git a/stationapi/src/stationchat/protocol/SendPersistentMessage.hpp b/stationapi/src/stationchat/protocol/SendPersistentMessage.hpp new file mode 100755 index 0000000..0e6d3bb --- /dev/null +++ b/stationapi/src/stationchat/protocol/SendPersistentMessage.hpp @@ -0,0 +1,83 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class PersistentMessageService; +class GatewayClient; + +/** Begin SENDPERSISTENTMESSAGE */ + +struct ReqSendPersistentMessage { + const ChatRequestType type = ChatRequestType::SENDPERSISTENTMESSAGE; + uint32_t track; + uint16_t avatarPresence; + uint32_t srcAvatarId; + std::u16string srcName; + std::u16string destName; + std::u16string destAddress; + std::u16string subject; + std::u16string msg; + std::u16string oob; + std::u16string category; + bool enforceInboxLimit; + uint32_t categoryLimit; +}; + +template +void read(StreamT& ar, ReqSendPersistentMessage& data) { + read(ar, data.track); + read(ar, data.avatarPresence); + + if (data.avatarPresence) { + read(ar, data.srcAvatarId); + } else { + read(ar, data.srcName); + } + + read(ar, data.destName); + read(ar, data.destAddress); + read(ar, data.subject); + read(ar, data.msg); + read(ar, data.oob); + read(ar, data.category); + read(ar, data.enforceInboxLimit); + read(ar, data.categoryLimit); +} + +/** Begin SENDPERSISTENTMESSAGE */ + +struct ResSendPersistentMessage { + ResSendPersistentMessage(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::SENDPERSISTENTMESSAGE; + uint32_t track; + ChatResultCode result; + uint32_t messageId; +}; + +template +void write(StreamT& ar, const ResSendPersistentMessage& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + if (data.result == ChatResultCode::SUCCESS) { + write(ar, data.messageId); + } +} + +class SendPersistentMessage { +public: + using RequestType = ReqSendPersistentMessage; + using ResponseType = ResSendPersistentMessage; + + SendPersistentMessage(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + PersistentMessageService* messageService_; +}; diff --git a/stationapi/src/stationchat/protocol/SendRoomMessage.hpp b/stationapi/src/stationchat/protocol/SendRoomMessage.hpp new file mode 100755 index 0000000..ce7bfc3 --- /dev/null +++ b/stationapi/src/stationchat/protocol/SendRoomMessage.hpp @@ -0,0 +1,68 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin SENDROOMMESSAGE */ + +struct ReqSendRoomMessage { + const ChatRequestType type = ChatRequestType::SENDROOMMESSAGE; + uint32_t track; + uint32_t srcAvatarId; + std::u16string destRoomAddress; + std::u16string message; + std::u16string oob; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqSendRoomMessage& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.destRoomAddress); + read(ar, data.message); + read(ar, data.oob); + read(ar, data.srcAddress); +} + +/** Begin SENDROOMMESSAGE */ + +struct ResSendRoomMessage { + explicit ResSendRoomMessage(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + ResSendRoomMessage(uint32_t track_, ChatResultCode result_, uint32_t roomId_) + : track{track_} + , result{result_} + , roomId{roomId_} {} + + const ChatResponseType type = ChatResponseType::SENDROOMMESSAGE; + uint32_t track; + ChatResultCode result; + uint32_t roomId; +}; + +template +void write(StreamT& ar, const ResSendRoomMessage& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.roomId); +} + +class SendRoomMessage { +public: + using RequestType = ReqSendRoomMessage; + using ResponseType = ResSendRoomMessage; + + SendRoomMessage(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/SetApiVersion.hpp b/stationapi/src/stationchat/protocol/SetApiVersion.hpp new file mode 100755 index 0000000..cb08a63 --- /dev/null +++ b/stationapi/src/stationchat/protocol/SetApiVersion.hpp @@ -0,0 +1,55 @@ + +#pragma once + +#include "ChatEnums.hpp" + +class ChatAvatarService; +class ChatRoomService; +class GatewayClient; + +/** Begin SETAPIVERSION */ + +struct ReqSetApiVersion { + const ChatRequestType type = ChatRequestType::SETAPIVERSION; + uint32_t track; + uint32_t version; +}; + +template +void read(StreamT& ar, ReqSetApiVersion& data) { + read(ar, data.track); + read(ar, data.version); +} + +/** Begin SETAPIVERSION */ + +struct ResSetApiVersion { + ResSetApiVersion(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::SETAPIVERSION; + uint32_t track; + ChatResultCode result; + uint32_t version; +}; + +template +void write(StreamT& ar, const ResSetApiVersion& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + write(ar, data.version); +} + +class SetApiVersion { +public: + using RequestType = ReqSetApiVersion; + using ResponseType = ResSetApiVersion; + + SetApiVersion(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; + ChatRoomService* roomService_; +}; diff --git a/stationapi/src/stationchat/protocol/SetAvatarAttributes.hpp b/stationapi/src/stationchat/protocol/SetAvatarAttributes.hpp new file mode 100755 index 0000000..98e09de --- /dev/null +++ b/stationapi/src/stationchat/protocol/SetAvatarAttributes.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "ChatAvatar.hpp" +#include "ChatEnums.hpp" + +class ChatAvatarService; +class GatewayClient; + +/** Begin SETAVATARATTRIBUTES */ + +struct ReqSetAvatarAttributes { + const ChatRequestType type = ChatRequestType::SETAVATARATTRIBUTES; + uint32_t track; + uint32_t avatarId; + uint32_t avatarAttributes; + uint32_t persistent; + std::u16string srcAddress; +}; + +template +void read(StreamT& ar, ReqSetAvatarAttributes& data) { + read(ar, data.track); + read(ar, data.avatarId); + read(ar, data.avatarAttributes); + read(ar, data.persistent); + read(ar, data.srcAddress); +} + +/** Begin SETAVATARATTRIBUTES */ + +struct ResSetAvatarAttributes { + ResSetAvatarAttributes(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::SETAVATARATTRIBUTES; + uint32_t track; + ChatResultCode result; + const ChatAvatar* avatar; +}; + +template +void write(StreamT& ar, const ResSetAvatarAttributes& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); + + if (data.result == ChatResultCode::SUCCESS) { + write(ar, data.avatar); + } +} + +class SetAvatarAttributes { +public: + using RequestType = ReqSetAvatarAttributes; + using ResponseType = ResSetAvatarAttributes; + + SetAvatarAttributes(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + ChatAvatarService* avatarService_; +}; diff --git a/stationapi/src/stationchat/protocol/UpdatePersistentMessage.hpp b/stationapi/src/stationchat/protocol/UpdatePersistentMessage.hpp new file mode 100755 index 0000000..7704477 --- /dev/null +++ b/stationapi/src/stationchat/protocol/UpdatePersistentMessage.hpp @@ -0,0 +1,56 @@ + +#pragma once + +#include "ChatEnums.hpp" +#include "PersistentMessage.hpp" + +class PersistentMessageService; +class GatewayClient; + +/** Begin UPDATEPERSISTENTMESSAGE */ + +struct ReqUpdatePersistentMessage { + const ChatRequestType type = ChatRequestType::UPDATEPERSISTENTMESSAGE; + uint32_t track; + uint32_t srcAvatarId; + uint32_t messageId; + PersistentState status; +}; + +template +void read(StreamT& ar, ReqUpdatePersistentMessage& data) { + read(ar, data.track); + read(ar, data.srcAvatarId); + read(ar, data.messageId); + read(ar, data.status); +} + +/** Begin UPDATEPERSISTENTMESSAGE */ + +struct ResUpdatePersistentMessage { + ResUpdatePersistentMessage(uint32_t track_) + : track{track_} + , result{ChatResultCode::SUCCESS} {} + + const ChatResponseType type = ChatResponseType::UPDATEPERSISTENTMESSAGE; + uint32_t track; + ChatResultCode result; +}; + +template +void write(StreamT& ar, const ResUpdatePersistentMessage& data) { + write(ar, data.type); + write(ar, data.track); + write(ar, data.result); +} + +class UpdatePersistentMessage { +public: + using RequestType = ReqUpdatePersistentMessage; + using ResponseType = ResUpdatePersistentMessage; + + UpdatePersistentMessage(GatewayClient* client, const RequestType& request, ResponseType& response); + +private: + PersistentMessageService* messageService_; +}; diff --git a/stationapi/tests/CMakeLists.txt b/stationapi/tests/CMakeLists.txt new file mode 100755 index 0000000..cdf669d --- /dev/null +++ b/stationapi/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(${PROJECT_SOURCE_DIR}/externals/catch + ${PROJECT_SOURCE_DIR}/src) + +add_executable(stationapi_tests + main.cpp) + +target_link_libraries(stationapi_tests + stationapi) diff --git a/stationapi/tests/main.cpp b/stationapi/tests/main.cpp new file mode 100755 index 0000000..063e878 --- /dev/null +++ b/stationapi/tests/main.cpp @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" \ No newline at end of file diff --git a/stationapi/udplibrary/CMakeLists.txt b/stationapi/udplibrary/CMakeLists.txt new file mode 100755 index 0000000..a349881 --- /dev/null +++ b/stationapi/udplibrary/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 2.8) + +project(udplibrary) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_definitions(-DREENTRANT) + +add_library(udplibrary + hashtable.hpp + PointerDeque.hpp + priority.hpp + UdpHandler.hpp + UdpHandler.h + UdpLibrary.cpp + UdpLibrary.hpp + UdpLibrary.h +) + diff --git a/stationapi/udplibrary/PointerDeque.hpp b/stationapi/udplibrary/PointerDeque.hpp new file mode 100755 index 0000000..6124afd --- /dev/null +++ b/stationapi/udplibrary/PointerDeque.hpp @@ -0,0 +1,126 @@ +#ifndef POINTERDEQUE_HPP +#define POINTERDEQUE_HPP + + // This is a simple double ended queue template. Any pointer-type can be stored in this deque + // I pop/peek return NULL if the queue is empty. +template class PointerDeque +{ + public: + PointerDeque(int entriesPerPage); + ~PointerDeque(); + + void PushLeft(T* obj); + T* PopLeft(); + void PushRight(T* obj); + T* PopRight(); + T* PeekLeft(); + T* PeekRight(); + T* Peek(int index); + int Count(); + + protected: + void Expand(); + + int mEntriesPerPage; + T** mEntries; + int mEntriesCount; + int mEntriesMax; + int mOffsetLeft; +}; + + +template PointerDeque::PointerDeque(int entriesPerPage) +{ + mEntriesPerPage = entriesPerPage; + mEntries = NULL; + mOffsetLeft = 0; + mEntriesMax = 0; + mEntriesCount = 0; +} + +template PointerDeque::~PointerDeque() +{ + delete[] mEntries; +} + +template void PointerDeque::Expand() +{ + int countToEnd = (mEntriesMax - mOffsetLeft); + mEntriesMax += mEntriesPerPage; + T** newHold = new T*[mEntriesMax]; + + if (countToEnd < mEntriesCount) + { + memcpy(newHold, &mEntries[mOffsetLeft], countToEnd * sizeof(T*)); + memcpy(newHold + countToEnd, mEntries, (mEntriesCount - countToEnd) * sizeof(T*)); + } + else + memcpy(newHold, &mEntries[mOffsetLeft], mEntriesCount * sizeof(T*)); + + delete[] mEntries; + mEntries = newHold; + mOffsetLeft = 0; +} + +template void PointerDeque::PushLeft(T* obj) +{ + if (mEntriesCount >= mEntriesMax) + Expand(); + mEntries[(mOffsetLeft - 1 + mEntriesMax) % mEntriesMax] = obj; + mEntriesCount++; +} + +template T* PointerDeque::PopLeft() +{ + if (mEntriesCount == 0) + return(NULL); + T* hold = mEntries[mOffsetLeft]; + mOffsetLeft = (mOffsetLeft + 1) % mEntriesMax; + mEntriesCount--; + return(hold); +} + +template void PointerDeque::PushRight(T* obj) +{ + if (mEntriesCount >= mEntriesMax) + Expand(); + mEntries[(mOffsetLeft + mEntriesCount) % mEntriesMax] = obj; + mEntriesCount++; +} + +template T* PointerDeque::PopRight() +{ + if (mEntriesCount == 0) + return(NULL); + mEntriesCount--; + return(mEntries[(mOffsetLeft + mEntriesCount) % mEntriesMax]); +} + +template T* PointerDeque::PeekLeft() +{ + if (mEntriesCount == 0) + return(NULL); + return(mEntries[mOffsetLeft]); +} + +template T* PointerDeque::PeekRight() +{ + if (mEntriesCount == 0) + return(NULL); + return(mEntries[(mOffsetLeft + mEntriesCount - 1) % mEntriesMax]); +} + +template T* PointerDeque::Peek(int index) +{ + if (index >= mEntriesCount) + return(NULL); + return(mEntries[(mOffsetLeft + index) % mEntriesMax]); +} + +template int PointerDeque::Count() +{ + return(mEntriesCount); +} + +#endif + diff --git a/stationapi/udplibrary/UdpHandler.h b/stationapi/udplibrary/UdpHandler.h new file mode 100755 index 0000000..ea2a2ca --- /dev/null +++ b/stationapi/udplibrary/UdpHandler.h @@ -0,0 +1,6 @@ +#ifndef _Network_UdpLibrary_H +#define _Network_UdpLibrary_H + +#include "UdpHandler.hpp" + +#endif//_Network_UdpLibrary_H diff --git a/stationapi/udplibrary/UdpHandler.hpp b/stationapi/udplibrary/UdpHandler.hpp new file mode 100755 index 0000000..d7b3869 --- /dev/null +++ b/stationapi/udplibrary/UdpHandler.hpp @@ -0,0 +1,76 @@ +#ifndef UDPHANDLER_HPP +#define UDPHANDLER_HPP + +class UdpManager; +class UdpConnection; +class LogicalPacket; + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +enum UdpCorruptionReason { cUdpCorruptionNone + , cUdpCorruptionMultiPacket + , cUdpCorruptionReliablePacketTooShort + , cUdpCorruptionInternalPacketTooShort + , cUdpCorruptionDecryptFailed + , cCorruptionReasonZeroLengthPacket + , cCorruptionReasonPacketShorterThanCrcBytes + }; + + + +class UdpManagerHandler +{ + public: + // This function is called when a listening UdpManager receives a new connection request. If the application wishes to + // accept this connection, it is the responsibility of the application to AddRef the UdpConnection object such that it + // doesn't get destroyed upon returning from the callback. Additionally, the application should set the handler object + // for the connection. See sample code in the UdpLibrary.doc file for an example. + virtual void OnConnectRequest(UdpConnection *con); + + // The following functions are called when user-supplied encryption is specified. In particular, the functions may wish + // to query the connection object for the encryption-code it is using. The encryption code is simply a randomly generated + // 32-bit value that the client and server negotiated during the connection-establishment. It can be used as a key + // of sorts for encrypting data uniquely on a per-connection basis. (see UdpConnection::GetEncryptCode()) + virtual int OnUserSuppliedEncrypt(UdpConnection *con, uchar *destData, const uchar *sourceData, int sourceLen); + virtual int OnUserSuppliedDecrypt(UdpConnection *con, uchar *destData, const uchar *sourceData, int sourceLen); + virtual int OnUserSuppliedEncrypt2(UdpConnection *con, uchar *destData, const uchar *sourceData, int sourceLen); + virtual int OnUserSuppliedDecrypt2(UdpConnection *con, uchar *destData, const uchar *sourceData, int sourceLen); +}; + +class UdpConnectionHandler +{ + public: + // This function is called whenever an application-packet is ready to be delivered to the application. + // Packets will only ever be delivered to the application during UdpManager::GiveTime calls, so the application + // has complete control of when it willing/able to receive packets. + virtual void OnRoutePacket(UdpConnection *con, const uchar *data, int dataLen) = 0; + + // This function is called whenever a UdpManager::EstablishConnection call succeeds in establishing the connection. + // Generally on client-side establishment of a connection, I prefer to sit in a tight loop polling the connection + // to see if it has connected yet or not, rather than using this callback (see sample code) + // note: If it is unable to establish the connection, the OnTerminated callback will be called. + // note: this is a change from previous behavior, see release notes for details. + virtual void OnConnectComplete(UdpConnection *con); + + // This function is called anytime the UdpConnection object changes to a cStatusDisconnected state. Every UdpConnection + // object is guaranteed to eventually call this once and only once, even connections that never successfully established. + // note: this is a change from previous behavior, see release notes for details + virtual void OnTerminated(UdpConnection *con); + + // This function is called anytime an incoming packet fails the CRC check. Normally these packets are just ignored, as + // corruption does occur from time to time; however, some applications may wish to log these events as often times, they + // are an indication of somebody attempting to hack the data stream. + virtual void OnCrcReject(UdpConnection *con, const uchar *data, int dataLen); + + // This function is called anytime a corrupt packet is sensed for some reason. + // The reason we give a callback for it is because you may wish to log it as it could be an early indicator of + // somebody trying to hack the protocol. + + virtual void OnPacketCorrupt(UdpConnection *con, const uchar *data, int dataLen, UdpCorruptionReason reason); +}; + +#endif + diff --git a/stationapi/udplibrary/UdpLibrary.cpp b/stationapi/udplibrary/UdpLibrary.cpp new file mode 100755 index 0000000..2ad0dfd --- /dev/null +++ b/stationapi/udplibrary/UdpLibrary.cpp @@ -0,0 +1,4364 @@ +#include +#include +#include +#include +#include + +#include "UdpLibrary.hpp" + +#if defined(WIN32) + #pragma warning(disable : 4710) + #if defined(UDPLIBRARY_WINSOCK2) + #include + #include + #else + #include + #endif + + typedef int socklen_t; +#elif defined(sparc) + #include + #include + #include + #include + #include + #include + #include + #include + #include + + const int INADDR_NONE = -1; + const int INVALID_SOCKET = 0xFFFFFFFF; + const int SOCKET_ERROR = 0xFFFFFFFF; +#else // for non-windows platforms (linux) + #include + #include + #include + #include + #include + #include + #include + #include // needed by gcc 3.1 for linux + const int INVALID_SOCKET = 0xFFFFFFFF; + const int SOCKET_ERROR = 0xFFFFFFFF; +#endif + +template +const ValueType & udpMax(const ValueType & a, const ValueType & b) +{ + if(a < b) + return b; + return a; +} + +template +const ValueType & udpMin(const ValueType & a, const ValueType & b) +{ + if(b < a) + return b; + return a; +} + + ///////////////////////////////////////////////////////////////////////////////////////////// + // operating system dependent initialization routines (internally called when needed) + ///////////////////////////////////////////////////////////////////////////////////////////// +void InitializeOperatingSystem() +{ +#if defined(WIN32) + WSADATA wsaData; + #if defined(UDPLIBRARY_WINSOCK2) + WSAStartup(MAKEWORD(2,2), &wsaData); + #else + WSAStartup(MAKEWORD(1,1), &wsaData); + #endif +#endif +} + +void TerminateOperatingSystem() +{ +#if defined(WIN32) + WSACleanup(); +#endif +} + + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // UdpConnectionHandler default implementation + ///////////////////////////////////////////////////////////////////////////////////////////////////// +void UdpConnectionHandler::OnConnectComplete(UdpConnection * /*con*/) +{ +} + +void UdpConnectionHandler::OnTerminated(UdpConnection * /*con*/) +{ +} + +void UdpConnectionHandler::OnCrcReject(UdpConnection * /*con*/, const uchar * /*data*/, int /*dataLen*/) +{ +} + +void UdpConnectionHandler::OnPacketCorrupt(UdpConnection * /*con*/, const uchar * /*data*/, int /*dataLen*/, UdpCorruptionReason /*reason*/) +{ +} + + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // UdpManagerHandler default implementation + ///////////////////////////////////////////////////////////////////////////////////////////////////// +void UdpManagerHandler::OnConnectRequest(UdpConnection * /*con*/) +{ +} + +int UdpManagerHandler::OnUserSuppliedEncrypt(UdpConnection * /*con*/, uchar *destData, const uchar *sourceData, int sourceLen) +{ + memcpy(destData, sourceData, sourceLen); + return(sourceLen); +} + +int UdpManagerHandler::OnUserSuppliedEncrypt2(UdpConnection * /*con*/, uchar *destData, const uchar *sourceData, int sourceLen) +{ + memcpy(destData, sourceData, sourceLen); + return(sourceLen); +} + +int UdpManagerHandler::OnUserSuppliedDecrypt(UdpConnection * /*con*/, uchar *destData, const uchar *sourceData, int sourceLen) +{ + memcpy(destData, sourceData, sourceLen); + return(sourceLen); +} + +int UdpManagerHandler::OnUserSuppliedDecrypt2(UdpConnection * /*con*/, uchar *destData, const uchar *sourceData, int sourceLen) +{ + memcpy(destData, sourceData, sourceLen); + return(sourceLen); +} + + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // UdpIpAddress implementation + ///////////////////////////////////////////////////////////////////////////////////////////////////// +UdpIpAddress::UdpIpAddress(unsigned int ip) +{ + mIp = ip; +} + +char *UdpIpAddress::GetAddress(char *buffer) const +{ + assert(buffer != NULL); + + struct sockaddr_in addr_serverUDP; + addr_serverUDP.sin_addr.s_addr = mIp; + strcpy(buffer, inet_ntoa(addr_serverUDP.sin_addr)); + return(buffer); +} + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // UdpManager::Params initializations constructor (ie. default values) + ///////////////////////////////////////////////////////////////////////////////////////////////////// +UdpManager::Params::Params() +{ + handler = NULL; + outgoingBufferSize = 64 * 1024; + incomingBufferSize = 64 * 1024; + packetHistoryMax = 100; + maxDataHoldTime = 50; + maxDataHoldSize = -1; + maxRawPacketSize = 512; + hashTableSize = 100; + avoidPriorityQueue = false; + clockSyncDelay = 0; + crcBytes = 0; + encryptMethod[0] = UdpManager::cEncryptMethodNone; + encryptMethod[1] = UdpManager::cEncryptMethodNone; + keepAliveDelay = 0; + portAliveDelay = 0; + noDataTimeout = 0; + maxConnections = 10; + port = 0; + portRange = 0; + pooledPacketMax = 1000; + pooledPacketSize = -1; + pooledPacketInitial = 0; + replyUnreachableConnection = true; + allowPortRemapping = true; + allowAddressRemapping = false; + icmpErrorRetryPeriod = 5000; + oldestUnacknowledgedTimeout = 90000; + processIcmpErrors = true; + processIcmpErrorsDuringNegotiating = false; + connectAttemptDelay = 1000; + reliableOverflowBytes = 0; + memset(bindIpAddress, 0, sizeof(bindIpAddress)); + + userSuppliedEncryptExpansionBytes = 0; + userSuppliedEncryptExpansionBytes2 = 0; + simulateIncomingByteRate = 0; + simulateIncomingLossPercent = 0; + simulateOutgoingByteRate = 0; + simulateOutgoingLossPercent = 0; + simulateDestinationOverloadLevel = 0; + simulateOutgoingOverloadLevel = 0; + + reliable[0].maxInstandingPackets = 400; + reliable[0].maxOutstandingBytes = 200 * 1024; + reliable[0].maxOutstandingPackets = 400; + reliable[0].outOfOrder = false; + reliable[0].processOnSend = false; + reliable[0].coalesce = true; + reliable[0].ackDeduping = true; + reliable[0].fragmentSize = 0; + reliable[0].resendDelayAdjust = 300; + reliable[0].resendDelayPercent = 125; + reliable[0].resendDelayCap = 5000; + reliable[0].congestionWindowMinimum = 0; + reliable[0].trickleRate = 0; + reliable[0].trickleSize = 0; + for (int j = 1; j < cReliableChannelCount; j++) + reliable[j] = reliable[0]; +} + + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // UdpManager implementation + ///////////////////////////////////////////////////////////////////////////////////////////////////// +UdpManager::UdpManager(const UdpManager::Params *params) +{ + assert(params->clockSyncDelay >= 0); // negative clockSyncDelay is not allowed (makes no sense) + assert(params->crcBytes >= 0 && params->crcBytes <= 4); // crc bytes must be between 0 and 4 + assert(params->encryptMethod[0] >= 0 && params->encryptMethod[0] < cEncryptMethodCount); // illegal encryption method specified + assert(params->encryptMethod[1] >= 0 && params->encryptMethod[1] < cEncryptMethodCount); // illegal encryption method specified + assert(params->hashTableSize > 0); // a hash table size greater than zero is required + assert(params->maxRawPacketSize >= 64); // raw packet size must be at least 64 bytes + assert(params->incomingBufferSize >= params->maxRawPacketSize); // incoming socket buffer size must be at least as big as one raw packet + assert(params->keepAliveDelay >= 0); // keep alive delay can't be negative + assert(params->portAliveDelay >= 0); // port alive delay can't be negative + assert(params->maxConnections > 0); // must have at least 1 connection allowed + assert(params->outgoingBufferSize >= params->maxRawPacketSize); // outgoing socket buffer must larger than a raw packet size + assert(params->packetHistoryMax > 0); // packet history must be at least 1 + assert(params->port >= 0); // port cannot be negative + assert(params->userSuppliedEncryptExpansionBytes + params->userSuppliedEncryptExpansionBytes2 < params->maxRawPacketSize); // if encryption expansion is larger than raw packet size, we are screwed + assert(params->reliable[0].maxOutstandingBytes >= params->maxRawPacketSize); + assert(params->reliable[1].maxOutstandingBytes >= params->maxRawPacketSize); + assert(params->reliable[2].maxOutstandingBytes >= params->maxRawPacketSize); + assert(params->reliable[3].maxOutstandingBytes >= params->maxRawPacketSize); + assert(params->port != 0 || params->portRange == 0); + + mRefCount = 1; + mParams = *params; + mParams.maxRawPacketSize = udpMin(mParams.maxRawPacketSize, (int)cHardMaxRawPacketSize); + if (mParams.maxDataHoldSize == -1) + { + mParams.maxDataHoldSize = mParams.maxRawPacketSize; + } + if (mParams.pooledPacketSize == -1) + { + mParams.pooledPacketSize = mParams.maxRawPacketSize; + } + + mParams.maxDataHoldSize = udpMin(mParams.maxDataHoldSize, mParams.maxRawPacketSize); + mParams.packetHistoryMax = udpMax(1, mParams.packetHistoryMax); + mPacketHistoryPosition = 0; + mPassThroughData = NULL; + + typedef PacketHistoryEntry *PacketHistoryEntryPtr; + mPacketHistory = new PacketHistoryEntryPtr[mParams.packetHistoryMax]; + + int i; + for (i = 0; i < mParams.packetHistoryMax; i++) + { + mPacketHistory[i] = new PacketHistoryEntry(mParams.maxRawPacketSize); + } + + ResetStats(); + mLastReceiveTime = 0; + mLastSendTime = 0; + mLastEmptySocketBufferStamp = 0; + mProcessingInducedLag = 0; + mStartTtl = 32; + mMinimumScheduledStamp = 0; + mUdpSocket = INVALID_SOCKET; + + mConnectionListCount = 0; + mConnectionList = NULL; + + mPoolCreated = 0; + mPoolAvailable = 0; + mPoolAvailableRoot = NULL; + mPoolCreatedRoot = NULL; + + mWrappedCreated = 0; + mWrappedAvailable = 0; + mWrappedAvailableRoot = NULL; + mWrappedCreatedRoot = NULL; + + for (i = 0; i < mParams.pooledPacketInitial && i < mParams.pooledPacketMax; i++) + { + mPoolCreated++; + PooledLogicalPacket *lp = new PooledLogicalPacket(this, mParams.pooledPacketSize); + PoolReturn(lp); + lp->Release(); + } + + mSimulateQueueStart = NULL; + mSimulateQueueEnd = NULL; + mSimulateNextOutgoingTime = 0; + mSimulateNextIncomingTime = 0; + mSimulateQueueBytes = 0; + + mDisconnectPendingList = NULL; + + if (mParams.avoidPriorityQueue) + mPriorityQueue = NULL; + else + mPriorityQueue = new PriorityQueue(mParams.maxConnections); + + mAddressHashTable = new ObjectHashTable(mParams.hashTableSize); + mConnectCodeHashTable = new ObjectHashTable(udpMax(mParams.hashTableSize / 5, 10)); // rarely used, so make it a fraction of the main tables size + + InitializeOperatingSystem(); + + if (mParams.portRange == 0) + { + CreateAndBindSocket(mParams.port); + } + else + { + int r = rand() % mParams.portRange; + for (int i = 0; i < mParams.portRange; i++) + { + CreateAndBindSocket(mParams.port + ((r + i) % mParams.portRange)); + if (mErrorCondition != cErrorConditionCouldNotBindSocket) + break; + } + } +} + +UdpManager::~UdpManager() +{ + { + // first we need to tell all the pooled packets we have created that they can no longer check themselves back into use + // when they are released + PooledLogicalPacket *walk = mPoolCreatedRoot; + while (walk != NULL) + { + walk->mUdpManager = NULL; + walk = walk->mCreatedNext; + } + // next release the ones we have in our available pool + walk = mPoolAvailableRoot; + while (walk != NULL) + { + PooledLogicalPacket *hold = walk; + walk = walk->mAvailableNext; + hold->Release(); + } + } + + + { + // next we need to tell all the warpped packets we have created that they can no longer check themselves back into use + // when they are released + WrappedLogicalPacket *walk = mWrappedCreatedRoot; + while (walk != NULL) + { + walk->mUdpManager = NULL; + walk = walk->mCreatedNext; + } + // next release the ones we have in our available pool + walk = mWrappedAvailableRoot; + while (walk != NULL) + { + WrappedLogicalPacket *hold = walk; + walk = walk->mAvailableNext; + hold->Release(); + } + } + + // next thing we must do is tell all the connections to disconnect (which severs their link to this dying manager) + // this has to be done first since they will call back into us and have themselves removed from our connection-list/priority-queue/etc + while (mConnectionList != NULL) + { + mConnectionList->InternalDisconnect(0, UdpConnection::cDisconnectReasonManagerDeleted); + // the above call ended up calling us back and removing them from our connection list, so now mConnectionList is pointing to the next entry + // hopefully the compiler will be smart enough not to over optimize this. + } + + // release any objects that were pending disconnection + while (mDisconnectPendingList != NULL) + { + UdpConnection *next = mDisconnectPendingList->mDisconnectPendingNextConnection; + mDisconnectPendingList->Release(); + mDisconnectPendingList = next; + } + + CloseSocket(); + + TerminateOperatingSystem(); + + delete mAddressHashTable; + delete mConnectCodeHashTable; + delete mPriorityQueue; + for (int i = 0; i < mParams.packetHistoryMax; i++) + { + delete mPacketHistory[i]; + } + delete[] mPacketHistory; + + while (mSimulateQueueStart != NULL) + { + SimulateQueueEntry *entry = mSimulateQueueStart; + mSimulateQueueStart = entry->mNext; + delete entry; + } +} + +void UdpManager::CreateAndBindSocket(int usePort) +{ + CloseSocket(); + mErrorCondition = cErrorConditionNone; + mUdpSocket = socket(PF_INET, SOCK_DGRAM, 0); + if (mUdpSocket != INVALID_SOCKET) + { +#if defined(WIN32) + ulong lb = 1; + int err = ioctlsocket(mUdpSocket, FIONBIO, &lb); + int nb = mParams.outgoingBufferSize; + err = setsockopt(mUdpSocket, SOL_SOCKET, SO_SNDBUF, (char *)&nb, sizeof(nb)); + nb = mParams.incomingBufferSize; + err = setsockopt(mUdpSocket, SOL_SOCKET, SO_RCVBUF, (char *)&nb, sizeof(nb)); + int optLen = sizeof(mStartTtl); + getsockopt(mUdpSocket, IPPROTO_IP, IP_TTL, (char *)&mStartTtl, &optLen); +#elif defined(sparc) + ulong nb = 1; + int err = ioctl(mUdpSocket, FIONBIO, &nb); + assert(err != -1); + nb = udpMin(256 * 1024, mParams.outgoingBufferSize); + err = setsockopt(mUdpSocket, SOL_SOCKET, SO_SNDBUF, &nb, sizeof(nb)); + assert(err == 0); + nb = udpMin(256 * 1024, mParams.incomingBufferSize); + err = setsockopt(mUdpSocket, SOL_SOCKET, SO_RCVBUF, &nb, sizeof(nb)); + assert(err == 0); + + int optLen = sizeof(mStartTtl); + getsockopt(mUdpSocket, IPPROTO_IP, IP_TTL, &mStartTtl, (socklen_t *)&optLen); + + nb = 1; + err = setsockopt(mUdpSocket, SOL_SOCKET, SO_DGRAM_ERRIND, &nb, sizeof(nb)); + assert(err == 0); +#else // linux is to remain the default compile mode + unsigned long nb = 1; + int err = ioctl(mUdpSocket, FIONBIO, &nb); + assert(err != -1); + nb = mParams.outgoingBufferSize; + err = setsockopt(mUdpSocket, SOL_SOCKET, SO_SNDBUF, &nb, sizeof(nb)); + assert(err == 0); + nb = mParams.incomingBufferSize; + err = setsockopt(mUdpSocket, SOL_SOCKET, SO_RCVBUF, &nb, sizeof(nb)); + assert(err == 0); + nb = 0; + err = setsockopt(mUdpSocket, SOL_SOCKET, SO_BSDCOMPAT, &nb, sizeof(nb)); + assert(err == 0); + nb = 1; + err = setsockopt(mUdpSocket, SOL_IP, IP_RECVERR, &nb, sizeof(nb)); + assert(err == 0); + + int optLen = sizeof(mStartTtl); + getsockopt(mUdpSocket, IPPROTO_IP, IP_TTL, &mStartTtl, (socklen_t *)&optLen); +#endif + + // bind it to any address + struct sockaddr_in addr_loc; + addr_loc.sin_family = PF_INET; + addr_loc.sin_port = htons((ushort)usePort); + + addr_loc.sin_addr.s_addr = htonl(INADDR_ANY); + if (mParams.bindIpAddress[0] != 0) + { + unsigned long address = inet_addr(mParams.bindIpAddress); + assert(address != INADDR_NONE); // if this asserts, it means you are trying to explicitly bind to an illegally-formatted IP address. + + if (address != INADDR_NONE) + addr_loc.sin_addr.s_addr = address; // this is already in network order from the call above + } + + if (bind(mUdpSocket, (struct sockaddr *)&addr_loc, sizeof(addr_loc)) != 0) + { + mErrorCondition = cErrorConditionCouldNotBindSocket; + CloseSocket(); + } + } + else + { + mErrorCondition = cErrorConditionCouldNotAllocateSocket; + } +} + +void UdpManager::CloseSocket() +{ + if (mUdpSocket != INVALID_SOCKET) + { +#if defined(WIN32) + closesocket(mUdpSocket); +#else + close(mUdpSocket); +#endif + mUdpSocket = INVALID_SOCKET; + } +} + + +UdpManager::ErrorCondition UdpManager::GetErrorCondition() const +{ + return(mErrorCondition); +} + + +void UdpManager::ProcessDisconnectPending() +{ + UdpConnection *entry = mDisconnectPendingList; + UdpConnection **prev = &mDisconnectPendingList; + while (entry != NULL) + { + if (entry->GetStatus() == UdpConnection::cStatusDisconnected) + { + *prev = entry->mDisconnectPendingNextConnection; + entry->mDisconnectPendingNextConnection = NULL; + entry->Release(); + entry = *prev; + } + else + { + prev = &entry->mDisconnectPendingNextConnection; + entry = entry->mDisconnectPendingNextConnection; + } + } +} + +void UdpManager::RemoveConnection(UdpConnection *con) +{ + assert(con != NULL); // attemped to remove a NULL connection object + + // note: it's a bug to Remove a connection object that is already removed...should never be able to happen. + mConnectionListCount--; + if (con->mPrevConnection != NULL) + con->mPrevConnection->mNextConnection = con->mNextConnection; + if (con->mNextConnection != NULL) + con->mNextConnection->mPrevConnection = con->mPrevConnection; + if (mConnectionList == con) + mConnectionList = con->mNextConnection; + con->mNextConnection = NULL; + con->mPrevConnection = NULL; + if (mPriorityQueue != NULL) + mPriorityQueue->Remove(con); + + mAddressHashTable->Remove(con, AddressHashValue(con->mIp, con->mPort)); + mConnectCodeHashTable->Remove(con, con->mConnectCode); +} + +void UdpManager::AddConnection(UdpConnection *con) +{ + assert(con != NULL); // attemped to add a NULL connection object + + con->mNextConnection = mConnectionList; + con->mPrevConnection = NULL; + if (mConnectionList != NULL) + mConnectionList->mPrevConnection = con; + mConnectionList = con; + mConnectionListCount++; + + mAddressHashTable->Insert(con, AddressHashValue(con->mIp, con->mPort)); + mConnectCodeHashTable->Insert(con, con->mConnectCode); +} + +void UdpManager::FlushAllMultiBuffer() +{ + AddRef(); + UdpConnection *cur = mConnectionList; + while (cur != NULL) + { + cur->FlushMultiBuffer(); + cur = cur->mNextConnection; + } + Release(); +} + +bool UdpManager::GiveTime(int maxPollingTime, bool giveConnectionsTime) +{ + // process incoming raw packets from the port + AddRef(); // keep a reference to ourself in case we callback to the application and the application releases us. + + mManagerStats.iterations++; + + bool found = false; + if (maxPollingTime != 0) + { + UdpMisc::ClockStamp start = UdpMisc::Clock(); + do + { + PacketHistoryEntry *e = ActualReceive(); + + if (e == NULL) + { + mLastEmptySocketBufferStamp = UdpMisc::Clock(); + break; + } + + // if the application takes too long to process packets, or doesn't give the UdpManager frequent enough time via GiveTime + // then it's possible that we will have a clock-sync packet that is sitting in the socket buffer waiting to be processed + // we don't want the applications inability to give us frequent processing time to totally whack up the clock sync stuff + // so we have the clock-sync code ignore clock-sync packets that get stalled in the socket buffer for too long because our + // application is busy processing other packets that were queued before it, or because the application paused for a long + // time before calling GiveTime. + // note: this is intended to prevent cpu induced stalls from causing a sync packet to appear to take longer. For example + // if the player is on a modem, it's possible for the socket-buffer to fill up while the application is stalled and cause the + // the sync-packet to actually get stalled at the terminal buffer on the other end up of the modem. When the application starts + // processing again, it will empty the socket-buffer, but then the get an empty-socket-buffer briefly until the terminal server + // can send the rest of the buffered packets on over. A large client side receive socket buffer may help in this regard. + mProcessingInducedLag = UdpMisc::ClockElapsed(mLastEmptySocketBufferStamp); + found = true; + if (e->mLen > 0) + ProcessRawPacket(e); + } while (UdpMisc::ClockElapsed(start) < maxPollingTime); + + ProcessIcmpErrors(); + } + + if (giveConnectionsTime) + { + if (mPriorityQueue != NULL) + { + // give time to everybody in the priority-queue that needs it + UdpMisc::ClockStamp curPriority = UdpMisc::Clock(); + + // at the time we start processing the priority queue, we should effectively be taking a snap-shot + // of everybody who needs time, before we give anybody time. Otherwise, it is possible that in the + // process of giving one connection time, another connection could get bumped up the queue to the point + // where it needs time now as well (for example, one connection sending another connection data during the + // give time phase). Although very rare, in theory this could result in an infinite loop situation. + // To solve this, we simply set the earliest time period that somebody can schedule for to 1 ms after + // the current time stamp that we are processing, effectively making it impossible for any connection + // to be given time twice in the same interation of the loop below + mMinimumScheduledStamp = curPriority + 1; + + for (;;) + { + UdpConnection *top = mPriorityQueue->TopRemove(curPriority); + if (top == NULL) + break; + top->AddRef(); + top->GiveTime(); + top->Release(); + mManagerStats.priorityQueueProcessed++; + } + mManagerStats.priorityQueuePossible += mConnectionListCount; + } + else + { + // give time to everybody + UdpConnection *cur = mConnectionList; + while (cur != NULL) + { + cur->GiveTime(); + cur = cur->mNextConnection; + } + } + + ProcessDisconnectPending(); + } + + if (mSimulateQueueStart != NULL && UdpMisc::Clock() >= mSimulateNextOutgoingTime) + { + SimulateQueueEntry *entry = mSimulateQueueStart; + mSimulateQueueStart = mSimulateQueueStart->mNext; + mSimulateNextOutgoingTime = UdpMisc::Clock() + (entry->mDataLen * 1000 / mParams.simulateOutgoingByteRate); + ActualSendHelper(entry->mData, entry->mDataLen, entry->mIp, entry->mPort); + + UdpConnection *con = AddressGetConnection(entry->mIp, entry->mPort); + if (con != NULL) + con->mSimulateQueueBytes -= entry->mDataLen; + mSimulateQueueBytes -= entry->mDataLen; + delete entry; + } + + Release(); + return(found); +} + +UdpConnection *UdpManager::EstablishConnection(const char *serverAddress, int serverPort, int timeout) +{ + assert(serverPort != 0); // can't connect to no port + assert(serverAddress != NULL); + assert(serverAddress[0] != 0); + + if (mConnectionListCount >= mParams.maxConnections) + return(NULL); + + // get server address + unsigned long address = inet_addr(serverAddress); + if (address == INADDR_NONE) + { + struct hostent * lphp; + lphp = gethostbyname(serverAddress); + if (lphp == NULL) + return(NULL); + address = ((struct in_addr *)(lphp->h_addr))->s_addr; + } + UdpIpAddress destIp(address); + + // first, see if we already have a connection object managing this ip/port, if we do, then fail + UdpConnection *con = AddressGetConnection(destIp, serverPort); + if (con != NULL) + return(NULL); + return(new UdpConnection(this, destIp, serverPort, timeout)); +} + +void UdpManager::KeepUntilDisconnected(UdpConnection *con) +{ + con->AddRef(); + con->mDisconnectPendingNextConnection = mDisconnectPendingList; + mDisconnectPendingList = con; +} + +void UdpManager::GetStats(UdpManagerStatistics *stats) const +{ + assert(stats != NULL); + *stats = mManagerStats; + stats->poolAvailable = mPoolAvailable; + stats->poolCreated = mPoolCreated; + stats->elapsedTime = UdpMisc::ClockElapsed(mManagerStatsResetTime); +} + +void UdpManager::ResetStats() +{ + mManagerStatsResetTime = UdpMisc::Clock(); + memset(&mManagerStats, 0, sizeof(mManagerStats)); +} + +void UdpManager::DumpPacketHistory(const char *filename) const +{ + assert(filename != NULL); + assert(filename[0] != 0); + FILE *file = fopen(filename, "wt"); + if (file != NULL) + { + // dump history of packets... + for (int i = 0; i < mParams.packetHistoryMax; i++) + { + int pos = (mPacketHistoryPosition + i) % mParams.packetHistoryMax; + + if (mPacketHistory[pos]->mLen > 0) + { + char hold[64]; + uchar *ptr = mPacketHistory[pos]->mBuffer; + fprintf(file, "%16s,%5d %3d: ", mPacketHistory[pos]->mIp.GetAddress(hold), mPacketHistory[pos]->mPort, mPacketHistory[pos]->mLen); + int len = mPacketHistory[pos]->mLen; + while (len-- > 0) + { + fprintf(file, "%02x ", *ptr); + ptr++; + } + fprintf(file, "\n"); + } + } + fclose(file); + } +} + +UdpIpAddress UdpManager::GetLocalIp() const +{ + struct sockaddr_in addr_self; + memset(&addr_self, 0, sizeof(addr_self)); + socklen_t len = sizeof(addr_self); + getsockname(mUdpSocket, (struct sockaddr *)&addr_self, &len); + return(UdpIpAddress(addr_self.sin_addr.s_addr)); +} + +int UdpManager::GetLocalPort() const +{ + struct sockaddr_in addr_self; + memset(&addr_self, 0, sizeof(addr_self)); + socklen_t len = sizeof(addr_self); + getsockname(mUdpSocket, (struct sockaddr *)&addr_self, &len); + return(ntohs(addr_self.sin_port)); +} + +UdpManager::PacketHistoryEntry *UdpManager::ActualReceive() +{ + if (mParams.simulateIncomingByteRate > 0 && UdpMisc::Clock() < mSimulateNextIncomingTime) + return(NULL); + + struct sockaddr_in addr_from; + socklen_t sf = sizeof(addr_from); + int pos = mPacketHistoryPosition; + int res = recvfrom(mUdpSocket, (char *)mPacketHistory[pos]->mBuffer, mParams.maxRawPacketSize, 0, (struct sockaddr *)&addr_from, &sf); + + if (res != SOCKET_ERROR) + { + if (mParams.simulateIncomingLossPercent > 0 && ((rand() % 100) < mParams.simulateIncomingLossPercent)) + return(NULL); // packet, what packet? + + if (mParams.simulateIncomingByteRate > 0) + mSimulateNextIncomingTime = UdpMisc::Clock() + (res * 1000 / mParams.simulateIncomingByteRate); + + mLastReceiveTime = UdpMisc::Clock(); + mPacketHistory[pos]->mLen = res; + mPacketHistory[pos]->mIp = UdpIpAddress(addr_from.sin_addr.s_addr); + mPacketHistory[pos]->mPort = (int)ntohs(addr_from.sin_port); + + mPacketHistoryPosition = (mPacketHistoryPosition + 1) % mParams.packetHistoryMax; + mManagerStats.bytesReceived += res; + mManagerStats.packetsReceived++; + return(mPacketHistory[pos]); + } + else + { +#if defined(WIN32) + // windows is kind enough to put ICMP error packets inline within the stream as errors, so we + // can easily see the errors indicating that the destination address is unreachable for some reason + if (WSAGetLastError() == WSAECONNRESET) + { + UdpIpAddress ip = UdpIpAddress(addr_from.sin_addr.s_addr); + int port = (int)ntohs(addr_from.sin_port); + UdpConnection *con = AddressGetConnection(ip, port); + if (con != NULL) + { + con->AddRef(); + con->PortUnreachable(); + con->Release(); + } + + // in order to get our parent to give us time again to poll another packet, we must return it a packet + // to process. We will do this by sending it an empty packet, which it will simply ignore and call us + // asking for yet another packet. We will enter an empty packet into the packet-history so we can effectively + // see these ICMP packets in the history. + mLastReceiveTime = UdpMisc::Clock(); + mPacketHistory[pos]->mLen = 0; + mPacketHistory[pos]->mIp = UdpIpAddress(addr_from.sin_addr.s_addr); + mPacketHistory[pos]->mPort = (int)ntohs(addr_from.sin_port); + mPacketHistoryPosition = (mPacketHistoryPosition + 1) % mParams.packetHistoryMax; + return(mPacketHistory[pos]); + } +#endif + } + return(NULL); +} + +void UdpManager::ProcessIcmpErrors() +{ +#if defined(WIN32) + // nothing needed for WIN32, it handles these errors inline with standard packets +#elif defined(sparc) + // Just to be bassackwards, solaris handles these errors on a subsequent sendto call +#else + // we can use some ICMP errors to our advantage to more quickly realize that the connection on the other end has disappeared + unsigned char msg_control[1024]; + struct msghdr msgh = {0}; + struct sockaddr_in msg_name; + socklen_t sf = sizeof(msg_name); + msgh.msg_name = &msg_name; + msgh.msg_namelen = sf; + msgh.msg_iov = 0; + msgh.msg_iovlen = 0; + msgh.msg_control = msg_control; + msgh.msg_controllen = sizeof(msg_control); + + int err = recvmsg(mUdpSocket, &msgh, MSG_ERRQUEUE); + if(err != -1) + { + struct cmsghdr * cmsg; + if(CMSG_FIRSTHDR(&msgh)) + { + for(cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg)) + { + if(cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) + { + // HACK! HACK! Can't find any portable definition of sock_extended_err! + unsigned char * errData = (unsigned char *)CMSG_DATA(cmsg); + if(errData[4] == 2) // ICMP origin + { + uchar code = errData[6]; + if(code == ICMP_PORT_UNREACH) + { + UdpIpAddress ip = UdpIpAddress(msg_name.sin_addr.s_addr); + int port = (int)htons(msg_name.sin_port); + UdpConnection *con = AddressGetConnection(ip, port); + if (con != NULL) + { + con->AddRef(); + con->PortUnreachable(); + con->Release(); + } + } + } + } + } + } + else + { + // ancillary data missing + } + } +#endif +} + +void UdpManager::ActualSend(const uchar *data, int dataLen, UdpIpAddress ip, int port) +{ + mLastSendTime = UdpMisc::Clock(); + mManagerStats.bytesSent += dataLen; + mManagerStats.packetsSent++; + + if (mParams.simulateOutgoingByteRate != 0) + { + // simulating outgoing byte-rate, so queue it up for sending later + UdpConnection *con = AddressGetConnection(ip, port); + if (con != NULL) + { + if (mParams.simulateDestinationOverloadLevel > 0 && con->mSimulateQueueBytes + dataLen > mParams.simulateDestinationOverloadLevel) + return; // no room, packet gets lost + } + if (mParams.simulateOutgoingOverloadLevel > 0 && mSimulateQueueBytes + dataLen > mParams.simulateOutgoingOverloadLevel) + return; // no room, packet gets lost + + if (con != NULL) + con->mSimulateQueueBytes += dataLen; + mSimulateQueueBytes += dataLen; + SimulateQueueEntry *entry = new SimulateQueueEntry(data, dataLen, ip, port); + + if (mSimulateQueueStart != NULL) + mSimulateQueueEnd->mNext = entry; + else + mSimulateQueueStart = entry; + mSimulateQueueEnd = entry; + mSimulateQueueEnd->mNext = NULL; + return; + } + ActualSendHelper(data, dataLen, ip, port); +} + +void UdpManager::ActualSendHelper(const uchar *data, int dataLen, UdpIpAddress ip, int port) +{ + if (mParams.simulateOutgoingLossPercent > 0 && ((rand() % 100) < mParams.simulateOutgoingLossPercent)) + return; + struct sockaddr_in addr_dest; + addr_dest.sin_family = PF_INET; + addr_dest.sin_addr.s_addr = ip.GetAddress(); + addr_dest.sin_port = htons((ushort)port); + if (SOCKET_ERROR == sendto(mUdpSocket, (const char *)data, dataLen, 0, (struct sockaddr *)&addr_dest, sizeof(addr_dest))) + { + // error writing to socket, what is the error? +#if defined(sparc) + if(errno == ECONNREFUSED || errno == EHOSTUNREACH) + { + // flag connection to terminate itself for port-unreachable error on next give time + // we need to flag it instead of actually terminating it to prevent callbacks from occuring during application sends + UdpConnection *con = AddressGetConnection(ip, port); + if (con != NULL) + con->FlagPortUnreachable(); + return; + } +#endif + + // error types are OS specific, so unless a particular OS grabs the error and treats it differently just above (as sparc does) + // then we are going to just treat all errors as socket overflows (which we only track for statistical purposes) + mManagerStats.socketOverflowErrors++; + } +} + +void UdpManager::SendPortAlive(UdpIpAddress ip, int port) +{ + uchar buf[2]; + buf[0] = 0; + buf[1] = UdpConnection::cUdpPacketPortAlive; + +#if defined(WIN32) + int val = 5; + setsockopt(mUdpSocket, IPPROTO_IP, IP_TTL, (char *)&val, sizeof(val)); + ActualSendHelper(buf, 2, ip, port); + setsockopt(mUdpSocket, IPPROTO_IP, IP_TTL, (char *)&mStartTtl, sizeof(mStartTtl)); +#else + unsigned long val = 5; + setsockopt(mUdpSocket, IPPROTO_IP, IP_TTL, &val, sizeof(val)); + ActualSendHelper(buf, 2, ip, port); + setsockopt(mUdpSocket, IPPROTO_IP, IP_TTL, &mStartTtl, sizeof(mStartTtl)); +#endif +} + +void UdpManager::ProcessRawPacket(const PacketHistoryEntry *e) +{ + if (e->mBuffer[0] == 0 && e->mBuffer[1] == UdpConnection::cUdpPacketPortAlive) + return; // port-alive packets are not supposed to reach the destination machine, but on the odd chance they do, pretend like they never existed + + UdpConnection *con = AddressGetConnection(e->mIp, e->mPort); + + if (con == NULL) + { + // packet coming from an unknown ip/port + // if it is a connection request packet, then establish a new connection object to reply to it + // connection establish packet must always be at least 6 bytes long as we must have a version number, no matter how it changes + if (e->mBuffer[0] == 0 && e->mBuffer[1] == UdpConnection::cUdpPacketConnect && e->mLen == UdpConnection::cUdpPacketConnectSize) + { + if (mConnectionListCount >= mParams.maxConnections) + return; // can't handle any more connections, so ignore this request entirely + + int protocolVersion = UdpMisc::GetValue32(e->mBuffer + 2); + if (protocolVersion == cProtocolVersion) + { + if (mParams.handler != NULL) + { + UdpConnection *newcon = new UdpConnection(this, e); + mParams.handler->OnConnectRequest(newcon); + if (newcon->GetRefCount() == 1) + { + // we are going to end up destroying this connection when we release it on this next line + // so disconnect it first giving it a reason + newcon->InternalDisconnect(0, UdpConnection::cDisconnectReasonConnectionRefused); + } + newcon->Release(); + } + } + } + else + { + if (mParams.allowPortRemapping) + { + if (e->mBuffer[0] == 0 && e->mBuffer[1] == UdpConnection::cUdpPacketRequestRemap) + { + // ok, we got a packet from somebody, that we don't know who they are, but, it appears they are asking + // for their address/port to be remapped. If we allow port (and/or address) remapping, then go ahead + // an honor their request if possible + uchar *ptr = e->mBuffer + 2; + int connectCode = UdpMisc::GetValue32(ptr); + ptr += 4; + int encryptCode = UdpMisc::GetValue32(ptr); + + UdpConnection *con = ConnectCodeGetConnection(connectCode); + if (con != NULL) + { + if (mParams.allowAddressRemapping || con->mIp == e->mIp) + { + // one final security check to ensure these are really the same connection, compare encryption codes + if (con->mConnectionConfig.encryptCode == encryptCode) + { + // remapping is allowed, remap ourselves to the address of the incoming request + mAddressHashTable->Remove(con, AddressHashValue(con->mIp, con->mPort)); + con->mIp = e->mIp; + con->mPort = e->mPort; + mAddressHashTable->Insert(con, AddressHashValue(con->mIp, con->mPort)); + return; + } + } + } + } + } + + + // got a packet from somebody and we don't know who they are and the packet we got was not a connection request + // just in case they are a previous client who thinks they are still connected, we will send them an internal + // packet telling them that we don't know who they are + if (mParams.replyUnreachableConnection) + { + // do not reply back with unreachable if the packet coming in is a terminate or unreachable packet itself + if (e->mBuffer[0] != 0 || (e->mBuffer[0] == 0 && e->mBuffer[1] != UdpConnection::cUdpPacketUnreachableConnection && e->mBuffer[1] != UdpConnection::cUdpPacketTerminate)) + { + // since we do not have a connection-object associated with this incoming packet, there is no way we could + // encrypt it or add CRC bytes to it, since we have no idea what the other end of the connection is expecting + // in this regard. As such, the UnreachableConnection packet (like the connect and confirm packets) is one + // of those internal packet types that is designated as not being encrypted or CRC'ed. + unsigned char buf[8]; + buf[0] = 0; + buf[1] = UdpConnection::cUdpPacketUnreachableConnection; + ActualSend(buf, 2, e->mIp, e->mPort); + } + } + } + return; + } + + con->AddRef(); + con->ProcessRawPacket(e); + con->Release(); +} + +UdpConnection *UdpManager::AddressGetConnection(UdpIpAddress ip, int port) const +{ + UdpConnection *found = static_cast(mAddressHashTable->FindFirst(AddressHashValue(ip, port))); + while (found != NULL) + { + if (found->mIp == ip && found->mPort == port) + return(found); + found = static_cast(mAddressHashTable->FindNext(found)); + } + return(NULL); +} + +UdpConnection *UdpManager::ConnectCodeGetConnection(int connectCode) const +{ + UdpConnection *found = static_cast(mConnectCodeHashTable->FindFirst(connectCode)); + while (found != NULL) + { + if (found->mConnectCode == connectCode) + return(found); + found = static_cast(mConnectCodeHashTable->FindNext(found)); + } + return(NULL); +} + +WrappedLogicalPacket *UdpManager::WrappedBorrow(const LogicalPacket *lp) +{ + if (mWrappedAvailable > 0) + { + WrappedLogicalPacket *wp = mWrappedAvailableRoot; + mWrappedAvailableRoot = mWrappedAvailableRoot->mAvailableNext; + mWrappedAvailable--; + wp->SetLogicalPacket(lp); + return(wp); + } + else + { + WrappedLogicalPacket *wp = new WrappedLogicalPacket(this); + wp->SetLogicalPacket(lp); + return(wp); + } +} + +void UdpManager::WrappedCreated(WrappedLogicalPacket *wp) +{ + wp->mCreatedNext = mWrappedCreatedRoot; + if (mWrappedCreatedRoot != NULL) + mWrappedCreatedRoot->mCreatedPrev = wp; + mWrappedCreatedRoot = wp; + mWrappedCreated++; +} + +void UdpManager::WrappedDestroyed(WrappedLogicalPacket *wp) +{ + if (wp->mCreatedNext != NULL) + { + wp->mCreatedNext->mCreatedPrev = wp->mCreatedPrev; + } + if (wp->mCreatedPrev != NULL) + { + wp->mCreatedPrev->mCreatedNext = wp->mCreatedNext; + } + else + { + // we are first entry, set root + mWrappedCreatedRoot = wp->mCreatedNext; + } + + wp->mCreatedPrev = NULL; + wp->mCreatedNext = NULL; + wp->mUdpManager = NULL; + mWrappedCreated--; +} + +LogicalPacket *UdpManager::CreatePacket(const void *data, int dataLen, const void *data2, int dataLen2) +{ + if (mParams.pooledPacketMax > 0) + { + int totalLen = dataLen + dataLen2; + if (totalLen <= mParams.pooledPacketSize) + { + if (mPoolAvailable > 0) + { + PooledLogicalPacket *lp = mPoolAvailableRoot; + mPoolAvailableRoot = mPoolAvailableRoot->mAvailableNext; + mPoolAvailable--; + lp->SetData(data, dataLen, data2, dataLen2); + return(lp); + } + else + { + // create a new pooled packet to fulfil request + PooledLogicalPacket *lp = new PooledLogicalPacket(this, mParams.pooledPacketSize); + lp->SetData(data, dataLen, data2, dataLen2); + return(lp); + } + } + } + + return(UdpMisc::CreateQuickLogicalPacket(data, dataLen, data2, dataLen2)); +} + +void UdpManager::PoolCreated(PooledLogicalPacket *packet) +{ + packet->mCreatedNext = mPoolCreatedRoot; + if (mPoolCreatedRoot != NULL) + mPoolCreatedRoot->mCreatedPrev = packet; + mPoolCreatedRoot = packet; + mPoolCreated++; +} + +void UdpManager::PoolDestroyed(PooledLogicalPacket *packet) +{ + if (packet->mCreatedNext != NULL) + { + packet->mCreatedNext->mCreatedPrev = packet->mCreatedPrev; + } + if (packet->mCreatedPrev != NULL) + { + packet->mCreatedPrev->mCreatedNext = packet->mCreatedNext; + } + else + { + // we are first entry, set root + mPoolCreatedRoot = packet->mCreatedNext; + } + + packet->mCreatedPrev = NULL; + packet->mCreatedNext = NULL; + packet->mUdpManager = NULL; + mPoolCreated--; +} + + + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // PacketHistory implementation + ///////////////////////////////////////////////////////////////////////////////////////////////////// +UdpManager::PacketHistoryEntry::PacketHistoryEntry(int maxRawPacketSize) +{ + mBuffer = new uchar[maxRawPacketSize]; + mPort = 0; + mLen = 0; +} + +UdpManager::PacketHistoryEntry::~PacketHistoryEntry() +{ + delete[] mBuffer; +} + + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // UdpConnection implementation + ///////////////////////////////////////////////////////////////////////////////////////////////////// + +UdpConnection::UdpConnection(UdpManager *udpManager, UdpIpAddress destIp, int destPort, int timeout) +{ + // client side initializations + Init(udpManager, destIp, destPort); + + mConnectAttemptTimeout = timeout; + mStatus = cStatusNegotiating; + mConnectCode = (rand() << 16) | rand(); + mUdpManager->AddConnection(this); + + GiveTime(); +} + +UdpConnection::UdpConnection(UdpManager *udpManager, const UdpManager::PacketHistoryEntry *e) +{ + // server side initialization + Init(udpManager, e->mIp, e->mPort); + + mStatus = cStatusConnected; + for (int j = 0; j < UdpManager::cEncryptPasses; j++) + mConnectionConfig.encryptMethod[j] = mUdpManager->mParams.encryptMethod[j]; + mConnectionConfig.crcBytes = mUdpManager->mParams.crcBytes; + mConnectionConfig.maxRawPacketSize = mUdpManager->mParams.maxRawPacketSize; + mConnectionConfig.encryptCode = (rand() << 16) | rand(); + SetupEncryptModel(); + + // steal the connect code out of the packet early such that ProcessRawPacket will think it's a valid connect packet instead of ignoring it + // plus, the AddConnection function needs to know our connect code + mConnectCode = UdpMisc::GetValue32(e->mBuffer + 6); + mUdpManager->AddConnection(this); + + ProcessRawPacket(e); + GiveTime(); +} + +void UdpConnection::Init(UdpManager *udpManager, UdpIpAddress destIp, int destPort) +{ + mRefCount = 1; + mUdpManager = udpManager; + mIp = destIp; + mPort = destPort; + + mFlaggedPortUnreachable = false; + + mLastPortAliveTime = mLastSendTime = 0; // makes it send out the first connect packet immediately (if we are in negotiating mode) + mLastReceiveTime = UdpMisc::Clock(); + mLastClockSyncTime = 0; + mDataHoldTime = 0; + mGettingTime = false; + mHandler = NULL; + + mNoDataTimeout = mUdpManager->mParams.noDataTimeout; + mKeepAliveDelay = mUdpManager->mParams.keepAliveDelay; + + mMultiBufferData = new uchar[mUdpManager->mParams.maxRawPacketSize]; + mMultiBufferPtr = mMultiBufferData; + + mDisconnectPendingNextConnection = NULL; + mNextConnection = NULL; + mPrevConnection = NULL; + mIcmpErrorRetryStartStamp = 0; // when the timer started for ICMP error retry delay (gets reset on a successful packet receive) + mPortRemapRequestStartStamp = 0; + + mEncryptXorBuffer = NULL; + mEncryptExpansionBytes = 0; + mOrderedCountOutgoing = 0; + mOrderedCountOutgoing2 = 0; + mOrderedStampLast = 0; + mOrderedStampLast2 = 0; + mDisconnectReason = cDisconnectReasonNone; + mOtherSideDisconnectReason = cDisconnectReasonNone; + + mConnectionCreateTime = UdpMisc::Clock(); + mSimulateQueueBytes = 0; + mPassThroughData = NULL; + mSilentDisconnect = false; + + mLastSendBin = 0; + mLastReceiveBin = 0; + mOutgoingBytesLastSecond = 0; + mIncomingBytesLastSecond = 0; + memset(mSendBin, 0, sizeof(mSendBin)); + memset(mReceiveBin, 0, sizeof(mReceiveBin)); + + PingStatReset(); + mSyncTimeDelta = 0; + memset(mChannel, 0, sizeof(mChannel)); + memset(&mConnectionStats, 0, sizeof(mConnectionStats)); +} + +UdpConnection::~UdpConnection() +{ + if (mUdpManager != NULL) + InternalDisconnect(0, mDisconnectReason); + + for (int i = 0; i < UdpManager::cReliableChannelCount; i++) + delete mChannel[i]; + delete[] mMultiBufferData; + delete[] mEncryptXorBuffer; +} + +void UdpConnection::PortUnreachable() +{ + if (!mUdpManager->mParams.processIcmpErrors) + return; + + if (!mUdpManager->mParams.processIcmpErrorsDuringNegotiating) + { + if (mStatus == cStatusNegotiating) // during negotiating phase, ignore port unreachable errors, since it may be a case of the client starting up first + return; + } + + if (mUdpManager->mParams.icmpErrorRetryPeriod != 0) + { + if (mIcmpErrorRetryStartStamp == 0) + { + mIcmpErrorRetryStartStamp = UdpMisc::Clock(); // start timer on how long we will ignore ICMP errors + return; + } + + if (UdpMisc::ClockElapsed(mIcmpErrorRetryStartStamp) < mUdpManager->mParams.icmpErrorRetryPeriod) + { + return; // ignoring ICMP errors for a period of time + } + } + + InternalDisconnect(0, cDisconnectReasonIcmpError); +} + +void UdpConnection::InternalDisconnect(int flushTimeout, DisconnectReason reason) +{ + mDisconnectReason = reason; + Status startStatus = mStatus; + UdpManager *startUdpManager = mUdpManager; + + // if we are in a negotiating state, then you can't have a flushTimeout, any disconnect will occur immediately + if (mStatus == cStatusNegotiating) + flushTimeout = 0; + + if (mUdpManager != NULL) + { + if (flushTimeout > 0) + { + FlushMultiBuffer(); + mDisconnectFlushStamp = UdpMisc::Clock(); + mDisconnectFlushTimeout = flushTimeout; + ScheduleTimeNow(); + + if (mStatus != cStatusDisconnectPending) + { + mStatus = cStatusDisconnectPending; + mUdpManager->KeepUntilDisconnected(this); + } + return; + } + + // send a termination packet to the other side + // do not send a termination packet if we are still negotiating (we are not allowed to send any packets while negotiating) + // if you attempt to send a packet while negotiating, then it will potentially attempt to encrypt it before an encryption + // method is determined, resulting in a function call through an invalid pointer + if (!mSilentDisconnect) + { + if (mStatus == cStatusConnected || mStatus == cStatusDisconnectPending) + { + SendTerminatePacket(mConnectCode, mDisconnectReason); + } + } + + mUdpManager->RemoveConnection(this); + mUdpManager = NULL; + } + mStatus = cStatusDisconnected; + + if (startStatus != cStatusDisconnected && startUdpManager != NULL) + { + if (mHandler != NULL) + mHandler->OnTerminated(this); + } +} + +void UdpConnection::SendTerminatePacket(int connectCode, DisconnectReason reason) +{ + uchar buf[256]; + buf[0] = 0; + buf[1] = cUdpPacketTerminate; + UdpMisc::PutValue32(buf + 2, connectCode); + UdpMisc::PutValue16(buf + 6, (ushort)reason); + PhysicalSend(buf, 8, true); +} + + +void UdpConnection::SetSilentDisconnect(bool silent) +{ + // this function tells the connection to disconnect silently, meaning that it should + // not send a packet to the other side telling it that the connection is being terminated + mSilentDisconnect = silent; +} + +bool UdpConnection::Send(UdpChannel channel, const void *data, int dataLen) +{ + assert(dataLen >= 0); + assert(channel >= 0 && channel < cUdpChannelCount); + assert(mStatus != cStatusNegotiating); // you are not allowed to start sending data on a connection that is still in the process of negotiating (only applicable client-side obviously since servers never have connections in this state) + + if (mStatus != cStatusConnected) // if we are no longer connected (not allowed to send more when we are pending disconnect either) + return(false); + if (dataLen == 0) // zero length packets are ignored + return(false); + + assert(data != NULL); // can't send a null packet + + mUdpManager->mManagerStats.applicationPacketsSent++; + mConnectionStats.applicationPacketsSent++; + + // zero-escape application packets that start with 0 + if ((*(const uchar *)data) == 0) + { + uchar hold = 0; + return(InternalSend(channel, &hold, 1, (const uchar *)data, dataLen)); + } + + return(InternalSend(channel, (const uchar *)data, dataLen)); +} + +bool UdpConnection::Send(UdpChannel channel, const LogicalPacket *packet) +{ + assert(packet != NULL); // can't send a null packet + assert(channel >= 0 && channel < cUdpChannelCount); + assert(mStatus != cStatusNegotiating); // you are not allowed to start sending data on a connection that is still in the process of negotiating (only applicable client-side obviously since servers never have connections in this state) + + if (mStatus != cStatusConnected) // if we are no longer connected + return(false); + int dataLen = packet->GetDataLen(); + if (dataLen == 0) + return(false); + + mUdpManager->mManagerStats.applicationPacketsSent++; + mConnectionStats.applicationPacketsSent++; + + // zero-escape application packets that start with 0 + const uchar *data = (const uchar *)packet->GetDataPtr(); + if (!packet->IsInternalPacket() && data[0] == 0) + { + uchar hold = 0; + return(InternalSend(channel, &hold, 1, data, dataLen)); + } + + return(InternalSend(channel, packet)); +} + +bool UdpConnection::InternalSend(UdpChannel channel, const uchar *data, int dataLen, const uchar *data2, int dataLen2) +{ + // promote unreliable packets that are larger than maxRawPacketSize to be reliable + int totalDataLen = dataLen + dataLen2; + + int rawDataBytesMax = (mConnectionConfig.maxRawPacketSize - mConnectionConfig.crcBytes - mEncryptExpansionBytes); + if ((channel == cUdpChannelUnreliable || channel == cUdpChannelUnreliableUnbuffered) && totalDataLen > rawDataBytesMax) + channel = cUdpChannelReliable1; + else if ((channel == cUdpChannelOrdered || channel == cUdpChannelOrderedUnbuffered) && totalDataLen > rawDataBytesMax - cUdpPacketOrderedSize) + channel = cUdpChannelReliable1; + + uchar tempBuffer[UdpManager::cHardMaxRawPacketSize]; + switch(channel) + { + case cUdpChannelUnreliable: + BufferedSend(data, dataLen, data2, dataLen2, false); + return(true); + break; + case cUdpChannelUnreliableUnbuffered: + { + uchar *bufPtr = tempBuffer; + memcpy(bufPtr, data, dataLen); + if (data2 != NULL) + memcpy(bufPtr + dataLen, data2, dataLen2); + PhysicalSend(bufPtr, totalDataLen, true); + return(true); + break; + } + case cUdpChannelOrdered: + { + uchar *bufPtr = tempBuffer; + bufPtr[0] = 0; + bufPtr[1] = cUdpPacketOrdered2; + UdpMisc::PutValue16(bufPtr + 2, (ushort)(++mOrderedCountOutgoing2 & 0xffff)); + memcpy(bufPtr + 4, data, dataLen); + if (data2 != NULL) + memcpy(bufPtr + 4 + dataLen, data2, dataLen2); + BufferedSend(bufPtr, totalDataLen + 4, NULL, 0, true); + return(true); + break; + } + case cUdpChannelOrderedUnbuffered: + { + uchar *bufPtr = tempBuffer; + bufPtr[0] = 0; + bufPtr[1] = cUdpPacketOrdered2; + UdpMisc::PutValue16(bufPtr + 2, (ushort)(++mOrderedCountOutgoing2 & 0xffff)); + memcpy(bufPtr + 4, data, dataLen); + if (data2 != NULL) + memcpy(bufPtr + 4 + dataLen, data2, dataLen2); + PhysicalSend(bufPtr, totalDataLen + 4, true); + return(true); + break; + } + case cUdpChannelReliable1: + case cUdpChannelReliable2: + case cUdpChannelReliable3: + case cUdpChannelReliable4: + { + int num = channel - cUdpChannelReliable1; + if (mChannel[num] == NULL) + mChannel[num] = new UdpReliableChannel(num, this, &mUdpManager->mParams.reliable[num]); + mChannel[num]->Send(data, dataLen, data2, dataLen2); + return(true); + break; + } + default: + break; + } + return(false); +} + +bool UdpConnection::InternalSend(UdpChannel channel, const LogicalPacket *packet) +{ + switch(channel) + { + case cUdpChannelReliable1: + case cUdpChannelReliable2: + case cUdpChannelReliable3: + case cUdpChannelReliable4: + { + int num = channel - cUdpChannelReliable1; + if (mChannel[num] == NULL) + mChannel[num] = new UdpReliableChannel(num, this, &mUdpManager->mParams.reliable[num]); + mChannel[num]->Send(packet); + return(true); + break; + } + default: // unhandled members (cUdpChannelUnreliable, etc...) + { + // 3 April 2002 - jrandall + // moved this from beginning of statement to + // 1) satisfy compiler warnings about unhandled enum member + // 2) avoid the additional branch unnecessarily incurred + // when sending reliable messages + if (channel < cUdpChannelReliable1) // if going unreliably + return(InternalSend(channel, (const uchar *)packet->GetDataPtr(), packet->GetDataLen())); + break; + } + } + return(false); +} + +void UdpConnection::PingStatReset() +{ + mLastClockSyncTime = 0; // tells it to resync the clock pronto + mSyncStatMasterFixupTime = 0; + mSyncStatMasterRoundTime = 0; + mSyncStatLow = 0; + mSyncStatHigh = 0; + mSyncStatLast = 0; + mSyncStatTotal = 0; + mSyncStatCount = 0; + mConnectionStats.averagePingTime = 0; + mConnectionStats.highPingTime = 0; + mConnectionStats.lowPingTime = 0; + mConnectionStats.lastPingTime = 0; + mConnectionStats.masterPingTime = 0; +} + +void UdpConnection::GetStats(UdpConnectionStatistics *cs) const +{ + assert(cs != NULL); + + if (mUdpManager == NULL) + return; + *cs = mConnectionStats; + + if (mUdpManager->mParams.clockSyncDelay == 0) + cs->masterPingAge = -1; + else + cs->masterPingAge = UdpMisc::ClockElapsed(mSyncStatMasterFixupTime); + + cs->percentSentSuccess = 1.0; + cs->percentReceivedSuccess = 1.0; + if (cs->syncOurSent > 0) + cs->percentSentSuccess = (float)cs->syncTheirReceived / (float)cs->syncOurSent; + if (cs->syncTheirSent > 0) + cs->percentReceivedSuccess = (float)cs->syncOurReceived / (float)cs->syncTheirSent; + cs->reliableAveragePing = 0; + if (mChannel[0] != NULL) + cs->reliableAveragePing = mChannel[0]->GetAveragePing(); +} + +void UdpConnection::ProcessRawPacket(const UdpManager::PacketHistoryEntry *e) +{ + if (mUdpManager == NULL) + return; + + if (e->mBuffer[0] != 0 || e->mBuffer[1] != cUdpPacketUnreachableConnection) + { + // if we get any type of packet other than an unreachable-connection packet, then we can assume that our remapping + // request succeeded, and clear the timer for how long we should attempt to do the remapping. The reason we need + // to send requests for a certain amount of time, is the server may already have dozens of unreachable-connection packets + // on the wire on the way to us, before we manage to request that the remapping occur. + mPortRemapRequestStartStamp = 0; + } + + mIcmpErrorRetryStartStamp = 0; // we received a packet successfully, so assume we have recovered from any ICMP error state we may have been in, so we can reset the timer + mLastReceiveTime = UdpMisc::Clock(); + mConnectionStats.totalPacketsReceived++; + mConnectionStats.totalBytesReceived += e->mLen; + + // track incoming data rate + mLastReceiveBin = ExpireReceiveBin(); + mReceiveBin[mLastReceiveBin % cBinCount] += e->mLen; + mIncomingBytesLastSecond += e->mLen; + + if (e->mBuffer[0] == 0 && e->mBuffer[1] == cUdpPacketKeepAlive) + { + // encryption can't mess up the first two bytes of an internal packet, so this is safe to check + // if it is a keep alive packet, then we don't need to do any more processing beyond setting + // the mLastReceiveTime. We do this check here instead of letting it pass on through harmlessly + // like we used to do in order to avoid getting rescheduled in the priority queue. There is absolutely + // no reason to reschedule us due to an incoming keep alive packet since the keep-alive packet has the + // longest rescheduling of anything that needs time, so the worst thing that might happen is we might + // end up getting sheduled time sooner than we might otherwise need to. And obviously scheduling + // ourselves for immediate-time is even sooner than that, so there is no point. + // This turns out to be important for applications that have lots of connections (tens of thousands) + // that rarely talk but send keep alives...no reason to make the server do a lot work over these things. + return; + } + + // whenever we receive a packet, it could potentially change when we want time scheduled again + // so effectively we should reprioritize ourself to the top. By doing it this way instead of + // simply giving time and recalculating, we can effectively avoid giving ourself time and reprioritizing + // ourself over and over again as more and more packets arrive in rapid succession + // note: this cannot happen while we are in our UdpConnection::GiveTime function, so there is no need to squeltch check + // it like we do the others. + // note: this was moved to the top of the function from the bottom. This doesn't effect anything as it doesn't matter + // when we schedule ourself for future processing. Moving it to the top allowed us to get scheduled even if the packet + // we processed got rejected for some reason (crc mismatch or bad size). + ScheduleTimeNow(); + + if (e->mLen < 1) + { + CallbackCorruptPacket(e->mBuffer, e->mLen, cCorruptionReasonZeroLengthPacket); + return; // invalid packet len + } + + // first see if we are a special connect/confirm/unreachable packet, if so, process us immediately + if (IsNonEncryptPacket(e->mBuffer)) + { + ProcessCookedPacket(e->mBuffer, e->mLen); + } + else + { + // if we are still awaiting confirmation packet, then we must ignore any other incoming data packets + // this can happen if the confirm packet is lost and the server has dumped a load of data on the newly created connection + if (mStatus == cStatusNegotiating) + return; + + uchar *finalStart = e->mBuffer; + int finalLen = e->mLen; + + if (mConnectionConfig.crcBytes > 0) + { + if (finalLen < mConnectionConfig.crcBytes) + { + CallbackCorruptPacket(e->mBuffer, e->mLen, cCorruptionReasonPacketShorterThanCrcBytes); + return; // invalid packet len + } + + uchar *crcPtr = finalStart + (finalLen - mConnectionConfig.crcBytes); + int actualCrc = UdpMisc::Crc32(finalStart, finalLen - mConnectionConfig.crcBytes, mConnectionConfig.encryptCode); + int wantCrc = 0; + switch(mConnectionConfig.crcBytes) + { + case 1: + wantCrc = *crcPtr; + actualCrc &= 0xff; + break; + case 2: + wantCrc = UdpMisc::GetValue16(crcPtr); + actualCrc &= 0xffff; + break; + case 3: + wantCrc = UdpMisc::GetValue24(crcPtr); + actualCrc &= 0xffffff; + break; + case 4: + wantCrc = UdpMisc::GetValue32(crcPtr); + break; + } + if (wantCrc != actualCrc) + { + mConnectionStats.crcRejectedPackets++; + mUdpManager->mManagerStats.crcRejectedPackets++; + if (mHandler != NULL) + mHandler->OnCrcReject(this, e->mBuffer, e->mLen); + return; + } + finalLen -= mConnectionConfig.crcBytes; + } + + uchar tempDecryptBuffer[2][UdpManager::cHardMaxRawPacketSize]; + + for (int j = UdpManager::cEncryptPasses - 1; j >= 0; j--) + { + if (mConnectionConfig.encryptMethod[j] != UdpManager::cEncryptMethodNone) + { + // connect/confirm/unreachable packets are not encrypted, other packets are encrypted from the second or third byte on as appropriate + uchar *decryptPtr = tempDecryptBuffer[j % 2]; + *decryptPtr++ = finalStart[0]; + + if (finalStart[0] == 0) + { + if (finalLen < 2) + { + CallbackCorruptPacket(e->mBuffer, e->mLen, cUdpCorruptionInternalPacketTooShort); + return; // invalid packet len + } + + *decryptPtr++ = finalStart[1]; + int len = (this->*(mDecryptFunction[j]))(decryptPtr, finalStart + 2, finalLen - 2); + if (len == -1) + { + CallbackCorruptPacket(e->mBuffer, e->mLen, cUdpCorruptionDecryptFailed); + return; // decrypt failed, throw away packet + } + decryptPtr += len; + } + else + { + int len = (this->*(mDecryptFunction[j]))(decryptPtr, finalStart + 1, finalLen - 1); + if (len == -1) + { + CallbackCorruptPacket(e->mBuffer, e->mLen, cUdpCorruptionDecryptFailed); + return; // decrypt failed, throw away packet + } + decryptPtr += len; + } + + finalStart = tempDecryptBuffer[j % 2]; + finalLen = decryptPtr - finalStart; + } + } + + ProcessCookedPacket(finalStart, finalLen); + } +} + +void UdpConnection::CallbackRoutePacket(const uchar *data, int dataLen) +{ + if (mStatus == cStatusConnected) + { + mUdpManager->mManagerStats.applicationPacketsReceived++; + mConnectionStats.applicationPacketsReceived++; + + if (mHandler != NULL) + mHandler->OnRoutePacket(this, data, dataLen); + } +} + +void UdpConnection::CallbackCorruptPacket(const uchar *data, int dataLen, UdpCorruptionReason reason) +{ + mConnectionStats.corruptPacketErrors++; + mUdpManager->mManagerStats.corruptPacketErrors++; + if (mHandler != NULL) + mHandler->OnPacketCorrupt(this, data, dataLen, reason); +} + +void UdpConnection::ProcessCookedPacket(const uchar *data, int dataLen) +{ + uchar buf[256]; + uchar *bufPtr; + if (mUdpManager == NULL) + return; + + if (data[0] == 0 && dataLen > 1) + { + // internal packet, so process it internally + switch(data[1]) + { + case cUdpPacketConnect: + { + int connectCode = UdpMisc::GetValue32(data + 6); + + if (mStatus == cStatusNegotiating) + { + // why are we receiving a connect-request coming from the guy we ourselves are currently + // in the process of trying to connect to? Odds are very high that what is actually + // happening is we are trying to connect to ourself. In either case, we should reply + // back telling them they are terminated. + if (connectCode == mConnectCode) + SendTerminatePacket(connectCode, cDisconnectReasonConnectingToSelf); + else + SendTerminatePacket(connectCode, cDisconnectReasonMutualConnectError); + return; + } + + if (connectCode == mConnectCode) + { + mConnectionConfig.maxRawPacketSize = udpMin((int)UdpMisc::GetValue32(data + 10), mConnectionConfig.maxRawPacketSize); + + // send confirm packet (if our connect code matches up) + // prepare UdpPacketConnect packet + bufPtr = buf; + *bufPtr++ = 0; + *bufPtr++ = cUdpPacketConfirm; + bufPtr += UdpMisc::PutValue32(bufPtr, mConnectCode); + bufPtr += UdpMisc::PutValue32(bufPtr, mConnectionConfig.encryptCode); + *bufPtr++ = (uchar)mConnectionConfig.crcBytes; + for (int j = 0; j < UdpManager::cEncryptPasses; j++) + *bufPtr++ = (uchar)mConnectionConfig.encryptMethod[j]; + bufPtr += UdpMisc::PutValue32(bufPtr, mConnectionConfig.maxRawPacketSize); + RawSend(buf, bufPtr - buf); + } + else + { + // ok, we got a connect-request packet from the ip/port of something we thought we already had a connection to. + // Additionally, the connect-request packet has a different code, meaning it is not just a stragling connect-request + // packet that got sent after we accepted the connection. + // This means that the other side has probably terminated the connection and is attempting to connect again. + // if we just ignore the new connect-request, it will actually result in the new connection-attempt effectively + // keeping this connection object alive. So, instead, when we get this situation, we will terminate this connection + // and ignore the connect-request packet. The connect-request packet will be sent again 1 second later by the client + // at which time we won't exist and out UdpManager will establish a new connection object for it. + InternalDisconnect(0, cDisconnectReasonNewConnectionAttempt); + return; + } + break; + } + case cUdpPacketConfirm: + { + // unpack UdpPacketConfirm packet + Configuration config; + int connectCode = UdpMisc::GetValue32(data + 2); + config.encryptCode = UdpMisc::GetValue32(data + 6); + config.crcBytes = *(data + 10); + for (int j = 0; j < UdpManager::cEncryptPasses; j++) + config.encryptMethod[j] = (UdpManager::EncryptMethod)*(data + 11 + j); + config.maxRawPacketSize = UdpMisc::GetValue32(data + 11 + UdpManager::cEncryptPasses); + + if (mStatus == cStatusNegotiating && mConnectCode == connectCode) + { + mConnectionConfig = config; + SetupEncryptModel(); + mStatus = cStatusConnected; + if (mHandler != NULL) + mHandler->OnConnectComplete(this); + } + break; + } + case cUdpPacketRequestRemap: + { + // if a request remap packet managed to get routed to our connection, it is because + // the mapping is already correct, so we can just ignore this packet at this point + // this will happen when the client sends multiple remap-requests, the first one will + // cause the actual remapping to occur, and the subsequent ones will manage to make + // it into here + break; + } + case cUdpPacketZeroEscape: + { + CallbackRoutePacket(data + 1, dataLen - 1); + break; + } + case cUdpPacketOrdered: + { + ushort orderedStamp = UdpMisc::GetValue16(data + 2); + int diff = (int)orderedStamp - (int)mOrderedStampLast; + if (diff <= 0) // equal here makes it strip dupes too + diff += 0x10000; + if (diff < 30000) + { + mOrderedStampLast = orderedStamp; + CallbackRoutePacket(data + cUdpPacketOrderedSize, dataLen - cUdpPacketOrderedSize); + } + else + { + mConnectionStats.orderRejectedPackets++; + mUdpManager->mManagerStats.orderRejectedPackets++; + } + break; + } + case cUdpPacketOrdered2: + { + ushort orderedStamp = UdpMisc::GetValue16(data + 2); + int diff = (int)orderedStamp - (int)mOrderedStampLast2; + if (diff <= 0) // equal here makes it strip dupes too + diff += 0x10000; + if (diff < 30000) + { + mOrderedStampLast2 = orderedStamp; + CallbackRoutePacket(data + cUdpPacketOrderedSize, dataLen - cUdpPacketOrderedSize); + } + else + { + mConnectionStats.orderRejectedPackets++; + mUdpManager->mManagerStats.orderRejectedPackets++; + } + break; + } + case cUdpPacketTerminate: + { + int connectCode = UdpMisc::GetValue32(data + 2); + if (dataLen >= 8) // to remain protocol compatible with previous version, the other side disconnect reason is an optional field on this packet + { + mOtherSideDisconnectReason = (DisconnectReason)UdpMisc::GetValue16(data + 6); + } + + if (mConnectCode == connectCode) + { + // since other side explicitly told us they had terminated, there is no reason for us to send a terminate + // packet back to them as well (as it will almost always result in some for of unreachable-destination reply) + // so, put ourselves in silent-disconnect mode when this happens + SetSilentDisconnect(true); + InternalDisconnect(0, cDisconnectReasonOtherSideTerminated); + return; + } + break; + } + case cUdpPacketUnreachableConnection: + { + if (mUdpManager->mParams.allowPortRemapping) + { + if (mPortRemapRequestStartStamp == 0) + { + mPortRemapRequestStartStamp = UdpMisc::Clock(); + } + + enum { cMaximumTimeAllowedForPortRemapping = 5000 }; + if (UdpMisc::ClockElapsed(mPortRemapRequestStartStamp) < cMaximumTimeAllowedForPortRemapping) + { + bufPtr = buf; + *bufPtr++ = 0; + *bufPtr++ = cUdpPacketRequestRemap; + bufPtr += UdpMisc::PutValue32(bufPtr, mConnectCode); + bufPtr += UdpMisc::PutValue32(bufPtr, mConnectionConfig.encryptCode); + RawSend(buf, bufPtr - buf); // since the destination doesn't have an associated connection for us to decrypt us, we must be sent unencrypted + break; + } + } + + InternalDisconnect(0, cDisconnectReasonUnreachableConnection); + return; + break; + } + case cUdpPacketMulti: + { + const uchar *ptr = data + 2; + const uchar *endPtr = data + dataLen; + while (ptr < endPtr) + { + int len = *(const uchar *)ptr++; + const uchar *nextPtr = ptr + len; + if (nextPtr > endPtr) + { + // multi-packet lengths didn't properly add up to total packet length + // meaning we likely got a corrupt packet. If you have CRC bytes enabled, it seems + // quite unlikely this could ever occur. Odds are it has happened because the application + // (while processing this packet) ended up touching the packet-data and corrupting the next + // packet in the multi-sequence. + CallbackCorruptPacket(data, dataLen, cUdpCorruptionMultiPacket); + } + else + { + ProcessCookedPacket(ptr, len); + } + ptr = nextPtr; + } + break; + } + case cUdpPacketClockSync: + { + if (mUdpManager->mProcessingInducedLag > 1000) // if it has been over a second since our manager got processing time, then we should ignore clock-sync packets as we will have introduced too much lag ourselves. + break; + + // unpacket UdpPacketClockSync packet + UdpPacketClockSync pp; + pp.zeroByte = *data; + pp.packetType = *(data + 1); + pp.timeStamp = UdpMisc::GetValue16(data + 2); + pp.masterPingTime = UdpMisc::GetValue32(data + 4); + pp.averagePingTime = UdpMisc::GetValue32(data + 8); + pp.lowPingTime = UdpMisc::GetValue32(data + 12); + pp.highPingTime = UdpMisc::GetValue32(data + 16); + pp.lastPingTime = UdpMisc::GetValue32(data + 20); + pp.ourSent = UdpMisc::GetValue64(data + 24); + pp.ourReceived = UdpMisc::GetValue64(data + 32); + + // prepare UdpPacketClockReflect packet + bufPtr = buf; + *bufPtr++ = 0; + *bufPtr++ = cUdpPacketClockReflect; + bufPtr += UdpMisc::PutValue16(bufPtr, pp.timeStamp); // timeStamp + bufPtr += UdpMisc::PutValue32(bufPtr, UdpMisc::LocalSyncStampLong()); // serverSyncStampLong + bufPtr += UdpMisc::PutValue64(bufPtr, pp.ourSent); // yourSent + bufPtr += UdpMisc::PutValue64(bufPtr, pp.ourReceived); // yourReceived + bufPtr += UdpMisc::PutValue64(bufPtr, mConnectionStats.totalPacketsSent); // ourSent + bufPtr += UdpMisc::PutValue64(bufPtr, mConnectionStats.totalPacketsReceived); // ourReceived + PhysicalSend(buf, bufPtr - buf, true); + + mConnectionStats.averagePingTime = pp.averagePingTime; + mConnectionStats.highPingTime = pp.highPingTime; + mConnectionStats.lowPingTime = pp.lowPingTime; + mConnectionStats.lastPingTime = pp.lastPingTime; + mConnectionStats.masterPingTime = pp.masterPingTime; + mConnectionStats.syncOurReceived = mConnectionStats.totalPacketsReceived; + mConnectionStats.syncOurSent = mConnectionStats.totalPacketsSent - 1; // minus 1 since we should not count the packet we just sent + mConnectionStats.syncTheirReceived = pp.ourReceived; + mConnectionStats.syncTheirSent = pp.ourSent; + break; + } + case cUdpPacketClockReflect: + { + if (mUdpManager->mProcessingInducedLag > 1000) // if it has been over a second since our manager got processing time, then we should ignore clock-sync packets as we will have introduced too much lag ourselves. + break; + + UdpPacketClockReflect pp; + pp.zeroByte = *data; + pp.packetType = *(data + 1); + pp.timeStamp = UdpMisc::GetValue16(data + 2); + pp.serverSyncStampLong = UdpMisc::GetValue32(data + 4); + pp.yourSent = UdpMisc::GetValue64(data + 8); + pp.yourReceived = UdpMisc::GetValue64(data + 16); + pp.ourSent = UdpMisc::GetValue64(data + 24); + pp.ourReceived = UdpMisc::GetValue64(data + 32); + + ushort curStamp = UdpMisc::LocalSyncStampShort(); + int roundTime = UdpMisc::SyncStampShortDeltaTime(pp.timeStamp, curStamp); + + mSyncStatCount++; + mSyncStatTotal += roundTime; + if (mSyncStatLow == 0 || roundTime < mSyncStatLow) + mSyncStatLow = roundTime; + if (roundTime > mSyncStatHigh) + mSyncStatHigh = roundTime; + mSyncStatLast = roundTime; + + // see if we should use this sync to reset the master sync time + // if have better (or close to better) round time or it has been a while + int elapsed = UdpMisc::ClockElapsed(mSyncStatMasterFixupTime); + if (roundTime <= mSyncStatMasterRoundTime + 20 || elapsed > 120000) + { + // resync on this packet unless this packet is a real loser (unless it just been a very long time, then sync up anyhow) + if (roundTime < mSyncStatMasterRoundTime * 2 || elapsed > 240000) + { + mSyncTimeDelta = (pp.serverSyncStampLong - UdpMisc::LocalSyncStampLong()) + (uint)(roundTime / 2); + mSyncStatMasterFixupTime = UdpMisc::Clock(); + mSyncStatMasterRoundTime = roundTime; + } + } + + // update connection statistics + mConnectionStats.averagePingTime = (mSyncStatCount > 0) ? (mSyncStatTotal / mSyncStatCount) : 0; + mConnectionStats.highPingTime = mSyncStatHigh; + mConnectionStats.lowPingTime = mSyncStatLow; + mConnectionStats.lastPingTime = roundTime; + mConnectionStats.masterPingTime = mSyncStatMasterRoundTime; + mConnectionStats.syncOurReceived = pp.yourReceived; + mConnectionStats.syncOurSent = pp.yourSent; + mConnectionStats.syncTheirReceived = pp.ourReceived; + mConnectionStats.syncTheirSent = pp.ourSent; + break; + } + case cUdpPacketKeepAlive: + break; + case cUdpPacketReliable1: + case cUdpPacketReliable2: + case cUdpPacketReliable3: + case cUdpPacketReliable4: + case cUdpPacketFragment1: + case cUdpPacketFragment2: + case cUdpPacketFragment3: + case cUdpPacketFragment4: + { + int num = (data[1] - cUdpPacketReliable1) % UdpManager::cReliableChannelCount; + if (mChannel[num] == NULL) + mChannel[num] = new UdpReliableChannel(num, this, &mUdpManager->mParams.reliable[num]); + mChannel[num]->ReliablePacket(data, dataLen); + break; + } + case cUdpPacketAck1: + case cUdpPacketAck2: + case cUdpPacketAck3: + case cUdpPacketAck4: + { + int num = data[1] - cUdpPacketAck1; + if (mChannel[num] != NULL) + mChannel[num]->AckPacket(data, dataLen); + break; + } + case cUdpPacketAckAll1: + case cUdpPacketAckAll2: + case cUdpPacketAckAll3: + case cUdpPacketAckAll4: + { + int num = data[1] - cUdpPacketAckAll1; + if (mChannel[num] != NULL) + mChannel[num]->AckAllPacket(data, dataLen); + break; + } + case cUdpPacketGroup: + { + const uchar *ptr = data + 2; + const uchar *endPtr = data + dataLen; + while (ptr < endPtr) + { + uint len; + ptr += UdpMisc::GetVariableValue(ptr, &len); + ProcessCookedPacket(ptr, len); + ptr += len; + } + break; + } + } + } + else + { + CallbackRoutePacket(data, dataLen); + } +} + +void UdpConnection::FlushChannels() +{ + AddRef(); // in case application tries to delete us during this give time (could only occur due to a ConnectComplete callback timeout) + GiveTime(); // gives our reliable channels time to send any data recently added to their queues. Reschedules us as well, which is ok. + FlushMultiBuffer(); + Release(); +} + +void UdpConnection::FlagPortUnreachable() +{ + mFlaggedPortUnreachable = true; +} + +void UdpConnection::GiveTime() +{ + if (mUdpManager == NULL) + return; + UdpManager *myManager = mUdpManager; + + myManager->AddRef(); // hold a reference to the UdpManager so it doesn't disappear while we are inside our GiveTime + mGettingTime = true; // lets the internal code know we are in the process of getting time. We do this so when actual packets are sent while we are getting time, we don't reprioritize ourselves to 0 + + InternalGiveTime(); + + mGettingTime = false; + myManager->Release(); +} + +void UdpConnection::InternalGiveTime() +{ + uchar buf[256]; + uchar *bufPtr; + + int nextSchedule = 10 * 60 * 1000; // give us time in 10 minutes (unless somebody wants it sooner) + mConnectionStats.iterations++; + + if (mFlaggedPortUnreachable) + { + mFlaggedPortUnreachable = false; + PortUnreachable(); + } + + switch(mStatus) + { + case cStatusNegotiating: + { + if (mConnectAttemptTimeout > 0 && ConnectionAge() > mConnectAttemptTimeout) + { + InternalDisconnect(0, cDisconnectReasonConnectFail); + return; + break; + } + + if (UdpMisc::ClockElapsed(mLastSendTime) >= mUdpManager->mParams.connectAttemptDelay) + { + // prepare UdpPacketConnect packet + bufPtr = buf; + *bufPtr++ = 0; + *bufPtr++ = cUdpPacketConnect; + bufPtr += UdpMisc::PutValue32(bufPtr, UdpManager::cProtocolVersion); + bufPtr += UdpMisc::PutValue32(bufPtr, mConnectCode); + bufPtr += UdpMisc::PutValue32(bufPtr, mUdpManager->mParams.maxRawPacketSize); + RawSend(buf, bufPtr - buf); + nextSchedule = udpMin(nextSchedule, mUdpManager->mParams.connectAttemptDelay); + } + break; + } + case cStatusConnected: + case cStatusDisconnectPending: + { + // sync clock if required + if (mUdpManager->mParams.clockSyncDelay > 0) + { + // sync periodically. If our current master round time is very bad, then sync more frequently (this is important to quickly get a sync up and running) + int elapsed = UdpMisc::ClockElapsed(mLastClockSyncTime); + if (elapsed > mUdpManager->mParams.clockSyncDelay + || (mSyncStatMasterRoundTime > 3000 && elapsed > 2000) + || (mSyncStatMasterRoundTime > 1000 && elapsed > 5000) + || (mSyncStatCount < 2 && elapsed > 10000)) + { + // send a clock-sync packet + int averagePing = (mSyncStatCount > 0) ? (mSyncStatTotal / mSyncStatCount) : 0; + + bufPtr = buf; + *bufPtr++ = 0; + *bufPtr++ = cUdpPacketClockSync; + bufPtr += UdpMisc::PutValue16(bufPtr, UdpMisc::LocalSyncStampShort()); // timeStamp + bufPtr += UdpMisc::PutValue32(bufPtr, mSyncStatMasterRoundTime); // masterPingTime + bufPtr += UdpMisc::PutValue32(bufPtr, averagePing); // averagePingTime + bufPtr += UdpMisc::PutValue32(bufPtr, mSyncStatLow); // lowPingTime + bufPtr += UdpMisc::PutValue32(bufPtr, mSyncStatHigh); // highPingTime + bufPtr += UdpMisc::PutValue32(bufPtr, mSyncStatLast); // lastPingTime + bufPtr += UdpMisc::PutValue64(bufPtr, mConnectionStats.totalPacketsSent + 1); // ourSent (add 1 to include this packet we are about to send since other side will count it as received before getting it) + bufPtr += UdpMisc::PutValue64(bufPtr, mConnectionStats.totalPacketsReceived); // ourReceived + PhysicalSend(buf, bufPtr - buf, true); // don't buffer this, we need it to be as timely as possible, it still needs to be encrypted though, so don't raw send it. + + mLastClockSyncTime = UdpMisc::Clock(); + elapsed = 0; + } + + // schedule us next time for a clock-sync packet + nextSchedule = udpMin(nextSchedule, mUdpManager->mParams.clockSyncDelay - elapsed); + } + + // give reliable channels processing time and see when they want more time + int totalPendingBytes = 0; + for (int i = 0; i < UdpManager::cReliableChannelCount; i++) + { + if (mChannel[i] != NULL) + { + totalPendingBytes += mChannel[i]->TotalPendingBytes(); + int myNext = mChannel[i]->GiveTime(); + if (mUdpManager == NULL) + return; // giving the reliable channel time caused it to callback the application which may disconnect us + nextSchedule = udpMin(nextSchedule, myNext); + } + } + + if (mUdpManager->mParams.reliableOverflowBytes != 0 && totalPendingBytes >= mUdpManager->mParams.reliableOverflowBytes) + { + InternalDisconnect(0, cDisconnectReasonReliableOverflow); + return; + } + + // if we have multi-buffer data + if (mMultiBufferPtr - mMultiBufferData > 2) + { + int elapsed = UdpMisc::ClockElapsed(mDataHoldTime); + if (elapsed >= mUdpManager->mParams.maxDataHoldTime) + FlushMultiBuffer(); // having just sent it, there is no data in the buffer so no reason to adjust the schedule for when it may be needed again + else + nextSchedule = udpMin(nextSchedule, mUdpManager->mParams.maxDataHoldTime - elapsed); // schedule us processing time for when it does need to be sent + } + + // see if we need to keep connection alive + int elapsed = UdpMisc::ClockElapsed(mLastSendTime); + if (mKeepAliveDelay > 0) + { + if (elapsed >= mKeepAliveDelay) + { + // send keep-alive packet + bufPtr = buf; + *bufPtr++ = 0; + *bufPtr++ = cUdpPacketKeepAlive; + PhysicalSend(buf, bufPtr - buf, true); + elapsed = 0; + } + + // schedule us next time for a keep-alive packet + nextSchedule = udpMin(nextSchedule, mKeepAliveDelay - elapsed); + } + + // see if we need to keep the port alive + if (mUdpManager->mParams.portAliveDelay > 0) + { + int portElapsed = UdpMisc::ClockElapsed(mLastPortAliveTime); + if (portElapsed >= mUdpManager->mParams.portAliveDelay) + { + mLastPortAliveTime = UdpMisc::Clock(); + mUdpManager->SendPortAlive(mIp, mPort); + portElapsed = 0; + } + + // schedule us next time for a keep-alive packet + nextSchedule = udpMin(nextSchedule, mUdpManager->mParams.portAliveDelay - portElapsed); + } + + if (mStatus == cStatusDisconnectPending) + { + int timeLeft = mDisconnectFlushTimeout - UdpMisc::ClockElapsed(mDisconnectFlushStamp); + if (timeLeft < 0 || TotalPendingBytes() == 0) + { + InternalDisconnect(0, mDisconnectReason); + return; + } + else + { + nextSchedule = udpMin(nextSchedule, timeLeft); + } + } + + if (mNoDataTimeout > 0) + { + int lrt = LastReceive(); + if (lrt >= mNoDataTimeout) + { + InternalDisconnect(0, cDisconnectReasonTimeout); + return; + } + else + { + nextSchedule = udpMin(nextSchedule, mNoDataTimeout - lrt); + } + } + + break; + } + default: + break; + } + + if (mUdpManager != NULL) + { + // safety to prevent us for scheduling ourselves for a time period that has already passed, + // as doing so could result in infinite looping in the priority queue processing. + // in theory this cannot happen, I should likely assert here just to make sure... + if (nextSchedule < 0) + nextSchedule = 0; + + mUdpManager->SetPriority(this, UdpMisc::Clock() + nextSchedule + 5); // add 5ms to ensure that we are indeed slightly past the scheduled time + } +} + +int UdpConnection::TotalPendingBytes() const +{ + int total = 0; + for (int i = 0; i < UdpManager::cReliableChannelCount; i++) + { + if (mChannel[i] != NULL) + total += mChannel[i]->TotalPendingBytes(); + } + return(total); +} + +void UdpConnection::RawSend(const uchar *data, int dataLen) +{ + // raw send resets last send time, so we need to potentially recalculate when we need time again + // sends the actual physical packet (usually just after it has be prepped by PacketSend, but for connect/confirm/unreachable packets are bypass that step) + mUdpManager->ActualSend(data, dataLen, mIp, mPort); + mConnectionStats.totalPacketsSent++; + mConnectionStats.totalBytesSent += dataLen; + mLastPortAliveTime = mLastSendTime = UdpMisc::Clock(); + + // track data rate + mLastSendBin = ExpireSendBin(); + mSendBin[mLastSendBin % cBinCount] += dataLen; + mOutgoingBytesLastSecond += dataLen; + ScheduleTimeNow(); +} + +int UdpConnection::ExpireSendBin() +{ + int curBin = abs((int)(UdpMisc::Clock() / cBinResolution)); + int binDiff = curBin - mLastSendBin; + if (binDiff > cBinCount) + { + memset(mSendBin, 0, sizeof(mSendBin)); + mOutgoingBytesLastSecond = 0; + } + else + { + for (int i = 0; i < binDiff; i++) + { + int clearBin = (curBin + i) % cBinCount; + mOutgoingBytesLastSecond -= mSendBin[clearBin]; + mSendBin[clearBin] = 0; + } + } + return(curBin); +} + +int UdpConnection::ExpireReceiveBin() +{ + int curBin = abs((int)(UdpMisc::Clock() / cBinResolution)); + int binDiff = curBin - mLastReceiveBin; + if (binDiff > cBinCount) + { + memset(mReceiveBin, 0, sizeof(mReceiveBin)); + mIncomingBytesLastSecond = 0; + } + else + { + for (int i = 0; i < binDiff; i++) + { + int clearBin = (curBin + i) % cBinCount; + mIncomingBytesLastSecond -= mReceiveBin[clearBin]; + mReceiveBin[clearBin] = 0; + } + } + return(curBin); +} + +void UdpConnection::PhysicalSend(const uchar *data, int dataLen, bool appendAllowed) +{ + if (mUdpManager == NULL) + return; + + // if we attempt to do a physical send (ie. encrypt/compress/crc a packet) while we are not connected + // (especially if we are cStatusNegotiating), then it will potentially crash, because in the case of + // cStatusNegotiating, we don't have the encryption method function pointer initialized yet, as the method + // is part of the negotiations + if (mStatus != cStatusConnected && mStatus != cStatusDisconnectPending) + return; + + // this is physical packet send routine that compressed, encrypts the packet, and adds crc bytes to it as appropriate + // no need to make sure we don't encrypt a connect/confirm/unreachable packet because those go directly to RawSend. + uchar tempEncryptBuffer[2][UdpManager::cHardMaxRawPacketSize + sizeof(int)]; + const uchar *finalStart = data; + int finalLen = dataLen; + for (int j = 0; j < UdpManager::cEncryptPasses; j++) + { + if (mConnectionConfig.encryptMethod[j] != UdpManager::cEncryptMethodNone) + { + uchar *destStart = tempEncryptBuffer[j % 2]; + *(int *)(destStart + finalLen + mEncryptExpansionBytes) = (int)0xcececece; // overwrite debug signature + + uchar *destPtr = destStart; + *destPtr++ = finalStart[0]; + if (finalStart[0] == 0) + { + // we know this internal packet will not be a connect or confirm packet since they are sent directly to RawSend to avoid getting encrypted + *destPtr++ = finalStart[1]; + int len = (this->*(mEncryptFunction[j]))(destPtr, finalStart + 2, finalLen - 2); + + // if this assert triggers, it means the encryption pass expanded the size of the encrypted + // data more than was specified by the userSuppliedEncryptExpansionBytes setting, or at least + // tampered with the destination buffer past that length. This is considered a buffer overwrite + // and will potentially cause bugs. + assert(*(int *)(destStart + finalLen + mEncryptExpansionBytes) == (int)0xcececece); + + if (len == -1) + return; // would be really odd for encryption to return an error, but if it does, throw it away + destPtr += len; + } + else + { + int len = (this->*(mEncryptFunction[j]))(destPtr, finalStart + 1, finalLen - 1); + + // if this assert triggers, it means the encryption pass expanded the size of the encrypted + // data more than was specified by the userSuppliedEncryptExpansionBytes setting, or at least + // tampered with the destination buffer past that length. This is considered a buffer overwrite + // and will potentially cause bugs. + assert(*(int *)(destStart + finalLen + mEncryptExpansionBytes) == (int)0xcececece); + + if (len == -1) + return; // would be really odd for encryption to return an error, but if it does, throw it away + destPtr += len; + } + + finalStart = destStart; + finalLen = destPtr - finalStart; + appendAllowed = true; + } + } + + if (mConnectionConfig.crcBytes > 0) + { + if (!appendAllowed) + { + // if the buffer we are going to append onto was our original (ie. no encryption took place) + // then we have to copy it all over to a temp buffer since we can't modify the original + memcpy(tempEncryptBuffer[0], finalStart, finalLen); + finalStart = tempEncryptBuffer[0]; + } + + int crc = UdpMisc::Crc32(finalStart, finalLen, mConnectionConfig.encryptCode); + uchar *crcPtr = const_cast(finalStart) + finalLen; // safe cast, since we make a copy of the data above if we would have ended up appending to the original + switch(mConnectionConfig.crcBytes) + { + case 1: + *crcPtr = (uchar)(crc & 0xff); + break; + case 2: + UdpMisc::PutValue16(crcPtr, (ushort)(crc & 0xffff)); + break; + case 3: + UdpMisc::PutValue24(crcPtr, crc & 0xffffff); + break; + case 4: + UdpMisc::PutValue32(crcPtr, crc); + break; + } + finalLen += mConnectionConfig.crcBytes; + } + + RawSend(finalStart, finalLen); +} + + // returns where it placed the data in the buffer (if it ended up in the buffer), such that the InternalAckSend + // function can do its job +uchar *UdpConnection::BufferedSend(const uchar *data, int dataLen, const uchar *data2, int dataLen2, bool appendAllowed) +{ + if (mUdpManager == NULL) + return(NULL); + int used = mMultiBufferPtr - mMultiBufferData; + + int actualMaxDataHoldSize = udpMin(mUdpManager->mParams.maxDataHoldSize, mConnectionConfig.maxRawPacketSize); + + int totalDataLen = dataLen + dataLen2; + if (totalDataLen > 255 || (totalDataLen + 3) > actualMaxDataHoldSize) + { + // too long of data to even attempt a multi-buffer of this packet, so let's just send it unbuffered + // but first, to ensure the packet-order integrity is somewhat maintained, flush the multi-buffer + // if it currently has something in it + if (used > 2) + FlushMultiBuffer(); + + // now send it (the multi-buffer is empty if you need to use it temporarily to concatenate two data chunks -- it is large enough to hold the largest raw packet) + if (data2 != NULL) + { + memcpy(mMultiBufferData, data, dataLen); + memcpy(mMultiBufferData + dataLen, data2, dataLen2); + PhysicalSend(mMultiBufferData, totalDataLen, true); + } + else + PhysicalSend(data, dataLen, appendAllowed); + return(NULL); + } + + // if this data will not fit into buffer + // note: we allow the multi-packet to grow as large as maxRawPacketSize, but down below we will flush it + // as soon as it gets larger than maxDataHoldSize. + if (used + totalDataLen + 1 > (mConnectionConfig.maxRawPacketSize - mConnectionConfig.crcBytes - mEncryptExpansionBytes)) + { + FlushMultiBuffer(); + used = 0; + } + + // add data to buffer + if (used == 0) + { + // no buffered data yet, create multi-packet header + *mMultiBufferPtr++ = 0; + *mMultiBufferPtr++ = cUdpPacketMulti; + + // new multi-buffer started, so we need to potentially recalculate when we need time again + mDataHoldTime = UdpMisc::Clock(); // set data hold time to when the first piece of data is stuck in the multi-buffer + ScheduleTimeNow(); + } + + *(uchar *)mMultiBufferPtr++ = (uchar)totalDataLen; + uchar *placementPtr = mMultiBufferPtr; + memcpy(mMultiBufferPtr, data, dataLen); + mMultiBufferPtr += dataLen; + if (data2 != NULL) + { + memcpy(mMultiBufferPtr, data2, dataLen2); + mMultiBufferPtr += dataLen2; + } + + if ((mMultiBufferPtr - mMultiBufferData) >= actualMaxDataHoldSize) + { + FlushMultiBuffer(); + placementPtr = NULL; // it got flushed + } + return(placementPtr); +} + +uchar *UdpConnection::InternalAckSend(uchar *bufferedAckPtr, const uchar *ackPtr, int ackLen) +{ + if (bufferedAckPtr != NULL) + { + memcpy(bufferedAckPtr, ackPtr, ackLen); + return(bufferedAckPtr); + } + + BufferedSend(ackPtr, ackLen, NULL, 0, false); + return(NULL); // FIX THIS +} + +void UdpConnection::FlushMultiBuffer() +{ + int len = mMultiBufferPtr - mMultiBufferData; + if (len > 2) + { + if ((int)((uchar)mMultiBufferData[2]) + 3 == len) + PhysicalSend(mMultiBufferData + 3, len - 3, true); // only one packet so don't send it as a multi-packet + else + PhysicalSend(mMultiBufferData, len, true); + + // notify all the reliable channels to clear their buffered acks + for (int i = 0; i < UdpManager::cReliableChannelCount; i++) + { + if (mChannel[i] != NULL) + { + mChannel[i]->ClearBufferedAck(); + } + } + + } + mMultiBufferPtr = mMultiBufferData; +} + +int UdpConnection::EncryptNone(uchar *destData, const uchar *sourceData, int sourceLen) +{ + memcpy(destData, sourceData, sourceLen); + return(sourceLen); +} + +int UdpConnection::DecryptNone(uchar *destData, const uchar *sourceData, int sourceLen) +{ + memcpy(destData, sourceData, sourceLen); + return(sourceLen); +} + +int UdpConnection::EncryptUserSupplied(uchar *destData, const uchar *sourceData, int sourceLen) +{ + UdpManagerHandler *manHandler = mUdpManager->GetHandler(); + if (manHandler != NULL) + return(manHandler->OnUserSuppliedEncrypt(this, destData, sourceData, sourceLen)); + assert(0); // if user-supplied encryption is specified, then you must have a manager handler installed to provide the routines + return(0); +} + +int UdpConnection::DecryptUserSupplied(uchar *destData, const uchar *sourceData, int sourceLen) +{ + UdpManagerHandler *manHandler = mUdpManager->GetHandler(); + if (manHandler != NULL) + return(manHandler->OnUserSuppliedDecrypt(this, destData, sourceData, sourceLen)); + assert(0); // if user-supplied encryption is specified, then you must have a manager handler installed to provide the routines + return(0); +} + +int UdpConnection::EncryptUserSupplied2(uchar *destData, const uchar *sourceData, int sourceLen) +{ + UdpManagerHandler *manHandler = mUdpManager->GetHandler(); + if (manHandler != NULL) + return(manHandler->OnUserSuppliedEncrypt2(this, destData, sourceData, sourceLen)); + assert(0); // if user-supplied encryption is specified, then you must have a manager handler installed to provide the routines + return(0); +} + +int UdpConnection::DecryptUserSupplied2(uchar *destData, const uchar *sourceData, int sourceLen) +{ + UdpManagerHandler *manHandler = mUdpManager->GetHandler(); + if (manHandler != NULL) + return(manHandler->OnUserSuppliedDecrypt2(this, destData, sourceData, sourceLen)); + assert(0); // if user-supplied encryption is specified, then you must have a manager handler installed to provide the routines + return(0); +} + +int UdpConnection::EncryptXorBuffer(uchar *destData, const uchar *sourceData, int sourceLen) +{ + uchar *destPtr = destData; + const uchar *walkPtr = sourceData; + const uchar *endPtr = sourceData + sourceLen; + uchar *encryptPtr = mEncryptXorBuffer; + int prev = mConnectionConfig.encryptCode; + while ((walkPtr + sizeof(int)) <= endPtr) + { + *(int *)destPtr = *(const int *)walkPtr ^ *(int *)encryptPtr ^ prev; + prev = *(int *)destPtr; + walkPtr += sizeof(int); + destPtr += sizeof(int); + encryptPtr += sizeof(int); + } + + while (walkPtr != endPtr) + { + *destPtr = (uchar)(*walkPtr ^ *encryptPtr); + destPtr++; + walkPtr++; + encryptPtr++; + } + return(sourceLen); +} + +int UdpConnection::DecryptXorBuffer(uchar *destData, const uchar *sourceData, int sourceLen) +{ + const uchar *walkPtr = sourceData; + const uchar *endPtr = sourceData + sourceLen; + uchar *encryptPtr = mEncryptXorBuffer; + uchar *destPtr = destData; + int hold; + int prev = mConnectionConfig.encryptCode; + while ((walkPtr + sizeof(int)) <= endPtr) + { + hold = *(const int *)walkPtr; + *(int *)destPtr = *(const int *)walkPtr ^ prev ^ *(int *)encryptPtr; + prev = hold; + walkPtr += sizeof(int); + destPtr += sizeof(int); + encryptPtr += sizeof(int); + } + + while (walkPtr != endPtr) + { + *destPtr = (uchar)(*walkPtr ^ *encryptPtr); + walkPtr++; + destPtr++; + encryptPtr++; + } + return(sourceLen); +} + +int UdpConnection::EncryptXor(uchar *destData, const uchar *sourceData, int sourceLen) +{ + uchar *destPtr = destData; + const uchar *walkPtr = sourceData; + const uchar *endPtr = sourceData + sourceLen; + int prev = mConnectionConfig.encryptCode; + while ((walkPtr + sizeof(int)) <= endPtr) + { + *(int *)destPtr = *(const int *)walkPtr ^ prev; + prev = *(int *)destPtr; + walkPtr += sizeof(int); + destPtr += sizeof(int); + } + + while (walkPtr != endPtr) + { + *destPtr = (uchar)(*walkPtr ^ prev); + destPtr++; + walkPtr++; + } + return(sourceLen); +} + +int UdpConnection::DecryptXor(uchar *destData, const uchar *sourceData, int sourceLen) +{ + const uchar *walkPtr = sourceData; + const uchar *endPtr = sourceData + sourceLen; + uchar *destPtr = destData; + int hold; + int prev = mConnectionConfig.encryptCode; + while ((walkPtr + sizeof(int)) <= endPtr) + { + hold = *(const int *)walkPtr; + *(int *)destPtr = *(const int *)walkPtr ^ prev; + prev = hold; + walkPtr += sizeof(int); + destPtr += sizeof(int); + } + + while (walkPtr != endPtr) + { + *destPtr = (uchar)(*walkPtr ^ prev); + walkPtr++; + destPtr++; + } + return(sourceLen); +} + +void UdpConnection::SetupEncryptModel() +{ + mEncryptExpansionBytes = 0; + for (int j = 0; j < UdpManager::cEncryptPasses; j++) + { + switch(mConnectionConfig.encryptMethod[j]) + { + default: + assert(0); // unknown encryption method specified during in UdpManager construction + break; + case UdpManager::cEncryptMethodNone: + { + // point to method functions + mDecryptFunction[j] = &UdpConnection::DecryptNone; + mEncryptFunction[j] = &UdpConnection::EncryptNone; + mEncryptExpansionBytes += 0; + break; + } + case UdpManager::cEncryptMethodUserSupplied: + { + // point to method functions + mDecryptFunction[j] = &UdpConnection::DecryptUserSupplied; + mEncryptFunction[j] = &UdpConnection::EncryptUserSupplied; + mEncryptExpansionBytes += mUdpManager->mParams.userSuppliedEncryptExpansionBytes; + break; + } + case UdpManager::cEncryptMethodUserSupplied2: + { + // point to method functions + mDecryptFunction[j] = &UdpConnection::DecryptUserSupplied2; + mEncryptFunction[j] = &UdpConnection::EncryptUserSupplied2; + mEncryptExpansionBytes += mUdpManager->mParams.userSuppliedEncryptExpansionBytes2; + break; + } + case UdpManager::cEncryptMethodXorBuffer: + { + // point to method functions + mDecryptFunction[j] = &UdpConnection::DecryptXorBuffer; + mEncryptFunction[j] = &UdpConnection::EncryptXorBuffer; + mEncryptExpansionBytes += 0; + + // set up encrypt buffer (random numbers generated based on seed) + if (mEncryptXorBuffer == NULL) + { + int len = ((mUdpManager->mParams.maxRawPacketSize + 1) / 4) * 4; + mEncryptXorBuffer = new uchar[len]; + int seed = mConnectionConfig.encryptCode; + uchar *sptr = mEncryptXorBuffer; + for (int i = 0; i < len; i++) + *sptr++ = (uchar)(UdpMisc::Random(&seed) & 0xff); + } + break; + } + case UdpManager::cEncryptMethodXor: + { + // point to method functions + mDecryptFunction[j] = &UdpConnection::DecryptXor; + mEncryptFunction[j] = &UdpConnection::EncryptXor; + mEncryptExpansionBytes += 0; + break; + } + } + } +} + +void UdpConnection::GetChannelStatus(UdpChannel channel, ChannelStatus *channelStatus) const +{ + memset(channelStatus, 0, sizeof(*channelStatus)); + switch (channel) + { + case cUdpChannelReliable1: + case cUdpChannelReliable2: + case cUdpChannelReliable3: + case cUdpChannelReliable4: + if (mChannel[channel - cUdpChannelReliable1] != NULL) + { + mChannel[channel - cUdpChannelReliable1]->GetChannelStatus(channelStatus); + } + break; + default: + break; + } +} + +const char *UdpConnection::DisconnectReasonText(DisconnectReason reason) +{ + static bool sInitialized = false; + static char *sDisconnectReason[cDisconnectReasonCount]; + + if (!sInitialized) + { + sInitialized = true; + memset(sDisconnectReason, 0, sizeof(sDisconnectReason)); + sDisconnectReason[cDisconnectReasonNone] = "DisconnectReasonNone"; + sDisconnectReason[cDisconnectReasonIcmpError] = "DisconnectReasonIcmpError"; + sDisconnectReason[cDisconnectReasonTimeout] = "DisconnectReasonTimeout"; + sDisconnectReason[cDisconnectReasonOtherSideTerminated] = "DisconnectReasonOtherSideTerminated"; + sDisconnectReason[cDisconnectReasonManagerDeleted] = "DisconnectReasonManagerDeleted"; + sDisconnectReason[cDisconnectReasonConnectFail] = "DisconnectReasonConnectFail"; + sDisconnectReason[cDisconnectReasonApplication] = "DisconnectReasonApplication"; + sDisconnectReason[cDisconnectReasonUnreachableConnection] = "DisconnectReasonUnreachableConnection"; + sDisconnectReason[cDisconnectReasonUnacknowledgedTimeout] = "DisconnectReasonUnacknowledgedTimeout"; + sDisconnectReason[cDisconnectReasonNewConnectionAttempt] = "DisconnectReasonNewConnectionAttempt"; + sDisconnectReason[cDisconnectReasonConnectionRefused] = "DisconnectReasonConnectionRefused"; + sDisconnectReason[cDisconnectReasonMutualConnectError] = "DisconnectReasonConnectError"; + sDisconnectReason[cDisconnectReasonConnectingToSelf] = "DisconnectReasonConnectingToSelf"; + sDisconnectReason[cDisconnectReasonReliableOverflow] = "DisconnectReasonReliableOverflow"; + } + + return(sDisconnectReason[reason]); +} + + + + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // ReliableChannel implementation + ///////////////////////////////////////////////////////////////////////////////////////////////////// +UdpReliableChannel::UdpReliableChannel(int channelNumber, UdpConnection *con, UdpManager::ReliableConfig *config) +{ + mUdpConnection = con; + mChannelNumber = channelNumber; + mConfig = *config; + mConfig.maxOutstandingPackets = udpMin(mConfig.maxOutstandingPackets, (int)UdpManager::cHardMaxOutstandingPackets); + + mAveragePingTime = 800; // start out fairly high so we don't do a lot of resends on a bad connection to start out with + mTrickleLastSend = 0; + + int fragmentSize = mConfig.fragmentSize; + if (fragmentSize == 0 || fragmentSize > mUdpConnection->mConnectionConfig.maxRawPacketSize) + fragmentSize = mUdpConnection->mConnectionConfig.maxRawPacketSize; + mMaxDataBytes = (fragmentSize - UdpConnection::cUdpPacketReliableSize - mUdpConnection->mConnectionConfig.crcBytes - mUdpConnection->mEncryptExpansionBytes); + assert(mMaxDataBytes > 0); // fragment size/max-raw-packet-size set too small to allow for reliable deliver + if (mConfig.trickleSize != 0) + mMaxDataBytes = udpMin(mMaxDataBytes, mConfig.trickleSize); + + mMaxCoalesceAttemptBytes = -1; + if (mConfig.coalesce) + { + mMaxCoalesceAttemptBytes = mMaxDataBytes - 5; // 2 bytes for group-header, 3 bytes for length of packet + } + + mReliableIncomingId = 0; + mReliableOutgoingId = 0; + mReliableOutgoingPendingId = 0; + mReliableOutgoingBytes = 0; + + mLogicalPacketsQueued = 0; + mLogicalBytesQueued = 0; + + mCoalescePacket = NULL; + mCoalesceStartPtr = NULL; + mCoalesceEndPtr = NULL; + mCoalesceCount = 0; + + mBufferedAckPtr = NULL; + + mStatDuplicatePacketsReceived = 0; + mStatResentPacketsAccelerated = 0; + mStatResentPacketsTimedOut = 0; + + mCongestionWindowMinimum = udpMax(mMaxDataBytes, mConfig.congestionWindowMinimum); + mCongestionWindowStart = udpMin(4 * mMaxDataBytes, udpMax(2 * mMaxDataBytes, 4380)); + mCongestionWindowStart = udpMax(mCongestionWindowStart, mCongestionWindowMinimum); + mCongestionSlowStartThreshhold = udpMin(mConfig.maxOutstandingPackets * mMaxDataBytes, mConfig.maxOutstandingBytes); + mCongestionWindowLargest = mCongestionWindowSize = mCongestionWindowStart; + + mBigDataLen = 0; + mBigDataTargetLen = 0; + mBigDataPtr = NULL; + mFragmentNextPos = 0; + mLastTimeStampAcknowledged = 0; + mMaxxedOutCurrentWindow = false; + mNextNeedTime = 0; + + mPhysicalPackets = new PhysicalPacket[mConfig.maxOutstandingPackets]; + mReliableIncoming = new IncomingQueueEntry[mConfig.maxInstandingPackets]; + + mLogicalRoot = NULL; + mLogicalEnd = NULL; +} + +UdpReliableChannel::~UdpReliableChannel() +{ + if (mCoalescePacket != NULL) + { + mCoalescePacket->Release(); + mCoalescePacket = NULL; + } + + const LogicalPacket *cur = mLogicalRoot; + while (cur != NULL) + { + const LogicalPacket *next = cur->mReliableQueueNext; + cur->mReliableQueueNext = NULL; // make sure and mark it available so others could possibly use it (if it is a shared logical packet) + cur->Release(); + if (cur == next) // pointing to self, so this is end of list + break; + cur = next; + } + + delete[] mPhysicalPackets; // destructor will release any logical packets as appropriate + delete[] mReliableIncoming; // destructor will delete any data as appropriate + + // delete any big packet that might be under construction + delete[] mBigDataPtr; +} + +void UdpReliableChannel::Send(const uchar *data, int dataLen, const uchar *data2, int dataLen2) +{ + if (mLogicalPacketsQueued == 0) + { + // if we are adding something to a previously empty logical queue, then it is possible that + // we may be able to send it, so mark ourselves to take time the next time it is offered + mNextNeedTime = 0; + mUdpConnection->ScheduleTimeNow(); + } + + if (dataLen + dataLen2 <= mMaxCoalesceAttemptBytes) + { + SendCoalesce(data, dataLen, data2, dataLen2); + } + else + { + FlushCoalesce(); + + LogicalPacket *packet = mUdpConnection->mUdpManager->CreatePacket(data, dataLen, data2, dataLen2); + QueueLogicalPacket(packet); + packet->Release(); + } + + if (mConfig.processOnSend) // make it give our channel time right now to determine outstanding and send this packet if there is room + GiveTime(); +} + +void UdpReliableChannel::Send(const LogicalPacket *packet) +{ + if (mLogicalPacketsQueued == 0) + { + // if we are adding something to a previously empty logical queue, then it is possible that + // we may be able to send it, so mark ourselves to take time the next time it is offered + mNextNeedTime = 0; + mUdpConnection->ScheduleTimeNow(); + } + + if (packet->GetDataLen() <= mMaxCoalesceAttemptBytes) + { + SendCoalesce((const uchar *)packet->GetDataPtr(), packet->GetDataLen()); + } + else + { + FlushCoalesce(); + QueueLogicalPacket(packet); + } + + if (mConfig.processOnSend) // make it give our channel time right now to determine outstanding and send this packet if there is room + GiveTime(); +} + +void UdpReliableChannel::FlushCoalesce() +{ + if (mCoalescePacket != NULL) + { + if (mCoalesceCount == 1) + { + int firstLen; + int skipLen = UdpMisc::GetVariableValue(mCoalesceStartPtr + 2, (uint *)&firstLen); + LogicalPacket *lp = mUdpConnection->mUdpManager->CreatePacket(mCoalesceStartPtr + 2 + skipLen, firstLen); + QueueLogicalPacket(lp); + lp->Release(); + } + else + { + mCoalescePacket->SetDataLen(mCoalesceEndPtr - mCoalesceStartPtr); + QueueLogicalPacket(mCoalescePacket); + } + + mCoalescePacket->Release(); + mCoalescePacket = NULL; + } +} + +void UdpReliableChannel::SendCoalesce(const uchar *data, int dataLen, const uchar *data2, int dataLen2) +{ + int totalLen = dataLen + dataLen2; + if (mCoalescePacket == NULL) + { + mCoalescePacket = mUdpConnection->mUdpManager->CreatePacket(NULL, mMaxDataBytes); + mCoalesceEndPtr = mCoalesceStartPtr = (uchar *)mCoalescePacket->GetDataPtr(); + *mCoalesceEndPtr++ = 0; + *mCoalesceEndPtr++ = UdpConnection::cUdpPacketGroup; + mCoalesceCount = 0; + } + else + { + int spaceLeft = mMaxDataBytes - (mCoalesceEndPtr - mCoalesceStartPtr); + if (totalLen + 3 > spaceLeft) // 3 bytes to ensure PutVariableValue has room for the length indicator (this limits us to 64k coalescing, which is ok, since fragments can't get that big) + { + FlushCoalesce(); + SendCoalesce(data, dataLen, data2, dataLen2); + return; + } + } + + // append on end of coalesce + mCoalesceCount++; + mCoalesceEndPtr += UdpMisc::PutVariableValue(mCoalesceEndPtr, totalLen); + if (data != NULL) + memcpy(mCoalesceEndPtr, data, dataLen); + mCoalesceEndPtr += dataLen; + if (data2 != NULL) + memcpy(mCoalesceEndPtr, data2, dataLen2); + mCoalesceEndPtr += dataLen2; +} + +void UdpReliableChannel::QueueLogicalPacket(const LogicalPacket *packet) +{ + mLogicalPacketsQueued++; + mLogicalBytesQueued += packet->GetDataLen(); + + if (packet->mReliableQueueNext != NULL) + { + packet = mUdpConnection->mUdpManager->WrappedBorrow(packet); + } + else + { + packet->AddRef(); + } + + packet->mReliableQueueNext = packet; // have it point to itself to signify that the mNext pointer is taken (ie. reserve it), yet still represents the end of the list + if (mLogicalEnd != NULL) + { + mLogicalEnd->mReliableQueueNext = packet; + } + mLogicalEnd = packet; + + if (mLogicalRoot == NULL) + { + mLogicalRoot = packet; + } +} + +bool UdpReliableChannel::PullDown(int windowSpaceLeft) +{ + // the pull-down on-demand method will give us the opportunity to late-combine as many tiny logical packets as we can, + // reducing the number of tracked-packets that are required. This effectively reduces the number of acks that we are + // going to get back, which can be substantial in situations where there are a LOT of tiny reliable packets being sent. + // operating with fewer outstanding physical-packets to track is more CPU efficient as well. + + // (NOTE: as of this writing, the below implementation does not do the late-combine techique yet) + // (NOTE: we could also combine-on-send if we so desired, I am not sure which way we will go) + // (NOTE: doing it on send could allow us to avoid LogicalPacket allocations...) + + bool pulledDown = false; + int physicalCount = (int)(mReliableOutgoingId - mReliableOutgoingPendingId); + while (windowSpaceLeft > 0 && physicalCount < mConfig.maxOutstandingPackets) + { + if (mLogicalRoot == NULL) + { + FlushCoalesce(); // this is guaranteed to stick + if (mLogicalRoot == NULL) + break; // nothing flushed, so we are done + } + + int nextSpot = (int)(mReliableOutgoingId % mConfig.maxOutstandingPackets); + + // ok, we can move something down, even if it is only a fragment of the logical packet + PhysicalPacket *entry = &mPhysicalPackets[nextSpot]; + entry->mParent = mLogicalRoot; + entry->mParent->AddRef(); // add ref from physical packet + entry->mFirstTimeStamp = 0; + entry->mLastTimeStamp = 0; + + // calculate how much we can send based on our starting position (mFragmentNextPos) in the logical packet. + // if we can't send it the rest of data to end of packet, then send the fragment portion and addref, otherwise send the whole thing and pop the logical packet + int dataLen = entry->mParent->GetDataLen(); + const uchar *data = (const uchar *)entry->mParent->GetDataPtr(); + int bytesLeft = dataLen - mFragmentNextPos; + int bytesToSend = udpMin(bytesLeft, mMaxDataBytes); + + entry->mDataPtr = data + mFragmentNextPos; + + // if not sending entire packet + if (bytesToSend != dataLen) + { + // mark it as a fragment + if (mFragmentNextPos == 0) + bytesToSend -= sizeof(int); // fragment start has a 4 byte header specifying size of following large data, so make room for it so we don't exceed max raw packet size + } + entry->mDataLen = bytesToSend; + mReliableOutgoingBytes += bytesToSend; + + if (bytesToSend == bytesLeft) + { + mFragmentNextPos = 0; + mLogicalPacketsQueued--; + const LogicalPacket *lp = mLogicalRoot; + mLogicalRoot = mLogicalRoot->mReliableQueueNext; + if (mLogicalRoot == lp) // ie, we were pointing to ourself, meaning we were the end of the list + { + mLogicalRoot = NULL; + mLogicalEnd = NULL; + } + lp->mReliableQueueNext = NULL; // clear our next link since we are no longer using it (so somebody else can use it potentially) + lp->Release(); // release from logical queue + } + else + { + mFragmentNextPos += bytesToSend; + } + + mLogicalBytesQueued -= bytesToSend; // as fragments are sent, decrease the number of logical bytes queued + mReliableOutgoingId++; + physicalCount++; + windowSpaceLeft -= bytesToSend; + pulledDown = true; + } + return(pulledDown); +} + +int UdpReliableChannel::GiveTime() +{ + uchar buf[256]; + uchar *bufPtr; + + UdpMisc::ClockStamp hotClock = UdpMisc::Clock(); + + if (hotClock < mNextNeedTime) + return(UdpMisc::ClockDiff(hotClock, mNextNeedTime)); + + // if we are a trickle channel, then don't try sending more until trickleRate has expired. We are only allowed + // to send up to trickleBytes at a time every trickleRate milliseconds; however, if we don't send the full trickleBytes + // in one GiveTime call, then it won't get to try sending more bytes until this timer has expired, even if we had not used + // up the entire trickleBytes allotment the last time we were in here...this should not cause any significant problems + if (mConfig.trickleRate > 0) + { + int nextAllowedSendTime = mConfig.trickleRate - UdpMisc::ClockDiff(mTrickleLastSend, hotClock); + if (nextAllowedSendTime > 0) + return(nextAllowedSendTime); + } + + + // lot a tweaking goes into calculating the optimal resend time. Set it too large and you can stall the pipe + // at the beginning of the connection fairly easily + int optimalResendDelay = (mAveragePingTime * mConfig.resendDelayPercent / 100) + mConfig.resendDelayAdjust; // percentage of average ping plus a fixed amount + optimalResendDelay = udpMin(mConfig.resendDelayCap, optimalResendDelay); // never let the resend delay get over max + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // see if any of the physical packets can actually be sent (either resends, or initial sends, whatever + // if not, calculate when exactly somebody is expected to need sending + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + mMaxxedOutCurrentWindow = false; + int outstandingNextSendTime = 10 * 60000; + // if we have something to do + + // this next branch was replaced by JeffP in the latest UdpLibrary drop. Please integrate + // that. If something catestrophic happens with reliable channels, uncomment this next line to + // replace the existing branch + //if (mReliableOutgoingId < mReliableOutgoingPendingId || mLogicalRoot != NULL || mCoalescePacket != NULL) + + if (mReliableOutgoingPendingId < mReliableOutgoingId || mLogicalRoot != NULL || mCoalescePacket != NULL) + { + // first, let's calculate how many bytes we figure is outstanding based on who is still waiting for an ack-packet + UdpMisc::ClockStamp oldestResendTime = udpMax(hotClock - optimalResendDelay, mLastTimeStampAcknowledged); // anything older than this, we need to resend + + int useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize); + int outstandingBytes = 0; + PhysicalPacket *readyQueue[10000]; + PhysicalPacket **readyEnd = readyQueue + (sizeof(readyQueue) / sizeof(PhysicalPacket *)); + PhysicalPacket **readyPtr = readyQueue; + + int windowSpaceLeft = useMaxOutstandingBytes; + for (udp_int64 i = mReliableOutgoingPendingId; i <= mReliableOutgoingId; i++) + { + if (i == mReliableOutgoingId) + { + // this packet is not really here yet, we need to pull it down if possible + // if not possible, we need to break out of the loop as we are done + if (!PullDown(windowSpaceLeft)) + break; + } + + // if this packet has not been acked and it is NOT ready to be sent (was recently sent) then we consider it outstanding + // note: packets needing re-sending probably got lost and are therefore not outstanding + PhysicalPacket *entry = &mPhysicalPackets[i % mConfig.maxOutstandingPackets]; + if (entry->mDataPtr != NULL) // acked packets set the dataPtr to NULL + { + // if this packet is ready to be sent (ie: needs time now, or some later packet has already been ack'ed) + windowSpaceLeft -= entry->mDataLen; // window-space is effectively taken whether we have sent it yet or not + if (entry->mLastTimeStamp < oldestResendTime) + { + if (readyPtr < readyEnd) // if we have queue space + *readyPtr++ = entry; + } + else + { + outstandingBytes += entry->mDataLen; + outstandingNextSendTime = udpMin(outstandingNextSendTime, optimalResendDelay - UdpMisc::ClockDiff(entry->mLastTimeStamp, hotClock)); + } + + // if we have reached a point in the queue where there are no sent packets + // and our outstanding bytes plus how much we intend to send is greater than the window we have + // then we can quit step-2, since there is nothing else to be gained by continuing. + if (entry->mFirstTimeStamp == 0 && (windowSpaceLeft <= 0)) + break; + } + } + + + // second, send ready entries until the max outstanding is reached + int trickleSent = 0; + PhysicalPacket **readyWalk = readyQueue; + PhysicalPacket *pendingReliableBasePtr = &mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets]; + while (readyWalk < readyPtr && outstandingBytes < useMaxOutstandingBytes) + { + // prepare packet and send it + PhysicalPacket *entry = *readyWalk++; + + // prepare reliable header and send it with data + const uchar *parentBase = (const uchar *)entry->mParent->GetDataPtr(); + + bool fragment = false; + if (entry->mDataPtr != parentBase || entry->mDataLen != entry->mParent->GetDataLen()) + fragment = true; + + // we can calculate what our reliableId should be based on our position in the array + // need to handle the case where we wrap around the end of the array + udp_int64 reliableId; + if (entry >= pendingReliableBasePtr) + { + reliableId = mReliableOutgoingPendingId + (entry - pendingReliableBasePtr); + } + else + { + reliableId = mReliableOutgoingPendingId + (&mPhysicalPackets[mConfig.maxOutstandingPackets] - pendingReliableBasePtr) + (entry - &mPhysicalPackets[0]); + } + + // prep the actual packet and send it + bufPtr = buf; + *bufPtr++ = 0; + *bufPtr++ = (uchar)(((fragment) ? UdpConnection::cUdpPacketFragment1 : UdpConnection::cUdpPacketReliable1) + mChannelNumber); // mark us as a fragment if we are one + bufPtr += UdpMisc::PutValue16(bufPtr, (ushort)(reliableId & 0xffff)); + if (fragment && entry->mDataPtr == parentBase) + bufPtr += UdpMisc::PutValue32(bufPtr, entry->mParent->GetDataLen()); // first fragment has a total-length byte after the reliable header + mUdpConnection->BufferedSend(buf, bufPtr - buf, entry->mDataPtr, entry->mDataLen, false); + + // update state information + if (entry->mFirstTimeStamp == 0) + { + entry->mFirstTimeStamp = hotClock; + } + else + { + // trying to send the packet again, let's see how long we have been trying to send this packet. If we + // have an unacknowledged timeout set and it is older than that, then terminate the connection. + // note: we only check for the oldest unacknowledged age against the timeout at the point in time + // that we are considering sending the packet again. This can technically cause it to wait slightly + // longer than the specified timeout setting before disconnecting the connection, but should be + // close enough for all practical purposes and allows for more efficient processing of this setting internally. + if (mUdpConnection->mUdpManager->mParams.oldestUnacknowledgedTimeout > 0) + { + int age = UdpMisc::ClockDiff(entry->mFirstTimeStamp, hotClock); + if (age > mUdpConnection->mUdpManager->mParams.oldestUnacknowledgedTimeout) + { + mUdpConnection->InternalDisconnect(0, UdpConnection::cDisconnectReasonUnacknowledgedTimeout); + return(0); + } + } + + // were we resent because of a later ack came in? or because we timed out? + if (entry->mLastTimeStamp < mLastTimeStampAcknowledged) + { + // we are resending this packet due to an accelleration (receiving a later packet ack) + // so recalc slow start threshhold and congestion window size as per Reno fast-recovery algorithm + mCongestionWindowSize = mCongestionWindowSize * 2 / 3; + mCongestionWindowSize = udpMax(mCongestionWindowMinimum, mCongestionWindowSize); // never let congestion window get smaller than a single packet + mCongestionSlowStartThreshhold = mCongestionWindowSize; + useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize); + + mStatResentPacketsAccelerated++; + mUdpConnection->mConnectionStats.resentPacketsAccelerated++; + mUdpConnection->mUdpManager->mManagerStats.resentPacketsAccelerated++; + } + else + { + // we are resending this packet due to a timeout, so we are seriously overloading things probably + // so recalc slow start threshhold and congestion window size as per Tahoe algorithm + mCongestionSlowStartThreshhold = udpMax(mMaxDataBytes * 2, outstandingBytes / 2); + mCongestionWindowSize = mCongestionWindowStart; + useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize); + + // because a resend has occurred due to a timeout, slow down the resend times slightly + // when things start flowing again, it will fix itself up quickly anyways + mAveragePingTime += 100; + + // When a connection goes temporarily dead, everything that is in the current + // window will end up getting timedout. If the window were large, this could result in the + // mAveragePingTime growing quite large and creating very long stalls in the pipe once it does + // start moving again. To prevent this, we cap mAveragePingTime when these events occur to prevent + // long stalls when the pipe finally reopens. + mAveragePingTime = udpMin(mConfig.resendDelayCap, mAveragePingTime); + + mStatResentPacketsTimedOut++; + mUdpConnection->mConnectionStats.resentPacketsTimedOut++; + mUdpConnection->mUdpManager->mManagerStats.resentPacketsTimedOut++; + } + } + + entry->mLastTimeStamp = hotClock; + + outstandingNextSendTime = udpMin(outstandingNextSendTime, optimalResendDelay); // this packet is now outstanding, so factor it into the outstandingNextSendTime calculation + outstandingBytes += entry->mDataLen; + mTrickleLastSend = hotClock; + trickleSent += entry->mDataLen; + + if (mConfig.trickleSize != 0 && trickleSent >= mConfig.trickleSize) + break; + } + + if (outstandingBytes >= useMaxOutstandingBytes) + mMaxxedOutCurrentWindow = true; + } + else + { + // we have nothing in the pipe at all, reset the congestion window (this means everything has been acked, so the pipe is totally empty + // we need to reset the window back to prevent a sudden flood next time a large chunk of data is sent. + // we also need to avoid having the slowly sent reliable packets constantly increase the window size (since none will ever get lost) + // such that when it does come time to send a big chunk of data, it thinks the window-size is enormous. + // resetting the window back to small will only have an effect if a large chunk of data is then sent, at which time it will quickly + // ramp up with the slow-start method. + // we will reset the slow-start threshhold to maximum level that the congestion window has ever been allowed to grow. This effectively + // allows the threshhold to increase after getting smaller, something it otherwise has not been able to do. + mCongestionWindowSize = mCongestionWindowStart; + mCongestionSlowStartThreshhold = mCongestionWindowLargest; + } + + int nextAllowedSendTime = mConfig.trickleRate - UdpMisc::ClockDiff(mTrickleLastSend, hotClock); + nextAllowedSendTime = udpMax(0, udpMax(nextAllowedSendTime, outstandingNextSendTime)); + mNextNeedTime = hotClock + nextAllowedSendTime; + return(nextAllowedSendTime); +} + +void UdpReliableChannel::GetChannelStatus(UdpConnection::ChannelStatus *channelStatus) const +{ + int coalesceBytes = 0; + if (mCoalescePacket != NULL) + coalesceBytes = mCoalesceEndPtr - mCoalesceStartPtr; + + channelStatus->totalPendingBytes = mLogicalBytesQueued + mReliableOutgoingBytes + coalesceBytes; + channelStatus->queuedPackets = mLogicalPacketsQueued; + channelStatus->queuedBytes = mLogicalBytesQueued; + channelStatus->incomingLargeTotal = mBigDataTargetLen; + channelStatus->incomingLargeSoFar = mBigDataLen; + channelStatus->duplicatePacketsReceived = mStatDuplicatePacketsReceived; + channelStatus->resentPacketsAccelerated = mStatResentPacketsAccelerated; + channelStatus->resentPacketsTimedOut = mStatResentPacketsTimedOut; + channelStatus->congestionSlowStartThreshhold = mCongestionSlowStartThreshhold; + channelStatus->congestionWindowSize = mCongestionWindowSize; + channelStatus->ackAveragePing = mAveragePingTime; + channelStatus->oldestUnacknowledgedAge = 0; + + if (mReliableOutgoingPendingId < mReliableOutgoingId) + { + // oldest pending packet will be definition be the oldestUnacknowledged, since it is impossible + // for any packet after it to have possibly been sent before it was sent for its first time + // since queue is effectively in first-send order. It is also impossible for this packet to have + // been acknowledged, since the pending id advances the moment the oldest is acknowledged, meaning + // that the pendingId is always pointing to something that has either been sent (or not sent at all) + PhysicalPacket *entry = &mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets]; + if (entry->mFirstTimeStamp != 0) // if has been sent (we know it hasn't been acknowledged or we couldn't possibly be pointing at it as pending) + { + channelStatus->oldestUnacknowledgedAge = UdpMisc::ClockElapsed(entry->mFirstTimeStamp); + } + } +} + +void UdpReliableChannel::ReliablePacket(const uchar *data, int dataLen) +{ + uchar buf[256]; + uchar *bufPtr; + + if (dataLen <= UdpConnection::cUdpPacketReliableSize) + { + mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReliablePacketTooShort); + return; + } + + int packetType = data[1]; + ushort reliableStamp = UdpMisc::GetValue16(data + 2); + udp_int64 reliableId = GetReliableIncomingId(reliableStamp); + + if (reliableId >= mReliableIncomingId + mConfig.maxInstandingPackets) + return; // if we do not have buffer space to hold onto this packet, then we simply must pretend like it was lost + + if (reliableId >= mReliableIncomingId) + { + ReliablePacketMode mode = (ReliablePacketMode)((packetType - UdpConnection::cUdpPacketReliable1) / UdpManager::cReliableChannelCount); + + // is this the packet we are waiting for + if (mReliableIncomingId == reliableId) + { + // if so, process it immediately + ProcessPacket(mode, data + UdpConnection::cUdpPacketReliableSize, dataLen - UdpConnection::cUdpPacketReliableSize); + mReliableIncomingId++; + + // process other packets that have arrived + while (mReliableIncoming[mReliableIncomingId % mConfig.maxInstandingPackets].mPacket != NULL) + { + int spot = (int)(mReliableIncomingId % mConfig.maxInstandingPackets); + if (mReliableIncoming[spot].mMode != cReliablePacketModeDelivered) + { + ProcessPacket(mReliableIncoming[spot].mMode, (uchar *)mReliableIncoming[spot].mPacket->GetDataPtr(), mReliableIncoming[spot].mPacket->GetDataLen()); + } + + mReliableIncoming[spot].mPacket->Release(); + mReliableIncoming[spot].mPacket = NULL; + mReliableIncomingId++; + } + } + else + { + // not the one we need next, but it is later than the one we need , so store it in our buffer until it's turn comes up + int spot = (int)(reliableId % mConfig.maxInstandingPackets); + if (mReliableIncoming[spot].mPacket == NULL) // only make the copy of it if we don't already have it in our buffer (in cases where it was sent twice, there would be no harm in the copy again since it must be the same packet, it's just inefficient) + { + mReliableIncoming[spot].mMode = mode; + mReliableIncoming[spot].mPacket = mUdpConnection->mUdpManager->CreatePacket(data + UdpConnection::cUdpPacketReliableSize, dataLen - UdpConnection::cUdpPacketReliableSize); + + // on out of order deliver, we need to keep a copy of it as if we were doing ordered-delivery in order to prevent duplicates + // we will mark the packet in the queue as already delivered to prevent it from getting delivered a second time when the stalled packet + // arrives and unwinds the queue + if (mode == cReliablePacketModeReliable && mConfig.outOfOrder) + { + ProcessPacket(cReliablePacketModeReliable, (uchar *)mReliableIncoming[spot].mPacket->GetDataPtr(), mReliableIncoming[spot].mPacket->GetDataLen()); + mReliableIncoming[spot].mMode = cReliablePacketModeDelivered; + } + } + else + { + mStatDuplicatePacketsReceived++; + mUdpConnection->mConnectionStats.duplicatePacketsReceived++; + mUdpConnection->mUdpManager->mManagerStats.duplicatePacketsReceived++; + } + } + } + else + { + mStatDuplicatePacketsReceived++; + mUdpConnection->mConnectionStats.duplicatePacketsReceived++; + mUdpConnection->mUdpManager->mManagerStats.duplicatePacketsReceived++; + } + + bufPtr = buf; + *bufPtr++ = 0; + if (mReliableIncomingId > reliableId) + { + // ack everything up to the current head of our chain (minus one since the stamp represents the next one we want to get) + *bufPtr++ = (uchar)(UdpConnection::cUdpPacketAckAll1 + mChannelNumber); + bufPtr += UdpMisc::PutValue16(bufPtr, (ushort)((mReliableIncomingId - 1) & 0xffff)); + } + else + { + // a simple ack for us only + *bufPtr++ = (uchar)(UdpConnection::cUdpPacketAck1 + mChannelNumber); + bufPtr += UdpMisc::PutValue16(bufPtr, (ushort)(reliableId & 0xffff)); + mBufferedAckPtr = NULL; // not allowed to replace an old one with a selective-ack + } + + if (mBufferedAckPtr != NULL && mConfig.ackDeduping) + { + memcpy(mBufferedAckPtr, buf, bufPtr - buf); + } + else + { + mBufferedAckPtr = mUdpConnection->BufferedSend(buf, bufPtr - buf, NULL, 0, true); // safe to append on our data, it is stack data + } +} + +void UdpReliableChannel::ProcessPacket(ReliablePacketMode mode, const uchar *data, int dataLen) +{ + assert(dataLen > 0); + + // if we have a big packet under construction already, or we are a fragment and thus need to be constructing one, then append this on the end (will create new if it is the first fragment) + if (mode == cReliablePacketModeReliable) + { + // we are not a fragment, nor was there a fragment in progress, so we are a simple reliable packet, just send it to the app + mUdpConnection->ProcessCookedPacket(data, dataLen); + } + else if (mode == cReliablePacketModeFragment) + { + // append onto end of big packet (or create new big packet if not existing already) + if (mBigDataPtr == NULL) + { + mBigDataTargetLen = UdpMisc::GetValue32(data); // first fragment has a total-length int header on it. + mBigDataPtr = new uchar[mBigDataTargetLen]; + mBigDataLen = 0; + data += sizeof(int); + dataLen -= sizeof(int); + } + + int safetyMax = udpMin(mBigDataTargetLen - mBigDataLen, dataLen); // can't happen in theory since they should add up exact, but protect against it if it does + + if(safetyMax != dataLen) + { + throw UdpLibraryException(); + } + + + memcpy(mBigDataPtr + mBigDataLen, data, safetyMax); + mBigDataLen += safetyMax; + + if (mBigDataTargetLen == mBigDataLen) + { + // send big-packet off to application + mUdpConnection->ProcessCookedPacket(mBigDataPtr, mBigDataLen); + + // delete big packet, and reset + delete[] mBigDataPtr; + mBigDataLen = 0; + mBigDataTargetLen = 0; + mBigDataPtr = NULL; + } + } +} + +void UdpReliableChannel::AckAllPacket(const uchar *data, int /*dataLen*/) +{ + udp_int64 reliableId = GetReliableOutgoingId((ushort)UdpMisc::GetValue16(data + 2)); + + if (mReliableOutgoingPendingId > reliableId) + { + // if we ackall'ed a packet and everything before the ackall address had already been acked, then we know + // for certainty that we sent a packet over again that did not need to be sent over again (ie. wasn't lost, just slow) + // so adjust the mAveragePingTime upward to slow down future resends + mAveragePingTime += 400; + mAveragePingTime = udpMin(mConfig.resendDelayCap, mAveragePingTime); + } + + for (udp_int64 i = mReliableOutgoingPendingId; i <= reliableId; i++) + Ack(i); +} + +void UdpReliableChannel::Ack(udp_int64 reliableId) +{ + // if packet being acknowledged is possibly in our resend queue, then check for it + if (reliableId >= mReliableOutgoingPendingId && reliableId < mReliableOutgoingId) + { + int pos = (int)(reliableId % mConfig.maxOutstandingPackets); + PhysicalPacket *entry = &mPhysicalPackets[pos]; + + if (entry->mDataPtr != NULL) // if this packet has not been acknowledged yet (sometimes we get back two acks for the same packet) + { + mNextNeedTime = 0; // something got acked, so we actually need to take the time next time it is offered + + // if the last time we gave this reliable channel processing time, it filled up the entire sliding window + // then go ahead and increase the window-size when incoming acks come in. However, if the window wasn't full + // then don't increase the window size. The problem is, a game application is likely to send reliable data + // at a relatively slow rate (2k/second for example), never filling the window. The net result would be that + // every acknowledged packet would increase the window size, giving the reliable channel the impression that + // it's window can be very very large, when in fact, it is only not losing packets because the application is pacing + // itself. The window could grow enormous, even 200k for a modem. Then, if the application were to dump a load + // of data onto us all at once, it would flush it all out at once thinking it had a big window. By only increasing + // the window size when we have high enough volume to fill the window, we ensure this does not happen. TCP does a similar + // thing, but what they do is reset the window if there has been a long stall. We do that too, but because we are a game + // application that is likely to pace the data at the application level, we have a unique circumstances that need addressing. + if (mMaxxedOutCurrentWindow) + { + if (mCongestionWindowSize < mCongestionSlowStartThreshhold) + mCongestionWindowSize += mMaxDataBytes; // slow-start mode + else + mCongestionWindowSize += udpMax(1, (mMaxDataBytes * mMaxDataBytes / mCongestionWindowSize)); // congestion mode + + mCongestionWindowLargest = udpMax(mCongestionWindowLargest, mCongestionWindowSize); + } + + if (entry->mLastTimeStamp == entry->mFirstTimeStamp) + { + + + // if the packet that is being acknowledged was only sent once, then we can safely use + // the round-trip time as an accurate measure of ping time. By knowing ping time, we can + // better (more agressively) schedule resends of lost packets. We will use a moving average + // that weights the current packet as 1/4 the average. + int thisPingTime = UdpMisc::ClockElapsed(entry->mFirstTimeStamp); + mAveragePingTime = (mAveragePingTime * 3 + thisPingTime) / 4; + } + + // what this is doing is if we receive an ACK for a packet that was sent at TIME_X + // we can assume that all packets sent before TIME_X that are not yet acknowledge were lost + // and we can resend them immediately + // since we do not know whether this ack is for last packet we sent, we have to assume it is for the first time this packet was sent (if sent multiple times) + // otherwise, we could resend it, then receive the ack from the first packet, and think our last-ack time is the time of the second outgoing packet + // which would cause about every packet in the queue to resend, even if they had just been sent + // in situations where the first packet truely was lost and this is an ACK of the second packet, then the only + // harm done is that the we may not resend some of the earlier sent packets quite as quickly. This will only + // happen in situations where a packet that was truely lost gets acked on it's second attempt...we just + // won't be using that ack for the purposes of accelerating other resends...since odds are a non-lost packet + // will accelerate those other resends shortly anyhow, there really is no loss + // (note: we used to only set this value forward for packets that were never lost (one time sends); however, if this stamp + // ever got set way high for some reason (in theory it can't happen), then we would get into a situation where it would + // rapidly resend and possibly never get reset, causing infinite rapid resends, so we now set it every time to the first-stamp) + // which will be safe, even if the packet were resent.) + mLastTimeStampAcknowledged = entry->mFirstTimeStamp; + + + // this packet we have queued has been acknowledged, so delete it from queue + mReliableOutgoingBytes -= entry->mDataLen; + entry->mDataLen = 0; + entry->mDataPtr = NULL; + entry->mParent->Release(); + entry->mParent = NULL; + + // advance the pending ptr until it reaches outgoingId or an entry that has yet to acknowledged + while (mReliableOutgoingPendingId < mReliableOutgoingId) + { + if (mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets].mDataPtr != NULL) + break; + mReliableOutgoingPendingId++; + } + } + else + { + // we got an ack for a packet that has already been acked. This could be due to an ack-all packet that covered us so statistically + // we can't do much with this information. + } + } + + // we don't need to try rescheduling ourself here, since our connection object reschedules to go immediately whenever any type + // of packet arrives (including ack packets) +} + + + + + +UdpReliableChannel::IncomingQueueEntry::IncomingQueueEntry() +{ + mPacket = NULL; + mMode = UdpReliableChannel::cReliablePacketModeReliable; +} + +UdpReliableChannel::IncomingQueueEntry::~IncomingQueueEntry() +{ + if (mPacket != NULL) + mPacket->Release(); +} + + +UdpReliableChannel::PhysicalPacket::PhysicalPacket() +{ + mParent = NULL; +} + +UdpReliableChannel::PhysicalPacket::~PhysicalPacket() +{ + if (mParent != NULL) + mParent->Release(); +} + + + + ///////////////////////////////////////////////////// + // LogicalPacket implementation + ///////////////////////////////////////////////////// +LogicalPacket::LogicalPacket() +{ + mRefCount = 1; + mReliableQueueNext = NULL; +} + +LogicalPacket::~LogicalPacket() +{ +} + +void LogicalPacket::AddRef() const +{ + mRefCount++; +} + +void LogicalPacket::Release() const +{ + if (--mRefCount == 0) + delete this; +} + +int LogicalPacket::GetRefCount() const +{ + return(mRefCount); +} + +bool LogicalPacket::IsInternalPacket() const +{ + return(false); +} + + ///////////////////////////////////////////////////// + // SimpleLogicalPacket implementation + ///////////////////////////////////////////////////// +SimpleLogicalPacket::SimpleLogicalPacket(const void *data, int dataLen) +{ + mDataLen = dataLen; + mData = new uchar[mDataLen]; + if (data != NULL) + memcpy(mData, data, mDataLen); +} + +SimpleLogicalPacket::~SimpleLogicalPacket() +{ + delete[] mData; +} + +void *SimpleLogicalPacket::GetDataPtr() +{ + return(mData); +} + +const void *SimpleLogicalPacket::GetDataPtr() const +{ + return(mData); +} + +int SimpleLogicalPacket::GetDataLen() const +{ + return(mDataLen); +} + +void SimpleLogicalPacket::SetDataLen(int len) +{ + assert(len <= mDataLen); + mDataLen = len; +} + ///////////////////////////////////////////////////// + // GroupLogicalPacket implementation + ///////////////////////////////////////////////////// +GroupLogicalPacket::GroupLogicalPacket() : LogicalPacket() +{ + mDataLen = 0; + mData = NULL; +} + +GroupLogicalPacket::~GroupLogicalPacket() +{ + UdpMisc::SmartResize(mData, 0); // free it +} + +void GroupLogicalPacket::AddPacket(const LogicalPacket *packet) +{ + assert(packet != NULL); + AddPacketInternal(packet->GetDataPtr(), packet->GetDataLen(), packet->IsInternalPacket()); +} + +void GroupLogicalPacket::AddPacket(const void *data, int dataLen) +{ + assert(data != NULL); + assert(dataLen >= 0); + AddPacketInternal(data, dataLen, false); +} + +void GroupLogicalPacket::AddPacketInternal(const void *data, int dataLen, bool isInternalPacket) +{ + if (dataLen == 0) + return; + mData = (uchar *)UdpMisc::SmartResize(mData, mDataLen + dataLen + 10, 512); // 7 is the most bytes that could be needed to specify the length of the data to follow, 2 is for internal header-bytes, 1 is for zero-escape if needed (if they need to be added on) + if (mDataLen == 0) + { + mData[0] = 0; + mData[1] = UdpConnection::cUdpPacketGroup; + mDataLen = 2; + } + + uchar *ptr = mData + mDataLen; + if (!isInternalPacket && *(const uchar *)data == 0) + { + ptr += UdpMisc::PutVariableValue(ptr, dataLen + 1); + *ptr++ = 0; // packet is not internal and starts with 0, so we need to zero-escape it so it knows it's an application packet + } + else + ptr += UdpMisc::PutVariableValue(ptr, dataLen); + + memcpy(ptr, data, dataLen); + ptr += dataLen; + mDataLen = ptr - mData; +} + +void *GroupLogicalPacket::GetDataPtr() +{ + return(mData); +} + +const void *GroupLogicalPacket::GetDataPtr() const +{ + return(mData); +} + +int GroupLogicalPacket::GetDataLen() const +{ + return(mDataLen); +} + +void GroupLogicalPacket::SetDataLen(int /*len*/) +{ + assert(0); // not allowed to set the len of a group logical packet +} + +bool GroupLogicalPacket::IsInternalPacket() const +{ + return(true); +} + + /////////////////////////////////////////////////////////////////////////////////////////// + // PooledLogicalPacket implementation + /////////////////////////////////////////////////////////////////////////////////////////// +PooledLogicalPacket::PooledLogicalPacket(UdpManager *manager, int len) +{ + mMaxDataLen = len; + mData = new uchar[mMaxDataLen]; + mDataLen = 0; + + mUdpManager = manager; + mAvailableNext = NULL; + mCreatedNext = NULL; + mCreatedPrev = NULL; + + mUdpManager->PoolCreated(this); +} + +PooledLogicalPacket::~PooledLogicalPacket() +{ + if (mUdpManager != NULL) + { + mUdpManager->PoolDestroyed(this); + } + + delete[] mData; +} + +void PooledLogicalPacket::AddRef() const +{ + LogicalPacket::AddRef(); +} + +void PooledLogicalPacket::Release() const +{ + if (mRefCount == 1 && mUdpManager != NULL) + mUdpManager->PoolReturn(const_cast(this)); // if pool wants to keep us, it will inc our ref count, preventing our destruction + // we cast off our const, as when we are added back to the pool, we can be modified + LogicalPacket::Release(); +} + +void *PooledLogicalPacket::GetDataPtr() +{ + return(mData); +} + +const void *PooledLogicalPacket::GetDataPtr() const +{ + return(mData); +} + +int PooledLogicalPacket::GetDataLen() const +{ + return(mDataLen); +} + +void PooledLogicalPacket::SetDataLen(int len) +{ + assert(len <= mMaxDataLen); + mDataLen = len; +} + +void PooledLogicalPacket::SetData(const void *data, int dataLen, const void *data2, int dataLen2) +{ + mDataLen = dataLen + dataLen2; + if (data != NULL) + memcpy(mData, data, dataLen); + if (data2 != NULL) + memcpy(mData + dataLen, data2, dataLen2); +} + + + + /////////////////////////////////////////////////////////////////////////////////////////// + // WrappedLogicalPacket implementation + /////////////////////////////////////////////////////////////////////////////////////////// +WrappedLogicalPacket::WrappedLogicalPacket(UdpManager *udpManager) +{ + mPacket = NULL; + mUdpManager = udpManager; + mAvailableNext = NULL; + mCreatedNext = NULL; + mCreatedPrev = NULL; + + mUdpManager->WrappedCreated(this); +} + +WrappedLogicalPacket::~WrappedLogicalPacket() +{ + if (mUdpManager != NULL) + { + mUdpManager->WrappedDestroyed(this); + } + + if (mPacket != NULL) + { + mPacket->Release(); + } +} + +void WrappedLogicalPacket::AddRef() const +{ + LogicalPacket::AddRef(); +} + +void WrappedLogicalPacket::Release() const +{ + if (mRefCount == 1 && mUdpManager != NULL) + mUdpManager->WrappedReturn(const_cast(this)); // if pool wants to keep us, it will inc our ref count, preventing our destruction + // we cast off our const, as when we are added back to the pool, we can be modified + LogicalPacket::Release(); +} + +void WrappedLogicalPacket::SetLogicalPacket(const LogicalPacket *packet) +{ + if (mPacket != NULL) + mPacket->Release(); + mPacket = packet; + if (mPacket != NULL) + mPacket->AddRef(); +} + +void *WrappedLogicalPacket::GetDataPtr() +{ + return(const_cast(mPacket->GetDataPtr())); // didn't have a choice really... +} + +const void *WrappedLogicalPacket::GetDataPtr() const +{ + return(mPacket->GetDataPtr()); +} + +int WrappedLogicalPacket::GetDataLen() const +{ + return(mPacket->GetDataLen()); +} + +void WrappedLogicalPacket::SetDataLen(int /*len*/) +{ + assert(0); // this should not be possible +} + + + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // SimulateQueueEntry functions + //////////////////////////////////////////////////////////////////////////////////////////////////// +UdpManager::SimulateQueueEntry::SimulateQueueEntry(const uchar *data, int dataLen, UdpIpAddress ip, int port) +{ + mData = new uchar[dataLen]; + mDataLen = dataLen; + memcpy(mData, data, dataLen); + mIp = ip; + mPort = port; + mNext = NULL; +} + +UdpManager::SimulateQueueEntry::~SimulateQueueEntry() +{ + delete[] mData; +} + + + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // UdpMisc functions + //////////////////////////////////////////////////////////////////////////////////////////////////// + +UdpMisc::ClockStamp UdpMisc::Clock() +{ +#if defined(WIN32) + static int sGlobalHigh = 0; + static unsigned sGlobalLow = 0; + + unsigned low = GetTickCount(); + if (low < sGlobalLow) + { + sGlobalHigh++; // not entirely safe, if the once every 47 days we happen to thread-slice right at this spot and some other thread called this, time could get messed for this call...I can live with those odds + } + sGlobalLow = low; + return(((ClockStamp)sGlobalHigh << 32) | sGlobalLow); +#else + // this implementation has the same sort of threading issues the windows version used to have + // before I started using thread-local-storage to solve them. The worst thing that can happen + // is the time-correction will occur more than once from seperate threads, which would result + // in time appearing to jump forward, which isn't that big of a deal. + static ClockStamp sLastStamp = 0; + static ClockStamp sCurrentCorrection = 0; + struct timeval tv; + gettimeofday(&tv, NULL); + UdpMisc::ClockStamp cs = static_cast(tv.tv_sec) * 1000 + static_cast(tv.tv_usec / 1000); + cs += sCurrentCorrection; + if (cs < sLastStamp) + { + // clock moved backwards (somebody changed it), don't ever let this happen + // if clock moves forward, there is no way we can recognize it, code will just + // have to deal with it. In the case of the UdpLibrary, it will likely result + // in a ton of pending packets thinking they have gotten lost and being sent, fairly harmless. + sCurrentCorrection += (sLastStamp - cs); + cs = sLastStamp; + } + sLastStamp = cs; + return(cs); +#endif +} + +int UdpMisc::SyncStampShortDeltaTime(ushort stamp1, ushort stamp2) +{ + ushort delta = (ushort)(stamp1 - stamp2); + if (delta > 0x7fff) + return((int)(0xffff - delta)); + return((int)delta); +} + +int UdpMisc::SyncStampLongDeltaTime(uint stamp1, uint stamp2) +{ + uint delta = stamp1 - stamp2; + if (delta > 0x7fffffff) + return((int)(0xffffffff - delta)); + return((int)delta); +} + +int UdpMisc::Random(int *seed) +{ + int hi = *seed / 127773; + int lo = *seed % 127773; + int t = lo * 16807 - hi * 2836 + 123; + *seed = (t > 0) ? t : (t + 2147483647); + return(*seed); +} + +int UdpMisc::Crc32(const void *buffer, int bufferLen, int encryptValue) +{ + static unsigned crc32_table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, + 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, + 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, + 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, + 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, + 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, + 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, + 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, + 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; + + int crc = 0xffffffff; + crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ (encryptValue & 0xff)) & 0x000000FFL]; + crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 8) & 0xff)) & 0x000000FFL]; + crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 16) & 0xff)) & 0x000000FFL]; + crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 24) & 0xff)) & 0x000000FFL]; + + const uchar *bufPtr = (const uchar *)buffer; + const uchar *endPtr = (const uchar *)buffer + bufferLen; + while (bufPtr < endPtr) + { + crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ *bufPtr) & 0x000000FFL]; + bufPtr++; + } + crc = ~crc; + + return(crc); +} + +void *UdpMisc::SmartResize(void *ptr, int bytes, int round) +{ + enum { cAlignment = sizeof(int) }; + + // rounding stuff for speed + bytes = ((bytes + round - 1) / round) * round; + + if (bytes == 0) + { + if (ptr != NULL) + { + free((uchar *)ptr - cAlignment); + } + return(NULL); + } + + uchar *ptr2; + if (ptr == NULL) + { + ptr2 = (uchar *)malloc(bytes + cAlignment); + if (ptr2 == NULL) + return(NULL); + *(int *)ptr2 = bytes; + return(ptr2 + cAlignment); + } + + int oldBytes = *((int *)((uchar *)ptr - cAlignment)); + if (bytes == oldBytes) + return(ptr); + + ptr2 = (uchar *)realloc((uchar *)ptr - cAlignment, bytes + cAlignment); + if (ptr2 == NULL) + return(NULL); + + *(int *)ptr2 = bytes; + return(ptr2 + cAlignment); +} + +uint UdpMisc::PutVariableValue(void *buffer, uint value) +{ + uchar *bufptr = (uchar *)buffer; + uint store = (uint)value; + if (store < 254) + { + *bufptr = (uchar)store; + return(1); + } + else if (store < 0xffff) + { + *bufptr = 0xff; + *(bufptr + 1) = (uchar)(store >> 8); + *(bufptr + 2) = (uchar)(store & 0xff); + return(3); + } + else + { + *bufptr = 0xff; + *(bufptr + 1) = 0xff; + *(bufptr + 2) = 0xff; + *(bufptr + 3) = (uchar)(store >> 24); + *(bufptr + 4) = (uchar)((store >> 16) & 0xff); + *(bufptr + 5) = (uchar)((store >> 8) & 0xff); + *(bufptr + 6) = (uchar)(store & 0xff); + return(7); + } +} + +uint UdpMisc::GetVariableValue(const void *buffer, uint *value) +{ + const uchar *bufptr = (const uchar *)buffer; + if (*bufptr == 0xff) + { + if (*(bufptr + 1) == 0xff && *(bufptr + 2) == 0xff) + { + *value = (uint)((*(bufptr + 3) << 24) | (*(bufptr + 4) << 16) | (*(bufptr + 5) << 8) | *(bufptr + 6)); + return(7); + } + + *value = (uint)((*(bufptr + 1) << 8) | *(bufptr + 2)); + return(3); + } + + *value = (uint)*bufptr; + return(1); +} + +LogicalPacket *UdpMisc::CreateQuickLogicalPacket(const void *data, int dataLen, const void *data2, int dataLen2) +{ + enum { cQuickFactor = 128 }; + int totalDataLen = dataLen + dataLen2; + int q = (totalDataLen - 1) / cQuickFactor; + LogicalPacket *tlp; + switch(q) + { + case 0: + tlp = new FixedLogicalPacket(NULL, totalDataLen); + break; + case 1: + tlp = new FixedLogicalPacket(NULL, totalDataLen); + break; + case 2: + case 3: + tlp = new FixedLogicalPacket(NULL, totalDataLen); + break; + case 4: + case 5: + case 6: + case 7: + tlp = new FixedLogicalPacket(NULL, totalDataLen); + break; + default: + tlp = new SimpleLogicalPacket(NULL, totalDataLen); + break; + } + + uchar *dest = (uchar *)tlp->GetDataPtr(); + if (data != NULL) + memcpy(dest, data, dataLen); + if (data2 != NULL) + memcpy(dest + dataLen, data2, dataLen2); + return(tlp); +} + +UdpIpAddress UdpMisc::GetHostByName(const char *hostName) +{ + InitializeOperatingSystem(); + + unsigned long address = inet_addr(hostName); + if (address == INADDR_NONE) + { + struct hostent * lphp; + lphp = gethostbyname(hostName); + if (lphp == NULL) + { + address = 0; + } + else + { + address = ((struct in_addr *)(lphp->h_addr))->s_addr; + } + } + + TerminateOperatingSystem(); + return(UdpIpAddress(address)); +} + +void UdpMisc::Sleep(int milliseconds) +{ +#if defined(WIN32) + ::Sleep((DWORD)milliseconds); +#else + struct timeval tv; + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = (milliseconds % 1000) * 1000; + select(0, 0, 0, 0, &tv); +#endif +} + +UdpLibraryException::UdpLibraryException() +{ +} + +UdpLibraryException::~UdpLibraryException() +{ +} + + diff --git a/stationapi/udplibrary/UdpLibrary.doc b/stationapi/udplibrary/UdpLibrary.doc new file mode 100755 index 0000000..9773547 Binary files /dev/null and b/stationapi/udplibrary/UdpLibrary.doc differ diff --git a/stationapi/udplibrary/UdpLibrary.h b/stationapi/udplibrary/UdpLibrary.h new file mode 100755 index 0000000..dfc17ef --- /dev/null +++ b/stationapi/udplibrary/UdpLibrary.h @@ -0,0 +1,6 @@ +#ifndef _Network_UdpLibrary_H +#define _Network_UdpLibrary_H + +#include "UdpLibrary.hpp" + +#endif//_Network_UdpLibrary_H diff --git a/stationapi/udplibrary/UdpLibrary.hpp b/stationapi/udplibrary/UdpLibrary.hpp new file mode 100755 index 0000000..77f909b --- /dev/null +++ b/stationapi/udplibrary/UdpLibrary.hpp @@ -0,0 +1,2095 @@ +#ifndef UDPLIBRARY_HPP +#define UDPLIBRARY_HPP + +#include +#include "UdpHandler.hpp" +#include "priority.hpp" +#include "hashtable.hpp" + +#if defined(WIN32) + typedef unsigned int SOCKET; // avoids us having to include winsock.h just for this + typedef __int64 udp_int64; +#else + typedef int SOCKET; + typedef long long udp_int64; +#endif + +class UdpReliableChannel; +class SimpleLogicalPacket; +class GroupLogicalPacket; + +enum UdpChannel { cUdpChannelUnreliable // unreliable/unordered/buffered + , cUdpChannelUnreliableUnbuffered // unreliable/unordered/unbuffered + , cUdpChannelOrdered // unreliable/ordered/buffered + , cUdpChannelOrderedUnbuffered // unreliable/ordered/unbuffered + , cUdpChannelReliable1 // reliable (as per channel config) + , cUdpChannelReliable2 // reliable (as per channel config) + , cUdpChannelReliable3 // reliable (as per channel config) + , cUdpChannelReliable4 // reliable (as per channel config) + , cUdpChannelCount // count of number of channels + }; + +struct UdpManagerStatistics +{ + udp_int64 bytesSent; + udp_int64 packetsSent; + udp_int64 bytesReceived; + udp_int64 packetsReceived; + udp_int64 connectionRequests; + udp_int64 crcRejectedPackets; + udp_int64 orderRejectedPackets; + udp_int64 duplicatePacketsReceived; + udp_int64 resentPacketsAccelerated; // number of times we have resent a packet due to receiving a later packet in the series + udp_int64 resentPacketsTimedOut; // number of times we have resent a packet due to the ack-timeout expiring + udp_int64 priorityQueueProcessed; // cumulative number of times a priority-queue entry has received processing time + udp_int64 priorityQueuePossible; // cumalative number of priority-queue entries that could have received processing time + udp_int64 applicationPacketsSent; + udp_int64 applicationPacketsReceived; + udp_int64 iterations; // number of times GiveTime has been called + udp_int64 corruptPacketErrors; // number of misformed/corrupt packets + udp_int64 socketOverflowErrors; // number of times the socket buffer was full when a send was attempted. + int poolCreated; // number of packets created in the pool + int poolAvailable; // number of packets available in the pool + int elapsedTime; // how long these statistics have been gathered (in milliseconds), useful for figuring out averages +}; + +struct UdpConnectionStatistics +{ + ///////////////////////////////////////////////////////////////////////// + // these statistics are valid even if clock-sync is not used + // these statistics are never reset and should not be as the negotiated + // packetloss stats would get messed up if they were + // as such, use UdpConnection::ConnectionAge to determine how long they have been accumulating + ///////////////////////////////////////////////////////////////////////// + udp_int64 totalBytesSent; + udp_int64 totalBytesReceived; + udp_int64 totalPacketsSent; // total packets we have sent + udp_int64 totalPacketsReceived; // total packets we have received + udp_int64 crcRejectedPackets; // total packets on our connection that have been rejected due to a crc error + udp_int64 orderRejectedPackets; // total packets on our connection that have been rejected due to an order error (only applicable for ordered channel) + udp_int64 duplicatePacketsReceived; // total reliable packets that we received where we had already received it before and threw it away + udp_int64 resentPacketsAccelerated; // number of times we have resent a packet due to receiving a later packet in the series + udp_int64 resentPacketsTimedOut; // number of times we have resent a packet due to the ack-timeout expiring + udp_int64 applicationPacketsSent; + udp_int64 applicationPacketsReceived; + udp_int64 iterations; // number of times this connection has been given processing time + udp_int64 corruptPacketErrors; // number of misformed/corrupt packets + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // these statistics are only valid if clock-sync'ing is enabled (highly recommended) (will be valid on both client and server side) + // these statistics are reset by PingStatReset and are negotiated periodically by the clock-sync stuff (Params::clockSyncDelay) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + int masterPingAge; // only valid (and applicable) on client side + int masterPingTime; + int averagePingTime; + int lowPingTime; + int highPingTime; + int lastPingTime; + int reliableAveragePing; // the average time (over last 3 acks) for a reliable packet to get acked (when packet is not lost) + udp_int64 syncOurSent; // total packets we have sent at time they reported their numbers + udp_int64 syncOurReceived; // total packets we have received at time they reported their numbers + udp_int64 syncTheirSent; // total packets they have sent + udp_int64 syncTheirReceived; // total packets they have received + float percentSentSuccess; + float percentReceivedSuccess; +}; + +class UdpIpAddress +{ + public: + UdpIpAddress(unsigned int ip = 0); + unsigned int GetAddress() const { return(mIp); } + char *GetAddress(char *buffer) const; + bool operator==(const UdpIpAddress& e) const { return(mIp == e.mIp); } + protected: + unsigned int mIp; +}; + +class UdpMisc +{ + //////////////////////////////////////////////////////////////////////////////////////////// + // This is a group of miscellaneous function used as part of the implementation + // The application is free to use these helper functions as well if finds them useful. + // they are stuck into this class so as to avoid conflicts with the application + //////////////////////////////////////////////////////////////////////////////////////////// + public: + + + // note: on these clock functions, ClockElapsed and ClockDiff are designed to provide a time difference that will safely not overflow an 'int'. + // overflowing can occur in situations where the initial stamp is 0. Since a ClockStamp is a 64-bit number, we also avoid promoting + // other application values involved in the calculation to the 64-bit level by using these helper-functions. If you don't mind doing everything + // in 64-bit values, then feel free to do simple math on the clock-stamps instead of using the functions. + typedef udp_int64 ClockStamp; + static ClockStamp Clock(); // returns a timestamp (most likely in milliseconds) + static int ClockElapsed(ClockStamp stamp); // returns a elapsed time since stamp in milliseconds (if elapsed is over 23 days, it returns 23 days) + static int ClockDiff(ClockStamp start, ClockStamp stop); // returns a time difference in milliseconds (if difference is over 23 days, it returns 23 days) + + static int Crc32(const void *buffer, int bufferLen, int encryptValue = 0); // calculate a 32-bit crc for a buffer (encrypt value simple scrambles the crc at the beginning so the same packet doesn't produce the same crc on different connections) + static int Random(int *seed); // random number generator + static void Sleep(int milliseconds); + + static ushort LocalSyncStampShort(); // gets a local-clock based sync-stamp. (only good for timings up to 32 seconds) + static uint LocalSyncStampLong(); // gets a local-clock based sync-stamp. (good for timings up to 23 days) + + // returns the time difference between two sync-stamps that are based on the same clock + // for example, two LocalSyncStampShort stamps can be compared. Only differences up to + // 32 seconds can be timed. There is also a UdpConnect::ServerSyncStampShort function that returns + // you a sync-stamp that you can compare with ServerSyncStampShort values generated on other + // machines that are synchronized against the same server. This ServerSyncStampShort function and + // this delta time function serve as the basis for calculating one-way travel times for packets. + static int SyncStampShortDeltaTime(ushort stamp1, ushort stamp2); + static int SyncStampLongDeltaTime(uint stamp1, uint stamp2); // same as Short version only no limit + + // used to alloc/resize/free an allocation previously created with this function + // rounding causes that it pre-allocates additional space up to the rounded buffer size + // this allows the function to be called over and over again as tiny members are added, yet + // only do an actual realloc periodically. A 4-byte header is invisibly prepended on the + // allocated block such that it can keep track of the actual size of the block. The built-in + // memory manager does a little bit of this, but if you know you have an allocation that is likely + // to grow quite a bit, you can set the round'ing size up to a fairly large number and avoid + // unnecessary reallocs at the cost of a little potentially wasted space + // initial allocations are done by passing in ptr==NULL, freeing is done by passing in bytes==0 + static void *SmartResize(void *ptr, int bytes, int round = 1); + + // the following two functions store values in the buffer as a variable length (B1, 0xffB2B1, 0xffffffB4B3B2B1) + // such that values under 254 take one byte, values under 65535 take three bytes, and larger values take seven bytes + static uint PutVariableValue(void *buffer, uint value); // returns the number of bytes it took to store the value in the buffer + static uint GetVariableValue(const void *buffer, uint *value); // returns the number of bytes it took to get the value from the buffer + + // these functions are used to aid in portability and serve to ensure that the packet-headers are interpretted in the same + // manner on all platforms + static int PutValue64(void *buffer, udp_int64 value); // puts a 64-bit value into the buffer in big-endian format, returns number of bytes used(8) + static int PutValue32(void *buffer, uint value); // puts a 32-bit value into the buffer in big-endian format, returns number of bytes used(4) + static int PutValue24(void *buffer, uint value); // puts a 24-bit value into the buffer in big-endian format, returns number of bytes used(4) + static int PutValue16(void *buffer, ushort value); // puts a 16-bit value into the buffer in big-endian format, returns number of bytes used(2) + + static udp_int64 GetValue64(const void *buffer); // gets a 64-bit value from the buffer in big-endian format + static uint GetValue32(const void *buffer); // gets a 32-bit value from the buffer in big-endian format + static uint GetValue24(const void *buffer); // gets a 24-bit value from the buffer in big-endian format + static ushort GetValue16(const void *buffer); // gets a 16-bit value from the buffer in big-endian format + + static LogicalPacket *CreateQuickLogicalPacket(const void *data, int dataLen, const void *data2 = NULL, int dataLen2 = 0); + + // looks up the specified name and translates it to an IP address + // this is a blocking call that can at times take a significant amount of time, but will generally be fast (less than 300ms) + static UdpIpAddress GetHostByName(const char *hostName); +}; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // The purpose of the LogicalPacket class is to provide means whereby multiple connections can share + // a queued packet to save having each connection make its own copy. It is highly recommended that + // logical packets be used for optimal performance when sending reliable data as well, since internally + // it will just end up creating one for you anyhow (LogicalPackets are used by the reliable layer to + // hold onto the data until it is acknowledged) + // + // Logical packets passed in are never modified by the udp library. + // After calling Send, application should immediately Release the logical packet if they don't need it for + // something else (like sending to another connection). The udp library will addref it if it decided it + // wants to keep it around past the Send call (for reliable data, it will always hang onto it, for unreliable + // data, it will only hang onto it if it gets promoted to reliable due to size.) + // + // Application is encouraged to derive their own application-specific packet classes from the LogicalPacket + // The object is required to be able to provide a pointer to the raw packet data that will remain valid + // for as long as the LogicalPacket object exists. + // + // for example: + // class PlayerLoginPacket : public LogicalPacket + // { + // public: + // PlayerLoginPacket(char *userName, char *password) { mData.packetType = cPacketTypePlayerLogin; strcpy(mData.userName, userName); strcpy(mData.password, password); } + // virtual const void *GetDataPtr() const { return(&mData); } + // virtual int GetDataLen() const { return(sizeof(mData)); } + // protected: + // virtual ~PlayerLoginPacket() {}; + // struct + // { + // uchar packetType; + // char userName[32]; + // char password[32]; + // } mData; + // }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////// +class LogicalPacket +{ + public: + LogicalPacket(); + virtual void AddRef() const; + virtual void Release() const; + virtual int GetRefCount() const; + + virtual void *GetDataPtr() = 0; + virtual const void *GetDataPtr() const = 0; + virtual int GetDataLen() const = 0; + virtual void SetDataLen(int len) = 0; + + // returns true if this logical packet is an internal packet + // by default this returns false, and applications should not override this function to do + // otherwise. Basically, if this returns true, it tells the udp library that the packet has + // an internal-packet header (starts with a zero byte) on it that should not be escaped when sent. + // The packet will ultimately be processed by the internal udp library code on the other side. + // The purpose of this is to support features such as GroupLogicalPacket, such that the application + // can prep grouped packets and then send them via traditional means (it's how the udp library + // knows that the packet is a group packet instead of just a application-packet that happens to start with 0) + virtual bool IsInternalPacket() const; + protected: + virtual ~LogicalPacket(); // protected since the class should only be deleted by Releasing it + + protected: + mutable int mRefCount; + + protected: + friend class UdpReliableChannel; + mutable const LogicalPacket *mReliableQueueNext; // owned/managed by the reliable channel object as an optimizations, do not touch + // NULL = unclaimed, point-to-self = claimed, but end of list +}; + +class SimpleLogicalPacket : public LogicalPacket +{ + // this class is a simply dynamically allocated packet. Applications are welcome to use it, but + // it was originally created to allow the internal code to handle reliable data that was sent + // via the Send(char *, int) api call. + public: + SimpleLogicalPacket(const void *data, int dataLen); // data can be NULL if you want to populate it after it is allocated (get the pointer and write to it) + virtual void *GetDataPtr(); + virtual const void *GetDataPtr() const; + virtual int GetDataLen() const; + virtual void SetDataLen(int len); + protected: + virtual ~SimpleLogicalPacket(); + uchar *mData; + int mDataLen; +}; + +class WrappedLogicalPacket : public LogicalPacket +{ + // this class is used internally by the library such that it can get a free and clear mNext pointer + // in cases where a logical packet is being shared by multiple connections (such that the packet + // can reside in the linked-list for the reliable channel queue) + public: + WrappedLogicalPacket(UdpManager *manager); // lives in a pool + + virtual void AddRef() const; + virtual void Release() const; + virtual void *GetDataPtr(); + virtual const void *GetDataPtr() const; + virtual int GetDataLen() const; + virtual void SetDataLen(int len); + protected: + virtual ~WrappedLogicalPacket(); + const LogicalPacket *mPacket; + + protected: + friend class UdpManager; + void SetLogicalPacket(const LogicalPacket *packet); + UdpManager *mUdpManager; + WrappedLogicalPacket *mAvailableNext; + + WrappedLogicalPacket *mCreatedNext; + WrappedLogicalPacket *mCreatedPrev; +}; + +template class FixedLogicalPacket : public LogicalPacket +{ + // this class is similar to the SimpleLogicalPacket in that it is designed to just store raw + // data of various sizes. The difference here is that this class may be created at compile + // time to be any size desired (via template parameters), and is more efficient than the + // SimpleLogicalPacket when used (it avoid the internal alloc as the data is inline). The + // UdpMisc::CreateQuickLogicalPacket function automatically attempts to use this for small + // packets and will fall back and use SimpleLogicalPackets for larger packets. + public: + FixedLogicalPacket(const void *data, int dataLen); + virtual void *GetDataPtr(); + virtual const void *GetDataPtr() const; + virtual int GetDataLen() const; + virtual void SetDataLen(int len); + protected: + uchar mData[t_quickSize]; + int mDataLen; +}; + +template class StructLogicalPacket : public LogicalPacket +{ + // this class is designed to turn any raw struct into a logical packet. The application + // can then access the struct through thisLogicalPacket->mStruct.structMember. The template + // will take care of all the wrappings necessary to turn the struct into a fully functional + // logical packet that can be sent to a connection. As a word of caution, sending struct's + // across the wire is not considered very portable. There are byte-ordering and structure-packing + // issues that are platform dependent. At a minimum, you will probably want to make sure that + // struct's you use in this way are packed to 1-byte boundaries, such that you are not sending + // wasteful information. Do not attempt to send the member-content of classes (particularly classes + // with virtual functions) via this method as they may contain hidden data-members (such as pointers + // to vtables). + public: + StructLogicalPacket(T *initData = NULL); + virtual void *GetDataPtr(); + virtual const void *GetDataPtr() const; + virtual int GetDataLen() const; + virtual void SetDataLen(int len); + protected: + T mStruct; +}; + +class GroupLogicalPacket : public LogicalPacket +{ + // this class is a helper object intended for use by the application (it is not used internally) + // It allows you to add multiple application-packets and them send them all as an autonomous unit. + // The receiving end will automatically split the packet up and send them to the application callback function + // as if the individual packets had all been sent one at at time. + // + // This facility is primarily intended for reliable data, though it can be used to group unreliable data as well. + // Grouping unreliable data together would effectively give you an all-or-nothing type of delivery system. Be + // mindful that if the group of packets gets larger than max-raw-packet-size, it will end up getting + // promoted to being a reliable-packet with the associated overhead involved in that. + // + // You might be wondering what the advantage would be of grouping packets together at the application level as opposed to + // just letting the internal multi-buffer take care of the problem for you. For starters, the internal multi-buffer + // is incapable of combining partial-packets, so you end up sending less than maximum-size packets if you let the + // internal layer take care of it. Additionally, there is additional overhead in that if you send 100 tiny logical + // packets, each one will need to be ack'ed by the receiving end (even though they are getting combined down at the physical-packet + // level in order to reduce UDP overhead). Finally, grouping them together at the higher level will allow the + // logical-packet compression helper routines to operate on larger chunks of data at a time, which tends to improve + // compression efficiency. The downside to combining is that none of the application packets get delivered until the + // entire group arrives, then all are delivered in one fell swoop...the net effect being that in order to gain these efficeincies, + // the first-packet takes longer to be delivered to the application than it otherwise would have (while the last packet will in theory + // get there is less time as there will be less overhead). + // + // Internal packets can be added to the group (though that is somewhat unlikely), which means you can add a group to a group + public: + GroupLogicalPacket(); + void AddPacket(const LogicalPacket *packet); // cannot add internal logical packets to the group + void AddPacket(const void *data, int dataLen); + virtual void *GetDataPtr(); + virtual const void *GetDataPtr() const; + virtual int GetDataLen() const; + virtual void SetDataLen(int len); + virtual bool IsInternalPacket() const; // returns true if this is considering an internal logical packet type + + protected: + virtual ~GroupLogicalPacket(); + void AddPacketInternal(const void *data, int dataLen, bool isInternalPacket); + + uchar *mData; + int mDataLen; +}; + +class PooledLogicalPacket : public LogicalPacket +{ + // a pooled logical packet is like other logical packets, only when it's refCount gets down to 1, it notifies + // its manager to add it back to the pool. (The manager keeps the last ref count on the packet) + public: + PooledLogicalPacket(UdpManager *manager, int len); + + virtual void AddRef() const; + virtual void Release() const; + virtual void *GetDataPtr(); + virtual const void *GetDataPtr() const; + virtual int GetDataLen() const; + virtual void SetDataLen(int len); + protected: + virtual ~PooledLogicalPacket(); + + uchar *mData; + int mDataLen; + int mMaxDataLen; + protected: + friend class UdpManager; + void SetData(const void *data, int dataLen, const void *data2 = NULL, int dataLen2 = 0); + UdpManager *mUdpManager; + PooledLogicalPacket *mAvailableNext; + PooledLogicalPacket *mCreatedNext; + PooledLogicalPacket *mCreatedPrev; +}; + + +class AddressHashTableMember : public HashTableMember +{ +}; + +class ConnectCodeHashTableMember : public HashTableMember +{ +}; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // The purpose of the UdpManager is to manage a set of connections that are coming in on a particular port. + // Typically an application will only have one UdpManager taking care of all incoming connections. The + // exception is if the application is talking to two distinct sets of individuals. For example, the leaf + // server application might have a UdpManager to manage the connections to all the users/players who will + // be connecting. It may then have a second UdpManager to manage its connection to a master server + // someplace (though in theory it could use one UdpManager for everything). + // + // The UdpManager owns the solitary socket that all data being sent/received by any of the managed connections uses. + // When the UdpManager is created, it is given a port-number that it uses for this purpose. The UdpManager is capable + // of establishing new connections to other UdpManager, or it is also capable of accepting new connections. + //////////////////////////////////////////////////////////////////////////////////////////////////////////// +class UdpManager +{ + public: + // encryption methods are allowed to change the packet-size, so raw packet level compression + // would actually be implemented as a new encryption-method if needed + // the user-supplied method requires that the both ends of the connection have the user-supplied + // encrypt handler functions setup to correspond to each other. + enum EncryptMethod { cEncryptMethodNone + , cEncryptMethodUserSupplied // use the UdpConnectionHandler::OnUserSuppliedEncrypt function + , cEncryptMethodUserSupplied2 // use the UdpConnectionHandler::OnUserSuppliedEncrypt2 function + , cEncryptMethodXorBuffer // slower xor method, but slightly more encrypted + , cEncryptMethodXor // faster using less memory, slightly less well encrypted, use this one as first choice typically + , cEncryptMethodCount }; + + enum ErrorCondition { cErrorConditionNone, cErrorConditionCouldNotAllocateSocket, cErrorConditionCouldNotBindSocket }; + + enum { cReliableChannelCount = 4 }; + enum { cEncryptPasses = 2 }; + enum { cProtocolVersion = 2 }; // protocol version must match on both ends, or connect packets are simply ignored by the server + enum { cHardMaxRawPacketSize = 16384 }; + enum { cHardMaxOutstandingPackets = 30000 }; // don't change this + + struct ReliableConfig + { + int maxOutstandingBytes; // maximum number of bytes that are allowed to be outstanding without an acknowledgement before more are sent (default=200k) + int maxOutstandingPackets; // maximum number of physical reliable packets that are allowed to be outstanding (default=400) + int maxInstandingPackets; // maximum number of incoming reliable packets it will queue for ordered delivery while waiting for the missing packet to arrive (should generally be same as maxOutstandingPackets setting on other side) (default=400) + int fragmentSize; // this is the size it should fragment large logical packets into (default=0=max allowed=maxRawPacketSize) + int trickleSize; // maximum number of bytes to send per trickleRate period of time (default=0=max allowed=fragmentSize) + int trickleRate; // how often trickleSize bytes are sent on the channel. (default=0=no trickle control) + int resendDelayAdjust; // amount of additional time (in ms) above the average ack-time before a packet should be deemed lost and resent (default=300) + int resendDelayPercent; // percent average ack-time it should use in calculating the resend delay (default = 125 or 125%) + int resendDelayCap; // maximum length of resend-delay that will ever be assigned to an outstanding packet. (default=5000) + int congestionWindowMinimum; // the minimum size to allow the congestion-window to shrink. This defaults to 0, though internally it the implementation will never let the window get smaller than a single raw packet (512 bytes by default). + // This setting is more intended to allow the application to set a higher minimum than that, effectively allowing the application to tell the connection to refuse to slow itself + // down as much. See release notes for more details. + bool outOfOrder; // whether incoming packets on this channel should be allowed to be delivered out of order (default=false) + bool processOnSend; // whether a packet sent on this channel should immediately be processed to determine if it can be sent, or whether it should wait for the next give time (default=false) + bool coalesce; // whether the reliable-channel should attempt to coalesce data to reduce ack's needed (note: rarely change this to false)(default=true) + bool ackDeduping; // whether ack-packets stuck into the low-level multi-buffer should be deduped (note: rarely change this to false)(default=true) + }; + + struct Params + { + Params(); // constructor merely sets default values for the structure members + + // instead of specifying a callbackConnectRequest pointer, you can also specify a handler object to receive + // the callback directly. To have the UdpManager call your object directly when connection requests come in, you + // simply need to derive your class (multiply if necessary) from UdpManagerHandler, then you can set this + // pointer equal to your object and the UdpManager will call it as appropriate. The UdpConnection object + // also has a handler mechanism that replaces the other callback functions below, see UdpConnection::SetHandler + // if a handler is specified, the callback function is ignored, even if specified. + // default = NULL (not used) + UdpManagerHandler *handler; + + // this is the maximum number of connections that can be established by this manager, any incoming/outgoing connections + // over this limit will be refused. On the client side, this typically only needs to be set to 1, though there + // is little harm in setting this number larger. + // default = 10 + int maxConnections; + + // this is the port number that this manager will use for all incoming and outgoing data. On the client side + // this is typically set to 0, which causes the manager object to randomly pick an available port. On the server + // side, this port should be set to a specific value as it will represent the port number that clients will use + // to connect to the server (ie. the listening port). It's generally a good idea to give the user on the client + // side the option of fixing this port number at a specific value as well as it is often necessary for them to + // do so in order to navigate company firewalls which may have specific port numbers open to them for this purpose. + // default = 0 + int port; + + // if port is not zero, setting this to a value greater than zero causes it to randomly pick a port in the range + // (port to port+portRange). This is desireable when you wish to have the manager bind to a random port within + // a specified range. If you specify a portRange to use, then port must not be 0 (since 0 means to let the OS + // pick the port to use). 0=don't use a range, bind only to the port specified in 'port' setting. + // default = 0 + int portRange; + + // the size of the outgoing socket buffer. The client will want to set this fairly small (32k or so), but the server + // will want to set this fairly large (512k) + // default = 64k + int outgoingBufferSize; + + // the size of the incoming socket buffer. The client will want to set this fairly small (32k or so), but the server + // will want to set this fairly large (512k) + // default = 64k + int incomingBufferSize; + + // the purpose of the packet history is to make debugging easier. Sometimes a processed packet will cause the server + // to crash (due to a bug or possibly just corruption). Typically the application will put an exception handler around + // the main loop and call UdpManager::DumpPacketHistory when it is triggered. This will dump a history of the last + // packets received such that they can be analyzed by hand to determine if one of them caused the problem and why. + // The packet history is done at the raw packet level (before logical packet re-assembly). This is the number of raw + // packets to buffer in the history. Typically this can be set fairly small (maybe 1000) since packets older than + // that have little debug value. (uses maxRawPacketSize * thisValue of memory) + // default = 100 + int packetHistoryMax; + + // how long a connection will wait before sending a keep-alive packet to the other side of the connection + // set this to 0 to never send keep-alive packets (typically the server will set this to 0 and never keep alive + // the client, but the client will typically set this some value that will ensure that the server will not kick them + // for inactivity. Keep alive packets are only sent if no other data is being sent. + // default = 0 + int keepAliveDelay; + + // this is very similar to the keep alive delay, but serves a completely different purpose and may not have the desired + // effect on all platforms. The purpose of the keepAliveDelay above is generally to keep data flowing to the server + // so it knows that the client is still there. In this manner if the server doesn't get data within a certain period + // of time, it can know that the connection is probably dead. Sometimes it is the case that the server does not need + // to be kept alive, or at least kept alive very often (like for a chat server perhaps where nobody is talking much). + // For some people, it may be necessary to send data more frequently in order to keep their NAT mapping fresh, or their + // firewall software happy. However, we don't want to be in a situation where our server is receiving a lot more data + // than it needs to just so these people can keep their port open. I have seen NAT's that lose mappings in as short + // as 20 seconds. What this feature does is a bit tricky. It changes the time-to-live (TTL) for a special keep-alive + // packet to some small value (4) which is enough for the packet to get past firewalls and NAT's, but not make it all + // the way to our server. In this manner, the port gets kept alive, but we don't waste bandwidth with these packets. + // These special packets are not counted statistically in any way and they do not reset any timers of any kind. Their + // sole purpose is to keep the port alive on the client side. Any other data (include standard keepAlive packets will + // reset the timer for this packet, so obviously the portAliveDelay must be smaller than the keepAliveDelay in order + // to be meaningful. + // default = 0 = off + int portAliveDelay; + + // whether this UdpManager should send back an unreachable-connection packet when it receives incoming data from + // a destination it does not have a UdpConnection object representing. This is the equivalent of a port-unreachable + // ICMP error packet, only taken up one level further to the virtual-connection object (UdpConnection). Imagine a + // server that has terminated a client's connection, but the client didn't get notice that such termination had occurred. + // The client would still think the connection was good and continue to try and send data to the server, but the data + // would simply be getting lost and queued up indefinately on the client. The client doesn't get port unreachable errors + // since the server is using the same port for lots of people. Having this true causes the server to notify the client + // that its connection is dead. If a client finds out it is terminated in this manner, it will set its disconnect reason + // to cDisconnectReasonUnreachableConnection. + // default = true + bool replyUnreachableConnection; + + // the UdpLibrary supports a feature whereby a connection can remap its ip/port on-the-fly. How it works is, if a client + // gets back a connection-unreachable error, it will send out a request for the server to remap its connection to the + // new port that it is coming from. This will happen if a NAT expires and the next outgoing packet causes a different + // port-mapping to be selected. The UdpLibrary can recover from this situation. The icmpErrorRetryPeriod must be + // set to a reasonably high value, such that the client has time to send data and request the remapping before icmp + // errors are processed. This value determines not only whether a server will honor a remapping request, but also + // whether the client will attempt a remapping request. The terms client as server are used loosely here, as it is + // possible for the server to request the remapping (though it is extremely unlikely that the server end + // of a connection will have its port changed on-the-fly). + // default = true + bool allowPortRemapping; + + // this is the same as allowPortRemapping, only it allows the full IP address to be remapped on-the-fly. I + // recommend that you do NOT enable this, as it represents a fairly serious security loophole, whereby a hacker could + // cause random people to be disconnected, and in theory possibly even hi-jack their connection. The odds of this + // actually being able to happen are incredibly rare unless the hacker has been snooping your packet stream, as they + // would effectively have to guess two 32-bit random numbers (one generated by the client and one generated by the server) + // default = false + bool allowAddressRemapping; + + + // how long (in milliseconds) the manager will ignore ICMP errors to a particular connection before it will act upon + // them and terminate the connection. When a packet is successfully received into the connection in question, the + // retry period is reset, such that the next time an ICMP error comes in, it has another period of time for it + // to resolve the issue. This servers a couple purpose, 1) it can allow for momentary outages that can be recovered + // from in short order, and 2) it is necessary for the port-remapping feature to work properly in situations where + // the server may send data to the old-port before the remapping negotiations are completed. In order for the + // remapping feature to work properly, this value should be set to larger than the longest time a connection typically + // goes without receiving data from the other side. (0=no grace period) + // default = 5000 + int icmpErrorRetryPeriod; + + // how long a connection will go without receiving any data before it will terminate the connection + // set this to 0 to never have the connection terminated automatically for receiving no data. + // the application will receive an OnTerminated callback when the connection is terminated due to this timeout + // this setting can be overridden on a per-connection basis by calling UdpConnection::SetNoDataTimeout + // default = 0 + int noDataTimeout; + + // when reliable data is sent, it remains in the queue until it is acknowledged by the other side. When you query + // the reliable channel status, you can find out the age of the oldest piece of data in the queue that has been sent + // yet has not been acknowledged yet. As a general rule, if things are operating correctly, it should be very rare + // for something that has been sent to not be acknowledged within a few seconds, even if resending had to occur. + // Eventually, the sender could use this statistic to determine that the other side is no longer talking and terminate + // the connection. In past version of the UdpLibrary, the sending-application has checked this statistic itself. This + // parameter will cause the UdpLibrary to monitor this for you and automatically change the connection to a disconnected + // state when the value goes over this setting (in milliseconds) (0 means do not perform this check at all) + // The default is set fairly liberally, on client-side connections, you could safely set this to as low as 30 seconds + // allowing for quicker realization of lost connections. Often times the connection will realize it is dead much quicker + // for other reasons. When disconnected due to this, the disconnect reason is set to cDisconnectReasonUnacknowledgedTimeout + // default = 90000 + int oldestUnacknowledgedTimeout; + + // maximum number of bytes that are allowed to be pending in all the reliable channels combined + // before it will terminate the connection with a cDisconnectReasonReliableOverflow. If this value is set to + // zero, then it will never overflow (it will run out of memory and crash first). If your application wants + // to do something other than disconnect on this condition, then the application will have to periodically check the status + // itself using UdpConnection::TotalPendingBytes and act as appropriate. + // default = 0 + int reliableOverflowBytes; + + // how long a connection will hold onto outgoing data in hopes of bundling together future outgoing data in the same + // raw packet (specified in milliseconds) + // setting this to 0 will cause it to effectively flush at the end of every frame. This is generally desireable in + // cases where frame-rates are slow (less than 10 fps), or for internal LAN connections. + // default = 50 + int maxDataHoldTime; + + // how much data a connection will hold onto before sending out a raw packet + // (0=no multi-packet buffering, all application sends result in immediate raw packet sends) + // (-1=use same value as maxRawPacketSize) + // this value will be effectively ignored if it is larger than the maxRawPacketSize specified below) + // default = -1 + int maxDataHoldSize; + + // maximum size that a raw packet is allowed to be, this must be set the same on both client and server side + // the reason for this is the incoming packets need some handling before the connection object they are associated + // with is determined. This means we can't have different connection objects using different sizes, so this + // effectively means all clients must be the same size as the server. Normally this won't be a problem, this value + // should likely be set to something like 496 or 1400 and then just kept there forever. + // the raw packet size won't have a significant effect on anything. Must be at least 64 bytes. + // default = 512 + int maxRawPacketSize; + + // how large the hash-table is for looking up connections. It takes 4*hashTableSize memory and it is recommended + // you set it fairly large to prevent collisions (10 times maximum number of connections should be fine) + // default = 100 + int hashTableSize; + + // whether a priority queue should be used. If a priority queue is not used, then everytime + // UdpManager::GiveTime is called, every UdpConnection object gets processing time as well. + // It is thought that if traffic is heavy enough, that managing the priority queue may end up being more + // cpu time than giving everybody time (as it is possible everybody would end up getting time anyways) + // if not using a priority queue, it is recommended that you GiveTime only periodically (every 50ms for example) + // the more often you GiveTime, the more critical the priority-queue is at reducing load compared to not using it + // default = false + bool avoidPriorityQueue; + + // how often the client synchronizes its timing-clock to the servers (0=never)(specified in ms). + // the server-side MUST specify this as 0, or else the server will attempt to synchronize it's clock with + // the client as well (which would just be a waste of packets generally, though would work) + // the client should generally always set this feature on by setting it to something like 45000 ms. + // the clock-sync is used to negotiate statistics as well, so if you are not using clock sync, then you + // will not be able to get packetloss/lag statistics for the connection. If you are using it, then you will + // be able to get these statistics from either end of the connection. + // default = 0 + int clockSyncDelay; + + + // these two values control the number of packets that the UdpManager will create in its packet pool. The packet pool + // is a means by which the UdpManager avoid allocating logical packets for every send, by instead using them from the pool. + // you need to specify the size of the packets that are in the pool. Then, when somebody calls UdpManager::CreatePacket, + // if the packet being created is small enough and there is room available, it grabs one from the pool, otherwise it + // creates a new non-pooled one. The largest the pool will ever grow is pooledPacketMax, and the memory used will be roughly + // pooledPacketMax * pooledPacketSize. You should be generous with the pool packet size and the pool max in order + // to avoid having to do allocations as much as possible + // pooledPacketMax default = 1000, (0 = don't use memory pooling at all) + int pooledPacketMax; + // pooledPacketInitial is the number of packets to allocate in the initial pool. The only reason to set this to + // something other than the default, which is 0, is to avoid having your memory fragmented as the pool grows on demand. + int pooledPacketInitial; + // as a general rule, ou should leave this at -1. This is critical. If the pooledPacketSize is smaller than the + // maxRawPacketSize, then all the coalescing that occurs in the reliable channel will result in allocations. You + // would just as well not have a pool if you don't set this at least as large as the maxRawPacketSize. If your application + // tends to send largish packets (larger than maxRawPacketSize), setting this large enough to cover those might buy you + // some speed as well. + // pooledPacketSize defaults to -1, which means use the same as maxRawPacketSize + int pooledPacketSize; + + // the maximum number of entries to allow in WrappedLogicalPacket pool before they start getting destroyed + // depending on the nature of the sending that you are doing, and the volume that you are doing, you may + // want to increase this value. WrappedLogicalPacket objects are used in situations where you send the + // same LogicalPacket object to multiple connections, and that LogicalPacket object is larger than the + // maxRawPacketSize. As you can imagine, this is a relatively rare event, so the pool doesn't need to be + // very large. Like hte standard pool, this is merely an optimization to avoid allocations, it will work + // either way. A server application may wish to to set this number a little bit higher. Unlike the + // packet-pool, we don't bother to even pre-create these things, they are just created for the first time + // when demanded. + // default = 1000 + int wrappedPoolMax; + + // whether ICMP error packets should be used to determine if a connection has gone dead. + // when the destination machine is not available, or there is no process on the destination machine + // talking on the port, then a ICMP error packet will sometimes be returned to the client when a packet is sent. + // Processing ICMP errors will often allow the client machine to quickly determine that the other end of the + // connection is no longer reachable, allowing it to quickly change to a disconnected state. The downside + // of having this feature enabled is that it is possible that if there is a network problem along the route, that + // the connection will be terminated, even though the hardware along the route may correct the problem by re-routing + // within a couple seconds. If you are having problems with clients getting disconnected for ICMP errors (see + // disconnect reason), and you know the server should have remained reachable the entire time, then you might + // want to set this setting to false. The only downside of setting this to false is that it might take the + // client a bit longer to realize when a server goes down. + // default = true + bool processIcmpErrors; + + // whether ICMP errors should be used to terminate connection negotiations. By default, this is set to + // false, since generally when you are trying to establish a new connection (ie. negotiating), you are + // are willing to wait for timeout specified in the EstablishConnection call, since it may be a case + // that the client process gets started slightly sooner than the server process. + // default = false + bool processIcmpErrorsDuringNegotiating; + + + // during connection negotiation, the client sends connect-attempt packets on a periodic basis until the + // server responds acknowledging the connection. This value represents how often the client sends those packets. + // By default this is set to 1000 or 1 second and generally should not be messed with. + int connectAttemptDelay; + + + // This settings allows you to bind the socket used by the library to a specific IP address on the machine. + // Normally, and by default, the library will bind the socket to any address in the machine. This setting should + // not be messed with unless you really know what you are doing. In multi-homed machines it is generally NOT + // necessary to bind to a particular address, even if there are firewall issues involved, and even if you want + // to limit traffic to a particular network (firewalls do a better job of serving that purpose). If you are having + // problems communicating with a server on a multi-homed machine and think this might solve the problem, think again. + // You most likely need to configure the OS to route data appropriately, or make sure that internal network clients + // are connecting to the machine via the machines internal IP address (or vice versa). + // by default this string is empty meaning the socket is bound to any address. To bind to specific IP address, it + // should be entered in a.b.c.d form (DNS names are not allowed). Figuring out what IP addresses are in the machine + // and which one should be bound to is left as an exercise for the user. + char bindIpAddress[32]; + + // you need to specify the characteristics of the various reliable channels here, generally you will want + // to make sure the client and server sides set these characteristics the same, though it is technically not + // required. Each channel decides locally whether it will accept out of order delivery on a particular channel or not. + // (note: out of order delivery is a tiny optimization that simply lets the channel deliver the packet the moment it + // arrives, even if previous packets have not yet arrived). Likewise trickle-rates are for outgoing data only obviously. + // reliable channel managers are not actually created internally until data is actually sent on the channel, so there + // is no overhead associated with channels that are not used, and you need not specify characteristics for channels + // that you know you will not be using. + // default = 400 packets in&out/200k outstanding, ordered, no trickle (all channels) + ReliableConfig reliable[cReliableChannelCount]; + + + // when user supplied encrypt and decrypt routines are specified, it becomes necessary to tell the UdpManager + // how many bytes the encryption process could possibly expand the chunk of data it was given. Often times this + // will simply be 0, but if the user supplied routines attempt compression, then it's possible that expansion could + // could actually occur. Typically I would have the compression routines put a leader-byte on the chunk of data + // specifying whether it was compressed or not. Then if the compression didn't pan out, it could alway abort and just + // prepend the byte in question and the rest of the data. In that sort of algorithm, you would set this value to 1 + // since the most it could ever expand beyond the original is the 1-byte indicator on the front. It's possible that + // a particular encryption routine might want to append a 4-byte encryption key on the end of the chunk of data, in + // which case you would need to reserve 4 bytes. This is necessary as it allows the udp library to guarantee that + // no packet will be larger than maxRawPacketSize, and at the same time ensures that the destination buffers supplied + // to the encrypt/decrypt functions will have enough room. Obviously this value is ignored if the encryptMethod + // is not set to cEncryptMethodUserSupplied. + // default = 0 + int userSuppliedEncryptExpansionBytes; + int userSuppliedEncryptExpansionBytes2; + + // the following parameters are used to simulate various line conditions that may occur. This allows the application + // program to test how well it performs under various conditions + // simulating an incoming byte-rate is simply done by internally not polling the port for a certain period of time + // after receiving a packet based on the amount of data in the last packet received + // simulating outgoing byte-rate is a bit more difficult as it requires queuing the data to be sent and slowly trickling + // it out to the socket. Simulating is very much akin to simulating lag; however, it is not exactly the same thing. It's + // possible to have low-bandwidth, yet good or bad ping times depending on how many hops away you are, but this should good + // enough for most testing. + // default = 0 + int simulateOutgoingLossPercent; // 0=no loss, 100=100% loss + int simulateIncomingLossPercent; // 0=no loss, 100=100% loss + int simulateIncomingByteRate; // simulates the incoming bandwidth specified (in bytes/second) (0=off) + + + // simulates the outgoing bandwidth specified (in bytes/second) (0=off) + // an interesting side-note of the outgoing byte-rate limit. In order to accomplish + // this we to queue stuff on the client and then pace the sending of it. When the client + // terminates the connection, the last thing it does is send a terminate packet to the other side + // The UdpManager is playing the role of the operating system when byte-limiting is on insofar as it + // is the UdpManager that is managing the virutal socket buffer. Most of the time when the client + // terminates, it also terminates the UdpManager. When you are using the outgoing byte rate simulator, + // terminating the UdpManager is akin to rebooting the box. The net effect is that any outgong packets + // in the queue are lost. This includes the ever important 'terminate-packet' that is sent to the server + // at the last moment. This means that when you are simulating an outgoing byte rate, that the server + // will never see the client properly terminate, UNLESS the client gives the UdpManager processing time + // for several seconds (long enough to flush the queue at the specified byte-rate) after the connection + // is destroyed, but before the UdpManager is destroyed. + // note: when using this setting, it is critical that you also set the simulateOutgoingOverloadLevel as + // well in order to simulate the limited-size socket buffer that the OS would have. + int simulateOutgoingByteRate; + + + // this is the number of bytes in the outgoing queue total before packets are simply lost + // this number is used to simulate the condition where you can't simply throw tons of data at a modem + // and expect it to not lose any of it. Normally this data would queue up in the socket buffer for however + // big your outgoing socket buffer is; however, if you are simulating limited outgoing bandwidth, then + // the socket buffer never actually has any data in it, instead all the queuing is taking place in the + // simulation queue. In order to handle this properly, we have to cap the outgoing simulation queue size + // total for all connection. + int simulateOutgoingOverloadLevel; + + + // this is the number of bytes in the outgoing queue/connection before packets are simply lost (0=infinite) + // this number is used to simulate the condition where some router or terminal-server down + // the line has a limited buffer size and once it gets overloaded it just throws away new incoming + // packets. The reason we have to do this is because otherwise our internal simulation queue + // would grow forever. The loss of packets is necessary for the flow control stuff to do its + // job. The incoming side uses the socket-buffer for this purpose and as such can simply + // set the incoming socket buffer to the desired size and the OS will throw away stuff that comes + // in yet doesn't fit. Since this parameter is supposed to simulate a line condition downward + // toward the destination and we may have multiple destinations in our queue, this handles it properly + // by tracking the amount of data queued for each destination ip/port address (it does this by actually + // back-link up to the UdpConnection object). + int simulateDestinationOverloadLevel; + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // the following values are ignored by connections initiated by this manager (ie. client side) since the server will tell + // the client the values that are in effect during the connection initialization process. + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // how many additional bytes of CRC data is added to raw packets to ensure integrity (0 through 4 allowed) + // default = 0 + int crcBytes; + + // which encryption method is to be used (see enumeration) (this occurs at the raw packet level) + // default = cEncryptMethodNone + EncryptMethod encryptMethod[cEncryptPasses]; + }; + + UdpManager(const Params *params); + + void SetHandler(UdpManagerHandler *handler); + UdpManagerHandler *GetHandler() const; + void SetPassThroughData(void *passThroughData); + void *GetPassThroughData() const; + ErrorCondition GetErrorCondition() const; + + // standard AddRef/Release scheme + void AddRef(); + void Release(); + int GetRefCount() const; + + + // this function MUST be called on a regular basis in order to give the manager object time to service the socket + // and give time to various connection objects that may need processing time to send/resend packets, etc. + // + // If you set maxPollingTime to 0, it will not attempt to receive any packets out of the socket. If no + // packets are gotten from the socket, then it is impossible for any packets to be forwarded to the application; + // hence, this is a good way to give the manager processing time for outgoing packets in situations + // where the application does not want to have to worry about processing incoming packets. + // + // If you set maxPollingTime to -1, it will poll at most 1 packet out of the socket at a time and process it. You + // should know that a single packet on the socket can possibly result in multiple data-packets being delivered to the application. + // For example, the incoming packet could be a previous lost reliable packet, which would cause all the stalled reliable packets to be + // delivered. Multi-buffering could also result in multiple route-packet callbacks from one physical packet coming off the socket. + // The purpose of this 1-packet feature is primarily to support the EstablishConnection process. By processing only one packet at a + // time, it gives the application a chance to check the status of a negotiating connection before any data is possibly delivered to + // that connection. This allows the client-app to loop on GetStatus checking for the connection to get established, then create all + // the infrastructure necessary to support that connection once it is established, without fear of packets being delivered before said + // infrastructure is in place. This is safe since connection-confirmation packets do not multi-buffer up. + // + // The only reason you might want to avoid giving the connections time is if you are a fairly non-packet-time critical application + // and are using the avoidPriorityQueue setting (and want to avoid using up too much CPU time while at the same + // time you don't want to avoid processing the socket). + // returns true if any incoming packets were processed during this time slice, otherwise returns false + bool GiveTime(int maxPollingTime = 50, bool giveConnectionsTime = true); + + // used to establish a connection to a server that is listening at the specified address and port. + // the serverAddress will do a DNS lookup as appropriate. This call will block long enough to resolve + // the DNS lookup, but then will return a UdpConnection object that will be in a cStatusNegotiating + // state until the connection is actually established. The application must give the manager + // object time after calling EstablishConnection or else the negotiation process to establish the + // connection will never have time to actually occur. Typically the client establishing the connection + // will call EstablishConnection, then sit in a loop calling UdpManager::GiveTime and checking to see + // if the status of the returned UdpConnection object is changed from cStatusNegotiating. This allows + // the application to look for the ESC key or timeout an attempted connection. + // This function will return NULL if the manager object has exceeded its maximum number of connections + // or if the serverAddress cannot be resolved to an IP address + // as is noted in the declaration, it is the responsibility of the application establishing the connection to delete it + // setting the timeout value (in milliseconds) to something greater than 0 will cause the UdpConnection object to change + // from a cStatusNegotiating state to a cStatusDisconnected state after the timeout has expired. It will also cause + // the connect-complete callback to be called. + UdpConnection *EstablishConnection(const char *serverAddress, int serverPort, int timeout = 0); + + // gets statistical information about this manager, which covers all connections going through this manager + // (useful for getting server-wide statistics) + void GetStats(UdpManagerStatistics *stats) const; + void ResetStats(); + + // this function will dump the packet history out to the specified filename. If no packet history is being kept (see UdmManagerParams::packetHistoryMax) + // then this function does nothing. + void DumpPacketHistory(const char *filename) const; + + // returns the ip address of this machine. If the machine is multi-homed, this value may be blank. + UdpIpAddress GetLocalIp() const; + + // returns the port the manager is actually using. This value will be the same as is specified in Params::port (or if Params::port was set to 0, this will be the dynamically assigned port number) + int GetLocalPort() const; + + // returns how long it has been (in milliseconds) since this manager last received data + int LastReceive() const; + + // returns how long it has been (in milliseconds) since this manager last sent data + int LastSend() const; + + // manually forces all live connections to flush their multi buffers immediately + void FlushAllMultiBuffer(); + + // creates a logical packet and populates it with data. data can be NULL, in which case it gives you logical packet + // of the size specified, but copies no data into it. If you are using pool management (see Params::poolPacketMax), + // it will give you a packet out of the pool if possible, otherwise it will create a packet for you. When logical + // are packets are needed internally for various things (like reliable channel sends that use the (void *, int) interface) + // they are gotten from this function, so your application can likely take advantage of pooling, even if it never bothers + // to explicitly call this function. + LogicalPacket *CreatePacket(const void *data, int dataLen, const void *data2 = NULL, int dataLen2 = 0); + + protected: + friend class PooledLogicalPacket; + void PoolReturn(PooledLogicalPacket *packet); // so pooled packets can add themselves back to the pool + void PoolCreated(PooledLogicalPacket *packet); + void PoolDestroyed(PooledLogicalPacket *packet); + + protected: + friend class WrappedLogicalPacket; + + WrappedLogicalPacket *WrappedBorrow(const LogicalPacket *lp); // used by reliable channels + void WrappedReturn(WrappedLogicalPacket *wp); + + void WrappedCreated(WrappedLogicalPacket *wp); + void WrappedDestroyed(WrappedLogicalPacket *wp); + + protected: + friend class UdpConnection; + friend class UdpReliableChannel; + + class PacketHistoryEntry + { + public: + PacketHistoryEntry(int maxRawPacketSize); + ~PacketHistoryEntry(); + + public: + uchar *mBuffer; + UdpIpAddress mIp; + int mPort; + int mLen; + }; + + Params mParams; + PacketHistoryEntry **mPacketHistory; + int mPacketHistoryPosition; + + void *mPassThroughData; + + UdpConnection *mConnectionList; // linked listed of connections + int mConnectionListCount; + + UdpConnection *mDisconnectPendingList; // linked list of connections to be released when they go to cStatusDisconnected + + ObjectHashTable *mAddressHashTable; + ObjectHashTable *mConnectCodeHashTable; + PriorityQueue *mPriorityQueue; + UdpMisc::ClockStamp mMinimumScheduledStamp; // soonest anybody is allowed to schedule themselves for more processing time + + SOCKET mUdpSocket; + UdpMisc::ClockStamp mLastReceiveTime; + UdpMisc::ClockStamp mLastSendTime; + UdpMisc::ClockStamp mLastEmptySocketBufferStamp; + int mProcessingInducedLag; // how long the currently being processed packet could have possibly been sitting in the socket-buffer waiting for processing (which is effectively how long it has been since we last quit polling packets from the socket queue) + unsigned long int mStartTtl; // initial TTL for packets (used if it is changed by the application for port-alive packets) + ErrorCondition mErrorCondition; + + UdpManagerStatistics mManagerStats; + UdpMisc::ClockStamp mManagerStatsResetTime; + + // stuff for managing the outgoing packet-rate simulation + class SimulateQueueEntry + { + public: + SimulateQueueEntry(const uchar *data, int dataLen, UdpIpAddress ip, int port); + ~SimulateQueueEntry(); + public: + uchar *mData; + int mDataLen; + UdpIpAddress mIp; + int mPort; + SimulateQueueEntry *mNext; + }; + friend class SimulateQueueEntry; + int mSimulateQueueBytes; + SimulateQueueEntry *mSimulateQueueStart; + SimulateQueueEntry *mSimulateQueueEnd; + UdpMisc::ClockStamp mSimulateNextIncomingTime; + UdpMisc::ClockStamp mSimulateNextOutgoingTime; + + // pool management + int mPoolCreated; + int mPoolAvailable; + PooledLogicalPacket *mPoolAvailableRoot; // those available + PooledLogicalPacket *mPoolCreatedRoot; // all those created + + // pool management + int mWrappedCreated; + int mWrappedAvailable; + WrappedLogicalPacket *mWrappedAvailableRoot; // those available + WrappedLogicalPacket *mWrappedCreatedRoot; // those created (available or not) + + protected: // internal functions + int AddressHashValue(UdpIpAddress ip, int port) const; + UdpConnection *AddressGetConnection(UdpIpAddress ip, int port) const; + UdpConnection *ConnectCodeGetConnection(int connectCode) const; + + PacketHistoryEntry *ActualReceive(); + void ActualSend(const uchar *data, int dataLen, UdpIpAddress ip, int port); + void ActualSendHelper(const uchar *data, int dataLen, UdpIpAddress ip, int port); + void SendPortAlive(UdpIpAddress ip, int port); + void ProcessRawPacket(const PacketHistoryEntry *e); + void AddConnection(UdpConnection *con); + void RemoveConnection(UdpConnection *con); + void SetPriority(UdpConnection *con, UdpMisc::ClockStamp stamp); + void ProcessIcmpErrors(); + void KeepUntilDisconnected(UdpConnection *con); + void ProcessDisconnectPending(); + void CloseSocket(); + void CreateAndBindSocket(int usePort); + + private: + ~UdpManager(); // does not destroy UdpConnection objects since it does not own them; however, it has a pointer to all the active + // ones and it will notify them that it no longer exists and set their state to cStatusDisconnected + // typically it is recommended that all UdpConnection objects be destroyed before destroying this manager object + + int mRefCount; +}; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // The purpose of the UdpConnection is to manage a single logical connection + //////////////////////////////////////////////////////////////////////////////////////////////////////////// +class UdpConnection : public PriorityQueueMember, public AddressHashTableMember, public ConnectCodeHashTableMember +{ + public: + enum Status { cStatusNegotiating, cStatusConnected, cStatusDisconnected, cStatusDisconnectPending, cStatusCount }; + + enum DisconnectReason { cDisconnectReasonNone, cDisconnectReasonIcmpError, cDisconnectReasonTimeout + , cDisconnectReasonOtherSideTerminated, cDisconnectReasonManagerDeleted + , cDisconnectReasonConnectFail, cDisconnectReasonApplication + , cDisconnectReasonUnreachableConnection, cDisconnectReasonUnacknowledgedTimeout + , cDisconnectReasonNewConnectionAttempt, cDisconnectReasonConnectionRefused + , cDisconnectReasonMutualConnectError, cDisconnectReasonConnectingToSelf + , cDisconnectReasonReliableOverflow + , cDisconnectReasonCount }; + + // standard AddRef/Release scheme + void AddRef(); + void Release(); + int GetRefCount() const; + + // returns the current status of this connection + Status GetStatus() const; + + // returns the reason that a connection was disconnected. See the enum above for a list of all the reasons + DisconnectReason GetDisconnectReason() const; + DisconnectReason GetOtherSideDisconnectReason() const; + static const char *DisconnectReasonText(DisconnectReason reason); // text-description of disconnect reason to aid in logging + + // sets the handler object for this connection. If a handler object is specified, then the callback functions specified + // in UdpManager::Params are ignored for this connection and the handler is used for the callback instead. + // by default there is no handler. + void SetHandler(UdpConnectionHandler *handler); + UdpConnectionHandler *GetHandler() const; + + // set and get the pass-through data value. Typically the application will set the pass through data + // in the callback function for establishing a connection, then it will use the pass through data + // in the callback function for routing packets. + void SetPassThroughData(void *passThroughData); + void *GetPassThroughData() const; + + // when called this connection is marked as terminated. It is the responsibility of the application + // to explicitly destroy connections that are no longer connected. When this object is disconnected + // it calls the UdpManager and has itself removed from the list of active connections, at which point + // the only person having a pointer to this object is the application itself (which owns it) + // (if the UdpManager is deleted before all UdpConnections are destroyed, the UdpManager loops through + // all of the connections it has calling Disconnect on them such that they know that they no longer + // have a udp manager that they can send data through) + // + // setting a flushTimeout tells the connection to stay alive for that amount of time trying to send any pending + // reliable data before shutting down. Once the application calls Disconnect even with a flushTimeout, the application + // should not attempt to use the connection in any significant way (see docs and release notes for details) + // note: the notifyApplication parameter was removed from this function and the functionality of the library + // was changed such that ANYTIME the connection objects state changes to cStatusDisconnected, the OnTerminated callback + // function gets called. + void Disconnect(int flushTimeout = 0); + + // sends a logical packet on the specified channel, returns FALSE if packet could not be queued for sending (should never happen) + // Internally, a packet that starts with a 0 byte is considered an internal control packet. If a logical packet starts with a 0 + // byte, then there will an extra control byte of overhead in order to facilitate it. It is recommended that if packet size + // is critical, that you don't start the packet with a 0 byte. Typically an application will have a packet-type byte on the front + // of application packets; the application packet types should simply start at 1. + bool Send(UdpChannel channel, const void *data, int dataLen); + + // same as the regular Send only it takes a LogicalPacket instead. There are two huge advantages to having it take a + // LogicalPacket. First, we can send the same LogicalPacket to multiple locations and each connection will not necessarily + // have to make its own copy of the data at the time it is put into the send queue (instead each connection just increments + // the buffer ref-count). Second, it allows the application to pre-generate very large packets (like file update packets potentially) + // and hold onto them for the entire length of the application, then, whenever any player needs that chunk of data, it can send them + // the already formatted LogicalPacket. + bool Send(UdpChannel channel, const LogicalPacket *packet); + + // manually forces all channels to send-off any data they have queued up waiting for processing time to send + // this mainly applies to reliable channels. When you send a reliable packet, it actually only adds it to the reliable + // queue until the connection is given processing time by the manager object. This call forces it to attempt to + // send that queued data immediately (subject to normal flow control restrictions). This also flushes the multi-buffer + // for the channel. If you send reliable data and want to ensure that it goes out immediately after the send, this is the + // best call to make. + void FlushChannels(); + + // manually forces buffered data to be sent immediately + void FlushMultiBuffer(); + + // returns the number of bytes sent/received in the last second to this connection (accurate to within cBinResolution(25) milliseconds) + // these functions are not const as they expire the older bin data internally in order to calculate the number + int OutgoingBytesLastSecond(); + int IncomingBytesLastSecond(); + + // returns the total number of bytes outstanding in all reliable channels. When this is zero, you know for sure + // that all sent reliable data has arrived at destination and is confirmed. + int TotalPendingBytes() const; + + // returns how long has elapsed since this connection received data (in milliseconds) + int LastReceive() const; + + // returns how long has elapsed since this connection received data (in milliseconds), using useStamp as the current time (optimization) + int LastReceive(UdpMisc::ClockStamp useStamp) const; + + // returns how long has elapsed since this connection sent data (in milliseconds) + int LastSend() const; + + // returns how long this connection has been in existence (in milliseconds) + int ConnectionAge() const; + + // returns the UdpManager object that is managing this connection + // will return NULL if the connection has been disconnected for some reason (because disconnecting severes the link to UdpManager) + UdpManager *GetUdpManager() const; + + // returns the 32-bit encryption-code that was negotiated as part of the connection-establishment process. + // this is a randomly generated number that both the client and the server have in common. It is exposed + // via this interface primarily to allow user-supplied encrypt routines access to it. + // this code is generated by the server side in response to a connect request. + int GetEncryptCode() const; + + // this returns the connection-code. This is very similar to the encrypt-code in that it is randomly + // generated and both ends of the connection will report the same value. The difference is that this + // code's purpose is part of the internal protocol to ensure that old connections don't try to process + // new connection request packets. Unlike the encrypt-code, this value is generated by the client + // and is part of the connect-request packet. Nevertheless, since this number will be the same random + // number on both ends of the connection, it too can be used as a potential encryption key for the user + // supplied encrypt routines. It's not quite as secure as the encrypt code since this value in theory + // could be hacked to be something predictable on the client side. + int GetConnectCode() const; + + // returns a sync-stamp that can be compared to other ServerSyncStamp's generated on other machines + // in order to calculate the one-way travel time for a packet. It can only accurate calculate + // packet travel times under 32 seconds, would should be completely safe. You must use the + // UdpManager::SyncStampDeltaTime function in order to calculate the elapsed time between + // the two stamps. + ushort ServerSyncStampShort() const; + uint ServerSyncStampLong() const; + int ServerSyncStampShortElapsed(ushort syncStamp) const; + int ServerSyncStampLongElapsed(uint syncStamp) const; + + // returns the IP address/port this connection is linked to + UdpIpAddress GetDestinationIp() const; + int GetDestinationPort() const; + + // statistical functions + void GetStats(UdpConnectionStatistics *cs) const; + void PingStatReset(); // resets the ping-stat information, causing it to resync the clock etc (if in clock-sync mode). Generally this is not done, it was added for backward compatibility + + + // functions for manipulating the automatic no-data-disconnect stuff on a per-connection basis + void SetNoDataTimeout(int noDataTimeout); // 0=never timeout, otherwise in milliseconds (overrides UdpManager::Params::noDataTimeout setting, which is the default) + int GetNoDataTimeout() const; + + // functions for manipulating the keep-alive packet sending on a per-connection basis + void SetKeepAliveDelay(int keepAliveDelay); + int GetKeepAliveDelay() const; + + // configures whether this connection is in silent-disconnect mode or not. By default, the connection is not in silent + // disconnect mode, which means that when this connection is terminated, it will send a final terminate-packet to the + // other side telling them that we are disconnected, allowing them to quickly realize that the connection is now dead. + // In some circumstances, it may be desireable to not do this, and this can be accomplished by calling this function + // passing in 'true' to put it in silent mode. This may be desireable in cases where you are disconnecting a cheater + // and don't want them to have immediate notification that they did something bad. Or, if you are attempting to test + // timeout functionality on the other end and want to simulate a truly dead connection. Normally, you will not want + // to mess with this. It was added to the API to support some internal functionality, see its use in the source-code + // or release-notes for details. + void SetSilentDisconnect(bool silent); + + + // returns the current queue-status of the reliable channel specified. Unreliable channels will always report zero. + struct ChannelStatus + { + int totalPendingBytes; // total bytes of data in channel that have yet to be acknowledged (includes queuedBytes plus physical-packet bytes that have yet to be acknowledged) + int queuedPackets; // number of logical packets in the queue + int queuedBytes; // number of bytes in the logical queue (the logical queue does NOT include pending physical packets) + int incomingLargeTotal; // total number of bytes in the currently incoming logical packet (only meaningful obviously if a fragmented file is in tranist) + int incomingLargeSoFar; // number of bytes received so far in the currently incoming logical packet + int oldestUnacknowledgedAge; // age of the oldest unacknowledged (but sent) packet (in milliseconds) + int duplicatePacketsReceived; // number of times we received a packet that we had already received + int resentPacketsAccelerated; // number of times we have resent a packet due to receiving a later packet in the series + int resentPacketsTimedOut; // number of times we have resent a packet due to the ack-timeout expiring + int congestionSlowStartThreshhold; // current threshhold for slow-start algorithm + int congestionWindowSize; // current sliding window size + int ackAveragePing; // average time for a packet to be acknowledged (used in calculating optimal resend timeouts) + }; + void GetChannelStatus(UdpChannel channel, ChannelStatus *channelStatus) const; + + protected: + friend class UdpManager; + friend class UdpReliableChannel; + + // note: if connectPacket is NULL, that means this connection object is being created to establish + // a new connection to the specified ip/port (ie. the connection starts out in cStatusNegotiating mode) + // if connectPacket is non-NULL, that menas this connection object is being created to handle an + // incoming connect request and it will start out in cStatusConnected mode. + UdpConnection(UdpManager *udpManager, UdpIpAddress destIp, int destPort, int timeout); // starts connection-establishment protocol + UdpConnection(UdpManager *udpManager, const UdpManager::PacketHistoryEntry *e); // starts already connected, replying to connection request + + // gives this connection processing time (only given processing time by the manager object and then + // only when the connection has scheduled itself to receive processing time) + void GiveTime(); + void ProcessRawPacket(const UdpManager::PacketHistoryEntry *e); + void PortUnreachable(); + void FlagPortUnreachable(); + protected: + UdpConnection *mNextConnection; // used by UdpManager to maintain list of connections + UdpConnection *mPrevConnection; // used by UdpManager to maintain list of connections + UdpConnection *mDisconnectPendingNextConnection; // used by UdpManager to maintain list of connections pending disconnect + UdpIpAddress mIp; + int mPort; + int mSimulateQueueBytes; // used by UdpManager to track how many bytes are in it's simulation queue headed to each destination + + private: + friend class GroupLogicalPacket; + + ~UdpConnection(); + void Init(UdpManager *udpManager, UdpIpAddress destIp, int destPort); + + // note: BufferedSend is capable of optionally taking two chunks of data at once, which are then concatenated together as if they were one chunk of data + // into the multi-buffer. Providing this facility prevents the UdpReliableChannel object from having to make a copy of all the data it sends + // in order to stick a realiable header on it. + // we don't bother extending this down to the PhysicalSend (in case the BufferedSend does a pass through due to size) because the encryption code + // is incapable of sourcing from two different chunks and outputting to one chunk. It's not possible to change that either, since the encyption + // takes place 32 bits at a time and you could end up straddling boundaries between chunks. + void RawSend(const uchar *data, int dataLen); // nothing happens to the data here, it is given to the udpmanager and sent out the port + void PhysicalSend(const uchar *data, int dataLen, bool appendAllowed); // sends a physical packet (encrypts and adds crc bytes) + uchar *BufferedSend(const uchar *data, int dataLen, const uchar *data2, int dataLen2, bool appendAllowed); // buffers logical packets waiting til we have more data (makes multi-packets) + bool InternalSend(UdpChannel channel, const LogicalPacket *packet); + bool InternalSend(UdpChannel channel, const uchar *data, int dataLen, const uchar *data2 = NULL, int dataLen2 = 0); + + uchar *InternalAckSend(uchar *bufferedAckPtr, const uchar *ackPtr, int ackLen); // used by reliable channel to send acks (special send that allows for deduping ack feature) + + void InternalGiveTime(); + void InternalDisconnect(int flushTimeout, DisconnectReason reason); + void ProcessCookedPacket(const uchar *data, int dataLen); + void DecryptIt(const uchar *data, int dataLen); + void ScheduleTimeNow(); + int ExpireSendBin(); + int ExpireReceiveBin(); + void SendTerminatePacket(int connectCode, DisconnectReason reason); + void CallbackRoutePacket(const uchar *data, int dataLen); + void CallbackCorruptPacket(const uchar *data, int dataLen, UdpCorruptionReason reason); + bool IsNonEncryptPacket(const uchar *data) const; + + // these encrypt-method functions return the length of the encrypted/decrypted data + // new methods of encryption/compression can be easily added by simply creating the + // functions for them and changing the SetupEncryptModel function as appropriate + // since raw packets are encrypted in the first place and have a limited size + // the decrypted data will never be larger than a maxRawPacketSize. Both of encrypt + // and decrypt are guaranteed to have enough room in dest buffers to hold the results. + // Encryption function is allowed to expand the data at most the number of bytes + // it reserves for this purpose in the SetupEncryptModel function. + int EncryptNone(uchar *destData, const uchar *sourceData, int sourceLen); + int DecryptNone(uchar *destData, const uchar *sourceData, int sourceLen); + int EncryptXor(uchar *destData, const uchar *sourceData, int sourceLen); + int DecryptXor(uchar *destData, const uchar *sourceData, int sourceLen); + int EncryptXorBuffer(uchar *destData, const uchar *sourceData, int sourceLen); + int DecryptXorBuffer(uchar *destData, const uchar *sourceData, int sourceLen); + int EncryptUserSupplied(uchar *destData, const uchar *sourceData, int sourceLen); + int DecryptUserSupplied(uchar *destData, const uchar *sourceData, int sourceLen); + int EncryptUserSupplied2(uchar *destData, const uchar *sourceData, int sourceLen); + int DecryptUserSupplied2(uchar *destData, const uchar *sourceData, int sourceLen); + void SetupEncryptModel(); + + int mRefCount; + Status mStatus; + void *mPassThroughData; + UdpManager *mUdpManager; + int mConnectCode; + UdpConnectionStatistics mConnectionStats; + UdpMisc::ClockStamp mConnectionCreateTime; + int mConnectAttemptTimeout; + int mNoDataTimeout; + DisconnectReason mDisconnectReason; + DisconnectReason mOtherSideDisconnectReason; + bool mFlaggedPortUnreachable; + bool mSilentDisconnect; + + UdpReliableChannel *mChannel[UdpManager::cReliableChannelCount]; + + struct Configuration + { + int encryptCode; + int crcBytes; + UdpManager::EncryptMethod encryptMethod[UdpManager::cEncryptPasses]; + int maxRawPacketSize; // negotiated maxRawPacketSize (ie. smaller of what two sides are set to) + }; + + Configuration mConnectionConfig; + + UdpMisc::ClockStamp mLastClockSyncTime; + UdpMisc::ClockStamp mDataHoldTime; + UdpMisc::ClockStamp mLastSendTime; + UdpMisc::ClockStamp mLastReceiveTime; + UdpMisc::ClockStamp mLastPortAliveTime; + uchar* mMultiBufferData; + uchar* mMultiBufferPtr; + + int mOrderedCountOutgoing; + int mOrderedCountOutgoing2; + ushort mOrderedStampLast; + ushort mOrderedStampLast2; + + typedef int (UdpConnection::* IEncryptFunction)(uchar *destData, const uchar *sourceData, int sourceLen); + typedef int (UdpConnection::* IDecryptFunction)(uchar *destData, const uchar *sourceData, int sourceLen); + IEncryptFunction mEncryptFunction[UdpManager::cEncryptPasses]; + IDecryptFunction mDecryptFunction[UdpManager::cEncryptPasses]; + + uchar *mEncryptXorBuffer; + int mEncryptExpansionBytes; + + uint mSyncTimeDelta; + int mSyncStatTotal; + int mSyncStatCount; + int mSyncStatLow; + int mSyncStatHigh; + int mSyncStatLast; + int mSyncStatMasterRoundTime; + UdpMisc::ClockStamp mSyncStatMasterFixupTime; + + bool mGettingTime; + UdpConnectionHandler *mHandler; + + int mKeepAliveDelay; + + UdpMisc::ClockStamp mIcmpErrorRetryStartStamp; + UdpMisc::ClockStamp mPortRemapRequestStartStamp; + + UdpMisc::ClockStamp mDisconnectFlushStamp; + int mDisconnectFlushTimeout; + + + // data rate management functions + enum { cBinResolution = 25, cBinCount = 1000 / cBinResolution }; + int mLastSendBin; + int mLastReceiveBin; + int mOutgoingBytesLastSecond; + int mIncomingBytesLastSecond; + int mSendBin[cBinCount]; + int mReceiveBin[cBinCount]; + + + + // note: cUdpPacketReliable, cUdpPacketFragment both indicate a reliable-packet header. They are marked + // differently such that we can support large packets without any additional header overhead, a fragment marked packet means + // that the packet is part of a larger packet being assembled. The first fragment has an additional 4 bytes on the header specifying + // the length to follow. The order of those entries is important + enum UdpPacketType { cUdpPacketZeroEscape, cUdpPacketConnect, cUdpPacketConfirm, cUdpPacketMulti, cUdpPacketBig + , cUdpPacketTerminate, cUdpPacketKeepAlive + , cUdpPacketClockSync, cUdpPacketClockReflect + , cUdpPacketReliable1, cUdpPacketReliable2, cUdpPacketReliable3, cUdpPacketReliable4 + , cUdpPacketFragment1, cUdpPacketFragment2, cUdpPacketFragment3, cUdpPacketFragment4 + , cUdpPacketAck1, cUdpPacketAck2, cUdpPacketAck3, cUdpPacketAck4 + , cUdpPacketAckAll1, cUdpPacketAckAll2, cUdpPacketAckAll3, cUdpPacketAckAll4 + , cUdpPacketGroup, cUdpPacketOrdered, cUdpPacketOrdered2, cUdpPacketPortAlive + , cUdpPacketUnreachableConnection, cUdpPacketRequestRemap }; + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // The following structs represent what the internal packets look like. In practice, most of these + // structs are never used and exist only for documentation clarity. Internally packets are + // manually assembled such that struct packing and byte-ordering issues won't be an issue. + ////////////////////////////////////////////////////////////////////////////////////////////////////// + struct UdpPacketConnect + { + uchar zeroByte; + uchar packetType; + int protocolVersion; + int connectCode; + int maxRawPacketSize; + }; + + struct UdpPacketConfirm + { + uchar zeroByte; + uchar packetType; + int connectCode; + Configuration config; + int maxRawPacketSize; + }; + + struct UdpPacketTerminate + { + uchar zeroByte; + uchar packetType; + int connectCode; + }; + + struct UdpPacketKeepAlive + { + uchar zeroByte; + uchar packetType; + }; + + struct UdpPacketGroup + { + // this format is prepped by the GroupLogicalPacket object, which reports itself to the UdpConnection object as an internal packet + // type such that it doesn't get treated as an application-packet even though the application is the one sending it + uchar zeroByte; + uchar packetType; + // variableValue/data, repeated... + }; + + struct UdpPacketClockSync + { + uchar zeroByte; + uchar packetType; + ushort timeStamp; + int masterPingTime; + int averagePingTime; + int lowPingTime; + int highPingTime; + int lastPingTime; + udp_int64 ourSent; + udp_int64 ourReceived; + }; + + struct UdpPacketClockReflect + { + uchar zeroByte; + uchar packetType; + ushort timeStamp; + uint serverSyncStampLong; + udp_int64 yourSent; + udp_int64 yourReceived; + udp_int64 ourSent; + udp_int64 ourReceived; + }; + + struct UdpPacketReliable + { + uchar zeroByte; + uchar packetType; + ushort reliableStamp; + }; + + struct UdpPacketReliableFragmentStart + { + UdpPacketReliable reliable; + int length; + }; + + struct UdpPacketAck + { + uchar zeroByte; + uchar packetType; + ushort reliableStamp; + }; + + struct UdpPacketOrdered + { + uchar zeroByte; + uchar packetType; + ushort orderStamp; + }; + + + enum { cUdpPacketReliableSize = 4 }; + enum { cUdpPacketOrderedSize = 4 }; + enum { cUdpPacketConnectSize = 14 }; +}; + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // The purpose of this class is to manage the reliable transmission of packets on top of the inherently + // unreliable UDP layer. This is an internal object and should not be manually created or talked to by the user. + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class UdpReliableChannel +{ + protected: + friend class UdpConnection; + + UdpReliableChannel(int channelNumber, UdpConnection *connection, UdpManager::ReliableConfig *config); + ~UdpReliableChannel(); + void GetChannelStatus(UdpConnection::ChannelStatus *channelStatus) const; + int GetAveragePing() const; + int TotalPendingBytes() const; // returns total bytes outstanding + + void ReliablePacket(const uchar *data, int dataLen); + void Send(const LogicalPacket *packet); + void Send(const uchar *data, int dataLen, const uchar *data2, int dataLen2); + void AckPacket(const uchar *data, int dataLen); + void AckAllPacket(const uchar *data, int dataLen); + void ClearBufferedAck(); + int GiveTime(); + + protected: + enum ReliablePacketMode { cReliablePacketModeReliable, cReliablePacketModeFragment, cReliablePacketModeDelivered }; + + class PhysicalPacket + { + public: + PhysicalPacket(); + ~PhysicalPacket(); + + public: + UdpMisc::ClockStamp mFirstTimeStamp; + UdpMisc::ClockStamp mLastTimeStamp; + const LogicalPacket *mParent; // physical packets hold an addref on the logical packet. Once all of the logical packet data has been divied out to physical packets, the logical queue releases it + const uchar *mDataPtr; // within parent's data (it's possible it is not pointing to the beginning in the case of large packets) + int mDataLen; + }; + + class IncomingQueueEntry + { + public: + IncomingQueueEntry(); + ~IncomingQueueEntry(); + + public: + LogicalPacket *mPacket; + ReliablePacketMode mMode; + }; + friend class IncomingQueueEntry; + + udp_int64 GetReliableOutgoingId(int reliableStamp) const; + udp_int64 GetReliableIncomingId(int reliableStamp) const; + void Ack(udp_int64 reliableId); + void ProcessPacket(ReliablePacketMode mode, const uchar *data, int dataLen); + bool PullDown(int windowSpaceLeft); + void FlushCoalesce(); + void SendCoalesce(const uchar *data, int dataLen, const uchar *data2 = NULL, int dataLen2 = 0); + void QueueLogicalPacket(const LogicalPacket *packet); + + UdpManager::ReliableConfig mConfig; + UdpConnection *mUdpConnection; + UdpMisc::ClockStamp mLastTimeStampAcknowledged; + UdpMisc::ClockStamp mTrickleLastSend; + UdpMisc::ClockStamp mNextNeedTime; + int mChannelNumber; + udp_int64 mReliableOutgoingId; + udp_int64 mReliableOutgoingPendingId; + int mReliableOutgoingBytes; + int mLogicalBytesQueued; + int mLogicalPacketsQueued; + uchar *mBigDataPtr; + int mBigDataLen; + int mBigDataTargetLen; + int mAveragePingTime; + int mMaxDataBytes; + int mFragmentNextPos; + PhysicalPacket *mPhysicalPackets; + const LogicalPacket *mLogicalRoot; + const LogicalPacket *mLogicalEnd; + + int mCongestionWindowStart; + int mCongestionWindowSize; + int mCongestionWindowLargest; + int mCongestionSlowStartThreshhold; + int mCongestionWindowMinimum; + bool mMaxxedOutCurrentWindow; + + udp_int64 mReliableIncomingId; + IncomingQueueEntry *mReliableIncoming; + + LogicalPacket *mCoalescePacket; + uchar *mCoalesceStartPtr; + uchar *mCoalesceEndPtr; + int mCoalesceCount; + int mMaxCoalesceAttemptBytes; + + uchar *mBufferedAckPtr; + + int mStatDuplicatePacketsReceived; + int mStatResentPacketsAccelerated; + int mStatResentPacketsTimedOut; +}; + + + ///////////////////////////////////////////////////////////////////////// + // FixedLogicalPacket implementation + ///////////////////////////////////////////////////////////////////////// +template FixedLogicalPacket::FixedLogicalPacket(const void *data, int dataLen) +{ + mDataLen = dataLen; + if (data != NULL) + memcpy(mData, data, mDataLen); +} + +template void *FixedLogicalPacket::GetDataPtr() +{ + return(mData); +} + +template const void *FixedLogicalPacket::GetDataPtr() const +{ + return(mData); +} + +template int FixedLogicalPacket::GetDataLen() const +{ + return(mDataLen); +} + +template void FixedLogicalPacket::SetDataLen(int len) +{ + mDataLen = len; +} + + + ///////////////////////////////////////////////////////////////////////// + // StructLogicalPacket implementation + ///////////////////////////////////////////////////////////////////////// +template StructLogicalPacket::StructLogicalPacket(T *initData) +{ + if (initData != NULL) + mStruct = *initData; +} + +template void *StructLogicalPacket::GetDataPtr() +{ + return(&mStruct); +} + +template const void *StructLogicalPacket::GetDataPtr() const +{ + return(&mStruct); +} + +template int StructLogicalPacket::GetDataLen() const +{ + return(sizeof(mStruct)); +} + +template void StructLogicalPacket::SetDataLen(int len) +{ +} + + + ///////////////////////////////////////////////////////////////////////// + // inline implementations + ///////////////////////////////////////////////////////////////////////// + + // UdpMisc +inline int UdpMisc::ClockElapsed(ClockStamp stamp) +{ + ClockStamp t = (UdpMisc::Clock() - stamp); + if (t > 2000000000) // only time differences up to 23 days can be measured with this function + return(2000000000); + return((int)t); +} + +inline int UdpMisc::ClockDiff(ClockStamp start, ClockStamp stop) +{ + ClockStamp t = (stop - start); + if (t > 2000000000) // only time differences up to 23 days can be measured with this function + return(2000000000); + return((int)t); +} + +inline ushort UdpMisc::LocalSyncStampShort() +{ + return((ushort)(Clock() & 0xffff)); +} + +inline uint UdpMisc::LocalSyncStampLong() +{ + return((uint)(Clock() & 0xffffffff)); +} + + +inline int UdpMisc::PutValue64(void *buffer, udp_int64 value) +{ + uchar *bufptr = (uchar *)buffer; + *bufptr++ = (uchar)(value >> 56); + *bufptr++ = (uchar)((value >> 48) & 0xff); + *bufptr++ = (uchar)((value >> 40) & 0xff); + *bufptr++ = (uchar)((value >> 32) & 0xff); + *bufptr++ = (uchar)((value >> 24) & 0xff); + *bufptr++ = (uchar)((value >> 16) & 0xff); + *bufptr++ = (uchar)((value >> 8) & 0xff); + *bufptr = (uchar)(value & 0xff); + return(8); +} + +inline udp_int64 UdpMisc::GetValue64(const void *buffer) +{ + const uchar *bufptr = (const uchar *)buffer; + return(((udp_int64)*bufptr << 56) | ((udp_int64)*(bufptr + 1) << 48) | ((udp_int64)*(bufptr + 2) << 40) | ((udp_int64)*(bufptr + 3) << 32) | ((udp_int64)*(bufptr + 4) << 24) | ((udp_int64)*(bufptr + 5) << 16) | ((udp_int64)*(bufptr + 6) << 8) | (udp_int64)*(bufptr + 7)); +} + +inline int UdpMisc::PutValue32(void *buffer, uint value) +{ + uchar *bufptr = (uchar *)buffer; + *bufptr++ = (uchar)(value >> 24); + *bufptr++ = (uchar)((value >> 16) & 0xff); + *bufptr++ = (uchar)((value >> 8) & 0xff); + *bufptr = (uchar)(value & 0xff); + return(4); +} + +inline uint UdpMisc::GetValue32(const void *buffer) +{ + const uchar *bufptr = (const uchar *)buffer; + return((*bufptr << 24) | (*(bufptr + 1) << 16) | (*(bufptr + 2) << 8) | *(bufptr + 3)); +} + +inline int UdpMisc::PutValue24(void *buffer, uint value) +{ + uchar *bufptr = (uchar *)buffer; + *bufptr++ = (uchar)((value >> 16) & 0xff); + *bufptr++ = (uchar)((value >> 8) & 0xff); + *bufptr = (uchar)(value & 0xff); + return(3); +} + +inline uint UdpMisc::GetValue24(const void *buffer) +{ + const uchar *bufptr = (const uchar *)buffer; + return((*bufptr << 16) | (*(bufptr + 1) << 8) | *(bufptr + 2)); +} + +inline int UdpMisc::PutValue16(void *buffer, ushort value) +{ + uchar *bufptr = (uchar *)buffer; + *bufptr++ = (uchar)((value >> 8) & 0xff); + *bufptr = (uchar)(value & 0xff); + return(2); +} + +inline ushort UdpMisc::GetValue16(const void *buffer) +{ + const uchar *bufptr = (const uchar *)buffer; + return((ushort)((*bufptr << 8) | *(bufptr + 1))); +} + + // UdpManager +inline int UdpManager::LastReceive() const +{ + return(UdpMisc::ClockElapsed(mLastReceiveTime)); +} + +inline int UdpManager::LastSend() const +{ + return(UdpMisc::ClockElapsed(mLastSendTime)); +} + +inline int UdpManager::AddressHashValue(UdpIpAddress ip, int port) const +{ + return((int)((ip.GetAddress() ^ port) & 0x7fffffff)); +} + +inline void UdpManager::SetPriority(UdpConnection *con, UdpMisc::ClockStamp stamp) +{ + // do not ever let anybody schedule themselves for processing time sooner then mMinimumScheduledStamp + // otherwise, they could end up getting processing time multiple times in a single UdpManager::GiveTime iteration + // of the priority queue, which under odd circumstances could result in an infinite loop + if (stamp < mMinimumScheduledStamp) + stamp = mMinimumScheduledStamp; + + if (mPriorityQueue != NULL) + mPriorityQueue->Add(con, stamp); +} + +inline void UdpManager::SetHandler(UdpManagerHandler *handler) +{ + mParams.handler = handler; +} + +inline UdpManagerHandler *UdpManager::GetHandler() const +{ + return(mParams.handler); +} + +inline void UdpManager::PoolReturn(PooledLogicalPacket *packet) +{ + if (mPoolAvailable < mParams.pooledPacketMax) + { + packet->AddRef(); + mPoolAvailable++; + packet->mAvailableNext = mPoolAvailableRoot; + mPoolAvailableRoot = packet; + } +} + +inline void UdpManager::WrappedReturn(WrappedLogicalPacket *wp) +{ + wp->SetLogicalPacket(NULL); + + if (mWrappedAvailable < mParams.wrappedPoolMax) + { + wp->AddRef(); + mWrappedAvailable++; + wp->mAvailableNext = mWrappedAvailableRoot; + mWrappedAvailableRoot = wp; + } +} + +inline void UdpManager::AddRef() +{ + mRefCount++; +} + +inline void UdpManager::Release() +{ + mRefCount--; + if (mRefCount == 0) + delete this; +} + +inline int UdpManager::GetRefCount() const +{ + return(mRefCount); +} + +inline void UdpManager::SetPassThroughData(void *passThroughData) +{ + mPassThroughData = passThroughData; +} + +inline void *UdpManager::GetPassThroughData() const +{ + return(mPassThroughData); +} + + + + // UdpConnection +inline void UdpConnection::ScheduleTimeNow() +{ + // if we are current in our GiveTime function getting time, then there is no need to reprioritize to 0 when we send a raw packet, since + // the last thing we do in out GiveTime is do a scheduling calculation based on the last time a packet was sent. This little check + // prevents us from reprioritizing to 0, only to shortly thereafter be reprioritized to where we actually belong. + if (!mGettingTime) + { + if (mUdpManager != NULL) + mUdpManager->SetPriority(this, 0); + } +} + +inline void UdpConnection::SetHandler(UdpConnectionHandler *handler) +{ + mHandler = handler; +} + +inline UdpConnectionHandler *UdpConnection::GetHandler() const +{ + return(mHandler); +} + +inline void UdpConnection::AddRef() +{ + mRefCount++; +} + +inline void UdpConnection::Release() +{ + mRefCount--; + if (mRefCount == 0) + delete this; +} + +inline bool UdpConnection::IsNonEncryptPacket(const uchar *data) const +{ + if (data[0] == 0) + { + if (data[1] == cUdpPacketConnect || data[1] == cUdpPacketConfirm || data[1] == cUdpPacketUnreachableConnection || data[1] == cUdpPacketRequestRemap) + return(true); + } + return(false); +} + + +inline int UdpConnection::GetRefCount() const +{ + return(mRefCount); +} + +inline int UdpConnection::GetEncryptCode() const +{ + return(mConnectionConfig.encryptCode); +} + +inline int UdpConnection::GetConnectCode() const +{ + return(mConnectCode); +} + +inline int UdpConnection::LastReceive(UdpMisc::ClockStamp useStamp) const +{ + return(UdpMisc::ClockDiff(mLastReceiveTime, useStamp)); +} + +inline int UdpConnection::LastReceive() const +{ + return(UdpMisc::ClockElapsed(mLastReceiveTime)); +} + +inline int UdpConnection::ConnectionAge() const +{ + return(UdpMisc::ClockElapsed(mConnectionCreateTime)); +} + +inline int UdpConnection::LastSend() const +{ + return(UdpMisc::ClockElapsed(mLastSendTime)); +} + +inline ushort UdpConnection::ServerSyncStampShort() const +{ + return((ushort)(UdpMisc::LocalSyncStampShort() + (mSyncTimeDelta & 0xffff))); +} + +inline uint UdpConnection::ServerSyncStampLong() const +{ + return(UdpMisc::LocalSyncStampLong() + mSyncTimeDelta); +} + +inline int UdpConnection::ServerSyncStampShortElapsed(ushort syncStamp) const +{ + return(UdpMisc::SyncStampShortDeltaTime(syncStamp, ServerSyncStampShort())); +} + +inline int UdpConnection::ServerSyncStampLongElapsed(uint syncStamp) const +{ + return(UdpMisc::SyncStampLongDeltaTime(syncStamp, ServerSyncStampLong())); +} + +inline UdpManager *UdpConnection::GetUdpManager() const +{ + return(mUdpManager); +} + +inline UdpConnection::Status UdpConnection::GetStatus() const +{ + return(mStatus); +} + +inline UdpConnection::DisconnectReason UdpConnection::GetDisconnectReason() const +{ + return(mDisconnectReason); +} + +inline UdpConnection::DisconnectReason UdpConnection::GetOtherSideDisconnectReason() const +{ + return(mOtherSideDisconnectReason); +} + +inline int UdpConnection::OutgoingBytesLastSecond() +{ + ExpireSendBin(); + return(mOutgoingBytesLastSecond); +} + +inline int UdpConnection::IncomingBytesLastSecond() +{ + ExpireReceiveBin(); + return(mIncomingBytesLastSecond); +} + +inline void UdpConnection::SetPassThroughData(void *passThroughData) +{ + mPassThroughData = passThroughData; +} + +inline void *UdpConnection::GetPassThroughData() const +{ + return(mPassThroughData); +} + +inline UdpIpAddress UdpConnection::GetDestinationIp() const +{ + return(mIp); +} + +inline int UdpConnection::GetDestinationPort() const +{ + return(mPort); +} + +inline void UdpConnection::SetNoDataTimeout(int noDataTimeout) +{ + mNoDataTimeout = noDataTimeout; +} + +inline int UdpConnection::GetNoDataTimeout() const +{ + return(mNoDataTimeout); +} + +inline void UdpConnection::Disconnect(int flushTimeout) +{ + InternalDisconnect(flushTimeout, cDisconnectReasonApplication); +} + +inline void UdpConnection::SetKeepAliveDelay(int keepAliveDelay) +{ + mKeepAliveDelay = keepAliveDelay; +} + +inline int UdpConnection::GetKeepAliveDelay() const +{ + return(mKeepAliveDelay); +} + + + // UdpReliableChannel +inline void UdpReliableChannel::AckPacket(const uchar *data, int /*dataLen*/) +{ + Ack(GetReliableOutgoingId((ushort)UdpMisc::GetValue16(data + 2))); +} + +inline int UdpReliableChannel::GetAveragePing() const +{ + return(mAveragePingTime); +} + +inline int UdpReliableChannel::TotalPendingBytes() const +{ + return(mLogicalBytesQueued + mReliableOutgoingBytes); +} + +inline void UdpReliableChannel::ClearBufferedAck() +{ + mBufferedAckPtr = NULL; +} + +inline udp_int64 UdpReliableChannel::GetReliableOutgoingId(int reliableStamp) const +{ + // since we can never have anywhere close to 65000 packets outstanding, we only need to + // to send the low order word of the reliableId in the UdpPacketReliable and UdpPacketAck + // packets, because we can reconstruct the full id from that, we just need to take + // into account the wrap around issue. We calculate it based of the high-word of the + // next packet we are going to send. If it ends up being larger then we know + // we wrapped and can fix it up by simply subtracting 1 from the high-order word. + udp_int64 reliableId = reliableStamp | (mReliableOutgoingId & (~(udp_int64)0xffff)); + if (reliableId > mReliableOutgoingId) + reliableId -= 0x10000; + return(reliableId); +} + +inline udp_int64 UdpReliableChannel::GetReliableIncomingId(int reliableStamp) const +{ + // since we can never have anywhere close to 65000 packets outstanding, we only need to + // to send the low order word of the reliableId in the UdpPacketReliable and UdpPacketAck + // packets, because we can reconstruct the full id from that, we just need to take + // into account the wrap around issue. We basically prepend the last-known + // high-order word. If we end up significantly below the head of our chain, then we + // know we need to pick the entry 0x10000 higher. If we fall significantly above + // our previous high-end, then we know we need to go the other way. + udp_int64 reliableId = reliableStamp | (mReliableIncomingId & (~(udp_int64)0xffff)); + if (reliableId < mReliableIncomingId - UdpManager::cHardMaxOutstandingPackets) + reliableId += 0x10000; + if (reliableId > mReliableIncomingId + UdpManager::cHardMaxOutstandingPackets) + reliableId -= 0x10000; + return(reliableId); +} + +class UdpLibraryException +{ +public: + UdpLibraryException(); + ~UdpLibraryException(); +private: +}; + + +#endif + diff --git a/stationapi/udplibrary/UdpLibraryRelease.txt b/stationapi/udplibrary/UdpLibraryRelease.txt new file mode 100755 index 0000000..934ab02 --- /dev/null +++ b/stationapi/udplibrary/UdpLibraryRelease.txt @@ -0,0 +1,1298 @@ +UdpLibrary Release Notes +================================================================================================================= + +----------------------------------------------------------------------------------------------------------------- +6/11/2003 +----------------------------------------------------------------------------------------------------------------- +- There are a lot of significant changes in this release. CPU usage, throughput ability, and memory-usage + have all been improved substantially, particularly for high-stress server-to-server type environments. + The optimizations are such that old settings that used up a lot of memory can be changed to more reasonable + values without adversely effecting throughput ability or CPU usage. To make things simpler in this regard, + below is a list of recommended settings for different types of connections. If you have connections of + different types, ideally you would setup your architecture such that the different types were managed by + different UdpManager's, such that optimal settings can be applied to both. As always, you should not have + more managers than is needed (perhaps one for all client connections and one for all interserver connections). + Settings not specified below should be left at their default. It is still important that you keep the GiveTime + frame rate reasonably high. For high-bandwidth server to server connections, I recommend at least 20, 8 minimum. + If your frame rate drops below that and you are sending more than 500k/sec of data, then you better seriously + consider putting the UdpManager into it's own thread. In a future release (next release probably), I plan on + including a threaded-wrapper version of the UdpLibrary to make this painless. Until then, you may wish to + talk to the SWG team, which has already done this, and may be able to provide you with some getting-started code. + + Server side settings for incoming player connections (ie. clients connecting are relatively low bandwidth) + (total bandwidth per connection is intended to be low) + ----------------------------------------------------------------------------------------------------------- + Params::maxConnections set as appropriate + Params::port set as appropriate + Params::outgoingBufferSize set to 1mb-4mb + Params::incomingBufferSize set to 1mb-4mb + Params::keepAliveDelay set to 15000 (15 second keep-alives generally do the trick) + Params::noDataTimeout set to 46000 (46 seconds, typically just over 3 times client side keepAliveDelay) + Params::reliableOverflowBytes set to 2mb (however much you are willing to let them have backlogged) + Params::hashTableSize set to 10 times max expected connections + Params::pooledPacketMax set to roughly 5 times max expected connections, but rarely over 10000 + Params::crcBytes set to 2 + Params::bindIpAddress[32] set as appropriate (I like to leave it blank) + + Params::clockSyncDelay leave at default of 0 (client side will do clock syncing) + Params::portRange leave at default + Params::packetHistoryMax leave at default + Params::portAliveDelay leave at default of 0 + Params::replyUnreachableConnection leave at default of true + Params::allowPortRemapping leave at default of true + Params::allowAddressRemapping leave at default of false + Params::icmpErrorRetryPeriod leave at default of 5000 + Params::oldestUnacknowledgedTimeout leave at default of 90000 (or set more aggressively even, down to 30000) + Params::maxDataHoldTime leave at default of 50 (or set longer up to 100 possibly) + Params::maxDataHoldSize leave at default of -1 (meaning same as raw packet size) + Params::maxRawPacketSize leave at default of 512 + Params::avoidPriorityQueue leave at default of false + Params::pooledPacketInitial leave at default of 0 (or set to pooledPacketMax for extra efficiency at cost of memory) + Params::pooledPacketSize leave at default of -1 (uses same size as raw packet size, this is critical) + Params::wrappedPoolMax leave at default of 1000 + Params::processIcmpErrors leave at default of true + Params::connectAttemptDelay leave at default (irrelevant for server side) + Params::reliable[0] leave all settings at defaults + + + Server-to-server settings for LAN connections (ie. connections are local on high-bandwidth) + (total bandwidth per connection is intended to be high) + (same settings used regardless of who intiates connection) + ----------------------------------------------------------------------------------------------------------- + Params::maxRawPacketSize set to 1460 + Params::maxConnections set as appropriate + Params::port set as appropriate + Params::outgoingBufferSize set to 4mb + Params::incomingBufferSize set to 4mb + Params::keepAliveDelay set to 15000 (15 second keep-alives generally do the trick) + Params::noDataTimeout set to 61000 (61 seconds, typically just over 4 times client side keepAliveDelay) + Params::reliableOverflowBytes set to 10mb (however much you are willing to let them have backlogged before disconnecting) + Params::hashTableSize set to 10 times max expected connections + Params::pooledPacketMax set to 5000 + Params::crcBytes set to 2 + Params::bindIpAddress[32] set as appropriate (I like to leave it blank) + Params::icmpErrorRetryPeriod set to 2000 (optional) + Params::allowPortRemapping set to false (optional) + Params::oldestUnacknowledgedTimeout set to 30000 (longer for debugging tolerance) + Params::maxDataHoldTime set to 0 (this is critical) + Params::reliable[].maxOutstandingBytes set to 2mb + Params::reliable[].maxOutstandingPackets set to 4000 + Params::reliable[].resendDelayAdjust set to 1500 + Params::reliable[].congestionWindowMinimum set to 50000 + + Params::reliable[] leave all other settings at defaults + Params::clockSyncDelay leave at default of 0 (client side will do clock syncing) + Params::portRange leave at default + Params::packetHistoryMax leave at default + Params::portAliveDelay leave at default of 0 + Params::replyUnreachableConnection leave at default of true + Params::allowAddressRemapping leave at default of false + Params::maxDataHoldSize leave at default of -1 (meaning same as raw packet size) + Params::avoidPriorityQueue leave at default of false + Params::pooledPacketInitial leave at default of 0 (or set to pooledPacketMax for extra efficiency at cost of memory) + Params::pooledPacketSize leave at default of -1 (uses same size as raw packet size, this is critical) + Params::wrappedPoolMax leave at default of 1000 + Params::processIcmpErrors leave at default of true + Params::connectAttemptDelay leave at default + + + Client side settings for player connections (ie. client is connecting over the internet to the server) + (total bandwidth per connection is intended to be low) + ----------------------------------------------------------------------------------------------------------- + Params::maxConnections set as appropropriate (probably something small like 5) + Params::port set to 0 (let client pick port) + Params::outgoingBufferSize set to 64k (default I believe) + Params::incomingBufferSize set to 64k (default I believe) + Params::keepAliveDelay set to 15000 (15 second keep-alives generally do the trick) + Params::noDataTimeout set to 46000 (46 seconds, typically just over 3 times client side keepAliveDelay) + Params::reliableOverflowBytes set to 512k (however much you are willing to let them have backlogged) + Params::hashTableSize set to 10 times max connections + Params::clockSyncDelay set to 60000 if you want packetloss stats or clock-sync features, otherwise 0 + Params::pooledPacketMax set to 50 + Params::crcBytes set to 2 + Params::maxDataHoldTime set to 20 (more agressive for client to server data generally) + Params::packetHistoryMax set to 5 (to save memory on client) + + Params::bindIpAddress[32] leave at default + Params::portRange leave at default + Params::portAliveDelay leave at default of 0 + Params::replyUnreachableConnection leave at default of true + Params::allowPortRemapping leave at default of true + Params::allowAddressRemapping leave at default of false + Params::icmpErrorRetryPeriod leave at default of 5000 + Params::oldestUnacknowledgedTimeout leave at default of 90000 (or set more aggressively even, down to 30000) + Params::maxDataHoldTime leave at default of 60 (or set longer up to 100 possibly) + Params::maxDataHoldSize leave at default of -1 (meaning same as raw packet size) + Params::maxRawPacketSize leave at default of 512 + Params::avoidPriorityQueue leave at default of false + Params::pooledPacketInitial leave at default of 0 (or set to pooledPacketMax for extra efficiency at cost of memory) + Params::pooledPacketSize leave at default of -1 (uses same size as raw packet size, this is critical) + Params::wrappedPoolMax leave at default of 1000 + Params::processIcmpErrors leave at default of true + Params::connectAttemptDelay leave at default + Params::reliable[0] leave all settings at defaults + + +- added reliable-channel level coalescing support. This is a major feature enhancements to the UdpLibrary. + Originally, the UdpLibrary only did coalescing at the low-level, such that it could combine together + unreliable, reliable, and internal data into the same physical packet. This is still a vital and key + feature of the UdpLibrary. Not coalescing up at the higher level of the reliable channel had its + problems: 1) it required that ACK packets on a per-logical packet basis, meaning that if + lots of tiny reliable packets were being sent, there was a bit more ack-overhead in the process. This + overhead is generally negligable except in the most extreme circumstances. 2) it required that the + reliable channel object track all those outstanding packets, which proved to be a bit of a performance + burden. 3) it required the reliable channel object allocated LogicalPacket objects to hold onto each + individual application packet (in the usual case where the application wasn't directly sending + LogicalPacket objects). To make matters worse in this regard, it ended up getting these objects from + the memory pool, which often effectively meant that tiny applications packets were stealing much + larger pooled packets and wasting memory. The new scheme has the advantage that many reliable sends + will now be performed without an allocation and without using up a pooled packet. + + One nice effect of this change will be that a much smaller pool needs to be reserved. Due to a + limitation in the low-level coalescing, if your application sends lots of medium sized packets + (256 to maxRawPacketSize bytes), then you may see a significant reduction in physical packets sent + per second. This change may sound like it would have a drastic effect on the protocol, but that is not + the case, we remain protocol compatible with old version. This is because the underlying protocol + has always supported the concept of a group logical packet. + + The benefits of this feature will be most evident in high-bandwidth server-to-server connections. You + should make sure you pooledPacketSize = maxRawPacketSize, and you should be able to reduce the size of + your pool substantially. + + Internally, the reliable channel will coalesce things up to the fragment size, which defaults to being + the same as the maxRawPacketSize. There is no hold-time for coalescing. The next time the connection + is given time (next time manager is given time), if there is flow-control-window space available, it + will flush the coalesce buffer in an attempt to use it up. So, it will effectively coalesce application + sends until there is an opening in the flow-control-window and GiveTime is called. + + The only disadvantage I can see to leaving this feature enabled (it is configurable in + UdpManager::Params::reliable[].coalesce) is for client-side low-bandwidth connections. In those situations, + it is important that the ACK traffic back to the server coalesce with application data. This will occur + even if this feature is enabled, but because the low-level coalescer is incapable of coalescing packets + larger than 256 bytes, it is possible the reliable-sends from the client headed to the server might + result in more physical packets per second. Even then, I don't see it being a performance issue, + so I would leave it enabled. The configurability was largely added such that the library could be + configured to operate as before, just in case there were some unforseen issues that cropped up with this. + + It's worth noting, that configuring the reliable channel to processOnSend will effectively circumvent + any coalescing that you might have been getting. + +- if reliable-channel coalescing is used (it defaults to ON), then the pooledPacketSize should be set + to the maxRawPacketSize. This is critical to performance. The previous recommendation of setting + it to the typical packet size won't work anymore, since it is now coalescing at the reliable channel + level, effectively increaseing the size of packet it will grab from the pool typically. + +- made substantial CPU-usage improvements to the reliable channel resend algorithm in situations where + thousands of outstanding packets needs to be supported to achieve server-to-server throughput. + This does not effect the protocol in any way, just uses slightly less memory to manage the process and + potentially substantially less CPU. + +- made a slight change to the reliable channel fast-recovery algorithm (based on the TCP Reno algorithm) + When we notice that packetloss is selective (ie. some packet after the lost packet is acked and serves + as a notice to do an accelerated resend on the other), we used to shrink the flow-control window + by 50% (as opposed to non-selective loss which completely resets it). We now only shrink the window + by 33%, in an attempt to make for an even faster recovery and less hiccup. I don't believe being this + aggressive will be an issue, as we know more about the nature of the circumstances than Reno does, since + we have selective acks. + +- fixed a nasty design flaw that was causing the maximum effective reliable logical packet transfer rate + to be limited to 256 times the number of GiveTime iterations per second. When small packets are being + sent, this can turn into a serious limitation, particularly on server-to-server communications where + these packet rates potentially get very high. The limit has been increased to 8192, which should be + plenty large for even the highest bandwidth connections. Note: this fix causes that an additional + 31k of stack space is used during the GiveTime call -- I am doubting this will cause anybody grief. + +- improved the pooled packet scheme. The old scheme used to fall back to standard SimpleLogicalPackets + after it has created the maximum number it was allowed in the pool. The new scheme always creates + packets that can go into the pool when they are returned (if possible size wise). It then releases + packets that are returned to the pool if the pool is already at its max. So as before when max + represent the max anywhere, the max now represents the max it will hold onto in the available queue. + This will make a big performance difference in situations where just one of many connections is + backlogging. In the old scheme, a backlogging connection could eat up the entire pool, leaving + all other connections left doing allocs and frees of their packets. With the new scheme, the + bad connection can't monopolize the pool, as when the bad connection finally clears up and release + all those packets, those packets will be destroyed even though they were gotten from the pool, because + the pool available will have likely grown as appropriate. Between this change and the reliable + coalescing, the pooledPacketMax setting should be set MUCH smaller than it previously was. I + wouldn't set it to over 5000, even for server-to-server connections. Additionally, I would set + pooledPacketInitial to under 1000, since the vast majority of the time I don't see this ever + being exceeded. + +- changed the default setting of pooledPacketMax down to 1000 (from 2000), in order to reflect the + recent optimizations noted above and the lack of need for a large pool most of the time. + +- changed the default setting of pooledPacketSize from 256 to -1. -1 means use whatever size the + maxRawPacketSize is set to. As a general rule, you should never change this setting from this. + Now that the reliable channel is coalescing, all allocations from the pool are of maxRawPacketSize. + If you set the pool size smaller than this, then odds are the pool will be completely worthless. + There are some odd cases where you may wish to set it higher, depending on the nature of the + packets your application sends. + +- got rid of the deque object that was used internally to track outstanding LogicalPackets that were + queued up on the reliable channel. Since we put in coalescing at the top, all packets smaller than + the maxRawPacketSize were getting coalesced into a larger logical packet anyways, meaning that the + logical packet that we owned was extremely unlikely to be shared. If we knew for a fact that all the + logical packets we had queued were not shared by other connections, then we could get rid of the deque + object and have a much faster and simpler linked-list scheme. Since shared packets are now so rare, + I decided it would be worthwhile to switch to a linked list and in situations where the logical packet + is shared, I create a WrapperLogicalPacket object to effectively make it appear unique from a linked-list + management point of view. This wrappers are actually gotten from a pool owned by the UdpManager. You + can set the size to allow this pool to grow with Params::wrappedPoolMax. I recommend leaving this + value at the default of 1000, though if you are a server doing a lot of large shared packets, then it + may be worth your while setting this to a larger value. This optimizations gets rid of the nasty + deque expansion problem that was eating up so much CPU when backlogging occurred. + + Note: This also means the PointerDeque.hpp has been removed from the UdpLibrary as it is no longer + used. You can remove this object from your build. + +- added ack-deduping support. This can be disabled (Params::reliable[].ackDeduping), but defaults to + enabled (true). I really can't think of any reason you would ever want to turn it off. What it does + is cause ack-packets in the low-level coalesce buffer to be deduped. There are no performance + implications (in fact, it is faster when enabled). How it works is if the library is sending an + ack-all packet and there is a previous ack-packet of some sort still sitting in the low-level + multi-buffer, then it will replace the existing ack instead of appending one onto the multi-buffer. + In high-bandwidth situations (or if reliable coalescing is disabled), this can result in an appreciable + bandwidth savings. The only situation where you might want to disable this is if for some reason you + were counting on the additional acks to intentionally overflow the multi-buffer and force it to send + rather than waiting for multi-buffer timeout. But, that doesn't make a whole lot of sense. + +- changed the default value of maxDataHoldSize to -1 (used to be 512). Setting it to -1 now causes it + to use the maxRawPacketSize for this. Generally you should always be wanting to coalesce to as + large of a packet as is allowed. + +- added new configuration parameter, icmpErrorRetryPeriod. This setting allows you to not terminate the + connection at the first sign of trouble (ie. an ICMP error), but instead wait for a period of time in + hopes that the situation will correct itself. Normally, a single ICMP error packet is sufficient cause + to terminate a connection. However, there are situations where it may be desireable to tolerate ICMP + errors for a few seconds as sometimes these situations resolve themselves. In particular, the new + port-remapping feature (see below) requires that some tolerance be set in this regard, as ICMP errors + will likely occur for a short period of time while remapping is occurring. Although it changes + previous behavior of the library, I have decided to make this new setting start out at 5000ms. This + means that ICMP errors for port unreachable and such will not occur for at least 5 seconds, whereas + before they may have occurred nearly instantly. If you want the previous behavior, setting this value + to 0 will cause it to disconnect on even a single ICMP error. Once the first ICMP error is received, + the timer is started. If a subsequent ICMP error comes in after the timeout period has expired, then + it will be processed and the connection will be closed. Once a successful packet is received on the + connection, the ICMP timeout period is reset, such that a subsequent ICMP error will be treated as if + it were the first. + +- added new configuration parameter, allowPortRemapping. This setting allows the UdpLibrary to effectively + change the port-number associated with a particular connection dynamically on-the-fly. Now, normally + this would never happen, but sometimes a user may be going through a NAT router that feels the need to + change the associated mapping mid-session. Normally when this occurs, connections are simply lost. + However, due to a bug in the #1 selling Linksys router whereby it does this remapping every 10 minutes, + we have decided to add this feature to deal with it. This feature is also generally useful in other + situations. For example, with the EQ channel-chat system, the user often goes for quite a while without + sending or receiving any messages. Many NAT routers expire the port-mappings fairly quickly if data + is not being sent. The usual solution to this problem is to configure it to send keep-alives, and + generally that works. In the case of the EQ channel-chat, we have keep-alives configured to go out + every 45 seconds, which keeps 99% of NAT routers alive and happy. The other 1% seem to expire after as + little as 30 seconds. We put in an override setting in EQ to have it send the keep alives every 15 + seconds instead, but it is a less than ideal solution as it requires the user changing things. We + don't want to configure everybody to send keep-alives that frequently, as there are 100,000 connections + to the server, and the keep-alives alone start to really add up. This port remapping system will + effectively allow the mapping to expire and the connection never miss a beat. See allowAddressRemapping + for a discussion of security concerns. This parameter defaults to 'true' or enabled. While this + feature adds some new packetting, it remains protocol compatible with old versions. You will need to + upgrade both ends of the connection for this feature to be available though. + +- added new configuration parameter, allowAddressRemapping. This setting is almost identical to the + allowPortRemapping setting, only it takes the concept one step further, and allows for the IP address + itself to be changed on-the-fly as well. Normally when only port remapping is enabled, if the IP address + is changed, it will not be able to pick up on it. By default, this parameter is 'false' or disabled. It + is highly unlikely that under any sort of normal operating conditions this will occur. About the only + reason I can think of for this is if you wanted to be so tolerant as to allow a modem user to disconnect + and reconnect from their ISP and maintain their connection. The biggest danger in enabling this feature + is the potential for somebody to hijack somebody elses connection. This is extremely unlikely however + as the UdpLibrary effectively uses a 64-bit randomly generated key to authenticate any request for + a remapping to occur. To hijack a connection, somebody would have to guess what that 64-bit key value + is for a particular IP address. + +- added a new static UdpConnection::DisconnectReasonText function that will translate a disconnect reason + into a displayable reason-string to make logging of disconnect reasons easier to read. + +- put in safety checks to try and identify packets that are corrupt for some reason. When one of these + packets is spotted, the application is notified via the UdpConnectionHandler::OnPacketCorrupt callback. + Statistics are also kept on the connection and on the manager regarding how many of these occur. If you + have CRC bytes enabled, then the odds of a corrupt packet making it through the check are extremely low. + In this case, odds are the actual cause of the corruption is intentional packet tampering by the user; + that is, they modify the packet and touchup the CRC so it passes. This is why we callback the application + as they may wish to log this event as a means of monitoring possible cheating. This new callback provides + the application with the packet data at the time the corruption is sensed. This means that if the + corruption is sensed after the decryption has occurred (say, by the multi-packet unpacker), then the + data passed to the application will reflect the decrypted packet. Additionally, the callback provides + a reason why the packet was deemed corrupt. If you are seeing a lot of corrupt packets for some reason, + it may be worth logging the reason and trying to find a pattern (I also like to log the IP of where it + came from, as often times bad packets tend to come from the same IP over and over, often due to + tampering). + +- added new configuration parameter, reliableOverflowBytes. Normally, you can send an unlimited amount + of data to a connection and it will never overflow, instead it will queue up the data until it runs + out of memory and crashes. In the past, it has been the applications responsibility to periodically + check how many bytes are pending using UdpConnection::TotalPendingBytes and take appropriate action. + This new parameter causes the UdpLibrary to do this check for the application, and if a connection + ever overflows this amount (all reliable channels added together), then the connection is disconnected + with a reason of cDisconnectReasonReliableOverflow. The default setting for this parameter is zero, + which disables this check and makes it operate as it always has. The check for an overflow condition + does not occur at send time as this would result in a callback to the application at an illegal time; + instead, the overflow condition is checked the next time the connection object is given time. + +- added a new sample-program to the distribution. This program is called 'udpstress' and simulates what + one might see in a backend server-to-server cluster implementation. Each udpstress client that starts + up connects to one of the existing udpstress clients that is already running, and effectively adds itself + to the cluster. Each node in the cluster transfers data to every other node in the cluster at a compile + time configurable rate. Since everybody is connecting to everybody else, this stress program operates as + both a client and a server. Unlike the other sample program, it fully takes advantage of the new + callbacks that were added and more accurately reflects the proper way of setting up an architecture. + This program is also portable to other platforms. + +- removed the old client/server test programs from the distribution. The udpstress program covers both + client and server examples, is far more flexible for testing, and is written to take advantage of the + latest features, providing a better example to follow. + +- moved the processing of the flagged port unreachable further up in the InternalGiveTime function. This + would have only effected the sparc version of the UdpLibrary and the way it handles ICMP errors. There + was a bug whereby it was going through the connected give-time logic when in fact it may have been + disconnected by a port unreachable. This likely didn't cause any problems, but I moved it just to be + safe. + +- added new UdpMisc::Sleep function. This is not used by the UdpLibrary, but is used by the sample + applications and in general provides a portable way for applications to sleep. + +- made some internal optimizations that allows the UdpLibrary to avoid making an extra copy of the data + (in most circumstances) in order to append CRC bytes. When coalescing is likely to occur and encryption + is not being used, this optimization will speed up the cost of CRC checking on the send side a fair amount. + +- created a new variation of the HashTable called an ObjectHashTable. The UdpLibrary now uses this variation + which is optimized to assume that the contents of the hash table are pointers to objects that are derived + from HashTableMember. This allows the hash table to avoid allocations/deallocations entirely when new + entries are added/removed. UdpConnection objects make use of this optimization. The new hash table object + has restrictions the old one does not, namely, its members can only ever be in one hash table, and then + only in it one time. For most applications, this is not an issue. The old version is no longer used by + the UdpLibrary but has been kept in the header file because some applications have started making use of it, + and we don't want to mess them up. + +- recently, the UdpLibrary has been seeing much heavier load as a server-to-server interprocess communications + protocol. While it was designed to handle this sort of thing, it did have one short-sighted limitation; it + was only capable of transferring 4 billion logical reliable packets per session before it would fall apart. + Even at a very high average trasfer rate of 1000 packets per second on a single connection, it would take over + a month to exceed this rate. It seemed at first that this might be a reasonable limitation, until Planetside + and SWG came along. These games have interprocess packet rates that are sometimes 10 times that high, meaning + that critical failure could occur in only a few days in extreme circumstances. Fixing this limitation did + not require a protocol change, merely some internal changes to the way packets are tracked. You need only + upgrade both sides of a connection simultaneously to maintain compatibility if that particular connection + will exceed 4 billion packets in a single session. There is now no limit on the amount of data that a single + session can send reliably. + +- for the Win32 version of the Clock function, I had come up with a rather clever technique to avoid threading + issues and clock-wrap. Turns out the thread-local-storage scheme I was using doesn't work if the UdpLibrary + is stuck in a DLL. The odds of a clock wrap that occurs once every 47 days thread-slicing between two + consecutive interger assignments and causing a problem are astronomically rare, so I have decided not to + worry about the potential issue. What I really need is a 64-bit interlocked increment, but Windows doesn't + add support for that until the next version (after XP). + +- added new configuration parameter, processIcmpErrorsDuringNegotiating. This boolean parameter defaults to + false, which was the default behavior of the UdpLibrary before this became configurable. Setting this to + true will cause a client side connection attempt to fail immediately if the server process is not running + (ie. doesn't have the port open). Usually you will want to leave this at the default as often times two + processes that talk to each other are started at the same time, and you don't want a race-condition to cause + the client to fail because it happened to startup quicker than the server. + +- put a hard cap on the maximum allowed maxOutstandingPackets configuration value. If this value were ever + set past 30000, things potentially could have fallen apart. I can't imagine there ever being a need for this + many outstanding packets. Eventually when I get the reliable-channel coalescing put in, this cap will become + even more meaningless. + +- made the maximum allowed resend delay configurable. This allows the application to put a cap on the longest + it will ever wait to resend a lost packet. Without a cap, lost-packet throttling can potentially cause the + resend delay to get excessively long. You should generally leave this value alone, but tweaking with it for + certain internal-network connections may be desireable to optimize performance. For example, if you know you + are on an internal network, then it may be safe to set this value to the larger of double the typical ping-time + and the longest GiveTime cycle time of either end of the connection. Like many values, unless you really + know what you are doing, you probably shouldn't mess with this. I would never set this value to less than + 1000 for anything, but setting it to a value like 2000 might be desireable for some internal server-to-server + connections. The default is 5000. This cap has always existed in the UdpLibrary, it has just been hard-coded + in the past. + +- got rid of the temporary encryption and decryption buffers in the UdpManager and replaced with stack-based + buffers. Doing this has two side effects. 1) more stack space is used, though it won't be very much. + 2) there is now a hard-limit on how large you can set the maxRawPacketSize. Currently that hard limit + is set to 16384 bytes. I can't imagine anybody in their right mind would want to set it anywhere near + that high. On some crazy chance that you did want to set it higher, you would simply need to change the + cHardMaxRawPacketSize constant in the header file. In theory it could never go over 65535 anyways, since + that is an IP protocol hard limit. The only implication to increasing this number is more stack space usage. + +- added debug signatures to the end of the encryption buffer and an assert to catch cases where user-supplied + encryption routines overflow the end of the destination buffer. Have yet to see this happen, but I am + learning more and more that the more stuff the UdpLibrary can do to help the application find bugs in its + usage of the library, the better. + +- made sure UdpManager constructor initialized mPassThroughData to NULL. + +- got rid of UdpMisc::FindAvailablePort function. The reason for getting rid of this is that any use of + it will almost surely be an unsafe/bad practice, as once it has found a port for you, it is entirely possible + that some other process on the machine may grab that port before you create a UdpManager and bind to it. We + found this out the hard way in EQ. + +- added a new configuration option, Params::portRange. This new parameter accomplished what FindAvailablePort + was supposed to do, only it does it in a safe manner. If you wanted a to bind to a random port between + 40000 and 40999, you would specify 'port' to be 40000 and portRange to be 1000. Upon construction, the + manager would then randomly pick a port in that range and try binding to it. It will try all ports in the + range before it completely fails and gives up. You cannot specify a portRange to use if you have 'port' + set to zero as this makes no sense. By default this parameter is 0, which causes it to act as it always has + previously. + +- against my better judgement, I added a GetRefCount function to the UdpConnection, UdpManager, + and LogicalPacket objects. I actually needed it on the UdpConnection object internally such that the + UdpManager could tell if the application had accepted a connection during an OnConnectRequest callback. + +- added a new disconnect reason, cDisconnectReasonConnectionRefused. You will never actually see this reason + returned from GetDisconnectReason, since the object that would return it will already be destroyed so you can't + ask it. Where you will actually see it is from the GetOtherSideDisconnectReason function. When a client + attempts to connect to a server and that server ignores their connect request (by simply not AddRef'ing + it during the OnConnectRequest callback), the server responds back to the client with a terminate-packet. The + client will see the disconnect reason as cDisconnectOtherSideTerminated, but can further query to see that the + server explicitly refused the connection. + +- added a new disconnect reason, cDisconnectReasonMutualConnectError. You will only ever see this from + GetDisconnectReasonOther and it is a form of connection-refusal as well. In this case, what it means is + that the guy you are trying to connect to is simulatanously in the process of trying to connect to you. The + odds of this happening are very rare, but since the code was able to distinguish it from other situations, I + figured it was worth clarifying if it ever did happen. + +- added a new disconnect reason, cDisconnectReasonConnectingToSelf. This is identical to the MutualConnectError + and occurs whenever you try to establish a connection to yourself. This is somewhat more likely, and in fact + was added to the library because it actually happened. For the sake of others who might run into this, I will + give a brief description of how we managed to get bit by it. We had enabled ICMP error processing during + connection-negotiation, such that we could instantly tell if the server were down. Our client application would + then fail the connection, and immediately try again. Each time it tried, it picked the next available port. + The client in this case was another server process that happened to be running on the same box as the server + it was trying to connect to. The server it was trying to connect to was down. The server it was trying to + connect to was supposed to be listening on port 44452. The application-level retry loop was very tight with + no delay, and eventually (30 seconds or so), it had managed to cycle through enough ports that the one it + randomly picked (next available) was 44452. This effectively made it connect to itself, which actually caused + the library to eventually crash. This was SWG btw. + +- fixed bug in CRC calculation whereby it was applying the encrypt code in a byte-order dependent manner. Nobody + actually found this bug since nobody is running on big-endian based hardware. There may be other endian issues + that have yet to be tested, but in general the library is supposed to handle endian issues. Internally, all + protocols and such are done in network-byte-order (motorola big-endian order that is) + +- added new api-function UdpConnection::GetOtherSideDisconnectReason(). When a connection is terminated, if the + GetDisconnectReason function returns to you the value cDisconnectReasonOtherSideTerminated, then you will be able to + call this new function and find out what the other-sides reason was for disconnecting. While this latest version of + the library remains backwards protocol compatible, this new API feature will only be available if both sides of the + connection are using the latest version of the library. If the other side is not using the latest version, then + this function will return cDisconnectReasonNone, meaning that it doesn't know. Likewise, if this function is ever + called when the primary disconnect reason is not OtherSideTerminated, then it will return None. This should make + tracking client-disconnects easier in situations where you don't have easy access to the client that is having the + problem. + +- added new api-function UdpConnection::SetSilentDisconnect(bool silent). This function allows the application to tell + a connection that it should not notify the other end of the connection when it terminates (by sending the usual terminate + packet to the other side). This was actually added for internal reasons such that we didn't reply to a terminate-packet + from the other side with a terminate packet of our own; however, it was thought that in some circumstances it may + be desireable to let the other side languish and have to time-out, rather than us explicitly telling them that we + have shutdown our side of the connection. An example of wanting to do this might be if you were testing timeout + functionality, or if you wanted to dump a cheater hard without telling them explicitly that they had been disconnected. + +- added statistics to track how many socket-overflow errors occurred. A socket overflow error can be caused if the + socket-buffer size is too small for the overall datarate that the manager is trying to send (which could very well + happen on a busy server if you gave it a small buffer. The net result is packetloss, which will cause extra resends + later on (for reliable data), etc. If you have a fairly big outgoing socket buffer size, then this may be caused + if the application is spamming too much unreliable data out the pipe. Remember, unreliable data is not flow-controlled + in any way, so if the server instantly dumps 500k of unreliable data to all connections combined and the server only + has a 256k outgoing socket buffer, then you are going to lose a healthy chunk of that data. This statistic can be + used to determine if you have this problem occurring on a frequent basis. + +- made UdpManagerHandler::OnConnectRequest non-pure-virtual (ie. provided a default implementation). It used to be the + case that the only reason you would want a UdpManagerHandler is if you were listening for connections, now it is quite + likely that you will want one for user-supplied encyrption on the client side, so it was thought that we really should + provide a default implementation that basically ignored incoming connection requests. + + +----------------------------------------------------------------------------------------------------------------- +3/7/2003 +----------------------------------------------------------------------------------------------------------------- +- Added new configuration parameter to reliable channels UdpManager::Params::reliable[].congestionWindowMinimum. It is very + unlikely that you would want to change this parameter from its default value of zero. What this parameter does is allow + you to control how small (in bytes) the flow-control congestion window is able to shrink. How flow control works is that + as connections get congested, they slow down sending data by controlling how much outstanding unacknowledged data there + is. The congestion-window itself represents how much data this is, and it changes constantly as appropriate. When packets + are getting lost it shrinks. This new parameter added here lets you control how small you will let it shrink in response + to bad connection conditions. The UdpLibrary will never let the congestion-window shrink below the size of a single + physical packet (512 bytes by default), so all values for this setting below maxRawPacketSize are meaningless. Instead, + the purpose of this setting is to allow you to set this minimum higher than that. Setting this number higher can result + in the connection getting flooded beyond its capacity, as it may not be able to slow itself down enough to meet + current line conditions. + + A situation where you may want to toy with this number is in a game (surprise), where you expect to be the only + process using bandwidth in the system, and you don't really care to be 'friendly' to other connections running + on the system. Normally, if a modem-user is playing the game and has an TCP transfer going on in the background, + then the TCP connection and the UdpLibrary connection will both effectively settle-in on using about half the available + bandwidth. That is, the flow-control algorithms used by the UdpLibrary and TCP are such that over a fairly short period + of time, they will both settle in on a happy split. However, if half-the-bandwidth was equal to a 2k window-size, and + the UdpLibrary was configured to have a minimum window size of 3k, then the UdpLibrary would not scale down. Because + the TCP connection is willing to scale down further, it would, and you would effectively end up with a 3k/1k split + with TCP kindly taking a backseat to the UdpLibrary (of course, if you had put two 3k-configured UdpLibrary connections + in this same environment, the net result would be massive/wasteful packetloss). + + The advantage of setting a higher minimum is just the scenario described above, you can effectively configure your + application to be a bandwidth pig at the expense of other connections in the system; like I said, sometimes this is + desireable. Another advantage of having a larger minimum window size is that it allows the connection to more + quickly scale-up to optimal speed in situations where packets are lost, or there are long stalls in the outgoing + packet stream. So, if the game is plugging away at 500 bytes per second and has a window size of a minimum of 3k, then + when a sudden burst of data transfer is needed for something, it can burst a good chunk of it all at once and not + wait for the flow control window to scale up via the slow-start algorithm (which really should be called the fast-start + algorithm). + + Another case where you might want to set this higher, is if you know for a fact that the connection is always going + to be very high quality (internal ethernet). In those situation, you may want to set the minimum window size to 30k + or so, again, such that transfers get up to full-speed quicker. + + Basically, you can look at this value as the guaranteed bandwidth that you want the connection to claim. The effects of + playing with this setting are going to be very difficult to quantify, but if anybody does decide to try playing with it + and notices an appreciable difference (good or bad), I would appreciate hearing about it, just so I know when others ask + me about it. + +- Added new helper function UdpMisc::FindAvailablePort(int startPortScan, int endPortScan), that will find you a randomly + available port in the specified range, or return 0 if no available port was found. This can be used in situations where + you want the server to be listening on a random port in a particular range. Sometimes it is nice to limit the random + port selection to a particular range as it allows easier configuration of firewalls. It's worth noting that when it + finds an available port, it does not reserve it, but rather releases it and returns which one it is. It is entirely + possible that the port could be claimed by somebody else before you effectively bind to it. A better way of handling + this situation would be to actually create UdpManager's giving them a random listenPort in the range and then checking + the UdpManager::GetErrorCondition to see if it successfully bound and try again if not. In this way, you don't have to + worry about your port getting stolen. This helper function was added merely to handle a situation that was unique to + Everquest where we did not have control of the underlying communications library in order to add this functionality. + +- API CHANGE: UdpManager::GetHostByName has been moved and made a static function of UdpMisc. This allows you to do this + function without creating a UdpManager. + +- API CHANGE: UdpConnection::Disconnect no longer gives the application the option of not being notified via OnTerminated for + the disconnect event. The application can only specify a flushTimeout. When the application does specify a flushTimeout + the OnTerminated callback does not occur until the actual connection is fully disconnected (by either timing out on the flush + time, or successfully sending all the pending data and terminating). While the connection is in a cStatusDisconnectPending + state, it is possible for some other event (like an ICMP error for example) to cause the connection to switch to a + cStatusDisconnected state before all pending data is sent. + +- API CHANGE: OnTerminated callback will now be called ANYTIME a UdpConnection object switches to a cStatusDisconnected state, + regardless of the reason. Once a UdpConnection is in a cStatusDisconnected state, it will always remain as such. Since + newly created UdpConnection objects start out in a non-disconnected state (they are either connected or negotiating to start + with), all UdpConnection objects created are guaranteed to eventually receive one and only-one OnTerminated callback. In + previous versions of the library, a connection that was still negotiating (trying to connect) would not call OnTerminated + if it failed, now it will. In this manner, it will be easier and more clear for an application to maintain a strict balance + between connection creation and destruction. Furthermore, connections that are explicitly terminated by an application + call to Disconnect will now callback via OnTerminated (if necessary). + +- API CHANGE: OnConnectComplete callback is now only called when a connection is successfully established (as initiated by an + EstablishConnection call). Previously, any negotiating connection would callback via this function when it left the + negotiating state (either succeed or fail). + +- API CHANGE: moved user-supplied encryption callbacks from the UdpConnectionHandler to the UdpManagerHandler. If you are + using the user-supplied encryption callbacks, then you will likely need to make some minor changes to your code, by creating + a handler object for the manager and moving the callback routines to there. This should in no way effect the type of + encryption you can do as you will still have access to the UdpConnection object. The purpose of this change was to deal + with a bug whereby the encryption routines could have been called on before the application had an opportunity to set + a connection-specific handler. + +- made API and library more const safe. There are likely a few other places where I could have used const that I did not, I + am sure they will all get tracked down eventually. + +- fixed bug where in theory an infinite loop situation could occur. This could have happened in a situation where one + connection was bumping another connection to the top of the priority queue and vice-versa during priority-queue processing. + This is the second time this issue has come up, so this time I put in a more comprehensive fix. Now, the priority queue + will refuse to let any connection schedule itself for processing time sooner than 1 ms after the current processing cutoff + line. This will also help to deal with issues where the clock might go backwards (though in theory that won't happen now + either). + +- documented the UdpHandler.hpp file more thoroughly to describe what the various callback functions do. + +- fixed a bug where the congestion-window used for flow control was able to shrink down to zero when certain rare packet-loss + patterns occurred. It turns out that if you are under heavy flow and the timing mechanism used by the library goes + backwards it can cause this to happen. This only happened on Linux which uses the real time clock for timing, and the system + clock was changed while the program was running. The fix prevents the congestion-window from dropping below the size of + a single packet. (Thanks Thomas Farthing for help in finding this one) + +- changed the Linux version of the UdpMisc::Clock function to compensate for a backwards moving clock caused by the system + time being changed mid-application. There is still and issue with time jumping forward when the clock is changed, but + that will tend to cause less problems. If anybody knows a relatively portable way of getting millisecond accurate timing + that isn't relative to the real-time clock under Linux (something equivalent to Windows GetTickCount), let me know. + (copied Vince Harron's time-correction routine for this purpose). + +- changed the Windows version of the Clock function to better handle clock-wrap in situations where there are multiple + threads in the process calling the function at the same time. (used thread-local-storage to solve the issue) + +- enhanced user-supplied encryption/decryption callback functions such that if they return -1 it is treated as an + error-condition and the packet is thrown away by the UdpLibrary. This could occur if the application detected during + decryption that something wasn't right. Perhaps somebody tampered with the packet, or it got corrupted on-route yet + still managed to make it through the UDP header checksum and any UdpLibrary CRC checks that might have been in place. + +- fixed bug in explicit bind where it was getting the network byte-ordering wrong (thanks Justin Randall for finding this one). + +- fixed a bug where the OnTerminated callback was not being called in situations where an existing connection was terminated + due to a subsequent new connect-request coming from the same IP and port. Basically, the InternalDisconnect function was + being called and told not to bother notifying the application of the event...I have no idea why I would have told it not + to notify the application of termination for this reason, but it is now fixed. + +- added direct winsock2 support. If you want the UdpLibrary to use version winsock v2.2, then you should define the + symbol UDPLIBRARY_WINSOCK2. This will cause it to use the winsock2.h header files and request version 2.2 of winsock + on WSAStartup. Since the library itself doesn't make use of any winsock2-only features, it really doesn't buy you much + to do this. The exception is if other parts of your program are using winsock2 functionality for some reason, you may + have to define this in order to successfully compile, since the winsock.h and winsock2.h headers conflict. + +- modified the hashtable template class and gave it the ability to iterate through its contents with WalkFirst and WalkNext. + This feature isn't actually used by the UdpLibrary, but some other stuff I am working on that shares that hash table object + needed it. + +- fixed some code issues that were causing level 4 warnings (all future releases will be checked against level 4 warnings now) + +- fixed problem where NULL was being implicitly converted to a UdpIpAddress inside of GetHostByName + + +----------------------------------------------------------------------------------------------------------------- +11/26/2002 +----------------------------------------------------------------------------------------------------------------- +- thanks to Ben Hulse for porting the library to Solaris/sparc. His changes have been rolled into the master distribution. + The ICMP error processing on this platform is done in yet another way from the other platforms. When you attempt + to send data to a destination that has previously returned and ICMP unreachable error, the sendto function fails. Given + that we use the same port for multiple destinations and that hundreds of outgoing packets to other connections could have + occurred before we send to the one that returned the error, I suspect this feature may not be working under Solaris. I + have seem some references on the web that indicate that UDP sockets do not receive ICMP errors unless they are connected + (which obviously we can't do). I didn't immediately see a way that this could work; though, I think it would be worth + trying to do it the basically the same way Windows does on the recvfrom call. + +- we have discovered that ICMP errors are not being forwarded up to the application under Windows NT and Windows 98. These + operating systems echo back ICMP errors as appropriate, but applications running on these platforms will not receive + them on the other end. Windows 2000/XP do not appear to have this problem. Our NT testing was using service-pack 6. + +- fixed bug in replyUnreachableConnection processing. The net result is that the library should more accurately + detect when a connection has gone dead in situations where the server has shutdown and restarted again fairly quickly. + +- optimized the code dealing with removing a connection from the manager. In previous versions of the library this required + a linear search through all connections as they were tracked with a single linked list. They are now tracked with a + double linked list such that they can be removed without the search. This change gets rid of the last linear operation in + the library relative to the number of the connections (in normal processing). + +- removed the UDPLIBRARY_SAME_ENDIAN flag. This turned out to be a source of confusion and in practice probably should + never have been used anyways. It was merely an optimization that allowed the integer packing routine to operate more + quickly in situation where both ends of the connection were guaranteed to be the same byte-order platform. + +- added function UdpManager::GetHostByName, which does a DNS lookup to translate a name into an IP address. Even though + functions such as EstablishConnection will automatically do the DNS lookup if necessary, it is recommended that the + application do this translation themselves on startup if the architecture is such that the connection might be established + multiple times. The reason for this is the lookup is a blocking call and it saves having to do it multiple times potentially. + +- added support for binding the socket used to a particular IP address (UdpManager::Params::bindIpAddress) + Normally, and by default, the library will bind the socket to any address in the machine. This setting should + not be messed with unless you really know what you are doing. In multi-homed machines it is generally NOT + necessary to bind to a particular address, even if there are firewall issues involved, and even if you want + to limit traffic to a particular network (firewalls do a better job of serving that purpose). If you are having + problems communicating with a server on a multi-homed machine and think this might solve the problem, think again. + You most likely need to configure the OS to route data appropriately, or make sure that internal network clients + are connecting to the machine via the machines internal IP address (or vice versa). The only reason I have come up + with to bind to a specific IP is for security reasons above and beyond those that would normally be provided by + a firewall. + +- added configuration parameter UdpManager::Params::processIcmpErrors to turn on/off ICMP error processing. By default + ICMP error processing will be enabled. If you are having problems with false ICMP errors causing disconnections, then + you may want to disable this option. False ICMP errors can occur when a router along the way momentarily goes down + but is traffic is quickly re-routed, such that termination of the connection wasn't really necessary (at least, that's + my theory). + +- added configuration parameter UdpManager::Params::connectAttemptDelay. During connection negotiation, the client will + periodically send the server connect-attempt packets until the server acknowledges the request for a connection. This + value represents how frequently the client sends the connect-attempt packets. By default this is set to 1000 and as a + general rule, should not be changed. The reason this configuration parameter was added was such that I could slow down + the attempt-rate used by clients of the EQ channel-server. At peak, the channel server will have upwards of 90,000 + simultaneous connections. Additionally, there will be up to 2000 people who are attempting to connect but cannot (most + likely due to a firewall issue on their end). These 2000 people will continously try to connect for 30 seconds, sending + the channel-server a packet once per second, resulting in an extra 2000 packets/second coming into the channel server from + people who will never be able to form a connection. Since packetloss is relatively rare and rapid connection establishment + is not critical, I wanted to slow down the rate at which these connection-attempts flooded the server. + +- included to get the definition of NULL. + +- added carriage return to end of some header files to prevent warnings from gcc compiler + +- removed an assert that could happen in normal processing after all. Turns out that some ICMP error packets will show + as coming from port 0 (presumeably when the entire IP address is not reachable?). We have no way of doing an IP-only + base connection query without looping through every connection, which we really don't want to do, so for now we are + ignoring ICMP error packets that have a port of 0. The appropriate behavior would probably be to terminate all + connections that came from that IP address. This doesn't appear to happen very often though; we had the EQ Channel Server + running for 3 hours with 80,000 connections before it happened for the first time. ICMP processing to accellerate + disconnection recognition is really a bit of a luxury, so I don't feel too bad ignoring this rare case. + + +----------------------------------------------------------------------------------------------------------------- +10/3/2002 +----------------------------------------------------------------------------------------------------------------- +- added new function UdpConnection::IncomingBytesLastSecond(), which returns the number of raw bytes the connection object + has received in the last second (accurate to within 25ms). + +- made sure ScheduleTimeNow was never executed during a GiveTime call. If it was, then the net result would potentially be + an immediate re-giving of time again, which I am thinking could possible create an infinite processing loop. + +- added new function UdpManager::GetErrorCondition(), which returns an enum ErrorCondition value (see header file). The + only error conditions that it currently checks for is the ability to create and bind the socket. If you are using a fixed + port and are concerned that somebody else (like another copy of the program running) might already have it, then you should + check the error condition after constructing the UdpManager object. + + +----------------------------------------------------------------------------------------------------------------- +9/18/2002 +----------------------------------------------------------------------------------------------------------------- +- fixed problem where it was unable to establish a new connection because the server-side thought it already had a connection. + This actually would not happen except under very unique circumstances. In particular, you had to establish a connection + to the server, then, using the same UdpManager object, establish another connection to the same server. It is illegal + to have multiple connections between the same two UdpManagers, as the resultant UdpConnection objects would effectively + have the same ip/port combo's, effectively making it impossible for the UdpManager to know who incoming data was coming + from. The problem arose when the client had actually terminated one of the connections, but the server was unaware of it. + Then, the next conenct-attempt would simply be ignored since the server would think it already had a connection to that + ip/port. You would think that the server would eventually timeout the old connection object, but the problem was that + the new connect-attempts (that were being ignored) were actually refreshing the no-data-timeout on the existing connection. + This effectively put it in a state where the client could never re-establish the connection. + + This would actually only occur if the second-connection attempt was using the same UdpManager as it had for the first + connection, or if the UdpManager it was using had been assigned the same port-number again (either by explicit override, + or by dumb-luck). The fix for this is to have the existing connection terminate if it gets a new connect-attempt that + is using a different encrypt-code. When this set of circumstances happens and is the cause for a connection to be + terminated, the disconnect-reason will be set to cDisconnectReasonNewConnectionAttempt. + + On a side note, since clients generally only have one outgoing connection for a given manager, it is generally a good + idea to have the UdpManager created and destroyed along with the connection. Each time the UdpManager is re-created, it + picks a new port to use, which effectively eliminates these issues. This is also a good reason not to explicitly + set the Params::port on client side connections, but instead leave it at 0, so the library will let the OS pick a random + port (that presumeably hasn't been used recently). + + The situation where this bug first appeared was one where the client was actually using the same UdpManager to connect + to dozens of servers simultaneously, so re-making the UdpManager wasn't really an option, since all the other connections + would still be operating. + +- got rid of C-style callback functions. If you are currently using this style of callback you will need to convert + your code over to using handlers. I don't think anybody was using user-supplied encryption/compression routines + yet, but those have been removed as C-style callbacks as well and added to the UdpConnectionHandler class. See the + UdpLibrary.doc file or the udpserver.cpp sample code for a description on how to use handler based callbacks. A quick + and dirty way of converting an existing C-style callback implementation into a handler based one would be to use the + following class: + + class MyHandler : public UdpManagerHandler, public UdpConnectionHandler + { + public: + inline virtual void OnConnectRequest(UdpConnection *con) { AppConnectRequest(NULL, con); } + inline virtual void OnRoutePacket(UdpConnection *con, const uchar *data, int dataLen) { AppRoutePacket(NULL, con, data, dataLen); } + inline virtual void OnConnectComplete(UdpConnection *con) { AppConnectComplete(NULL, con); } + inline virtual void OnTerminated(UdpConnection *con) { AppTerminated(NULL, con); } + }; + + Where the AppXXXXXXXXX functions are functions you had previously passed in as callback functions in the UdpManager::Params. + The only thing lost is there is not a UdpManager mainPassThrough pointer anymore (first parameter to the ConnectRequest + callback in the old scheme), but odds are it wasn't used anyways. Finally, you need to create an instance of the above + class and set it up as the UdpManager::Params::handler for the UdpManager, and call UdpConnection::SetHandler(...) for + each newly created connection as well (just after either EstablishConnection or on the server-side in the OnConnectRequest + function). + + The above dummy-handler is primarily intended to handle converting existing code that uses the old method. New code + should generally have one handler object for the UdpManager and a seperate handler object for each UdpConnection created. + Again, see the udpserver.cpp code for an example of this should be set up. + +- when reliable data is sent, it remains in the queue until it is acknowledged by the other side. When you query + the reliable channel status, you can find out the age of the oldest piece of data in the queue that has been sent + yet has not been acknowledged yet. As a general rule, if things are operating correctly, it should be very rare + for something that has been sent to not be acknowledged within a few seconds, even if resending had to occur. + Eventually, the sender could use this statistic to determine that the other side is no longer talking and terminate + the connection. In past version of the UdpLibrary, the sending-application has checked this statistic itself. A new + parameter has been added UdpManager::Params::oldestUnacknowledgedTimeout. This parameter will cause the UdpLibrary + to monitor this for you and automatically change the connection to a disconnected state when the value goes over + this setting (in milliseconds) (0 means do not perform this check at all) The default is set fairly liberally, + on client-side connections, you could safely set this to as low as 30 seconds (30000) allowing for quicker realization + of lost connections. Often times the connection will realize it is dead much quicker for other reasons. + When disconnected due to this, the disconnect reason is set to cDisconnectReasonUnacknowledgedTimeout + +- added UdpManager::Params::replyUnreachableConnection. When this parameter is set to true (the default), it causes the + UdpManager to reply back to destinations with unreachable-connection error packets when the server does not have a + UdpConnection object representing the destination. The purpose of this is to allow a client who has had its connection + terminated by the server to quickly realize that the server no longer considers the connection valid. Normally the + client would be told when the server terminated the connection, but that termination notice can get lost leaving a client + thinking it is connected for longer than it should. See the header file for this new parameter for more details. + (note: the client can sort of sense this is occurring now by monitoring the oldestUnacknowledgedAge in the reliable + channel status, this just makes it occur quickly instead of having to time-out). + + +----------------------------------------------------------------------------------------------------------------- +8/26/2002 +----------------------------------------------------------------------------------------------------------------- +- when I fixing the bug below, I also took the liberty of having the UdpConnection object hold a reference to the + UdpManager during processing, such that it would handle the case where the UdpManager was deleted during a callback + as well. The problem is, I made the same mistake that caused me to mess with the code in the first place, that is, + referencing the mUdpManager pointer after a callback. But the release still needs to take place, even if we lose + our link to the UdpManager due to a disconnect, so to fix it, I have the UdpConnection object hold its reference to + the UdpManager in a stack variable (effectively). + + +----------------------------------------------------------------------------------------------------------------- +8/23/2002 +----------------------------------------------------------------------------------------------------------------- +- fixed bug where during a UdpConnection::GiveTime call, the mUdpManager member could become NULL (if the application + did a hard-disconnect during a callback function), and the GiveTime function later on used the mUdpManager pointer + without checking it. I think in practice this would have only happened if the application did a hard Disconnect call + during the processing of an incoming reliable application packet. Whatever the case, I have went through the code + and verified that mUdpManager is checked for NULL everywhere it could possibly be used after a callback. + +- added the ability to modify the keep-alive delay on a per-connection basis (see UdpConnection::SetKeepAliveDelay) + +- fixed some compiler warnings/errors for the Linux compile (I don't actually compile the Linux version, so I count on + those who do to tell me when I break things :). + + +----------------------------------------------------------------------------------------------------------------- +8/19/2002 +----------------------------------------------------------------------------------------------------------------- +- added a UdpConnection::GetDisconnectReason function that returns the reason that a connection was terminated. + +- fixed bug where it wasn't tracking the duplicate-packet-received statistic correctly + +- added two new parameters for configuring reliable-channels. I now allow you to set the resendDelayAdjust and the + resendDelayPercent. These two values allow you to fine-tune the resending algorithm to make the channel either more or + less aggressive about correcting lost packets. On some channels, you may be willing to unnecessarily send the packet twice + to ensure that it gets there as soon as possible; whereas on other channels that are less critical, you may be willing + to tolerate a slower correction time to avoid sending something you might not need to. See header-file for details on these + parameter values. + +- changed several of the UdpMisc functions to take void* instead of uchar*. This was to make it easier for applications to + use these helper functions to hand-pack packets in order to be byte-ordering and structure-packing safe. + +- added string-hashing functions to the hashtable.hpp file, since I have found myself using the hash table object in a lot + of places for strings and it seemed generally useful. + +- added the ability to force the UdpManager to pre-allocate a certain portion of the packet pool. You might want to do this + if you are very concerned about memory fragmentation and you know for sure that you are going to be using a certain minimum + number of packets from the pool anyways. The new parameter is UdpManager::Params::pooledPacketInitial. + +- modified the ReliableChannel object to get temporary packets from the pool instead of allocating them. It uses temporary + LogicalPackets to hold onto incoming data that arrives out of order. Again, if you are not configuring the UdpManager to + have a pool and having your application use it as well, you are doing yourself a huge disservice. + +- changed the logical-packet queue to expand itself 64 packet-pointers at a time instead of 1024 at a time. This effectively + reduces the minimum connection footprint by almost 4k at the cost of having to expand this buffer on the fly if the number + of queued logical packets ever exceeds 64. Most of the time this expansion will not have to occur, if I find otherwise in + some application, I will make the expansion rate of this queue a setting instead of hardcoded. + +- added a slightly more efficient, though not quite as effective non-buffer based Xor encryption method. I ended up adding + this as I am trying to avoid the 512 bytes/connection overhead of the XorBuffer method. This should be good enough for + most situations, we are just trying to make the packet-stream look like work to hack to the casual hacker. This small amount + of memory saved actually turns out to be critical for the application I am doing as I am trying to get 200,000 players + connected to a single server. + + +----------------------------------------------------------------------------------------------------------------- +7/1/2002 +----------------------------------------------------------------------------------------------------------------- +- added a new 'interations' statistic to the UdpManagerStatistics. This represents how many times UdpManager::GiveTime has + been called. Divide this by elapsedTime to get an average number of times per second GiveTime is called. This should + generally be at least 10 or else you are likely creating too much processing-induced-lag (as I call it). A similar statistic + has been added to UdpConnectionStatistics + +- changed code to ignore ICMP port unreachable errors while in cStatusNegotiating mode. This was necessary in order to prevent + a client who happened to startup moments before the server did from aborting it's connection attempt because the port wasn't + open yet when the first packet went out. + +- added a new UdpManager::Params::portAliveDelay parameter which is intended to be used on the client side to keep the NAT or + firewall port alive without having to actually send the server unnecessary keep alive packets. This is very similar to the + keep alive delay, but serves a completely different purpose and may not have the desired effect on all platforms. The purpose + of the keepAliveDelay is generally to keep data flowing to the server so it knows that the client is still there. In this + manner if the server doesn't get data within a certain period of time, it can know that the connection is probably dead. + Sometimes it is the case that the server does not need to be kept alive, or at least kept alive very often (like for a chat + server perhaps where nobody is talking much). For some people, it may be necessary to send data more frequently in order to + keep their NAT mapping fresh, or their firewall software happy. However, we don't want to be in a situation where our server + is receiving a lot more data than it needs to just so these people can keep their port open. I have seen NAT's that lose + mappings in as short as 20 seconds. What this feature does is a bit tricky. It changes the time-to-live (TTL) for a special + keep-alive packet to some small value (5) which is enough for the packet to get past local firewalls and NAT's, but not make + it all the way to our server. In this manner, the port gets kept alive, but we don't waste bandwidth with these packets. + These special packets are not counted statistically in any way and they do not reset any timers of any kind. Their sole + purpose is to keep the port alive on the client side. Any other data (including standard keepAlive packets will reset the + timer for this packet, so obviously the portAliveDelay must be smaller than the keepAliveDelay in order to be meaningful. + By default this feature is off. It's worth noting that the client-side will receive-back an ICMP TTL-expired packet; however, + the client will simply ignore it. It is unknown as to whether all NAT's will ignore the ICMP return packet, it is assumed + that they will. You should not set this value too aggressively as the returning ICMP packets will be taking up valuable + incoming bandwidth on the client side, even though they have no effect on our end at all. + + (update: we put this live on EQ and it fixed the problem for a lot of people; however, it also caused a different small + set of players to start having problems...probably firewalls or NATs that interpret the ICMP error packet as a reason to + shut down the port/mapping. Anyways, I am not sure it is a viable option because of this. You could make it an advanced + user option, but clearly it isn't the fool-proof solution we had hoped it would be.) + +- added an optional parameter to UdpConnection::Disconnect as to whether it should callback the application via OnTerminated + to notify of it of this Disconnect call. In either case, the callback would only occur if connection had previously + been in a cStatusConnected state. This api enhancement was primarily added for internal purposes, though it will + allow the application to receive OnTerminated callbacks when it explicitly calls Disconnect itself (if it so desires) + +- added the ability for the library to auto-terminate connections that are not receiving data. This feature is supported + via the UdpManager::Params::noDataTimeout setting. The timeout can also be overridden/controlled on a per-connection + basis via the UdpConnection::SetNoDataTimeout and UdpConnection::GetNoDataTimeout functions. Setting the timeout to + zero (the default) causes the connections to never auto-disconnect due to not receiving data. + +- added UdpManager::GetHandler and UdpConnection::GetHandler calls (added for completeness, not currently used by anybody) + +- some minor changes to the non-windows version so it compiled cleaner. + +- made it so keep-alive packets do not cause UdpConnection objects to be rescheduled in the priority queue. They really + don't need to reschedule things since the worst thing that can happen is it might get processing time sooner than it + might otherwise need to. But scheduling it for immediate processing time just makes that worse if you think about it. + + +----------------------------------------------------------------------------------------------------------------- +5/15/2002 +----------------------------------------------------------------------------------------------------------------- +- added the ability to have a Disconnect'ed connection continue processing until there is no more data outstanding + on any of the reliable channels. The UdpConnection::Disconnect function now takes a 'flushTimeout' value, which is + how long (in milliseconds) the connection will attempt to finish sending queued reliable data before giving up. If + the application specifies a flushTimeout of zero (the default), then the connection is immediately terminated as before. + + When the application specifies a timeout, the connection status is changed to cStatusDisconnectPending. From the + applications point of view the connection should be thought of as disconnected the moment the call is made. The + connection will never callback the application again to deliver data and it will not allow the application to Send + any more data on the connection. About all the application can with the connection is query its various status', or + force an immediate disconnect by calling Disconnect again giving it no flush timeout. + + It is generally thought that the application will call Disconnect with a timeout and then immediately Release the + connection and have nothing else to do with it again. This is how it is intended to work. The Release by the application + will not actually free the connection if there is still data pending to be sent. + + Internally how this works is when the application calls Disconnect with a timeout, the status of the connection + is changed to cStatusDisconnectPending. The connection object registers itself with the UdpManager telling it + to keep a reference to the connection until the connection status changes to cStatusDisconnected. The connection will + be given scheduled time by the UdpManager as before and when the connection has no more reliable data to send, or when + its 'flushTime' expires, the connection will change itself to a cStatusDisconnected state, at which point the UdpManager + will 'Release' its reference to the connection and the connection will actually be deleted. So, take the following + code for example: + + void ApplicationFunction() + { + myConnection->Send(cUdpChannelReliable, data, 100000); // send 100,000 byte packet + myConnection->Disconnect(30000); // disconnect us, but give it up to 30 seconds to get any pending reliable data sent + myConnection->Release(); // we are finished with this connection + myConnection = NULL; + } + + The above code will not block and will deliver the 100k packet if it can manage to do it in the 30 second timeframe. + Releasing the UdpManager while there is pending disconnects will result in everybody being disconnected immediately + and the data will not be delivered. + +- added a new function UdpConnection::TotalPendingBytes(), which returns the total number of reliable-bytes that + are outstanding. When this returns zero, you can be assured that all data on all reliable channels has been successfully + delivered. (note: this is same as adding up ChannelStatus::totalPendingBytes on all reliable channels) + +- added a GetStatistics function to the internally used hash-table object (in case anybody is using it externally for their + own purposes. The UdpLibrary itself does not make use of the statistics. + + +----------------------------------------------------------------------------------------------------------------- +5/9/2002 +----------------------------------------------------------------------------------------------------------------- +- seeded the CRC algorithm with the negotiated encryption key, such that the CRC values calculated for packets cannot + be easily calculated by off the shelf CRC algorithms (to prevent packet tampering). This gives us some level of + protection without the huge disadvantage of encryption which is 1) potentially slow, and 2) encrypting of a typical + data stream reduces how effective modems hardware compress. In Infantry, we found turning encryption on (where the + data did not change size) significantly lagged modem users. For this reason, encryption should likely only be used + in conjunction with compression. General byte-level compression itself is probably sufficient encryption for out needs. + +- added OnCrcReject handler function. This function is only available if a handler object is used (there is no callback + equivalent right now...if somebody wants one, let me know; but, I would encourage everybody to switch over to handler based + callbacks instead). Whenever a packet is rejected due to a CRC error, this function is called to report the incident to + the application. Corrupt packets are fairly rare as they must first get through the 16 bit checksum that the UDP packet + inherently puts on it. If it makes it through that, then our CRC check will probably catch it. The reason why an + application would want to know this, is because there is a very good chance that the corruption was caused by somebody + intentionally trying to tamper with the packet data, but not touching up the CRC on the end. The application may want + to take note of the IP address and if too many corrupt packets come from the same IP, consider banning the player for + cheating. + +- made manager check the protocol version for compatibility before creating an associated UdpConnection object. This prevents + the application from seeing connections that may later be rejected because of protocol version incompatibility. + +- tweaked the flow control algorithm a bit. The two main changes were 1) selective acks now adjust the congestion window + differently similar to the TCP Reno algorithm, and 2) The slow start threshhold was reset when the pipe went dead (before + we only reset the congestion window, but this resulted in a circumstance whereby the slow-start threshhold once low would + never be able to go back up. + +- modified the resend-timer to not take into account the amount of outstanding data in the window. It turns out that + this adjustments is redundant in that the slow-start flow control mechanisms will ramp things up to speed in a manner + such that the average ack time will grow longer as the window gets bigger as appropriate. + +- Added a new parameter to the reliable channel configuration. ReliableConfig::processOnSend if 'true' will configure that + particular reliable channel to do processing on sent packets at the time they are sent instead of waiting until the next + UdpManager::GiveTime call. Normally the application will want to leave this at its default value of 'false' because it + is more efficient on CPU usage, since it can avoid doing all the ReliableChannel::GiveTime processing on a per-sent packet + basis by delaying it until the next global GiveTime. The downside of delaying is that it is often the case that the next + GiveTime simply causes the reliable packet to get moved into the multi-buffer inside the UdpConnection object. Then another + GiveTime a bit later finally sends it. + + Setting this value to 'true' is similar to issuing a FlushChannels command after each reliable Send (with the + exception that rather than giving every channel and the UdpConnection object time, it only has to give the channel + doing the send time). + + This feature is most useful when you are on the client side and the overhead of processing the packet on-send + will not add up to much, compared to what the server-side has to do managing thousands of connections. Most clients + should probably set this to 'true', since timeliness of sent packets generally exceeds any need for code efficiency on + the client side in this regard. + + It's worth noting that the reliable channel object ends up scheduling time for the connection the next GiveTime, + regardless of how this value is set. + + If you have the multi-buffer set to 0 (no multibuffering) and you have this parameter set to 'true', then the actual + send of a physical packet will end up occuring during your reliable-send call (assuming there is room in the reliable + flow control window for the packet to go out at that time). As a reminder, I don't recommend setting the multibuffer + to zero, even on the client side, because it effectively circumvents the mechanism by which ACK packets get combined + and piggy-backed on other packets (ie. every ACK would end up taking its own physical packet). A better approach I feel + is to have a multibuffer and then do explicit FlushMultiBuffer calls after sends. In this manner, ACK's still have some + chance of piggy-backing as they will wait around until the next send call (or the multibuffer timeout). + +- fixed a tiny problem whereby packets that were rejected due to CRC mismatches were not causing the connection receiving + them to schedule itself for processing time as it probably should. In practice this didn't really effect anything. + +- added a (UdpConnection *) parameter to the UdpConnectionHandler callback functions. This was done such that an application + could setup one object as the handler for multiple connections and be able to distinguish between which connection the + callback was coming from. You will have to change your handler functions appropriately to make them compile, and odds + are you will be able to simply ignore the incoming UdpConnection parameter. + +- added UdpManager::GetPassThroughData and UdpManager::SetPassThroughData calls to allow application to change the pass through + data after construction (makes it more consistent with the way UdpConnection objects work as well) + +- made it negotiate the maxRawPacketSize between the client and server side during connection establishment. It will actually + end up using the smaller of the two values specified on the client or server side. In this manner, the server can be + configured to support 1k packets (for example) and some client could be configured to only support 512 byte packets and they + would still be able to talk to each other (they would end up doing all talking with 512 byte packets in this case). This + will prevent the client and server from accidentally getting configured differently and then refusing to talk to each other. + It will also allow clients who are behind networks that can only handle tiny packets to be configured to talk in the smaller + chunks to support that. You will have to update both ends of the connection with this code in order for them to talk, since + this change effected the initial negotiation process. + + +----------------------------------------------------------------------------------------------------------------- +4/12/2002 +----------------------------------------------------------------------------------------------------------------- +- Removed the Handler base class declarations from being nested and put them in their own header file such that we could avoid + having to expose the entire UdpLibrary header everywhere (along with all it's baggage). You can now only include UdpHandler.hpp + to get these declarations, which have now been named UdpConnectionHandler and UdpManagerHandler. + +- Removed the nesting of ConnectionStatistics and ManagerStatistics, they are now UdpConnectionStatistics and UdpManagerStatistics. + +- Optimized the UdpLibrary.hpp header file to avoid including stuff we really didn't need to. In particular, with a few trivial + changes, we managed to be able to avoid including any other header files (including winsock.h). The only file we could not + get rid of including is stddef.h, which has the offsetof() macro that we need in the hashtable.hpp template implementation. + +- merged latest Linux implementation into main distribution. Hopefully I didn't change anything in the Linux implementation in + doing so as I have not tested it. + + +----------------------------------------------------------------------------------------------------------------- +4/9/2002 +----------------------------------------------------------------------------------------------------------------- +- switched UdpManager over to an AddRef/Release scheme as well. This is to allow the application to release/delete the UdpManager + during a callback as well. Internally the UdpManager keeps a reference to itself when it calls back the application, such + that the actual delete doesn't occur until the UdpManager is safely off the stack. + +- added UdpManager::SetHandler function. Normally the handler for the UdpManager is set in the UdpManager::Params and passed to the + constructor; however, I wanted to let the application change or get rid of the handler if needed. In particular, if the application + has set the handler to a particular object, and that object gets deleted before the manager, then that object should clear itself + from being the handler for the manager by setting the handler to NULL (presumeably). In particular, it is probably a good idea + to get in the habit of having handler objects set and clear themselves on construction and destruction as appropriate, for both + UdpConnection and UdpManager handlers. For example, this is the best way to set things up (similar things should be done + for UdpManagers): + + class Player : public UdpConnection::Handler; + + Player::Player(UdpConnection *con) + { + mConnection = con; + mConnection->AddRef(); + mConnection->SetHandler(this); + } + + Player::~Player() + { + mConnection->SetHandler(NULL); + mConnection->Disconnect(); + mConnection->Release(); + } + + Clearing the Handler on destruction is probably not necessary, but on the odd chance that somebody else has a reference to + your object and it lives on, it would seem to be a safe practice. In particular, during callbacks the connection will actually keep + a reference to itself momentarily. The Disconnect call should keep it from every trying to call you back, but clearing the + handler is a good safety step to be sure. + +- fixed bugged array delete in PriorityQueue. + +- fixed another bug in the ICMP handling that prevented the application from deleting the connection during that callback. + +- changed ICMP processing such that it would not quit trying to poll packets when it got an ICMP error return. I am not sure if + Windows avoids sending ICMP error results via recvfrom if there are legit packets in the queue or not, but just in case, this + change will make it handle the situation slightly better. + + +----------------------------------------------------------------------------------------------------------------- +4/8/2002 +----------------------------------------------------------------------------------------------------------------- +- added ICMP port-unreachable support. This will allow the sending end of a connection to quickly determine that the other side + has terminated servicing its port for some reason. Again, props to Justin for this idea. I tested it and it appears to work. + +- changed UdpConnection objects to be AddRef/Release based. This effectively meant we could get rid of the UDPGIFT ownership + passing paradigm because ownership is now a function of having a reference to it. This means that when you are called back + and given a UdpConnection object from a ConnectRequest callback, you must AddRef it if you intend to keep a hold of it; otherwise + it will be destroyed by the UdpManager when the callback returns. If you have an existing application you must remember to put + the AddRef in the callback, otherwise your connection will be destroyed immediately after it is created. The compiler will give you + errors for the destructors which need to be changed as noted below. + + Similarly, the application should Disconnect() and Release() the UdpConnection object when it is done using it and wants + it to go away. This change was needed such that the application would be free to delete the connection object + during packet processing inside a callback. The UdpManager keeps a reference to the object during packet processing + and releases it when done. The problem was that the callback to the application to route the packet came from deep + inside the UdpConnection object which was being destoryed. Because the UdpConnection object may be breaking apart + a multi-packet at the time the application releases the UdpConnection object, it will continue to route the remaining parts of the + multi-packet, as it has no idea that the application no longer wants to receive them. The application may have released its reference + to the UdpConnection, but the UdpManager maintains a reference until the raw packet is finished processing. What this means is that + if the application wants to ensure that when it releases the UdpConnection object that it also prevents all future callbacks from that + connection, it must also call UdpConnection::Disconnect before calling Release(). The bottom line is the proper way for the application + to destroy a connection during packet processing is to call Disconnect() and Release(). A Disconnected UdpConnection object will + never callback the application to route a packet. + +- fixed UdpMisc::Clock such that if different threads were calling it at the same time and got time sliced away, it wouldn't accidentally + double-increment or miscalculate in some way the next stamp. + +- Justin Randall modified library to compile properly under Linux and his changes have been included into official distribution. + The sample client/server programs are still WIN32 specific. + + +----------------------------------------------------------------------------------------------------------------- +4/2/2002 +----------------------------------------------------------------------------------------------------------------- +- made API const safe for most things. + +- added support for using object-based handler objects instead of callback functions for notifications. See the header file + under Params::handler and UdpConnection::SetHandler for more details on how these work. The UdpLibrary.doc file has been updated + to talk briefly about them as well. The UdpServer sample application included in the distribution has been modified to use + handlers instead of callbacks by default as well. + +- changed hashtable template interface slightly. It now has a simpler FindFirst/FindNext interface, avoiding the need to expose + the linked-list implementation details. + + +----------------------------------------------------------------------------------------------------------------- +3/24/2002 +----------------------------------------------------------------------------------------------------------------- +- added a pooled memory manager for logical packets to the UdpManager object. This should virtually eliminate + allocations for the vast majority of usage if configured properly. Using the UdpManager::CreatePacket is now the recommended + method for allocating logical packets to send since it will use the pool automatically if possible. + +- implemented function UdpConnection::OutgoingBytesLastSecond (replaces the unimplemented BytesInLastSecond) + +- added new FlushChannels function which allows the application to force all channels (particularly reliable ones) to send + out any queued data that they have time for immediately, instead of waiting for the next time they get processing time. + +- added new totalBytesPending statistic to the ChannelStatus, so you could tell for certainty that everything had made it + +- tweaked resend-time calculation...I will probably toy with this for a while til I am perfectly happy with it. + +- moved clock-sync packet sending ahead of reliable-queue packet processing to better the odds of getting a fast ping. + + +----------------------------------------------------------------------------------------------------------------- +3/22/2002 +----------------------------------------------------------------------------------------------------------------- +- optimized the flow-control window algorithm for our unique situation. Basically, I changed it so that the window-size does not + increase when the application is pacing the data rate itself such that the window is not getting filled. The problem was that + a slow stream from the application would give the flow-control mechanism the impression that packets were never being lost and it + would keep increasing it's window size forever. Then if the application suddenly dumped a huge chunk of data into the pipe, the large + window size would cause the client to get seriously over-flooded. This is one area where TCP falls flat on its face as well. + For more information on this flow-optimization, search for the mMaxxedOutCurrentWindow variable in the implementation and read the + associated comments. + +- fixed nasty bug where UdpReliableChannel::mLastTimeStampAcknowledged was not being initialized. This potentially caused the + first reliable packet sent to think that it had been ack-accelerated by a later packet and resent a second time. What made the bug + really nasty is that would resend this packet over and over again very very rapidly until it had been acknowledged. But because + the packet had been sent more than once, it would not use that packets last send stamp to reseed the accelleration stamp. This meant + that the next reliable packet sent experienced the same problem. The net effect was a ton of traffic and 80% packetloss to the destination. + I'm surprised this did not show up more in testing, but it should be fixed now. + +- fixed bug where it would crash if the timeout expired on EstablishConnection call (optional feature) +- fixed bug in trickle channels where they were sending faster than the specified trickle rate +- fixed bug in UdpConnection::LastReceive(...) (the version added last rev, accidentally compared the wrong stamp) +- added more ChannelStatus statistics (not real useful, but they have some gee-wiz value) + + +----------------------------------------------------------------------------------------------------------------- +3/12/2002 +----------------------------------------------------------------------------------------------------------------- +- enhanced UdpManager::GiveTime to allow it to be told to process only 1 physical packet out of the socket per call. See the docs + for GiveTime for an explanation of when this might be useful. + +- added support for reliable-channel specific fragment sizes (as opposed to using maxRawPacketSize for all channels). See the docs + for ReliableConfig::fragmentSize for details on when you might want to do this. + +- fixed bug whereby a client-side connection could possibly try and process a packet before it received the connection confirmation + This was a serious problem, but would only happen if the very first communication a client/server did originated from the server + side and the connection confirmation packet had been lost, and encryption was being used. + +- changed UdpMisc::CreateQuickLogicalPacket to allow it to take a NULL primary pointer (ie. just allocate space) + +- added a UdpConnection::LastReceive function that takes the current time as a parameter, this saves the connection having + to poll the clock to accomplish this, which can add up if you are looping through a ton of connections just to check this info + +- moved several functions into the header as inline implementations for performance + + +----------------------------------------------------------------------------------------------------------------- +3/8/2002 +----------------------------------------------------------------------------------------------------------------- +- Fixed bug in UdpConnection::GetLocalPort, it was incorrectly returning the port number in network-byte-order +- Added a UdpConnection::GetUdpManager call to get the UdpManager associated with a connection +- initialized UdpConnection's pass-through pointer to NULL +- got rid of UdpManager::Initialize and UdpManager::Terminate. These functions weren't needed after all since WSAStartup/WSACleanup do ref-counting. + You can now simply create the UdpManager immediately and it will take care of it. + + +----------------------------------------------------------------------------------------------------------------- +3/6/2002 +----------------------------------------------------------------------------------------------------------------- +- UdpManager::ManagerStatistics::elapsedTime changed to milliseconds from seconds. +- Added bytesSent tracking to UdpConnection::ConnectionStatistics +- changed all byte and packet count tracking to 64-bit values to prevent rollover. (cleaned up stat tracking code internally while at it) +- fixed an off-by-one bug in statistics where it was improperly counting the clock-reflect packet it had just sent when calculating packet-loss statistics +- added a UdpManager::GetLocalPort call to complement the GetLocalIp call (oversight, it should have been there all along). +- documentation continues to be fleshed out (still a lot of sections missing though, UdpManager::GiveTime section is worth reading) + + +----------------------------------------------------------------------------------------------------------------- +3/5/2002 +----------------------------------------------------------------------------------------------------------------- +- Added two new callback functions, one for when an EstablishConnection + call either succeeds or fails and one for when the connection is + is terminated by the other end. EstablishConnection function used for + establishing client-side connections now takes a timeout value for how + long it should attempt to establish the connection. Both are completely optional + if the application wishes to have callback notification of these events. + +- Fixed compiler incompatibility with MSVC 7.0 (template friend function related) +- Fixed compiler level 4 warnings for unused parameters +- Modified the resend protocol to throttle back resend times when packetloss + occurs to prevent unnecessary spamming on truely link-dead connections. + + +----------------------------------------------------------------------------------------------------------------- +2/26/2002 +----------------------------------------------------------------------------------------------------------------- +- Initial release of library to EQ2 team (only) + Documentation not yet finished, see header file comments for full documentation. + + +----------------------------------------------------------------------------------------------------------------- +Distribution Files +----------------------------------------------------------- +UdpLibrary.cpp - main source file +UdpLibrary.hpp - main include file +UdpHandler.hpp - include needed to be a handler-only +hashtable.hpp - include file needed internally +priority.hpp - include file needed internally +udpstress.cpp - sample application (acts as both client and server) +UdpLibrary.doc - documentation file +UdpLibraryRelease.txt - this file diff --git a/stationapi/udplibrary/hashtable.hpp b/stationapi/udplibrary/hashtable.hpp new file mode 100755 index 0000000..8c7340b --- /dev/null +++ b/stationapi/udplibrary/hashtable.hpp @@ -0,0 +1,619 @@ +#ifndef HASHTABLE_HPP +#define HASHTABLE_HPP + +#include +#include + + // This is a simple hash table template. Any type of object can be stored in this hash table + // provided it can be copied. Resizing the table while in use is generally not recommended. + // putting the same object in the table twice is generally not recommended as it makes the find ambiguous + + + //////////////////////////////////////////////////////// + // helper functions for the below hash table classes + //////////////////////////////////////////////////////// + +inline bool IsPrime(int value) // works on positive numbers up 62,710,561 +{ + static int sPrimeTab[] = + { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, + 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, + 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, + 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, + 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, + 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, + 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, + 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, + 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, + 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, + 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, + 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, + 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, + 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, + 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, + 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, + 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, + 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, + 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, + 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, + 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, + 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, + 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, + 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, + 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, + 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, + 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, + 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, + 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, + 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, + 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, + 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, + 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, + 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, + 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, + 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, + 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, + 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, + 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, + 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, + 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, + 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, + 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, + 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, + 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, + 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, + 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, + 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, + 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, + 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, + 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, + 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, + 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, + 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, + 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, + 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, + 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, + 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, + 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, + 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, + 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, + 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, + 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, + 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, + 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, + 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, + 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, + 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, + 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, + 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, + 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, + 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, + 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, + 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, + 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, + 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, + 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, + 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, + 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, + 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, + 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, + 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, + 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, + 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, + 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, + 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, + 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, + 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919 + }; + + for (unsigned int j = 0; j < sizeof(sPrimeTab) / sizeof(int) && sPrimeTab[j] * sPrimeTab[j] < value; j++) + { + if (value % sPrimeTab[j] == 0) + return(false); + } + return(true); +} + +inline int NextPrime(int value) +{ + if (value % 2 == 0) + value++; + while (!IsPrime(value)) + value += 2; + return(value); +} + +inline int HashString(char *string) +{ + int h = 0; + while (*string != 0) + h = ((31 * h) + *string++) ^ (h >> 26); + return(h); +} + +inline int UpperHashString(char *string) +{ + int h = 0; + while (*string != 0) + { + h = ((31 * h) + (((*string >= 'a' && *string <= 'z') ? (*string - 0x20) : *string) ^ (h >> 26))); + string++; + } + return(h); +} + +struct HashTableStatistics +{ + int tableSize; + int usedSlots; + int totalEntries; +}; + + + ////////////////////////////////////////////////////////////////////////// + // HashTable + ////////////////////////////////////////////////////////////////////////// +template class HashTable +{ + public: + HashTable(int hashSize); + ~HashTable(); + + void Insert(T& obj, int hashValue); + bool Remove(T& obj, int hashValue); + void Reset(); // removes all entries from the table + + T *FindFirst(int hashValue) const; // returns NULL if not found + T *FindNext(T *prevResult) const; // returns NULL if not found + + T *WalkFirst() const; // returns NULL if not found + T *WalkNext(T *prevResult) const; // returns NULL if not found + + void Resize(int hashSize); // the actual hash size will be rounded up to the next larger prime number (very slow, not recommended) + void GetStatistics(HashTableStatistics *stats) const; + + protected: + struct HashEntry + { + T obj; + int hashValue; + HashEntry *nextEntry; + }; + + HashEntry **mTable; + int mTableSize; + int mEntryCount; + int mStatUsedSlots; +}; + + +template HashTable::HashTable(int hashSize) +{ + mTable = NULL; + mTableSize = 0; + mEntryCount = 0; + mStatUsedSlots = 0; + Resize(hashSize); +} + +template HashTable::~HashTable() +{ + Reset(); // deletes everything + delete[] mTable; +} + +template void HashTable::Insert(T& obj, int hashValue) +{ + HashEntry *entry = new HashEntry; + entry->obj = obj; + entry->hashValue = hashValue; + + int spot = ((unsigned)hashValue) % mTableSize; + if (mTable[spot] == NULL) + { + entry->nextEntry = NULL; + mTable[spot] = entry; + mStatUsedSlots++; + } + else + { + entry->nextEntry = mTable[spot]; + mTable[spot] = entry; + } + mEntryCount++; +} + +template bool HashTable::Remove(T& obj, int hashValue) +{ + int spot = ((unsigned)hashValue) % mTableSize; + HashEntry* next = mTable[spot]; + HashEntry** prev = &mTable[spot]; + while (next != NULL) + { + if (next->obj == obj && next->hashValue == hashValue) + { + *prev = next->nextEntry; + delete next; + mEntryCount--; + if (mTable[spot] == NULL) + mStatUsedSlots--; + return(true); + break; + + } + prev = &next->nextEntry; + next = next->nextEntry; + } + return(false); +} + +template void HashTable::Reset() +{ + for (int spot = 0; spot < mTableSize; spot++) + { + HashEntry *curr = mTable[spot]; + if (curr != NULL) + { + mStatUsedSlots--; + while (curr != NULL) + { + HashEntry *next = curr->nextEntry; + delete curr; + mEntryCount--; + curr = next; + } + mTable[spot] = NULL; + } + } +} + +template T *HashTable::FindFirst(int hashValue) const +{ + HashEntry *entry = mTable[((unsigned)hashValue) % mTableSize]; + while (entry != NULL) + { + if (entry->hashValue == hashValue) + return(&entry->obj); + entry = entry->nextEntry; + } + return(NULL); +} + +template T *HashTable::FindNext(T *prevResult) const +{ + HashEntry *entry = (HashEntry *)(((char *)prevResult) - offsetof(HashEntry, obj)); + int hashValue = entry->hashValue; + entry = entry->nextEntry; + while (entry != NULL) + { + if (entry->hashValue == hashValue) + return(&entry->obj); + entry = entry->nextEntry; + } + return(NULL); +} + +template T *HashTable::WalkFirst() const +{ + for (int bucket = 0; bucket < mTableSize; bucket++) + { + HashEntry *entry = mTable[bucket]; + if (entry != NULL) + return(&entry->obj); + } + return(NULL); +} + +template T *HashTable::WalkNext(T *prevResult) const +{ + HashEntry *entry = (HashEntry *)(((char *)prevResult) - offsetof(HashEntry, obj)); + int bucket = ((unsigned)entry->hashValue) % mTableSize; + + entry = entry->nextEntry; + if (entry != NULL) + return(&entry->obj); + + bucket++; // go onto next bucket + for (; bucket < mTableSize; bucket++) + { + HashEntry *entry = mTable[bucket]; + if (entry != NULL) + return(&entry->obj); + } + return(NULL); +} + +template void HashTable::Resize(int hashSize) +{ + HashEntry **oldTable = mTable; + int oldSize = mTableSize; + + // calculate next prime number greater than hashSize. + mTableSize = NextPrime(hashSize); + mTable = new HashEntry*[mTableSize]; + memset(mTable, 0, sizeof(HashEntry *) * mTableSize); + + mStatUsedSlots = 0; + + if (mEntryCount > 0) + { + for (int i = 0; i < oldSize; i++) + { + HashEntry* next = oldTable[i]; + while (next != NULL) + { + HashEntry* hold = next; + next = next->nextEntry; + + // insert hold into new table + int spot = ((unsigned)hold->hashValue) % mTableSize; + if (mTable[spot] == NULL) + { + hold->nextEntry = NULL; + mTable[spot] = hold; + mStatUsedSlots++; + } + else + { + hold->nextEntry = mTable[spot]; + mTable[spot] = hold; + } + } + } + } + + delete[] oldTable; +} + +template void HashTable::GetStatistics(HashTableStatistics *stats) const +{ + stats->totalEntries = mEntryCount; + stats->usedSlots = mStatUsedSlots; + stats->tableSize = mTableSize; +} + + + ////////////////////////////////////////////////////////////////////////////// + // ObjectHashTable + // + // similar to basic HashTable object, only this version requires the contents + // be pointers to objects derived from class HashTableMember. If you can use + // this version, it avoids allocations of HashEntry objects. + // making the cost of object inserts/deletes dirt-cheap + ////////////////////////////////////////////////////////////////////////////// +class HashTableMember +{ +#if defined(_MSC_VER) && (_MSC_VER >= 1300) // MSVC 7.0 is the first version to support friend templates + template friend class ObjectHashTable; + protected: +#else + public: +#endif + int mHashValue; + HashTableMember *mHashNextEntry; +}; + +template class ObjectHashTable +{ + public: + ObjectHashTable(int hashSize); + ~ObjectHashTable(); + + void Insert(T obj, int hashValue); + bool Remove(T obj, int hashValue); + void Reset(); // removes all entries from the table + + T FindFirst(int hashValue) const; // returns NULL if not found + T FindNext(T prevResult) const; // returns NULL if not found + + T WalkFirst() const; // returns NULL if not found + T WalkNext(T prevResult) const; // returns NULL if not found + + void Resize(int hashSize); // the actual hash size will be rounded up to the next larger prime number (very slow, not recommended) + void GetStatistics(HashTableStatistics *stats) const; + + protected: + T *mTable; + int mTableSize; + int mEntryCount; + int mStatUsedSlots; +}; + + +template ObjectHashTable::ObjectHashTable(int hashSize) +{ + mTable = NULL; + mTableSize = 0; + mEntryCount = 0; + mStatUsedSlots = 0; + Resize(hashSize); +} + +template ObjectHashTable::~ObjectHashTable() +{ + Reset(); + delete[] mTable; +} + +template void ObjectHashTable::Insert(T obj, int hashValue) +{ + obj->mHashValue = hashValue; + + int spot = ((unsigned)hashValue) % mTableSize; + if (mTable[spot] == NULL) + { + obj->mHashNextEntry = NULL; + mTable[spot] = obj; + mStatUsedSlots++; + } + else + { + obj->mHashNextEntry = mTable[spot]; + mTable[spot] = obj; + } + mEntryCount++; +} + +template bool ObjectHashTable::Remove(T obj, int hashValue) +{ + int spot = ((unsigned)hashValue) % mTableSize; + T next = mTable[spot]; + T *prev = &mTable[spot]; + while (next != NULL) + { + if (next == obj) + { + *prev = (T)next->mHashNextEntry; + next->mHashNextEntry = NULL; + mEntryCount--; + if (mTable[spot] == NULL) + mStatUsedSlots--; + return(true); + break; + + } + prev = (T *)&next->mHashNextEntry; + next = (T)next->mHashNextEntry; + } + return(false); +} + +template void ObjectHashTable::Reset() +{ + for (int spot = 0; spot < mTableSize; spot++) + { + T curr = mTable[spot]; + if (curr != NULL) + { + while (curr != NULL) + { + T next = (T)curr->mHashNextEntry; + curr->mHashNextEntry = NULL; + mEntryCount--; + curr = next; + } + mTable[spot] = NULL; + mStatUsedSlots--; + } + } +} + +template T ObjectHashTable::FindFirst(int hashValue) const +{ + T entry = mTable[((unsigned)hashValue) % mTableSize]; + while (entry != NULL) + { + if (entry->mHashValue == hashValue) + return(entry); + entry = (T)entry->mHashNextEntry; + } + return(NULL); +} + +template T ObjectHashTable::FindNext(T prevResult) const +{ + T entry = prevResult; + int hashValue = entry->mHashValue; + entry = (T)entry->mHashNextEntry; + while (entry != NULL) + { + if (entry->mHashValue == hashValue) + return(entry); + entry = (T)entry->mHashNextEntry; + } + return(NULL); +} + +template T ObjectHashTable::WalkFirst() const +{ + for (int bucket = 0; bucket < mTableSize; bucket++) + { + T entry = mTable[bucket]; + if (entry != NULL) + return(entry); + } + return(NULL); +} + +template T ObjectHashTable::WalkNext(T prevResult) const +{ + T entry = prevResult; + int bucket = ((unsigned)entry->mHashValue) % mTableSize; + + entry = (T)entry->mHashNextEntry; + if (entry != NULL) + return(entry); + + bucket++; // go onto next bucket + for (; bucket < mTableSize; bucket++) + { + entry = mTable[bucket]; + if (entry != NULL) + return(entry); + } + return(NULL); +} + +template void ObjectHashTable::Resize(int hashSize) +{ + T *oldTable = mTable; + int oldSize = mTableSize; + + // calculate next prime number greater than hashSize. + mTableSize = NextPrime(hashSize); + mTable = new T[mTableSize]; + memset(mTable, 0, sizeof(T) * mTableSize); + + mStatUsedSlots = 0; + + if (mEntryCount > 0) + { + for (int i = 0; i < oldSize; i++) + { + T next = oldTable[i]; + while (next != NULL) + { + T hold = next; + next = (T)next->mHashNextEntry; + + // insert hold into new table + int spot = ((unsigned)hold->mHashValue) % mTableSize; + if (mTable[spot] == NULL) + { + hold->mHashNextEntry = NULL; + mTable[spot] = hold; + mStatUsedSlots++; + } + else + { + hold->mHashNextEntry = mTable[spot]; + mTable[spot] = hold; + } + } + } + } + + delete[] oldTable; +} + +template void ObjectHashTable::GetStatistics(HashTableStatistics *stats) const +{ + stats->totalEntries = mEntryCount; + stats->usedSlots = mStatUsedSlots; + stats->tableSize = mTableSize; +} + +#endif + diff --git a/stationapi/udplibrary/priority.hpp b/stationapi/udplibrary/priority.hpp new file mode 100755 index 0000000..19f34ab --- /dev/null +++ b/stationapi/udplibrary/priority.hpp @@ -0,0 +1,225 @@ +#ifndef PRIORITY_HPP +#define PRIORITY_HPP + + +template class PriorityQueue; + + // classes that wish to be members of the priority queue below must derive themselves from this class + // in order to pull in the two member variables. Unlike normal priority queue classes which don't + // have this restriction, it is required in order to support Remove and reprioritize functionality + // in a timely manner (otherwise, in order to remove an entry that is not at the top, you would have + // to linearly scan the entire queue). This is accomplished by having the member itself contain + // a pointer to it's position in the queue (mPriorityQueuePosition). + // note: a restriction of this ability is that an object cannot participate in more than one priority + // queue at a time. +class PriorityQueueMember +{ + public: + PriorityQueueMember(); + +#if defined(_MSC_VER) && (_MSC_VER >= 1300) // MSVC 7.0 is the first version to support friend templates + template friend class PriorityQueue; + protected: +#else + public: +#endif + int mPriorityQueuePosition; // -1=not in queue +}; + + // this class provides a priority queue that is capable of reprioritizing/removing entries. The + // compiler will ensure that objects stored in this class are derived from PriorityQueueMember. + // We don't really need this to be a template class, since we could just treat everything as a + // PriorityQueueMember, but then the application would lose some type checking. We don't use references + // in the api as most priority queue templates do as we can't support non pointer types anyways. +template class PriorityQueue +{ + public: + PriorityQueue(int queueSize); + ~PriorityQueue(); + + T* Top(); // returns NULL if queue is empty + T* TopRemove(); // returns NULL if queue is empty + T* TopRemove(P priority); // removes item from queue if it has a lower priority value, otherwise return NULL + T* Add(T* entry, P priority); // reprioritizes if already in queue, returns entry always + T* Remove(T* entry); // returns entry always (even if it was not in the queue) + P *GetPriority(T* entry); // returns NULL if entry is not in the queue + int QueueUsed(); // returns how many entries are in the queue + protected: + struct QueueEntry + { + T* entry; + P priority; + }; + + QueueEntry* mQueue; + int mQueueSize; + int mQueueEnd; + + void Refloat(T* entry); +}; + + + + ////////////////////////////////////////////////////////////////////////// + // PriorityQueueMember implementation + ////////////////////////////////////////////////////////////////////////// +inline PriorityQueueMember::PriorityQueueMember() +{ + mPriorityQueuePosition = -1; +} + + + ////////////////////////////////////////////////////////////////////////// + // PriorityQueue implementation + ////////////////////////////////////////////////////////////////////////// +template PriorityQueue::PriorityQueue(int queueSize) +{ + mQueueEnd = 0; + mQueueSize = queueSize; + mQueue = new QueueEntry[mQueueSize]; + memset(mQueue, 0, mQueueSize); +} + +template PriorityQueue::~PriorityQueue() +{ + delete[] mQueue; +} + +template T* PriorityQueue::Top() +{ + if (mQueueEnd == 0) + return(NULL); + return(mQueue[0].entry); +} + +template T* PriorityQueue::TopRemove() +{ + if (mQueueEnd == 0) + return(NULL); + T* top = mQueue[0].entry; + Remove(top); + return(top); +} + +template T* PriorityQueue::TopRemove(P priority) +{ + if (mQueueEnd > 0 && mQueue[0].priority <= priority) + return(Remove(mQueue[0].entry)); + return(NULL); +} + +template P* PriorityQueue::GetPriority(T* entry) +{ + if (entry->mPriorityQueuePosition >= 0) + return(&mQueue[entry->mPriorityQueuePosition].priority); + return(NULL); +} + +template T* PriorityQueue::Add(T* entry, P priority) +{ + if (entry->mPriorityQueuePosition == -1) + { + // not in queue, so add it to the bottom + if (mQueueEnd >= mQueueSize) + return(NULL); + mQueue[mQueueEnd].entry = entry; + mQueue[mQueueEnd].priority = priority; + mQueue[mQueueEnd].entry->mPriorityQueuePosition = mQueueEnd; + mQueueEnd++; + } + else + { + // see if priority has actually changed, if not, just return, otherwise change priority and fall through to refloat it + if (mQueue[entry->mPriorityQueuePosition].priority == priority) + return(entry); + mQueue[entry->mPriorityQueuePosition].priority = priority; + } + + Refloat(entry); + return(entry); +} + +template T* PriorityQueue::Remove(T* entry) +{ + if (entry->mPriorityQueuePosition == -1) + return(entry); + + // move end entry into place of one being removed + mQueueEnd--; + int spot = entry->mPriorityQueuePosition; + if (spot != mQueueEnd) // don't remove last item in queue (bottom of tree), so no need to copy bottom one up and refloat it (we would be refloating our own removed entry) + { + mQueue[spot] = mQueue[mQueueEnd]; + mQueue[spot].entry->mPriorityQueuePosition = spot; + Refloat(mQueue[spot].entry); + } + entry->mPriorityQueuePosition = -1; + return(entry); +} + +template void PriorityQueue::Refloat(T* entry) +{ + // float upward + int spot = entry->mPriorityQueuePosition; + bool tryDown = true; + while (spot > 0 && mQueue[spot].priority < mQueue[(spot - 1) / 2].priority) + { + int newSpot = (spot - 1) / 2; + QueueEntry hold = mQueue[spot]; + mQueue[spot] = mQueue[newSpot]; + mQueue[newSpot] = hold; + mQueue[spot].entry->mPriorityQueuePosition = spot; + mQueue[newSpot].entry->mPriorityQueuePosition = newSpot; + spot = newSpot; + tryDown = false; + } + + if (tryDown) + { + // if we didn't manage to float up at all, then we need to try floating down + for (;;) + { + // pick smallest child + int downSpot1 = (spot * 2) + 1; + if (downSpot1 >= mQueueEnd) + break; + int downSpot2 = (spot * 2) + 2; + + if (downSpot2 >= mQueueEnd || mQueue[downSpot1].priority < mQueue[downSpot2].priority) + { + if (mQueue[downSpot1].priority < mQueue[spot].priority) + { + QueueEntry hold = mQueue[spot]; + mQueue[spot] = mQueue[downSpot1]; + mQueue[downSpot1] = hold; + mQueue[spot].entry->mPriorityQueuePosition = spot; + mQueue[downSpot1].entry->mPriorityQueuePosition = downSpot1; + spot = downSpot1; + } + else + break; + } + else + { + if (mQueue[downSpot2].priority < mQueue[spot].priority) + { + QueueEntry hold = mQueue[spot]; + mQueue[spot] = mQueue[downSpot2]; + mQueue[downSpot2] = hold; + mQueue[spot].entry->mPriorityQueuePosition = spot; + mQueue[downSpot2].entry->mPriorityQueuePosition = downSpot2; + spot = downSpot2; + } + else + break; + } + } + } +} + +template int PriorityQueue::QueueUsed() +{ + return(mQueueEnd); +} + +#endif diff --git a/stationapi/udplibrary/udpclient.cpp b/stationapi/udplibrary/udpclient.cpp new file mode 100755 index 0000000..9debac0 --- /dev/null +++ b/stationapi/udplibrary/udpclient.cpp @@ -0,0 +1,245 @@ +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif +#include +#include +#include +#include +#include + +#include + +#ifdef WIN32 +#ifdef _DEBUG + #include +#endif +#endif + +#include "UdpLibrary.hpp" + + +class MyConnectionHandler : public UdpConnectionHandler +{ + public: + virtual void OnRoutePacket(UdpConnection *con, const uchar *data, int dataLen); +}; + + +int main(int argc, char **argv) +{ +#ifdef _DEBUG + _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); +#endif + + printf("UdpLibrary Test Client, press SHIFT-F12 to terminate.\n"); + printf(" /IP:xxx.xxx.xxx.xxx\n"); + printf(" /PORT:xxx\n"); + + char connectIp[256]; + strcpy(connectIp, "127.0.0.1"); + int connectPort = 9950; + for (int i = 0; i < argc; i++) + { + if (memicmp(argv[i], "/IP:", 4) == 0) + strcpy(connectIp, argv[i] + 4); + if (memicmp(argv[i], "/PORT:", 6) == 0) + connectPort = atoi(argv[i] + 6); + } + + ////////////////////////////////////////////////// + // initialize everything + ////////////////////////////////////////////////// + srand(clock()); + UdpManager::Params params; + params.clockSyncDelay = 45000; + params.crcBytes = 2; + params.hashTableSize = 10; + params.incomingBufferSize = 32 * 1024; + params.keepAliveDelay = 8000; + params.portAliveDelay = 5000; + params.maxConnections = 3; + params.maxDataHoldSize = 400; + params.maxDataHoldTime = 60; + params.maxRawPacketSize = 496; + params.outgoingBufferSize = 32 * 1024; + params.packetHistoryMax = 1000; + params.port = 0; + params.pooledPacketMax = 50; + params.pooledPacketSize = 512; + +#if 0 + params.simulateIncomingByteRate = 0; + params.simulateIncomingLossPercent = 10; + params.simulateOutgoingByteRate = 0; + params.simulateOutgoingLossPercent = 10; + params.simulateDestinationOverloadLevel = 0; + params.simulateOutgoingOverloadLevel = 0; +#endif + + params.reliable[0].maxInstandingPackets = 500; + params.reliable[0].maxOutstandingBytes = 200000; + params.reliable[0].maxOutstandingPackets = 500; + params.reliable[0].outOfOrder = false; + params.reliable[0].trickleRate = 0; + params.reliable[0].trickleSize = 0; + params.reliable[0].processOnSend = true; + params.reliable[1].maxInstandingPackets = 20; + params.reliable[1].maxOutstandingBytes = 5000; + params.reliable[1].maxOutstandingPackets = 30; + params.reliable[1].outOfOrder = false; + params.reliable[1].trickleRate = 200; + params.reliable[1].trickleSize = 200; + params.reliable[2] = params.reliable[0]; + params.reliable[2].outOfOrder = true; + params.reliable[3] = params.reliable[1]; + params.reliable[3].outOfOrder = true; + UdpManager *myUdpManager = new UdpManager(¶ms); + + + // establish connection + MyConnectionHandler myConnectionHandler; + printf("Connecting to: %s,%d.", connectIp, connectPort); + UdpConnection *myConnection = myUdpManager->EstablishConnection(connectIp, connectPort); + myConnection->SetHandler(&myConnectionHandler); + assert(myConnection != NULL); + int count = 0; + while (myConnection->GetStatus() == UdpConnection::cStatusNegotiating) + { + myUdpManager->GiveTime(); + Sleep(50); + if ((count++ % 20) == 0) + printf("."); + } + printf("\n"); + + + + ////////////////////////////////////////////////// + // master loop (sending data) + ////////////////////////////////////////////////// + count = 0; + for (;;) + { + // check for shutdown keys + if (kbhit()) + { + int k = getch(); + if ((k == 0 || k == 0xe0) && (getch() == 0x88)) // hit shift-F12 to exit gracefully + break; + if (k == 32) + { + // hit space bar to see statistics + UdpConnectionStatistics stats; + myConnection->GetStats(&stats); + char hold[256]; + printf("%s,%d AVE=%d HIGH=%d LOW=%d MSTR=%d,%d CRC=%I64d ORD=%I64d %I64d<<%I64d %I64d>>%I64d\n", myConnection->GetDestinationIp().GetAddress(hold), myConnection->GetDestinationPort() + , stats.averagePingTime, stats.highPingTime, stats.lowPingTime, stats.masterPingTime, stats.masterPingAge, stats.crcRejectedPackets, stats.orderRejectedPackets, stats.syncOurReceived, stats.syncTheirSent + , stats.syncOurSent, stats.syncTheirReceived); + } + + if (k == 'd') + { + myConnection->Disconnect(5000); // disconnect when channel is clear + printf("Disconnecting... (giving connection time to flush)\n"); + } + } + + // see if server terminated out connection, if so, break out and exit app + if (myConnection->GetStatus() == UdpConnection::cStatusDisconnected) + { + printf("Connection broken.\n"); + break; + } + + +#if 1 + // randomly send a medium sized packet on a random channel + if (rand() % 200 == 0) + { + char buf[8000]; + for (int i = 1; i < sizeof(buf); i++) + { + buf[i] = (char)(i % 100); + } + + int pc = rand() % 5; + for (i = 0; i < pc; i++) + { + buf[0] = (char)(rand() % 50); + int len = (rand() % 1800 + 2); + printf("OUTLEN=%d \n", len); + myConnection->Send(cUdpChannelReliable1, buf, len); + } + } +#elif 0 + // send a very large packet one after the other (don't send next packet til the previous one has made it) + if (myConnection->TotalPendingBytes() == 0) + { + // send another packet + SimpleLogicalPacket *lp = new SimpleLogicalPacket(NULL, 30000000); + int dlen = lp->GetDataLen(); + char *ptr = (char *)lp->GetDataPtr(); + + ptr[0] = (char)(rand() % 50); + for (int i = 1; i < dlen; i++) + { + ptr[i] = (char)(i % 100); + } + + printf("OUT LEN=%d \n", dlen); + myConnection->Send(cUdpChannelReliable1, lp); + lp->Release(); + } +#else + // prepare a random sized group of medium packets and then send the group, don't prepare next group until previous one has made it + UdpConnection::ChannelStatus cs; + myConnection->GetChannelStatus(cUdpChannelReliable1, &cs); + if (cs.queuedBytes == 0) + { + int count = 0; + GroupLogicalPacket *glp = new GroupLogicalPacket(); + int di = rand() % 20 + 1; + for (int i = 0; i < di; i++) + { + GroupLogicalPacket *sub = new GroupLogicalPacket(); + for (int j = 0; j < di; j++) + { + char buf[8000]; + *(ushort *)buf = count; + int len = (rand() % 1000) + 10; + *(ushort *)(buf + len - 2) = count; + sub->AddPacket(buf, len); + count++; + } + glp->AddPacket(sub); + sub->Release(); + } + myConnection->Send(cUdpChannelReliable1, glp); + glp->Release(); + printf("Group of %d packets sent\n", count); + } +#endif + + myUdpManager->GiveTime(); + Sleep(10); + } + + ////////////////////////////////////////////////// + // terminate everything + ////////////////////////////////////////////////// + myConnection->Release(); + myUdpManager->Release(); + return(0); +} + + +void MyConnectionHandler::OnRoutePacket(UdpConnection * /*con*/, const uchar *data, int dataLen) +{ + printf("IN=%d/%d LEN=%d \n", *(ushort *)data, *(ushort *)(data + dataLen - 2), dataLen); +} + diff --git a/stationapi/udplibrary/udplibrary.dsp b/stationapi/udplibrary/udplibrary.dsp new file mode 100755 index 0000000..8aecdb9 --- /dev/null +++ b/stationapi/udplibrary/udplibrary.dsp @@ -0,0 +1,152 @@ +# Microsoft Developer Studio Project File - Name="udplibrary" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=udplibrary - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "udplibrary.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "udplibrary.mak" CFG="udplibrary - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "udplibrary - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "udplibrary - Win32 Optimized" (based on "Win32 (x86) Static Library") +!MESSAGE "udplibrary - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "udplibrary" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "udplibrary - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\..\..\compile\win32\udplibrary\Release" +# PROP Intermediate_Dir "..\..\..\..\compile\win32\udplibrary\Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /WX /GX /Zi /O2 /I "..\stlport453\stlport" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "udplibrary - Win32 Optimized" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Optimized" +# PROP BASE Intermediate_Dir "Optimized" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\..\compile\win32\udplibrary\Optimized" +# PROP Intermediate_Dir "..\..\..\..\compile\win32\udplibrary\Optimized" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /WX /Gm /GX /Zi /Od /I "..\stlport453\stlport" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "udplibrary - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\..\..\compile\win32\udplibrary\Debug" +# PROP Intermediate_Dir "..\..\..\..\compile\win32\udplibrary\Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /WX /Gm /GX /Zi /Od /I "..\stlport453\stlport" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "udplibrary - Win32 Release" +# Name "udplibrary - Win32 Optimized" +# Name "udplibrary - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\UdpLibrary.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\hashtable.hpp +# End Source File +# Begin Source File + +SOURCE=.\PointerDeque.hpp +# End Source File +# Begin Source File + +SOURCE=.\priority.hpp +# End Source File +# Begin Source File + +SOURCE=.\UdpHandler.h +# End Source File +# Begin Source File + +SOURCE=.\UdpHandler.hpp +# End Source File +# Begin Source File + +SOURCE=.\UdpLibrary.h +# End Source File +# Begin Source File + +SOURCE=.\UdpLibrary.hpp +# End Source File +# End Group +# End Target +# End Project diff --git a/stationapi/udplibrary/udpserver.cpp b/stationapi/udplibrary/udpserver.cpp new file mode 100755 index 0000000..c523a05 --- /dev/null +++ b/stationapi/udplibrary/udpserver.cpp @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef _DEBUG + #include +#endif + +#include "UdpLibrary.hpp" + +class Player : public UdpConnectionHandler +{ + public: + Player(UdpConnection *con); + virtual ~Player(); + virtual void OnRoutePacket(UdpConnection *con, const uchar *data, int dataLen); + public: + UdpConnection *mConnection; +}; + +class PlayerManager : public UdpManagerHandler +{ + public: + PlayerManager(); + ~PlayerManager(); + + void AddPlayer(Player *player); + void GiveTime(); + void DumpStats(); + void SendPacketToAll(UdpChannel channel, void *data, int dataLen); + + virtual void OnConnectRequest(UdpConnection *con); + + protected: + Player *mPlayers[5000]; + int mPlayersCount; + UdpMisc::ClockStamp mStartPacketTime; + int mLastIncomingLargeSoFar; +}; + + + + +void MyCallbackRoutePacket(void *mainPassThrough, UdpConnection *con, const uchar *data, int dataLen); +void MyCallbackConnectRequest(void *mainPassThrough, UdpConnection *con); + +UdpManager *myUdpManager; + +int main(int argc, char **argv) +{ + printf("UdpLibrary Test Server, press SHIFT-F12 to terminate.\n"); + printf(" /PORT:xxx\n"); + + int listenPort = 9950; + for (int i = 0; i < argc; i++) + { + if (memicmp(argv[i], "/PORT:", 6) == 0) + listenPort = atoi(argv[i] + 6); + } + +#ifdef _DEBUG + _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); +#endif + + ////////////////////////////////////////////////// + // initialize everything + ////////////////////////////////////////////////// + srand(clock()); + + PlayerManager *myPlayerManager = new PlayerManager(); + + UdpManager::Params params; + params.handler = myPlayerManager; + params.crcBytes = 2; + params.encryptMethod[0] = UdpManager::cEncryptMethodXorBuffer; + params.hashTableSize = 1000; + params.incomingBufferSize = 256 * 1024; + params.outgoingBufferSize = 256 * 1024; + params.clockSyncDelay = 0; + params.keepAliveDelay = 0; + params.maxConnections = 4000; + params.maxDataHoldSize = 400; + params.maxDataHoldTime = 60; + params.maxRawPacketSize = 256; + params.packetHistoryMax = 1000; + params.port = listenPort; + params.pooledPacketMax = 5000; + params.pooledPacketSize = 512; + +#if 0 + params.simulateIncomingByteRate = 2000; + params.simulateIncomingLossPercent = 0; + params.simulateOutgoingByteRate = 2000; + params.simulateOutgoingLossPercent = 0; + params.simulateDestinationOverloadLevel = 8000; + params.simulateOutgoingOverloadLevel = 8000; +#endif + params.reliable[0].maxInstandingPackets = 500; + params.reliable[0].maxOutstandingBytes = 200000; + params.reliable[0].maxOutstandingPackets = 500; + params.reliable[0].outOfOrder = false; + params.reliable[0].trickleRate = 0; + params.reliable[0].trickleSize = 0; + params.reliable[1].maxInstandingPackets = 30; + params.reliable[1].maxOutstandingBytes = 200000; + params.reliable[1].maxOutstandingPackets = 30; + params.reliable[1].outOfOrder = false; + params.reliable[1].trickleRate = 200; + params.reliable[1].trickleSize = 200; + params.reliable[2] = params.reliable[0]; + params.reliable[2].outOfOrder = true; + params.reliable[3] = params.reliable[1]; + params.reliable[3].outOfOrder = true; + myUdpManager = new UdpManager(¶ms); + + ////////////////////////////////////////////////// + // master loop (processing incoming data/connections) + ////////////////////////////////////////////////// + printf("Listening on port %d\n", listenPort); + int count = 0; + for (;;) + { + // check for shutdown keys + if (kbhit()) + { + int k = getch(); + if ((k == 0 || k == 0xe0) && (getch() == 0x88)) + break; + if (k == 32) + myPlayerManager->DumpStats(); + } + + if ((rand() % 300) == 0) + { + count++; + char buf[2000]; + *(ushort *)buf = (ushort)count; + int len = (rand() % 1000) + 10; + *(ushort *)(buf + len - 2) = (ushort)count; + myPlayerManager->SendPacketToAll(cUdpChannelReliable1, buf, len); + printf("OUT=%d/%d LEN=%d \n", *(ushort *)buf, *(ushort *)(buf + len - 2), len); + } + + myUdpManager->GiveTime(); + myPlayerManager->GiveTime(); + Sleep(10); + } + + ////////////////////////////////////////////////// + // terminate everything + ////////////////////////////////////////////////// + delete myPlayerManager; + myUdpManager->Release(); + return(0); +} + + + //////////////////////////////////////// + // Player implementation + //////////////////////////////////////// +Player::Player(UdpConnection *con) +{ + mConnection = con; + mConnection->AddRef(); + mConnection->SetHandler(this); + + char hold[256]; + printf("CONNECTION %s,%d \n", mConnection->GetDestinationIp().GetAddress(hold), mConnection->GetDestinationPort()); +} + +Player::~Player() +{ + char hold[256]; + printf("TERMINATE %s,%d \n", mConnection->GetDestinationIp().GetAddress(hold), mConnection->GetDestinationPort()); + mConnection->SetHandler(NULL); + mConnection->Disconnect(); + mConnection->Release(); +} + +void Player::OnRoutePacket(UdpConnection * /*con*/, const uchar *data, int dataLen) +{ + for (int i = 1; i < dataLen; i++) + { + bool match = (data[i] == (i % 100)); + assert(match); + } + + char hold[256]; + printf("FROM=%s,%d LEN=%d \n", mConnection->GetDestinationIp().GetAddress(hold), mConnection->GetDestinationPort(), dataLen); + +#if 0 + // reflect exact packet back to client + mConnection->Send(cUdpChannelReliable1, data, dataLen); +#endif +} + + + + + //////////////////////////////////////// + // PlayerManager implementation + //////////////////////////////////////// +PlayerManager::PlayerManager() +{ + mPlayersCount = 0; + mStartPacketTime = 0; + mLastIncomingLargeSoFar = 90000000; +} + +PlayerManager::~PlayerManager() +{ + for (int i = mPlayersCount - 1; i >= 0; i--) + delete mPlayers[i]; +} + +void PlayerManager::OnConnectRequest(UdpConnection *con) +{ + AddPlayer(new Player(con)); +} + +void PlayerManager::AddPlayer(Player *player) +{ + mPlayers[mPlayersCount++] = player; +} + +void PlayerManager::GiveTime() +{ + // see if the player object is no longer connected + for (int i = 0; i < mPlayersCount; i++) + { + if (mPlayers[i]->mConnection->GetStatus() == UdpConnection::cStatusDisconnected || mPlayers[i]->mConnection->LastReceive() > 2000000) + { + Player *p = mPlayers[i]; + mPlayersCount--; + memmove(&mPlayers[i], &mPlayers[i + 1], (mPlayersCount - i) * sizeof(Player *)); + i--; + delete p; + } + } + + if (mPlayersCount > 0) + { + UdpConnection::ChannelStatus cs; + mPlayers[0]->mConnection->GetChannelStatus(cUdpChannelReliable1, &cs); + UdpManagerStatistics ms; + myUdpManager->GetStats(&ms); + if (cs.incomingLargeSoFar < mLastIncomingLargeSoFar) + mStartPacketTime = UdpMisc::Clock(); + + mLastIncomingLargeSoFar = cs.incomingLargeSoFar; + int e = UdpMisc::ClockElapsed(mStartPacketTime); + if (e > 0) + fprintf(stderr, "%d of %d (%d bps)(%d) \r", cs.incomingLargeSoFar, cs.incomingLargeTotal, (int)(((__int64)cs.incomingLargeSoFar * 1000) / (__int64)e), ms.packetsReceived); + } +} + +void PlayerManager::SendPacketToAll(UdpChannel channel, void *data, int dataLen) +{ + LogicalPacket *lp = myUdpManager->CreatePacket(data, dataLen); + for (int i = 0; i < mPlayersCount; i++) + { + mPlayers[i]->mConnection->Send(channel, lp); + } + lp->Release(); +} + +void PlayerManager::DumpStats() +{ + for (int i = 0; i < mPlayersCount; i++) + { + UdpConnectionStatistics stats; + mPlayers[i]->mConnection->GetStats(&stats); + + char hold[256]; + printf("%s,%d AVE=%d HIGH=%d LOW=%d MSTR=%d,%d CRC=%I64d ORD=%I64d %I64d<<%I64d %I64d>>%I64d\n", mPlayers[i]->mConnection->GetDestinationIp().GetAddress(hold), mPlayers[i]->mConnection->GetDestinationPort() + , stats.averagePingTime, stats.highPingTime, stats.lowPingTime, stats.masterPingTime, stats.masterPingAge, stats.crcRejectedPackets, stats.orderRejectedPackets, stats.syncOurReceived, stats.syncTheirSent + , stats.syncOurSent, stats.syncTheirReceived); + } +} +