From 4ba1363775832788646f1e141b2be432771aedab Mon Sep 17 00:00:00 2001 From: Anonymous Date: Fri, 17 Jan 2014 05:18:10 -0700 Subject: [PATCH] Added VChatAPI library --- .../application/ChatServer/src/CMakeLists.txt | 105 +- .../3rd/library/soePlatform/CMakeLists.txt | 1 + .../ChatAPI/projects/ChatMono/CMakeLists.txt | 4 +- .../soePlatform/VChatAPI/CMakeLists.txt | 2 + .../VChatAPI/projects/CMakeLists.txt | 2 + .../VChatAPI/projects/VChat/CMakeLists.txt | 2 + .../projects/VChat/VChatAPI/CMakeLists.txt | 100 + .../projects/VChat/VChatAPI/VChatAPI.cpp | 456 ++ .../projects/VChat/VChatAPI/VChatAPI.h | 532 ++ .../projects/VChat/VChatAPI/VChatAPICore.cpp | 332 ++ .../projects/VChat/VChatAPI/VChatAPICore.h | 60 + .../projects/VChat/VChatAPI/VChatAPITypes.h | 215 + .../projects/VChat/VChatAPI/clientSerialize.h | 114 + .../projects/VChat/VChatAPI/common.cpp | 334 ++ .../VChatAPI/projects/VChat/VChatAPI/common.h | 114 + .../projects/VChat/VChatAPI/messages.cpp | 277 ++ .../projects/VChat/VChatAPI/messages.h | 350 ++ .../projects/VChat/VChatAPI/strings.h | 149 + .../VChat/VChatUnitTest/VChatClient.cpp | 514 ++ .../VChat/VChatUnitTest/VChatClient.h | 235 + .../projects/VChat/VChatUnitTest/main.cpp | 352 ++ .../VChatAPI/utils2.0/utils/Api/api.cpp | 1045 ++++ .../VChatAPI/utils2.0/utils/Api/api.h | 266 + .../VChatAPI/utils2.0/utils/Api/apiMacros.h | 145 + .../utils2.0/utils/Api/apiMessages.cpp | 553 +++ .../VChatAPI/utils2.0/utils/Api/apiMessages.h | 454 ++ .../VChatAPI/utils2.0/utils/Api/apiPinned.cpp | 443 ++ .../VChatAPI/utils2.0/utils/Api/apiPinned.h | 121 + .../utils2.0/utils/Api/apiTypeNameValuePair.h | 65 + .../utils/Api/apiTypePlatUnicodeString.h | 22 + .../VChatAPI/utils2.0/utils/Api/protocol.h | 23 + .../VChatAPI/utils2.0/utils/Base/Base32.cpp | 216 + .../VChatAPI/utils2.0/utils/Base/Base32.h | 44 + .../VChatAPI/utils2.0/utils/Base/Base64.cpp | 499 ++ .../VChatAPI/utils2.0/utils/Base/Base64.h | 40 + .../VChatAPI/utils2.0/utils/Base/MD5.cpp | 317 ++ .../VChatAPI/utils2.0/utils/Base/MD5.h | 66 + .../utils2.0/utils/Base/basicConfig.cpp | 128 + .../utils2.0/utils/Base/basicConfig.h | 43 + .../VChatAPI/utils2.0/utils/Base/cmdLine.cpp | 285 ++ .../VChatAPI/utils2.0/utils/Base/cmdLine.h | 245 + .../VChatAPI/utils2.0/utils/Base/date.cpp | 178 + .../VChatAPI/utils2.0/utils/Base/date.h | 94 + .../VChatAPI/utils2.0/utils/Base/dateUtils.h | 227 + .../utils2.0/utils/Base/expirationQueue.h | 103 + .../utils/Base/genericRateLimitingMechanism.h | 533 ++ .../VChatAPI/utils2.0/utils/Base/hash.h | 239 + .../utils2.0/utils/Base/hashtable.hpp | 623 +++ .../VChatAPI/utils2.0/utils/Base/log.cpp | 790 +++ .../VChatAPI/utils2.0/utils/Base/log.h | 149 + .../utils2.0/utils/Base/monitorAPI.cpp | 608 +++ .../VChatAPI/utils2.0/utils/Base/monitorAPI.h | 105 + .../utils2.0/utils/Base/monitorData.cpp | 598 +++ .../utils2.0/utils/Base/monitorData.h | 252 + .../VChatAPI/utils2.0/utils/Base/pid.cpp | 57 + .../VChatAPI/utils2.0/utils/Base/pid.h | 23 + .../VChatAPI/utils2.0/utils/Base/priority.hpp | 225 + .../VChatAPI/utils2.0/utils/Base/profile.cpp | 454 ++ .../VChatAPI/utils2.0/utils/Base/profile.h | 82 + .../VChatAPI/utils2.0/utils/Base/rateLimit.h | 22 + .../VChatAPI/utils2.0/utils/Base/refptr.h | 106 + .../VChatAPI/utils2.0/utils/Base/serialize.h | 16 + .../utils2.0/utils/Base/serializeClasses.h | 934 ++++ .../utils/Base/serializeStringVector.cpp | 14 + .../utils/Base/serializeStringVector.h | 245 + .../utils2.0/utils/Base/serializeTemplates.h | 784 +++ .../utils2.0/utils/Base/stringutils.cpp | 174 + .../utils2.0/utils/Base/stringutils.h | 64 + .../utils2.0/utils/Base/substringSearchTree.h | 795 +++ .../utils2.0/utils/Base/systemLog.cpp | 174 + .../VChatAPI/utils2.0/utils/Base/systemLog.h | 37 + .../VChatAPI/utils2.0/utils/Base/thread.cpp | 413 ++ .../VChatAPI/utils2.0/utils/Base/thread.h | 148 + .../VChatAPI/utils2.0/utils/Base/timer.h | 93 + .../utils/Base/trackMessageFailures.cpp | 114 + .../utils/Base/trackMessageFailures.h | 29 + .../VChatAPI/utils2.0/utils/Base/types.h | 79 + .../VChatAPI/utils2.0/utils/Base/utf8.cpp | 312 ++ .../VChatAPI/utils2.0/utils/Base/utf8.h | 33 + .../utils2.0/utils/TcpLibrary/Clock.cpp | 119 + .../utils2.0/utils/TcpLibrary/Clock.h | 71 + .../utils2.0/utils/TcpLibrary/IPAddress.cpp | 33 + .../utils2.0/utils/TcpLibrary/IPAddress.h | 51 + .../utils/TcpLibrary/TcpBlockAllocator.cpp | 94 + .../utils/TcpLibrary/TcpBlockAllocator.h | 44 + .../utils/TcpLibrary/TcpConnection.cpp | 792 +++ .../utils2.0/utils/TcpLibrary/TcpConnection.h | 160 + .../utils2.0/utils/TcpLibrary/TcpHandlers.h | 52 + .../utils2.0/utils/TcpLibrary/TcpListener.cpp | 435 ++ .../utils2.0/utils/TcpLibrary/TcpListener.h | 160 + .../utils2.0/utils/TcpLibrary/TcpManager.cpp | 761 +++ .../utils2.0/utils/TcpLibrary/TcpManager.h | 300 ++ .../utils2.0/utils/UdpLibrary/UdpHandler.hpp | 82 + .../utils2.0/utils/UdpLibrary/UdpLibrary.cpp | 4380 +++++++++++++++++ .../utils2.0/utils/UdpLibrary/UdpLibrary.hpp | 2104 ++++++++ .../utils2.0/utils/UdpLibrary/UdpListener.cpp | 394 ++ .../utils2.0/utils/UdpLibrary/UdpListener.h | 145 + 97 files changed, 29311 insertions(+), 62 deletions(-) create mode 100644 external/3rd/library/soePlatform/VChatAPI/CMakeLists.txt create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/CMakeLists.txt create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/CMakeLists.txt create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/CMakeLists.txt create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPI.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPI.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPICore.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPICore.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPITypes.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/clientSerialize.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/common.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/common.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/messages.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/messages.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/strings.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/VChatClient.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/VChatClient.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/main.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/api.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/api.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMacros.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMessages.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMessages.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiPinned.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiPinned.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiTypeNameValuePair.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiTypePlatUnicodeString.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/protocol.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base32.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base32.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base64.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base64.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/MD5.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/MD5.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/basicConfig.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/basicConfig.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/cmdLine.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/cmdLine.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/date.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/date.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/dateUtils.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/expirationQueue.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/genericRateLimitingMechanism.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/hash.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/hashtable.hpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/log.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/log.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorAPI.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorAPI.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorData.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorData.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/pid.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/pid.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/priority.hpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/profile.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/profile.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/rateLimit.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/refptr.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serialize.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeClasses.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeStringVector.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeStringVector.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeTemplates.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/stringutils.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/stringutils.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/substringSearchTree.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/systemLog.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/systemLog.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/thread.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/thread.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/timer.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/trackMessageFailures.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/trackMessageFailures.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/types.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/utf8.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/utf8.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/Clock.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/Clock.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/IPAddress.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/IPAddress.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpBlockAllocator.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpBlockAllocator.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpConnection.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpConnection.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpHandlers.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpListener.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpListener.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpManager.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpManager.h create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpHandler.hpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpLibrary.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpLibrary.hpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpListener.cpp create mode 100644 external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpListener.h diff --git a/engine/server/application/ChatServer/src/CMakeLists.txt b/engine/server/application/ChatServer/src/CMakeLists.txt index d02b7edb..174554a6 100644 --- a/engine/server/application/ChatServer/src/CMakeLists.txt +++ b/engine/server/application/ChatServer/src/CMakeLists.txt @@ -40,36 +40,31 @@ endif() include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/shared - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedCommandParser/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedCompression/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDatabaseInterface/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedCompression/include/public ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFile/include/public ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedGame/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedLog/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMath/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedLog/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMath/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMathArchive/include/public ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMessageDispatch/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMessageDispatch/include/public ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetwork/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetworkMessages/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedRandom/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedSynchronization/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedThread/include/public - #${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedUtility/include/public - #${SWG_ENGINE_SOURCE_DIR}/server/library/serverKeyShare/include/public - #${SWG_ENGINE_SOURCE_DIR}/server/library/serverNetworkMessages/include/public - #${SWG_ENGINE_SOURCE_DIR}/server/library/serverUtility/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetworkMessages/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedThread/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverMetrics/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverNetworkMessages/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverUtility/include/public ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/archive/include - #${SWG_EXTERNALS_SOURCE_DIR}/ours/library/localization/include - #${SWG_EXTERNALS_SOURCE_DIR}/ours/library/localizationArchive/include/public - #${SWG_EXTERNALS_SOURCE_DIR}/ours/library/singleton/include - #${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicode/include - #${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicodeArchive/include/public - #${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/platform/projects - #${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/platform/utils - #${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/udplibrary + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/localization/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/localizationArchive/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/singleton/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicode/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicodeArchive/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/soePlatform/ChatAPI/projects + ${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/soePlatform/ChatAPI/utils + ${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/soePlatform/VChatAPI/projects/VChat + ${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/soePlatform/VChatAPI/utils2.0/utils ) link_directories(${STLPORT_LIBDIR}) @@ -80,41 +75,33 @@ add_executable(ChatServer ) target_link_libraries(ChatServer -# sharedCommandParser -# sharedCompression -# sharedDatabaseInterface -# sharedDatabaseInterface_oci -# sharedDebug -# sharedFile -# sharedFoundation -# sharedGame -# sharedLog -# sharedMath -# sharedMemoryManager -# sharedMessageDispatch -# sharedNetwork -# sharedNetworkMessages -# sharedRandom -# sharedSynchronization -# sharedThread -# sharedUtility -# serverKeyShare -# serverNetworkMessages -# serverUtility -# archive -# crypto -# fileInterface -# localization -# localizationArchive -# unicode -# unicodeArchive -# Base -# CommonAPI -# LoginAPI -# MonAPI2 -# udplibrary -# ${ZLIB_LIBRARY} -# ${ORACLE_LIBRARY} + sharedCompression + sharedDebug + sharedFile + sharedFoundation + sharedLog + sharedMath + sharedMemoryManager + sharedMessageDispatch + sharedNetwork + sharedNetworkMessages + sharedRandom + sharedSynchronization + sharedThread + sharedUtility + serverMetrics + serverNetworkMessages + serverUtility + archive + fileInterface + localization + localizationArchive + unicode + unicodeArchive + udplibrary + ChatMono + VChatAPI + ${ZLIB_LIBRARY} ) if(WIN32) diff --git a/external/3rd/library/soePlatform/CMakeLists.txt b/external/3rd/library/soePlatform/CMakeLists.txt index 05bc1665..b6a0ed46 100644 --- a/external/3rd/library/soePlatform/CMakeLists.txt +++ b/external/3rd/library/soePlatform/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(ChatAPI) +add_subdirectory(VChatAPI) diff --git a/external/3rd/library/soePlatform/ChatAPI/projects/ChatMono/CMakeLists.txt b/external/3rd/library/soePlatform/ChatAPI/projects/ChatMono/CMakeLists.txt index fd200c5a..f852930d 100644 --- a/external/3rd/library/soePlatform/ChatAPI/projects/ChatMono/CMakeLists.txt +++ b/external/3rd/library/soePlatform/ChatAPI/projects/ChatMono/CMakeLists.txt @@ -151,9 +151,7 @@ else() include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../utils/Base/linux) endif() -include_directories() - add_library(ChatMono ${SHARED_SOURCES} ${PLATFORM_SOURCES} -) \ No newline at end of file +) diff --git a/external/3rd/library/soePlatform/VChatAPI/CMakeLists.txt b/external/3rd/library/soePlatform/VChatAPI/CMakeLists.txt new file mode 100644 index 00000000..8acab2ed --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(projects) diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/CMakeLists.txt b/external/3rd/library/soePlatform/VChatAPI/projects/CMakeLists.txt new file mode 100644 index 00000000..1f285f2c --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(VChat) diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/CMakeLists.txt b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/CMakeLists.txt new file mode 100644 index 00000000..1dc6a371 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(VChatAPI) diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/CMakeLists.txt b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/CMakeLists.txt new file mode 100644 index 00000000..af98ca82 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/CMakeLists.txt @@ -0,0 +1,100 @@ + +add_definitions(-DAPI_NAMESPACE=VChatSystem) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../../../utils2.0/utils + ${CMAKE_CURRENT_SOURCE_DIR}/../../../utils2.0/utils/Api +) + +add_library(VChatAPI + clientSerialize.h + common.cpp + common.h + messages.cpp + messages.h + strings.h + VChatAPI.cpp + VChatAPI.h + VChatAPICore.cpp + VChatAPICore.h + VChatAPITypes.h + + ../../../utils2.0/utils/Api/api.cpp + ../../../utils2.0/utils/Api/api.h + ../../../utils2.0/utils/Api/apiMacros.h + ../../../utils2.0/utils/Api/apiMessages.cpp + ../../../utils2.0/utils/Api/apiMessages.h + ../../../utils2.0/utils/Api/apiPinned.cpp + ../../../utils2.0/utils/Api/apiPinned.h + ../../../utils2.0/utils/Api/apiTypeNameValuePair.h + ../../../utils2.0/utils/Api/apiTypePlatUnicodeString.h + ../../../utils2.0/utils/Api/protocol.h + + ../../../utils2.0/utils/Base/Base32.cpp + ../../../utils2.0/utils/Base/Base32.h + ../../../utils2.0/utils/Base/Base64.cpp + ../../../utils2.0/utils/Base/Base64.h + ../../../utils2.0/utils/Base/basicConfig.cpp + ../../../utils2.0/utils/Base/basicConfig.h + ../../../utils2.0/utils/Base/cmdLine.cpp + ../../../utils2.0/utils/Base/cmdLine.h + ../../../utils2.0/utils/Base/date.cpp + ../../../utils2.0/utils/Base/date.h + ../../../utils2.0/utils/Base/dateUtils.h + ../../../utils2.0/utils/Base/expirationQueue.h + ../../../utils2.0/utils/Base/genericRateLimitingMechanism.h + ../../../utils2.0/utils/Base/hash.h + ../../../utils2.0/utils/Base/hashtable.hpp + ../../../utils2.0/utils/Base/log.cpp + ../../../utils2.0/utils/Base/log.h + ../../../utils2.0/utils/Base/MD5.cpp + ../../../utils2.0/utils/Base/MD5.h + ../../../utils2.0/utils/Base/monitorAPI.cpp + ../../../utils2.0/utils/Base/monitorAPI.h + ../../../utils2.0/utils/Base/monitorData.cpp + ../../../utils2.0/utils/Base/monitorData.h + ../../../utils2.0/utils/Base/pid.cpp + ../../../utils2.0/utils/Base/pid.h + ../../../utils2.0/utils/Base/priority.hpp + ../../../utils2.0/utils/Base/profile.cpp + ../../../utils2.0/utils/Base/profile.h + ../../../utils2.0/utils/Base/refptr.h + ../../../utils2.0/utils/Base/serialize.h + ../../../utils2.0/utils/Base/serializeClasses.h + ../../../utils2.0/utils/Base/serializeStringVector.cpp + ../../../utils2.0/utils/Base/serializeStringVector.h + ../../../utils2.0/utils/Base/serializeTemplates.h + ../../../utils2.0/utils/Base/stringutils.cpp + ../../../utils2.0/utils/Base/stringutils.h + ../../../utils2.0/utils/Base/substringSearchTree.h + ../../../utils2.0/utils/Base/systemLog.cpp + ../../../utils2.0/utils/Base/systemLog.h + ../../../utils2.0/utils/Base/thread.cpp + ../../../utils2.0/utils/Base/thread.h + ../../../utils2.0/utils/Base/timer.h + ../../../utils2.0/utils/Base/trackMessageFailures.cpp + ../../../utils2.0/utils/Base/trackMessageFailures.h + ../../../utils2.0/utils/Base/types.h + ../../../utils2.0/utils/Base/utf8.cpp + ../../../utils2.0/utils/Base/utf8.h + + ../../../utils2.0/utils/TcpLibrary/Clock.cpp + ../../../utils2.0/utils/TcpLibrary/Clock.h + ../../../utils2.0/utils/TcpLibrary/IPAddress.cpp + ../../../utils2.0/utils/TcpLibrary/IPAddress.h + ../../../utils2.0/utils/TcpLibrary/TcpBlockAllocator.cpp + ../../../utils2.0/utils/TcpLibrary/TcpBlockAllocator.h + ../../../utils2.0/utils/TcpLibrary/TcpConnection.cpp + ../../../utils2.0/utils/TcpLibrary/TcpConnection.h + ../../../utils2.0/utils/TcpLibrary/TcpHandlers.h + ../../../utils2.0/utils/TcpLibrary/TcpListener.cpp + ../../../utils2.0/utils/TcpLibrary/TcpListener.h + ../../../utils2.0/utils/TcpLibrary/TcpManager.cpp + ../../../utils2.0/utils/TcpLibrary/TcpManager.h + + ../../../utils2.0/utils/UdpLibrary/UdpHandler.hpp + ../../../utils2.0/utils/UdpLibrary/UdpLibrary.cpp + ../../../utils2.0/utils/UdpLibrary/UdpLibrary.hpp + ../../../utils2.0/utils/UdpLibrary/UdpListener.cpp + ../../../utils2.0/utils/UdpLibrary/UdpListener.h +) diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPI.cpp b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPI.cpp new file mode 100644 index 00000000..fc1c9496 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPI.cpp @@ -0,0 +1,456 @@ +#ifdef WIN32 +#pragma warning (disable : 4786 4514) +#endif + +#include +#include +#include "VChatAPICore.h" +#include "messages.h" +#include "common.h" +#include "Base/hash.h" + +#ifdef WIN32 + #include +#elif linux + #include + #include + #include +#endif + +using namespace std; + +namespace VChatSystem +{ + + ////////////////////////////////////////////////////////////////////////////////////////////// + // VChatAPI + // + + VChatAPI::VChatAPI(const char * hostList, unsigned connectionType, const char * failoverHostList, unsigned connectionLimit, unsigned maxMsgSize, unsigned bufferSize) + : mConnectionType(connectionType) + { + mCore = new VChatAPICore(*this, hostList, failoverHostList, connectionLimit, maxMsgSize, bufferSize); + } + + VChatAPI::~VChatAPI() + { + delete mCore; + } + + void VChatAPI::Process() + { + mCore->Process(); + } + + unsigned VChatAPI::ConnectionCount() const + { + return mCore->GetConnectionCount(); + } + + unsigned VChatAPI::GetAccount(const std::string &avatarName, + const std::string &game, + const std::string &server, + unsigned userID, + unsigned avatarID, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::GetAccountRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetAvatarName(avatarName); + message.SetGame(game); + message.SetServer(server); + message.SetUserID(userID); + message.SetAvatarID(avatarID); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_GET_ACCOUNT_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::GetChannel(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::GetChannelRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetChannelName(channelName); + message.SetGame(game); + message.SetServer(server); + message.SetDescription(description); + message.SetPassword(password); + message.SetLimit(limit); + message.SetPersistent(persistent); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_GET_CHANNEL_REQUEST, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::GetProximityChannel( const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent, + unsigned maxRange, + unsigned clamping, + float rollOff, + float maxGain, + unsigned distModel, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::GetProximityChannelRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetChannelName(channelName); + message.SetGame(game); + message.SetServer(server); + message.SetDescription(description); + message.SetPassword(password); + message.SetLimit(limit); + message.SetPersistent(persistent); + message.SetMaxRange(maxRange); + message.SetClamping(clamping); + message.SetRollOff(rollOff); + message.SetMaxGain(maxGain); + message.SetDistModel(distModel); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_GET_PROXIMITY_CHANNEL_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::ChannelCommand( const std::string &srcUserName, + const std::string &destUserName, + const std::string &destChannelAddress, + unsigned command, + unsigned banTimeout, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::ChannelCommandRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetSrcUserName(srcUserName); + message.SetDestUserName(destUserName); + message.SetDestChannelAddress(destChannelAddress); + message.SetCommand(command); + message.SetBanTimeout(banTimeout); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_CHANNEL_COMMAND_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::DeactivateVoiceAccount(const std::string &avatarName, + const std::string &gameName, + const std::string &serverName, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::DeactivateVoiceAccountRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetAvatarName(avatarName); + message.SetGame(gameName); + message.SetServer(serverName); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_DEACTIVATE_ACCOUNT_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::ChangePassword(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &password, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::ChangePasswordRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetChannelName(channelName); + message.SetGame(game); + message.SetServer(server); + message.SetPassword(password); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_CHANGE_PASSWORD_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::GetAllChannels(void *user, unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::GetAllChannelsRequest message; + message.SetTrackingNumber (trackingNumber); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_GET_ALL_CHANNELS_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + + unsigned VChatAPI::DeleteChannel(const std::string &channelName, + const std::string &game, + const std::string &server, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::DeleteChannelRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetChannelName(channelName); + message.SetGame(game); + message.SetServer(server); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_DELETE_CHANNEL_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::SetUserData( unsigned userID, + unsigned attributes, + const std::string &email, + const std::string &phoneNumber, + const std::string & PIN, + const std::string & smsAccount, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::SetUserDataRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetUserID(userID); + message.SetAttributes(attributes); + message.SetEmail(email); + message.SetPhoneNumber(phoneNumber); + message.SetPin(PIN); + message.SetSmsAccount(smsAccount); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_SET_USER_DATA_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::SetBanStatus(unsigned userID, + unsigned banStatus, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::SetBanStatusRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetUserID(userID); + message.SetBanStatus(banStatus); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_SET_BAN_STATUS_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + + unsigned VChatAPI::GetChannelInfo(const std::string &channelName, + const std::string &game, + const std::string &server, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::GetChannelInfoRequest message; + message.SetTrackingNumber (trackingNumber); + message.SetChannelName(channelName); + message.SetGame(game); + message.SetServer(server); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_GET_CHANNEL_INFO_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + + + unsigned VChatAPI::GetChannelV2(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::GetChannelRequestV2 message; + message.SetTrackingNumber (trackingNumber); + message.SetChannelName(channelName); + message.SetGame(game); + message.SetServer(server); + message.SetDescription(description); + message.SetPassword(password); + message.SetLimit(limit); + message.SetPersistent(persistent); + + // send message + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + mCore->PrepareCallback(MESSAGE_GET_CHANNEL_REPLY_V2, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + + unsigned VChatAPI::AddCharacterChannel(const unsigned stationID, + const unsigned avatarID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + const std::string &channelType, + const std::string &channelDescription, + const std::string &password, + const std::string &channelAddress, + const std::string &locale, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::AddCharacterChannelRequest message; + message.SetTrackingNumber (trackingNumber); + + message.SetStationID(stationID); + message.SetAvatarID(avatarID); + message.SetCharacterName(characterName); + message.SetWorldName(worldName); + message.SetGameCode(gameCode); + message.SetChannelType(channelType); + message.SetChannelDescription(channelDescription); + message.SetPassword(password); + message.SetChannelAddress(channelAddress); + message.SetLocale(locale); + + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + + mCore->PrepareCallback(MESSAGE_ADD_CHARACTER_CHANNEL_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + unsigned VChatAPI::RemoveCharacterChannel(const unsigned stationID, + const unsigned avatarID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + const std::string &channelType, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::RemoveCharacterChannelRequest message; + message.SetTrackingNumber (trackingNumber); + + message.SetStationID(stationID); + message.SetAvatarID(avatarID); + message.SetCharacterName(characterName); + message.SetWorldName(worldName); + message.SetGameCode(gameCode); + message.SetChannelType(channelType); + + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + + mCore->PrepareCallback(MESSAGE_REMOVE_CHARACTER_CHANNEL_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + + + unsigned VChatAPI::GetCharacterChannel(const unsigned stationID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + void *user, + unsigned timeout) + { + unsigned trackingNumber = mCore->GetTrackingNumber(); + Message::GetCharacterChannelRequest message; + message.SetTrackingNumber (trackingNumber); + + message.SetStationID(stationID); + message.SetCharacterName(characterName); + message.SetWorldName(worldName); + message.SetGameCode(gameCode); + + unsigned char buffer[10240]; + memset(buffer, 0, 10240); + unsigned length = message.Write(buffer, sizeof(buffer)); + mCore->Send(buffer, length); + + mCore->PrepareCallback(MESSAGE_GET_CHARACTER_CHANNEL_REPLY, trackingNumber, RESULT_TIMEOUT, user, timeout); + return trackingNumber; + } + + +} // end VChatAPI namespace + + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPI.h b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPI.h new file mode 100644 index 00000000..1ffc8d4b --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPI.h @@ -0,0 +1,532 @@ +#ifndef _VCHAT_SERVER_API_H_ +#define _VCHAT_SERVER_API_H_ + +#include +#include "VChatAPITypes.h" +//#include "CardinalServerUtils.h" + + +namespace VChatSystem +{ + // forward declarations + class VChatAPICore; + + static const unsigned DEFAULT_VCHAT_API_TIMEOUT = 25; + + class VChatAPI + { + protected: + VChatAPICore * mCore; + + protected: + unsigned mConnectionType; + public: + VChatAPI( const char * hostList, unsigned connectionType = CONNECTION_TYPE_VHCAT_SYSTEM,const char * failoverHostList = 0, unsigned connectionLimit = 0, unsigned maxMsgSize = 1*1024, unsigned bufferSize = 64*1024 ); + virtual ~VChatAPI(); + + void Process(); + unsigned ConnectionCount() const; + + //////////////////////////////////////// + // Connection Callbacks + virtual void OnConnectionOpened( const char * address ) = 0; + virtual void OnConnectionFailed( const char * address ) = 0; + virtual void OnConnectionClosed( const char * address, const char * reason ) = 0; + virtual void OnConnectionShutdownNotified( const char * address, unsigned outstandingRequests ) { } + + //////////////////////////////////////// + + + ////////////////////////////////////////// + // GetAccount - will get a players login credentials. If none exist + // it will create the player with Vivox. + // avatarName - Name of the avatar to login. + // game – the game code of the calling game + // server – the world, server or some other identifier for the area of the game that the character is tied to. + // + // The name will parsed and transformed into: + // “.@.vivox.com” + // + // userID – station ID of the player + // avatarID – the ID of the chat avatar, or some other character identifier. + // + unsigned GetAccount(const std::string &avatarName, + const std::string &game, + const std::string &server, + unsigned userID, + unsigned avatarID, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + /////////////////////////////////////////////// + // OnGetAccount + // + // userID – station ID of the player. + // accountID – the Vivox account ID. + // voicePassword – the password for this user’s Vivox account. + // encodedVoiceAccount - the account name encoded with modified + // base64 algorithm. This should be used to login. + // URI - the url that this server is connected to. + virtual void OnGetAccount(unsigned track, + unsigned result, + unsigned userID, + unsigned accountID, + const std::string &voicePassword, + const std::string &encodedVoiceAccount, + const std::string &URI, + void *user) = 0; + + ////////////////////////////////////////// + // channelName - Name of the channel to retrieve. See below for examples. + // game – the game code of the calling game + // server – the world, server or some other identifier for the area of the game that the channel is tied to. + // + // The name will parsed and transformed into: + // group (private) - ..group. channelName would be: group. + // raid (private) - ..raid. channelName would be: raid. + // raid leader (optional, private) - ..raidleader. channelName would be: raidleader. + // guild (private, persistent) - ..guild. channelName would be: guild. + // guild leader (private, persistent) - ..guildleader. channelName would be: guildleader. + // tell (private) - game.server.characterA_characterB channelName would be: characterA_characterB + // say (public, proximity) - game.server.zone channelName would be: zone + // user generated (public/private) - ..user. channelName would be: user. + + // description – description of the room. + // password – password needed to enter the channel. If the password is empty it will not be passed to Vivox. (optional) + // limit - Optional hard limit on the number of users allowed on this channel + // persistent – true – persistent channel, false – temporary channel. + unsigned GetChannel(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + + ////////////////////////////////////////// + // channelName – the channel name. Form is above. + // channelURI - uri returned by Vivox + // channelID – Vivox assigned channel id. + virtual void OnGetChannel(unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user) = 0; + + /////////////////////////////////////////////// + // channelName - Name of the channel to retrieve. + // game – the game code of the calling game + // server – the world, server or some other identifier for the area of the game that the channel is tied to. + // description – description of the room. + // password – password needed to enter the channel. If the password is empty it will not be passed to Vivox. (optional) + // limit - Optional hard limit on the number of users allowed on this channel + // persistent – true – persistent channel, false – temporary channel. + // maxRange - Also known as the audible threshold distance. This is the distance beyond which a participant is considered 'out of range'. When participants cross this threshold distance from a particular listening position in a channel, a roster list updates for the channel occur, which indi-cate an entry being added (or removed, as the case may be) from the participants list. No audio is received by the listener for participants beyond this range. (The default value 80). + // clamping - This is the distance from the listener below which the 'gain roll-off' effects for a given audio roll-off model (see below) are not applied. In effect, it is the 'audio plateau' distance (in the sense that the gain is constant up this distance, and then falls off). (The default value is 10) + // rollOff - This value indicates how sharp the audio attenuation will 'roll-off' between the clamping and maximum distances. Larger values will result in steeper roll-off. The extent of roll-off will depend on the distance model chosen. (Default value is 2.0). + // maxGain - The gain setting for all speakers in this channel. Note, that in practice this should not be raised above 2.5 or auditory distress may result. (The default value is 1.0). + // distModel =<0 | 1| 2 | 3> - There are four possible values as described below: + //(The default model is Inverse Distance Clamped.) + // • None: No distance based attenuation is applied. All speakers are rendered as if they were in the same position as the listener. The audio from speakers will drop to 0 abruptly at the maximum distance. + // • (1) Inverse Distance Clamped: The attenuation increases in inverse proportion to the distance. The roll-off factor n is the inverse of the slope of the attenuation curve. + // • (2) Linear Distance Clamped: The attenuation increases in linear proportion to the distance. The roll-off factor is the negative slope of the attenuation curve. + // • (3) Exponent Distance Clamped: The attenuation increases in inverse proportion to the distance raised to the power of the roll-off factor. + + unsigned GetProximityChannel(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent, + unsigned maxRange, + unsigned clamping, + float rollOff, + float maxGain, + unsigned distModel, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + /////////////////////////////////////////////// + // channelName – the channel name. + // channelURI - uri returned by Vivox + // channelID – Vivox assigned channel id. + virtual void OnGetProximityChannel (unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user) = 0; + + + ////////////////////////////////////////// + // This will be used to operate on existing active channels and to provide moderators and ‘owner’ the ability to mute, kick, ban users along with other commands. + // srcUserName – the owner and/or moderator of this channel with the ability to perform this command. + // destUserName – the user on whom to perform the operation + // Both user names should be of the form: + // “.” + // destChannelAddress – Full address of the channel where the operation will be performed. + // command – The commands below will be placed in an enum that will be distributed with the API. + // • mute - mute user + // • unmute - unmute user + // • kick - drop user from the channel, reentry only after 60 seconds + // • hangup – drop user from the channel without banning them + // • invite - add user to the channel + // • ban - ban user, permanently from the channel + // • unban - unban user from the channel + // • chan_lock - lock the channel for any additional participants + // • chan_unlock - unlock the channel for any additional participants + // • mute_all – mute all callers except the requestor + // • unmute_all – unmute all callers + // banTimeout - if command is ban, timeout for ban in seconds. + unsigned ChannelCommand(const std::string &srcUserName, + const std::string &destUserName, + const std::string &destChannelAddress, + unsigned command, + unsigned banTimeout, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + virtual void OnChannelCommand(unsigned track, + unsigned result, + void *user) = 0; + + + /////////////////////////////////////////////// + // DeactivateVoiceAccount + // In the event that a character is moved, transferred or + // banned the account needs to be de-activated. A new voice + // account will be created for the moved/transferred user when + // they log in with their new character. + unsigned DeactivateVoiceAccount(const std::string &avatarName, + const std::string &game, + const std::string &server, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + virtual void OnDeactivateVoiceAccount(unsigned track, + unsigned result, + void *user) = 0; + + + /////////////////////////////////////////////// + // ChangePassword + // This will reset the password for the channel. + // NOTE: If a blank password is passed in this will + // be sent to the Vivox API. + // + // channelName - Name of the channel to retrieve. + // game – the game code of the calling game + // server – the world, server or some other identifier for the area of the game that the channel is tied to. + // password – password needed to enter the channel. This will modify the password for the channel + + unsigned ChangePassword(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &password, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + /////////////////////////////////////////////// + // OnChangePassword + virtual void OnChangePassword(unsigned track, + unsigned result, + void *user) = 0; + + //////////////////////////////////////////////////////////// + // GetAllChannels + // Will get all channels from the Vivox server. + // + unsigned GetAllChannels(void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + /////////////////////////////////////////////// + // OnGetAllChannels + // + // channels - a vector of channels that Vivox returned to the server. + // + // from common.h: + // struct Channel + // { + // std::string m_channelName; + // std::string m_channelURI; + // std::string m_channelPassword; + // unsigned m_channelID; + // unsigned m_channelType; + // }; + // + // Channel Type can be the following: + // typedef enum ChannelType + // { + // CHAN_TYPE_UNKNOWN = 0, + // CHAN_TYPE_DIR, + // CHAN_TYPE_CHANNEL, + // CHAN_TYPE_PROXIMITY + // }; + // + virtual void OnGetAllChannels(unsigned track, + unsigned result, + const ChannelVec_t & channels, + void *user) = 0; + + + /////////////////////////////////////////////// + // DeleteChannel + // + // Removes the channel from the Vivox server. + // + // channelName - Name of the channel to retrieve. + // game – the game code of the calling game + // server – the world, server or some other identifier for the area of the game that the channel is tied to. + // + unsigned DeleteChannel( const std::string &channelName, + const std::string &game, + const std::string &server, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + /////////////////////////////////////////////// + // OnDeleteChannel + virtual void OnDeleteChannel(unsigned track, + unsigned result, + void *user) = 0; + + + /////////////////////////////////////////////// + // SetUserData + // + // Sets data at the user level (ie. Station Account) that is valid + // valid across all characters for that user. + // + // userID - the userID that is tied to the station account + unsigned SetUserData(unsigned userID, + unsigned attributes, + const std::string & email, + const std::string & phoneNumber, + const std::string & PIN, + const std::string & smsAccount, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + /////////////////////////////////////////////// + // OnSetUserData + virtual void OnSetUserData( unsigned track, + unsigned result, + void *user) = 0; + + /////////////////////////////////////////////// + // SetBanStatus + // + // Sets the ban status at the user level (ie. Station Account) + // This primarily for CS to ban users from Voice for all of + // their characters. + // + // userID - the userID that is tied to the station account + // banStatus - one of the following: + // typedef enum BanStatus + // { + // BAN = 0, + // UNBAN + // }; + // + unsigned SetBanStatus(unsigned userID, + unsigned banStatus, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + /////////////////////////////////////////////// + // OnSetBanStatus + virtual void OnSetBanStatus(unsigned track, + unsigned result, + void *user) = 0; + + + ////////////////////////////////////////// + // channelName - Name of the channel to retrieve. See below for examples. + // game – the game code of the calling game + // server – the world, server or some other identifier for the area of the game that the channel is tied to. + // + // The name will parsed and transformed into: + // group (private) - ..group. channelName would be: group. + // raid (private) - ..raid. channelName would be: raid. + // raid leader (optional, private) - ..raidleader. channelName would be: raidleader. + // guild (private, persistent) - ..guild. channelName would be: guild. + // guild leader (private, persistent) - ..guildleader. channelName would be: guildleader. + // tell (private) - game.server.characterA_characterB channelName would be: characterA_characterB + // say (public, proximity) - game.server.zone channelName would be: zone + // user generated (public/private) - ..user. channelName would be: user. + unsigned GetChannelInfo(const std::string &channelName, + const std::string &game, + const std::string &server, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + + ////////////////////////////////////////// + // channelName – the channel name. Form is above. + // channelURI - uri returned by Vivox + // channelID – Vivox assigned channel id. + virtual void OnGetChannelInfo(unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user) = 0; + + ////////////////////////////////////////// + // This is identical to GetChannel except the callback will + // let the caller know if the channel was created or an existing + // channel was retrieved. + // + // channelName - Name of the channel to retrieve. See below for examples. + // game – the game code of the calling game + // server – the world, server or some other identifier for the area of the game that the channel is tied to. + // + // The name will parsed and transformed into: + // group (private) - ..group. channelName would be: group. + // raid (private) - ..raid. channelName would be: raid. + // raid leader (optional, private) - ..raidleader. channelName would be: raidleader. + // guild (private, persistent) - ..guild. channelName would be: guild. + // guild leader (private, persistent) - ..guildleader. channelName would be: guildleader. + // tell (private) - game.server.characterA_characterB channelName would be: characterA_characterB + // say (public, proximity) - game.server.zone channelName would be: zone + // user generated (public/private) - ..user. channelName would be: user. + + // description – description of the room. + // password – password needed to enter the channel. If the password is empty it will not be passed to Vivox. (optional) + // limit - Optional hard limit on the number of users allowed on this channel + // persistent – true – persistent channel, false – temporary channel. + unsigned GetChannelV2(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + + ////////////////////////////////////////// + // channelName – the channel name. Form is above. + // channelURI - uri returned by Vivox + // channelID – Vivox assigned channel id. + // isNewChannel + virtual void OnGetChannelV2(unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + unsigned isNewChannel, + void *user) = 0; + + + ////////////////////////////////////////// + // Character channels are to be maintained by the game teams. + // These will be used to store channels that the customer is allowed to access in game. + // the descriptions will be localized so that the display will match what the customer sees in game + // There will be just straight through db access on each of these calls + // When a character is deleted they should all be removed, when it is renamed these should be updated, etc. + // Common params: + // stationID -- the station id associated with the character + // avatarID -- the internal id for the character uint64 + // characterName -- utf8 character name + // worldName -- utf8 string for the world name + // gameCode -- utf8 string with the gamecode such as EQ, EQ2, SWG, etc. + // channelType -- (utf8 internal description to identify the channel type (guild, officers, etc.) + // channelDescription -- localized description will be displayed to the user from station launcher + // channelID -- unsigned channelID + // channelURI -- URI for the channel with vivox + // locale -- locale for the description such as en_US + + ////////////////////////////////////////// + + ////////////////////////////////////////// + // AddCharacterChannel + // + // params: + // stationID + // avatarID + // characterName + // channelType + // channelDescription -- localized + // channelID + // channelURI + // locale + unsigned AddCharacterChannel(const unsigned stationID, + const unsigned avatarID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + const std::string &channelType, + const std::string &channelDescription, + const std::string &password, + const std::string &channelAddress, + const std::string &locale, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + virtual void OnAddCharacterChannel(unsigned track, + unsigned result, + void *user) = 0; + + ////////////////////////////////////////// + // RemoveCharacterChannel + // + // params: + // stationID + // avatarID + // characterName + // worldName + // gameCode + // channelType + // channelDescription -- localized + // channelID + // channelURI + // locale + unsigned RemoveCharacterChannel(const unsigned stationID, + const unsigned avatarID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + const std::string &channelType, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + virtual void OnRemoveCharacterChannel(unsigned track, + unsigned result, + void *user) = 0; + + ////////////////////////////////////////// + // GetCharacterChannel + // + // params: + // stationID + // avatarID + // characterName + // channelType + // channelDescription -- localized + // channelID + // channelURI + // locale + unsigned GetCharacterChannel(const unsigned stationID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + void *user, + unsigned timeout = DEFAULT_VCHAT_API_TIMEOUT); + + virtual void OnGetCharacterChannel(unsigned track, + unsigned result, + const CharacterChannelVec_t &characterChannels, + void * user) = 0; + +}; + +} // end namespace + +#endif // end #ifndef _VCHAT_SERVER_API_H_ diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPICore.cpp b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPICore.cpp new file mode 100644 index 00000000..e05bfb56 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPICore.cpp @@ -0,0 +1,332 @@ +#ifdef WIN32 +#pragma warning (disable : 4786 4514) +#endif + +#include +#include +#include "VChatAPICore.h" +#include "messages.h" +#include "Base/hash.h" + +using namespace std; +//using namespace API_NAMESPACE::Message; + + +namespace VChatSystem +{ + + VChatAPICore::VChatAPICore(VChatAPI & client, const char * hostList, const char * failoverHostList, + unsigned connectionLimit, unsigned maxMsgSize, unsigned bufferSize) + : CommonAPI(hostList, failoverHostList, connectionLimit, maxMsgSize, bufferSize) + , mClient(client) + { + // requests + RegisterTrackedMessage(MESSAGE_GET_ACCOUNT_REQUEST); + RegisterTrackedMessage(MESSAGE_GET_CHANNEL_REQUEST); + RegisterTrackedMessage(MESSAGE_GET_PROXIMITY_CHANNEL_REQUEST); + RegisterTrackedMessage(MESSAGE_CHANNEL_COMMAND_REQUEST); + RegisterTrackedMessage(MESSAGE_DEACTIVATE_ACCOUNT_REQUEST); + RegisterTrackedMessage(MESSAGE_CHANGE_PASSWORD_REQUEST); + RegisterTrackedMessage(MESSAGE_GET_ALL_CHANNELS_REQUEST); + RegisterTrackedMessage(MESSAGE_DELETE_CHANNEL_REQUEST); + RegisterTrackedMessage(MESSAGE_SET_USER_DATA_REQUEST); + RegisterTrackedMessage(MESSAGE_SET_BAN_STATUS_REQUEST); + RegisterTrackedMessage(MESSAGE_GET_CHANNEL_INFO_REQUEST); + RegisterTrackedMessage(MESSAGE_GET_CHANNEL_REQUEST_V2); + RegisterTrackedMessage(MESSAGE_ADD_CHARACTER_CHANNEL); + RegisterTrackedMessage(MESSAGE_REMOVE_CHARACTER_CHANNEL); + RegisterTrackedMessage(MESSAGE_GET_CHARACTER_CHANNEL); + RegisterTrackedMessage(MESSAGE_UPDATE_CHARACTER_CHANNEL); + + // replies + RegisterTrackedMessage(MESSAGE_GET_ACCOUNT_REPLY); + RegisterTrackedMessage(MESSAGE_GET_CHANNEL_REPLY); + RegisterTrackedMessage(MESSAGE_GET_PROXIMITY_CHANNEL_REPLY); + RegisterTrackedMessage(MESSAGE_CHANNEL_COMMAND_REPLY); + RegisterTrackedMessage(MESSAGE_DEACTIVATE_ACCOUNT_REPLY); + RegisterTrackedMessage(MESSAGE_CHANGE_PASSWORD_REPLY); + RegisterTrackedMessage(MESSAGE_GET_ALL_CHANNELS_REPLY); + RegisterTrackedMessage(MESSAGE_DELETE_CHANNEL_REPLY); + RegisterTrackedMessage(MESSAGE_SET_USER_DATA_REPLY); + RegisterTrackedMessage(MESSAGE_SET_BAN_STATUS_REPLY); + RegisterTrackedMessage(MESSAGE_GET_CHANNEL_INFO_REPLY); + RegisterTrackedMessage(MESSAGE_GET_CHANNEL_REPLY_V2); + RegisterTrackedMessage(MESSAGE_ADD_CHARACTER_CHANNEL_REPLY); + RegisterTrackedMessage(MESSAGE_REMOVE_CHARACTER_CHANNEL_REPLY); + RegisterTrackedMessage(MESSAGE_GET_CHARACTER_CHANNEL_REPLY); + RegisterTrackedMessage(MESSAGE_UPDATE_CHARACTER_CHANNEL_REPLY); + + } + + VChatAPICore::~VChatAPICore() + { + } + + const char * VChatAPICore::GetVersion() + { + return "2008.04.25"; + } + + HashValue_t VChatAPICore::CreateHashValue(const string & name) + { + return HashUserName(name.c_str()); + } + + unsigned VChatAPICore::CheckMessageId(const unsigned char * data, unsigned dataLen) + { + API_NAMESPACE::Message::Basic message; + message.Read(data, dataLen); + return message.GetMsgId(); + } + + unsigned VChatAPICore::CheckTrackingNumber(const unsigned char * data, unsigned dataLen) + { + API_NAMESPACE::Message::Tracked message; + message.Read(data, dataLen); + return message.GetTrackingNumber(); + } + + unsigned VChatAPICore::FormatStatusMsg(unsigned char * buffer, unsigned size) + { + API_NAMESPACE::Message::Connect message; + message.SetVersion(GetVersion()); + return message.Write(buffer, size); + } + + bool VChatAPICore::CheckConnectReply(const unsigned char * data, unsigned dataLen) + { + API_NAMESPACE::Message::ConnectReply message; + return (message.Read(data, dataLen) && + message.GetMsgId() == MESSAGE_CONNECT_REPLY && + message.GetResult() == RESULT_SUCCESS); + } + + void VChatAPICore::OnConnectionOpened(const char * address) + { + mClient.OnConnectionOpened(address); + } + + void VChatAPICore::OnConnectionFailed(const char * address) + { + mClient.OnConnectionFailed(address); + } + + void VChatAPICore::OnConnectionClosed(const char * address, const char * reason) + { + mClient.OnConnectionClosed(address, reason); + } + + void VChatAPICore::OnConnectionShutdownNotified(const char * address, unsigned outstandingRequests) + { + mClient.OnConnectionShutdownNotified(address, outstandingRequests); + } + + ///////////////////////////////////////// + // request callbacks for successful communication + ///////////////////////////////////////// + + void VChatAPICore::Callback(const unsigned char * data, unsigned dataLen, unsigned messageId, void * userData) + { + switch (messageId) + { + case MESSAGE_GET_ACCOUNT_REPLY: + { + Message::GetAccountReply message; + if(message.Read(data, dataLen)) + mClient.OnGetAccount(message.GetTrackingNumber(), message.GetResult(), message.GetUserID(), message.GetAccountID(), message.GetVoicePassword(), message.GetEncodedAccountName(), message.GetURI(), userData); + break; + } + case MESSAGE_GET_CHANNEL_REPLY: + { + Message::GetChannelReply message; + if(message.Read(data, dataLen)) + mClient.OnGetChannel(message.GetTrackingNumber(), message.GetResult(), message.GetChannelName(), message.GetChannelURI(), message.GetChannelID(), userData); + break; + } + case MESSAGE_GET_PROXIMITY_CHANNEL_REPLY: + { + Message::GetProximityChannelReply message; + if(message.Read(data, dataLen)) + mClient.OnGetProximityChannel(message.GetTrackingNumber(), message.GetResult(), message.GetChannelName(), message.GetChannelURI(), message.GetChannelID(), userData); + break; + } + case MESSAGE_CHANNEL_COMMAND_REPLY: + { + Message::ChannelCommandReply message; + if(message.Read(data, dataLen)) + mClient.OnChannelCommand(message.GetTrackingNumber(), message.GetResult(), userData); + break; + } + case MESSAGE_DEACTIVATE_ACCOUNT_REPLY: + { + Message::DeactivateVoiceAccountReply message; + if(message.Read(data, dataLen)) + mClient.OnDeactivateVoiceAccount(message.GetTrackingNumber(), message.GetResult(), userData); + break; + } + case MESSAGE_CHANGE_PASSWORD_REPLY: + { + Message::ChangePasswordReply message; + if(message.Read(data, dataLen)) + mClient.OnChangePassword(message.GetTrackingNumber(), message.GetResult(), userData); + break; + } + case MESSAGE_GET_ALL_CHANNELS_REPLY: + { + Message::GetAllChannelsReply message; + if(message.Read(data, dataLen)) + mClient.OnGetAllChannels(message.GetTrackingNumber(), message.GetResult(), message.GetChannelsVec(), userData); + break; + } + case MESSAGE_DELETE_CHANNEL_REPLY: + { + Message::DeleteChannelReply message; + if(message.Read(data, dataLen)) + mClient.OnDeleteChannel(message.GetTrackingNumber(), message.GetResult(), userData); + break; + } + case MESSAGE_SET_USER_DATA_REPLY: + { + Message::SetUserDataReply message; + if(message.Read(data, dataLen)) + mClient.OnSetUserData(message.GetTrackingNumber(), message.GetResult(), userData); + break; + } + case MESSAGE_SET_BAN_STATUS_REPLY: + { + Message::SetBanStatusReply message; + if(message.Read(data, dataLen)) + mClient.OnSetBanStatus(message.GetTrackingNumber(), message.GetResult(), userData); + break; + } + case MESSAGE_GET_CHANNEL_INFO_REPLY: + { + Message::GetChannelInfoReply message; + if(message.Read(data, dataLen)) + mClient.OnGetChannelInfo(message.GetTrackingNumber(), message.GetResult(), message.GetChannelName(), message.GetChannelURI(), message.GetChannelID(), userData); + break; + } + case MESSAGE_GET_CHANNEL_REPLY_V2: + { + Message::GetChannelReplyV2 message; + if(message.Read(data, dataLen)) + mClient.OnGetChannelV2(message.GetTrackingNumber(), message.GetResult(), message.GetChannelName(), message.GetChannelURI(), message.GetChannelID(), message.GetIsNewChannel(),userData); + break; + } + case MESSAGE_ADD_CHARACTER_CHANNEL_REPLY: + { + Message::AddCharacterChannelReply message; + if(message.Read(data, dataLen)) + mClient.OnAddCharacterChannel(message.GetTrackingNumber(), message.GetResult(), userData); + break; + } + case MESSAGE_REMOVE_CHARACTER_CHANNEL_REPLY: + { + Message::RemoveCharacterChannelReply message; + if(message.Read(data, dataLen)) + mClient.OnRemoveCharacterChannel(message.GetTrackingNumber(), message.GetResult(), userData); + break; + } + case MESSAGE_GET_CHARACTER_CHANNEL_REPLY: + { + Message::GetCharacterChannelReply message; + if(message.Read(data, dataLen)) + mClient.OnGetCharacterChannel(message.GetTrackingNumber(), message.GetResult(), message.GetChannelList(), userData); + break; + } + + default: + break; + } + } + + + ///////////////////////////////////////// + // TIMEOUT callbacks + ///////////////////////////////////////// + void VChatAPICore::Callback(unsigned messageId, unsigned trackingNumber, unsigned result, void * userData) + { + switch (messageId) + { + case MESSAGE_GET_ACCOUNT_REPLY: + { + mClient.OnGetAccount(trackingNumber, result, 0, 0, std::string(), std::string(), std::string(), userData); + break; + } + case MESSAGE_GET_CHANNEL_REPLY: + { + mClient.OnGetChannel(trackingNumber, result, std::string(), std::string(), 0, userData); + break; + } + case MESSAGE_GET_PROXIMITY_CHANNEL_REPLY: + { + mClient.OnGetProximityChannel(trackingNumber, result, std::string(), std::string(), 0, userData); + break; + } + case MESSAGE_CHANNEL_COMMAND_REPLY: + { + mClient.OnChannelCommand(trackingNumber, result, userData); + break; + } + case MESSAGE_DEACTIVATE_ACCOUNT_REPLY: + { + mClient.OnDeactivateVoiceAccount(trackingNumber, result, userData); + break; + } + case MESSAGE_CHANGE_PASSWORD_REPLY: + { + mClient.OnChangePassword(trackingNumber, result, userData); + break; + } + case MESSAGE_GET_ALL_CHANNELS_REPLY: + { + mClient.OnGetAllChannels(trackingNumber, result, ChannelVec_t(), userData); + break; + } + case MESSAGE_DELETE_CHANNEL_REPLY: + { + mClient.OnDeleteChannel(trackingNumber, result, userData); + break; + } + case MESSAGE_SET_USER_DATA_REPLY: + { + mClient.OnSetUserData(trackingNumber, result, userData); + break; + } + case MESSAGE_SET_BAN_STATUS_REPLY: + { + mClient.OnSetBanStatus(trackingNumber, result, userData); + break; + } + case MESSAGE_GET_CHANNEL_INFO_REPLY: + { + mClient.OnGetChannelInfo(trackingNumber, result, std::string(), std::string(), 0, userData); + break; + } + case MESSAGE_GET_CHANNEL_REPLY_V2: + { + mClient.OnGetChannelV2(trackingNumber, result, std::string(), std::string(), 0, 0, userData); + break; + } + case MESSAGE_ADD_CHARACTER_CHANNEL_REPLY: + { + mClient.OnAddCharacterChannel(trackingNumber, result, userData); + break; + } + case MESSAGE_REMOVE_CHARACTER_CHANNEL_REPLY: + { + mClient.OnRemoveCharacterChannel(trackingNumber, result,userData); + break; + } + case MESSAGE_GET_CHARACTER_CHANNEL_REPLY: + { + mClient.OnGetCharacterChannel(trackingNumber, result, CharacterChannelVec_t(), userData); + break; + } + default : + break; + } + } +} + + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPICore.h b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPICore.h new file mode 100644 index 00000000..63dd7390 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPICore.h @@ -0,0 +1,60 @@ +#ifndef _VCHAT_SERVER_API_CORE_H_ +#define _VCHAT_SERVER_API_CORE_H_ + +#include "clientSerialize.h" +#include "VChatAPI.h" + +#ifndef _VCHAT_API_API_HEADERS_ +#define _VCHAT_API_API_HEADERS_ + #ifndef API_NAMESPACE + #define API_NAMESPACE VChatSystem + #include "Api/api.h" + #undef API_NAMESPACE + #else + #include "Api/api.h" + #endif +#endif + +using namespace API_NAMESPACE; + +namespace VChatSystem +{ + + extern const char * encryptionKey; + + + class VChatAPICore : public CommonAPI + { + private: + VChatAPI & mClient; + + public: + VChatAPICore(VChatAPI & client, const char * hostList, const char * failoverHostList = 0, unsigned connectionLimit = 0, unsigned maxMsgSize = 1*1024, unsigned bufferSize = 64*1024); + virtual ~VChatAPICore(); + + virtual const char * GetVersion(); + virtual unsigned CheckMessageId(const unsigned char * data, unsigned dataLen); + virtual unsigned CheckTrackingNumber(const unsigned char * data, unsigned dataLen); + virtual unsigned FormatStatusMsg(unsigned char * buffer, unsigned size); + virtual bool CheckConnectReply(const unsigned char * data, unsigned dataLen); + + virtual void OnConnectionOpened(const char * address); + virtual void OnConnectionFailed(const char * address); + virtual void OnConnectionClosed(const char * address, const char * reason); + virtual void OnConnectionShutdownNotified(const char * address, unsigned outstandingRequests); + virtual void Callback(const unsigned char * data, unsigned dataLen, unsigned messageId, void * userData); + virtual void Callback(unsigned messageId, unsigned trackingNumber, unsigned result, void * userData); + + HashValue_t CreateHashValue(const std::string & name); + + private: + VChatAPICore(const VChatAPICore &); + VChatAPICore & operator=(const VChatAPICore &); + }; + + +} + + +#endif //_VCHAT_SERVER_API_CORE_H_ + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPITypes.h b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPITypes.h new file mode 100644 index 00000000..a7b0cbb9 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/VChatAPITypes.h @@ -0,0 +1,215 @@ +#ifndef _VCHAT_SERVER_API_TYPES_H_ +#define _VCHAT_SERVER_API_TYPES_H_ + +#include "common.h" +#include "Api/apiTypeNameValuePair.h" + +#define MAX_ARRAY 100 +#define MAX_LENGTH_METHOD_ID 32 +#define MAX_LENGTH_RAW_XML_MESSAGE 4096 + + +//////////////////////////////////////////////////////////////////////////// +// defines for dealing with 64 bit integers that work between unix and windows +// EX: +// printf(" a long variable["FMT_QUAD"]\n", QUAD(1234567890123456)); +#ifdef _WIN32 + #define QUAD(X) X##i64 + #define UQUAD(X) X##ui64 + #define SQUAD(X) X##i64 + #define FMT_QUAD "%I64d" + #define FMT_QUADU "%I64u" +#else + #define QUAD(X) X##LL + #define UQUAD(X) X##ULL + #define SQUAD(X) X##LL + #define FMT_QUAD "%lld" + #define FMT_QUADU "%llu" +#endif + + + +namespace VChatSystem +{ + #define RETURN_TEST(X) if (X != RESULT_SUCCESS) return X; + + + typedef unsigned TrackingNumber; + typedef unsigned TransactionResultCode; + typedef long long TransactionId; + typedef unsigned ResultCode; + + + typedef enum ResultCode_t + { + RESULT_SUCCESS = 0, + RESULT_TIMEOUT, + RESULT_FAILURE, + RESULT_INVALID_PARAM, + RESULT_DATABASE_FAILURE, + RESULT_NETWORK_FAILURE, + RESULT_NOT_IMPLEMENTED, + RESULT_NOT_SUPPORTED, + RESULT_SYSTEM_FAILURE, + RESULT_PROTOCOL_VIOLATION, + RESULT_NOT_FOUND, + RESULT_TCP_CONNECTION_NOT_AVAILABLE, + RESULT_RESPONSE_XML_INVALID, + RESULT_RESPONSE_VIVOX_FAILURE, + RESULT_USER_ACCOUNT_BANNED, + RESULT_NAME_EXCEEDS_VIVOX_LIMIT, + RESULT_NO_CHANNELS_FOUND_FOR_CHARACTER, + + // vivox error codes + RESULT_VIVOX_WRONG_USER_CREDENTIALS = 200, + RESULT_VIVOX_EXPIRED_USER_CREDENTIALS = 201, + RESULT_VIVOX_MISSING_USER_CREDENTIALS = 202, + RESULT_VIVOX_USER_NOT_LOGGED_IN = 203, + RESULT_VIVOX_ACCOUNT_CANT_PERFORM_OP = 212, + RESULT_VIVOX_FAILED_RETRIEVING_DIR = 220, + + RESULT_VIVOX_PARAMS_MISSING = 300, + RESULT_VIVOX_UNKNOWN_MODE = 301, + RESULT_VIVOX_UNABLE_TO_PARSE_ARG = 302, + + RESULT_VIVOX_ACCOUNT_CREATION_FAILED = 400, + RESULT_VIVOX_ACCOUNT_DEACTIVATE_FAILED = 401, + RESULT_VIVOX_ACCOUNT_ACTIVATE_FAILED = 402, + RESULT_VIVOX_ACCOUNT_DOESNT_EXIST = 403, + RESULT_VIVOX_ACCOUNT_RETRIEVAL_FAILED = 404, + RESULT_VIVOX_ACCOUNT_UPDATE_FAILED = 405, + RESULT_VIVOX_ACCOUNT_DELETE_FAILED = 406, + + RESULT_VIVOX_RETRIEVING_SRC_USER = 450, + RESULT_VIVOX_CHANNEL_ADD_FAILED = 451, + RESULT_VIVOX_CHANNEL_UPDATE_FAILED = 452, + RESULT_VIVOX_CHANNEL_RETRIEVE_FAILED = 453, + + RESULT_VIVOX_FEATURE_NAME_UNKNOWN = 500, + RESULT_VIVOX_FEATURE_RETRIEVAL_FAILED = 501, + RESULT_VIVOX_FEATURE_SET_FAILED = 502, + RESULT_VIVOX_FEATURE_UPDATE_FAILED = 503, + RESULT_VIVOX_FEATURE_DELETE_FAILED = 504, + RESULT_VIVOX_RETRIEVE_USER_FAILED = 505, + RESULT_VIVOX_RETRIEVE_FEATURE_LIST_FAILED = 506, + + RESULT_VIVOX_CHANNEL_RETRIEVE_USERS_FAILED = 700, + RESULT_VIVOX_CHANNEL_MUTE_ALL_FAILED = 701, + RESULT_VIVOX_CHANNEL_UNMUTE_ALL_FAILED = 702, + RESULT_VIVOX_CHANNEL_INVITE_FAILED = 703, + RESULT_VIVOX_CHANNEL_RETRIEVING_ACL_FAILED = 704, + RESULT_VIVOX_CHANNEL_DELETE_ACL_FAILED = 706, + RESULT_VIVOX_CHANNEL_LOCKING_FAILED = 707, + RESULT_VIVOX_CHANNEL_UNLOCKING_FAILED = 708, + RESULT_VIVOX_CHANNEL_CREATING_FAILED = 709, + RESULT_VIVOX_CHANNEL_RETRIEVE_FAILED_EX = 710, + RESULT_VIVOX_USER_NOT_ON_CHANNEL = 711, + RESULT_VIVOX_UNABLE_TO_COMPLETE_CHANNEL_LOOKUP = 712, + + RESULT_END + }; + + typedef enum CommandType_t + { + COMMAND_MUTE = 0, + COMMAND_UNMUTE, + COMMAND_KICK, + COMMAND_HANGUP, + COMMAND_INVITE, + COMMAND_BAN, + COMMAND_UNBAN, + COMMAND_CHAN_LOCK, + COMMAND_CHAN_UNLOCK, + COMMAND_MUTE_ALL, + COMMAND_UNMUTE_ALL, + COMMAND_ADD_MODERATOR, + COMMAND_DELETE_MODERATOR, + COMMAND_ADD_ACL, + COMMAND_DELETE_ACL, + COMMAND_DROP_ALL, + COMMAND_END + }; + + typedef enum ConnectionType + { + CONNECTION_TYPE_UNKNOWN = 0, + CONNECTION_TYPE_VHCAT_SYSTEM + }; + + typedef enum ChannelType + { + CHAN_TYPE_UNKNOWN = 0, + CHAN_TYPE_DIR, + CHAN_TYPE_CHANNEL, + CHAN_TYPE_PROXIMITY + }; + + typedef enum BanStatus + { + BAN = 0, + UNBAN + }; + + //Following from Account Server + + const unsigned MAX_MSG_SIZE=64*1024; + const unsigned REQ_ID_FIELD_SIZE = 30; + const unsigned MIN_ACCOUNT_NAME_SIZE = 4; const unsigned MAX_ACCOUNT_NAME_SIZE = 15; + const unsigned MIN_ACCOUNT_PASSWORD_SIZE = 5; const unsigned MAX_ACCOUNT_PASSWORD_SIZE = 255; + const unsigned MIN_REASON_SIZE = 0; const unsigned MAX_REASON_SIZE = 2000; + const unsigned MIN_PASSWORD_SIZE = 8; const unsigned MAX_PASSWORD_SIZE = 255; + const unsigned MIN_SECURITY_ANSWER_SIZE = 0; const unsigned MAX_SECURITY_ANSWER_SIZE = 255; + const unsigned MIN_EMAIL_SIZE = 0; const unsigned MAX_EMAIL_SIZE = 255; + const unsigned MIN_STATION_HANDLE_SIZE = 0; const unsigned MAX_STATION_HANDLE_SIZE = 31; + const unsigned MIN_STATION_NAME_SIZE = 0; const unsigned MAX_STATION_NAME_SIZE = 50; + const unsigned MIN_MEMBER_TYPE_SIZE = 0; const unsigned MAX_MEMBER_TYPE_SIZE = 31; + const unsigned MIN_MEMBER_ID_SIZE = 0; const unsigned MAX_MEMBER_ID_SIZE = 31; + const unsigned MIN_RESET_TYPE_SIZE = 0; const unsigned MAX_RESET_TYPE_SIZE = 31; + const unsigned MIN_PASSWORD_EMAIL_SIZE = 0; const unsigned MAX_PASSWORD_EMAIL_SIZE = 255; + const unsigned MIN_COUNTRY_CODE_SIZE = 2; const unsigned MAX_COUNTRY_CODE_SIZE = 2; + const unsigned MIN_COUNTRY_NAME_SIZE = 0; const unsigned MAX_COUNTRY_NAME_SIZE = 255; + const unsigned MIN_CURRENCY_SIZE = 0; const unsigned MAX_CURRENCY_SIZE = 31; + const unsigned MIN_RESET_ID_SIZE = 0; const unsigned MAX_RESET_ID_SIZE = 20; + const unsigned MIN_LANG_SIZE = 2; const unsigned MAX_LANG_SIZE = 2 + 1 + 2; + const unsigned MIN_OPTIN_TYPE_SIZE = 0; const unsigned MAX_OPTIN_TYPE_SIZE = 255; + const unsigned MIN_OPTIN_ID_SIZE = 0; const unsigned MAX_OPTIN_ID_SIZE = 255; + + typedef unsigned SessionType_t; + + /*enum RequestType + { + REQUEST_TRANS_INIT_LOOKUP = 0x0, + REQUEST_TRANS_AUTH, + REQUEST_TRANS_AUTH_BRIDGE_LOOKUP, + + // Add new request type codes here + REQUEST_END + }; +*/ + + + enum ApiResultCode + { + API_RESULT_UNKNOWN = 0 + ,API_RESULT_SUCCESS= 1 + ,API_RESULT_FAIL = 2 + ,API_RESULT_FAIL_DISCONNECTED + ,API_RESULT_FAIL_RATE_LIMIT_EXCEEDED + + // uncertain result error codes + ,API_RESULT_FAIL_API_TIMEOUT = 100 + ,API_RESULT_FAIL_SERVER_TIMEOUT + ,API_RESULT_FAIL_BAD_MESSAGE_FORMAT + ,API_RESULT_FAIL_TRANSPORT + ,API_RESULT_FAIL_READ_MESSAGE + ,API_RESULT_FAIL_SERVER_READ_MESSAGE + ,API_RESULT_FAIL_SERVER_WRITE_MESSAGE + + // API parameter error codeS(do not use for server side) + ,API_RESULT_FAIL_BAD_PARAMS = 200 + + }; + +} // end namespace CardinalServerAPI + +#endif //_VCHAT_SERVER_API_TYPES_H_ diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/clientSerialize.h b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/clientSerialize.h new file mode 100644 index 00000000..596cd926 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/clientSerialize.h @@ -0,0 +1,114 @@ +#ifndef _VCHAT_CLIENT_SERIALIZE_H_ +#define _VCHAT_CLIENT_SERIALIZE_H_ + +#include "common.h" + +namespace soe +{ + + inline unsigned Write(unsigned char * stream, unsigned size, const VChatSystem::Channel & data, unsigned version = 0) + { + return data.Write(stream, size); + } + + inline unsigned Read(const unsigned char * stream, unsigned size, VChatSystem::Channel & data, unsigned, unsigned version = 0) + { + return data.Read(stream, size); + } + + inline unsigned Write(unsigned char * stream, unsigned size, const VChatSystem::CharacterChannel & data, unsigned version = 0) + { + return data.Write(stream, size); + } + + inline unsigned Read(const unsigned char * stream, unsigned size, VChatSystem::CharacterChannel & data, unsigned, unsigned version = 0) + { + return data.Read(stream, size); + } + + inline unsigned Write(unsigned char * stream, unsigned size, const VChatSystem::ChannelVec_t & data, unsigned version = 0) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + bytesTotal += (bytes = soe::Write(stream + bytesTotal, size - bytesTotal, (unsigned)data.size())); + if (!bytes) return 0; + + for (size_t i = 0; i < data.size(); i++) + { + bytesTotal += (bytes = soe::Write(stream + bytesTotal, size - bytesTotal, data[i])); + if (!bytes) return 0; + } + + return bytesTotal; + } + + + inline unsigned Read(const unsigned char * stream, unsigned size, VChatSystem::ChannelVec_t & data, unsigned maxLen = 1, unsigned version = 0) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + data.clear(); + int vsize = 0; + + bytesTotal += (bytes = soe::Read(stream + bytesTotal, size - bytesTotal, vsize)); + if (!bytes) return 0; + + for (int i = 0; i < vsize; i++) + { + VChatSystem::Channel tmpData; + bytesTotal += (bytes = soe::Read(stream + bytesTotal, size - bytesTotal, tmpData, 1)); + if (!bytes) + { + return 0; + } + data.push_back(tmpData); + } + + return bytesTotal; + } + + inline unsigned Write(unsigned char * stream, unsigned size, const VChatSystem::CharacterChannelVec_t & data, unsigned version = 0) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + bytesTotal += (bytes = soe::Write(stream + bytesTotal, size - bytesTotal, (unsigned)data.size())); + if (!bytes) return 0; + + for (size_t i = 0; i < data.size(); i++) + { + bytesTotal += (bytes = soe::Write(stream + bytesTotal, size - bytesTotal, data[i])); + if (!bytes) return 0; + } + + return bytesTotal; + } + + + inline unsigned Read(const unsigned char * stream, unsigned size, VChatSystem::CharacterChannelVec_t & data, unsigned maxLen = 1, unsigned version = 0) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + data.clear(); + int vsize = 0; + + bytesTotal += (bytes = soe::Read(stream + bytesTotal, size - bytesTotal, vsize)); + if (!bytes) return 0; + + for (int i = 0; i < vsize; i++) + { + VChatSystem::CharacterChannel tmpData; + bytesTotal += (bytes = soe::Read(stream + bytesTotal, size - bytesTotal, tmpData, 1)); + if (!bytes) + { + return 0; + } + data.push_back(tmpData); + } + + return bytesTotal; + } +}; + +#endif //_VCHAT_CLIENT_SERIALIZE_H_ diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/common.cpp b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/common.cpp new file mode 100644 index 00000000..fa46951c --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/common.cpp @@ -0,0 +1,334 @@ +#include "common.h" +#include "Base/Base64.h" +#include "Base/Base32.h" +#include +#include "Base/stringutils.h" + +using namespace Base; + +namespace VChatSystem +{ + + unsigned Channel::Write(unsigned char * stream, unsigned size) const + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_channelName)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_channelURI)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_channelPassword)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_channelID)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_channelType)); + if (!bytes) return 0; + + return bytesTotal; + } + + unsigned Channel::Read(const unsigned char * stream, unsigned size) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_channelName, MAX_CHANNEL_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_channelURI, MAX_CHANNEL_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_channelPassword, MAX_PASSWORD_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_channelID)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_channelType)); + if (!bytes) return 0; + + return bytesTotal; + } + + unsigned CharacterChannel::Write(unsigned char * stream, unsigned size) const + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_stationID)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_avatarID)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_characterName)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_worldName)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_gameCode)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_channelType)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_channelDescription)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_channelAddress)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_locale)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, m_password)); + if (!bytes) return 0; + + return bytesTotal; + } + unsigned CharacterChannel::Read(const unsigned char * stream, unsigned size) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_stationID)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_avatarID)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_characterName, MAX_FULL_NAME_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_worldName, MAX_WORLD_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_gameCode, MAX_GAME_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_channelType, MAX_FULL_NAME_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_channelDescription, MAX_DESCRIPTION_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_channelAddress, MAX_CHANNEL_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_locale, MAX_FULL_NAME_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, m_password, MAX_PASSWORD_LEN)); + if (!bytes) return 0; + + return bytesTotal; + } + + + const char * VIV0X_DOT_COM = ".vivox.com"; + + void CreateUserURI(const std::string & avatarName, const std::string & game, const std::string & server, std::string & outUserURI) + { + outUserURI = server; + outUserURI += "."; + outUserURI += avatarName; + outUserURI += "@"; + outUserURI += game; + outUserURI += VIV0X_DOT_COM; + } + + void CreateUserName(const std::string & avatarName, const std::string & server, std::string & outUserName) + { + outUserName = server; + outUserName += "."; + outUserName += avatarName; + } + + + void CreateChannelURI(const std::string & channelName, const std::string & game, const std::string & server, std::string & outChannelURI) + { + outChannelURI = game; + outChannelURI += "."; + outChannelURI += server; + outChannelURI += "."; + outChannelURI += channelName; + } + + void GetChannelComponents(const std::string & channelURI, std::string & name, std::string & server, std::string & game) + { + char seps[] = ".\n"; + char *token; + + // this will muck with the actual string so make a copy + // not very efficient but who cares... + std::string tmpTokenee = channelURI; + token = strtok((char*)tmpTokenee.c_str(), seps ); + + + if( token != NULL ) + { + game = token; + + /* Get next token: */ + token = strtok( NULL, seps ); + } + + if( token != NULL ) + { + server = token; + + /* Get next token: */ + token = strtok( NULL, seps ); + } + + if( token != NULL ) + { + name = token; + + /* Get next token: */ + token = strtok( NULL, seps ); + } + + while ( token != NULL ) + { + name += "."; + name += token; + + /* Get next token: */ + token = strtok( NULL, seps ); + } + } + + void GetUserNameComponents(const std::string & userName, std::string & server, std::string & name) + { + + char seps[] = ".\n"; + char *token; + + // this will muck with the actual string so make a copy + // not very efficient but who cares... + std::string tmpTokenee = userName; + token = strtok((char*)tmpTokenee.c_str(), seps ); + + if( token != NULL ) + { + server = token; + + /* Get next token: */ + token = strtok( NULL, seps ); + } + + if( token != NULL ) + { + name = token; + + /* Get next token: */ + token = strtok( NULL, seps ); + } + + while ( token != NULL ) + { + name += "."; + name += token; + + /* Get next token: */ + token = strtok( NULL, seps ); + } + } + + + std::string HexEncodeString(const std::string & input) + { + std::string retString; + soe::Base64::HexEncode((unsigned char *)input.c_str(), (unsigned)input.length(), retString, true); + return retString; + } + + void EncodeVivoxString(const std::string & input, std::string & output) + { + std::string tmpInput = soe::tolowercase(input); + + const unsigned outLen = Base32::GetEncode32Length((int)tmpInput.length()); + + unsigned char * outBuffer = new unsigned char[outLen + 1]; + + if(Base32::Encode32((unsigned char*)tmpInput.c_str(), (int)tmpInput.length(), outBuffer)) + { + Base32::Map32(outBuffer, outLen); + + outBuffer[outLen] = 0; + output = (char*)outBuffer; + } + + if (outBuffer) + { + delete [] outBuffer; + } + } + + void DecodeVivoxString(const std::string & input, std::string & output) + { + std::string tmpInput = soe::touppercase(input); + unsigned char tmpBuffer[256]; + + int inputLen = (int)tmpInput.length(); + memset(tmpBuffer, 0, 256); + memcpy(tmpBuffer, tmpInput.c_str(), inputLen); + + const unsigned outLen = Base32::GetDecode32Length((int)inputLen); + + unsigned char * outBuffer = new unsigned char[outLen + 1]; + + Base32::Unmap32(tmpBuffer, inputLen); + + if(Base32::Decode32(tmpBuffer, inputLen, outBuffer)) + { + outBuffer[outLen] = 0; + output = (char*)outBuffer; + } + + if (outBuffer) + { + delete [] outBuffer; + } + } + + + void GetEncodedUserName(const std::string & name, const std::string & server, std::string & encodedName) + { + std::string tmpName; + EncodeVivoxString(name, tmpName); + CreateUserName(tmpName, server, encodedName); + } + + void GetDecodedUserName(const std::string & username, std::string & decodedName) + { + std::string name, server, decoded; + GetUserNameComponents(username, server, name); + DecodeVivoxString(name, decoded); + CreateUserName(decoded, server, decodedName); + } + + void GetEncodedChannelName(const std::string & name, const std::string & server, const std::string & game, std::string & encodedName) + { + std::string tmpName; + EncodeVivoxString(name, tmpName); + CreateChannelURI(tmpName, game, server, encodedName); + } + + void GetDecodedChannelName(const std::string & channelName, std::string & decodedChannelName) + { + std::string name, server, game, decoded; + GetChannelComponents(channelName, name, server, game); + DecodeVivoxString(name, decoded); + CreateChannelURI(decoded, game, server, decodedChannelName); + } +}; + + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/common.h b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/common.h new file mode 100644 index 00000000..c81f38eb --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/common.h @@ -0,0 +1,114 @@ +#ifndef _VCHAT_SERVER_COMMON_H_ +#define _VCHAT_SERVER_COMMON_H_ + +#include +#include +#ifndef SOE__DATE_H + #include +#endif + +namespace VChatSystem +{ + // Add common enumerated types or constants here + + typedef unsigned char HashValue_t; + + const unsigned MAX_PARAM_LEN = 6400; + const unsigned MAX_STUFF_LEN = 64000; + const unsigned MAX_NAME_PAIR_LEN = 256; + const unsigned MAX_VALUE_PAIR_LEN = 256; + const unsigned MAX_AVATAR_LEN = 50; + const unsigned MAX_CHANNEL_LEN = 255; + const unsigned MAX_DESCRIPTION_LEN = 256; + const unsigned MAX_FULL_NAME_LEN = 1024; + const unsigned MAX_GAME_LEN = 255; + const unsigned MAX_PASSWORD_LEN = 30; + const unsigned MAX_CHAT_AVATAR_LEN = 255; + const unsigned MAX_WORLD_LEN = 255; + const unsigned MAX_EMAIL_LEN = 255; + const unsigned MAX_PHONE_NUMBER_LEN = 255; + const unsigned MAX_PIN_LEN = 255; + const unsigned MAX_SMS_ACCT_LEN = 255; + const unsigned MAX_CHANNEL_ID_LEN = 100; + const unsigned MAX_URI_LEN = 255; + const unsigned VIVOX_NAME_LIMIT = 63; + + + struct Channel + { + Channel(): m_channelID(0) , m_channelType(0) {} + ~Channel() {} + + std::string m_channelName; + std::string m_channelURI; + std::string m_channelPassword; + unsigned m_channelID; + unsigned m_channelType; + + unsigned Write(unsigned char * stream, unsigned size) const; + unsigned Read(const unsigned char * stream, unsigned size); + }; + + typedef std::vector ChannelVec_t; + + + struct CharacterChannel + { + CharacterChannel(): m_stationID(0), m_avatarID(0){} + ~CharacterChannel() {} + + unsigned m_stationID; + unsigned m_avatarID; + std::string m_characterName; + std::string m_worldName; + std::string m_gameCode; + std::string m_channelType; + std::string m_channelDescription; + std::string m_channelAddress; + std::string m_locale; + std::string m_password; + + unsigned Write(unsigned char * stream, unsigned size) const; + unsigned Read(const unsigned char * stream, unsigned size); + + }; + + typedef std::vector CharacterChannelVec_t; + + // given avatarName, game and server returns outUserURI in the form of server.avatarName@game.vivox.com + void CreateUserURI(const std::string & avatarName, const std::string & game, const std::string & server, std::string & outUserURI); + + // given avatarName and server returns outUserName in the form of server.avatarName + void CreateUserName(const std::string & avatarName, const std::string & server, std::string & outUserName); + + // given channelName, game and server returns outChannelURI in the form of game.server.channelName + void CreateChannelURI(const std::string & channelName, const std::string & game, const std::string & server, std::string & outChannelURI); + + // given input of game.server.channel will break into seperate strings + void GetChannelComponents(const std::string & channelURI, std::string & name, std::string & server, std::string & game); + + // given input of server.userName will break into seperate strings + void GetUserNameComponents(const std::string & userName, std::string & server, std::string & name); + + std::string HexEncodeString(const std::string & input); + + // encodes the string using a modified base64 encoding. + void EncodeVivoxString(const std::string & input, std::string & output); + + // reverses a string encoded with EncodeVivoxString + void DecodeVivoxString(const std::string & input, std::string & output); + + // given a name and server will return encodedName in the form of game.server.EncodedName + void GetEncodedUserName(const std::string & name, const std::string & server, std::string & encodedName); + + // given an input of server.EncodedName will return server.DecodedName + void GetDecodedUserName(const std::string & username, std::string & decodedName); + + // given a name and server will return encodedName in the form of server.EncodedName + void GetEncodedChannelName(const std::string & name, const std::string & server, const std::string & game, std::string & encodedName); + + // given an input of game.server.EncodedName will return game.server.DecodedName + void GetDecodedChannelName(const std::string & channelName, std::string & decodedChannelName); +} + +#endif //_VCHAT_SERVER_COMMON_H_ diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/messages.cpp b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/messages.cpp new file mode 100644 index 00000000..c15b3044 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/messages.cpp @@ -0,0 +1,277 @@ +#ifdef WIN32 +#pragma warning (disable : 4786 4514) +#endif + +#include "messages.h" + +using namespace std; + +namespace VChatSystem +{ + + namespace Message + { + //////////////////////////////////////// + // Login + ImplementMessageBegin(GetAccountRequest, Tracked) + ImplementMessageMemberEx(AvatarName, std::string(), MAX_AVATAR_LEN) + ImplementMessageMemberEx(Game, std::string(), MAX_GAME_LEN) + ImplementMessageMemberEx(Server, std::string(), MAX_WORLD_LEN) + ImplementMessageMember(UserID, 0) + ImplementMessageMember(AvatarID, 0) + ImplementMessageEnd + + ImplementMessageBegin(GetAccountReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageMember(UserID, 0) + ImplementMessageMember(AccountID, 0) + ImplementMessageMemberEx(VoicePassword, std::string(), MAX_PASSWORD_LEN) + ImplementMessageMemberEx(EncodedAccountName, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMemberEx(URI, std::string(), MAX_URI_LEN) + ImplementMessageEnd + + + //////////////////////////////////////// + // GetChannel + ImplementMessageBegin(GetChannelRequest, Tracked) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_CHANNEL_LEN) + ImplementMessageMemberEx(Game, std::string(), MAX_GAME_LEN) + ImplementMessageMemberEx(Server, std::string(), MAX_WORLD_LEN) + ImplementMessageMemberEx(Description, std::string(), MAX_DESCRIPTION_LEN) + ImplementMessageMemberEx(Password, std::string(), MAX_PASSWORD_LEN) + ImplementMessageMember(Limit, 0) + ImplementMessageMember(Persistent, 0) + ImplementMessageEnd + + ImplementMessageBegin(GetChannelReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMemberEx(ChannelURI, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMember(ChannelID, 0) + ImplementMessageEnd + + /////////////////////////////////////// + // GetProximityChannel + ImplementMessageBegin(GetProximityChannelRequest, Tracked) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_CHANNEL_LEN) + ImplementMessageMemberEx(Game, std::string(), MAX_GAME_LEN) + ImplementMessageMemberEx(Server, std::string(), MAX_WORLD_LEN) + ImplementMessageMemberEx(Description, std::string(), MAX_DESCRIPTION_LEN) + ImplementMessageMemberEx(Password, std::string(), MAX_PASSWORD_LEN) + ImplementMessageMember(Limit, 0) + ImplementMessageMember(Persistent, 0) + ImplementMessageMember(MaxRange, 0) + ImplementMessageMember(Clamping, 0) + ImplementMessageMember(RollOff, 0) + ImplementMessageMember(MaxGain, 0) + ImplementMessageMember(DistModel, 0) + ImplementMessageEnd + + ImplementMessageBegin(GetProximityChannelReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMemberEx(ChannelURI, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMember(ChannelID, 0) + ImplementMessageEnd + + + /////////////////////////////////////// + // ChannelCommand + ImplementMessageBegin(ChannelCommandRequest, Tracked) + ImplementMessageMemberEx(SrcUserName, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMemberEx(DestUserName, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMemberEx(DestChannelAddress, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMember(Command, 0) + ImplementMessageMember(BanTimeout, 0) + ImplementMessageEnd + + ImplementMessageBegin(ChannelCommandReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + /////////////////////////////////////// + // DeactivateVoiceAccount + ImplementMessageBegin(DeactivateVoiceAccountRequest, Tracked) + ImplementMessageMemberEx(AvatarName, std::string(), MAX_AVATAR_LEN) + ImplementMessageMemberEx(Game, std::string(), MAX_GAME_LEN) + ImplementMessageMemberEx(Server, std::string(), MAX_WORLD_LEN) + ImplementMessageEnd + + ImplementMessageBegin(DeactivateVoiceAccountReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + /////////////////////////////////////// + // ChangePassword + ImplementMessageBegin(ChangePasswordRequest, Tracked) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_CHANNEL_LEN) + ImplementMessageMemberEx(Game, std::string(), MAX_GAME_LEN) + ImplementMessageMemberEx(Server, std::string(), MAX_WORLD_LEN) + ImplementMessageMemberEx(Password, std::string(), MAX_PASSWORD_LEN) + ImplementMessageEnd + + ImplementMessageBegin(ChangePasswordReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + //////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////// + // GetAllChannels + ImplementMessageBegin(GetAllChannelsRequest, Tracked) + ImplementMessageEnd + + //////////////////////////////////////////////////////////////////////////////// + // GetAllChannelsReply + ImplementMessageBegin(GetAllChannelsReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageMemberEx(ChannelsVec, ChannelVec_t(), 4096) + ImplementMessageEnd + + + /////////////////////////////////////// + // DeleteChannelRequest + ImplementMessageBegin(DeleteChannelRequest, Tracked) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_CHANNEL_LEN) + ImplementMessageMemberEx(Game, std::string(), MAX_GAME_LEN) + ImplementMessageMemberEx(Server, std::string(), MAX_WORLD_LEN) + ImplementMessageEnd + + ImplementMessageBegin(DeleteChannelReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + /////////////////////////////////////// + // SetUserData + ImplementMessageBegin(SetUserDataRequest, Tracked) + ImplementMessageMember(UserID, 0) + ImplementMessageMember(Attributes, 0) + ImplementMessageMemberEx(Email, std::string(), MAX_EMAIL_LEN) + ImplementMessageMemberEx(PhoneNumber, std::string(), MAX_PHONE_NUMBER_LEN) + ImplementMessageMemberEx(Pin, std::string(), MAX_PIN_LEN) + ImplementMessageMemberEx(SmsAccount, std::string(), MAX_SMS_ACCT_LEN) + ImplementMessageEnd + + ImplementMessageBegin(SetUserDataReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + /////////////////////////////////////// + // SetBanStatus + ImplementMessageBegin(SetBanStatusRequest, Tracked) + ImplementMessageMember(UserID, 0) + ImplementMessageMember(BanStatus, 0) + ImplementMessageEnd + + ImplementMessageBegin(SetBanStatusReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + + //////////////////////////////////////// + // GetChannel + ImplementMessageBegin(GetChannelInfoRequest, Tracked) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_CHANNEL_LEN) + ImplementMessageMemberEx(Game, std::string(), MAX_GAME_LEN) + ImplementMessageMemberEx(Server, std::string(), MAX_WORLD_LEN) + ImplementMessageEnd + + ImplementMessageBegin(GetChannelInfoReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMemberEx(ChannelURI, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMember(ChannelID, 0) + ImplementMessageEnd + + //////////////////////////////////////// + // GetChannel + ImplementMessageBegin(GetChannelRequestV2, Tracked) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_CHANNEL_LEN) + ImplementMessageMemberEx(Game, std::string(), MAX_GAME_LEN) + ImplementMessageMemberEx(Server, std::string(), MAX_WORLD_LEN) + ImplementMessageMemberEx(Description, std::string(), MAX_DESCRIPTION_LEN) + ImplementMessageMemberEx(Password, std::string(), MAX_PASSWORD_LEN) + ImplementMessageMember(Limit, 0) + ImplementMessageMember(Persistent, 0) + ImplementMessageEnd + + ImplementMessageBegin(GetChannelReplyV2, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageMemberEx(ChannelName, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMemberEx(ChannelURI, std::string(), MAX_FULL_NAME_LEN) + ImplementMessageMember(ChannelID, 0) + ImplementMessageMember(IsNewChannel, 0) + ImplementMessageEnd + + //////////////////////////////////////// + // AddCharacterChannel + ImplementMessageBegin(AddCharacterChannelRequest, Tracked) + ImplementMessageMember( StationID,0 ) + ImplementMessageMember( AvatarID, 0 ) + ImplementMessageMemberEx( CharacterName, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageMemberEx( WorldName, std::string(), MAX_WORLD_LEN ) + ImplementMessageMemberEx( GameCode, std::string(), MAX_GAME_LEN ) + ImplementMessageMemberEx( ChannelType, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageMemberEx( ChannelDescription, std::string(), MAX_DESCRIPTION_LEN ) + ImplementMessageMemberEx( Password, std::string(), MAX_PASSWORD_LEN) + ImplementMessageMemberEx( ChannelAddress, std::string(),MAX_CHANNEL_LEN ) + ImplementMessageMemberEx( Locale, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageEnd + + ImplementMessageBegin(AddCharacterChannelReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + //////////////////////////////////////// + // RemoveCharacterChannel + ImplementMessageBegin(RemoveCharacterChannelRequest, Tracked) + ImplementMessageMember( StationID,0 ) + ImplementMessageMember( AvatarID, 0 ) + ImplementMessageMemberEx( CharacterName, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageMemberEx( WorldName, std::string(), MAX_WORLD_LEN ) + ImplementMessageMemberEx( GameCode, std::string(), MAX_GAME_LEN ) + ImplementMessageMemberEx( ChannelType, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageEnd + + ImplementMessageBegin(RemoveCharacterChannelReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + + //////////////////////////////////////// + // GetCharacterChannel + ImplementMessageBegin(GetCharacterChannelRequest, Tracked) + ImplementMessageMember( StationID,0 ) + ImplementMessageMemberEx( CharacterName, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageMemberEx( WorldName, std::string(), MAX_WORLD_LEN ) + ImplementMessageMemberEx( GameCode, std::string(), MAX_GAME_LEN ) + ImplementMessageEnd + + ImplementMessageBegin(GetCharacterChannelReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageMemberEx(ChannelList, CharacterChannelVec_t(), MAX_NAME_PAIR_LEN) + ImplementMessageEnd + + //////////////////////////////////////// + // UpdateCharacterChannel + ImplementMessageBegin(UpdateCharacterChannelRequest, Tracked) + ImplementMessageMember( StationID,0 ) + ImplementMessageMember( AvatarID, 0 ) + ImplementMessageMemberEx( CharacterName, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageMemberEx( WorldName, std::string(), MAX_WORLD_LEN ) + ImplementMessageMemberEx( GameCode, std::string(), MAX_GAME_LEN ) + ImplementMessageMemberEx( ChannelType, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageMemberEx( ChannelDescription, std::string(), MAX_DESCRIPTION_LEN ) + ImplementMessageMember( ChannelID, 0 ) + ImplementMessageMemberEx( Password, std::string(), MAX_PASSWORD_LEN) + ImplementMessageMemberEx( ChannelURI, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageMemberEx( Locale, std::string(),MAX_FULL_NAME_LEN ) + ImplementMessageEnd + + ImplementMessageBegin(UpdateCharacterChannelReply, TrackedReply) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + } + +} + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/messages.h b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/messages.h new file mode 100644 index 00000000..89e50146 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/messages.h @@ -0,0 +1,350 @@ +#ifndef _VCHAT_SERVER_API_MESSAGES_H_ +#define _VCHAT_SERVER_API_MESSAGES_H_ + +#include +#include + +#include "clientSerialize.h" + +#ifndef _VCHAT_API_API_HEADERS_ +#define _VCHAT_API_API_HEADERS_ + #ifndef API_NAMESPACE + #define API_NAMESPACE VChatSystem + #include "Api/api.h" + #undef API_NAMESPACE + #else + #include "Api/api.h" + #endif +#endif + +namespace VChatSystem +{ + + enum + { + // internal message + MESSAGE_RETRY_NOTIFICATIONS = 1, + // Requests + MESSAGE_GET_ACCOUNT_REQUEST = 0x1100, + MESSAGE_GET_CHANNEL_REQUEST, + MESSAGE_GET_PROXIMITY_CHANNEL_REQUEST, + MESSAGE_CHANNEL_COMMAND_REQUEST, + MESSAGE_DEACTIVATE_ACCOUNT_REQUEST, + MESSAGE_CHANGE_PASSWORD_REQUEST, + MESSAGE_GET_ALL_CHANNELS_REQUEST, + MESSAGE_DELETE_CHANNEL_REQUEST, + MESSAGE_SET_USER_DATA_REQUEST, + MESSAGE_SET_BAN_STATUS_REQUEST, + MESSAGE_GET_CHANNEL_INFO_REQUEST, + MESSAGE_GET_CHANNEL_REQUEST_V2, + MESSAGE_ADD_CHARACTER_CHANNEL, + MESSAGE_REMOVE_CHARACTER_CHANNEL, + MESSAGE_GET_CHARACTER_CHANNEL, + MESSAGE_UPDATE_CHARACTER_CHANNEL, + + // Replies + MESSAGE_GET_ACCOUNT_REPLY = 0x8100, + MESSAGE_GET_CHANNEL_REPLY, + MESSAGE_GET_PROXIMITY_CHANNEL_REPLY, + MESSAGE_CHANNEL_COMMAND_REPLY, + MESSAGE_DEACTIVATE_ACCOUNT_REPLY, + MESSAGE_CHANGE_PASSWORD_REPLY, + MESSAGE_GET_ALL_CHANNELS_REPLY, + MESSAGE_DELETE_CHANNEL_REPLY, + MESSAGE_SET_USER_DATA_REPLY, + MESSAGE_SET_BAN_STATUS_REPLY, + MESSAGE_GET_CHANNEL_INFO_REPLY, + MESSAGE_GET_CHANNEL_REPLY_V2, + MESSAGE_ADD_CHARACTER_CHANNEL_REPLY, + MESSAGE_REMOVE_CHARACTER_CHANNEL_REPLY, + MESSAGE_GET_CHARACTER_CHANNEL_REPLY, + MESSAGE_UPDATE_CHARACTER_CHANNEL_REPLY, + }; + + + namespace Message + { + +//////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////// + // Login + DefineMessageBegin(GetAccountRequest, Tracked, MESSAGE_GET_ACCOUNT_REQUEST) + DefineMessageMember(AvatarName, std::string) + DefineMessageMember(Game, std::string) + DefineMessageMember(Server, std::string) + DefineMessageMember(UserID, unsigned) + DefineMessageMember(AvatarID, unsigned) + DefineMessageEnd + + DefineMessageBegin(GetAccountReply, TrackedReply, MESSAGE_GET_ACCOUNT_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageMember(UserID, unsigned) + DefineMessageMember(AccountID, unsigned) + DefineMessageMember(VoicePassword, std::string) + DefineMessageMember(EncodedAccountName, std::string) + DefineMessageMember(URI, std::string) + DefineMessageEnd + + + //////////////////////////////////////// + // GetChannel + DefineMessageBegin(GetChannelRequest, Tracked, MESSAGE_GET_CHANNEL_REQUEST) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(Game, std::string) + DefineMessageMember(Server, std::string) + DefineMessageMember(Description, std::string) + DefineMessageMember(Password, std::string) + DefineMessageMember(Limit, unsigned) + DefineMessageMember(Persistent, unsigned) + DefineMessageEnd + + DefineMessageBegin(GetChannelReply, TrackedReply, MESSAGE_GET_CHANNEL_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(ChannelURI, std::string) + DefineMessageMember(ChannelID, unsigned) + DefineMessageEnd + + /////////////////////////////////////// + // GetProximityChannel + DefineMessageBegin(GetProximityChannelRequest, Tracked, MESSAGE_GET_PROXIMITY_CHANNEL_REQUEST) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(Game, std::string) + DefineMessageMember(Server, std::string) + DefineMessageMember(Description, std::string) + DefineMessageMember(Password, std::string) + DefineMessageMember(Limit, unsigned) + DefineMessageMember(Persistent, unsigned) + DefineMessageMember(MaxRange, unsigned) + DefineMessageMember(Clamping, unsigned) + DefineMessageMember(RollOff, float) + DefineMessageMember(MaxGain, float) + DefineMessageMember(DistModel, unsigned) + DefineMessageEnd + + DefineMessageBegin(GetProximityChannelReply, TrackedReply, MESSAGE_GET_PROXIMITY_CHANNEL_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(ChannelURI, std::string) + DefineMessageMember(ChannelID, unsigned) + DefineMessageEnd + + + /////////////////////////////////////// + // ChannelCommand + DefineMessageBegin(ChannelCommandRequest, Tracked, MESSAGE_CHANNEL_COMMAND_REQUEST) + DefineMessageMember(SrcUserName, std::string) + DefineMessageMember(DestUserName, std::string) + DefineMessageMember(DestChannelAddress, std::string) + DefineMessageMember(Command, unsigned) + DefineMessageMember(BanTimeout, unsigned) + DefineMessageEnd + + DefineMessageBegin(ChannelCommandReply, TrackedReply, MESSAGE_CHANNEL_COMMAND_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + /////////////////////////////////////// + // DeactivateVoiceAccount + DefineMessageBegin(DeactivateVoiceAccountRequest, Tracked, MESSAGE_DEACTIVATE_ACCOUNT_REQUEST) + DefineMessageMember(AvatarName, std::string) + DefineMessageMember(Game, std::string) + DefineMessageMember(Server, std::string) + DefineMessageEnd + + DefineMessageBegin(DeactivateVoiceAccountReply, TrackedReply, MESSAGE_DEACTIVATE_ACCOUNT_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + /////////////////////////////////////// + // ChangePassword + DefineMessageBegin(ChangePasswordRequest, Tracked, MESSAGE_CHANGE_PASSWORD_REQUEST) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(Game, std::string) + DefineMessageMember(Server, std::string) + DefineMessageMember(Password, std::string) + DefineMessageEnd + + DefineMessageBegin(ChangePasswordReply, TrackedReply, MESSAGE_CHANGE_PASSWORD_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + + + //////////////////////////////////////////////////////////////////////////////// + // GetAllChannels + DefineMessageBegin(GetAllChannelsRequest, Tracked, MESSAGE_GET_ALL_CHANNELS_REQUEST) + DefineMessageEnd + + DefineMessageBegin(GetAllChannelsReply, TrackedReply, MESSAGE_GET_ALL_CHANNELS_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageMember(ChannelsVec, ChannelVec_t) + DefineMessageEnd + + + /////////////////////////////////////// + // DeleteChannel + DefineMessageBegin(DeleteChannelRequest, Tracked, MESSAGE_DELETE_CHANNEL_REQUEST) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(Game, std::string) + DefineMessageMember(Server, std::string) + DefineMessageEnd + + DefineMessageBegin(DeleteChannelReply, TrackedReply, MESSAGE_DELETE_CHANNEL_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + + /////////////////////////////////////// + // SetUserData + DefineMessageBegin(SetUserDataRequest, Tracked, MESSAGE_SET_USER_DATA_REQUEST) + DefineMessageMember(UserID, unsigned) + DefineMessageMember(Attributes, unsigned) + DefineMessageMember(Email, std::string) + DefineMessageMember(PhoneNumber, std::string) + DefineMessageMember(Pin, std::string) + DefineMessageMember(SmsAccount, std::string) + DefineMessageEnd + + DefineMessageBegin(SetUserDataReply, TrackedReply, MESSAGE_SET_USER_DATA_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + + /////////////////////////////////////// + // SetBanStatus + DefineMessageBegin(SetBanStatusRequest, Tracked, MESSAGE_SET_BAN_STATUS_REQUEST) + DefineMessageMember(UserID, unsigned) + DefineMessageMember(BanStatus, unsigned) + DefineMessageEnd + + DefineMessageBegin(SetBanStatusReply, TrackedReply, MESSAGE_SET_BAN_STATUS_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + + //////////////////////////////////////// + // GetChannelInfo + DefineMessageBegin(GetChannelInfoRequest, Tracked, MESSAGE_GET_CHANNEL_INFO_REQUEST) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(Game, std::string) + DefineMessageMember(Server, std::string) + DefineMessageEnd + + DefineMessageBegin(GetChannelInfoReply, TrackedReply, MESSAGE_GET_CHANNEL_INFO_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(ChannelURI, std::string) + DefineMessageMember(ChannelID, unsigned) + DefineMessageEnd + + //////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////// + // GetChannel + DefineMessageBegin(GetChannelRequestV2, Tracked, MESSAGE_GET_CHANNEL_REQUEST_V2) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(Game, std::string) + DefineMessageMember(Server, std::string) + DefineMessageMember(Description, std::string) + DefineMessageMember(Password, std::string) + DefineMessageMember(Limit, unsigned) + DefineMessageMember(Persistent, unsigned) + DefineMessageEnd + + DefineMessageBegin(GetChannelReplyV2, TrackedReply, MESSAGE_GET_CHANNEL_REPLY_V2) + DefineMessageMember(Result, unsigned) + DefineMessageMember(ChannelName, std::string) + DefineMessageMember(ChannelURI, std::string) + DefineMessageMember(ChannelID, unsigned) + DefineMessageMember(IsNewChannel, unsigned) + DefineMessageEnd + + + //////////////////////////////////////// + // AddCharacterChannel + DefineMessageBegin(AddCharacterChannelRequest, Tracked, MESSAGE_ADD_CHARACTER_CHANNEL) + DefineMessageMember( StationID,unsigned ) + DefineMessageMember( AvatarID, unsigned ) + DefineMessageMember( CharacterName, std::string ) + DefineMessageMember( WorldName, std::string ) + DefineMessageMember( GameCode, std::string ) + DefineMessageMember( ChannelType, std::string ) + DefineMessageMember( ChannelDescription, std::string ) + DefineMessageMember( Password, std::string) + DefineMessageMember( ChannelAddress, std::string ) + DefineMessageMember( Locale, std::string ) + DefineMessageEnd + + DefineMessageBegin(AddCharacterChannelReply, TrackedReply, MESSAGE_ADD_CHARACTER_CHANNEL_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + //////////////////////////////////////// + // RemoveCharacterChannel + DefineMessageBegin(RemoveCharacterChannelRequest, Tracked, MESSAGE_REMOVE_CHARACTER_CHANNEL) + DefineMessageMember( StationID,unsigned ) + DefineMessageMember( AvatarID, unsigned ) + DefineMessageMember( CharacterName, std::string ) + DefineMessageMember( WorldName, std::string ) + DefineMessageMember( GameCode, std::string ) + DefineMessageMember( ChannelType, std::string ) + DefineMessageEnd + + DefineMessageBegin(RemoveCharacterChannelReply, TrackedReply, MESSAGE_REMOVE_CHARACTER_CHANNEL_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + + //////////////////////////////////////// + // GetCharacterChannel + DefineMessageBegin(GetCharacterChannelRequest, Tracked, MESSAGE_GET_CHARACTER_CHANNEL) + DefineMessageMember( StationID,unsigned ) + DefineMessageMember( CharacterName, std::string ) + DefineMessageMember( WorldName, std::string ) + DefineMessageMember( GameCode, std::string ) + DefineMessageEnd + + DefineMessageBegin(GetCharacterChannelReply, TrackedReply, MESSAGE_GET_CHARACTER_CHANNEL_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageMember(ChannelList, CharacterChannelVec_t) + DefineMessageEnd + + //////////////////////////////////////// + // UpdateCharacterChannel + DefineMessageBegin(UpdateCharacterChannelRequest, Tracked, MESSAGE_UPDATE_CHARACTER_CHANNEL) + DefineMessageMember( StationID,unsigned ) + DefineMessageMember( AvatarID, unsigned ) + DefineMessageMember( CharacterName, std::string ) + DefineMessageMember( WorldName, std::string ) + DefineMessageMember( GameCode, std::string ) + DefineMessageMember( ChannelType, std::string ) + DefineMessageMember( ChannelDescription, std::string ) + DefineMessageMember( ChannelID, unsigned ) + DefineMessageMember( Password, std::string) + DefineMessageMember( ChannelURI, std::string ) + DefineMessageMember( Locale, std::string ) + DefineMessageEnd + + DefineMessageBegin(UpdateCharacterChannelReply, TrackedReply, MESSAGE_UPDATE_CHARACTER_CHANNEL_REPLY) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + /* , + , + , + MESSAGE_UPDATE_CHARACTER_CHANNEL,*/ + + + + //////////////////////////////////////////////////////////////////////////////// + + } // namepsace Message + +} // namespace API_NAMESPACE + + +#endif //_VCHAT_SERVER_API_MESSAGES_H_ + + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/strings.h b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/strings.h new file mode 100644 index 00000000..8e471d8b --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatAPI/strings.h @@ -0,0 +1,149 @@ +#ifndef _VCHAT_SERVER_API_STRINGS_H_ +#define _VCHAT_SERVER_API_STRINGS_H_ + +#include +#include +#include "VChatAPI.h" +#include "messages.h" +#include "common.h" + + +namespace VChatSystem +{ + + typedef std::pair Id2Text_t; + typedef std::map Id2TextMap_t; + + ////////////////////////////////////////////////////////////////////////////////// + // ResultString + // + static const Id2Text_t _resultString[] = + { + Id2Text_t(RESULT_SUCCESS, "RESULT_SUCCESS"), + Id2Text_t(RESULT_TIMEOUT, "RESULT_TIMEOUT"), + Id2Text_t(RESULT_FAILURE, "RESULT_SYSTEM_ERROR"), + Id2Text_t(RESULT_INVALID_PARAM, "RESULT_INVALID_PARAM"), + Id2Text_t(RESULT_DATABASE_FAILURE, "RESULT_DATABASE_FAILURE"), + Id2Text_t(RESULT_NETWORK_FAILURE, "RESULT_NETWORK_FAILURE"), + Id2Text_t(RESULT_NOT_IMPLEMENTED, "RESULT_NOT_IMPLEMENTED"), + Id2Text_t(RESULT_NOT_SUPPORTED, "RESULT_NOT_SUPPORTED"), + Id2Text_t(RESULT_TCP_CONNECTION_NOT_AVAILABLE, "RESULT_TCP_CONNECTION_NOT_AVAILABLE"), + Id2Text_t(RESULT_RESPONSE_XML_INVALID, "RESULT_RESPONSE_XML_INVALID"), + Id2Text_t(RESULT_RESPONSE_VIVOX_FAILURE, "RESULT_RESPONSE_VIVOX_FAILURE"), + Id2Text_t(RESULT_USER_ACCOUNT_BANNED, "RESULT_USER_ACCOUNT_BANNED"), + Id2Text_t(RESULT_NAME_EXCEEDS_VIVOX_LIMIT, "RESULT_NAME_EXCEEDS_VIVOX_LIMIT"), + Id2Text_t(RESULT_NO_CHANNELS_FOUND_FOR_CHARACTER, "RESULT_NO_CHANNELS_FOUND_FOR_CHARACTER"), + + Id2Text_t(RESULT_VIVOX_WRONG_USER_CREDENTIALS, "RESULT_VIVOX_WRONG_USER_CREDENTIALS"), + Id2Text_t(RESULT_VIVOX_EXPIRED_USER_CREDENTIALS, "RESULT_VIVOX_EXPIRED_USER_CREDENTIALS"), + Id2Text_t(RESULT_VIVOX_MISSING_USER_CREDENTIALS, "RESULT_VIVOX_MISSING_USER_CREDENTIALS"), + Id2Text_t(RESULT_VIVOX_USER_NOT_LOGGED_IN, "RESULT_VIVOX_USER_NOT_LOGGED_IN"), + Id2Text_t(RESULT_VIVOX_ACCOUNT_CANT_PERFORM_OP, "RESULT_VIVOX_ACCOUNT_CANT_PERFORM_OP"), + Id2Text_t(RESULT_VIVOX_FAILED_RETRIEVING_DIR, "RESULT_VIVOX_FAILED_RETRIEVING_DIR"), + + Id2Text_t(RESULT_VIVOX_PARAMS_MISSING, "RESULT_VIVOX_PARAMS_MISSING"), + Id2Text_t(RESULT_VIVOX_UNKNOWN_MODE, "RESULT_VIVOX_UNKNOWN_MODE"), + Id2Text_t(RESULT_VIVOX_UNABLE_TO_PARSE_ARG, "RESULT_VIVOX_UNABLE_TO_PARSE_ARG"), + + Id2Text_t(RESULT_VIVOX_ACCOUNT_CREATION_FAILED, "RESULT_VIVOX_ACCOUNT_CREATION_FAILED"), + Id2Text_t(RESULT_VIVOX_ACCOUNT_DEACTIVATE_FAILED, "RESULT_VIVOX_ACCOUNT_DEACTIVATE_FAILED"), + Id2Text_t(RESULT_VIVOX_ACCOUNT_ACTIVATE_FAILED, "RESULT_VIVOX_ACCOUNT_ACTIVATE_FAILED"), + Id2Text_t(RESULT_VIVOX_ACCOUNT_DOESNT_EXIST, "RESULT_VIVOX_ACCOUNT_DOESNT_EXIST"), + Id2Text_t(RESULT_VIVOX_ACCOUNT_RETRIEVAL_FAILED, "RESULT_VIVOX_ACCOUNT_RETRIEVAL_FAILED"), + Id2Text_t(RESULT_VIVOX_ACCOUNT_UPDATE_FAILED, "RESULT_VIVOX_ACCOUNT_UPDATE_FAILED"), + Id2Text_t(RESULT_VIVOX_ACCOUNT_DELETE_FAILED, "RESULT_VIVOX_ACCOUNT_DELETE_FAILED"), + + Id2Text_t(RESULT_VIVOX_RETRIEVING_SRC_USER, "RESULT_VIVOX_RETRIEVING_SRC_USER"), + Id2Text_t(RESULT_VIVOX_CHANNEL_ADD_FAILED, "RESULT_VIVOX_CHANNEL_ADD_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_UPDATE_FAILED, "RESULT_VIVOX_CHANNEL_UPDATE_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_RETRIEVE_FAILED, "RESULT_VIVOX_CHANNEL_RETRIEVE_FAILED"), + + Id2Text_t(RESULT_VIVOX_FEATURE_NAME_UNKNOWN, "RESULT_VIVOX_FEATURE_NAME_UNKNOWN"), + Id2Text_t(RESULT_VIVOX_FEATURE_RETRIEVAL_FAILED, "RESULT_VIVOX_FEATURE_RETRIEVAL_FAILED"), + Id2Text_t(RESULT_VIVOX_FEATURE_SET_FAILED, "RESULT_VIVOX_FEATURE_SET_FAILED"), + Id2Text_t(RESULT_VIVOX_FEATURE_UPDATE_FAILED, "RESULT_VIVOX_FEATURE_UPDATE_FAILED"), + Id2Text_t(RESULT_VIVOX_FEATURE_DELETE_FAILED, "RESULT_VIVOX_FEATURE_DELETE_FAILED"), + Id2Text_t(RESULT_VIVOX_RETRIEVE_USER_FAILED, "RESULT_VIVOX_RETRIEVE_USER_FAILED"), + Id2Text_t(RESULT_VIVOX_RETRIEVE_FEATURE_LIST_FAILED, "RESULT_VIVOX_RETRIEVE_FEATURE_LIST_FAILED"), + + Id2Text_t(RESULT_VIVOX_CHANNEL_RETRIEVE_USERS_FAILED, "RESULT_VIVOX_CHANNEL_RETRIEVE_USERS_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_MUTE_ALL_FAILED, "RESULT_VIVOX_CHANNEL_MUTE_ALL_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_UNMUTE_ALL_FAILED, "RESULT_VIVOX_CHANNEL_UNMUTE_ALL_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_INVITE_FAILED, "RESULT_VIVOX_CHANNEL_INVITE_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_RETRIEVING_ACL_FAILED, "RESULT_VIVOX_CHANNEL_RETRIEVING_ACL_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_DELETE_ACL_FAILED, "RESULT_VIVOX_CHANNEL_DELETE_ACL_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_LOCKING_FAILED, "RESULT_VIVOX_CHANNEL_LOCKING_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_UNLOCKING_FAILED, "RESULT_VIVOX_CHANNEL_UNLOCKING_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_CREATING_FAILED, "RESULT_VIVOX_CHANNEL_CREATING_FAILED"), + Id2Text_t(RESULT_VIVOX_CHANNEL_RETRIEVE_FAILED_EX, "RESULT_VIVOX_CHANNEL_RETRIEVE_FAILED_EX"), + Id2Text_t(RESULT_VIVOX_USER_NOT_ON_CHANNEL, "RESULT_VIVOX_USER_NOT_ON_CHANNEL"), + Id2Text_t(RESULT_VIVOX_UNABLE_TO_COMPLETE_CHANNEL_LOOKUP, "RESULT_VIVOX_UNABLE_TO_COMPLETE_CHANNEL_LOOKUP"), + + Id2Text_t(RESULT_END, "RESULT_END") + }; + static const int _ResultCount = sizeof(_resultString) / sizeof(Id2Text_t); + static Id2TextMap_t ResultString((const Id2TextMap_t::value_type *)&_resultString[0],(const Id2TextMap_t::value_type *)&_resultString[_ResultCount]); + + ////////////////////////////////////////////////////////////////////////////////// + // CommandString + // + static const Id2Text_t _commandString[] = + { + Id2Text_t(MESSAGE_CONNECT, "MSG_CONNECT"), + Id2Text_t(MESSAGE_GET_ACCOUNT_REQUEST, "MESSAGE_GET_ACCOUNT_REQUEST"), + Id2Text_t(MESSAGE_GET_CHANNEL_REQUEST, "MESSAGE_GET_CHANNEL_REQUEST"), + Id2Text_t(MESSAGE_GET_PROXIMITY_CHANNEL_REQUEST, "MESSAGE_GET_PROXIMITY_CHANNEL_REQUEST"), + Id2Text_t(MESSAGE_CHANNEL_COMMAND_REQUEST, "MESSAGE_CHANNEL_COMMAND_REQUEST"), + Id2Text_t(MESSAGE_DEACTIVATE_ACCOUNT_REQUEST, "MESSAGE_DEACTIVATE_ACCOUNT_REQUEST"), + Id2Text_t(MESSAGE_CHANGE_PASSWORD_REQUEST, "MESSAGE_CHANGE_PASSWORD_REQUEST"), + Id2Text_t(MESSAGE_GET_ALL_CHANNELS_REQUEST, "MESSAGE_GET_ALL_CHANNELS_REQUEST"), + Id2Text_t(MESSAGE_DELETE_CHANNEL_REQUEST, "MESSAGE_DELETE_CHANNEL_REQUEST"), + Id2Text_t(MESSAGE_SET_USER_DATA_REQUEST, "MESSAGE_SET_USER_DATA_REQUEST"), + Id2Text_t(MESSAGE_SET_BAN_STATUS_REQUEST, "MESSAGE_SET_BAN_STATUS_REQUEST"), + Id2Text_t(MESSAGE_GET_CHANNEL_INFO_REQUEST, "MESSAGE_GET_CHANNEL_INFO_REQUEST"), + Id2Text_t(MESSAGE_GET_CHANNEL_REQUEST_V2, "MESSAGE_GET_CHANNEL_REQUEST_V2"), + Id2Text_t(MESSAGE_ADD_CHARACTER_CHANNEL, "MESSAGE_ADD_CHARACTER_CHANNEL"), + Id2Text_t(MESSAGE_REMOVE_CHARACTER_CHANNEL, "MESSAGE_REMOVE_CHARACTER_CHANNEL"), + Id2Text_t(MESSAGE_GET_CHARACTER_CHANNEL, "MESSAGE_GET_CHARACTER_CHANNEL"), + Id2Text_t(MESSAGE_UPDATE_CHARACTER_CHANNEL, "MESSAGE_UPDATE_CHARACTER_CHANNEL") + + }; + + static const int _CommandCount = sizeof(_commandString) / sizeof(Id2Text_t); + static Id2TextMap_t CommandString((const Id2TextMap_t::value_type *)&_commandString[0],(const Id2TextMap_t::value_type *)&_commandString[_CommandCount]); + + + + ////////////////////////////////////////////////////////////////////////////////// + // ChannelCommandString + // + static const Id2Text_t _channelCommandString[] = + { + Id2Text_t(COMMAND_MUTE, "mute"), + Id2Text_t(COMMAND_UNMUTE, "unmute"), + Id2Text_t(COMMAND_KICK, "kick"), + Id2Text_t(COMMAND_HANGUP, "hangup"), + Id2Text_t(COMMAND_INVITE, "invite"), + Id2Text_t(COMMAND_BAN, "ban"), + Id2Text_t(COMMAND_UNBAN, "unban"), + Id2Text_t(COMMAND_CHAN_LOCK, "chan_lock"), + Id2Text_t(COMMAND_CHAN_UNLOCK, "chan_unlock"), + Id2Text_t(COMMAND_MUTE_ALL, "mute_all"), + Id2Text_t(COMMAND_UNMUTE_ALL, "unmute_all"), + Id2Text_t(COMMAND_ADD_MODERATOR, "mod_addu"), + Id2Text_t(COMMAND_DELETE_MODERATOR, "mod_delu"), + Id2Text_t(COMMAND_ADD_ACL, "acl_add"), + Id2Text_t(COMMAND_DELETE_ACL, "acl_delete"), + Id2Text_t(COMMAND_DROP_ALL, "drop_all"), + Id2Text_t(COMMAND_END, "COMMAND_END") + }; + + static const int _ChannelCommandCount = sizeof(_channelCommandString) / sizeof(Id2Text_t); + static Id2TextMap_t ChannelCommandString((const Id2TextMap_t::value_type *)&_channelCommandString[0],(const Id2TextMap_t::value_type *)&_channelCommandString[_ChannelCommandCount]); + + +} + + +#endif //_VCHAT_SERVER_API_STRINGS_H_ + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/VChatClient.cpp b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/VChatClient.cpp new file mode 100644 index 00000000..869bf94a --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/VChatClient.cpp @@ -0,0 +1,514 @@ +#include "VChatClient.h" +#include "Base/timer.h" +#include "VChatAPI/strings.h" + +VChatClient::VChatClient(const char *hostlist) +: VChatAPI(hostlist) +, m_isDone(false) +, m_failed(false) +, m_isConnected(false) +, m_result(RESULT_FAILURE) +{ +} + +VChatClient::~VChatClient(void) +{ +} + +unsigned VChatClient::GetAccountEx(const std::string &avatarName, + const std::string &game, + const std::string &world, + unsigned userID, + unsigned avatarID) +{ + + Reset(); + VChatAPI::GetAccount(avatarName, game, world, userID, avatarID, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + return m_result; +} + +void VChatClient::OnGetAccount(unsigned track, + unsigned result, + unsigned userID, + unsigned accountID, + const std::string &voicePassword, + const std::string &encodedVoiceAccount, + const std::string &URI, + void *user) +{ + printf ("OnLogin track = %d, result = %s\n", track, ResultString[result].c_str()); + + printf ("\t UserID(%d)\n\t AccountID(%d)\n\t", + userID, + accountID); + + printf ("\t Password(%s)\n", + voicePassword.c_str()); + printf ("\t EncodedName(%s)\n", + encodedVoiceAccount.c_str()); + printf ("\t URI(%s)\n", + URI.c_str()); + + m_avatarID = accountID; + + m_track = track; + //m_previousUser = encodedVoiceAccount; + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::DeactivateVoiceAccount(const std::string &avatarName, + const std::string &game, + const std::string &world) +{ + + Reset(); + VChatAPI::DeactivateVoiceAccount(avatarName, game, world, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + return m_result; +} + +void VChatClient::OnDeactivateVoiceAccount( unsigned track, + unsigned result, + void *user) +{ + printf ("OnDeactivateVoiceAccount track = %d, result = %s\n", track, ResultString[result].c_str()); + + m_track = track; + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::GetChannelEx( const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent) +{ + Reset(); + VChatAPI::GetChannel(channelName, game, server, description, password, limit, persistent, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnGetChannel( unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user) +{ + printf ("OnGetChannel track = %d, result = %s\n", track, ResultString[result].c_str()); + + printf ("\t ChannelName(%s)\n\t ChannelURI(%s)\n\t ChannelID(%d)\n", + channelName.c_str(), + channelURI.c_str(), + channelID); + + m_channelID = channelID; + m_channelURI = channelURI; + + m_track = track; + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::GetProximityChannelEx(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent, + unsigned maxRange, + unsigned clamping, + float rollOff, + float maxGain, + unsigned distModel) +{ + Reset(); + VChatAPI::GetProximityChannel(channelName, game, server, description, password, limit, persistent, maxRange, clamping, rollOff, maxGain, distModel, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnGetProximityChannel(unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user) +{ + printf ("OnGetProximityChannel track = %d, result = %s\n", track, ResultString[result].c_str()); + + printf ("\t ChannelName(%s)\n\t ChannelURI(%s)\n\t ChannelID(%d)\n", + channelName.c_str(), + channelURI.c_str(), + channelID); + + m_track = track; + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::ChannelCommandEx( const std::string &srcUserName, + const std::string &destUserName, + const std::string &destChannelAddress, + unsigned command, + unsigned banTimeout) +{ + Reset(); + VChatAPI::ChannelCommand(srcUserName, destUserName, destChannelAddress, command, banTimeout, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnChannelCommand( unsigned track, + unsigned result, + void *user) +{ + printf ("OnChannelCommand track = %d, result = %s\n", track, ResultString[result].c_str()); + + m_track = track; + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::ChangePasswordEx(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &password) +{ + Reset(); + VChatAPI::ChangePassword(channelName, game, server, password, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnChangePassword( unsigned track, + unsigned result, + void *user) +{ + printf ("OnChangePassword track = %d, result = %s\n", track, ResultString[result].c_str()); + + m_track = track; + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::GetAllChannelsEx() +{ + Reset(); + VChatAPI::GetAllChannels(NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnGetAllChannels( unsigned track, + unsigned result, + const ChannelVec_t & channels, + void *user) +{ + printf ("OnGetAllChannels track = %d, result = %s\n", track, ResultString[result].c_str()); + + for(unsigned i = 0; i < channels.size(); ++i) + { + printf ("\t%d) ChannelName(%s)\n\t ChannelType(%d)\n\t ChannelURI(%s)\n\t ChannelID(%d)\n", + i+1, + channels[i].m_channelName.c_str(), + channels[i].m_channelType, + channels[i].m_channelURI.c_str(), + channels[i].m_channelID); + } + + m_track = track; + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::DeleteChannelEx(const std::string &channelName, + const std::string &game, + const std::string &server) +{ + Reset(); + VChatAPI::DeleteChannel(channelName, game, server, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnDeleteChannel(unsigned track, + unsigned result, + void *user) +{ + printf ("OnDeleteChannel track = %d, result = %s\n", track, ResultString[result].c_str()); + + m_track = track; + m_result = result; + m_isDone = true; +} + + +unsigned VChatClient::SetBanStatusEx(unsigned userID, + unsigned banStatus) +{ + Reset(); + VChatAPI::SetBanStatus(userID, banStatus, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnSetBanStatus(unsigned track, + unsigned result, + void *user) +{ + printf ("OnSetBanStatus track = %d, result = %s\n", track, ResultString[result].c_str()); + + m_track = track; + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::GetChannelInfoEx( const std::string &channelName, + const std::string &game, + const std::string &server) +{ + Reset(); + VChatAPI::GetChannelInfo(channelName, game, server, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + + +void VChatClient::OnGetChannelInfo( unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user) +{ + printf ("OnGetChannelInfo track = %d, result = %s\n", track, ResultString[result].c_str()); + + printf ("\t ChannelName(%s)\n\t ChannelURI(%s)\n\t ChannelID(%d)\n", + channelName.c_str(), + channelURI.c_str(), + channelID); + + m_track = track; + m_result = result; + m_isDone = true; +} + + +unsigned VChatClient::GetChannelV2Ex(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent) +{ + Reset(); + VChatAPI::GetChannelV2(channelName, game, server, description, password, limit, persistent, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnGetChannelV2( unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + unsigned isNewChannel, + void *user) +{ + printf ("OnGetChannelV2 track = %d, result = %s\n", track, ResultString[result].c_str()); + + printf ("\t ChannelName(%s)\n\t ChannelURI(%s)\n\t ChannelID(%d)\n\t NewChannel(%s)\n", + channelName.c_str(), + channelURI.c_str(), + channelID, + isNewChannel?"true":"false"); + + m_channelID = channelID; + m_channelURI = channelURI; + + m_result = result; + m_isDone = true; +} + + + +unsigned VChatClient::AddCharacterChannelEx(const unsigned stationID, + const unsigned avatarID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + const std::string &channelType, + const std::string &channelDescription, + const std::string &password, + const std::string &channelAddress, + const std::string &locale) +{ + Reset(); + VChatAPI::AddCharacterChannel(stationID, avatarID, characterName, worldName, + gameCode, channelType, channelDescription, + password, channelAddress, locale, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnAddCharacterChannel(unsigned track, + unsigned result, + void *user) +{ + printf ("OnAddCharacterChannel track = %d, result = %s\n", track, ResultString[result].c_str()); + + m_result = result; + m_isDone = true; +} + +unsigned VChatClient::RemoveCharacterChannelEx( const unsigned stationID, + const unsigned avatarID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + const std::string &channelType) +{ + Reset(); + VChatAPI::RemoveCharacterChannel(stationID, avatarID, characterName, worldName, + gameCode, channelType, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnRemoveCharacterChannel(unsigned track, + unsigned result, + void *user) +{ + printf ("OnRemoveCharacterChannel track = %d, result = %s\n", track, ResultString[result].c_str()); + + m_result = result; + m_isDone = true; +} + + +unsigned VChatClient::GetCharacterChannelEx(const unsigned stationID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode) +{ + Reset(); + VChatAPI::GetCharacterChannel(stationID, characterName, worldName, gameCode, NULL); + + while(!IsDone() && !HasFailed()) + { + Process(); + soe::Sleep(10); + } + + return m_result; +} + +void VChatClient::OnGetCharacterChannel(unsigned track, + unsigned result, + const CharacterChannelVec_t &characterChannels, + void * user) +{ + printf ("OnGetCharacterChannel track = %d, result = %s numChannels=%d\n", track, ResultString[result].c_str(), characterChannels.size()); + + for (unsigned i = 0; i < characterChannels.size(); ++i) + { + PrintChannel(characterChannels[i]); + } + + m_result = result; + m_isDone = true; +} + + +void VChatClient::PrintChannel(const CharacterChannel &characterChannel) +{ + printf("Character Channel \n\t m_stationID(%d) \n\t m_avatarID(%d) \n\t m_characterName(%s) \n\t m_worldName(%s) \n", characterChannel.m_stationID, characterChannel.m_avatarID, characterChannel.m_characterName.c_str(), characterChannel.m_worldName.c_str()); + printf("\t m_gameCode(%s) \n\t m_channelType(%s) \n\t m_channelDescription(%s) \n", characterChannel.m_gameCode.c_str(), characterChannel.m_channelType.c_str(), characterChannel.m_channelDescription.c_str()); + printf("\t m_channelAddress(%s) \n\t m_locale(%s) \n\t m_password(%s)\n", characterChannel.m_channelAddress.c_str(), characterChannel.m_locale.c_str(), characterChannel.m_password.c_str()); +} + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/VChatClient.h b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/VChatClient.h new file mode 100644 index 00000000..68bec147 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/VChatClient.h @@ -0,0 +1,235 @@ +#ifndef _VCHAT_CLIENT_H_ +#define _VCHAT_CLIENT_H_ + +#include "VChatAPI/VChatAPI.h" + +using namespace VChatSystem; + +class VChatClient : public VChatAPI +{ +public: + VChatClient(const char * hostList); + virtual ~VChatClient(void); + + bool IsConnected() const { return m_isConnected; } + bool HasFailed() const { return m_failed; } + bool IsDone() const { return m_isDone; } + void Reset() { m_isDone = false; m_failed = false; } + + inline virtual void OnConnectionOpened( const char * address ) + { + m_isConnected = true; + printf("Connection opened to %s\n", address); + } + + inline virtual void OnConnectionFailed( const char * address ) + { + m_failed = true; + m_isConnected = false; + printf("Connection failed to %s\n", address); + } + + inline virtual void OnConnectionClosed( const char * address, const char * reason ) + { + m_isConnected = false; + printf("Connection closed to %s because %s\n", address, reason); + } + + virtual void OnGetAccount(unsigned track, + unsigned result, + unsigned userID, + unsigned accountID, + const std::string &voicePassword, + const std::string &encodedVoiceAccount, + const std::string &URI, + void *user); + + virtual void OnGetChannel(unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user); + + virtual void OnGetProximityChannel( unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user ); + + virtual void OnChannelCommand(unsigned track, + unsigned result, + void *user); + + virtual void OnDeactivateVoiceAccount(unsigned track, + unsigned result, + void *user); + + virtual void OnChangePassword(unsigned track, + unsigned result, + void *user); + + virtual void OnGetAllChannels(unsigned track, + unsigned result, + const ChannelVec_t & channels, + void *user); + + virtual void OnDeleteChannel(unsigned track, + unsigned result, + void *user); + + virtual void OnSetBanStatus(unsigned track, + unsigned result, + void *user); + + virtual void OnGetChannelV2(unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + unsigned isNewChannel, + void *user); + + + ///////////////////////////////////////////////////////////////////////////// + + + unsigned GetAccountEx(const std::string &avatarName, + const std::string &game, + const std::string &world, + unsigned userID, + unsigned avatarID); + + unsigned DeactivateVoiceAccount(const std::string &avatarName, + const std::string &game, + const std::string &world); + + + unsigned GetChannelEx( const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent); + + unsigned GetProximityChannelEx( const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent, + unsigned maxRange, + unsigned clamping, + float rollOff, + float maxGain, + unsigned distModel); + + unsigned ChannelCommandEx(const std::string &srcUserName, + const std::string &destUserName, + const std::string &destChannelAddress, + unsigned command, + unsigned banTimeout); + + + unsigned ChangePasswordEx(const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &password); + + unsigned GetAllChannelsEx(); + + unsigned DeleteChannelEx( const std::string &channelName, + const std::string &game, + const std::string &server); + + unsigned SetBanStatusEx(unsigned userID, + unsigned banStatus); + + + virtual void OnSetUserData( unsigned track, + unsigned result, + void *user) + { + //XXX need to implement + } + + unsigned GetChannelInfoEx(const std::string &channelName, + const std::string &game, + const std::string &server); + + + virtual void OnGetChannelInfo(unsigned track, + unsigned result, + const std::string &channelName, + const std::string &channelURI, + unsigned channelID, + void *user); + + + virtual unsigned GetChannelV2Ex( const std::string &channelName, + const std::string &game, + const std::string &server, + const std::string &description, + const std::string &password, + unsigned limit, + bool persistent); + + unsigned AddCharacterChannelEx( const unsigned stationID, + const unsigned avatarID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + const std::string &channelType, + const std::string &channelDescription, + const std::string &password, + const std::string &channelAddress, + const std::string &locale ); + + virtual void OnAddCharacterChannel(unsigned track, + unsigned result, + void *user); + + unsigned RemoveCharacterChannelEx( const unsigned stationID, + const unsigned avatarID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode, + const std::string &channelType); + + virtual void OnRemoveCharacterChannel(unsigned track, + unsigned result, + void *user); + + unsigned GetCharacterChannelEx(const unsigned stationID, + const std::string &characterName, + const std::string &worldName, + const std::string &gameCode); + + virtual void OnGetCharacterChannel(unsigned track, + unsigned result, + const CharacterChannelVec_t &characterChannels, + void * user); + + + unsigned m_result; + unsigned m_userID; + unsigned m_avatarID; + unsigned m_channelID; + std::string m_channelURI; + unsigned m_track; + std::string m_previousUser; + +private: + void PrintChannel(const CharacterChannel &characterChannel); + bool m_isDone; + bool m_failed; + bool m_isConnected; + +}; + +#endif + + diff --git a/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/main.cpp b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/main.cpp new file mode 100644 index 00000000..90794ce5 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/projects/VChat/VChatUnitTest/main.cpp @@ -0,0 +1,352 @@ +#include +#include +//#include "Clock.h" +#include "Base/cmdLine.h" +#include "Base/timer.h" +#include "VChatClient.h" +#include +#include "Base/stringutils.h" + +//---------------------------------------------------------------------- +//-- this is a SWG hack to work with our modified STLPORT +#ifdef WIN32 +enum MemoryManagerNotALeak +{ + MM_notALeak +}; + +void * __cdecl operator new(size_t size, MemoryManagerNotALeak) +{ + return operator new(size); +} +#endif + +#define HELP_SWITCH "-?" +#define HOST_SWITCH "-h" +#define PORT_SWITCH "-p" +#define USER_SWITCH "-u" +#define TIMEOUT_SWITCH "-t" +#define INTERACTIVE_SWITCH "-i" +#define TEST_INIT_SWITCH "-test" + +void printUsage(const char *executableName) +{ + printf("Usage : %s [" HELP_SWITCH "] " + HOST_SWITCH " hostlist " + "[" TIMEOUT_SWITCH " timeout_in_seconds] " + "[" INTERACTIVE_SWITCH " waits_for_user_to_exit ] " + "[" TEST_INIT_SWITCH " runs_Init_test ]" + "[" USER_SWITCH " used_with -test valid_user_id ]\n", + executableName); +} + +int main(int argc, char **argv){ + + CmdLine cmdLine; + + cmdLine.SplitLine(argc, argv); + + std::string hostList = cmdLine.GetSafeArgument(HOST_SWITCH, 0, "localhost:9100"); + bool showUsage = cmdLine.HasSwitch(HELP_SWITCH) || !cmdLine.HasSwitch(HOST_SWITCH); + unsigned timeout_in_seconds = atoi(cmdLine.GetSafeArgument(TIMEOUT_SWITCH, 0, "10").c_str()); + + if (showUsage) { + printUsage(argv[0]); + return 0; + } + + srand((unsigned)time(0)); + int x = rand(); + int y = rand(); + + y *= x; + + unsigned count = 0; + + char c; + do + { + VChatClient api(hostList.c_str()); + printf("Connecting to %s...\n", hostList.c_str()); + + while(!api.IsConnected() && !api.HasFailed()) + { + api.Process(); + soe::Sleep(10); + } + + y++; + unsigned result = 0; + char numBuff[32]; + sprintf(numBuff, "RandomName%d", y); + + std::string name = numBuff; + std::string game = "EQ2"; + std::string server = "Butcherblock"; + + std::string lowerName = soe::tolowercase(name); + std::string lowerServer = soe::tolowercase(server); + std::string lowerGame = soe::tolowercase(game); + + printf("Login Request: %s\n", name.c_str()); + + // first one adds + printf("GetAccountEx -- user: %s\n", name.c_str()); + + result = api.GetAccountEx(name, game, server, 1111, 9999); + + if (!api.m_previousUser.empty()) + { + std::string tmpName, tmpServer; + + GetUserNameComponents(api.m_previousUser, tmpServer, tmpName); + + printf("GetAccountEx -- user: %s\n", tmpName.c_str()); + + // second one gets. + result = api.GetAccountEx(tmpName, game, tmpServer, 1111, 9999); + } + + + printf("GetAccountEx -- user: %s\n", name.c_str()); + + // second one gets. + result = api.GetAccountEx(name, game, server, 1111, 9999); + +// +// printf("GetAccountEx -- user: %s\n", lowerName.c_str()); +// +// // gets with all lower +// result = api.GetAccountEx(lowerName, lowerGame, lowerServer, 1111, 9999); +// +// printf("GetAccountEx -- user: %s\n", lowerName.c_str()); +// +// +// // gets with lowerName +// result = api.GetAccountEx(lowerName, game, server, 1111, 9999); +// +// +// printf("GetAccountEx -- user: %s\n", name.c_str()); +// +// // gets with lowerGamer. +// result = api.GetAccountEx(name, lowerGame, server, 1111, 9999); +// +// sprintf(numBuff, "Random Name%d", y); +// +// +// +// // creates account with space. +// result = api.GetAccountEx(numBuff, game, lowerServer, 1111, 9999); + +// +// +// // gets account with space. +// result = api.GetAccountEx(numBuff, game, lowerServer, 1111, 9999); + +// +// +// sprintf(numBuff, "Random's Name%d", y); +// +// // creates account with space and apostrophe. +// result = api.GetAccountEx(numBuff, game, lowerServer, 1111, 9999); + + + + std::string guild = "guild."; + guild += name; + + + +// result = api.GetChannelInfoEx(guild, game, server); +// +// +// +// result = api.GetChannelEx(guild, game, server, "Description space", "", 3000, 0); +// +// +// +// result = api.GetChannelEx(guild, game, server, "Description space", "", 3000, 0); + + + std::string guild2 = "guild2."; + guild2 += name; + + + result = api.GetChannelV2Ex(guild2, game, server, "Description space", "firewall", 100, 0); +// +// result = api.GetChannelV2Ex(guild2, game, server, "Description space", "firewall", 100, 0); +// +// +// result = api.ChangePasswordEx(guild, game, server, "sony1234"); +// +// +// +// result = api.GetChannelInfoEx(guild, game, server); +// +// + +// std::string space = "Ga illed."; +// space += name; +// result = api.GetChannelEx(space, game, server, "Description's apostrophe", "", 100, 0); +// +// +// +// result = api.GetChannelEx(space, game, server, "Description's apostrophe", "", 100, 0); +// +// +// +// result = api.DeleteChannelEx(space, game, server); +// +// if (count < 100) +// { +// +// +// std::string proximity = "prox-"; +// proximity += name; +// +// result = api.GetProximityChannelEx(proximity, game, server, "Description", "", 100, 0, 80, 10, 2.5, 1.5, 1); +// +// +// +// result = api.GetProximityChannelEx(proximity, game, server, "Description", "", 100, 0, 80, 10, 2.5, 1.5, 1); +// +// +// +// result = api.DeleteChannelEx(proximity, game, server); +// +// } +// +// char userBuff[512]; +// sprintf (userBuff, "%s.%s", server.c_str(), name.c_str()); +// +// +// char channelBuff[512]; +// sprintf(channelBuff, "%s.%s.guild.%s", game.c_str(), server.c_str(), name.c_str()); +// +// +// +// printf("ChannelCommand user(%s) channel(%s)\n", userBuff, channelBuff); +// +// printf("COMMAND_ADD_MODERATOR "); +// result = api.ChannelCommandEx(userBuff, userBuff, channelBuff, COMMAND_ADD_MODERATOR, 0); +// +// +// +// printf("COMMAND_DELETE_MODERATOR "); +// result = api.ChannelCommandEx(userBuff, userBuff, channelBuff, COMMAND_DELETE_MODERATOR, 0); +// +// +// +// printf("COMMAND_ADD_ACL "); +// result = api.ChannelCommandEx(userBuff, userBuff, channelBuff, COMMAND_ADD_ACL, 0); +// +// +// +// +// printf("COMMAND_DELETE_ACL "); +// result = api.ChannelCommandEx(userBuff, userBuff, channelBuff, COMMAND_DELETE_ACL, 0); +// +// +// +// result = api.DeleteChannelEx(guild, game, server); +// +// +// printf ("\nExpect it return a new channel.\n;"); +// result = api.GetChannelV2Ex(guild, game, server, "Description space", "", 100, 0); +// +// // delete again +// result = api.DeleteChannelEx(guild, game, server); +// +// +// //result = api.DeactivateVoiceAccount(name, game, server); +// +// +// sprintf(numBuff, "Deleter.%d", y); +// +// result = api.GetChannelEx(numBuff, game, server, "My Description", "firewall", 400, 1); +// +// +// +// result = api.GetAllChannelsEx(); +// +// +// +// printf("Deleting Channel %s\n", numBuff); +// +// result = api.DeleteChannelEx(numBuff, game, server); +// +// +// +// result = api.GetAllChannelsEx(); +// +// +// printf("Login user: %s\n", name.c_str()); +// +// // first one adds +// result = api.GetAccountEx(name, game, server, 1111, 0); +// +// +// +// result = api.SetBanStatusEx(1111, BAN); +// +// + + printf("Login after ban -- user: %s\n", name.c_str()); + + // first one adds + result = api.GetAccountEx(name, game, server, 1111, 9999); + + + + result = api.SetBanStatusEx(1111, UNBAN); + + + + printf("Login after UNban -- user: %s\n", name.c_str()); + + // first one adds + result = api.GetAccountEx(name, game, server, 1111, 9999); + + + result = api.AddCharacterChannelEx(1111, 9999, name, server, game, "Guild", + "Guilds for Geldings", "sony1234", "addr", "en_US"); + + + result = api.GetCharacterChannelEx(1111, name, server, game); + +// result = api.UpdateCharacterChannelEx(1111, 9999, name, server, game, "Guild", +// "Guilds for Geldings", api.m_channelID, "secret", api.m_channelURI, "en_US"); +// + result = api.AddCharacterChannelEx(1111, 9999, name, server, game, "Guild", + "Guilds for Geldings", "secret", "addr", "en_US"); + + result = api.GetCharacterChannelEx(1111, name, server, game); + +// result = api.UpdateCharacterChannelEx(1111, 9999, name, server, game, "PartyCentral", +// "Wills for Weldings", api.m_channelID, "secret", api.m_channelURI, "en_US"); +// + result = api.AddCharacterChannelEx(1111, 9999, name, server, game, "PartyCentral", + "Wills for Weldings", "secret", "addr", "en_US"); + + result = api.GetCharacterChannelEx(1111, name, server, game); + + result = api.RemoveCharacterChannelEx(1111, 9999, name, server, game, "Guild"); + + result = api.GetCharacterChannelEx(1111, name, server, game); + + ////////////////////////////////////////////////////////////////////////////////////////////// + printf("Press Enter to go again, x to exit:"); + c = getchar(); + y++; + + count++; + } while (c != 'x'); + + if (cmdLine.HasSwitch(INTERACTIVE_SWITCH)) + { + printf("Press Enter to exit:"); + getchar(); + } + return 0; +} + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/api.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/api.cpp new file mode 100644 index 00000000..c9f69290 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/api.cpp @@ -0,0 +1,1045 @@ +#ifdef WIN32 +#pragma warning (disable: 4786) +#endif + + +#include +#include +#include +#include +#include "Base/timer.h" +#include "Base/serialize.h" +#include "api.h" + +using namespace std; + +#ifdef API_NAMESPACE +namespace API_NAMESPACE +{ +#endif + + const unsigned CONNECT_TIMEOUT = 5000; + const unsigned RECONNECT_TIMEOUT = 5; + + //////////////////////////////////////////////////////////////////////////////// + + TrackedRequest::TrackedRequest() : + mMsgId(0), + mTrackingNumber(0), + mResult(0), + mUserData(0), + mTimeout(0) + { + } + + TrackedRequest::TrackedRequest(unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned duration) : + mMsgId(messageId), + mTrackingNumber(trackingNumber), + mResult(result), + mUserData((void *)userData), + mTimeout(0) + { + mTimeout = (unsigned)time(0) + duration; + } + + //////////////////////////////////////////////////////////////////////////////// + + CommonAPI::CommonAPI(const char * hostList, const char * failoverHostList, unsigned connectionLimit, unsigned maxMsgSize, unsigned bufferSize) + : mManager(NULL) + , mHostReconnectTimeout() + , mIdleHosts() + , mHostMap() + , mActiveHosts() + , mSendIterator() + , mCallbackQueue() + , mRequestMap() + , mTimeoutMap() + , mTimeoutTimer(0) + , mStatusTimer(0) + , mLastRequestInputTime(0) + , mConnectionLimit(connectionLimit) + , mInCallback(false) + , mRetriedConnection(false) + , mShouldTryToConnect(true) + , mTrackingIndex(1) + { +#ifdef UDP_LIBRARY + UdpManager::Params params = GetConnectionParams(); + mManager = new UdpManager(¶ms); +#else + TcpManager::TcpParams params = GetConnectionParams(); + mManager = new TcpManager(params); +#endif + srand((unsigned)time(0)); + if (hostList && hostList[0]) + { + std::vector orderArray; + std::string hosts = hostList; + size_t offset = 0; + do + { + size_t index = hosts.find(' ', offset); + orderArray.push_back(hosts.substr(offset, (index != std::string::npos) ? index-offset : index)); + offset = (index != std::string::npos) ? index+1 : index; + } + while (offset != std::string::npos); + + random_shuffle(orderArray.begin(), orderArray.end()); + while (!orderArray.empty()) + { + mHostReconnectTimeout[orderArray.back()] = 0; + mIdleHosts[0].push_back(orderArray.back()); + orderArray.pop_back(); + } + } + if (failoverHostList && failoverHostList[0]) + { + std::vector orderArray; + std::string hosts = failoverHostList; + size_t offset = 0; + do + { + size_t index = hosts.find(' ', offset); + orderArray.push_back(hosts.substr(offset, (index != std::string::npos) ? index-offset : index)); + offset = (index != std::string::npos) ? index+1 : index; + } + while (offset != std::string::npos); + + random_shuffle(orderArray.begin(), orderArray.end()); + while (!orderArray.empty()) + { + mHostReconnectTimeout[orderArray.back()] = 0; + mIdleHosts[1].push_back(orderArray.back()); + orderArray.pop_back(); + } + } + } + + CommonAPI::~CommonAPI() + { + // ensure that callback mecahnism isn't triggered at this point + HostMap_t::iterator iter; + for (iter = mHostMap[0].begin(); iter != mHostMap[0].end(); iter++) + { + iter->first->SetHandler(0); + iter->first->Release(); + } + for (iter = mHostMap[1].begin(); iter != mHostMap[1].end(); iter++) + { + iter->first->SetHandler(0); + iter->first->Release(); + } + mManager->Release(); + } + + void CommonAPI::DisconnectAll() + { + HostMap_t::iterator iter; + while (!mHostMap[0].empty()) + { + mHostMap[0].begin()->first->Disconnect(); + } + while (!mHostMap[1].empty()) + { + mHostMap[1].begin()->first->Disconnect(); + } + + mShouldTryToConnect = false; + } + + void CommonAPI::AllowConnections() + { + mShouldTryToConnect = true; + } + +#ifdef UDP_LIBRARY + UdpManager::Params CommonAPI::GetConnectionParams() + + { + UdpManager::Params params; + // LAN connection + params.maxRawPacketSize = 1460; + params.outgoingBufferSize = 4 * 1024 * 1024; + params.incomingBufferSize = 4 * 1024 * 1024; + params.keepAliveDelay = 15 * 1000; + params.reliableOverflowBytes = 10 * 1024 * 1024; + params.pooledPacketMax = 5000; + params.crcBytes = 2; + params.icmpErrorRetryPeriod = 2000; + params.allowPortRemapping = false; // set to false (optional) + params.oldestUnacknowledgedTimeout = 15 * 1000; // set to 30000 (longer for debugging tolerance) + params.maxDataHoldTime = 0; // set to 0 (this is critical) + params.reliable[0].maxOutstandingBytes = 2 * 1024 * 1024; // set to 2mb + params.reliable[0].maxOutstandingPackets = 4000; // set to 4000 + params.reliable[0].resendDelayAdjust = 1500; // set to 1500 + params.reliable[0].congestionWindowMinimum = 50000; // set to 50000 + + return params; + } +#else + TcpManager::TcpParams CommonAPI::GetConnectionParams() + + { + TcpManager::TcpParams params; + + params.incomingBufferSize = 512*1024; + params.outgoingBufferSize = 512*1024; + params.allocatorBlockSize = 8*1024; + params.allocatorBlockCount = 1024; + params.maxRecvMessageSize = 2048*1024; + params.keepAliveDelay = 0; + //params.noDataTimeout = 30 * 1000; server only setting + + return params; + } +#endif + + + void CommonAPI::RegisterTrackedMessage(unsigned messageId) + { + mTrackedMessages.insert(messageId); + } + + bool CommonAPI::IsTracked(unsigned messageId) + { + return mTrackedMessages.find(messageId) != mTrackedMessages.end(); + } + + unsigned CommonAPI::GetTrackingNumber() + { + if (!mTrackingIndex) + mTrackingIndex++; + return mTrackingIndex++; + } + + unsigned CommonAPI::GetConnectionCount() const + { + if (!mActiveHosts[0].empty()) + return (unsigned)mActiveHosts[0].size(); + else + return (unsigned)mActiveHosts[1].size(); + } + + unsigned CommonAPI::GetOutstandingRequestCount() const + { + return mRequestMap.size(); + } + + unsigned CommonAPI::GetLastRequestInputTime() const + { + return mLastRequestInputTime; + } + +#ifdef UDP_LIBRARY + void CommonAPI::SendStatusMsg(UdpConnection *connection) +#else + void CommonAPI::SendStatusMsg(TcpConnection *connection) +#endif + { + unsigned char buffer[1024]; + unsigned size = FormatStatusMsg(buffer, sizeof(buffer)); + if (size) + { +#ifdef UDP_LIBRARY + connection->Send(cUdpChannelReliable1, buffer, size); + connection->FlushChannels(); +#else + connection->Send((const char*)buffer, size); +#endif + } + mLastRequestInputTime = time(NULL); + } + +#ifdef UDP_LIBRARY + void CommonAPI::OnConnectComplete(UdpConnection * connection) +#else + void CommonAPI::OnConnectRequest(TcpConnection * connection) +#endif + { + HostMap_t::iterator iter; + if ((iter = mHostMap[0].find(connection)) != mHostMap[0].end()) + { + mActiveHosts[0][connection].mConnection = connection; + mUsableHosts[0].insert(connection); + mSendIterator[0] = mUsableHosts[0].begin(); + } + else if ((iter = mHostMap[1].find(connection)) != mHostMap[1].end()) + { + mActiveHosts[1][connection].mConnection = connection; + mUsableHosts[1].insert(connection); + mSendIterator[1] = mUsableHosts[1].begin(); + } + OnConnectionOpened(iter->second.c_str()); + // send initial status message + SendStatusMsg(connection); + } + +#ifdef UDP_LIBRARY + void CommonAPI::OnTerminated(UdpConnection * connection) +#else + void CommonAPI::OnTerminated(TcpConnection * connection) +#endif + { + std::string host; + HostMap_t::iterator iter; + ApiConnectionInfo * pInfo = FindConnectionInfo(connection); + bool isShuttingDown = pInfo ? (pInfo->mIsShuttingDown && pInfo->mOutstandingRequests.empty()) : false; + + if ((iter = mHostMap[0].find(connection)) != mHostMap[0].end()) + { + if (mActiveHosts[0].erase(connection) != 0) + { + const char * reason = "unknown"; + + if (isShuttingDown) + { + reason = "server shutdown"; + } + else if (!mShouldTryToConnect) + { + reason = "intentional disconnect"; + } + mUsableHosts[0].erase(connection); + mSendIterator[0] = mUsableHosts[0].begin(); + OnConnectionClosed(iter->second.c_str(), reason); + + } + else + { + OnConnectionFailed(iter->second.c_str()); + } + iter->first->Release(); + mIdleHosts[0].push_back(iter->second); + mHostMap[0].erase(iter); + } + else if ((iter = mHostMap[1].find(connection)) != mHostMap[1].end()) + { + if (mActiveHosts[1].erase(connection) != 0) + { + const char * reason = "unknown"; + + if (isShuttingDown) + { + reason = "server shutdown"; + } + else if (!mShouldTryToConnect) + { + reason = "intentional disconnect"; + } + mUsableHosts[1].erase(connection); + mSendIterator[1] = mUsableHosts[1].begin(); + OnConnectionClosed(iter->second.c_str(), reason); + } + else + { + OnConnectionFailed(iter->second.c_str()); + } + iter->first->Release(); + mIdleHosts[1].push_back(iter->second); + mHostMap[1].erase(iter); + } + } + +#ifdef UDP_LIBRARY + void CommonAPI::OnRoutePacket(UdpConnection * connection, const unsigned char * data, int dataLen) +#else + void CommonAPI::OnRoutePacket(TcpConnection * connection, const unsigned char * data, int dataLen) +#endif + { + // read message id + unsigned msgId = CheckMessageId(data, dataLen); + switch(msgId ) + { + case MESSAGE_CONNECT_REPLY: + if (!CheckConnectReply(data, dataLen) && !mRetriedConnection) { + // retry once if bad version + SendStatusMsg(connection); + mRetriedConnection = true; + } + break; + case MESSAGE_SHUTDOWN_NOTIFY_REPLY: + { + ApiConnectionInfo * pInfo = FindConnectionInfo(connection); + HostMap_t::iterator iter = mHostMap[0].find(connection); + std::string hostString("ERROR:0"); + + if (iter != mHostMap[0].end()) { + hostString = iter->second; + mUsableHosts[0].erase(connection); + mSendIterator[0] = mUsableHosts[0].begin(); + } else if ((iter = mHostMap[1].find(connection)) != mHostMap[1].end()) { + hostString = iter->second; + mUsableHosts[1].erase(connection); + mSendIterator[1] = mUsableHosts[1].begin(); + } + // stop sending messages along this connection; it is about to shut down + if (pInfo) { + if ((iter = mHostMap[0].find(connection)) != mHostMap[0].end()) + pInfo->mIsShuttingDown = true; + SendShutdownNotification(pInfo); + OnConnectionShutdownNotified(hostString.c_str(), pInfo->mOutstandingRequests.size()); + } + } + break; + default: + // if tracked message, lookup in request map + if (IsTracked(msgId)) + { + // read tracking number + unsigned trackingNumber = CheckTrackingNumber(data, dataLen); + + RequestMap_t::iterator reqIterator = mRequestMap.find(trackingNumber); + if (reqIterator != mRequestMap.end()) + { + TrackedRequest & request = reqIterator->second; + Callback(data, dataLen, msgId, (void *)request.mUserData); + StopTrackingRequest(trackingNumber); + mRequestMap.erase(reqIterator); + } + } + else if (msgId) + { + Callback(data, dataLen, msgId, 0); + } + break; + } + } + + bool CommonAPI::Send(const unsigned char *data, unsigned dataLen) + { + ApiConnectionInfo * connectionInfo = GetNextUsableConnection(); + + if (connectionInfo) + { +#ifdef UDP_LIBRARY + connectionInfo->mConnection->Send(cUdpChannelReliable1, data, dataLen); +#else + connectionInfo->mConnection->Send((const char*)data, dataLen); +#endif + return true; + } + return false; + } + + void CommonAPI::PrepareCallback(unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned timeout) + { + TrackedRequest request(messageId, trackingNumber, result, userData, timeout); + mRequestMap[trackingNumber] = request; + mLastRequestInputTime = request.mTimeout - timeout; + + if (timeout) + { + mTimeoutMap[timeout].push_back(std::pair(trackingNumber,(unsigned)time(0)+timeout)); + } + else + { + mCallbackQueue.push_back(trackingNumber); + } + } + + bool CommonAPI::SendAndPrepareCallback(const unsigned char *data, unsigned dataLen, unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned timeout) + { + ApiConnectionInfo * connectionInfo = GetNextUsableConnection(); + TrackedRequest request(messageId, trackingNumber, result, userData, timeout); + + mRequestMap[trackingNumber] = request; + mLastRequestInputTime = request.mTimeout - timeout; + mTimeoutMap[timeout].push_back(std::pair(trackingNumber,(unsigned)time(0)+timeout)); + + if (connectionInfo) + { +#ifdef UDP_LIBRARY + connectionInfo->mConnection->Send(cUdpChannelReliable1, data, dataLen); +#else + connectionInfo->mConnection->Send((const char*)data, dataLen); +#endif + // keep track of outstanding tracked requests + connectionInfo->mOutstandingRequests.insert(trackingNumber); + mRequestConnections[trackingNumber] = connectionInfo->mConnection; + + return true; + } + return false; + } + + bool CommonAPI::GetCallbackUserData(unsigned trackingNumber, const void **pUserData) + { + RequestMap_t::iterator reqIterator = mRequestMap.find(trackingNumber); + bool found = false; + + *pUserData = NULL; + if (reqIterator != mRequestMap.end()) + { + TrackedRequest & request = reqIterator->second; + *pUserData = request.mUserData; + found = true; + } + + return found; + } + + void CommonAPI::Process() + { + if (mInCallback) + return; + mInCallback = true; + unsigned currentTime = (unsigned)time(0); + + //////////////////////////////////////// + // Process connection manager + if (mShouldTryToConnect) + { + if (!mIdleHosts[0].empty() && (mHostMap[0].size() < mConnectionLimit || !mConnectionLimit)) + { + std::string hostName = mIdleHosts[0].front(); + mIdleHosts[0].pop_front(); + + ApiConnection * connection = 0; + // check reconnect timeout + if (mHostReconnectTimeout[hostName] <= time(0)) + { + size_t index = hostName.find(':'); + std::string host = hostName.substr(0, index); + int port = (index != std::string::npos) ? atoi(hostName.substr(index+1, index).c_str()) : 0; + + mHostReconnectTimeout[hostName] = time(0) + RECONNECT_TIMEOUT; + connection = mManager->EstablishConnection(host.c_str(), port, CONNECT_TIMEOUT); + if (connection) + { + connection->SetHandler(this); + mHostMap[0][connection] = hostName; + } + else + { + OnConnectionFailed(hostName.c_str()); + } + } + if (!connection) + { + mIdleHosts[0].push_back(hostName); + } + } + if (!mIdleHosts[1].empty() && (mHostMap[1].size() < mConnectionLimit || !mConnectionLimit)) + { + std::string hostName = mIdleHosts[1].front(); + mIdleHosts[1].pop_front(); + ApiConnection * connection = 0; + // check reconnect timeout + if (mHostReconnectTimeout[hostName] <= time(0)) + { + size_t index = hostName.find(':'); + std::string host = hostName.substr(0, index); + int port = (index != std::string::npos) ? atoi(hostName.substr(index+1, index).c_str()) : 0; + + mHostReconnectTimeout[hostName] = time(0) + RECONNECT_TIMEOUT; + connection = mManager->EstablishConnection(host.c_str(), port, CONNECT_TIMEOUT); + if (connection) + { + connection->SetHandler(this); + mHostMap[1][connection] = hostName; + } + else + { + OnConnectionFailed(hostName.c_str()); + } + } + if (!connection) + { + mIdleHosts[1].push_back(hostName); + } + } + } + mManager->GiveTime(); + + //////////////////////////////////////// + // Check for requests that have timed out once per second + if (mTimeoutTimer != currentTime) + { + TimeoutMap_t::iterator iterator; + for (iterator = mTimeoutMap.begin(); iterator != mTimeoutMap.end(); iterator++) + { + TimeoutList_t & timeoutList = iterator->second; + while (!timeoutList.empty() && timeoutList.front().second < currentTime) + { + RequestMap_t::iterator reqIterator = mRequestMap.find(timeoutList.front().first); + if (reqIterator != mRequestMap.end()) + { + TrackedRequest & request = reqIterator->second; + Callback(request.mMsgId, request.mTrackingNumber, request.mResult, (void *)request.mUserData); + StopTrackingRequest(request.mTrackingNumber); + mRequestMap.erase(reqIterator); + } + timeoutList.pop_front(); + } + } + mTimeoutTimer = currentTime; + } + + //////////////////////////////////////// + // Process Callback Queue + while (!mCallbackQueue.empty()) + { + RequestMap_t::iterator reqIterator = mRequestMap.find(mCallbackQueue.front()); + if (reqIterator != mRequestMap.end()) + { + TrackedRequest & request = reqIterator->second; + Callback(request.mMsgId, request.mTrackingNumber, request.mResult, (void *)request.mUserData); + StopTrackingRequest(request.mTrackingNumber); + mRequestMap.erase(reqIterator); + } + mCallbackQueue.pop_front(); + } + + mInCallback = false; + } + + CommonAPI::ApiConnectionInfo * CommonAPI::GetNextUsableConnection() + { + ApiConnectionInfo * connectionInfo = NULL; + + if (!mUsableHosts[0].empty()) + { + connectionInfo = FindConnectionInfo(*mSendIterator[0]); + mSendIterator[0]++; + if (mSendIterator[0] == mUsableHosts[0].end()) + mSendIterator[0] = mUsableHosts[0].begin(); + } + else if (!mUsableHosts[1].empty()) + { + connectionInfo = FindConnectionInfo(*mSendIterator[1]); + mSendIterator[1]++; + if (mSendIterator[1] == mUsableHosts[1].end()) + mSendIterator[1] = mUsableHosts[1].begin(); + } + + return connectionInfo; + } + + CommonAPI::ApiConnectionInfo * CommonAPI::FindConnectionInfo(ApiConnection * connection) + { + // find the connection in the set + ApiConnectionInfo * pInfo = NULL; + ConnectionMap_t::iterator iter; + if ( ((iter = mActiveHosts[0].find(connection)) != mActiveHosts[0].end()) || + ((iter = mActiveHosts[1].find(connection)) != mActiveHosts[1].end()) ) + { + pInfo = &iter->second; + } + + return pInfo; + } + + void CommonAPI::StopTrackingRequest(unsigned trackingNumber) + { + // get the connection on which the request was sent + ConnectionTrackMap_t::iterator trackIter = mRequestConnections.find(trackingNumber); + if (trackIter != mRequestConnections.end()) + { + // stop tracking the request + ApiConnectionInfo * pInfo = FindConnectionInfo(trackIter->second); + if (pInfo) + { + pInfo->mOutstandingRequests.erase(trackingNumber); + + if (pInfo->mIsShuttingDown && pInfo->mOutstandingRequests.empty()) + { + SendShutdownNotification(pInfo); + } + } + mRequestConnections.erase(trackIter); + } + } + + void CommonAPI::SendShutdownNotification(ApiConnectionInfo *pInfo) + { + Message::ShutdownNotify shutdownMessage; + + shutdownMessage.SetUnfinishedRequests(pInfo->mOutstandingRequests.size()); + + unsigned char buffer[1024]; + unsigned size = shutdownMessage.Write(buffer, sizeof(buffer)); + + if (size) + { +#ifdef UDP_LIBRARY + pInfo->mConnection->Send(cUdpChannelReliable1, buffer, size); + pInfo->mConnection->FlushChannels(); +#else + pInfo->mConnection->Send((const char*)buffer, size); +#endif + } + } + + //////////////////////////////////////////////////////////////////////////////// + + VersionMap::InsertionObject::InsertionObject(const std::string &versionString, unsigned enumeratedValue) + { + addVersionPair(versionString, enumeratedValue); + } + + void VersionMap::addVersionPair(const std::string &versionString, unsigned enumeratedValue) + { + initializeMaps(); + + (*mspVersionStringToEnumerationMap)[versionString] = enumeratedValue; + (*mspEnumerationToVersionStringMap)[enumeratedValue] = versionString; + } + + const std::string &VersionMap::getVersionString(unsigned enumeratedValue) + { + initializeMaps(); + + std::map::const_iterator vIter = mspEnumerationToVersionStringMap->find(enumeratedValue); + static std::string blankVersion = ""; + + if (vIter != mspEnumerationToVersionStringMap->end()) { + return vIter->second; + } else { + return blankVersion; + } + } + + unsigned VersionMap::getEnumeratedValue(const std::string &versionString) + { + initializeMaps(); + + std::map::const_iterator vIter = mspVersionStringToEnumerationMap->find(versionString); + unsigned enumeratedValue = 0; + + if (vIter != mspVersionStringToEnumerationMap->end()) { + enumeratedValue = vIter->second; + } + + return enumeratedValue; + } + + const std::string &VersionMap::getEarliestVersionString() + { + initializeMaps(); + + std::map::iterator vIter = mspEnumerationToVersionStringMap->begin(); + static std::string blankVersion = ""; + + if (vIter == mspEnumerationToVersionStringMap->end()) { + return vIter->second; + } else { + return blankVersion; + } + } + + unsigned VersionMap::getEarliestVersionEnumeration() + { + initializeMaps(); + + std::map::iterator vIter = mspVersionStringToEnumerationMap->begin(); + unsigned enumeratedValue = 0; + + if (vIter != mspVersionStringToEnumerationMap->end()) { + enumeratedValue = vIter->second; + } + + return enumeratedValue; + } + + const std::string &VersionMap::getLatestVersionString() + { + initializeMaps(); + + std::map::reverse_iterator vIter = mspEnumerationToVersionStringMap->rbegin(); + static std::string blankVersion = ""; + + if (vIter == mspEnumerationToVersionStringMap->rend()) { + return vIter->second; + } else { + return blankVersion; + } + } + + unsigned VersionMap::getLatestVersionEnumeration() + { + initializeMaps(); + + std::map::reverse_iterator vIter = mspVersionStringToEnumerationMap->rbegin(); + unsigned enumeratedValue = 0; + + if (vIter != mspVersionStringToEnumerationMap->rend()) { + enumeratedValue = vIter->second; + } + + return enumeratedValue; + } + + void VersionMap::initializeMaps() + { + static std::map versionStringToEnumerationMap; + static std::map enumerationToVersionStringMap; + + mspVersionStringToEnumerationMap = &versionStringToEnumerationMap; + mspEnumerationToVersionStringMap = &enumerationToVersionStringMap; + } + + std::map *VersionMap::mspVersionStringToEnumerationMap = NULL; + std::map *VersionMap::mspEnumerationToVersionStringMap = NULL; + + //////////////////////////////////////////////////////////////////////////////// + + ConfigurationMap::InsertionObject::InsertionObject(const std::string &label, std::string &valueHolder) + { + ConfigurationMap::ConfigurationEntry entry(label, &valueHolder, eValueTypeString); + addConfigurationEntry(entry); + } + + ConfigurationMap::InsertionObject::InsertionObject(const std::string &label, soe::uint32 &valueHolder) + { + ConfigurationMap::ConfigurationEntry entry(label, &valueHolder, eValueTypeUInt32); + addConfigurationEntry(entry); + } + + ConfigurationMap::InsertionObject::InsertionObject(const std::string &label, soe::int32 &valueHolder) + { + ConfigurationMap::ConfigurationEntry entry(label, &valueHolder, eValueTypeInt32); + addConfigurationEntry(entry); + } + + ConfigurationMap::InsertionObject::InsertionObject(const std::string &label, soe::uint16 &valueHolder) + { + ConfigurationMap::ConfigurationEntry entry(label, &valueHolder, eValueTypeUInt16); + addConfigurationEntry(entry); + } + + ConfigurationMap::InsertionObject::InsertionObject(const std::string &label, soe::int16 &valueHolder) + { + + } + + ConfigurationMap::InsertionObject::InsertionObject(const std::string &label, double &valueHolder) + { + ConfigurationMap::ConfigurationEntry entry(label, &valueHolder, eValueTypeDouble); + addConfigurationEntry(entry); + } + + ConfigurationMap::InsertionObject::InsertionObject(const std::string &label, float &valueHolder) + { + ConfigurationMap::ConfigurationEntry entry(label, &valueHolder, eValueTypeFloat); + addConfigurationEntry(entry); + } + + ConfigurationMap::ConfigurationEntry::ConfigurationEntry(const std::string &labelIn, void * valueHolderIn, EValueType valueTypeIn) + : label(labelIn) + , valueHolder(valueHolderIn) + , valueType(valueTypeIn) + { } + + void ConfigurationMap::addConfigurationEntry(const ConfigurationMap::ConfigurationEntry &entry) + { + initializeMap(); + + (*mspLabelToEntryMap)[entry.label] = entry; + } + + std::string api_uitoa(soe::uint64 number) + { + char buffer[256]; + + sprintf(buffer, FMT_UINT64, number); + + return buffer; + } + + std::string api_itoa(soe::int64 number) + { + char buffer[256]; + + sprintf(buffer, FMT_INT64, number); + + return buffer; + } + + std::string api_ftoa(double number) + { + char buffer[1024]; + + sprintf(buffer, "%f", number); + + return buffer; + } + + void ConfigurationMap::getConfigurationEntries(soe::NameValuePairs_t &labelsAndValues) + { + initializeMap(); + + size_t i = labelsAndValues.size(); + + labelsAndValues.resize(labelsAndValues.size() + mspLabelToEntryMap->size()); + for (LabelToEntryMap_t::const_iterator it = mspLabelToEntryMap->begin(); it != mspLabelToEntryMap->end(); it++, i++) + { + string value; + + labelsAndValues[i].name = it->second.label; + + switch(it->second.valueType) + { + case eValueTypeString: + { + const std::string * pStr = (const std::string *)it->second.valueHolder; + + value = *pStr; + } + break; + case eValueTypeUInt32: + { + const soe::uint32 * pInt = (const soe::uint32 *)it->second.valueHolder; + + value = api_uitoa(*pInt); + } + break; + case eValueTypeInt32: + { + const soe::int32 * pInt = (const soe::int32 *)it->second.valueHolder; + + value = api_itoa(*pInt); + } + break; + case eValueTypeUInt16: + { + const soe::uint16 * pInt = (const soe::uint16 *)it->second.valueHolder; + + value = api_uitoa(*pInt); + } + break; + case eValueTypeInt16: + { + const soe::int16 * pInt = (const soe::int16 *)it->second.valueHolder; + + value = api_itoa(*pInt); + } + break; + case eValueTypeDouble: + { + const double * pFloat = (const double *)it->second.valueHolder; + + value = api_ftoa(*pFloat); + } + break; + case eValueTypeFloat: + { + const float * pFloat = (const float *)it->second.valueHolder; + + value = api_ftoa(*pFloat); + } + break; + default: + break; + } + labelsAndValues[i].value = value; + } + } + + void ConfigurationMap::setConfigurationEntries(const soe::NameValuePairs_t &labelsAndValues) + { + initializeMap(); + + for (soe::NameValuePairs_t::const_iterator it = labelsAndValues.begin(); it != labelsAndValues.end(); it++) + { + LabelToEntryMap_t::iterator mIter = mspLabelToEntryMap->find(it->name); + + if (mIter != mspLabelToEntryMap->end()) { + + switch(mIter->second.valueType) + { + case eValueTypeString: + { + std::string * pStr = (std::string *)mIter->second.valueHolder; + + *pStr = it->value; + } + break; + case eValueTypeUInt32: + { + soe::uint32 * pInt = (soe::uint32 *)mIter->second.valueHolder; + + *pInt = (soe::uint32)atoi64(it->value.c_str()); + } + break; + case eValueTypeInt32: + { + soe::int32 * pInt = (soe::int32 *)mIter->second.valueHolder; + + *pInt = (soe::int32)atoi64(it->value.c_str()); + } + break; + case eValueTypeUInt16: + { + soe::uint16 * pInt = (soe::uint16 *)mIter->second.valueHolder; + + *pInt = (soe::uint16)atoi64(it->value.c_str()); + } + break; + case eValueTypeInt16: + { + soe::int16 * pInt = (soe::int16 *)mIter->second.valueHolder; + + *pInt = (soe::int16)atoi64(it->value.c_str()); + } + break; + case eValueTypeDouble: + { + double * pFloat = (double *)mIter->second.valueHolder; + + *pFloat = atof(it->value.c_str()); + } + break; + case eValueTypeFloat: + { + float * pFloat = (float *)mIter->second.valueHolder; + + *pFloat = atof(it->value.c_str()); + } + break; + default: + break; + } + } + } + } + + void ConfigurationMap::initializeMap() + { + static LabelToEntryMap_t labelToEntryMap; + + mspLabelToEntryMap = &labelToEntryMap; + } + + ConfigurationMap::LabelToEntryMap_t *ConfigurationMap::mspLabelToEntryMap = NULL; + + //////////////////////////////////////////////////////////////////////////////// + + typedef std::set ClassScribeSet_t; + ClassScribeSet_t classScribeSet; + + void AddClassScribeToGlobalSet(soe::ClassScribeBase * pScribe) + { + classScribeSet.insert(pScribe); + } + + /************************************************************************/ + /* Call this function after changing any configurable variables */ + /* that affect class serialization */ + /************************************************************************/ + void ForceReinitializationOfAllClassScribes() + { + ClassScribeSet_t::iterator scribeIter; + + for (scribeIter = classScribeSet.begin(); scribeIter != classScribeSet.end(); scribeIter++) + { + // This will cause the scribe to reinitialize itself next time it's used, + // thereby copying the new values. Storing references was too dangerous. + (*scribeIter)->SetInitialized(false); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +#ifdef API_NAMESPACE +} +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/api.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/api.h new file mode 100644 index 00000000..37512cbd --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/api.h @@ -0,0 +1,266 @@ +//#ifndef BASE__API_H +//#define BASE__API_H + +#include +#include +#include +#include +#include "Base/profile.h" +#include "Base/stringutils.h" + +#ifdef UDP_LIBRARY +#include "UdpLibrary/UdpLibrary.hpp" +#else +#include "TcpLibrary/TcpConnection.h" +#include "TcpLibrary/TcpManager.h" +#endif +#include "apiMessages.h" + +#ifndef API_NAMESPACE +#pragma message ("api.h: API_NAMESPACE undefined") +//#else +//#pragma message ("api.h: API_NAMESPACE:") +//#pragma message API_NAMESPACE +#endif + +#ifdef API_NAMESPACE +namespace API_NAMESPACE +{ +#endif + + //////////////////////////////////////////////////////////////////////////////// + + struct TrackedRequest + { + TrackedRequest(); + TrackedRequest(unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned timeout = 5); + + unsigned mMsgId; + unsigned mTrackingNumber; + unsigned mResult; + void * mUserData; + unsigned mTimeout; + }; + + //////////////////////////////////////////////////////////////////////////////// + +#ifdef UDP_LIBRARY + class CommonAPI : UdpConnectionHandler +#else + class CommonAPI : TcpConnectionHandler +#endif + { +#ifdef UDP_LIBRARY + typedef UdpConnection ApiConnection; +#else + typedef TcpConnection ApiConnection; +#endif + typedef std::set TrackingSet_t; + struct ApiConnectionInfo + { + ApiConnectionInfo(ApiConnection * Connection = NULL) : mConnection(Connection), mIsShuttingDown(false) { } + + ApiConnection * mConnection; + TrackingSet_t mOutstandingRequests; + bool mIsShuttingDown; + }; + typedef std::map HostMap_t; + typedef std::set ConnectionSet_t; + typedef std::map ConnectionMap_t; + typedef std::map RequestMap_t; + typedef std::list > TimeoutList_t; + typedef std::map ConnectionTrackMap_t; + typedef std::map TimeoutMap_t; + typedef std::list TrackingList_t; + typedef std::set MessagIdSet_t; + public: + CommonAPI(const char * hostList, const char * failoverHostList = 0, unsigned connectionLimit = 0, unsigned maxMsgSize = 1*1024, unsigned bufferSize = 64*1024); + virtual ~CommonAPI(); + + void Process(); + void DisconnectAll(); + void AllowConnections(); + unsigned GetConnectionCount() const; + unsigned GetOutstandingRequestCount() const; + unsigned GetLastRequestInputTime() const; + unsigned GetTrackingNumber(); + bool Send(const unsigned char *data, unsigned dataLen); + void PrepareCallback(unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned timeout = 0); + bool SendAndPrepareCallback(const unsigned char *data, unsigned dataLen, unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned timeout); + bool GetCallbackUserData(unsigned trackingNumber, const void **pUserData); + + protected: +#ifdef UDP_LIBRARY + virtual UdpManager::Params GetConnectionParams(); + virtual void OnConnectComplete(UdpConnection *con); + virtual void OnTerminated(UdpConnection *con); + virtual void OnRoutePacket(UdpConnection *con, const uchar *data, int dataLen); + void SendStatusMsg(UdpConnection *); +#else + virtual TcpManager::TcpParams GetConnectionParams(); + virtual void OnConnectRequest(TcpConnection *con); + virtual void OnTerminated(TcpConnection *con); + virtual void OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen); + void SendStatusMsg(TcpConnection *); +#endif + + virtual unsigned CheckMessageId(const unsigned char * data, unsigned dataLen) = 0; + virtual unsigned CheckTrackingNumber(const unsigned char * data, unsigned dataLen) = 0; + virtual bool CheckConnectReply(const unsigned char * data, unsigned dataLen) { return true; } + + bool IsTracked(unsigned msgId); + void RegisterTrackedMessage(unsigned msgId); + // FormatStatusMsg provides a buffer/size for the derived class to write to and returns the size of the message written + // This function is called after connecting and periodically once connected. + virtual unsigned FormatStatusMsg(unsigned char * buffer, unsigned bufferLen) = 0; + + virtual void OnConnectionOpened(const char * address) = 0; + virtual void OnConnectionFailed(const char * address) = 0; + virtual void OnConnectionClosed(const char * address, const char * reason) = 0; + virtual void OnConnectionShutdownNotified(const char * address, unsigned outstandingRequests) { } + virtual void Callback(const unsigned char * data, unsigned dataLen, unsigned messageId, void * userData) = 0; + virtual void Callback(unsigned messageId, unsigned trackingNumber, unsigned result, void * userData) = 0; + + private: + ApiConnectionInfo * GetNextUsableConnection(); + ApiConnectionInfo * FindConnectionInfo(ApiConnection * connection); + void StopTrackingRequest(unsigned trackingNumber); + void SendShutdownNotification(ApiConnectionInfo *pInfo); +#ifdef UDP_LIBRARY + UdpManager * mManager; +#else + TcpManager * mManager; +#endif + std::map mHostReconnectTimeout; + std::list mIdleHosts[2]; + HostMap_t mHostMap[2]; + ConnectionMap_t mActiveHosts[2]; + ConnectionSet_t mUsableHosts[2]; + ConnectionSet_t::iterator mSendIterator[2]; + ConnectionTrackMap_t mRequestConnections; + TrackingList_t mCallbackQueue; + RequestMap_t mRequestMap; + TimeoutMap_t mTimeoutMap; + unsigned mTrackingIndex; + MessagIdSet_t mTrackedMessages; + unsigned mTimeoutTimer; + unsigned mStatusTimer; + unsigned mLastRequestInputTime; + unsigned mConnectionLimit; + unsigned mMaxMsgSize; + unsigned mBufferSize; + bool mInCallback; + bool mRetriedConnection; + bool mShouldTryToConnect; + }; + + //////////////////////////////////////////////////////////////////////////////// + + class DECLSPEC VersionMap + { + public: + class InsertionObject + { + public: + InsertionObject(const std::string &versionString, unsigned enumeratedValue); + ~InsertionObject() { } + }; + + static const std::string &getEarliestVersionString(); + static unsigned getEarliestVersionEnumeration(); + static const std::string &getLatestVersionString(); + static unsigned getLatestVersionEnumeration(); + + static const std::string &getVersionString(unsigned enumeratedValue); + static unsigned getEnumeratedValue(const std::string &versionString); + static void addVersionPair(const std::string &versionString, unsigned enumeratedValue); + + private: + VersionMap() { } + ~VersionMap() { } + + static void initializeMaps(); + + static std::map *mspVersionStringToEnumerationMap; + static std::map *mspEnumerationToVersionStringMap; + }; + + inline bool IsAtLeastVersion(unsigned version, unsigned test) + { + return ((version != 0) && (version >= test)); + } + + inline bool IsBeforeVersion(unsigned version, unsigned test) + { + return ((version != 0) && (version < test)); + } + +#define ADD_VERSION_SUPPORT(__name__, __number__) VersionMap::InsertionObject supportsVersion ## __number__(__name__, __number__) + + class DECLSPEC ConfigurationMap + { + public: + class InsertionObject + { + public: + InsertionObject(const std::string &label, std::string &valueHolder); + InsertionObject(const std::string &label, soe::uint32 &valueHolder); + InsertionObject(const std::string &label, soe::int32 &valueHolder); + InsertionObject(const std::string &label, soe::uint16 &valueHolder); + InsertionObject(const std::string &label, soe::int16 &valueHolder); + InsertionObject(const std::string &label, double &valueHolder); + InsertionObject(const std::string &label, float &valueHolder); + ~InsertionObject() { } + }; + + enum EValueType + { + eValueTypeUnknown, + eValueTypeString, + eValueTypeUInt32, + eValueTypeInt32, + eValueTypeUInt16, + eValueTypeInt16, + eValueTypeDouble, + eValueTypeFloat + }; + + struct ConfigurationEntry + { + ConfigurationEntry() : valueType(eValueTypeUnknown) {} + ConfigurationEntry(const std::string &labelIn, void * valueHolderIn, EValueType valueTypeIn); + + std::string label; + void * valueHolder; + EValueType valueType; + }; + + static void addConfigurationEntry(const ConfigurationEntry &entry); + static void getConfigurationEntries(soe::NameValuePairs_t &labelsAndValues); + static void setConfigurationEntries(const soe::NameValuePairs_t &labelsAndValues); + + private: + ConfigurationMap() { } + ~ConfigurationMap() { } + + static void initializeMap(); + + typedef std::map LabelToEntryMap_t; + static LabelToEntryMap_t *mspLabelToEntryMap; + }; + +#define ADD_CONFIG_VAR(Type, Name, Default) \ + DECLSPEC Type Name = Default; \ + ConfigurationMap::InsertionObject configVariable ## Name(#Name, Name); + + /************************************************************************/ + /* Call this function after changing any configurable variables */ + /* that affect class serialization */ + /************************************************************************/ + void ForceReinitializationOfAllClassScribes(); + +#ifdef API_NAMESPACE +} +#endif + +//#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMacros.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMacros.h new file mode 100644 index 00000000..f250cf9d --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMacros.h @@ -0,0 +1,145 @@ +#ifndef COMMON_API__MESSAGE_MACROS_H +#define COMMON_API__MESSAGE_MACROS_H + + +#define DefineMessageBegin(ClassName,BaseClass,MessageID) \ + class DECLSPEC ClassName : public BaseClass \ + { \ + enum { MESSAGE_ID = MessageID }; \ + private: \ + class Entry \ + { \ + public: \ + Entry(); \ + virtual ~Entry(); \ + }; \ + \ + static Entry ms_classEntry; \ + void InitializeMembers(); \ + public: \ + ClassName(); \ + ClassName(const unsigned messageId); \ + ClassName(const ClassName & source); \ + virtual Basic * Clone() const; \ + virtual Basic * Clone(StorageVector_t &storageVector) const; \ + virtual const char * MessageName() const { return #ClassName; } \ + virtual const char * MessageIDString() const { return #MessageID; } + +#if defined(PRINTABLE_MESSAGES) || defined(TRACK_READ_WRITE_FAILURES) +# define DefineMessageMember(MemberName,Type) \ + public: \ + class Print##MemberName : public soe::AutoVariable \ + { \ + public: \ + virtual const char * VariableName() const { return #MemberName; } \ + }; \ + public: \ + Print##MemberName m##MemberName; \ + public: \ + const Type & Get##MemberName() const \ + { return m##MemberName.Get(); } \ + Type & Get##MemberName() \ + { return m##MemberName.Get(); } \ + void Set##MemberName(const Type & value) \ + { m##MemberName.Set((const Type &)value); } + +#else +# define DefineMessageMember(MemberName,Type) \ + public: \ + soe::AutoVariable m##MemberName; \ + public: \ + const Type & Get##MemberName() const \ + { return m##MemberName.Get(); } \ + Type & Get##MemberName() \ + { return m##MemberName.Get(); } \ + void Set##MemberName(const Type & value) \ + { m##MemberName.Set((const Type &)value); } + +#endif + +#define DefineMessageEnd \ + }; + +#define ImplementMessageBegin(ClassName,BaseClass) \ + ClassName::ClassName() : BaseClass(MESSAGE_ID) \ + { \ + InitializeMembers(); \ + } \ + ClassName::ClassName(const unsigned messageId) : BaseClass(messageId) \ + { \ + InitializeMembers(); \ + } \ + ClassName::ClassName(const ClassName & source) \ + { \ + InitializeMembers(); \ + \ + MemberVector_t::iterator iterator; \ + MemberVector_t::const_iterator sourceIterator = source.mMembers.begin(); \ + for(iterator=mMembers.begin(); iterator!=mMembers.end(); iterator++, sourceIterator++) \ + { \ + iterator->data->Copy(sourceIterator->data); \ + iterator->size = sourceIterator->size; \ + } \ + } \ + Basic * ClassName::Clone() const \ + { \ + ClassName * msg = new ClassName(*this); \ + /*msg->InitializeMembers(); */ \ + return msg; \ + } \ + Basic * ClassName::Clone(StorageVector_t & storageVector) const \ + { \ + ClassName * msg = new(storageVector) ClassName(*this); \ + /*msg->InitializeMembers(); */ \ + return msg; \ + } \ + ClassName::Entry::Entry() \ + { \ + ClassName msg; \ + \ + AddClassMapEntry(msg.GetMsgId(), &msg); \ + } \ + ClassName::Entry::~Entry() \ + { \ + } \ + ClassName::Entry ClassName::ms_classEntry; \ + void ClassName::InitializeMembers() \ + { + +#define ImplementMessageMember(MemberName,DefaultValue) \ + m##MemberName.Set(DefaultValue); \ + Insert(m##MemberName); + +#define ImplementMessageMemberEx(MemberName,DefaultValue,MaxLen) \ + m##MemberName.Set(DefaultValue); \ + Insert(m##MemberName, MaxLen); + +#define ImplementVersionAddedMessageMember(MemberName,DefaultValue,Version) \ + m##MemberName.Set(DefaultValue); \ + Insert(m##MemberName, 1, Version, eAdded); + +#define ImplementVersionDroppedMessageMember(MemberName,DefaultValue,Version) \ + m##MemberName.Set(DefaultValue); \ + Insert(m##MemberName, 1, Version, eDropped); + +#define ImplementVersionAddedMessageMemberEx(MemberName,DefaultValue,MaxLen,Version) \ + m##MemberName.Set(DefaultValue); \ + Insert(m##MemberName, MaxLen, Version, eAdded); + +#define ImplementVersionDroppedMessageMemberEx(MemberName,DefaultValue,MaxLen,Version) \ + m##MemberName.Set(DefaultValue); \ + Insert(m##MemberName, MaxLen, Version, eDropped); + +#define ImplementMessageMemberOptional(MemberName,DefaultValue) \ + m##MemberName.Set(DefaultValue); \ + InsertOptional(m##MemberName); + +#define ImplementMessageMemberOptionalEx(MemberName,DefaultValue,MaxLen) \ + m##MemberName.Set(DefaultValue); \ + InsertOptional(m##MemberName, MaxLen); + +#define ImplementMessageEnd \ + } + +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMessages.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMessages.cpp new file mode 100644 index 00000000..c38875ab --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMessages.cpp @@ -0,0 +1,553 @@ +#pragma warning (disable: 4514 4786) + +#include "apiMessages.h" + + +namespace API_NAMESPACE +{ + + + namespace Message + { + + +//////////////////////////////////////////////////////////////////////////////// + + const std::map & Base::GetMessageMap() { return *ms_pClassMap; } + + // returns the number of bytes Read, or zero if an error occured + unsigned Base::Read(const unsigned char * stream, unsigned size, unsigned version) + { + unsigned bytesTotal = 0; + bool succeeded = true; + MemberVector_t::iterator iterator; +#ifdef TRACK_READ_WRITE_FAILURES + soe::ClearMessageFailureStack(); +#endif + for(iterator=mMembers.begin(); iterator!=mMembers.end(); iterator++) + { + MemberInfo_t & memberInfo = *iterator; + + if (IsMemberIncluded(version, memberInfo.version, memberInfo.effect)) + { + unsigned bytes = memberInfo.data->Read(stream, size, memberInfo.size, version); + if (bytes == 0) + { +#ifdef TRACK_READ_WRITE_FAILURES + soe::PushMessageFailure(soe::PrintToString("%s, %s:%u - failed to read %s.%s, size = %u, version = %u", __FUNCTION__, __FILE__, __LINE__, MessageName(), memberInfo.data->VariableName(), size, version)); +#endif + bytesTotal = 0; + succeeded = false; + break; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + } + if (succeeded) + { + for(iterator=mOptionalMembers.begin(); iterator!=mOptionalMembers.end(); iterator++) + { + MemberInfo_t & memberInfo = *iterator; + unsigned bytes = memberInfo.data->Read(stream, size, memberInfo.size, version); + if (bytes == 0) + { + // break, but don't fail the reading process + break; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + } + return bytesTotal; + } + + // returns the number of bytes written, or zero if an error occured + unsigned Base::Write(unsigned char * stream, unsigned size, unsigned version) const + { + unsigned bytesTotal = 0; + bool succeeded = true; + MemberVector_t::const_iterator iterator; +#ifdef TRACK_READ_WRITE_FAILURES + soe::ClearMessageFailureStack(); +#endif + + for (iterator=mMembers.begin(); iterator!=mMembers.end(); iterator++) + { + const MemberInfo_t & memberInfo = *iterator; + + if (IsMemberIncluded(version, memberInfo.version, memberInfo.effect)) + { + unsigned bytes = memberInfo.data->Write(stream, size, version); + if (bytes == 0) + { +#ifdef TRACK_READ_WRITE_FAILURES + soe::PushMessageFailure(soe::PrintToString("%s, %s:%u - failed to write %s.%s, size = %u, version = %u", __FUNCTION__, __FILE__, __LINE__, MessageName(), memberInfo.data->VariableName(), size, version)); +#endif + bytesTotal = 0; + succeeded = false; + break; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + + } + if (succeeded) + { + for (iterator=mOptionalMembers.begin(); iterator!=mOptionalMembers.end(); iterator++) + { + const MemberInfo_t & memberInfo = *iterator; + unsigned bytes = memberInfo.data->Write(stream, size, version); + if (bytes == 0) + { + // break, but don't fail the writing process + break; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + } + return bytesTotal; + } + +#ifdef PRINTABLE_MESSAGES + int Base::Print(char * stream, unsigned size, unsigned maxDepth) const + { + int bytesTotal = 0; + int bytes = 0; + + if (maxDepth == 0) { + bytes = snprintf(stream, size, "%s{mMembers(%u), mOptionalMembers(%u)}", MessageName(), mMembers.size(), mOptionalMembers.size()); + return bytes; + } + + bytes = snprintf(stream, size, "{"); + + if (soe::FailedToPrint(bytes, size)) { + bytesTotal = bytes; + return bytesTotal; + } else { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + + bool printedOne = false; + MemberVector_t::const_iterator iterator; + for (iterator=mMembers.begin(); iterator!=mMembers.end(); iterator++) + { + int bytes = 0; + + if (printedOne) { + bytes = snprintf(stream, size, ", "); + if (soe::FailedToPrint(bytes, size)) { + bytesTotal = bytes; + return bytesTotal; + } else { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } else { + printedOne = true; + } + + const MemberInfo_t & memberInfo = *iterator; + + bytes = memberInfo.data->Print(stream, size, maxDepth-1); + if (soe::FailedToPrint(bytes, size)) + { + bytesTotal = bytes; + return bytesTotal; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + for (iterator=mOptionalMembers.begin(); iterator!=mOptionalMembers.end(); iterator++) + { + int bytes = 0; + + if (printedOne) { + bytes = snprintf(stream, size, ", "); + if (soe::FailedToPrint(bytes, size)) { + bytesTotal = bytes; + return bytesTotal; + } else { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } else { + printedOne = true; + } + + const MemberInfo_t & memberInfo = *iterator; + bytes = memberInfo.data->Print(stream, size, maxDepth-1); + if (soe::FailedToPrint(bytes, size)) + { + bytesTotal = bytes; + return bytesTotal; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + if (!soe::FailedToPrint(bytes, size)) { + bytes = snprintf(stream, size, "}"); + + if (soe::FailedToPrint(bytes, size)) { + bytesTotal = bytes; + } else { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + return bytesTotal; + } +#endif + + void * Base::operator new(size_t size) + { + return ::operator new(size); + } + + void * Base::operator new(size_t size, void *ptr) + { + return ::operator new(size, ptr); + } + + Base * Base::Clone() const + { + return new Base(*this); + } + + Base * Base::Clone(StorageVector_t &storageVector) const + { + return new(storageVector) Base(*this); + } + + Base * Base::Create(unsigned MsgId, StorageVector_t *pStorageVector) + { + Base * msg = NULL; + + if (ms_pClassMap) { + std::map::iterator classIter = ms_pClassMap->find(MsgId); + + if (classIter != ms_pClassMap->end()) { + if (pStorageVector) { + msg = classIter->second->Clone(*pStorageVector); + } else { + msg = classIter->second->Clone(); + } + } + } + + return msg; + } + + const char * Base::GetMessageName(unsigned msgId) + { + const char * requestString = NULL; + + if (ms_pClassMap) { + std::map::iterator classIter = ms_pClassMap->find(msgId); + + if (classIter != ms_pClassMap->end()) { + requestString = classIter->second->MessageName(); + } + } + + return requestString; + } + + const char * Base::GetMessageIDString(unsigned msgId) + { + const char * idString = NULL; + + if (ms_pClassMap) { + std::map::iterator classIter = ms_pClassMap->find(msgId); + + if (classIter != ms_pClassMap->end()) { + idString = classIter->second->MessageIDString(); + } + } + + return idString; + } + + void Base::AddClassMapEntry(unsigned msgId, const Base * msg) + { + static std::map classMap; + + if (ms_pClassMap == NULL) { + ms_pClassMap = &classMap; + } + + std::map::iterator classIter = ms_pClassMap->find(msgId); + + if (classIter == ms_pClassMap->end()) { + (*ms_pClassMap)[msgId] = msg; + } + } + + void * Base::operator new(size_t storageSize, StorageVector_t &storageVector) + { + storageSize = (storageSize / sizeof(Storage_t)) + 1; + if (storageVector.size() < storageSize) { + storageVector.resize(storageSize); + } + + return &storageVector[0]; + } + + Base::DeepPointer::DeepPointer() : + m_ptr(NULL) + { + } + + Base::DeepPointer::DeepPointer(const Base & source) : + m_ptr(NULL) + { + m_ptr = source.Clone(m_storageVector); + } + + Base::DeepPointer::DeepPointer(const Base * source) : + m_ptr(NULL) + { + m_ptr = source->Clone(m_storageVector); + } + + Base::DeepPointer::DeepPointer(const DeepPointer & source) : + m_ptr(NULL) + { + if (source.m_ptr) { m_ptr = source->Clone(m_storageVector); } + } + + Base::DeepPointer::~DeepPointer() + { + if (m_ptr) { m_ptr->~Base(); } + } + + void Base::DeepPointer::SetMessageType(unsigned MsgId) + { + if (m_ptr) { m_ptr->~Base(); } + + m_ptr = Create(MsgId, &m_storageVector); + } + + Base::DeepPointer & Base::DeepPointer::operator=(const Base * rhs) + { + Base *oldPtr = m_ptr; + + m_ptr = rhs ? rhs->Clone(m_storageVector) : NULL; + + if (oldPtr) { + oldPtr->~Base(); + } + + return *this; + } + + Base::DeepPointer & Base::DeepPointer::operator=(const DeepPointer & rhs) + { + Base *oldPtr = m_ptr; + + m_ptr = rhs.m_ptr ? rhs->Clone(m_storageVector) : NULL; + + if (oldPtr) { + oldPtr->~Base(); + } + + return *this; + } + + Base * Base::DeepPointer::operator->() const + { + return m_ptr; + } + + Base & Base::DeepPointer::operator*() const + { + return *m_ptr; + } + + std::map *Base::ms_pClassMap = NULL; + +#if defined(PRINTABLE_MESSAGES) || defined(TRACK_READ_WRITE_FAILURES) + Basic::Basic() + { + mMsgId.Set(0); + Insert(mMsgId); + mConnectId.Set(0); + Insert(mConnectId); + } + + Basic::Basic(const unsigned messageId, const unsigned connectId) + { + mMsgId.Set(messageId); + Insert(mMsgId); + mConnectId.Set(connectId); + Insert(mConnectId); + } +#else + Basic::Basic() : + mMsgId(0), + mConnectId(0) + + { + Insert(mMsgId); + Insert(mConnectId); + } + + Basic::Basic(const unsigned messageId, const unsigned connectId) : + mMsgId(messageId), + mConnectId(connectId) + + { + Insert(mMsgId); + Insert(mConnectId); + } +#endif + + Basic::~Basic() + { + } + + Basic::Basic(const Basic & source) : + mMsgId(source.mMsgId), + mConnectId(source.mConnectId) + { + // do not copy member pointers - would lead to dangling pointers + + Insert(mMsgId); + Insert(mConnectId); + } + + Basic & Basic::operator=(const Basic & rhs) + { + // do not copy member pointers - would lead to dangling pointers + + mMsgId = rhs.mMsgId; + mConnectId = rhs.mConnectId; + + return *this; + } + +//////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////// + // NameValuePair + ImplementMessageBegin(NameValuePair, Basic) + ImplementMessageMemberEx(Name, std::string(), 256) + ImplementMessageMemberEx(Value, std::string(), 512) + ImplementMessageEnd + + //////////////////////////////////////// + // Connect + ImplementMessageBegin(Connect, Basic) + ImplementMessageMemberEx(Version, std::string(), 30) + ImplementMessageMemberEx(Description, std::string(), 64) + + ImplementMessageEnd + + + //////////////////////////////////////// + // ConnectReply + ImplementMessageBegin(ConnectReply, Basic) + ImplementMessageMember(Result, 0) + ImplementMessageMemberEx(Version, std::string(), 30) + ImplementMessageEnd + + +//////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////// + // Tracked + ImplementMessageBegin(Tracked, Basic) + ImplementMessageMember(TrackingNumber, 0) + ImplementMessageEnd + + + //////////////////////////////////////// + // TrackedReply + ImplementMessageBegin(TrackedReply, Tracked) + ImplementMessageMember(Result, 0) + ImplementMessageEnd + + //////////////////////////////////////// + // TrackedReplyEx + ImplementMessageBegin(TrackedReplyEx, TrackedReply) + ImplementMessageMember(Status, 0) + ImplementMessageEnd + + //////////////////////////////////////// + // TrackedInterimReply + ImplementMessageBegin(TrackedInterimReply, TrackedReply) + ImplementMessageMember(ParentTrackingNumber, 0) + ImplementMessageEnd + + //////////////////////////////////////// + // GetStatus + ImplementMessageBegin(GetStatus, Tracked) + ImplementMessageEnd + + //////////////////////////////////////// + // GetStatusReply + ImplementMessageBegin(GetStatusReply, TrackedReply) + ImplementMessageMemberEx(NameValuePairs, std::vector(), 100000) + ImplementMessageEnd + + //////////////////////////////////////// + // ShutdownNotify + ImplementMessageBegin(ShutdownNotify, Basic) + ImplementMessageMember(UnfinishedRequests, 0) + ImplementMessageEnd + + ImplementMessageBegin(ShutdownNotifyReply, Basic) + ImplementMessageMember(UnfinishedRequests, 0) + ImplementMessageEnd + +//////////////////////////////////////////////////////////////////////////////// + + + } + + void Read(soe::NameValuePair &dest, const Message::NameValuePair &src) + { + dest.name = src.GetName(); + dest.value = src.GetValue(); + } + + void Write(const soe::NameValuePair &src, Message::NameValuePair &dest) + { + dest.SetName(src.name); + dest.SetValue(src.value); + } +} + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMessages.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMessages.h new file mode 100644 index 00000000..abe24836 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiMessages.h @@ -0,0 +1,454 @@ +// Must protect from multiple definition from outside this header +//#ifndef _API_MESSAGES_H +//#define _API_MESSAGES_H + + +#ifndef API_NAMESPACE +#pragma message ("apiMessages.h: API_NAMESPACE undefined") +//#else +//#pragma message ("apiMessages.h: API_NAMESPACE:") +//#pragma message API_NAMESPACE +#endif + +#include +#include +#include "Base/serializeClasses.h" +#include "apiMacros.h" +#include "apiTypeNameValuePair.h" + +// Storage *will* be freed if an exception is thrown in the constructor, +// so disable that warning here. +#pragma warning(disable:4291) + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef API_NAMESPACE +namespace API_NAMESPACE +{ +#endif + + enum + { + MESSAGE_NULL = 0x0000, + MESSAGE_NAME_VALUE_PAIR = 0x0001, + + // Client Messages + MESSAGE_CONNECT = 0x0ff3, + MESSAGE_GET_STATUS = 0x0ff4, + MESSAGE_GET_CONFIGURATION = 0x0ff5, + + MESSAGE_SHUTDOWN_NOTIFY = 0x0f00, + + // Server Messages + MESSAGE_CONNECT_REPLY = 0x7ff3, + MESSAGE_GET_STATUS_REPLY = 0x7ff4, + MESSAGE_GET_CONFIGURATION_REPLY = 0x7ff5, + + MESSAGE_SHUTDOWN_NOTIFY_REPLY = 0x7f00 + }; + + + namespace Message + { + + +//////////////////////////////////////////////////////////////////////////////// + + class DECLSPEC Base + { + protected: + struct DECLSPEC MemberInfo + { + MemberInfo(soe::AutoVariableBase *dataIn = NULL, unsigned sizeIn = 1, unsigned versionIn = 0, soe::EVersionEffect effectIn = soe::eNoEffect) + : data(dataIn) + , size(sizeIn) + , version(versionIn) + , effect(effectIn) + { } + + soe::AutoVariableBase *data; + unsigned size; + unsigned version; + soe::EVersionEffect effect; + }; + typedef struct MemberInfo MemberInfo_t; + // typedef std::pair MemberInfo_t; + typedef std::vector MemberVector_t; + + public: + + typedef soe::uint32 Storage_t; + typedef std::vector StorageVector_t; + + Base() { } + virtual ~Base() { } + void * operator new(size_t size); + void * operator new(size_t size, void *ptr); + + void Insert(soe::AutoVariableBase & member, unsigned size = 1, unsigned version = 0, soe::EVersionEffect effect = soe::eNoEffect); + void InsertOptional(soe::AutoVariableBase & member, unsigned size = 1, unsigned version = 0, soe::EVersionEffect effect = soe::eNoEffect); + virtual unsigned Write(unsigned char * stream, unsigned size, unsigned version = 0) const; + virtual unsigned Read(const unsigned char * stream, unsigned size, unsigned version = 0); +#ifdef PRINTABLE_MESSAGES + virtual int Print(char * stream, unsigned size, unsigned maxDepth = INT_MAX) const; +#endif + + virtual Base * Clone() const; + virtual Base * Clone(StorageVector_t &storageVector) const; + virtual const char * MessageName() const { return "Base"; } + virtual const char * MessageIDString() const { return "NULL"; } + static Base * Create(unsigned MsgId, StorageVector_t *pStorageVector = NULL); + static const char * GetMessageName(unsigned msgId); + static const char * GetMessageIDString(unsigned msgId); + + class DECLSPEC DeepPointer + { + public: + DeepPointer(); + DeepPointer(const Base & source); + explicit DeepPointer(const Base * source); + DeepPointer(const DeepPointer & source); + virtual ~DeepPointer(); + + void SetMessageType(unsigned MsgId); + + DeepPointer & operator=(const Base * rhs); + DeepPointer & operator=(const DeepPointer & rhs); + + Base * operator->() const; + Base & operator*() const; + + operator Base * () const { return m_ptr; } + + private: + Base *m_ptr; + StorageVector_t m_storageVector; + }; + + static const std::map & GetMessageMap(); + + protected: + Base(const Base & source) { } + Base & operator=(const Base & rhs) { return *this; } + void * operator new(size_t storageSize, StorageVector_t &storageVector); + public: + static void AddClassMapEntry(unsigned msgId, const Base * msg); + protected: + MemberVector_t mMembers; + MemberVector_t mOptionalMembers; + + static std::map *ms_pClassMap; + }; + + } + + +#ifdef API_NAMESPACE +} +#endif + + +#include "Base/serializeTemplates.h" + +#ifdef API_NAMESPACE +namespace API_NAMESPACE +{ +#endif + namespace Message + { + + class DECLSPEC Basic : public Base + { + public: + Basic(); + Basic(const unsigned messageId, const unsigned connectId=0); + virtual ~Basic(); + + const unsigned GetMsgId() const; + const unsigned GetConnectionId() const; + void SetConnectionId(const unsigned connectId); + + virtual const char * MessageName() const { return "Basic"; } + virtual const char * MessageIDString() const { return "NULL_2"; } + + protected: + Basic(const Basic & source); + Basic & operator=(const Basic & rhs); +#if defined(PRINTABLE_MESSAGES) || defined(TRACK_READ_WRITE_FAILURES) + public: + class DECLSPEC PrintMsgId : public soe::AutoVariable + { + public: + virtual const char * VariableName() const { return "MsgId"; } +# ifdef PRINTABLE_MESSAGES + virtual int Print(char * stream, unsigned size, unsigned maxDepth=INT_MAX) const + { + int bytes = snprintf(stream, size, "%s(", this->VariableName()); + int bytesTotal = 0; + + if (soe::FailedToPrint(bytes, size)) { + return bytes; + } + + bytesTotal += bytes; + bytes = snprintf(stream+bytesTotal, size, "0x%4.4X", Get()); + + if (soe::FailedToPrint(bytes, size)) { + return bytes; + } + + bytesTotal += bytes; + bytes = snprintf(stream+bytesTotal, size-bytesTotal, ")"); + + if (soe::FailedToPrint(bytes, size-bytesTotal)) { + return bytes; + } + + bytesTotal += bytes; + + return bytesTotal; + } +# endif // PRINTABLE_MESSAGES + }; + class PrintConnectionId : public soe::AutoVariable + { + public: + virtual const char * VariableName() const { return "CID"; } +# ifdef PRINTABLE_MESSAGES + virtual int Print(char * stream, unsigned size, unsigned maxDepth=INT_MAX) const + { + int bytesTotal = 0; + + if (Get()) { + int bytes = snprintf(stream, size, "%s(", this->VariableName()); + + if (soe::FailedToPrint(bytes, size)) { + return bytes; + } + + bytesTotal += bytes; + bytes = snprintf(stream+bytesTotal, size, "0x%4.4X", Get()); + + if (soe::FailedToPrint(bytes, size)) { + return bytes; + } + + bytesTotal += bytes; + bytes = snprintf(stream+bytesTotal, size-bytesTotal, ")"); + + if (soe::FailedToPrint(bytes, size-bytesTotal)) { + return bytes; + } + + bytesTotal += bytes; + } + return bytesTotal; + } +# endif // PRINTABLE_MESSAGES + }; + protected: + PrintMsgId mMsgId; + PrintConnectionId mConnectId; +#else + soe::AutoVariable mMsgId; + soe::AutoVariable mConnectId; +#endif // defined(PRINTABLE_MESSAGES) || defined(TRACK_READ_WRITE_FAILURES) + }; + + + inline void Base::Insert(soe::AutoVariableBase & member, unsigned size, unsigned version, soe::EVersionEffect effect) + { + mMembers.push_back(MemberInfo_t(&member,size,version,effect)); + } + + inline void Base::InsertOptional(soe::AutoVariableBase & member, unsigned size, unsigned version, soe::EVersionEffect effect) + { + mOptionalMembers.push_back(MemberInfo_t(&member,size,version,effect)); + } + + inline const unsigned Basic::GetMsgId() const + { + return mMsgId.Get(); + } + + inline const unsigned Basic::GetConnectionId() const + { + return mConnectId.Get(); + } + + inline void Basic::SetConnectionId(const unsigned connectId) + { + mConnectId.Set(connectId); + mMembers[1] = MemberInfo_t(&mConnectId,1,0,soe::eNoEffect); + } + +//////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////// + // NameValuePair + DefineMessageBegin(NameValuePair, Basic, MESSAGE_NAME_VALUE_PAIR) + DefineMessageMember(Name, std::string) + DefineMessageMember(Value, std::string) + DefineMessageEnd + + //////////////////////////////////////// + // Connect + DefineMessageBegin(Connect, Basic, MESSAGE_CONNECT) + DefineMessageMember(Version, std::string) + DefineMessageMember(Description, std::string) + + DefineMessageEnd + + + //////////////////////////////////////// + // ConnectReply + DefineMessageBegin(ConnectReply, Basic, MESSAGE_CONNECT_REPLY) + DefineMessageMember(Result, unsigned char) + DefineMessageMember(Version, std::string) + DefineMessageEnd + +//////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////// + // Tracked + DefineMessageBegin(Tracked, Basic, MESSAGE_NULL) + DefineMessageMember(TrackingNumber, unsigned) + DefineMessageEnd + + + //////////////////////////////////////// + // TrackedReply + DefineMessageBegin(TrackedReply, Tracked, MESSAGE_NULL) + DefineMessageMember(Result, unsigned) + DefineMessageEnd + + //////////////////////////////////////// + // TrackedReplyEx + DefineMessageBegin(TrackedReplyEx, TrackedReply, MESSAGE_NULL) + DefineMessageMember(Status, unsigned) + DefineMessageEnd + + //////////////////////////////////////// + // TrackedInterimReply + DefineMessageBegin(TrackedInterimReply, TrackedReply, MESSAGE_NULL) + DefineMessageMember(ParentTrackingNumber, unsigned) + DefineMessageEnd + + + //////////////////////////////////////// + // GetStatus + DefineMessageBegin(GetStatus, Tracked, MESSAGE_GET_STATUS) + DefineMessageEnd + + DefineMessageBegin(GetStatusReply, TrackedReply, MESSAGE_GET_STATUS_REPLY) + DefineMessageMember(NameValuePairs, std::vector) + DefineMessageEnd + + //////////////////////////////////////// + // ShutdownNotify + DefineMessageBegin(ShutdownNotify, Basic, MESSAGE_SHUTDOWN_NOTIFY) + DefineMessageMember(UnfinishedRequests, unsigned) + DefineMessageEnd + + DefineMessageBegin(ShutdownNotifyReply, Basic, MESSAGE_SHUTDOWN_NOTIFY_REPLY) + DefineMessageMember(UnfinishedRequests, unsigned) + DefineMessageEnd + +//////////////////////////////////////////////////////////////////////////////// + + + } + + DECLSPEC void Read(soe::NameValuePair &dest, const Message::NameValuePair &src); + DECLSPEC void Write(const soe::NameValuePair &src, Message::NameValuePair &dest); +#ifdef API_NAMESPACE +} +#endif + +namespace soe +{ + inline unsigned Write(unsigned char * stream, unsigned size, const API_NAMESPACE::Message::Base & data, unsigned version = 0) + { + unsigned written = 0; + unsigned writtenSize = Write(stream, size, written, version); + + if (writtenSize <= 0) { return 0; } + written = data.Write(stream + writtenSize, size - writtenSize, version); + if (written <= 0) { return 0; } + writtenSize = Write(stream, size, written, version); + + return (writtenSize + written); + } + + inline unsigned Read(const unsigned char * stream, unsigned size, API_NAMESPACE::Message::Base & data, unsigned, unsigned version = 0) + { + unsigned messageSize = 0; + unsigned readSize = Read(stream, size, messageSize, version); + unsigned readIn = 0; + + size -= readSize; + stream += readSize; + if (messageSize > size) { return 0; } + readIn = data.Read(stream, messageSize, version); + if (readIn <= 0) { return 0; } + + return (readSize + messageSize); + } + + inline unsigned Write(unsigned char * stream, unsigned size, const API_NAMESPACE::Message::Base::DeepPointer & data, unsigned version = 0) + { + API_NAMESPACE::Message::Base *msg = (API_NAMESPACE::Message::Base *)data; + + if (msg) { + return Write(stream, size, *msg, version); + } else { + return 0; + } + } + + inline unsigned Read(const unsigned char * stream, unsigned size, API_NAMESPACE::Message::Base::DeepPointer & data, unsigned, unsigned version = 0) + { + unsigned msgSize = 0; + unsigned msgId = 0; + unsigned sizeSize = Read(stream, size, msgSize, version); + unsigned idSize = (sizeSize > 0) ? Read(stream + sizeSize, size - sizeSize, msgId, version) : 0; + + if (idSize <= 0) { + return 0; + } + + data.SetMessageType(msgId); + + API_NAMESPACE::Message::Base *msg = data; + + if (msg) { + return Read(stream, size, *msg, 0, version); + } else { + return 0; + } + } + +#ifdef PRINTABLE_MESSAGES + inline int Print(char * stream, unsigned size, const API_NAMESPACE::Message::Base & data, unsigned maxDepth=INT_MAX) + { + return data.Print(stream, size, maxDepth); + + } + + inline int Print(char * stream, unsigned size, const API_NAMESPACE::Message::Base::DeepPointer & data, unsigned maxDepth=INT_MAX) + { + API_NAMESPACE::Message::Base *msg = (API_NAMESPACE::Message::Base *)data; + + if (msg) { + return Print(stream, size, *msg, maxDepth); + } else { + return 0; + } + } +#endif + +} + + +//#endif //_API_MESSAGES_H diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiPinned.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiPinned.cpp new file mode 100644 index 00000000..b743eb24 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiPinned.cpp @@ -0,0 +1,443 @@ +#ifdef WIN32 +#pragma warning (disable: 4786) +#endif + + +#include +#include +#include +#include +#include "Base/timer.h" +#include "Base/serialize.h" +#include "apiPinned.h" + +using namespace std; + +#ifdef NAMESPACE +namespace NAMESPACE +{ +#endif + + const unsigned CONNECT_TIMEOUT = 5; + + //////////////////////////////////////////////////////////////////////////////// + + TrackedRequest::TrackedRequest() : + mMsgId(0), + mTrackingNumber(0), + mResult(0), + mUserData(0), + mTimeout(0) + { + } + + TrackedRequest::TrackedRequest(unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned duration) : + mMsgId(messageId), + mTrackingNumber(trackingNumber), + mResult(result), + mUserData((void *)userData), + mTimeout(0) + { + mTimeout = time(0) + duration; + } + + //////////////////////////////////////////////////////////////////////////////// + + CommonAPI::CommonAPI(const char * hostList, const char * failoverHostList, unsigned connectionLimit, unsigned maxMsgSize, unsigned bufferSize) + : mManager() + , mIdleHosts() + , mHostMap() + , mActiveHosts() + , mCallbackQueue() + , mRequestMap() + , mTimeoutMap() + , mTimeoutTimer(0) + , mStatusTimer(0) + , mConnectionLimit(connectionLimit) + , mInCallback(false) + , mTrackingIndex(1) + { +#ifdef UDP_LIBRARY + UdpManager::Params params = GetConnectionParams(); + mManager = new UdpManager(¶ms); +#else + TcpManager::TcpParams params = GetConnectionParams(); + mManager = new TcpManager(params); +#endif + srand(time(0)); + if (hostList && hostList[0]) + { + std::vector orderArray; + std::string hosts = hostList; + unsigned offset = 0; + do + { + unsigned index = hosts.find(' ', offset); + orderArray.push_back(hosts.substr(offset, (index != std::string::npos) ? index-offset : index)); + offset = (index != std::string::npos) ? index+1 : index; + } + while (offset != std::string::npos); + + while (!orderArray.empty()) + { + mIdleHosts[0].push_back(orderArray.back()); + orderArray.pop_back(); + } + } + if (failoverHostList && failoverHostList[0]) + { + std::vector orderArray; + std::string hosts = failoverHostList; + unsigned offset = 0; + do + { + unsigned index = hosts.find(' ', offset); + orderArray.push_back(hosts.substr(offset, (index != std::string::npos) ? index-offset : index)); + offset = (index != std::string::npos) ? index+1 : index; + } + while (offset != std::string::npos); + + while (!orderArray.empty()) + { + mIdleHosts[1].push_back(orderArray.back()); + orderArray.pop_back(); + } + } + } + + CommonAPI::~CommonAPI() + { + // ensure that callback mecahnism isn't triggered at this point + HostMap_t::iterator iter; + for (iter = mHostMap[0].begin(); iter != mHostMap[0].end(); iter++) + { + iter->first->SetHandler(0); + iter->first->Release(); + } + for (iter = mHostMap[1].begin(); iter != mHostMap[1].end(); iter++) + { + iter->first->SetHandler(0); + iter->first->Release(); + } + mManager->Release(); + } + + +#ifdef UDP_LIBRARY + UdpManager::Params CommonAPI::GetConnectionParams() + + { + UdpManager::Params params; + // LAN connection + params.maxRawPacketSize = 1460; + params.outgoingBufferSize = 4 * 1024 * 1024; + params.incomingBufferSize = 4 * 1024 * 1024; + params.keepAliveDelay = 15 * 1000; + params.reliableOverflowBytes = 10 * 1024 * 1024; + params.pooledPacketMax = 5000; + params.crcBytes = 2; + params.icmpErrorRetryPeriod = 2000; + params.allowPortRemapping = false; // set to false (optional) + params.oldestUnacknowledgedTimeout = 15 * 1000; // set to 30000 (longer for debugging tolerance) + params.maxDataHoldTime = 0; // set to 0 (this is critical) + params.reliable[0].maxOutstandingBytes = 2 * 1024 * 1024; // set to 2mb + params.reliable[0].maxOutstandingPackets = 4000; // set to 4000 + params.reliable[0].resendDelayAdjust = 1500; // set to 1500 + params.reliable[0].congestionWindowMinimum = 50000; // set to 50000 + + return params; + } +#else + TcpManager::TcpParams CommonAPI::GetConnectionParams() + + { + TcpManager::TcpParams params; + + params.incomingBufferSize = 512*1024; + params.outgoingBufferSize = 512*1024; + params.allocatorBlockSize = 8*1024; + params.allocatorBlockCount = 1024; + params.maxRecvMessageSize = 8*1024; + params.keepAliveDelay = 15 * 1000; + //params.noDataTimeout = 30 * 1000; // server only setting. + + return params; + } +#endif + + + void CommonAPI::RegisterTrackedMessage(unsigned messageId) + { + mTrackedMessages.insert(messageId); + } + + bool CommonAPI::IsTracked(unsigned messageId) + { + return mTrackedMessages.find(messageId) != mTrackedMessages.end(); + } + + unsigned CommonAPI::GetTrackingNumber() + { + if (!mTrackingIndex) + mTrackingIndex++; + return mTrackingIndex++; + } + + unsigned CommonAPI::GetConnectionCount() + { + if (mActiveHosts[0].size()) + return mActiveHosts[0].size(); + else + return mActiveHosts[1].size(); + } + +#ifdef UDP_LIBRARY + void CommonAPI::OnConnectComplete(UdpConnection * connection) +#else + void CommonAPI::OnConnectRequest(TcpConnection * connection) +#endif + { + HostMap_t::iterator iter; + if ((iter = mHostMap[0].find(connection)) != mHostMap[0].end()) + { + mActiveHosts[0].insert(connection); + } + else if ((iter = mHostMap[1].find(connection)) != mHostMap[1].end()) + { + mActiveHosts[1].insert(connection); + } + OnConnectionOpened(iter->second.c_str()); + // send initial status message + unsigned char buffer[1024]; + unsigned size = FormatStatusMsg(buffer, sizeof(buffer)); + if (size) + { +#ifdef UDP_LIBRARY + connection->Send(cUdpChannelReliable1, buffer, size); + connection->FlushChannels(); +#else + connection->Send((const char*)buffer, size); +#endif + } + } + +#ifdef UDP_LIBRARY + void CommonAPI::OnTerminated(UdpConnection * connection) +#else + void CommonAPI::OnTerminated(TcpConnection * connection) +#endif + { + std::string host; + HostMap_t::iterator iter; + if ((iter = mHostMap[0].find(connection)) != mHostMap[0].end()) + { + if (mActiveHosts[0].erase(connection) != 0) + { + OnConnectionClosed(iter->second.c_str(), "unknown"); + } + else + { + OnConnectionFailed(iter->second.c_str()); + } + iter->first->Release(); + mIdleHosts[0].push_back(iter->second); + mHostMap[0].erase(iter); + } + else if ((iter = mHostMap[1].find(connection)) != mHostMap[1].end()) + { + if (mActiveHosts[1].erase(connection) != 0) + { + OnConnectionClosed(iter->second.c_str(), "unknown"); + } + else + { + OnConnectionFailed(iter->second.c_str()); + } + iter->first->Release(); + mIdleHosts[1].push_back(iter->second); + mHostMap[1].erase(iter); + } + } + +#ifdef UDP_LIBRARY + void CommonAPI::OnRoutePacket(UdpConnection * connection, const unsigned char * data, int dataLen) +#else + void CommonAPI::OnRoutePacket(TcpConnection * connection, const unsigned char * data, int dataLen) +#endif + { + // read message id + unsigned msgId = CheckMessageId(data, dataLen); + // if tracked message, lookup in request map + if (IsTracked(msgId)) + { + // read tracking number + unsigned trackingNumber = CheckTrackingNumber(data, dataLen); + + RequestMap_t::iterator reqIterator = mRequestMap.find(trackingNumber); + if (reqIterator != mRequestMap.end()) + { + TrackedRequest & request = reqIterator->second; + Callback(data, dataLen, msgId, (void *)request.mUserData); + mRequestMap.erase(reqIterator); + } + } + else if (msgId) + { + Callback(data, dataLen, msgId, 0); + } + } + + bool CommonAPI::Send(unsigned char hashValue, const unsigned char *data, unsigned dataLen) + { + if (!mActiveHosts[0].empty()) + { + // get connection that corresponds to hash index + ApiConnection * connection = NULL; + unsigned hashIndex = hashValue % mActiveHosts[0].size(); + unsigned curIndex = 0; + for (ConnectionSet_t::iterator it = mActiveHosts[0].begin(); it != mActiveHosts[0].end(); it++, curIndex++) + { + if (hashIndex == curIndex) + { + connection = *it; + break; + } + } + if (!connection) + return false; +#ifdef UDP_LIBRARY + connection->Send(cUdpChannelReliable1, data, dataLen); +#else + connection->Send((const char*)data, dataLen); +#endif + return true; + } + else if (!mActiveHosts[1].empty()) + { + // get connection that corresponds to hash index + ApiConnection * connection = NULL; + unsigned hashIndex = hashValue % mActiveHosts[1].size(); + unsigned curIndex = 0; + for (ConnectionSet_t::iterator it = mActiveHosts[1].begin(); it != mActiveHosts[1].end(); it++, curIndex++) + { + if (hashIndex == curIndex) + { + connection = *it; + break; + } + } + if (!connection) + return false; + +#ifdef UDP_LIBRARY + connection->Send(cUdpChannelReliable1, data, dataLen); +#else + connection->Send((const char *)data, dataLen); +#endif + return true; + } + return false; + } + + void CommonAPI::PrepareCallback(unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned timeout) + { + TrackedRequest request(messageId, trackingNumber, result, userData, timeout); + mRequestMap[trackingNumber] = request; + + if (timeout) + { + mTimeoutMap[timeout].push_back(std::pair(trackingNumber,time(0)+timeout)); + } + else + { + mCallbackQueue.push_back(trackingNumber); + } + } + + void CommonAPI::Process() + { + if (mInCallback) + return; + mInCallback = true; + unsigned currentTime = time(0); + + //////////////////////////////////////// + // Process connection manager + if (!mIdleHosts[0].empty() && (mHostMap[0].size() < mConnectionLimit || !mConnectionLimit)) + { + std::string hostName = mIdleHosts[0].front(); + mIdleHosts[0].pop_front(); + unsigned index = hostName.find(':'); + std::string host = hostName.substr(0, index); + int port = (index != std::string::npos) ? atoi(hostName.substr(index+1, index).c_str()) : 0; + + ApiConnection * connection = mManager->EstablishConnection(host.c_str(), port, CONNECT_TIMEOUT); + if (connection) + { + connection->SetHandler(this); + mHostMap[0][connection] = hostName; + } + } + if (!mIdleHosts[1].empty() && (mHostMap[1].size() < mConnectionLimit || !mConnectionLimit)) + { + std::string hostName = mIdleHosts[1].front(); + mIdleHosts[1].pop_front(); + unsigned index = hostName.find(':'); + std::string host = hostName.substr(0, index); + int port = (index != std::string::npos) ? atoi(hostName.substr(index+1, index).c_str()) : 0; + + ApiConnection * connection = mManager->EstablishConnection(host.c_str(), port, CONNECT_TIMEOUT); + if (connection) + { + connection->SetHandler(this); + mHostMap[1][connection] = hostName; + } + } + mManager->GiveTime(); + + //////////////////////////////////////// + // Check for requests that have timed out once per second + if (mTimeoutTimer != currentTime) + { + TimeoutMap_t::iterator iterator; + for (iterator = mTimeoutMap.begin(); iterator != mTimeoutMap.end(); iterator++) + { + TimeoutList_t & timeoutList = iterator->second; + while (!timeoutList.empty() && timeoutList.front().second < currentTime) + { + RequestMap_t::iterator reqIterator = mRequestMap.find(timeoutList.front().first); + if (reqIterator != mRequestMap.end()) + { + TrackedRequest & request = reqIterator->second; + Callback(request.mMsgId, request.mTrackingNumber, request.mResult, (void *)request.mUserData); + mRequestMap.erase(reqIterator); + } + timeoutList.pop_front(); + } + } + mTimeoutTimer = currentTime; + } + + //////////////////////////////////////// + // Process Callback Queue + while (!mCallbackQueue.empty()) + { + RequestMap_t::iterator reqIterator = mRequestMap.find(mCallbackQueue.front()); + if (reqIterator != mRequestMap.end()) + { + TrackedRequest & request = reqIterator->second; + Callback(request.mMsgId, request.mTrackingNumber, request.mResult, (void *)request.mUserData); + mRequestMap.erase(reqIterator); + } + mCallbackQueue.pop_front(); + } + + mInCallback = false; + } + + + //////////////////////////////////////////////////////////////////////////////// + +#ifdef NAMESPACE +} +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiPinned.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiPinned.h new file mode 100644 index 00000000..23036780 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiPinned.h @@ -0,0 +1,121 @@ +#ifndef BASE__API_H +#define BASE__API_H + +#include +#include +#include +#include +#include "Base/profile.h" + +#ifdef UDP_LIBRARY +#include "UdpLibrary/UdpLibrary.hpp" +#else +#include "TcpLibrary/TcpConnection.h" +#include "TcpLibrary/TcpManager.h" +#endif +#include "apiMessages.h" + +#ifdef NAMESPACE +namespace NAMESPACE +{ +#endif + + //////////////////////////////////////////////////////////////////////////////// + + struct TrackedRequest + { + TrackedRequest(); + TrackedRequest(unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned timeout = 5); + + unsigned mMsgId; + unsigned mTrackingNumber; + unsigned mResult; + void * mUserData; + unsigned mTimeout; + }; + + //////////////////////////////////////////////////////////////////////////////// + +#ifdef UDP_LIBRARY + class CommonAPI : UdpConnectionHandler +#else + class CommonAPI : TcpConnectionHandler +#endif + { +#ifdef UDP_LIBRARY + typedef UdpConnection ApiConnection; +#else + typedef TcpConnection ApiConnection; +#endif + typedef std::map HostMap_t; + typedef std::set ConnectionSet_t; + typedef std::map RequestMap_t; + typedef std::list > TimeoutList_t; + typedef std::map TimeoutMap_t; + typedef std::list TrackingList_t; + typedef std::set MessagIdSet_t; + public: + CommonAPI(const char * hostList, const char * failoverHostList = 0, unsigned connectionLimit = 0, unsigned maxMsgSize = 1*1024, unsigned bufferSize = 64*1024); + virtual ~CommonAPI(); + + void Process(); + unsigned GetConnectionCount(); + unsigned GetTrackingNumber(); + bool Send(unsigned char hashValue, const unsigned char *data, unsigned dataLen); + void PrepareCallback(unsigned messageId, unsigned trackingNumber, unsigned result, const void * userData, unsigned timeout = 0); + + protected: +#ifdef UDP_LIBRARY + virtual UdpManager::Params GetConnectionParams(); + virtual void OnConnectComplete(UdpConnection *con); + virtual void OnTerminated(UdpConnection *con); + virtual void OnRoutePacket(UdpConnection *con, const uchar *data, int dataLen); +#else + virtual TcpManager::TcpParams GetConnectionParams(); + virtual void OnConnectRequest(TcpConnection *con); + virtual void OnTerminated(TcpConnection *con); + virtual void OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen); +#endif + + virtual unsigned CheckMessageId(const unsigned char * data, unsigned dataLen) = 0; + virtual unsigned CheckTrackingNumber(const unsigned char * data, unsigned dataLen) = 0; + + bool IsTracked(unsigned msgId); + void RegisterTrackedMessage(unsigned msgId); + // FormatStatusMsg provides a buffer/size for the derived class to write to and returns the size of the message written + // This function is called after connecting and periodically once connected. + virtual unsigned FormatStatusMsg(unsigned char * buffer, unsigned bufferLen) = 0; + + virtual void OnConnectionOpened(const char * address) = 0; + virtual void OnConnectionFailed(const char * address) = 0; + virtual void OnConnectionClosed(const char * address, const char * reason) = 0; + virtual void Callback(const unsigned char * data, unsigned dataLen, unsigned messageId, void * userData) = 0; + virtual void Callback(unsigned messageId, unsigned trackingNumber, unsigned result, void * userData) = 0; + + private: +#ifdef UDP_LIBRARY + UdpManager * mManager; +#else + TcpManager * mManager; +#endif + std::list mIdleHosts[2]; + HostMap_t mHostMap[2]; + ConnectionSet_t mActiveHosts[2]; + TrackingList_t mCallbackQueue; + RequestMap_t mRequestMap; + TimeoutMap_t mTimeoutMap; + unsigned mTrackingIndex; + MessagIdSet_t mTrackedMessages; + unsigned mTimeoutTimer; + unsigned mStatusTimer; + unsigned mConnectionLimit; + unsigned mMaxMsgSize; + unsigned mBufferSize; + bool mInCallback; + }; + +#ifdef NAMESPACE +} +#endif + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiTypeNameValuePair.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiTypeNameValuePair.h new file mode 100644 index 00000000..22105160 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiTypeNameValuePair.h @@ -0,0 +1,65 @@ +#ifndef API_TYPE_NAME_VALUE_PAIR +#define API_TYPE_NAME_VALUE_PAIR + +#include +#include +#include "Base/serializeClasses.h" + +const int MAX_NAME_VALUE_PAIR_NAME_LEN = 256; +const int MAX_NAME_VALUE_PAIR_VALUE_LEN = 512; + +namespace soe +{ + struct NameValuePair + { + unsigned Write(unsigned char * stream, unsigned size) const + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, name)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, value)); + if (!bytes) return 0; + + return bytesTotal; + } + + unsigned Read(const unsigned char * stream, unsigned size) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, name, MAX_NAME_VALUE_PAIR_NAME_LEN)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, value, MAX_NAME_VALUE_PAIR_VALUE_LEN)); + if (!bytes) return 0; + + return bytesTotal; + } + + std::string name; //256 + std::string value; //512 + }; + typedef std::vector NameValuePairs_t; + + inline unsigned Read(const unsigned char * stream, unsigned size, NameValuePair & data, unsigned maxLen, unsigned version = 0) + { + return data.Read(stream, size); + } + + inline unsigned Write(unsigned char * stream, unsigned size, const NameValuePair & data, unsigned version = 0) + { + return data.Write(stream, size); + } + + inline int Print(char * stream, unsigned size, const NameValuePair & data, unsigned maxDepth = INT_MAX) + { + return 0; + } +} + + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiTypePlatUnicodeString.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiTypePlatUnicodeString.h new file mode 100644 index 00000000..59feea5f --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/apiTypePlatUnicodeString.h @@ -0,0 +1,22 @@ +#ifndef API_TYPE_NAME_VALUE_PAIR +#define API_TYPE_NAME_VALUE_PAIR + +#include +#include +#include "Base/serializeClasses.h" + +const int MAX_NAME_VALUE_PAIR_NAME_LEN = 256; +const int MAX_NAME_VALUE_PAIR_VALUE_LEN = 512; + +namespace soe +{ + + + inline int Print(char * stream, unsigned size, const NameValuePair & data, unsigned maxDepth = INT_MAX) + { + return 0; + } +} + + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/protocol.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/protocol.h new file mode 100644 index 00000000..d08b3bd6 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Api/protocol.h @@ -0,0 +1,23 @@ +#ifndef _PROTOCOL_H_ +#define _PROTOCOL_H_ + +#ifdef _USE_TCP + +#include "TcpLibrary/TcpConnection.h" +#include "TcpLibrary/TcpManager.h" +#define PlatConnectionHandler TcpConnectionHandler +#define PlatConnection TcpConnection +#define PlatManager TcpManager +#define PlatManagerHandler TcpManagerHandler +#define PlatParams TcpManager::TcpParams + +#else +#include "UdpLibrary/UdpLibrary.hpp" +#define PlatConnectionHandler UdpConnectionHandler +#define PlatConnection UdpConnection +#define PlatManager UdpManager +#define PlatParams UdpManager::Params + +#endif + +#endif \ No newline at end of file diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base32.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base32.cpp new file mode 100644 index 00000000..c1fb487a --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base32.cpp @@ -0,0 +1,216 @@ +#include "Base32.h" +#include +#include "types.h" + + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ + +#endif + namespace Base + { + const unsigned char Base32::ms_base32Table[] = + { + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + '0', + '1', + '2', + '3', + '4', + '5', + }; + + + int Base32::GetEncode32Length(int bytes) + { + int bits = bytes * 8; + int length = bits / 5; + if((bits % 5) > 0) + { + length++; + } + return length; + } + + int Base32::GetDecode32Length(int bytes) + { + int bits = bytes * 5; + int length = bits / 8; + return length; + } + + static bool Encode32Block(unsigned char* in5, unsigned char* out8) + { + // pack 5 bytes + soe::uint64 buffer = 0; + for(int i = 0; i < 5; i++) + { + if(i != 0) + { + buffer = (buffer << 8); + } + buffer = buffer | in5[i]; + } + // output 8 bytes + for(int j = 7; j >= 0; j--) + { + buffer = buffer << (24 + (7 - j) * 5); + buffer = buffer >> (24 + (7 - j) * 5); + unsigned char c = (unsigned char)(buffer >> (j * 5)); + // self check + if(c >= 32) return false; + out8[7 - j] = c; + } + return true; + } + + bool Base32::Encode32(unsigned char* in, int inLen, unsigned char* out) + { + if((in == 0) || (inLen <= 0) || (out == 0)) return false; + + int d = inLen / 5; + int r = inLen % 5; + + unsigned char outBuff[8]; + + for(int j = 0; j < d; j++) + { + if(!Encode32Block(&in[j * 5], &outBuff[0])) return false; + memmove(&out[j * 8], &outBuff[0], sizeof(unsigned char) * 8); + } + + unsigned char padd[5]; + memset(padd, 0, sizeof(unsigned char) * 5); + for(int i = 0; i < r; i++) + { + padd[i] = in[inLen - r + i]; + } + if(!Encode32Block(&padd[0], &outBuff[0])) return false; + memmove(&out[d * 8], &outBuff[0], sizeof(unsigned char) * GetEncode32Length(r)); + + return true; + } + + static bool Decode32Block(unsigned char* in8, unsigned char* out5) + { + // pack 8 bytes + soe::uint64 buffer = 0; + for(int i = 0; i < 8; i++) + { + // input check + if(in8[i] >= 32) return false; + if(i != 0) + { + buffer = (buffer << 5); + } + buffer = buffer | in8[i]; + } + // output 5 bytes + for(int j = 4; j >= 0; j--) + { + out5[4 - j] = (unsigned char)(buffer >> (j * 8)); + } + return true; + } + + bool Base32::Decode32(unsigned char* in, int inLen, unsigned char* out) + { + if((in == 0) || (inLen <= 0) || (out == 0)) return false; + + int d = inLen / 8; + int r = inLen % 8; + + unsigned char outBuff[5]; + + for(int j = 0; j < d; j++) + { + if(!Decode32Block(&in[j * 8], &outBuff[0])) return false; + memmove(&out[j * 5], &outBuff[0], sizeof(unsigned char) * 5); + } + + unsigned char padd[8]; + memset(padd, 0, sizeof(unsigned char) * 8); + for(int i = 0; i < r; i++) + { + padd[i] = in[inLen - r + i]; + } + if(!Decode32Block(&padd[0], &outBuff[0])) return false; + memmove(&out[d * 5], &outBuff[0], sizeof(unsigned char) * GetDecode32Length(r)); + + return true; + } + + bool Base32::Map32(unsigned char* inout32, int inout32Len, unsigned char* alpha32) + { + if((inout32 == 0) || (inout32Len <= 0) || (alpha32 == 0)) return false; + for(int i = 0; i < inout32Len; i++) + { + if(inout32[i] >=32) return false; + inout32[i] = alpha32[inout32[i]]; + } + return true; + } + + static void ReverseMap(unsigned char* inAlpha32, unsigned char* outMap) + { + memset(outMap, 0, sizeof(unsigned char) * 256); + for(int i = 0; i < 32; i++) + { + outMap[(int)inAlpha32[i]] = i; + } + } + + bool Base32::Unmap32(unsigned char* inout32, int inout32Len, unsigned char* alpha32) + { + if((inout32 == 0) || (inout32Len <= 0) || (alpha32 == 0)) return false; + unsigned char rmap[256]; + ReverseMap(alpha32, rmap); + for(int i = 0; i < inout32Len; i++) + { + unsigned char c = inout32[i]; + int index = (int)c; + inout32[i] = rmap[index]; + } + return true; + } + + bool Base32::Map32(unsigned char* inout32, int inout32Len) + { + return Map32(inout32, inout32Len, (unsigned char *)ms_base32Table); + } + + bool Base32::Unmap32(unsigned char* inout32, int inout32Len) + { + return Unmap32(inout32, inout32Len, (unsigned char *)ms_base32Table); + } + + }; +#ifdef EXTERNAL_DISTRO +}; +#endif \ No newline at end of file diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base32.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base32.h new file mode 100644 index 00000000..6bdd6fec --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base32.h @@ -0,0 +1,44 @@ +#if !defined(_BASE32_H_) +#define _BASE32_H_ + + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + + namespace Base + { + struct Base32 + { + // Decode32 input is the output of Encode32. The out parameters should be unsigned char[] of + // length GetDecode32Length(inLen) and GetEncode32Length(inLen) respectively. + static bool Decode32(unsigned char* in, int inLen, unsigned char* out); + + // Encode32 outputs at out bytes with values from 0 to 32 that can be mapped to 32 signs. + static bool Encode32(unsigned char* in, int inLen, unsigned char* out); + + static int GetDecode32Length(int bytes); + static int GetEncode32Length(int bytes); + + // Both Map32 and Unmap32 do inplace modification of the inout32 array. + // The alpha32 array must be exactly 32 chars long. + // To map the output of Encode32 to an alphabet of 32 characters use Map32. + static bool Map32(unsigned char* inout32, int inout32Len, unsigned char* alpha32); + + // To unmap back the output of Map32 to an array understood by Decode32 use Unmap32. + static bool Unmap32(unsigned char* inout32, int inout32Len, unsigned char* alpha32); + + // same as above but user the static map, ms_base32Table + static bool Map32(unsigned char* inout32, int inout32Len); + static bool Unmap32(unsigned char* inout32, int inout32Len); + + static const unsigned char ms_base32Table[]; + }; + }; + +#ifdef EXTERNAL_DISTRO +}; +#endif + +#endif // _BASE32_H_ diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base64.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base64.cpp new file mode 100644 index 00000000..a2f504a5 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base64.cpp @@ -0,0 +1,499 @@ +#include "Base64.h" +#include + +#ifdef WIN32 +#define snprintf _snprintf +#endif + +namespace soe +{ + + //---------------------------------------------------------------------------- + // Constants + //---------------------------------------------------------------------------- + #define BASE64_PAD '=' + #define BASE64_PAD_INDEX 64 + #define BASE64_ERR ((unsigned char)-1) + #define BASE64_PAD_CODE ((unsigned char)100) + #define BASE64_OUTPUT_LINE 76 + #define BASE64_ENCODE_BUFFER ((BASE64_OUTPUT_LINE*3)/4) + #define BASE64_DECODE_ERROR ((unsigned)-1) + + #define HEX_ERR 16 + #define HEX_LOWER_FORMAT "%2.2x" + #define HEX_UPPER_FORMAT "%2.2X"; + + const char Base64::ms_base64Table[] = + { + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '+', + '/', + BASE64_PAD + }; + + + //---------------------------------------------------------------------------- + // Functions + //---------------------------------------------------------------------------- + + unsigned char Base64::GetBase64Code(char base64char) + { + if (base64char == BASE64_PAD) + return BASE64_PAD_CODE; + + if (base64char >= 'A' && base64char <= 'Z') + return (unsigned char)(base64char - 'A'); + if (base64char >= 'a' && base64char <= 'z') + return (unsigned char)(base64char - 'a' + 26); + if (base64char >= '0' && base64char <= '9') + return (unsigned char)(base64char - '0' + 2*26); + if (base64char == '+') + return 2*26+10; + if (base64char == '/') + return 2*26+11; + + return BASE64_ERR; + } + + unsigned char Base64::GetHexCode(char hexChar) + { + if (hexChar >= '0' && hexChar <= '9') + return (unsigned char)(hexChar - '0'); + if (hexChar >= 'A' && hexChar <= 'F') + return (unsigned char)(hexChar - 'A' + 10); + if (hexChar >= 'a' && hexChar <= 'f') + return (unsigned char)(hexChar - 'a' + 10); + + return HEX_ERR; + } + + unsigned Base64::UUDecodeQuadToTriple(const char *quad, unsigned char *triple) + { + unsigned char b; + unsigned char quadByteCodes[4]; + unsigned bytes = 3, i; + + for (i = 0; i < 4; i++) + { + b = GetBase64Code(quad[i]); + + if (b == BASE64_PAD_CODE) + { + switch(i) + { + case 0: bytes=0; break; + case 1: bytes=0; break; + case 2: bytes=1; break; + case 3: bytes=2; break; + } + break; + } + else if (b == BASE64_ERR) + { + bytes = BASE64_DECODE_ERROR; + } + else + { + quadByteCodes[i] = b; + } + + } + + triple[0] = (quadByteCodes[0] << 2) | (quadByteCodes[1] >> 4); + triple[1] = (quadByteCodes[1] << 4) | (quadByteCodes[2] >> 2); + triple[2] = (quadByteCodes[2] << 6) | (quadByteCodes[3] >> 0); + + return bytes; + } + + bool Base64::UUDecode(const char *source, unsigned sourceLen, unsigned char *dest, unsigned *destLen) + { + unsigned char q[3]; + unsigned index; + unsigned bytes; + unsigned char *destIn = dest; + bool noErrors = true; + + for (index = 0; index < sourceLen; index += 4) + { + bytes = UUDecodeQuadToTriple(source + index, q); + + if (bytes != BASE64_DECODE_ERROR) { + *destLen -= bytes; + if (*destLen >= 0) { + memcpy(dest, (char*)&q, bytes); + } else { + noErrors = false; + } + dest += bytes; + } else { + noErrors = false; + } + } + *destLen = (int)(dest - destIn); + + return noErrors; + } + + bool Base64::UUDecode(const char *source, unsigned sourceLen, std::string &destString) + { + unsigned char q[3]; + unsigned index; + unsigned bytes; + bool noErrors = true; + + for (index = 0; index < sourceLen; index += 4) + { + bytes = UUDecodeQuadToTriple(source + index, q); + + if (bytes != BASE64_DECODE_ERROR) { + destString.append((char*)&q, bytes); + } else { + noErrors = false; + } + } + + return noErrors; + } + + bool Base64::UUDecode(const char *source, unsigned sourceLen, std::vector &destVector) + { + unsigned char q[3]; + unsigned index; + unsigned bytes; + bool noErrors = true; + + for (index = 0; index < sourceLen; index += 4) + { + bytes = UUDecodeQuadToTriple(source + index, q); + + if (bytes != BASE64_DECODE_ERROR) { + for (size_t index = 0; index < bytes; index++) + { + destVector.push_back(q[index]); + } + } else { + noErrors = false; + } + } + + return noErrors; + } + + unsigned Base64::UUEncodeTripleToQuadPtr(const unsigned char *triple, char* quad, unsigned bytes) + { + unsigned char indices[4]; + int quadIndex; + + memset(indices, BASE64_PAD_INDEX, sizeof(indices)); + + switch(bytes) + { + case 3: + indices[3] = (triple[2] ) & 0x0000003F; + indices[2] = (triple[2] >> 6) & 0x0000003F; + case 2: + indices[2] = (indices[2] | (triple[1] << 2)) & 0x0000003F; + indices[1] = (triple[1] >> 4) & 0x0000003F; + case 1: + indices[1] = (indices[1] | (triple[0] << 4)) & 0x0000003F; + indices[0] = (triple[0] >> 2) & 0x0000003F; + case 0: + default: + break; + }; + + for (quadIndex = 0; quadIndex < 4; quadIndex++) { + quad[quadIndex] = ms_base64Table[indices[quadIndex]]; + } + + return 0; + } + + unsigned Base64::UUEncodeTripleToQuadRef(const std::vector::const_iterator &triple, char* quad, unsigned bytes) + { + unsigned char indices[4]; + int quadIndex; + memset(indices, BASE64_PAD_INDEX, sizeof(indices)); + + switch(bytes) + { + case 3: + indices[3] = (*(triple+2) ) & 0x0000003F; + indices[2] = (*(triple+2) >> 6) & 0x0000003F; + case 2: + indices[2] = (indices[2] | (*(triple+1) << 2)) & 0x0000003F; + indices[1] = (*(triple+1) >> 4) & 0x0000003F; + case 1: + indices[1] = (indices[1] | (*(triple+0) << 4)) & 0x0000003F; + indices[0] = (*(triple+0) >> 2) & 0x0000003F; + case 0: + default: + break; + }; + + for (quadIndex = 0; quadIndex < 4; quadIndex++) { + quad[quadIndex] = ms_base64Table[indices[quadIndex]]; + } + + return 0; + } + + bool Base64::UUEncode(const unsigned char *source, unsigned sourceLen, char *dest, unsigned destLen) + { + char quad[5]; + unsigned z; + bool noErrors = true; + + quad[4] = 0; + + // The normal case + for(z = 0; z < sourceLen;z += 3) + { + UUEncodeTripleToQuadPtr(source + z,quad,(z + 3 < sourceLen) ? 3 : (sourceLen - z)); + destLen -= strlen(quad); + if (destLen > 0) { + dest += snprintf(dest, sizeof(dest), quad); + } else { + noErrors = false; + } + } + + return noErrors; + } + + bool Base64::UUEncode(const unsigned char *source, unsigned sourceLen, std::string &destString) + { + char quad[5]; + unsigned z; + bool noErrors = true; + + quad[4] = 0; + + // The normal case + for(z = 0; z < sourceLen;z += 3) + { + UUEncodeTripleToQuadPtr(source + z,quad,(z + 3 < sourceLen) ? 3 : (sourceLen - z)); + + destString.append(quad); + } + + return noErrors; + } + bool Base64::UUEncode(const std::vector &source, char *dest, unsigned destLen) + { + char quad[5]; + unsigned z; + bool noErrors = true; + size_t sourceLen = source.size(); + quad[4] = 0; + + // The normal case + for(z = 0; z < sourceLen;z += 3) + { + UUEncodeTripleToQuadRef(source.begin() + z,quad,(z + 3 < sourceLen) ? 3 : (sourceLen - z)); + if (destLen > 0) { + dest += snprintf(dest, sizeof(dest), quad); + } else { + noErrors = false; + } + } + return noErrors; + } + bool Base64::UUEncode(const std::vector &source, std::string &destString) + { + char quad[5]; + unsigned z; + bool noErrors = true; + size_t sourceLen = source.size(); + quad[4] = 0; + + // The normal case + for(z = 0; z < sourceLen;z += 3) + { + UUEncodeTripleToQuadRef(source.begin() + z,quad,(z + 3 < sourceLen) ? 3 : (sourceLen - z)); + + destString.append(quad); + } + + return noErrors; + } + + bool Base64::HexEncode(const unsigned char *source, unsigned sourceLen, char *dest, unsigned destLen, bool lowercase) + { + const char *format = lowercase ? HEX_LOWER_FORMAT : HEX_UPPER_FORMAT; + bool noErrors = true; + + for (; sourceLen > 0; sourceLen--, source++) + { + if (destLen >= 2) { + destLen -= snprintf(dest, sizeof(dest), format, *source); + } else { + noErrors = false; + } + } + + return noErrors; + } + + bool Base64::HexEncode(const unsigned char *source, unsigned sourceLen, std::string &destString, bool lowercase) + { + const char *format = lowercase ? HEX_LOWER_FORMAT : HEX_UPPER_FORMAT; + char hexBuffer[3]; + bool noErrors = true; + + hexBuffer[2] = '\0'; + + for (; sourceLen > 0; sourceLen--, source++) + { + hexBuffer[0] = '\0'; + snprintf(hexBuffer, sizeof(hexBuffer), format, *source); + destString.append(hexBuffer); + } + + return noErrors; + } + + bool Base64::HexDecode(const char *source, unsigned sourceLen, unsigned char *dest, unsigned *destLen) + { + unsigned char *destIn = dest; + unsigned char digits[2]; + bool noErrors = true; + + for (; sourceLen > 1; sourceLen -= 2, source += 2) + { + if (*destLen > 0) { + digits[0] = GetHexCode(source[0]); + digits[1] = GetHexCode(source[1]); + if ((digits[0] != HEX_ERR) && (digits[1] != HEX_ERR)) { + *dest = (digits[0] * 16) + digits[1]; + dest++; + (*destLen)--; + } else { + noErrors = false; + } + } else { + noErrors = false; + } + } + *destLen = dest - destIn; + + if (sourceLen == 1) { + noErrors = false; + } + + return noErrors; + } + + bool Base64::HexDecode(const char *source, unsigned sourceLen, std::string &destString) + { + unsigned char digits[2]; + unsigned char byte; + bool noErrors = true; + + for (; sourceLen > 1; sourceLen -= 2, source += 2) + { + digits[0] = GetHexCode(source[0]); + digits[1] = GetHexCode(source[1]); + if ((digits[0] != HEX_ERR) && (digits[1] != HEX_ERR)) { + byte = (digits[0] * 16) + digits[1]; + destString.append((char *)&byte, 1); + } else { + noErrors = false; + } + } + + if (sourceLen == 1) { + noErrors = false; + } + + return noErrors; + } + + bool Base64::HexDecode(const char *source, unsigned sourceLen, std::vector &destVector) + { + unsigned char digits[2]; + unsigned char byte; + bool noErrors = true; + + for (; sourceLen > 1; sourceLen -= 2, source += 2) + { + digits[0] = GetHexCode(source[0]); + digits[1] = GetHexCode(source[1]); + if ((digits[0] != HEX_ERR) && (digits[1] != HEX_ERR)) { + byte = (digits[0] * 16) + digits[1]; + destVector.push_back(byte); + } else { + noErrors = false; + } + } + + if (sourceLen == 1) { + noErrors = false; + } + + return noErrors; + } + +}; diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base64.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base64.h new file mode 100644 index 00000000..ac450794 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/Base64.h @@ -0,0 +1,40 @@ +#ifndef BASE64_H +#define BASE64_H + +#include +#include + +namespace soe +{ + class Base64 + { + public: + static bool UUEncode(const unsigned char *source, unsigned sourceLen, char *dest, unsigned destLen); + static bool UUEncode(const unsigned char *source, unsigned sourceLen, std::string &destString); + static bool UUEncode(const std::vector &source, char *dest, unsigned destLen); + static bool UUEncode(const std::vector &source, std::string &destString); + static bool UUDecode(const char *source, unsigned sourceLen, unsigned char *dest, unsigned *destLen); + static bool UUDecode(const char *source, unsigned sourceLen, std::vector &destVector); + static bool UUDecode(const char *source, unsigned sourceLen, std::string &destString); + + // these don't *exactly* belong here, but it's a convenient place for them + static bool HexEncode(const unsigned char *source, unsigned sourceLen, char *dest, unsigned destLen, bool lowercase = false); + static bool HexEncode(const unsigned char *source, unsigned sourceLen, std::string &destString, bool lowercase = false); + static bool HexDecode(const char *source, unsigned sourceLen, unsigned char *dest, unsigned *destLen); + static bool HexDecode(const char *source, unsigned sourceLen, std::string &destString); + static bool HexDecode(const char *source, unsigned sourceLen, std::vector &destVector); + + private: + static unsigned UUEncodeTripleToQuadPtr(const unsigned char *triple, char *quad, unsigned bytes); + static unsigned UUEncodeTripleToQuadRef(const std::vector::const_iterator &triple, char* quad, unsigned bytes); + static unsigned UUDecodeQuadToTriple(const char *quad, unsigned char *triple); + + static void Init(); + static unsigned char GetBase64Code(char base64char); + static unsigned char GetHexCode(char hexChar); + + static const char ms_base64Table[]; + }; +}; + +#endif // BASE64_H diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/MD5.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/MD5.cpp new file mode 100644 index 00000000..50f3887d --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/MD5.cpp @@ -0,0 +1,317 @@ +// MD5.cpp: implementation of the MD5 class. +// +////////////////////////////////////////////////////////////////////// + +#include "MD5.h" +#include "types.h" + +using namespace std; + +namespace soe +{ + + using namespace soe; + + MD5::State::State() : + state(4,0), + count(2,0), + buffer(64,0) + { + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + } + + static char const init[64] = + { + -128, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + vector MD5::padding(init,init+sizeof(init)/sizeof(init[0])); + + MD5::MD5() + { + Init(); + } + + MD5::MD5(const string & input) + { + Init(); + Update(input); + } + + vector MD5::Decode(vector achar0, int i, int j) + { + vector ai(16,0); + int l; + int k = l = 0; + for(; l < i; l += 4) + { + ai[k] = achar0[l + j] & 0xff | (achar0[l + 1 + j] & 0xff) << 8 | (achar0[l + 2 + j] & 0xff) << 16 | (achar0[l + 3 + j] & 0xff) << 24; + k++; + } + + return ai; + } + + vector MD5::Encode(vector ai, int i) + { + vector achar0(i,0); + int k; + int j = k = 0; + for(; k < i; k += 4){ + achar0[k] = (char)(ai[j] & 0xff); + achar0[k + 1] = (char)(ai[j] >> 8 & 0xff); + achar0[k + 2] = (char)(ai[j] >> 16 & 0xff); + achar0[k + 3] = (char)(ai[j] >> 24 & 0xff); + j++; + } + + return achar0; + } + + int MD5::FF(int i, int j, int k, int l, int i1, int j1, int k1) + { + i = uadd(i, j & k | ~j & l, i1, k1); + return uadd(rotate_left(i, j1), j); + } + + vector MD5::Final() + { + if (finalsNull) + { + State &state1 = state; + vector achar0 = Encode(state1.count, 8); + int i = state1.count[0] >> 3 & 0x3f; + int j = i >= 56 ? 120 - i : 56 - i; + Update(state1, padding, 0, j); + Update(state1, achar0, 0, 8); + finals = state1; + finalsNull = false; + } + return Encode(finals.state, 16); + } + + int MD5::GG(int i, int j, int k, int l, int i1, int j1, int k1) + { + i = uadd(i, j & l | k & ~l, i1, k1); + return uadd(rotate_left(i, j1), j); + } + + int MD5::HH(int i, int j, int k, int l, int i1, int j1, int k1) + { + i = uadd(i, j ^ k ^ l, i1, k1); + return uadd(rotate_left(i, j1), j); + } + + int MD5::II(int i, int j, int k, int l, int i1, int j1, int k1) + { + i = uadd(i, k ^ (j | ~l), i1, k1); + return uadd(rotate_left(i, j1), j); + } + + void MD5::Init() + { + if (padding.size() == 0) + { + padding.resize(64,0); + padding[0] = -128; + } + finalsNull = true; + } + + void MD5::Transform(State & state1, vector achar0, int i) + { + int j = state1.state[0]; + int k = state1.state[1]; + int l = state1.state[2]; + int i1 = state1.state[3]; + vector ai = Decode(achar0, 64, i); + j = FF(j, k, l, i1, ai[0], 7, 0xd76aa478); + i1 = FF(i1, j, k, l, ai[1], 12, 0xe8c7b756); + l = FF(l, i1, j, k, ai[2], 17, 0x242070db); + k = FF(k, l, i1, j, ai[3], 22, 0xc1bdceee); + j = FF(j, k, l, i1, ai[4], 7, 0xf57c0faf); + i1 = FF(i1, j, k, l, ai[5], 12, 0x4787c62a); + l = FF(l, i1, j, k, ai[6], 17, 0xa8304613); + k = FF(k, l, i1, j, ai[7], 22, 0xfd469501); + j = FF(j, k, l, i1, ai[8], 7, 0x698098d8); + i1 = FF(i1, j, k, l, ai[9], 12, 0x8b44f7af); + l = FF(l, i1, j, k, ai[10], 17, -42063); + k = FF(k, l, i1, j, ai[11], 22, 0x895cd7be); + j = FF(j, k, l, i1, ai[12], 7, 0x6b901122); + i1 = FF(i1, j, k, l, ai[13], 12, 0xfd987193); + l = FF(l, i1, j, k, ai[14], 17, 0xa679438e); + k = FF(k, l, i1, j, ai[15], 22, 0x49b40821); + j = GG(j, k, l, i1, ai[1], 5, 0xf61e2562); + i1 = GG(i1, j, k, l, ai[6], 9, 0xc040b340); + l = GG(l, i1, j, k, ai[11], 14, 0x265e5a51); + k = GG(k, l, i1, j, ai[0], 20, 0xe9b6c7aa); + j = GG(j, k, l, i1, ai[5], 5, 0xd62f105d); + i1 = GG(i1, j, k, l, ai[10], 9, 0x2441453); + l = GG(l, i1, j, k, ai[15], 14, 0xd8a1e681); + k = GG(k, l, i1, j, ai[4], 20, 0xe7d3fbc8); + j = GG(j, k, l, i1, ai[9], 5, 0x21e1cde6); + i1 = GG(i1, j, k, l, ai[14], 9, 0xc33707d6); + l = GG(l, i1, j, k, ai[3], 14, 0xf4d50d87); + k = GG(k, l, i1, j, ai[8], 20, 0x455a14ed); + j = GG(j, k, l, i1, ai[13], 5, 0xa9e3e905); + i1 = GG(i1, j, k, l, ai[2], 9, 0xfcefa3f8); + l = GG(l, i1, j, k, ai[7], 14, 0x676f02d9); + k = GG(k, l, i1, j, ai[12], 20, 0x8d2a4c8a); + j = HH(j, k, l, i1, ai[5], 4, 0xfffa3942); + i1 = HH(i1, j, k, l, ai[8], 11, 0x8771f681); + l = HH(l, i1, j, k, ai[11], 16, 0x6d9d6122); + k = HH(k, l, i1, j, ai[14], 23, 0xfde5380c); + j = HH(j, k, l, i1, ai[1], 4, 0xa4beea44); + i1 = HH(i1, j, k, l, ai[4], 11, 0x4bdecfa9); + l = HH(l, i1, j, k, ai[7], 16, 0xf6bb4b60); + k = HH(k, l, i1, j, ai[10], 23, 0xbebfbc70); + j = HH(j, k, l, i1, ai[13], 4, 0x289b7ec6); + i1 = HH(i1, j, k, l, ai[0], 11, 0xeaa127fa); + l = HH(l, i1, j, k, ai[3], 16, 0xd4ef3085); + k = HH(k, l, i1, j, ai[6], 23, 0x4881d05); + j = HH(j, k, l, i1, ai[9], 4, 0xd9d4d039); + i1 = HH(i1, j, k, l, ai[12], 11, 0xe6db99e5); + l = HH(l, i1, j, k, ai[15], 16, 0x1fa27cf8); + k = HH(k, l, i1, j, ai[2], 23, 0xc4ac5665); + j = II(j, k, l, i1, ai[0], 6, 0xf4292244); + i1 = II(i1, j, k, l, ai[7], 10, 0x432aff97); + l = II(l, i1, j, k, ai[14], 15, 0xab9423a7); + k = II(k, l, i1, j, ai[5], 21, 0xfc93a039); + j = II(j, k, l, i1, ai[12], 6, 0x655b59c3); + i1 = II(i1, j, k, l, ai[3], 10, 0x8f0ccc92); + l = II(l, i1, j, k, ai[10], 15, 0xffeff47d); + k = II(k, l, i1, j, ai[1], 21, 0x85845dd1); + j = II(j, k, l, i1, ai[8], 6, 0x6fa87e4f); + i1 = II(i1, j, k, l, ai[15], 10, 0xfe2ce6e0); + l = II(l, i1, j, k, ai[6], 15, 0xa3014314); + k = II(k, l, i1, j, ai[13], 21, 0x4e0811a1); + j = II(j, k, l, i1, ai[4], 6, 0xf7537e82); + i1 = II(i1, j, k, l, ai[11], 10, 0xbd3af235); + l = II(l, i1, j, k, ai[2], 15, 0x2ad7d2bb); + k = II(k, l, i1, j, ai[9], 21, 0xeb86d391); + state1.state[0] += j; + state1.state[1] += k; + state1.state[2] += l; + state1.state[3] += i1; + } + + void MD5::Update(char char0) + { + vector achar0(1,0); + achar0[0] = char0; + Update(achar0, 1); + } + + void MD5::Update(State & state1, vector achar0, int i, int j) + { + finalsNull = true; + if (j - i > (int)achar0.size()) + j = achar0.size() - i; + int k = state1.count[0] >> 3 & 0x3f; + if ((state1.count[0] += j << 3) < j << 3) + state1.count[1]++; + state1.count[1] += j >> 29; + int l = 64 - k; + int j1; + if(j >= l) + { + for(int i1 = 0; i1 < l; i1++) + state1.buffer[i1 + k] = achar0[i1 + i]; + + Transform(state1, state1.buffer, 0); + for(j1 = l; j1 + 63 < j; j1 += 64) + Transform(state1, achar0, j1); + + k = 0; + } + else + { + j1 = 0; + } + if (j1 < j) + { + int k1 = j1; + for(; j1 < j; j1++) + state1.buffer[(k + j1) - k1] = achar0[j1 + i]; + } + } + + void MD5::Update(string s) + { + vector achar(s.size(),0); + for (int i=0; i<(int)s.size(); i++) + achar[i] = s[i]; + Update( achar, achar.size() ); + } + + void MD5::Update(vector achar0) + { + Update(achar0, 0, achar0.size()); + } + + void MD5::Update(vector achar0, int i) + { + Update(state, achar0, 0, i); + } + + void MD5::Update(vector achar0, int i, int j) + { + Update(state, achar0, i, j); + } + + string MD5::asHex() + { + return asHex(Final()); + } + + string MD5::asHex(vector achar0) + { + const string hex = "0123456789abcdef"; + + string stringbuffer; + for (int i = 0; i < (int)achar0.size(); i++) + { + stringbuffer += hex.substr((achar0[i] >> 4) & 0x0f,1); + stringbuffer += hex.substr(achar0[i] & 0x0f,1); + } + + return stringbuffer; + } + + int MD5::rotate_left(int i, int j) + { + unsigned i1 = i; + unsigned j1 = j; + return i1 << j1 | i1 >> (32 - j1); + } + + int MD5::uadd(int i, int j) + { + int64 l = (int64)i & 0x0ffffffffL; + int64 l1 = (int64)j & 0x0ffffffffL; + l += l1; + return (int)(l & 0x0ffffffffL); + } + + int MD5::uadd(int i, int j, int k) + { + return uadd(uadd(i, j), k); + } + + int MD5::uadd(int i, int j, int k, int l) + { + return uadd(uadd(i, j, k), l); + } + +}; + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/MD5.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/MD5.h new file mode 100644 index 00000000..d741ba1b --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/MD5.h @@ -0,0 +1,66 @@ +// MD5.h: interface for the MD5 class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef MD5_H +#define MD5_H + +#include +#include + + +namespace soe +{ + + + class MD5 + { + struct State + { + State(); + + std::vector state; + std::vector count; + std::vector buffer; + }; + + public: + MD5::MD5(); + MD5(const std::string &input); + + void Init(); + std::vector Final(); + void Update(char char0); + void Update(State & state1, std::vector achar0, int i, int j); + void Update(std::string s); + void Update(std::vector achar0); + void Update(std::vector achar0, int i); + void Update(std::vector achar0, int i, int j); + std::string asHex(); + static std::string asHex(std::vector achar0); + + private: + std::vector Decode(std::vector achar0, int i, int j); + std::vector Encode(std::vector ai, int i); + int FF(int i, int j, int k, int l, int i1, int j1, int k1); + int GG(int i, int j, int k, int l, int i1, int j1, int k1); + int HH(int i, int j, int k, int l, int i1, int j1, int k1); + int II(int i, int j, int k, int l, int i1, int j1, int k1); + void Transform(State & state1, std::vector achar0, int i); + int rotate_left(int i, int j); + int uadd(int i, int j); + int uadd(int i, int j, int k); + int uadd(int i, int j, int k, int l); + + private: + State state; + State finals; + bool finalsNull; + static std::vector padding; + + }; + + +}; + +#endif // MD5_H diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/basicConfig.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/basicConfig.cpp new file mode 100644 index 00000000..7fe3361c --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/basicConfig.cpp @@ -0,0 +1,128 @@ +#ifdef WIN32 +#pragma warning( disable: 4786 ) +#endif + +#include +#include "basicConfig.h" +#include "profile.h" + +#ifdef WIN32 +#define snprintf _snprintf +#endif + + +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// + + +BasicConfig::BasicConfig() + : mParamMap() + , mConfigFileName() +{ +} + +BasicConfig::~BasicConfig() +{ +} + +void BasicConfig::Push(const soe::NameValuePairs_t & paramValuePairs) +{ + for (soe::NameValuePairs_t::const_iterator pIter = paramValuePairs.begin(); pIter != paramValuePairs.end(); pIter++) + { + Set(pIter->name, pIter->value); + } +} + +void BasicConfig::Pull(soe::NameValuePairs_t & paramValuePairs) const +{ + size_t index = paramValuePairs.size(); + + paramValuePairs.resize(index + mParamMap.size()); + for (ConfigMap_t::const_iterator pIter = mParamMap.begin(); pIter != mParamMap.end(); pIter++, index++) + { + paramValuePairs[index].name = pIter->first; + paramValuePairs[index].value = pIter->second.first; + } +} + +void BasicConfig::Load(const std::string & configFile) +{ + static const char * config_delimiters1 = " \t\n:[]="; + static const char * config_delimiters2 = "\t\n\"#"; + + Profile profile("BasicConfig::Load()"); + FILE * file; + if (!(file = fopen(configFile.c_str(), "rt"))) + return; + + char buffer[1024]; + while(fgets(buffer, 1024, file)) + { + char * key = 0; + char * value = 0; + + // read key and value off line + key = strtok(buffer, config_delimiters1); + if (!key || key[0] == '#') + continue; + if (key) + { + value = key + strlen(key) + 1; + while (*value == ' ' || *value == '\t') + value++; + value = strtok(value, config_delimiters2); + if (value) + { + while (value[strlen(value)-1] == ' ') + value[strlen(value)-1] = 0; + } + } + + // insert key/value pair into the param map + if (value) + mParamMap[key] = ConfigPair_t(value, atoi(value)); + else + mParamMap[key] = ConfigPair_t("", 0); + } + + mConfigFileName = configFile; + + fclose(file); + + OnLoad(); +} + +void BasicConfig::Set(const std::string & param, int value) +{ + // convert integer to string + char buffer[64]; + snprintf(buffer, sizeof(buffer), "%d", value); + + mParamMap[param] = ConfigPair_t(buffer, value); +} + +void BasicConfig::Set(const std::string & param, const std::string & value) +{ + mParamMap[param] = ConfigPair_t(value, atoi(value.c_str())); +} + +int BasicConfig::GetNumber(const std::string & param) +{ + return mParamMap[param].second; +} + +std::string BasicConfig::GetString(const std::string & param) +{ + return mParamMap[param].first; +} + +bool BasicConfig::IsSet(const std::string & param) +{ + return mParamMap.find(param) != mParamMap.end(); +} + +void BasicConfig::OnLoad() +{ +} + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/basicConfig.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/basicConfig.h new file mode 100644 index 00000000..27035882 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/basicConfig.h @@ -0,0 +1,43 @@ +#ifndef SERVER__BASIC_CONFIG_H +#define SERVER__BASIC_CONFIG_H + +#include +#include +#include + +#include "Api/apiTypeNameValuePair.h" +#include "stringutils.h" + +class BasicConfig +{ + public: + typedef std::pair ConfigPair_t; + typedef std::map ConfigMap_t; + + BasicConfig(); + virtual ~BasicConfig(); + + void Push(const soe::NameValuePairs_t & paramValuePairs); + void Pull(soe::NameValuePairs_t & paramValuePairs) const; + void Load(const std::string & configFile); + void Set(const std::string & param, int value); + void Set(const std::string & param, const std::string & value); + int GetNumber(const std::string & param); + std::string GetString(const std::string & param); + bool IsSet(const std::string & param); + std::string GetConfigFileName() const { return mConfigFileName; } + + virtual void OnLoad(); + + protected: + ConfigMap_t mParamMap; + std::string mConfigFileName; + private: + char * pConfig; // pointer to file memory + char * pCursor; // current cursor into file memory +}; + + +#endif + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/cmdLine.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/cmdLine.cpp new file mode 100644 index 00000000..a90d602c --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/cmdLine.cpp @@ -0,0 +1,285 @@ +/*------------------------------------------------------ + CmdLine + + A utility for parsing command lines. + + Copyright (C) 1999 Chris Losinger, Smaller Animals Software. + http://www.smalleranimals.com + + This software is provided 'as-is', without any express + or implied warranty. In no event will the authors be + held liable for any damages arising from the use of this software. + + Permission is granted to anyone to use this software + for any purpose, including commercial applications, and + to alter it and redistribute it freely, subject to the + following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + See SACmds.h for more info. +------------------------------------------------------*/ + +#pragma warning (disable: 4267) +// if you're using MFC, you'll need to un-comment this line +// #include "stdafx.h" + +#include "cmdLine.h" +//#include "crtdbg.h" +#include + +#ifdef NAMESPACE +namespace NAMESPACE +{ +#endif + +/*------------------------------------------------------ + int CmdLine::SplitLine(int argc, char **argv) + + parse the command line into switches and arguments + + returns number of switches found +------------------------------------------------------*/ +int CmdLine::SplitLine(int argc, char **argv) +{ + clear(); + + StringType curParam; // current argv[x] + + // skip the exe name (start with i = 1) + for (int i = 1; i < argc; i++) + { + // if it's a switch, start a new CmdLine + if (IsSwitch(argv[i])) + { + curParam = argv[i]; + + StringType arg; + + // look at next input string to see if it's a switch or an argument + if (i + 1 < argc) + { + if (!IsSwitch(argv[i + 1])) + { + // it's an argument, not a switch + arg = argv[i + 1]; + + // skip to next + i++; + } + else + { + arg = ""; + } + } + + // add it + CmdParam cmd; + + // only add non-empty args + if (arg != "") + { + cmd.m_strings.push_back(arg); + } + + // add the CmdParam to 'this' + std::pair res = insert(CmdLine::value_type(curParam, cmd)); + if (res.second) continue; + } + else + { + // it's not a new switch, so it must be more stuff for the last switch + + // ...let's add it + CmdLine::iterator theIterator; + + // get an iterator for the current param + theIterator = find(curParam); + if (theIterator!=end()) + { + (*theIterator).second.m_strings.push_back(argv[i]); + } + else + { + // ?? + } + } + } + + return size(); +} + +/*------------------------------------------------------ + + protected member function + test a parameter to see if it's a switch : + + switches are of the form : -x + where 'x' is one or more characters. + the first character of a switch must be non-numeric! + +------------------------------------------------------*/ + +bool CmdLine::IsSwitch(const char *pParam) +{ + if (pParam==NULL) + return false; + + // switches must non-empty + // must have at least one character after the '-' + int len = strlen(pParam); + if (len <= 1) + { + return false; + } + + // switches always start with '-' + if (pParam[0]=='-') + { + // allow negative numbers as arguments. + // ie., don't count them as switches + return (!isdigit(pParam[1])); + } + else + { + return false; + } +} + +/*------------------------------------------------------ + bool CmdLine::HasSwitch(const char *pSwitch) + + was the switch found on the command line ? + + ex. if the command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5 + + call return + ---- ------ + cmdLine.HasSwitch("-a") true + cmdLine.HasSwitch("-z") false +------------------------------------------------------*/ + +bool CmdLine::HasSwitch(const char *pSwitch) +{ + CmdLine::iterator theIterator; + theIterator = find(pSwitch); + return (theIterator!=end()); +} + +/*------------------------------------------------------ + + StringType CmdLine::GetSafeArgument(const char *pSwitch, int iIdx, const char *pDefault) + + fetch an argument associated with a switch . if the parameter at + index iIdx is not found, this will return the default that you + provide. + + example : + + command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5 + + call return + ---- ------ + cmdLine.GetSafeArgument("-a", 0, "zz") p1 + cmdLine.GetSafeArgument("-a", 1, "zz") p2 + cmdLine.GetSafeArgument("-b", 0, "zz") p4 + cmdLine.GetSafeArgument("-b", 1, "zz") zz + +------------------------------------------------------*/ + +StringType CmdLine::GetSafeArgument(const char *pSwitch, int iIdx, const char *pDefault) +{ + StringType sRet; + + if (pDefault!=NULL) + sRet = pDefault; + + try + { + sRet = GetArgument(pSwitch, iIdx); + } + catch (...) + { + } + + return sRet; +} + +/*------------------------------------------------------ + + StringType CmdLine::GetArgument(const char *pSwitch, int iIdx) + + fetch a argument associated with a switch. throws an exception + of (int)0, if the parameter at index iIdx is not found. + + example : + + command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5 + + call return + ---- ------ + cmdLine.GetArgument("-a", 0) p1 + cmdLine.GetArgument("-b", 1) throws (int)0, returns an empty string + +------------------------------------------------------*/ + +StringType CmdLine::GetArgument(const char *pSwitch, int iIdx) +{ + if (HasSwitch(pSwitch)) + { + CmdLine::iterator theIterator; + + theIterator = find(pSwitch); + if (theIterator!=end()) + { + if ((int)(*theIterator).second.m_strings.size() > iIdx) + { + return (*theIterator).second.m_strings[iIdx]; + } + } + } + + throw (int)0; + + return ""; +} + +/*------------------------------------------------------ + int CmdLine::GetArgumentCount(const char *pSwitch) + + returns the number of arguments found for a given switch. + + returns -1 if the switch was not found + +------------------------------------------------------*/ + +int CmdLine::GetArgumentCount(const char *pSwitch) +{ + int iArgumentCount = -1; + + if (HasSwitch(pSwitch)) + { + CmdLine::iterator theIterator; + + theIterator = find(pSwitch); + if (theIterator!=end()) + { + iArgumentCount = (*theIterator).second.m_strings.size(); + } + } + + return iArgumentCount; +} + +#ifdef NAMESPACE +} +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/cmdLine.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/cmdLine.h new file mode 100644 index 00000000..3ed7fc82 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/cmdLine.h @@ -0,0 +1,245 @@ +/*------------------------------------------------------ + CmdLine + + A utility for parsing command lines. + + Copyright (C) 1999 Chris Losinger, Smaller Animals Software. + http://www.smalleranimals.com + + This software is provided 'as-is', without any express + or implied warranty. In no event will the authors be + held liable for any damages arising from the use of this software. + + Permission is granted to anyone to use this software + for any purpose, including commercial applications, and + to alter it and redistribute it freely, subject to the + following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + ------------------------- + + Example : + + Our example application uses a command line that has two + required switches and two optional switches. The app should abort + if the required switches are not present and continue with default + values if the optional switches are not present. + + Sample command line : + MyApp.exe -p1 text1 text2 -p2 "this is a big argument" -opt1 -55 -opt2 + + Switches -p1 and -p2 are required. + p1 has two arguments and p2 has one. + + Switches -opt1 and -opt2 are optional. + opt1 requires a numeric argument. + opt2 has no arguments. + + Also, assume that the app displays a 'help' screen if the '-h' switch + is present on the command line. + + #include "CmdLine.h" + + void main(int argc, char **argv) + { + // our cmd line parser object + CmdLine cmdLine; + + // parse argc,argv + if (cmdLine.SplitLine(argc, argv) < 1) + { + // no switches were given on the command line, abort + ASSERT(0); + exit(-1); + } + + // test for the 'help' case + if (cmdLine.HasSwitch("-h")) + { + show_help(); + exit(0); + } + + // get the required arguments + StringType p1_1, p1_2, p2_1; + try + { + // if any of these fail, we'll end up in the catch() block + p1_1 = cmdLine.GetArgument("-p1", 0); + p1_2 = cmdLine.GetArgument("-p1", 1); + p2_1 = cmdLine.GetArgument("-p2", 0); + + } + catch (...) + { + // one of the required arguments was missing, abort + ASSERT(0); + exit(-1); + } + + // get the optional parameters + + // convert to an int, default to '100' + int iOpt1Val = atoi(cmdLine.GetSafeArgument("-opt1", 0, 100)); + + // since opt2 has no arguments, just test for the presence of + // the '-opt2' switch + bool bOptVal2 = cmdLine.HasSwitch("-opt2"); + + .... and so on.... + + } + + If this class is used in an MFC application, StringType is CString, else + it uses the STL 'string' type. + + If this is an MFC app, you can use the __argc and __argv macros from + you CYourWinApp::InitInstance() function in place of the standard argc + and argv variables. + +------------------------------------------------------*/ +#ifndef SACMDSH +#define SACMDSH + + +//#include // you may need this +#include +#include +#include +//using namespace std ; + +#ifdef NAMESPACE +namespace NAMESPACE +{ +#endif + +#ifdef __AFX_H__ +// if we're using MFC, use CStrings +#define StringType CString +#else +// if we're not using MFC, use STL strings +#define StringType std::string +#endif + +// tell the compiler to shut up +#pragma warning(disable:4786) + + +// handy little container for our argument vector +struct CmdParam +{ + std::vector m_strings; +}; + +// this class is actually a map of strings to vectors +typedef std::map _CmdLine; + +// the command line parser class +class CmdLine : public _CmdLine +{ + +public: + /*------------------------------------------------------ + int CmdLine::SplitLine(int argc, char **argv) + + parse the command line into switches and arguments. + + returns number of switches found + ------------------------------------------------------*/ + int SplitLine(int argc, char **argv); + + /*------------------------------------------------------ + bool CmdLine::HasSwitch(const char *pSwitch) + + was the switch found on the command line ? + + ex. if the command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5 + + call return + ---- ------ + cmdLine.HasSwitch("-a") true + cmdLine.HasSwitch("-z") false + ------------------------------------------------------*/ + bool HasSwitch(const char *pSwitch); + + /*------------------------------------------------------ + + StringType CmdLine::GetSafeArgument(const char *pSwitch, int iIdx, const char *pDefault) + + fetch an argument associated with a switch . if the parameter at + index iIdx is not found, this will return the default that you + provide. + + example : + + command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5 + + call return + ---- ------ + cmdLine.GetSafeArgument("-a", 0, "zz") p1 + cmdLine.GetSafeArgument("-a", 1, "zz") p2 + cmdLine.GetSafeArgument("-b", 0, "zz") p4 + cmdLine.GetSafeArgument("-b", 1, "zz") zz + + ------------------------------------------------------*/ + + StringType GetSafeArgument(const char *pSwitch, int iIdx, const char *pDefault); + + /*------------------------------------------------------ + + StringType CmdLine::GetArgument(const char *pSwitch, int iIdx) + + fetch a argument associated with a switch. throws an exception + of (int)0, if the parameter at index iIdx is not found. + + example : + + command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5 + + call return + ---- ------ + cmdLine.GetArgument("-a", 0) p1 + cmdLine.GetArgument("-b", 1) throws (int)0, returns an empty string + + ------------------------------------------------------*/ + StringType GetArgument(const char *pSwitch, int iIdx); + + /*------------------------------------------------------ + int CmdLine::GetArgumentCount(const char *pSwitch) + + returns the number of arguments found for a given switch. + + returns -1 if the switch was not found + + ------------------------------------------------------*/ + int GetArgumentCount(const char *pSwitch); + +protected: + /*------------------------------------------------------ + + protected member function + test a parameter to see if it's a switch : + + switches are of the form : -x + where 'x' is one or more characters. + the first character of a switch must be non-numeric! + + ------------------------------------------------------*/ + bool IsSwitch(const char *pParam); +}; + +#ifdef NAMESPACE +} +#endif + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/date.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/date.cpp new file mode 100644 index 00000000..025883cd --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/date.cpp @@ -0,0 +1,178 @@ +#include "date.h" + +#ifdef WIN32 +# include +# include +#endif + +#include +#include + +#ifdef WIN32 +#define snprintf _snprintf +#endif + + +namespace soe +{ + static time_t date_timegm(struct tm *T) + { +#ifdef WIN32 + struct _timeb t; + + memset((void*)&t, 0, sizeof(t)); + t.timezone = 0; + t.dstflag = -1; + _ftime(&t); + T->tm_min -= t.timezone; + T->tm_isdst = 0; + + return mktime(T); +#else + return timegm(T); +#endif + } + + Date::Date(time_t src) + : mYear(1900) + , mMonth(1) + , mDay(1) + , mHour(0) + , mMinute(0) + , mSecond(0) + { + struct tm timeStruct; + struct tm *gotTime = NULL; +#ifdef WIN32 + gotTime = gmtime(&src); +#else + gotTime = gmtime_r(&src, &timeStruct); +#endif + + if (gotTime) { +#ifdef WIN32 + timeStruct = *gotTime; +#endif + mYear = timeStruct.tm_year + 1900; + mMonth = timeStruct.tm_mon + 1; + mDay = timeStruct.tm_mday; + mHour = timeStruct.tm_hour; + mMinute = timeStruct.tm_min; + mSecond = timeStruct.tm_sec; + } + } + + Date::Date(const std::string &src) + : mYear(0) + , mMonth(0) + , mDay(0) + , mHour(0) + , mMinute(0) + , mSecond(0) + { +#ifdef WIN32 + // This code should actually be portable to Linux as well - rlsmith 05/31/2006 + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int minute = 0; + int second = 0; + int res(0); + if ( + (6 == (res=sscanf(src.c_str(), "%4d-%02d-%02d %02d:%02d:%02d", &year, &month, &day, &hour, &minute, &second))) || + (6 == (res=sscanf(src.c_str(), "%4d/%02d/%02d %02d:%02d:%02d", &year, &month, &day, &hour, &minute, &second))) || + (6 == (res=sscanf(src.c_str(), "%02d-%02d-%4d %02d:%02d:%02d", &month, &day, &year, &hour, &minute, &second))) || + (6 == (res=sscanf(src.c_str(), "%02d/%02d/%4d %02d:%02d:%02d", &month, &day, &year, &hour, &minute, &second))) || + (3 == (res=sscanf(src.c_str(), "%02d-%02d-%4d", &month, &day, &year))) || + (3 == (res=sscanf(src.c_str(), "%02d/%02d/%4d", &month, &day, &year))) || + (3 == (res=sscanf(src.c_str(), "%4d-%02d-%02d", &year, &month, &day))) || + (3 == (res=sscanf(src.c_str(), "%4d/%02d/%02d", &year, &month, &day))) + ) + { + if (res < 6) // not all time values were parsed, so reset time to 00:00:00 + { + mHour = 0; + mMinute = 0; + mSecond = 0; + } + mYear = year; + mMonth = month; + mDay = day; + mHour = hour; + mMinute = minute; + mSecond = second; + } +#else + tm timeStruct; + memset(&timeStruct, 0, sizeof(timeStruct)); + + if ( + strptime(src.c_str(), "%Y-%m-%d %H:%M:%S", &timeStruct) || strptime(src.c_str(), "%Y/%m/%d %H:%M:%S", &timeStruct) + || strptime(src.c_str(), "%m-%d-%Y %H:%M:%S", &timeStruct) || strptime(src.c_str(), "%m/%d/%Y %H:%M:%S", &timeStruct) + || strptime(src.c_str(), "%Y-%m-%d", &timeStruct) || strptime(src.c_str(), "%Y/%m/%d", &timeStruct) + || strptime(src.c_str(), "%m-%d-%Y", &timeStruct) || strptime(src.c_str(), "%m/%d/%Y", &timeStruct) + ) + { + mYear = timeStruct.tm_year + 1900; + mMonth = timeStruct.tm_mon + 1; + mDay = timeStruct.tm_mday; + mHour = timeStruct.tm_hour; + mMinute = timeStruct.tm_min; + mSecond = timeStruct.tm_sec; + } +#endif + } + + Date::operator std::string() const + { + char buffer[256]; + + snprintf(buffer, sizeof(buffer), PRINT_FORMAT(), mYear, mMonth, mDay, mHour, mMinute, mSecond); + + return std::string(buffer); + } + + Date::operator time_t() const + { + struct tm timeStruct; + + timeStruct.tm_year = mYear - 1900; + timeStruct.tm_mon = mMonth - 1; + timeStruct.tm_mday = mDay; + timeStruct.tm_hour = mHour; + timeStruct.tm_min = mMinute; + timeStruct.tm_sec = mSecond; + + time_t t = date_timegm(&timeStruct); + + return t; + } + +#define COMPARE_MEMBER(__member_name__, __oper__) \ + if (__member_name__ != other.__member_name__) { \ + return (__member_name__ __oper__ other.__member_name__); \ + } else + +#define COMPARE_MEMBER_END(__member_name__, __oper__) \ + { \ + return (__member_name__ __oper__ other.__member_name__); \ + } + +#define COMPARE_DATE_OPER_IMPL(__oper__) \ + bool Date::operator __oper__(const Date & other) const \ + { \ + COMPARE_MEMBER(mYear, __oper__) \ + COMPARE_MEMBER(mMonth, __oper__) \ + COMPARE_MEMBER(mDay, __oper__) \ + COMPARE_MEMBER(mHour, __oper__) \ + COMPARE_MEMBER(mMinute, __oper__) \ + COMPARE_MEMBER_END(mSecond, __oper__) \ + } + + COMPARE_DATE_OPER_IMPL(>) + COMPARE_DATE_OPER_IMPL(>=) + COMPARE_DATE_OPER_IMPL(==) + COMPARE_DATE_OPER_IMPL(<=) + COMPARE_DATE_OPER_IMPL(<) +} diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/date.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/date.h new file mode 100644 index 00000000..e9bdb311 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/date.h @@ -0,0 +1,94 @@ +#ifndef SOE__DATE_H +#define SOE__DATE_H + +#include "types.h" +#include "serializeClasses.h" + +namespace soe +{ + + struct Date + { + Date() + : mYear(0) + , mMonth(0) + , mDay(0) + , mHour(0) + , mMinute(0) + , mSecond(0) + { } + + Date(time_t src); + Date(const std::string &src); + + operator std::string() const; + operator time_t() const; + + bool operator >(const Date & other) const; + bool operator >=(const Date & other) const; + bool operator ==(const Date & other) const; + bool operator <=(const Date & other) const; + bool operator <(const Date & other) const; + + static const char * const DATA_FORMAT() { return "YYYY-MM-DD HH24:MI:SS"; } + static const char * const PRINT_FORMAT() { return "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d"; } + + soe::uint16 mYear; + soe::uint8 mMonth; + soe::uint8 mDay; + soe::uint8 mHour; + soe::uint8 mMinute; + soe::uint8 mSecond; + }; + + inline unsigned Read(const unsigned char * stream, unsigned size, Date & data, unsigned maxLen = 1, unsigned version = 0) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = Read(stream + bytesTotal, size - bytesTotal, data.mYear)); + if (!bytes) return 0; + bytesTotal += (bytes = Read(stream + bytesTotal, size - bytesTotal, data.mMonth)); + if (!bytes) return 0; + bytesTotal += (bytes = Read(stream + bytesTotal, size - bytesTotal, data.mDay)); + if (!bytes) return 0; + bytesTotal += (bytes = Read(stream + bytesTotal, size - bytesTotal, data.mHour)); + if (!bytes) return 0; + bytesTotal += (bytes = Read(stream + bytesTotal, size - bytesTotal, data.mMinute)); + if (!bytes) return 0; + bytesTotal += (bytes = Read(stream + bytesTotal, size - bytesTotal, data.mSecond)); + if (!bytes) return 0; + + return bytesTotal; + } + + inline unsigned Write(unsigned char * stream, unsigned size, const Date & data, unsigned version = 0) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = Write(stream + bytesTotal, size - bytesTotal, data.mYear)); + if (!bytes) return 0; + bytesTotal += (bytes = Write(stream + bytesTotal, size - bytesTotal, data.mMonth)); + if (!bytes) return 0; + bytesTotal += (bytes = Write(stream + bytesTotal, size - bytesTotal, data.mDay)); + if (!bytes) return 0; + bytesTotal += (bytes = Write(stream + bytesTotal, size - bytesTotal, data.mHour)); + if (!bytes) return 0; + bytesTotal += (bytes = Write(stream + bytesTotal, size - bytesTotal, data.mMinute)); + if (!bytes) return 0; + bytesTotal += (bytes = Write(stream + bytesTotal, size - bytesTotal, data.mSecond)); + if (!bytes) return 0; + + return bytesTotal; + } +#ifdef PRINTABLE_MESSAGES + inline int Print(char * stream, unsigned size, const Date & data, unsigned maxDepth = INT_MAX) + { + return snprintf(stream, size, Date::PRINT_FORMAT(), data.mYear, data.mMonth, data.mDay, data.mHour, data.mMinute, data.mSecond); + } +#endif +} + +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/dateUtils.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/dateUtils.h new file mode 100644 index 00000000..fecca883 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/dateUtils.h @@ -0,0 +1,227 @@ +#ifndef _SOE_DATE_UTILS_H_ +#define _SOE_DATE_UTILS_H_ + +#include + +#ifdef USE_ICU + #include + #include + #include +#endif + +#define DST_OFFSET_MINUTES_USA 60 // For USA, offset is 1 hour (60 minutes) for Daylight Saving Time +namespace soe +{ + inline time_t startOfWeek(time_t now, int offset) + { + time_t result = now; + + result += (offset * 7 * 24 * 60 * 60);//adjust time by #seconds in week for every week offset specifies (could be negative) + + tm nowTm; + + //get tm struct + memcpy(&nowTm, localtime(&now), sizeof(tm)); + + //reduce input time_t by seconds applied to the current week in progress + result -= (nowTm.tm_wday * 24 * 60 * 60);//reduce by whole days that have passed since beginning of week + result -= (nowTm.tm_hour * 60 * 60);//reduce by seconds that have passed today in complete hours today + result -= (nowTm.tm_min * 60);//reduce by seconds that have passed in partial hours today + result -= (nowTm.tm_sec);//reduce by seconds that have passed in partial minutes today + + return result; + } + + inline time_t startOfDay(time_t now, int offset) + { + time_t result = now; + + now += (offset * 24 * 60 * 60);//adjust time by #seconds in day for every day offset specifies (could be negative) + + tm nowTm; + + //get tm struct + memcpy(&nowTm, localtime(&now), sizeof(tm)); + + //reset hour/min/sec so go to beginning of day + nowTm.tm_hour = 0; + nowTm.tm_min = 0; + nowTm.tm_sec = 0; + + //turn that into time_t result + result = mktime(&nowTm); + + return result; + } + + inline time_t startOfMonth(time_t now, int offset) + { + //TODO: seriously check this algorithm + time_t result = now; + + //note: can't do pre-adjustment for offset of months, since months can vary in length + + tm nowTm; + + //get tm struct + memcpy(&nowTm, localtime(&now), sizeof(tm)); + + nowTm.tm_hour = 0; + nowTm.tm_min = 0; + nowTm.tm_sec = 0; + nowTm.tm_mday = 1;//day of month will be 1, since we want time_t of beginning of month + + + //calc new month (and possibly year) from offset + if (offset < 0) + { + if (nowTm.tm_mon >= (-1 * offset)) + { + //stay in current year + nowTm.tm_mon += offset; + } + else + { + + nowTm.tm_mon = nowTm.tm_mon + (offset % 12) - 12; + + //need to adjust year also + if (-12 <= offset) + { + nowTm.tm_year -= 1; + } + else + { + nowTm.tm_year += (offset / 12); + } + } + } + else + { + if (11 - nowTm.tm_mon <= offset) + { + //stay in current year + nowTm.tm_mon += offset; + } + else + { + + nowTm.tm_mon = (nowTm.tm_mon + (offset % 12)) - 12; + + //need to adjust year also + if (12 >= offset) + { + nowTm.tm_year += 1; + } + else + { + nowTm.tm_year += (offset / 12); + } + } + } + + + //turn that into time_t result + result = mktime(&nowTm); + + return result; + } + + inline bool isTimeBeforeDay(time_t now, time_t check, int offset=0) + { + return (startOfDay(now, offset) > check); + } + + inline bool isTimeBeforeWeek(time_t now, time_t check, int offset=0) + { + return (startOfWeek(now, offset) > check); + } + + inline bool isTimeBeforeMonth(time_t now, time_t check, int offset=0) + { + return (startOfMonth(now, offset) > check); + } + + inline time_t localTimeToGmt(int dstOffsetMinutes = DST_OFFSET_MINUTES_USA) + { + time_t localTime = time(NULL); + struct tm gt = *(gmtime(&localTime)); + struct tm lt = *(localtime(&localTime)); + bool isDst = (lt.tm_isdst? true:false); + return (mktime(>) - (isDst? (60 * dstOffsetMinutes):0)); + } + +#ifdef USE_ICU + inline int getOffsets(const char *id, time_t gmtTime, bool isLocal, int & timeZoneOffsetSecs, int & dstOffsetSecs) + { + double d = gmtTime * 1000.00; + UDate date(d); + UBool local(isLocal); + int32_t rawOffsetMS; + int32_t dstOffsetMS; + UErrorCode ec = U_ZERO_ERROR; + int retval(0); + + TimeZone *tz; + if (!id) + tz = TimeZone::createDefault(); + else + tz = TimeZone::createTimeZone(id); + + Calendar *cal = Calendar::createInstance(tz, ec); + + if (U_FAILURE(ec)) + { + //cerr<<"cal failed: ec=("<getNow(); + tz->getOffset(date, local, rawOffsetMS, dstOffsetMS, ec); + if (U_FAILURE(ec)) + { + //cerr<<"tz failed: ec=("<getNow(); //FIXME: this is not returning UTC. it is our time in UTC. + //date = cal->getTime(ec); + tz->getOffset(date, isLocal, rawOffsetMS, dstOffsetMS, ec); + + UDate tmpDate = date + rawOffsetMS + (cal->inDaylightTime(ec) ? dstOffsetMS : 0); + theTime = (long)(tmpDate/1000.00); + + return retval; + } + +#endif +} + +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/expirationQueue.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/expirationQueue.h new file mode 100644 index 00000000..59f30167 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/expirationQueue.h @@ -0,0 +1,103 @@ +#ifndef EXPIRATION_QUEUE_H +#define EXPIRATION_QUEUE_H + +#include +#include +#include + +template +class expiration_queue +{ +public: + expiration_queue() { } + virtual ~expiration_queue() { } + + bool empty() const { return m_expirationMap.empty(); } + bool contains_expired_items(TimeType when) const; + bool contains_item(const T & item) const; + size_t size() const { return m_itemMap.size(); } + const T & top() const; + bool insert(const T & item, TimeType expiration_time); + bool erase(const T & item); + void clear(); + +private: + typedef std::set item_set_t; + typedef std::map expiration_map_t; + typedef std::map item_map_t; + + expiration_map_t m_expirationMap; + item_map_t m_itemMap; +}; + +template +bool expiration_queue::contains_expired_items(TimeType when) const +{ + return (!m_expirationMap.empty() && + m_expirationMap.begin()->first <= when); +} + +template +bool expiration_queue::contains_item(const T & item) const +{ + return (m_itemMap.find(item) != m_itemMap.end()); +} + +template +const T & expiration_queue::top() const +{ + static T dummy; + + if (!m_expirationMap.empty() && !m_expirationMap.begin()->second.empty()) { + const item_set_t & firstSet = m_expirationMap.begin()->second; + + return *firstSet.begin(); + } + + return dummy; +} + +template +bool expiration_queue::insert(const T & item, TimeType expiration_time) +{ + bool erased = erase(item); + + m_itemMap[item] = expiration_time; + m_expirationMap[expiration_time].insert(item); + + return !erased; +} + +template +bool expiration_queue::erase(const T & item) +{ + typename item_map_t::iterator itIter = m_itemMap.find(item); + bool erased = false; + + if (itIter != m_itemMap.end()) { + typename expiration_map_t::iterator expIter = m_expirationMap.find(itIter->second); + + if (expIter != m_expirationMap.end()) { + item_set_t & foundSet = expIter->second; + + erased = (foundSet.erase(item) > 0); + + if (foundSet.empty()) { + m_expirationMap.erase(expIter); + } + } + + m_itemMap.erase(itIter); + } + + return erased; +} + +template +void expiration_queue::clear() +{ + m_expirationMap.clear(); m_itemMap.clear(); +} + +#endif // EXPIRATION_QUEUE_H + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/genericRateLimitingMechanism.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/genericRateLimitingMechanism.h new file mode 100644 index 00000000..20b16fa1 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/genericRateLimitingMechanism.h @@ -0,0 +1,533 @@ +#ifndef __GENERIC_RATE_LIMITING_MECHANISM_H__ +#define __GENERIC_RATE_LIMITING_MECHANISM_H__ + +#include "expirationQueue.h" +#include "refptr.h" +#include "rateLimit.h" + +#define KEY_PAIR_COMP_OPER(__oper__) \ + bool operator __oper__ (const KeyPair & rhs) const \ + { \ + if (mFixedKey != rhs.mFixedKey) { \ + return (mFixedKey __oper__ rhs.mFixedKey); \ + } else { \ + return (mArbitraryKey __oper__ rhs.mArbitraryKey); \ + } \ + } + +namespace soe +{ + // create a class with the same interface in order to use different limit and counter types for customized behavior + class RateCounter + { + public: + RateCounter() : mCount(0), mLimit(0), mIndex(0), mInterval(0) { } + RateCounter(limit_t count, limit_t limit, interval_t interval, size_t index) + : mCount(count), mLimit(limit), mIndex(index), mInterval(interval) { } + virtual ~RateCounter() { } + + unsigned Write(unsigned char * stream, unsigned size) const + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, mCount)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, mLimit)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, mInterval)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Write(stream+bytesTotal, size-bytesTotal, mIndex)); + if (!bytes) return 0; + + return bytesTotal; + } + + unsigned Read(const unsigned char * stream, unsigned size) + { + unsigned bytesTotal = 0; + unsigned bytes = 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, mCount)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, mLimit)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, mInterval)); + if (!bytes) return 0; + + bytesTotal += (bytes = soe::Read(stream+bytesTotal, size-bytesTotal, mIndex)); + if (!bytes) return 0; + + return bytesTotal; + } + + void Initialize(const RateLimits_t & rateLimits) + { + if (mIndex < rateLimits.size()) { + mLimit = rateLimits[mIndex].mLimit; + mInterval = rateLimits[mIndex].mInterval; + } else { + mLimit = 0; + mInterval = 0; + } + // reset count + mCount = 0; + } + + limit_t GetCount() const { return mCount; } + limit_t GetLimit() const { return mLimit; } + interval_t GetInterval() const { return mInterval; } + size_t GetIndex() const { return mIndex; } + + limit_t Consume(const RateLimits_t & rateLimits, limit_t count) + { + mCount += count; + + return (mLimit - mCount); + } + + bool Expire(const RateLimits_t & rateLimits) + { + bool remove = false; + + if (mCount > mLimit) { + // raise to next rate limit if count was exceeded + if (mIndex < rateLimits.size()) { + mIndex++; + } else { + // can't go up; make sure we go down if limit vector has shrunk + mIndex = rateLimits.size() - 1; + } + } else { + if (mIndex > 0) { + // drop to previous rate limit if count was not exceeded + mIndex--; + } else { + // remove counter if unused and on initial rate limit + if (mCount == 0) { + remove = true; + } + } + } + // reinitialize + Initialize(rateLimits); + + return remove; + } + + + protected: + limit_t mCount; + limit_t mLimit; + interval_t mInterval; + size_t mIndex; + private: + }; + typedef std::vector RateCounters_t; + + inline unsigned Read(const unsigned char * stream, unsigned size, RateCounter & data, unsigned maxLen, unsigned version = 0) + { + return data.Read(stream, size); + } + + inline unsigned Write(unsigned char * stream, unsigned size, const RateCounter & data, unsigned version = 0) + { + return data.Write(stream, size); + } + + inline int Print(char * stream, unsigned size, const RateCounter & data, unsigned maxDepth = INT_MAX) + { + return 0; + } + + template + class GenericRateLimitingMechanism + { + public: + typedef std::vector FixedKeyTypes_t; + typedef std::vector ArbitraryKeyTypes_t; + typedef std::vector LimitTypes_t; + typedef std::vector CounterTypes_t; + + GenericRateLimitingMechanism() { } + virtual ~GenericRateLimitingMechanism() { } + + void SetRateLimit(const FixedKeyType & fixedKey, const LimitType & rateLimit); + void UnsetRateLimit(const FixedKeyType & fixedKey); + const LimitType * GetRateLimit(const FixedKeyType & fixedKey) const; + const LimitType * GetNextRateLimit(FixedKeyType & fixedKey) const; + + const CounterType * GetRateCounter(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey) const; + unsigned GetRateCountersAndFixedKeys(const ArbitraryKeyType & arbitraryKey, CounterTypes_t & counters, FixedKeyTypes_t & fixedKeys) const; + void PutRateCounter(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey, const CounterType & counter); + void PutRateCountersAndFixedKeys(const ArbitraryKeyType & arbitraryKey, const CounterTypes_t & counters, const FixedKeyTypes_t & fixedKeys); + bool RemoveRateCounter(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey); + + unsigned RemoveArbitraryRateCounters(const FixedKeyType & fixedKey); + unsigned RemoveFixedRateCounters(const ArbitraryKeyType & arbitraryKey); + unsigned RemoveAllRateCounters(); + + limit_t ConsumeRateAllowance(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey, limit_t amount, TimeType when); + + void ProcessRateCounters(TimeType when); + + private: + class KeyPair + { + public: + KeyPair() + { } + + KeyPair(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey) + : mFixedKey(fixedKey) + , mArbitraryKey(arbitraryKey) + { } + + virtual ~KeyPair() + { } + + KEY_PAIR_COMP_OPER(<) + KEY_PAIR_COMP_OPER(<=) + KEY_PAIR_COMP_OPER(==) + KEY_PAIR_COMP_OPER(>=) + KEY_PAIR_COMP_OPER(>) + + FixedKeyType mFixedKey; + ArbitraryKeyType mArbitraryKey; + }; + class RateInfo + { + public: + RateInfo() + { } + + virtual ~RateInfo() + { } + + KeyPair mKeyPair; + CounterType mCounter; + }; + + typedef ref_ptr RateInfoRefPtr; + + // map types + typedef std::map RateLimitByFixedKeyMap_t; + typedef std::map RateCounterByKeyPairMap_t; + + // expiration queue type + typedef expiration_queue RateCounterExpirationQueue_t; + + // maps + RateLimitByFixedKeyMap_t mRateLimits; + RateCounterByKeyPairMap_t mRateCounters; + + // queue + RateCounterExpirationQueue_t mExpirationQueue; + }; + + template + void GenericRateLimitingMechanism::SetRateLimit(const FixedKeyType & fixedKey, const LimitType & rateLimit) + { + mRateLimits[fixedKey] = rateLimit; + } + + template + void GenericRateLimitingMechanism::UnsetRateLimit(const FixedKeyType & fixedKey) + { + mRateLimits.erase(fixedKey); + } + + template + const LimitType * GenericRateLimitingMechanism::GetRateLimit(const FixedKeyType & fixedKey) const + { + LimitType const * rateLimit = NULL; + typename RateLimitByFixedKeyMap_t::const_iterator limIter = mRateLimits.find(fixedKey); + + if (limIter != mRateLimits.end()) { + rateLimit = &(limIter->second); + } + + return rateLimit; + } + + template + const LimitType * GenericRateLimitingMechanism::GetNextRateLimit(FixedKeyType & fixedKey) const + { + LimitType const * rateLimit = NULL; + typename RateLimitByFixedKeyMap_t::const_iterator limIter = mRateLimits.upper_bound(fixedKey); + + if (limIter != mRateLimits.end()) { + fixedKey = limIter->first; + rateLimit = &(limIter->second); + } + + return rateLimit; + } + + template + const CounterType * GenericRateLimitingMechanism::GetRateCounter(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey) const + { + const CounterType * counter = NULL; + typename RateCounterByKeyPairMap_t::const_iterator counterIter = mRateCounters.find(KeyPair(fixedKey, arbitraryKey)); + if (counterIter != mRateCounters.end()) { + counter = &(counterIter->second->mCounter); + } + + return counter; + } + + template + unsigned GenericRateLimitingMechanism::GetRateCountersAndFixedKeys(const ArbitraryKeyType & arbitraryKey, CounterTypes_t & counters, FixedKeyTypes_t & fixedKeys) const + { + typename RateCounterByKeyPairMap_t::const_iterator counterIter; + typename RateLimitByFixedKeyMap_t::const_iterator limitIter; + KeyPair keyPair; + + keyPair.mArbitraryKey = arbitraryKey; + for (limitIter = mRateLimits.begin(); limitIter != mRateLimits.end(); limitIter++) + { + keyPair.mFixedKey = limitIter->first; + counterIter = mRateCounters.find(keyPair); + if (counterIter != mRateCounters.end()) { + fixedKeys.push_back(counterIter->first.mFixedKey); + counters.push_back(counterIter->second->mCounter); + } + } + + return counters.size(); + } + + template + void GenericRateLimitingMechanism::PutRateCounter(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey, const CounterType & counter) + { + KeyPair keyPair(fixedKey, arbitraryKey); + RateInfoRefPtr info = mRateCounters[keyPair]; + + info->mKeyPair = keyPair; + info->mCounter = counter; + } + + template + void GenericRateLimitingMechanism::PutRateCountersAndFixedKeys(const ArbitraryKeyType & arbitraryKey, const CounterTypes_t & counters, const FixedKeyTypes_t & fixedKeys) + { + unsigned count = counters.size() < fixedKeys.size() ? counters.size() : fixedKeys.size(); + KeyPair keyPair; + + keyPair.mArbitraryKey = arbitraryKey; + for (unsigned index =0; index < count; index++){ + keyPair.mFixedKey = fixedKeys[index]; + RateInfoRefPtr rateInfoRefPtr = mRateCounters[keyPair]; + + rateInfoRefPtr->mKeyPair = keyPair; + rateInfoRefPtr->mCounter = counters[index]; + } + } + + template + bool GenericRateLimitingMechanism::RemoveRateCounter(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey) + { + bool removed = false; + typename RateCounterByKeyPairMap_t::iterator counterIter = mRateCounters.find(KeyPair(fixedKey, arbitraryKey)); + if (counterIter != mRateCounters.end()) { + mExpirationQueue.erase(counterIter->first); + mRateCounters.erase(counterIter); + removed = true; + } + + return removed; + } + + template + unsigned GenericRateLimitingMechanism::RemoveArbitraryRateCounters(const FixedKeyType & fixedKey) + { + unsigned numRemoved = 0; + typename RateCounterByKeyPairMap_t::iterator counterIter = mRateCounters.begin(); + std::list keysToRemove; + for (; counterIter != mRateCounters.end(); counterIter++) + { + if (counterIter->first.mFixedKey == fixedKey) { + keysToRemove.push_back(counterIter->first); + } + } + for (typename std::list::iterator remIter = keysToRemove.begin(); remIter != keysToRemove.end(); remIter++) + { + mExpirationQueue.erase(*remIter); + mRateCounters.erase(*remIter); + numRemoved++; + } + + return numRemoved; + } + + template + unsigned GenericRateLimitingMechanism::RemoveFixedRateCounters(const ArbitraryKeyType & arbitraryKey) + { + unsigned numRemoved = 0; + typename RateCounterByKeyPairMap_t::iterator counterIter = mRateCounters.begin(); + std::list keysToRemove; + for (; counterIter != mRateCounters.end(); counterIter++) + { + if (counterIter->first.mArbitraryKey == arbitraryKey) { + keysToRemove.push_back(counterIter->first); + } + } + for (typename std::list::iterator remIter = keysToRemove.begin(); remIter != keysToRemove.end(); remIter++) + { + mExpirationQueue.erase(*remIter); + mRateCounters.erase(*remIter); + numRemoved++; + } + + return numRemoved; + } + + template + unsigned GenericRateLimitingMechanism::RemoveAllRateCounters() + { + unsigned numRemoved = mRateCounters.size(); + + mExpirationQueue.clear(); + mRateCounters.clear(); + + return numRemoved; + } + + template + limit_t GenericRateLimitingMechanism::ConsumeRateAllowance(const FixedKeyType & fixedKey, const ArbitraryKeyType & arbitraryKey, limit_t amount, TimeType when) + { + limit_t remainingAllowance = INT_MAX; + const LimitType * rateLimit = GetRateLimit(fixedKey); + + if (rateLimit) { + typename RateCounterByKeyPairMap_t::iterator counterIter = mRateCounters.find(KeyPair(fixedKey, arbitraryKey)); + RateInfoRefPtr info; + KeyPair keyPair(fixedKey, arbitraryKey); + + if (counterIter == mRateCounters.end()) { + info = mRateCounters[keyPair]; + info->mKeyPair = keyPair; + info->mCounter.Initialize(*rateLimit); + mExpirationQueue.insert(keyPair, when + info->mCounter.GetInterval()); + } else { + info = counterIter->second; + } + remainingAllowance = info->mCounter.Consume(*rateLimit, amount); + } + + return remainingAllowance; + } + + template + void GenericRateLimitingMechanism::ProcessRateCounters(TimeType when) + { + while (mExpirationQueue.contains_expired_items(when)) + { + KeyPair top = mExpirationQueue.top(); + typename RateLimitByFixedKeyMap_t::const_iterator limIter = mRateLimits.find(top.mFixedKey); + RateInfoRefPtr & info = mRateCounters[top]; + + if ((limIter == mRateLimits.end()) || (info->mCounter.Expire(limIter->second))) { + // clear cache of unused counters + mRateCounters.erase(top); + mExpirationQueue.erase(top); + } else { + // reset expiration time + mExpirationQueue.insert(top, when + info->mCounter.GetInterval()); + } + } + } + + enum ESingleMemberClass + { + eSingleClassMember = 0 + }; + + template + class GenericRateLimitingMechanismFixedOnly : public GenericRateLimitingMechanism + { + public: + GenericRateLimitingMechanismFixedOnly() { } + virtual ~GenericRateLimitingMechanismFixedOnly() { } + + const CounterType * GetRateCounter(const FixedKeyType & fixedKey) const + { + return GenericRateLimitingMechanism::GetRateCounter(fixedKey, eSingleClassMember); + } + + unsigned GetRateCountersAndFixedKeys(typename GenericRateLimitingMechanism::CounterTypes_t & counters, typename GenericRateLimitingMechanism::FixedKeyTypes_t & fixedKeys) const + { + return GenericRateLimitingMechanism::GetRateCountersAndFixedKeys(eSingleClassMember, counters, fixedKeys); + } + + void PutRateCounter(const FixedKeyType & fixedKey, const CounterType & counter) + { + GenericRateLimitingMechanism::PutRateCounter(fixedKey, eSingleClassMember, counter); + } + + void PutRateCountersAndFixedKeys(const typename GenericRateLimitingMechanism::CounterTypes_t & counters, const typename GenericRateLimitingMechanism::FixedKeyTypes_t & fixedKeys) + { + return GenericRateLimitingMechanism::PutRateCountersAndFixedKeys(eSingleClassMember, counters, fixedKeys); + } + + bool RemoveRateCounter(const FixedKeyType & fixedKey) + { + return GenericRateLimitingMechanism::RemoveRateCounter(fixedKey, eSingleClassMember); + } + + limit_t ConsumeRateAllowance(const FixedKeyType & fixedKey, limit_t amount, TimeType when) + { + return GenericRateLimitingMechanism::ConsumeRateAllowance(fixedKey, eSingleClassMember, amount, when); + } + }; + + template + class GenericRateLimitingMechanismArbitraryOnly : public GenericRateLimitingMechanism + { + public: + GenericRateLimitingMechanismArbitraryOnly() { } + virtual ~GenericRateLimitingMechanismArbitraryOnly() { } + + void SetRateLimit(const LimitType & rateLimit) + { + GenericRateLimitingMechanism::SetRateLimit(eSingleClassMember, rateLimit); + } + + void UnsetRateLimit() + { + GenericRateLimitingMechanism::UnsetRateLimit(eSingleClassMember); + } + + const LimitType * GetRateLimit() const + { + return GenericRateLimitingMechanism::GetRateLimit(eSingleClassMember); + } + + const CounterType * GetRateCounter(const ArbitraryKeyType & arbitraryKey) const + { + return GenericRateLimitingMechanism::GetRateCounter(eSingleClassMember, arbitraryKey); + } + + void PutRateCounter(const ArbitraryKeyType & arbitraryKey, const CounterType & counter) + { + GenericRateLimitingMechanism::PutRateCounter(eSingleClassMember, arbitraryKey, counter); + } + + bool RemoveRateCounter(const ArbitraryKeyType & arbitraryKey) + { + return GenericRateLimitingMechanism::RemoveRateCounter(eSingleClassMember, arbitraryKey); + } + + limit_t ConsumeRateAllowance(const ArbitraryKeyType & arbitaryKey, limit_t amount, TimeType when) + { + return GenericRateLimitingMechanism::ConsumeRateAllowance(eSingleClassMember, arbitaryKey, amount, when); + } + }; +}; + +#endif // __GENERIC_RATE_LIMITING_MECHANISM_H__ + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/hash.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/hash.h new file mode 100644 index 00000000..dcb77b8f --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/hash.h @@ -0,0 +1,239 @@ +#ifndef API__HASH_H +#define API__HASH_H + +#include +#include +#include +#include +#include +#include +#include "types.h" +#include "Base/stringutils.h" +#include + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + + #define MAX_HASH_VALUE 0xfe + #define BROADCAST_HASH_VALUE 0xff + + inline unsigned char HashAccountId(unsigned accountId) { return (accountId & MAX_HASH_VALUE); } + + inline unsigned char HashUserName(const char * name) + { + unsigned value = 0; + while (*name) + { + value += tolower(*name); + name++; + } + return value % MAX_HASH_VALUE; + } + + inline unsigned char HashRandom() { return (rand() % MAX_HASH_VALUE); } + +const soe::uint64 nMaxCharBits = 0x3f; +const unsigned char nMaxBits = 64; +const unsigned char nEncodeCharBits = 6; +const unsigned char nMaxHashChar = 2; +const unsigned char nMaxRandomChar = 3; +const unsigned nEncodeChars = nMaxBits/nEncodeCharBits; +const unsigned nMaxEncodeChars = nEncodeChars + nMaxRandomChar+ nMaxHashChar + 1; + +static const char base64[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.?"; + +unsigned CalcMaxNumforBits(unsigned nBits); +char DecodeChar(char c); +//int HexStringToInt(const char *value); +int HexStringToInt(const std::string &value); + +#ifdef _DEBUG +inline std::string printBase64() +{ + std::string tmp; + for (unsigned i = 0; i < nMaxBits; i++) + { + char buf[16]; + sprintf(buf, "%02d %c=%d, %d\n", i, base64[i], base64[i], DecodeChar(base64[i])); + tmp += buf; + } + return tmp; +} +#endif + +///////////////////////////////////////////////////////////////////////////// +// Session ID is a 16 byte string constructed with account ID, random number, +// and hash value. Here is the detail: +// Account ID = 11 bytes (base64 encoded) +// Random Number = 3 bytes (base64 encoded) +// Hash Value = 2 bytes (Hex encoded) +// + +inline std::string EncodeSessionID(soe::uint64 accountID, unsigned char hash) +{ + char encodeBuf[64]; + memset(encodeBuf,0,64); + + for (unsigned i = 0; i < nEncodeChars; i++) + { + soe::uint64 shift = nMaxBits-(nEncodeCharBits*(i+1)); + soe::uint64 tmp = accountID >> shift; + unsigned char ch = (unsigned char)(tmp & nMaxCharBits); + encodeBuf[i] = base64[ch]; + } + + unsigned char remainingBits = nMaxBits % nEncodeCharBits; + if (remainingBits != 0) + { + unsigned char nMaxRemainingBits = CalcMaxNumforBits(remainingBits); + unsigned char tmp = (unsigned char)(accountID & nMaxRemainingBits); + encodeBuf[nEncodeChars] = base64[tmp]; + } + + char randomNum[4]; + randomNum[0] = base64[rand()%nMaxBits]; + randomNum[1] = base64[rand()%nMaxBits]; + randomNum[2] = base64[rand()%nMaxBits]; + randomNum[3] = 0; + + // add hash + char hashBuf[8]; + sprintf(hashBuf, "%02x", hash); + std::string tmp = std::string(encodeBuf) + std::string(randomNum) + std::string(hashBuf); + + return tmp; +} + + +inline bool DecodeSessionID(const std::string & sessionID, soe::uint64 & accountID, unsigned char & hash) +{ + if (sessionID.empty() || sessionID.size() > nMaxEncodeChars) + return false; + + accountID = 0; + std::string sDecode = sessionID.substr(0, sessionID.size()-nMaxHashChar-nMaxRandomChar); + std::string sHash = sessionID.substr(sessionID.size()-nMaxHashChar, nMaxHashChar); + + hash = (unsigned char)HexStringToInt(sHash); + + for (unsigned i = 0; i < nEncodeChars; i++) + { + char c = DecodeChar(sDecode[i]); + if (c == -1) + { + // invalid character + return false; + } + + soe::uint64 decodeNum = (soe::uint64)c; + if (i == 0) + { + accountID = decodeNum; + } + else + { + accountID = (accountID << nEncodeCharBits) | decodeNum; + } + } + + unsigned char remainingBits = nMaxBits % nEncodeCharBits; + if (remainingBits != 0) + { + soe::uint64 decodeNum = (soe::uint64)DecodeChar(sDecode[sDecode.size()-1]); + accountID = (accountID << remainingBits) | decodeNum; + } + + return true; +} + +inline unsigned CalcMaxNumforBits(unsigned nBits) +{ + unsigned maxNum = 0; + for (unsigned i = 0; i < nBits; i++) + { + maxNum |= 1 << i; // pow(2,i); + } + return maxNum; +} + +inline char DecodeChar(char c) +{ + char num = -1; + if (c >= 48 && c <= 57) + { + num = c - 48; + } + else if (c >= 97 && c <= 122) + { + num = c - 87; + } + else if (c >= 65 && c <= 90) + { + num = c - 29; + } + else if (c == 46) + { + num = 62; + } + else if (c == 63) + { + num = 63; + } + + return num; +} + +inline int HexStringToInt(const std::string &value) +{ + struct CHexMap + { + char chr; + int value; + }; + const int HexMapL = 16; + CHexMap HexMap[HexMapL] = + { + {'0', 0}, {'1', 1}, + {'2', 2}, {'3', 3}, + {'4', 4}, {'5', 5}, + {'6', 6}, {'7', 7}, + {'8', 8}, {'9', 9}, + {'A', 10}, {'B', 11}, + {'C', 12}, {'D', 13}, + {'E', 14}, {'F', 15} + }; + + //char *mstr = strupr(strdup(value)); + std::string upperstr = soe::touppercase(value); + const char *s = upperstr.c_str(); + int result = 0; + if (*s == '0' && *(s + 1) == 'X') s += 2; + bool firsttime = true; + while (*s != '\0') + { + bool found = false; + for (int i = 0; i < HexMapL; i++) + { + if (*s == HexMap[i].chr) + { + if (!firsttime) result <<= 4; + result |= HexMap[i].value; + found = true; + break; + } + } + if (!found) break; + s++; + firsttime = false; + } + //free(mstr); + return result; +} + +#ifdef EXTERNAL_DISTRO +}; +#endif + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/hashtable.hpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/hashtable.hpp new file mode 100644 index 00000000..0256f297 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/hashtable.hpp @@ -0,0 +1,623 @@ +#ifndef HASHTABLE_HPP +#define HASHTABLE_HPP + + +#include +#include + +//////////////////////////////////////////////////////// +// helper functions for the above 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); +} + +// 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 +struct HashTableStatistics +{ + int tableSize; + int usedSlots; + int totalEntries; +}; + +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); +} + +////////////////////////////////////////////////////////////////////////// +// 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/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/log.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/log.cpp new file mode 100644 index 00000000..aa213b1b --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/log.cpp @@ -0,0 +1,790 @@ + +#include "log.h" +#if defined (_MT) +#include "thread.h" +#endif +#include +#include "types.h" + +#ifdef WIN32 +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + + +#ifdef WIN32 +#define snprintf _snprintf +#endif + + +using namespace std; + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ + +#endif +namespace soe +{ + +const long kDateLenAllowance = 256; + +#ifdef WIN32 +const char file_sep = '\\'; + +#define LOGGER_GET_CURR_TIME(__tm_struct__, __time_t_var__) \ + memcpy(&__tm_struct__, localtime(&__time_t_var__), sizeof(__tm_struct__)) + +#define LOGGER_MAKE_DIR(__dir_name__, __mode__) mkdir(__dir_name__) + +#define vsnprintf _vsnprintf + +#else // ifndef WIN32 +const char file_sep = '/'; + +const char *syslogLevels[] = { + "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG" }; + +const int numSysLogLevels = sizeof(syslogLevels) / sizeof(char *); + +#define LOGGER_GET_CURR_TIME(__tm_struct__, __time_t_var__) \ + localtime_r(&__time_t_var__, &__tm_struct__) + +#define LOGGER_MAKE_DIR(__dir_name__, __mode__) mkdir(__dir_name__, __mode__) + +#endif // WIN32 + +#ifdef _MT +#define LOGGER_LOCK ScopeLock logLock(rLock); +#else // ifndef _MT +#define LOGGER_LOCK +#endif // _MT + +Logger::Logger(const char *prefix, + int level, + unsigned size, + bool rollDate) +: m_defaultLevel(level), + m_defaultSize(size), + m_dirPrefix(prefix), + m_rollDate(rollDate), + m_unlimitedLineSize(false) +{ + m_logType = eUseLocalFile; + LoggerInit("UnNamedServer"); +} + +Logger::Logger(const char *programName, + const ELogType logType, + const char *prefix, + int level, + unsigned size, + bool rollDate) +: m_defaultLevel(level), + m_defaultSize(size), + m_dirPrefix(prefix), + m_rollDate(rollDate), + m_logType(logType), + m_unlimitedLineSize(false) +{ + LoggerInit(programName); +} + + +void Logger::LoggerInit(const char *programName) +{ + char buf[1024]; + FILE *logDir = NULL; + + if (0 != (m_logType & eUseLocalFile)) + { + logDir = fopen(m_dirPrefix.c_str(), "r+"); + if(errno == ENOENT) + { + cmkdir(m_dirPrefix.c_str(), 0755); + } + else if(logDir != NULL) + { + fclose(logDir); + } + + tm now; + time_t t = time(NULL); + + LOGGER_GET_CURR_TIME(now, t); + + memcpy(&m_lastDateTime, &now, sizeof(tm)); + if(m_rollDate) + { + snprintf(buf, sizeof(buf), "%s%c%2.2d-%2.2d-%2.2d", m_dirPrefix.c_str(), file_sep, (now.tm_mon + 1), now.tm_mday, (now.tm_year % 100)); + } + else + { + snprintf(buf, sizeof(buf), "%s", m_dirPrefix.c_str()); + } + logDir = fopen(buf, "r+"); + if(errno == ENOENT) + { + cmkdir(buf, 0755); + } + else if(logDir != NULL) + { + fclose(logDir); + } + m_logPrefix = buf; + m_logRolloverSize = 0x80000000; // Default: 2 GB upper limit on file size + } + + // need to do this because syslog does not copy the buffer; it copies the *pointer*. + setProgramName(programName); + +#ifndef WIN32 + // Open connection to syslog + if (0 != (m_logType & eUseSysLog)) { + openlog(m_programName, LOG_NDELAY | LOG_PID, LOG_LOCAL5); + } +#endif // !WIN32 + +} + +Logger::~Logger() +{ + map::iterator iter; + for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++) + { + logWithSys((*iter).first, LOG_INFO, LOG_FILEONLY, "---=== Log Stopped ===---"); + fflush((*iter).second->file); + fclose((*iter).second->file); + delete((*iter).second); + + } + m_logTable.clear(); + +#ifndef WIN32 + // close connection to syslog + if (0 != (m_logType & eUseSysLog)) + closelog(); +#endif // !WIN32 +} + +void Logger::setProgramName(const char *programName) +{ + if (programName) + { + strncpy(m_programName, programName, sizeof(m_programName)); + m_programName[sizeof(m_programName) - 1] = '\0'; + } +} + +void Logger::flush(unsigned logenum) +{ + map::iterator iter = m_logTable.find(logenum); + + if(iter != m_logTable.end()) + { + LogInfo *info = (*iter).second; + fflush(info->file); + info->used = 0; + } +} + +void Logger::flushAll() +{ + if (0 != (m_logType & eUseLocalFile)) + { + map::iterator iter; + + for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++) + { + LogInfo *info = (*iter).second; + fflush(info->file); + info->used = 0; + } + } +} + +void Logger::addLog(const char *id, unsigned logenum, int level, unsigned size) +{ + if (0 != (m_logType & eUseLocalFile)) + { + LogInfo *newLog = new LogInfo; + newLog->filename = m_logPrefix + file_sep + id + ".log"; + newLog->name = id; + newLog->used = 0; + newLog->max = size; + newLog->last = 0; + newLog->level = level; + + m_logTable.insert(pair(logenum, newLog)); + + if(strcmp("stdout", id) == 0) + { + newLog->file = stdout; + } + else if(strcmp("stderr", id) == 0) + { + newLog->file = stderr; + } + else + { + newLog->file = fopen(newLog->filename.c_str(), "a+"); + } + logWithSys(logenum, LOG_INFO, LOG_FILEONLY, "---=== Log Started ===---"); + } +} + +void Logger::addLog(const char *id, unsigned logenum) +{ + if (0 != (m_logType & eUseLocalFile)) + { + LogInfo *newLog = new LogInfo; + newLog->filename = m_logPrefix + file_sep + id + ".log"; + newLog->name = id; + newLog->used = 0; + newLog->max = m_defaultSize; + newLog->last = 0; + newLog->level = m_defaultLevel; + + m_logTable.insert(pair(logenum, newLog)); + + if(strcmp("stdout", id) == 0) + { + newLog->file = stdout; + } + else if(strcmp("stderr", id) == 0) + { + newLog->file = stderr; + } + else + { + newLog->file = fopen(newLog->filename.c_str(), "a+"); + } + logWithSys(logenum, LOG_INFO, LOG_FILEONLY, "---===Log Started ===---"); + } +} + +void Logger::updateLog(unsigned logenum, int level, unsigned size) +{ + if (0 != (m_logType & eUseLocalFile)) + { + map::iterator iter = m_logTable.find(logenum); + + if(iter != m_logTable.end()) + { + (*iter).second->level = level; + (*iter).second->max = size; + } + } +} + +void Logger::removeLog(unsigned logenum) +{ + if (0 != (m_logType & eUseLocalFile)) + { + map::iterator iter = m_logTable.find(logenum); + + if(iter != m_logTable.end()) + { +#ifdef WIN32 + logWithSys((*iter).first, LOG_INFO, LOG_FILEONLY, "---=== Log Stopped ===---"); +#else // ifndef WIN32 + logWithSys((*iter).first, LOG_INFO, LOG_ALWAYS, "---=== Log Stopped ===---"); +#endif // WIN32 + fflush((*iter).second->file); + fclose((*iter).second->file); + delete((*iter).second); + } + } +} + + + + + +void Logger::logSimple(unsigned logenum, int level, const char *message) +{ + map::iterator iter = m_logTable.find(logenum); + + if(iter == m_logTable.end()) + { + return; + } + time_t t = time(NULL); + LogInfo *info = (*iter).second; + if(level >= info->level) + { + return; + } +#ifndef WIN32 + if(level == LOG_FILEONLY && ((info->file == stderr) || (info->file == stdout))) + { + return; + } +#endif // !WIN32 + if(info->last != t) + { + memcpy(&info->ts, localtime(&t), sizeof(tm)); + info->last = t; + } + + if(m_rollDate && info->ts.tm_mday != m_lastDateTime.tm_mday) + { + if(info->ts.tm_mday != m_lastDateTime.tm_mday) + { + memcpy(&m_lastDateTime, &info->ts, sizeof(tm)); + rollDate(t); + } + } + if(iter != m_logTable.end()) + { + if(info->max > 0) + { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message); + info->used += fputc('\n', info->file); + if(info->used > info->max) + { + fflush(info->file); + info->used = 0; + } + } + else + { + fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message); + fputc('\n', info->file); + fflush(info->file); + } + } +} + + + + + + + + + + + +void Logger::logSimpleWithSys(unsigned logenum, unsigned priority, int level, const char *message) +{ + +#ifndef WIN32 + if (0 != (m_logType & eUseSysLog)) + syslog(priority, "%s: %s", syslogLevels[priority % numSysLogLevels], message); +#endif // !WIN32 + + if (0 != (m_logType & eUseLocalFile)) + { + map::iterator iter = m_logTable.find(logenum); + + if(iter == m_logTable.end()) + { + return; + } + time_t t = time(NULL); + LogInfo *info = (*iter).second; + + if(level >= info->level) + { + return; + } + +#ifndef WIN32 + if(level == LOG_FILEONLY && ((info->file == stderr) || (info->file == stdout))) + { + return; + } +#endif // !WIN32 + + if(info->last != t) + { + memcpy(&info->ts, localtime(&t), sizeof(tm)); + info->last = t; + } + + if(m_rollDate && info->ts.tm_mday != m_lastDateTime.tm_mday) + { + if(info->ts.tm_mday != m_lastDateTime.tm_mday) + { + memcpy(&m_lastDateTime, &info->ts, sizeof(tm)); + rollDate(t); + } + } + if(iter != m_logTable.end()) + { + if (m_logRolloverSize> 0) + checkLogRoll(info, (unsigned long)::strlen(message) + kDateLenAllowance); + + if(info->max > 0) + { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message); + info->used += fputc('\n', info->file); + if(info->used > info->max) + { + fflush(info->file); + info->used = 0; + } + } + else + { + fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message); + fputc('\n', info->file); + fflush(info->file); + } + } + } +} + +void Logger::log(unsigned logenum, int level, const char *message, ...) +{ + va_list varg; + + va_start(varg, message); + vlogWithSys(logenum, LOG_NOTICE, level, message, varg); + va_end(varg); +} + + +void Logger::logWithSys(unsigned logenum, unsigned priority, int level, const char *message, ...) +{ + char buf[2048]; + va_list varg; + va_start(varg, message); + vsnprintf(buf, 2047, message, varg); + va_end(varg); + buf[2047] = 0; + +#ifndef WIN32 + if (0 != (m_logType & eUseSysLog)) + syslog(priority, "%s: %s", syslogLevels[priority % numSysLogLevels], buf); +#endif // !WIN32 + + if (0 != (m_logType & eUseLocalFile)) + { + map::iterator iter = m_logTable.find(logenum); + if(iter == m_logTable.end()) + { + return; + } + time_t t = time(NULL); + LogInfo *info = (*iter).second; + + if(level >= info->level) + { + return; + } + if(level == LOG_FILEONLY && ((info->file == stderr) || (info->file == stdout))) + { + return; + } + if(info->last != t) + { + LOGGER_GET_CURR_TIME(info->ts, t); + + info->last = t; + } + if(m_rollDate && info->ts.tm_mday != m_lastDateTime.tm_mday) + { + if(info->ts.tm_mday != m_lastDateTime.tm_mday) + { + memcpy(&m_lastDateTime, &info->ts, sizeof(tm)); + rollDate(t); + } + } + + if(iter != m_logTable.end()) + { + if (m_logRolloverSize> 0) + checkLogRoll(info, (unsigned long)::strlen(buf) + kDateLenAllowance); + + if(info->max > 0) + { + if (m_unlimitedLineSize) { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] ", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec); + va_start(varg, message); + info->used += vfprintf(info->file, message, varg); + va_end(varg); + } else { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, buf); + } + info->used += fputc('\n', info->file); + if(info->used > info->max) + { + fflush(info->file); + info->used = 0; + } + } + else + { + if (m_unlimitedLineSize) { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] ", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec); + va_start(varg, message); + info->used += vfprintf(info->file, message, varg); + va_end(varg); + } else { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, buf); + } + fputc('\n', info->file); + fflush(info->file); + } + } + } +} + +void Logger::vlogWithSys(unsigned logenum, unsigned priority, int level, const char *message, va_list argptr) +{ + char buf[2048]; + vsnprintf(buf, 2047, message, argptr); + buf[2047] = 0; + +#ifndef WIN32 + if (0 != (m_logType & eUseSysLog)) + syslog(priority, "%s: %s", syslogLevels[priority % numSysLogLevels], buf); +#endif // !WIN32 + + if (0 != (m_logType & eUseLocalFile)) + { + map::iterator iter = m_logTable.find(logenum); + if(iter == m_logTable.end()) + { + return; + } + time_t t = time(NULL); + LogInfo *info = (*iter).second; + + if(level >= info->level) + { + return; + } + if(level == LOG_FILEONLY && ((info->file == stderr) || (info->file == stdout))) + { + return; + } + if(info->last != t) + { + LOGGER_GET_CURR_TIME(info->ts, t); + + info->last = t; + } + if(m_rollDate && info->ts.tm_mday != m_lastDateTime.tm_mday) + { + if(info->ts.tm_mday != m_lastDateTime.tm_mday) + { + memcpy(&m_lastDateTime, &info->ts, sizeof(tm)); + rollDate(t); + } + } + + if(iter != m_logTable.end()) + { + if (m_logRolloverSize> 0) + checkLogRoll(info, (unsigned long)::strlen(buf) + kDateLenAllowance); + + if(info->max > 0) + { + if (m_unlimitedLineSize) { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] ", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec); + info->used += vfprintf(info->file, message, argptr); + } else { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, buf); + } + info->used += fputc('\n', info->file); + if(info->used > info->max) + { + fflush(info->file); + info->used = 0; + } + } + else + { + if (m_unlimitedLineSize) { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] ", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec); + info->used += vfprintf(info->file, message, argptr); + } else { + info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, buf); + } + fputc('\n', info->file); + fflush(info->file); + } + } + } +} + +void Logger::vlog(unsigned logenum, int level, const char *message, va_list argptr) +{ + vlogWithSys(logenum, LOG_NOTICE, level, message, argptr); +} + +// void Logger::vlogWithSys(unsigned logenum, unsigned priority, int level, const char *message, va_list argptr) + +void Logger::rollDate(time_t t) +{ + char buf[80]; + FILE *logDir = NULL; + + logDir = fopen(m_dirPrefix.c_str(), "r+"); + if(errno == ENOENT) + { + cmkdir(m_dirPrefix.c_str(), 0755); + } + else if(logDir != NULL) + { + fclose(logDir); + } + + tm now; + + LOGGER_GET_CURR_TIME(now, t); + + snprintf(buf, sizeof(buf), "%s%c%2.2d-%2.2d-%2.2d", m_dirPrefix.c_str(), file_sep, (now.tm_mon + 1), now.tm_mday, (now.tm_year % 100)); + logDir = fopen(buf, "r+"); + + if(errno == ENOENT) + { + cmkdir(buf, 0755); + } + else if(logDir != NULL) + { + fclose(logDir); + } + m_logPrefix = buf; + + map::iterator iter; + for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++) + { + (*iter).second->filename = m_logPrefix + file_sep + (*iter).second->name.c_str() + ".log"; + fflush((*iter).second->file); + fclose((*iter).second->file); + (*iter).second->file = fopen((*iter).second->filename.c_str(), "a+"); + if ((*iter).second->file == 0) + abort(); + memcpy(&((*iter).second->ts), &now, sizeof(tm)); + } +} + +// mkdir function that creates intermediate directories +void Logger::cmkdir(const char *dir, int mode) +{ + char dirbuf[128]; + strncpy(dirbuf, dir, 127); + dirbuf[127] = 0; + char *j = dirbuf, *i = dirbuf; + int handle; + + while(*i) + { + if(*i == file_sep) + { + (*i) = 0; +#ifdef WIN32 + handle = open(j, O_EXCL); + + if((handle > 0) || (errno != EISDIR && errno != ENOENT && errno != EACCES)) + { + perror("Logger::cmkdir():"); + abort(); + } +#else // ifndef WIN32 +// handle = open(j, O_EXCL); // Ben's original code +// if((handle > 0) || (errno != EISDIR && errno != ENOENT)) +// { +// perror("Logger::cmkdir():"); +// abort(); +// } + +// This doesnt work under Linux, it returns a valid handle +// Instead: see if file exists. If it doesnt, create directory ok +// If it exists, do stat to see if it is a dir. +// If it is a dir, then ok, create the directory. +// If it is a file, error +// ging 9-16-2002 + + handle = open(j, O_RDONLY); + if (handle > 0) + { + struct stat stat_buffer; + int ret = fstat(handle,&stat_buffer); + if ((ret == -1) || ((stat_buffer.st_mode | S_IFDIR) == 0)) + { + perror("Logger::cmkdir():"); + abort(); + } + } +#endif // WIN32 + LOGGER_MAKE_DIR(j, mode); + close(handle); + (*i) = file_sep; + } + else if(*(i + 1) == 0) + { + LOGGER_MAKE_DIR(j, mode); + } + i++; + } +} + +void Logger::rollLog(LogInfo *logInfo) +{ + std::string newLogName; + char timeStampBuffer[256]; + time_t t = time(NULL); + tm now; + int r; + int nTries = 10; + + fflush(logInfo->file); + fclose(logInfo->file); + + do + { + LOGGER_GET_CURR_TIME(now, t); + snprintf(timeStampBuffer, sizeof(timeStampBuffer), "-%4.4d-%2.2d-%2.2d-%2.2d-%2.2d-%2.2d.log", + (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec); + newLogName = logInfo->filename.substr(0, logInfo->filename.length() - 4) + timeStampBuffer; + r = rename(logInfo->filename.c_str(), newLogName.c_str()); + nTries--; + t++; + } + while ((r != 0) && (nTries > 0)); + + logInfo->file = fopen(logInfo->filename.c_str(), "a+"); + memcpy(&(logInfo->ts), &now, sizeof(tm)); +} + +void Logger::checkLogRoll(LogInfo *logInfo, unsigned long lenToAdd) +{ + unsigned long logSize = ftell(logInfo->file); + + if ((logSize + lenToAdd) > m_logRolloverSize) + { + rollLog(logInfo); + } +} + +}; +#ifdef EXTERNAL_DISTRO +}; +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/log.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/log.h new file mode 100644 index 00000000..a4b06815 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/log.h @@ -0,0 +1,149 @@ +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +// make the compiler shut up +#pragma warning (disable : 4786) + +#include +#include +#include +#if defined (_MT) || defined(_REENTRANT) +# include "thread.h" +#endif +#ifndef WIN32 +#include +#endif + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ + +#endif +namespace soe +{ + +#define LOG_ALWAYS -1 +#define LOG_FILEONLY -2 + +struct LogInfo +{ + FILE *file; + unsigned used; + tm ts; + time_t last; + unsigned max; + int level; + std::string filename; + std::string name; +}; + +/* Use these flags defined in syslog.h for the priority level + LOG_EMERG, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG +*/ + +//----------------------------------------------- +// Windows doesn't have syslog (?) so we define the +// syslog warning levels here to satisfy the compiler +// but then ignore them in log() and logSimple() +//----------------------------------------------- + +#ifdef WIN32 +enum _WIN32_LOG_LEVELS_ +{ + LOG_EMERG = 0, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG +}; +#endif + +enum ELogType +{ + eUseNone = 0, + eUseLocalFile = 1, + eUseSysLog = 2, + eUseBoth = 3 +}; + +#define MAX_LOG_BUFFER 10240 +class Logger +{ +public: + Logger(const char *programName, + const ELogType logType = eUseLocalFile, + const char *prefix = "logs", + int level = 10, + unsigned size = 0, + bool rollDate = true); + + Logger(const char *prefix, + int level, + unsigned size, + bool rollDate = true); // Backwards compatibility + void LoggerInit(const char *programName); + + ~Logger(); + void addLog(const char *id, unsigned logenum, int level, unsigned size); + void addLog(const char *id, unsigned logenum); + void removeLog(unsigned logenum); + void updateLog(unsigned logenum, int level, unsigned size); + void logSimple(unsigned logenum, int level, const char *message); + void logSimpleWithSys(unsigned logenum, unsigned priority, int level, const char *message); + void logWithSys(unsigned logenum, unsigned priority, int level, const char *message, ...); + void log(unsigned logenum, int level, const char *message, ...); + void vlogWithSys(unsigned logenum, unsigned priority, int level, const char *message, va_list argptr); + void vlog(unsigned logenum, int level, const char *message, va_list argptr); + + void flushAll(); + void flush(unsigned logenum); + unsigned long getLogRolloverSize() { return m_logRolloverSize; } + void setLogRolloverSize(unsigned long maxLogSize) { m_logRolloverSize = maxLogSize; } + void setUnlimitedLineSize(bool isUnlimited) { m_unlimitedLineSize = isUnlimited; } + void setProgramName(const char *programName); + +#if defined (_MT) || defined(_REENTRANT) + Mutex & getMutex() { return rLock; } +#endif + +private: + void rollDate(time_t now); + void cmkdir(const char *dir, int mode); + void rollLog(LogInfo *logInfo); + void checkLogRoll(LogInfo *logInfo, unsigned long lenToAdd); + + unsigned m_defaultLevel; + unsigned m_defaultSize; + std::string m_lastDateText; + tm m_lastDateTime; + std::map m_logTable; + std::string m_logPrefix; + std::string m_dirPrefix; +#if defined (_MT) || defined(_REENTRANT) + Mutex rLock; +#endif + bool m_rollDate; + ELogType m_logType; + unsigned long m_logRolloverSize; + bool m_unlimitedLineSize; + char m_programName[2048]; +}; + +extern const char file_sep; + +}; +#ifdef EXTERNAL_DISTRO +}; +#endif + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorAPI.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorAPI.cpp new file mode 100644 index 00000000..49d0c3dc --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorAPI.cpp @@ -0,0 +1,608 @@ +#include +#include +#include +#include +#include "monitorAPI.h" + +//********************************************************************************************** +//********************************************************************************************** +//********************************** DO NOT EDIT THIS FILE *********************************** +//********************************************************************************************** +//********************************************************************************************** +void set_bit( unsigned char p[], int bit){ p[bit >> 3] |= 1 << (bit & 0x7); } +void unset_bit(unsigned char p[], int bit){ p[bit >> 3] &= ~(1 << (bit & 0x7)); } +int get_bit( unsigned char p[], int bit){ return (p[bit >> 3] >> (bit & 0x7)) & 1; } + +//////////////////////////////////////// +// Player implementation +//////////////////////////////////////// +MonitorObject::MonitorObject(UdpConnection *con,CMonitorData *_gamedata, char *passwd, char **addressList,bool _bprint) +{ + mbprint = _bprint; + mMonitorData = _gamedata; + mbAuthenticated = false; + mPasswd = passwd; + mAddressList = addressList; + mHierarchySent = false; + mSequence = 1; + mlastUpdateTime = 0 ; + mConnection = con; + mConnection->AddRef(); + mConnection->SetHandler(this); + + // Flag all the descriptions + mMark = new unsigned char [ (mMonitorData->DataMax()/8)+2]; + memset(mMark,0,(mMonitorData->DataMax()/8)+2); + char hold[256]; + if( mbprint ) + { + fprintf(stderr,"MONITOR API CONNECTED: %s:%d\n", + mConnection->GetDestinationIp().GetAddress(hold), + mConnection->GetDestinationPort()); + } +} + +MonitorObject::~MonitorObject() +{ + if( mMark ) + delete [] mMark; + + if( mConnection ) + { + mConnection->SetHandler(NULL); + mConnection->Disconnect(); + mConnection->Release(); + } +} + +void MonitorObject::OnTerminated( UdpConnection * /* con */) +{ +char hold[214]; + + if( mConnection ) + { + if( mbprint ) + { + mConnection->GetDestinationIp().GetAddress(hold); + if( mbprint ) + { + fprintf(stderr,"MONITOR API TERMINATE %s:%d %s - %s\n", + hold, + mConnection->GetDestinationPort(), + UdpConnection::DisconnectReasonText( mConnection->GetDisconnectReason()), + UdpConnection::DisconnectReasonText( mConnection->GetOtherSideDisconnectReason())); + } + } + mConnection->SetHandler(NULL); + mConnection->Disconnect(); + mConnection->Release(); + mConnection = NULL; + } +} + + +void MonitorObject::OnRoutePacket(UdpConnection * con, const uchar *data, int dataLen) +{ + + if( dataLen < 6 ) + { + if( mbprint ) + fprintf(stderr,"MONITOR API: This should not happen, line %d\n",__LINE__); + } + + simpleMessage msg(data); + + if( con == NULL ) + { + if( mbprint) + fprintf(stderr,"MONITOR API: MonitorObject.OnRoutePacket, recived a NULL connection.?\n"); + return; + } + + //************************* Login Process *************************** + if ( mbAuthenticated == false ) + { + if ( msg.getCommand() != MON_MSG_AUTH ) + return; + + if( processAuthRequest(data, dataLen) == false ) + return; + + mbAuthenticated = true; + return; + } + + //************************* Process Commands *********************** + switch(msg.getCommand()) + { + + // *********************** Game Server Processing ****************** + case MON_MSG_QUERY_ELEMENTS: + if( mHierarchySent == true ) + { + if( mMonitorData->processElementsRequest(con,mSequence,(char *)&data[6],dataLen,mlastUpdateTime )) + mlastUpdateTime = time(NULL); + break; + } + // NOTE: if mHierarchy is not sent or changed, then send it. + + case MON_MSG_QUERY_HIERARCHY: + if( mMonitorData->processHierarchyRequest(con,mSequence) == false) + { + break; + } + mHierarchySent = true; + break; + + case MON_MSG_QUERY_DESCRIPTION: + mMonitorData->processDescriptionRequest(con,mSequence,(char *)&data[6], dataLen,mMark); + break; + + case MON_MSG_ERROR: + processError(data); + break; + + default: + fprintf(stderr,"MONITOR API: unknown message type received from client [%x]\n",msg.getCommand()); + break; + } +} + + +bool MonitorObject::checkAddress(const char * address) +{ +int x = 0; + while( mAddressList[x] ) + { + if (strncmp(mAddressList[x], address, strlen(mAddressList[x]))== 0) + return true; + x++; + } + return false; +} + +bool MonitorObject::checkPasswd( const char *passwd) +{ + if (strcmp(mPasswd,passwd)== 0 ) + return true; + return false; +} + +bool MonitorObject::processAuthRequest(const unsigned char * data, int /* dataLen */) +{ +char reply; +stringMessage strMsg(data); +char addBuff[16] = {0}; +char sendBuf[32]; +int len; + + + reply = '1'; + + if ( checkAddress(mConnection->GetDestinationIp().GetAddress(addBuff)) == false) + { + reply = '3'; + } + else if (checkPasswd(strMsg.getData()) == false) + { + reply = '2'; + } + + memset(sendBuf, 0, sizeof(sendBuf)); + + len = 0; + packShort( sendBuf + len, len,(short)MON_MSG_AUTHREPLY); + packShort( sendBuf + len, len, mSequence); + packShort( sendBuf + len, len,(short)3); + packShort( sendBuf + len, len,(short)CURRENT_API_VERSION); + packByte( sendBuf + len, len, reply); + + mConnection->Send(cUdpChannelReliable1, sendBuf, 9 ); + return true; +} + +void MonitorObject::DescriptionMark(int x,int mode) +{ + if( mode == 0 ) set_bit(mMark,x); else unset_bit(mMark,x); +} + +char * getErrorString(unsigned short errorCode) +{ + if (errorCode == 0) + { + return "none"; + } + else + { + switch (errorCode) + { + case INVALID_HEADER: + case INVALID_SEQUENCE: + case INVALID_MSG_LENGTH: + case INVALID_MSG_TYPE: + return "Invalid message"; + + case INVALID_AUTH_REQUEST: + return "Invalid authorization request"; + + case INVALID_ELEMENT_REQUEST: + case INVALID_HIERARCHY_REQUEST: + return "Invalid data request"; + + case INVALID_AUTH_REPLY: + return "Invalid reply to authentication request"; + + case INVALID_ELEMENT_REPLY: + case INVALID_HIERARCHY_REPLY: + return "Badly formatted data in dataReplyMessage"; + + case INVALID_ERROR_CODE: + return "Received invalid error message"; + + } + } + return "Undefined error code"; +} + +bool MonitorObject::processError(const unsigned char * data) +{ + stringMessage strMsg(data); + unsigned short errCode = (unsigned short)atoi(strMsg.getData()); + fprintf(stderr,"MONITOR API Error: %s\n", getErrorString(errCode)); + return true; +} + +/////////////////////////////////////// +// MonitorManager implementation +//////////////////////////////////////// +MonitorManager::MonitorManager(char *configFile, CMonitorData *_gamedata, UdpManager *manager, bool _bprint) +{ + mManager = manager; + mbprint = _bprint; + passString = NULL; + mMonitorData = _gamedata; + mObjectCount = 0; + + for(int x=0;xSetHandler(NULL); + con->Disconnect(); + con->Release(); + } + if( mbprint ) + fprintf(stderr,"MonitorAPI: Collector connection reached max (%d).\n",CONNECTION_MAX); + return; + } + + AddObject(new MonitorObject(con,mMonitorData,passString,allowedAddressList,mbprint)); +} + +void MonitorManager::AddObject(MonitorObject *Object) +{ + mObject[mObjectCount++] = Object; +} + +void MonitorManager::GiveTime() +{ + // check if the monitor object is no longer connected + for (int i = 0; i < mObjectCount; i++) + { + if( mObject[i]->mConnection == NULL || + mObject[i]->mConnection->GetStatus() == UdpConnection::cStatusDisconnected ) + { + MonitorObject *o = mObject[i]; + mObjectCount--; + memmove(&mObject[i], &mObject[i + 1], (mObjectCount - i) * sizeof(MonitorObject *)); + i--; + delete o; + } + } +} + +void MonitorManager::HierarchyChanged() +{ + for (int i = 0; i < mObjectCount; i++) + mObject[i]->HeirarchyChanged(); +} + +void MonitorManager::DescriptionMark(int x ,int mode) +{ + for (int i = 0; i < mObjectCount; i++) + mObject[i]->DescriptionMark(x,mode); +} + +bool MonitorManager::loadAuthData(const char * filename) +{ +int nline; +int len; +int x; +char buffer[1024]; + + FILE *fp = fopen(filename,"r"); + if (fp == NULL) + { + fprintf(stderr,"Monitor API: could not open %s file\nTHIS FILE IS REQUIRED.\n", filename); + return false; + } + + if( passString ) + free(passString); + + + for(x=0;x 0 ) + { + if( nline == 0 ) + { + passString = (char *)malloc( len + 1 ); + strcpy(passString,buffer); + } + else + { + allowedAddressList[x] = (char *)malloc( len + 1 ); + strcpy(allowedAddressList[x],buffer); + x++; + } + } + nline++; + } + + // clean up + fclose(fp); + return true; +} + +//****************************************************************************************************** +//****************************************************************************************************** +//****************************************************************************************************** +//****************************************************************************************************** + +CMonitorAPI::CMonitorAPI( char *configFile, unsigned short Port, bool _bprint , char *address, UdpManager * mang ) +{ + mbprint = _bprint; + mPort = Port; + + mAddress = NULL; + if( address ) + { + mAddress = (char *)malloc(strlen(address)+1); + strcpy(mAddress,address); + } + + if( mang == NULL ) + { + UdpManager::Params params; + params.handler = NULL; + params.maxConnections = CONNECTION_MAX; + params.outgoingBufferSize = 1000000; + params.noDataTimeout = 130000; + params.oldestUnacknowledgedTimeout = 120000; + params.processIcmpErrors = false; + params.port = mPort; + mManager = new UdpManager(¶ms); + mMonitorData = new CMonitorData(); + } + + mObjectManager = new MonitorManager(configFile,mMonitorData,mManager,mbprint); + + mManager->SetHandler(mObjectManager); + + if( mbprint ) + fprintf(stderr,"MonitorAPI: started on port->%d\n",mPort); +} + +CMonitorAPI::~CMonitorAPI() +{ + if( mAddress ) + free(mAddress); + + if( mObjectManager ) + delete mObjectManager; + + mManager->Release(); + + if( mMonitorData ) + delete mMonitorData; + +} + +void CMonitorAPI::Update() +{ + if( mManager ) + mManager->GiveTime(); + if( mObjectManager ) + mObjectManager->GiveTime(); +} + +void CMonitorAPI::add(const char *label, int id, int ping, char *des ) +{ + if( mMonitorData->add(label,id,ping,des)) + mObjectManager->HierarchyChanged(); +} + +void CMonitorAPI::remove(int Id) +{ + mMonitorData->remove(Id); + mObjectManager->HierarchyChanged(); +} + +void CMonitorAPI::setDescription( int Id, const char *Description ) +{ +int x; +int mode; + + x = mMonitorData->setDescription(Id,Description,mode); + if( x == -1 ) + return; + mObjectManager->DescriptionMark(x,mode); +} + +void CMonitorAPI::dump(){ mMonitorData->dump(); } + +//---------------------------------------------------------------- +monMessage::monMessage():command(0),sequence(0),size(0){} + +//---------------------------------------------------------------- +monMessage::monMessage(short cmd,short seq,short s):command(cmd),sequence(seq),size(s){} + +//---------------------------------------------------------------- +monMessage::monMessage(const unsigned char * source):command(0),sequence(0),size(0) +{ +int len; + + len = 0; + unpackShort( (char *)source + len, len,command); + unpackShort( (char *)source + len, len,sequence); + unpackShort( (char *)source + len, len,size); +} + +//---------------------------------------------------------------- +monMessage::monMessage(const monMessage ©):command(copy.command),sequence(copy.sequence),size(copy.size){} + +//---------------------------------------------------------------- +monMessage::~monMessage(){} + + +//---------------------------------------------------------------- +stringMessage::stringMessage(const unsigned char * source):monMessage(source) +{ +int size; + + size = strlen((char *)source+6) +1; + data = new char [ size ]; + strcpy(data,(char *)source+6); // Add six for the size of the header +} + +//---------------------------------------------------------------- +stringMessage::stringMessage(const unsigned short command,const unsigned short sequence,const unsigned short size,char * newData): +monMessage(command, sequence, size) +{ + data = new char [strlen(newData) + 1]; + if (data) + strncpy(data, newData, strlen(newData + 1)); + else + data = NULL; +} + +//---------------------------------------------------------------- +stringMessage::~stringMessage() +{ + if(data) + delete [] data; + data = 0; +} + +//---------------------------------------------------------------- +authReplyMessage::authReplyMessage(const unsigned char * source) : +monMessage(source) +{ + int len = 6; + unpackShort((char *)source+len,len,version); + unpackByte((char *)source+len,len,data); +} + +//---------------------------------------------------------------- +authReplyMessage::authReplyMessage(const unsigned short command, + const unsigned short sequence, + const unsigned short size, + unsigned char newData): +monMessage(command, sequence, size),data(newData){} + +//---------------------------------------------------------------- +authReplyMessage::~authReplyMessage(){} + +//---------------------------------------------------------------- +dataReplyMessage::dataReplyMessage(const unsigned char * source) : +monMessage(source) +{ + data = new unsigned char[getSize()+1]; + + if (data) + { + memcpy(data, source + 6, getSize()); + data[getSize()] = 0; + } + else + data = NULL; +} + +//---------------------------------------------------------------- +dataReplyMessage::dataReplyMessage(const unsigned short command, + const unsigned short sequence, + const unsigned short size, + unsigned char * newData, + int newDataLen): +monMessage(command, sequence, size) +{ + data = new unsigned char[newDataLen]; + if (data) + memcpy(data, newData, newDataLen); + else + data = NULL; +} + +//---------------------------------------------------------------- +dataReplyMessage::~dataReplyMessage() +{ + if (data) + { + delete [] data; + data = 0; + } +} + +//---------------------------------------------------------------- +simpleMessage::simpleMessage(const unsigned char * source):monMessage(source){} +//---------------------------------------------------------------- +simpleMessage::simpleMessage(const unsigned short command, + const unsigned short sequence, + const unsigned short size): +monMessage(command, sequence, size){} +//---------------------------------------------------------------- +simpleMessage::~simpleMessage(){} + +//---------------------------------------------------------------- + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorAPI.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorAPI.h new file mode 100644 index 00000000..fa60d698 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorAPI.h @@ -0,0 +1,105 @@ +#ifndef __MONITOR_API__ +#define __MONITOR_API__ + +//********************************************************************************************** +//********************************************************************************************** +//********************************** DO NOT EDIT THIS FILE *********************************** +//********************************************************************************************** +//********************************************************************************************** + +#include "UdpLibrary/UdpHandler.hpp" +#include "UdpLibrary/UdpLibrary.hpp" +#include "monitorData.h" + +#define AUTHADDRESS_MAX 30 +#define CONNECTION_MAX 8 + +class MonitorObject : public UdpConnectionHandler +{ + CMonitorData *mMonitorData; + short mSequence; + char *mPasswd; + char **mAddressList; + unsigned char *mMark; + int mbAuthenticated; + bool mHierarchySent; + bool mbprint; + long mlastUpdateTime; + bool checkAddress(const char * address); + bool checkPasswd( const char *passwd); + + public: + + UdpConnection *mConnection; + MonitorObject(UdpConnection *con, CMonitorData *, char *passwd, char **addressList ,bool _bprint ); + virtual ~MonitorObject(); + virtual void OnRoutePacket(UdpConnection *con, const uchar *data, int dataLen); + virtual void OnTerminated( UdpConnection *con); + bool processAuthRequest(const unsigned char * data, int dataLen); + bool processError(const unsigned char * data); + void HeirarchyChanged(){ mHierarchySent = false; } + void DescriptionMark(int x,int mode); +}; + +class MonitorManager : public UdpManagerHandler +{ + bool mbprint; + CMonitorData *mMonitorData; + char *passString; + char *allowedAddressList[AUTHADDRESS_MAX]; + bool loadAuthData(const char * filename); + +public: + MonitorManager(char *configFile, CMonitorData *gamedata, UdpManager *manager , bool _bprint = false); + virtual ~MonitorManager(); + + void AddObject(MonitorObject *player); + void GiveTime(); + void HierarchyChanged(); + void DescriptionMark(int x,int mode); + virtual void OnConnectRequest(UdpConnection *con); + + protected: + + MonitorObject *mObject[CONNECTION_MAX]; + int mObjectCount; + UdpManager *mManager; +}; + +class CMonitorAPI { + + bool mbprint; + +public: + + CMonitorAPI( char *configFile, unsigned short Port, bool _bprint = false , char * address = NULL, UdpManager * mang = NULL ); + + ~CMonitorAPI(); + + void Update(); + + void add( const char *label, int id, int ping = MON_PING_5MIN, char *des = NULL ); + + void setDescription( int Id, const char *Description ) ; + + int PingValue(int p){ return mMonitorData->pingValue(p); } + + void set(int Id, int value){ mMonitorData->set(Id,value); } + + void remove(int Id); + + void dump(); + +private: + + CMonitorData *mMonitorData; + UdpManager *mManager; + MonitorManager *mObjectManager; + char *mAddress; + unsigned short mPort; + +}; + +#endif + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorData.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorData.cpp new file mode 100644 index 00000000..f9e7d1af --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/monitorData.cpp @@ -0,0 +1,598 @@ +// #include +#include +#include +#include +#include +#include "monitorData.h" + + +#ifdef WIN32 +#define snprintf _snprintf +#endif + + +//********************************************************************************************** +//********************************************************************************************** +//********************************** DO NOT EDIT THIS FILE *********************************** +//********************************************************************************************** +//********************************************************************************************** + + +#define ELEMENT_START_VALUE 600 +#define BUFFER_SIZE_START 2048 +#define BUFFER_RESIZE_VALUE 1024 + +extern void set_bit( unsigned char p[], int bit); +extern void unset_bit(unsigned char p[], int bit); +extern int get_bit( unsigned char p[], int bit); + + +// ------------------ Support Routine ----------------------------- +////////////////////////////////////////////////////////////////////// +// qsort compare routine +////////////////////////////////////////////////////////////////////// + +int compar( const void *e1, const void *e2 ) +{ +MON_ELEMENT *data1,*data2; + + data1 = (MON_ELEMENT *)e1; + data2 = (MON_ELEMENT *)e2; + +#ifdef _WIN32 + return stricmp(data1->label, data2->label); +#else + return strcasecmp(data1->label,data2->label); +#endif +} + + +// ------------------ Game Data Object ---------------------------- +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CMonitorData::CMonitorData() +{ + + m_sequence = 1; + m_buffer = NULL; + m_nbuffer = 0; + m_count = 0; + m_max = ELEMENT_START_VALUE; + m_data = new MON_ELEMENT[m_max]; + for(int x=0;xSend(cUdpChannelReliable1, p, size + 6); + free(p); + sequence++; +} + +bool CMonitorData::processHierarchyRequest( UdpConnection *con, short & sequence ) +{ +int x,size; +char temp[215]; + + if( m_count == 0 ) + { + send(con,sequence,MON_MSG_REPLY_HIERARCHY,"NOTREADY"); + return 0; + } + + if( m_sort ) + { + qsort(m_data,m_count,sizeof(MON_ELEMENT),compar); + m_sort = 0; + } + + memset(m_buffer,0, 16); + + size = 0; + for(x=0;x= m_nbuffer ) + resize_buffer(size+BUFFER_RESIZE_VALUE); + strcat(m_buffer,temp); + } + send(con,sequence,MON_MSG_REPLY_HIERARCHY,m_buffer); + return 1; +} + +bool CMonitorData::processElementsRequest(UdpConnection *con, short & sequence, char * data, int /* dataLen */ , long lastUpdateTime ) +{ +char tmp[200]; +int x,id; +int size; +int count; +char **list; + + + memset(m_buffer,0,16); + + if( !strcmp(data,"-1") ) + { + size = 0; + for (x=0;x= m_nbuffer ) + resize_buffer(size+BUFFER_RESIZE_VALUE); + strcat(m_buffer,tmp); + } + send(con,sequence,MON_MSG_REPLY_ELEMENTS,m_buffer); + return 1; + } + + if( !strcmp(data,"-2") ) + { + size = 0; + count = 0; + for (x=0;x= m_nbuffer ) + resize_buffer(size+BUFFER_RESIZE_VALUE); + strcat(m_buffer,tmp); + count++; + } + } + if ( count ) + { + send(con,sequence,MON_MSG_REPLY_ELEMENTS,m_buffer); + return 1; + } + return 0; + } + + + + list = new char * [ELEMENT_MAX]; + + count = parseList( list, data,'|',ELEMENT_MAX); + + memset(m_buffer,0,16); + + size = 0; + for(x=0;x= m_nbuffer ) + resize_buffer(size+BUFFER_RESIZE_VALUE); + strcat(m_buffer,tmp); + } + } + + delete [] list; + + if( count > 0 ) + send(con,sequence,MON_MSG_REPLY_ELEMENTS,m_buffer); + + return 0; +} + + +bool CMonitorData::processDescriptionRequest(UdpConnection *con, short & sequence, char * userData, int dataLen, unsigned char *mark) +{ +char line[4096]; +char tmp[400]; +int x,id,size; +int flag; +char *p; + + size = 0; + memset(m_buffer,0,16); + strcpy(line,userData); + p = strtok(line,"|"); + flag = 0; + if( strstr(p,"next") ) + { + for(x=0;x= m_nbuffer ) + resize_buffer(size+BUFFER_RESIZE_VALUE); + strcat(m_buffer,tmp); + send(con,sequence,MON_MSG_REPLY_DESCRIPTION,m_buffer); + return 1; + } + } + } + else + { + if( p ) + { + size = 0; + flag = 0; + id = atoi(p); + for(x=0;x= m_nbuffer ) + resize_buffer(size+BUFFER_RESIZE_VALUE); + strcat(m_buffer,tmp); + if( size >= 2000 || m_data[x].discription ) + { + send(con,sequence,MON_MSG_REPLY_DESCRIPTION,m_buffer); + return 1; + } + } + } + } + } + + + if( flag == 2 ) + send(con,sequence,MON_MSG_REPLY_DESCRIPTION,m_buffer); + send(con,sequence,MON_MSG_REPLY_DESCRIPTION,"none"); + return 0; + +} + +int CMonitorData::add(const char *label, int id, int ping, char *des ) +{ +int x; + + if( strlen(label) >= 127 ) + { + printf("MonAPI Error: add() label exceeds length of 127. ->%s\n",label); + return 0; + } + + for(x=0;x %s\n",id,m_data[x].label,label); + m_data[x].ping = pingValue( ping ); + m_data[x].value = 0; + m_data[x].id = id; + + //******* Label *********** + if( m_data[x].label ) + delete m_data[x].label; + m_data[x].label = new char [strlen(label)+1]; + strcpy(m_data[x].label,label); + + //******* Discription ****** + if( m_data[x].discription != NULL ) + delete [] m_data[x].discription; + m_data[x].discription = NULL; + if( des ) + { + m_data[x].discription = new char [strlen(des)+1]; + strcpy(m_data[x].discription,des); + } + m_sort = 1; + return 1; + } + + if( !strcmp(label,m_data[x].label) ) + { + printf("MonAPI ERROR: Label already found %s Id: old(%d) new(%d).\n",label,m_data[x].id,id); + return 0; + } + } + + if( m_max == m_count + 1 ) + { + printf("MonitorObject: max element reached [%d].\n%s\nContact David Taylor (858) 577-3155.\n",m_max,label); + return 0; + } + m_data[m_count].ping = pingValue( ping ); + m_data[m_count].value = 0; + m_data[m_count].id = id; + m_data[m_count].label = new char [strlen(label)+1]; + strcpy(m_data[m_count].label,label); + if( des ) + { + if( m_data[x].discription ) + delete [] m_data[x].discription; + m_data[x].discription = new char [strlen(des)+1]; + strcpy(m_data[x].discription,des); + } + m_count++; + m_sort = 1; + return 1; +} + +int CMonitorData::setDescription( int Id, const char *Description , int & mode) +{ +int x; + + for(x=0;x 0 ) + { + if( *data == tok ) + { + *data = 0; + data++; + cnt++; + list[count] = data; + if( *data ) count++; + } + cnt++; + if( *data ) data++; + } + return count; +} + + +void CMonitorData::dump() +{ + if( m_sort ) + { + qsort(m_data,m_count,sizeof(MON_ELEMENT),compar); + m_sort = 0; + } + printf("********** Monitor API Dump *******************\n"); + printf(" Version: %d\n",CURRENT_API_VERSION); + printf("\ncount: %d\n", m_count); + printf("\t%-40s %8s\t%s\t%s\n","Label","Id","Ping","Value"); + for(int x=0;x +class dataReplyMessage : public monMessage +{ +public: + explicit dataReplyMessage(const unsigned short command, const unsigned short sequence, const unsigned short size, unsigned char *data, int dataLen); + explicit dataReplyMessage(const unsigned char * source); + + ~dataReplyMessage(); + + inline const unsigned char * getData() const {return data;}; + +private: + unsigned char * data; +}; + + +//---------------------------------------------------------------- +// simpleMessage +// This is the most basic monitor API message. It can be created from an +// Archive::ByteStream in order to discover the message type for more complex messages, +// or it could be sent explicitly for query message types. +class simpleMessage : public monMessage +{ +public: + explicit simpleMessage(const unsigned short command, const unsigned short sequence, const unsigned short size); + explicit simpleMessage(const unsigned char * source); + + ~simpleMessage(); + +private: +}; + +//*************** Internal Struct *************************** +typedef struct { + + int value; // Value of the element + int id; // Id for users, user control, could be any value, must be unique + int ping; // Ping time, use one of the API defined values, MON_PING_15,... + char *label; // Label of element, use periods to make sub labels, last name sperated + // by periods is the name of the element. + // + // Example: "Universe.Planet.Zone1.count" + // Universe.Planet.Zone1 = Tree name + // count = element name + // + // NOTE: This label is the name of the data element, change the label name + // and a new history of data is started. You will not be able to change + // the name at run time. + // + char *discription; // Discription of element. This can be changed at will. + + + long ChangedTime; + +} MON_ELEMENT; + +class CMonitorData { + + + + MON_ELEMENT *m_data; // Pointer to MON_DATA elements + int m_ndata; + int m_count; + int m_max; + int m_sort; + char *m_buffer; + int m_nbuffer; + short m_sequence; + + + int parseList( char **list, char *data, char tok , int max ); + void resize_buffer(int new_size); + void send( UdpConnection *mConnection,short & sequence, short msg,char *data ); + +public: + + CMonitorData(); + ~CMonitorData(); + + int getCount(){ return m_ndata; } + int DataMax(){ return m_max; } + bool processHierarchyRequest(UdpConnection *con, short & sequence); + bool processElementsRequest( UdpConnection *con, short & sequence, char * userData, int dataLen , long lastUpdateTime); + bool processDescriptionRequest(UdpConnection *con, short & sequence, char * userData, int dataLen,unsigned char *mark); + int add(const char *label, int id, int ping, char *des ); + int setDescription( int Id, const char *Description , int & mode); + char *getDescription(int x){ if( m_ndata >= x ) return NULL; return m_data[x].discription;} + int pingValue(int p); + void set(int Id, int value); + void remove(int Id); + void dump(); +}; + +#endif + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/pid.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/pid.cpp new file mode 100644 index 00000000..0b8fc8c3 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/pid.cpp @@ -0,0 +1,57 @@ +#include "pid.h" + +#ifdef WIN32 +#include +#else +#include +extern int errno; +#endif + +#include + +#define NAME_SIZE 256 + +#ifdef WIN32 +std::string soegethostname() +{ + std::string hn = ""; + WORD wVersionRequested; + WSADATA wsaData; + char name[NAME_SIZE]; + wVersionRequested = MAKEWORD( 2, 0 ); + + if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) + { + + if( gethostname ( name, sizeof(name)) != 0) + { + int err = WSAGetLastError(); + printf("gethostname error: %d\n", err); + } + + WSACleanup( ); + } + + hn = name; + return hn; +} + +#else + +std::string soegethostname() +{ + char res[NAME_SIZE]; + memset(res, 0, NAME_SIZE); + std::string retVal; + int error = gethostname(res, NAME_SIZE-1); + + int errdetail; + if(error !=0) + { + errdetail = errno; + } + retVal = res; + return retVal; +} + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/pid.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/pid.h new file mode 100644 index 00000000..80d994a7 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/pid.h @@ -0,0 +1,23 @@ +#ifndef SOE_PID_H +#define SOE_PID_H + +#ifndef WIN32 +#include +#include +#define soegetpid() (long)getpid() +#endif + +#ifdef WIN32 +// include winsock 2 before windows to not have winsock 1 included . . . +#include +#include + +#define soegetpid() (long)GetCurrentProcessId() + +#endif + +#include + +std::string soegethostname(); + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/priority.hpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/priority.hpp new file mode 100644 index 00000000..82751efa --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/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, sizeof(mQueue)); +} + +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/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/profile.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/profile.cpp new file mode 100644 index 00000000..8b3917cb --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/profile.cpp @@ -0,0 +1,454 @@ +#ifdef WIN32 +#pragma warning (disable: 4786) +#endif + + +#include +#include +#include "profile.h" +#include "timer.h" + + +#ifdef WIN32 +#define snprintf _snprintf +#endif + + +Profiler globalProfiler; + + +Profiler::NodeData::NodeData() : + mStartTimer(0), + mTotalLatency(0), + mChildLatency(0), + mTotalChildLatency(0), + mMaxLatency(0), + mMinLatency(0), + mTotalCalls(0) +{ +} + + +Profiler::Profiler() : + mActive(false), + mStartTime(0), + mStartTimer(0), + mDataMap(), + mDataStack(), + mNodeMap(), + mNodeStack() +{ + mStartTime = (unsigned)time(0); + mStartTimer = soe::GetTimer(); +} + +Profiler::~Profiler() +{ +} + +void Profiler::AddNode(Node & lhs, const Node & rhs) +{ + lhs.mData.mTotalLatency += rhs.mData.mTotalLatency; + lhs.mData.mTotalChildLatency += rhs.mData.mTotalChildLatency; + lhs.mData.mTotalCalls += rhs.mData.mTotalCalls; + if (rhs.mData.mMaxLatency > lhs.mData.mMaxLatency) + lhs.mData.mMaxLatency = rhs.mData.mMaxLatency; + if (lhs.mData.mMinLatency == 0 || rhs.mData.mMinLatency < lhs.mData.mMinLatency) + lhs.mData.mMinLatency = rhs.mData.mMinLatency; + + NodeMap_t::const_iterator iter; + for (iter = rhs.mNodeMap.begin(); iter != rhs.mNodeMap.end(); iter++) + { + Node & node = lhs.mNodeMap[iter->first]; + AddNode(node, iter->second); + } +} + +void Profiler::AddData(const Profiler & rhs, bool hierarchy) +{ + if (hierarchy) + { + NodeMap_t::const_iterator nodeIter; + for (nodeIter = rhs.mNodeMap.begin(); nodeIter != rhs.mNodeMap.end(); nodeIter++) + { + Node & node = mNodeMap[nodeIter->first]; + AddNode(node, nodeIter->second); + } + } + + DataMap_t::const_iterator iter; + for (iter = rhs.mDataMap.begin(); iter != rhs.mDataMap.end(); iter++) + { + NodeData & lhs = mDataMap[iter->first]; + const NodeData & rhs = iter->second; + + lhs.mTotalLatency += rhs.mTotalLatency; + lhs.mTotalChildLatency += rhs.mTotalChildLatency; + lhs.mTotalCalls += rhs.mTotalCalls; + if (rhs.mMaxLatency > lhs.mMaxLatency) + lhs.mMaxLatency = rhs.mMaxLatency; + if (lhs.mMinLatency == 0 || rhs.mMinLatency < lhs.mMinLatency) + lhs.mMinLatency = rhs.mMinLatency; + } +} + +void Profiler::Open(const std::string & description) +{ + NodeData & data = mDataMap[description]; + data.mStartTimer = soe::GetTimer(); + data.mChildLatency = 0; + mDataStack.push_front(&data); + + if (mNodeStack.empty()) + { + Node & node = mNodeMap[description]; + node.mData.mStartTimer = data.mStartTimer; + node.mData.mChildLatency = 0; + mNodeStack.push_front(&node); + } + else + { + Node & node = mNodeStack.front()->mNodeMap[description]; + node.mData.mStartTimer = data.mStartTimer; + node.mData.mChildLatency = 0; + mNodeStack.push_front(&node); + } +} + +void Profiler::Close() +{ + if (mNodeStack.empty() || mDataStack.empty()) + return; + + double latency; + double trueLatency; + + NodeData & data = *mDataStack.front(); + Node & node = *mNodeStack.front(); + mDataStack.pop_front(); + mNodeStack.pop_front(); + + latency = soe::GetTimerLatency(data.mStartTimer); + + trueLatency = latency - data.mChildLatency; + if (data.mMaxLatency < trueLatency) + data.mMaxLatency = trueLatency; + if (data.mMinLatency == 0 || data.mMinLatency > trueLatency) + data.mMinLatency = trueLatency; + data.mTotalLatency += latency; + data.mTotalCalls++; + + trueLatency = latency - node.mData.mChildLatency; + if (node.mData.mMaxLatency < trueLatency) + node.mData.mMaxLatency = trueLatency; + if (node.mData.mMinLatency == 0 || node.mData.mMinLatency > trueLatency) + node.mData.mMinLatency = trueLatency; + node.mData.mTotalLatency += latency; + node.mData.mTotalCalls++; + + if (!mNodeStack.empty() && !mDataStack.empty()) + { + NodeData & parentData = *mDataStack.front(); + Node & parentNode = *mNodeStack.front(); + parentData.mChildLatency += latency; + parentData.mTotalChildLatency += latency; + parentNode.mData.mChildLatency += latency; + parentNode.mData.mTotalChildLatency += latency; + } +} + +void Profiler::Clear() +{ + mStartTime = (unsigned)time(0); + mStartTimer = soe::GetTimer(); + mDataMap.clear(); + mDataStack.clear(); + mNodeMap.clear(); + mNodeStack.clear(); +} + +void Profiler::GetOutput(std::string & output, bool hierarchy) +{ + char buffer[256]; + double profileDuration = soe::GetTimerLatency(mStartTimer); + + // print header + struct tm timeStruct = *localtime((time_t *)&mStartTime); + strftime(buffer,128,"%m/%d/%Y %H:%M:%S", &timeStruct); + output = buffer; + snprintf(buffer, sizeof(buffer), " profile duration: %.1fms\n", profileDuration*1000); + output += buffer; + + if (hierarchy && !mNodeMap.empty()) + { + // print legend + snprintf(buffer, sizeof(buffer), "prcnt latency max min avg count prcnt latency description\n"); + output += buffer; + + // recursively print node map (hierarchical call profile) + GetNodeOutput(output, mNodeMap, profileDuration); + } + + // print legend + snprintf(buffer, sizeof(buffer), "prcnt latency max min avg count prcnt latency description\n"); + output += buffer; + + // print data map (linear call profile) + DataMap_t::const_iterator iter; + for (iter = mDataMap.begin(); iter != mDataMap.end(); iter++) + { + const NodeData & data = iter->second; + double trueLatency = data.mTotalLatency-data.mTotalChildLatency; + snprintf(buffer, sizeof(buffer), "%5.1f %9.3f %7.3f %7.3f %7.3f %5u %5.1f %9.3f ", trueLatency/profileDuration*100, trueLatency*1000, data.mMaxLatency*1000, data.mMinLatency*1000, trueLatency/data.mTotalCalls*1000, data.mTotalCalls, data.mTotalLatency/profileDuration*100, data.mTotalLatency*1000); + output += buffer; + snprintf(buffer, sizeof(buffer), "%s\n", iter->first.c_str()); + output += buffer; + } +} + +void Profiler::GetXmlOutput(std::string & output, bool hierarchy) +{ + char buffer[256]; + double profileDuration = soe::GetTimerLatency(mStartTimer); + + output += ""; + + // print header + struct tm timeStruct = *localtime((time_t *)&mStartTime); + strftime(buffer,128,"%m/%d/%Y %H:%M:%S", &timeStruct); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%.1f\n", profileDuration*1000); + output += ""; + output += buffer; + output += ""; + + output += ""; + + if (hierarchy && !mNodeMap.empty()) + { + // print legend + //sprintf(buffer,"prcnt latency max min avg count prcnt latency description\n"); + //output += buffer; + + // recursively print node map (hierarchical call profile) + //GetNodeOutput(output, mNodeMap, profileDuration); + output += ""; + GetXmlNodeOutput(output, mNodeMap, profileDuration); + output += ""; + } + + // print legend + //sprintf(buffer,"prcnt latency max min avg count prcnt latency description\n"); + //output += buffer; + + output += ""; + + output += ""; + snprintf(buffer, sizeof(buffer), "%u", mDataMap.size()); + output += buffer; + output += ""; + + // print data map (linear call profile) + int i = 1; + DataMap_t::const_iterator iter; + for (iter = mDataMap.begin(); iter != mDataMap.end(); iter++) + { + const NodeData & data = iter->second; + double trueLatency = data.mTotalLatency-data.mTotalChildLatency; + + /* + sprintf(buffer, "%5.1f %9.3f %7.3f %7.3f %7.3f %5u %5.1f %9.3f ", + trueLatency/profileDuration*100, + trueLatency*1000, + data.mMaxLatency*1000, + data.mMinLatency*1000, + trueLatency/data.mTotalCalls*1000, + data.mTotalCalls, + data.mTotalLatency/profileDuration*100, + data.mTotalLatency*1000); + */ + snprintf(buffer, sizeof(buffer), "", i); + output += buffer; + + output += ""; + output += iter->first; + output += ""; + + snprintf(buffer, sizeof(buffer), "%5.1f", trueLatency/profileDuration*100); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%9.3f", trueLatency*1000); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%7.3f", data.mMaxLatency*1000); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%7.3f", data.mMinLatency*1000); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%7.3f", trueLatency/data.mTotalCalls*1000); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%5u", data.mTotalCalls); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%5.1f", data.mTotalLatency/profileDuration*100); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%9.3f", data.mTotalLatency*1000); + output += ""; + output += buffer; + output += ""; + + //sprintf(buffer,"%s\n",iter->first.c_str()); + //output += buffer; + + snprintf(buffer, sizeof(buffer), "", i++); + output += buffer; + } + output += ""; +} + +void Profiler::GetNodeOutput(std::string & output, const NodeMap_t & nodeMap, double profileDuration, int level) +{ + char buffer[256]; + + // recursively print node map + NodeMap_t::const_iterator iter; + for (iter = nodeMap.begin(); iter != nodeMap.end(); iter++) + { + // output node description + const Node & node = iter->second; + double trueLatency = node.mData.mTotalLatency-node.mData.mTotalChildLatency; + snprintf(buffer, sizeof(buffer), "%5.1f %9.3f %7.3f %7.3f %7.3f %5u %5.1f %9.3f ", trueLatency/profileDuration*100, trueLatency*1000, node.mData.mMaxLatency*1000, node.mData.mMinLatency*1000, trueLatency/node.mData.mTotalCalls*1000, node.mData.mTotalCalls, node.mData.mTotalLatency/profileDuration*100, node.mData.mTotalLatency*1000); + output += buffer; + for (int i=0; ifirst.c_str()); + output += buffer; + + // recursively print node's node map + GetNodeOutput(output, node.mNodeMap, profileDuration, level+1); + } +} + +void Profiler::GetXmlNodeOutput(std::string & output, const NodeMap_t & nodeMap, double profileDuration, int level) +{ + char buffer[256]; + + output += ""; + snprintf(buffer, sizeof(buffer), "%u", nodeMap.size()); + output += buffer; + output += ""; + + // recursively print node map + int i = 1; + NodeMap_t::const_iterator iter; + for (iter = nodeMap.begin(); iter != nodeMap.end(); iter++) + { + snprintf(buffer, sizeof(buffer), "", i); + output += buffer; + + output += ""; + output += iter->first; + output += ""; + + // output node description + const Node & node = iter->second; + double trueLatency = node.mData.mTotalLatency-node.mData.mTotalChildLatency; + /* + sprintf(buffer, "%5.1f %9.3f %7.3f %7.3f %7.3f %5u %5.1f %9.3f ", + trueLatency/profileDuration*100, + trueLatency*1000, + node.mData.mMaxLatency*1000, + node.mData.mMinLatency*1000, + trueLatency/node.mData.mTotalCalls*1000, + node.mData.mTotalCalls, + node.mData.mTotalLatency/profileDuration*100, + node.mData.mTotalLatency*1000); + */ + snprintf(buffer, sizeof(buffer), "%5.1f", trueLatency/profileDuration*100); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%9.3f", trueLatency*1000); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%7.3f", node.mData.mMaxLatency*1000); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%7.3f", node.mData.mMinLatency*1000); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%7.3f", trueLatency/node.mData.mTotalCalls*1000); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%5u", node.mData.mTotalCalls); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%5.1f", node.mData.mTotalLatency/profileDuration*100); + output += ""; + output += buffer; + output += ""; + + snprintf(buffer, sizeof(buffer), "%9.3f", node.mData.mTotalLatency*1000); + output += ""; + output += buffer; + output += ""; + + // recursively print node's node map + GetXmlNodeOutput(output, node.mNodeMap, profileDuration, level+1); + + snprintf(buffer, sizeof(buffer), "", i++); + output += buffer; + } +} + + +Profile::Profile(const char * description, Profiler & profile) : + mProfiler(profile) +{ + if (mProfiler.IsActive()) + { + mProfiler.Open(description); + } +} + +Profile::~Profile() +{ + if (mProfiler.IsActive()) + { + mProfiler.Close(); + } +} + + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/profile.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/profile.h new file mode 100644 index 00000000..704b6094 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/profile.h @@ -0,0 +1,82 @@ +#ifndef SERVER_PROFILE_H +#define SERVER_PROFILE_H + + +#include +#include +#include +#include "types.h" + + +class Profiler +{ + struct NodeData; + struct Node; + typedef std::map DataMap_t; + typedef std::map NodeMap_t; + typedef std::list DataStack_t; + typedef std::list NodeStack_t; + struct NodeData + { + NodeData(); + soe::uint64 mStartTimer; + double mTotalLatency; + double mChildLatency; + double mTotalChildLatency; + double mMaxLatency; + double mMinLatency; + unsigned mTotalCalls; + }; + struct Node + { + NodeData mData; + NodeMap_t mNodeMap; + }; + + public: + Profiler(); + ~Profiler(); + + void AddNode(Node & lhs, const Node & rhs); + void AddData(const Profiler & rhs, bool hierarchy=true); + + bool IsActive() { return mActive; } + void Start() { mActive = true; } + void Stop() { mActive = false; } + + void Open(const std::string & description); + void Close(); + void Clear(); + + void GetOutput(std::string & output, bool hierarchy=true); + void GetXmlOutput(std::string & output, bool hierarchy=true); + + private: + void GetNodeOutput(std::string & output, const NodeMap_t & nodeMap, double profileDuration, int level = 0); + void GetXmlNodeOutput(std::string & output, const NodeMap_t & nodeMap, double profileDuration, int level = 0); + void MakeValidXmlTag(std::string& tag); + + private: + bool mActive; + unsigned mStartTime; + soe::uint64 mStartTimer; + DataMap_t mDataMap; // stores linear call totals + DataStack_t mDataStack; // stores linear call stack + NodeMap_t mNodeMap; // stores hierarchical call totals + NodeStack_t mNodeStack; // stores hierarchical call stack +}; + +extern Profiler globalProfiler; + +class Profile +{ + public: + Profile(const char * description, Profiler & profile = globalProfiler); + ~Profile(); + private: + Profiler & mProfiler; +}; + + +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/rateLimit.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/rateLimit.h new file mode 100644 index 00000000..98b9dce9 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/rateLimit.h @@ -0,0 +1,22 @@ +#ifndef __RATE_LIMIT_H__ +#define __RATE_LIMIT_H__ + +namespace soe +{ + typedef long limit_t; + typedef long interval_t; + + struct RateLimit + { + RateLimit() + : mLimit(0) + , mInterval(0) + { } + + limit_t mLimit; + interval_t mInterval; + }; + typedef std::vector RateLimits_t; +}; + +#endif // __RATE_LIMIT_H__ diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/refptr.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/refptr.h new file mode 100644 index 00000000..288cd8bc --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/refptr.h @@ -0,0 +1,106 @@ +#ifndef UTIL_H +#define UTIL_H + +#include +#include + +template +class ref_ptr +{ +private: + struct Node + { + T object; + unsigned refCount; + }; + struct Allocator + { + public: + static std::vector & getBlockArray() + { + static std::vector * blockArray = new std::vector; + return *blockArray; + } + static std::list & getBlockList() + { + static std::list * blockList = new std::list; + return *blockList; + } + static T * allocate() + { + if (getBlockList().empty()) + grow(); + T * ptr = getBlockList().front(); + getBlockList().pop_front(); + _blocksAvailable--; + return new (ptr) T; + } + static T * allocate(const T & copy) + { + if (getBlockList().empty()) + grow(); + T * ptr = getBlockList().front(); + getBlockList().pop_front(); + _blocksAvailable--; + return new (ptr) T(copy); + } + static void release(T * ptr) + { + ptr->~T(); + getBlockList().push_front(ptr); + _blocksAvailable++; + if (_blocksAvailable == _blocksTotal) + { + for (unsigned i=0; i(getBlockArray()[i]); + getBlockArray().clear(); + getBlockList().clear(); + _blocksAvailable = _blocksTotal = 0; + } + } + private: + static void grow() + { + unsigned blockCount = (unsigned)(getBlockArray().size()+1)*32; + unsigned blockSize = (unsigned)blockCount * sizeof(Node)/sizeof(unsigned); + getBlockArray().push_back(new unsigned[blockSize]); + + Node * ptr = static_cast(getBlockArray().back()); + for (size_t i=0; i & copy) { copy.checkRef(); ptr = copy.ptr; addRef(); } + ~ref_ptr() { unref(); } + + //ref_ptr & operator=(T * p) { unref(); ptr = p; addRef(); return *this; } + ref_ptr & operator=(const ref_ptr & rhs) { rhs.checkRef(); if (&rhs != this && rhs.ptr != ptr) { unref(); ptr = rhs.ptr; addRef(); } return *this; } + T & operator*() const { checkRef(); return *ptr; } + T * operator->() const { checkRef(); return ptr; } + operator bool() const { return ptr != 0; } + bool unique() const { return (!ptr || node()->refCount == 1); } + void clone() { if (!unique()) { T * temp = Allocator::allocate(*ptr); unref(); ptr = temp; node()->refCount = 1; } } +private: + Node * node() const { return static_cast((void *)ptr); } + void checkRef() const { if (!ptr) { ptr = Allocator::allocate(); node()->refCount = 1; } } + void addRef() const { if (ptr) { node()->refCount++; } } + void unref() { if (ptr && !--(node()->refCount)) Allocator::release(ptr); } +private: + mutable T * ptr; +}; + +template +unsigned ref_ptr::Allocator::_blocksAvailable = 0; +template +unsigned ref_ptr::Allocator::_blocksTotal = 0; + + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serialize.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serialize.h new file mode 100644 index 00000000..3ffd98c7 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serialize.h @@ -0,0 +1,16 @@ +#ifndef SOE__SERIALIZE_H +#define SOE__SERIALIZE_H + +// this file has been split into two sections +// to enable separate inclusion in problematic cases +// with GCC 3.4.4 + +#include "serializeClasses.h" + +#ifdef USE_SERIALIZE_STRING_VECTOR +#include "serializeStringVector.h" +#endif + +#include "serializeTemplates.h" + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeClasses.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeClasses.h new file mode 100644 index 00000000..b094efde --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeClasses.h @@ -0,0 +1,934 @@ +#ifndef SOE__SERIALIZE_CLASSES_H +#define SOE__SERIALIZE_CLASSES_H + + +#include +#include +#include +#include + +#ifdef PRINTABLE_MESSAGES + +# include +# include + +# if defined(WIN32) && !defined(snprintf) +# define snprintf _snprintf +# endif + +#endif + +#ifdef TRACK_READ_WRITE_FAILURES +# include "trackMessageFailures.h" +# define MESSAGE_FAILURE_TRACE_GENERIC \ + soe::PushMessageFailure(soe::PrintToString("%s, %s:%u - failed to transmit, size = %u", __FUNCTION__, __FILE__, __LINE__, size)) +# define MESSAGE_FAILURE_TRACE_LABEL(__label__) \ + soe::PushMessageFailure(soe::PrintToString("%s, %s:%u - failed to transmit %s, size = %u", __FUNCTION__, __FILE__, __LINE__, __label__, size)) +# define MESSAGE_FAILURE_TRACE_INDEX(__index__) \ + soe::PushMessageFailure(soe::PrintToString("%s, %s:%u - failed to transmit item #%u, size = %u", __FUNCTION__, __FILE__, __LINE__, __index__, size)) +# define MESSAGE_FAILURE_TRACE_LABEL_INDEX(__label__, __index__) \ + soe::PushMessageFailure(soe::PrintToString("%s, %s:%u - failed to transmit %s, item #%u, size = %u", __FUNCTION__, __FILE__, __LINE__, __label__, __index__, size)) +#else +# define MESSAGE_FAILURE_TRACE_GENERIC +# define MESSAGE_FAILURE_TRACE_LABEL(__label__) +# define MESSAGE_FAILURE_TRACE_INDEX(__index__) +#endif + +#define DefineClassScribe(ClassName) \ + inline unsigned Read(const unsigned char * stream, unsigned size, ClassName & data, unsigned maxLen, unsigned version = 0) \ + { \ + return data.Read(stream, size, version); \ + } \ + inline unsigned Write(unsigned char * stream, unsigned size, const ClassName & data, unsigned version = 0) \ + { \ + return data.Write(stream, size, version); \ + } \ + inline int Print(char * stream, unsigned size, const ClassName & data, unsigned maxDepth = INT_MAX) \ + { \ + return data.Print(stream, size, maxDepth); \ + } + +#define DECLARE_SCRIBE_MEMBERS_INTERNAL \ + unsigned Read(const unsigned char * stream, unsigned size, unsigned version = 0); \ + unsigned Write(unsigned char * stream, unsigned size, unsigned version = 0) const; \ + int Print(char * stream, unsigned size, unsigned maxDepth = INT_MAX) const; \ + class Scribe; \ + static Scribe & GetScribe(); + +#define DECLARE_VIRTUAL_SCRIBE_MEMBERS_INTERNAL \ + virtual unsigned Read(const unsigned char * stream, unsigned size, unsigned version = 0); \ + virtual unsigned Write(unsigned char * stream, unsigned size, unsigned version = 0) const; \ + virtual int Print(char * stream, unsigned size, unsigned maxDepth = INT_MAX) const; \ + class Scribe; \ + static Scribe & GetScribe(); + +#define DECLARE_SCRIBE_MEMBERS \ + unsigned Read(const unsigned char * stream, unsigned size, unsigned version = 0); \ + unsigned Write(unsigned char * stream, unsigned size, unsigned version = 0) const; \ + int Print(char * stream, unsigned size, unsigned maxDepth = INT_MAX) const; + +#define DECLARE_VIRTUAL_SCRIBE_MEMBERS \ + virtual unsigned Read(const unsigned char * stream, unsigned size, unsigned version = 0); \ + virtual unsigned Write(unsigned char * stream, unsigned size, unsigned version = 0) const; \ + virtual int Print(char * stream, unsigned size, unsigned maxDepth = INT_MAX) const; + +#ifdef PRINTABLE_MESSAGES +# define PrintEnum(EnumName) \ + long lData = data; \ + return snprintf(stream, size, "('%s')%d", #EnumName, lData); +#else +# define PrintEnum(EnumName) \ + return 0; +#endif + +#define SCRIBE_ENUM(EnumName) \ + inline unsigned Write( unsigned char * stream, unsigned size, const EnumName & data, unsigned version = 0) \ + { \ + unsigned dataSize = sizeof(EnumName); \ + if (size < dataSize) \ + return 0; \ + memcpy(stream, (void*)&data, dataSize); \ + return dataSize; \ + } \ + inline unsigned Read( const unsigned char * stream, unsigned size, EnumName & data, unsigned, unsigned version = 0) \ + { \ + unsigned dataSize = sizeof(EnumName); \ + if (size < dataSize) \ + return 0; \ + memcpy((void*)&data, stream, dataSize); \ + return dataSize; \ + } \ + inline int Print( char * stream, unsigned size, const EnumName & data, unsigned maxDepth = 0) \ + { \ + PrintEnum(EnumName) \ + } + +#ifdef WIN32 +# define mapped_type referent_type +#endif + +#define DefineMapScribe(MapName) \ + unsigned Read(const unsigned char * stream, unsigned size, MapName & data, unsigned maxLen, unsigned version = 0); + +#define ImplementMapScribeMember(MapName, KeyName) \ + unsigned Read(const unsigned char * stream, unsigned size, MapName & data, unsigned maxLen, unsigned version) \ + { \ + return Read(stream, size, data, MapName::mapped_type::KeyName, maxLen, version); \ + } + +#define ImplementMapScribeAccessor(MapName, KeyName) \ + unsigned Read(const unsigned char * stream, unsigned size, MapName & data, unsigned maxLen, unsigned version) \ + { \ + return Read(stream, size, data, &MapName::mapped_type::KeyName, maxLen, version); \ + } + +#ifdef API_NAMESPACE + // forward/extern declarations + namespace soe { class ClassScribeBase; } + namespace API_NAMESPACE { void AddClassScribeToGlobalSet(soe::ClassScribeBase * pScribe); } + // macro to add scribe to global set for APIs +# define AddScribeToResetSet API_NAMESPACE::AddClassScribeToGlobalSet(&classScribe); +#else +# define AddScribeToResetSet // NOP - nothing to do in this case +#endif + +#define ImplementClassScribeBeginInternal(ClassName) \ + class ClassName::Scribe : public soe::ClassScribe \ + { \ + public: \ + Scribe(); \ + void Initialize(); \ + }; \ + ClassName::Scribe & ClassName::GetScribe() \ + { \ + static ClassName::Scribe classScribe; \ + if (!classScribe.IsInitialized()) { \ + classScribe.ClearMembers(); \ + classScribe.Initialize(); \ + } \ + return classScribe; \ + } \ + unsigned ClassName::Read(const unsigned char * stream, unsigned size, unsigned version) \ + { \ + return GetScribe().Read(stream, size, *this, version); \ + } \ + unsigned ClassName::Write(unsigned char * stream, unsigned size, unsigned version) const \ + { \ + return GetScribe().Write(stream, size, *this, version); \ + } \ + int ClassName::Print(char * stream, unsigned size, unsigned maxDepth) const \ + { \ + return GetScribe().Print(stream, size, *this, maxDepth); \ + } \ + ClassName::Scribe::Scribe() \ + : soe::ClassScribe(#ClassName) \ + { \ + Initialize(); \ + } \ + void ClassName::Scribe::Initialize() \ + { \ + typedef ClassName ScribedClass_t; \ + Scribe & classScribe = *this; \ + ClearMembers(); + +#define ImplementClassScribeBegin(ClassName) \ + soe::ClassScribe & GetScribe(const ClassName & object); \ + unsigned ClassName::Read(const unsigned char * stream, unsigned size, unsigned version) \ + { \ + return GetScribe(*this).Read(stream, size, *this, version); \ + } \ + unsigned ClassName::Write(unsigned char * stream, unsigned size, unsigned version) const \ + { \ + return GetScribe(*this).Write(stream, size, *this, version); \ + } \ + int ClassName::Print(char * stream, unsigned size, unsigned maxDepth) const \ + { \ + return GetScribe(*this).Print(stream, size, *this, maxDepth); \ + } \ + soe::ClassScribe & GetScribe(const ClassName & object) \ + { \ + typedef ClassName ScribedClass_t; \ + static soe::ClassScribe classScribe(#ClassName); \ + if (!classScribe.IsInitialized()) { \ + classScribe.ClearMembers(); + +#define ImplementClassScribeBeginEx(ClassName, ParentClassName) \ + soe::ClassScribe & GetScribe(const ClassName & object); \ + unsigned ClassName::Read(const unsigned char * stream, unsigned size, unsigned version) \ + { \ + unsigned bytesTotal = 0; \ + unsigned bytes = 0; \ + bytesTotal += (bytes = GetScribe((const ParentClassName &)*this).Read(stream, size, *this, version)); \ + if (!bytes && (GetScribe((const ParentClassName &)*this).CountMembers() > 0)) { return 0; } \ + bytesTotal += (bytes = GetScribe(*this).Read(stream+bytesTotal, size-bytesTotal , *this, version)); \ + if (!bytes && (GetScribe(*this).CountMembers() > 0)) { return 0; } \ + return bytesTotal; \ + } \ + unsigned ClassName::Write(unsigned char * stream, unsigned size, unsigned version) const \ + { \ + unsigned bytesTotal = 0; \ + unsigned bytes = 0; \ + bytesTotal += (bytes = GetScribe((const ParentClassName &)*this).Write(stream, size, *this, version)); \ + if (!bytes && (GetScribe((const ParentClassName &)*this).CountMembers() > 0)) { return 0; } \ + bytesTotal += (bytes = GetScribe(*this).Write(stream+bytesTotal, size-bytesTotal , *this, version)); \ + if (!bytes && (GetScribe(*this).CountMembers() > 0)) { return 0; } \ + return bytesTotal; \ + } \ + int ClassName::Print(char * stream, unsigned size, unsigned maxDepth) const \ + { \ + int bytesTotal = 0; \ + int bytes = 0; \ + bytesTotal += (bytes = GetScribe((const ParentClassName &)*this).Print(stream, size, *this, maxDepth)); \ + if (soe::FailedToPrint(bytes, size)) { return bytes; } \ + bytesTotal += (bytes = GetScribe(*this).Print(stream+bytesTotal , size-bytesTotal, *this, maxDepth)); \ + if (soe::FailedToPrint(bytes, size-bytesTotal)) { return bytes; } \ + return bytesTotal; \ + } \ + soe::ClassScribe & GetScribe(const ClassName & object) \ + { \ + typedef ClassName ScribedClass_t; \ + static soe::ClassScribe classScribe(#ClassName); \ + if (!classScribe.IsInitialized()) { \ + classScribe.ClearMembers(); + +#define ImplementClassScribeMember(MemberName) \ + classScribe.AddMember(#MemberName, &ScribedClass_t::MemberName, 1, 0, eNoEffect); + +#define ImplementClassScribeMemberEx(MemberName,MaxLen) \ + classScribe.AddMember(#MemberName, &ScribedClass_t::MemberName, MaxLen, 0, eNoEffect); + +#define ImplementClassScribeMemberContainerEx(MemberName,MaxLen,MaxElementLen) \ + classScribe.AddMemberContainer(#MemberName, &ScribedClass_t::MemberName, MaxLen, MaxElementLen, 0, eNoEffect); + +#define ImplementVersionAddedClassScribeMember(MemberName,Version) \ + classScribe.AddMember(#MemberName, &ScribedClass_t::MemberName, 1, Version, eAdded); + +#define ImplementVersionAddedClassScribeMemberEx(MemberName,MaxLen,Version) \ + classScribe.AddMember(#MemberName, &ScribedClass_t::MemberName, MaxLen, Version, eAdded); + +#define ImplementVersionAddedClassScribeMemberContainerEx(MemberName,MaxLen,MaxElementLen,Version) \ + classScribe.AddMemberContainer(#MemberName, &ScribedClass_t::MemberName, MaxLen, MaxElementLen, Version, eAdded); + +#define ImplementVersionDroppedClassScribeMember(MemberName,Version) \ + classScribe.AddMember(#MemberName, &ScribedClass_t::MemberName, 1, Version, eDropped); + +#define ImplementVersionDroppedClassScribeMemberEx(MemberName,MaxLen,Version) \ + classScribe.AddMember(#MemberName, &ScribedClass_t::MemberName, MaxLen, Version, eDropped); + +#define ImplementVersionDroppedClassScribeMemberContainerEx(MemberName,MaxLen,MaxElementLen,Version) \ + classScribe.AddMemberContainer(#MemberName, &ScribedClass_t::MemberName, MaxLen, MaxElementLen, Version, eDropped); + +#define ImplementClassScribeEndInternal \ + SetInitialized(true); \ + AddScribeToResetSet \ + } + +#define ImplementClassScribeEnd \ + classScribe.SetInitialized(true); \ + AddScribeToResetSet \ + } \ + return classScribe; \ + } + +#include "types.h" + +#if (defined(WIN32) || defined(linux)) && !defined(NETWORK_BIG_ENDIAN) + #define BYTE1 0 + #define BYTE2 1 + #define BYTE3 2 + #define BYTE4 3 + #define BYTE5 4 + #define BYTE6 5 + #define BYTE7 6 + #define BYTE8 7 +#elif defined(sparc) || defined(NETWORK_BIG_ENDIAN) + #define BYTE1 7 + #define BYTE2 6 + #define BYTE3 5 + #define BYTE4 4 + #define BYTE5 3 + #define BYTE6 2 + #define BYTE7 1 + #define BYTE8 0 +#endif + + +//////////////////////////////////////////////////////////////////////////////// + + +namespace soe +{ + + + //////////////////////////////////////////////////////////////////////////////// + + + // 388 ns (50 bytes) + inline unsigned Write(unsigned char * stream, unsigned size, const unsigned char * data, unsigned dataLen, unsigned version = 0) + { + if (size < dataLen) + return 0; + memcpy(stream, data, dataLen); + return dataLen; + } + + // 353 ns + inline unsigned Write(unsigned char * stream, unsigned size, bool data, unsigned version = 0) + { + if (size < sizeof(uint8)) + return 0; + stream[BYTE1] = data; + return sizeof(uint8); + } + + // 362 ns + inline unsigned Write(unsigned char * stream, unsigned size, int8 data, unsigned version = 0) + { + if (size < sizeof(int8)) + return 0; + stream[BYTE1] = data; + return sizeof(int8); + } + + // 362 ns + inline unsigned Write(unsigned char * stream, unsigned size, uint8 data, unsigned version = 0) + { + if (size < sizeof(uint8)) + return 0; + stream[BYTE1] = data; + return sizeof(uint8); + } + + // 355 ns + inline unsigned Write(unsigned char * stream, unsigned size, int16 data, unsigned version = 0) + { + if (size < sizeof(int16)) + return 0; + stream[BYTE1] = data&0xff; + stream[BYTE2] = (data>>8)&0xff; + return sizeof(int16); + } + + // 360 ns + inline unsigned Write(unsigned char * stream, unsigned size, uint16 data, unsigned version = 0) + { + if (size < sizeof(uint16)) + return 0; + stream[BYTE1] = data&0xff; + stream[BYTE2] = (data>>8)&0xff; + return sizeof(uint16); + } + + // 360 ns + inline unsigned Write(unsigned char * stream, unsigned size, int32 data, unsigned version = 0) + { + if (size < sizeof(int32)) + return 0; + stream[BYTE1] = data&0xff; + stream[BYTE2] = (data>>8)&0xff; + stream[BYTE3] = (data>>16)&0xff; + stream[BYTE4] = (data>>24)&0xff; + return sizeof(int32); + } + + // 360 ns + inline unsigned Write(unsigned char * stream, unsigned size, uint32 data, unsigned version = 0) + { + if (size < sizeof(uint32)) + return 0; + stream[BYTE1] = data&0xff; + stream[BYTE2] = (data>>8)&0xff; + stream[BYTE3] = (data>>16)&0xff; + stream[BYTE4] = (data>>24)&0xff; + return sizeof(uint32); + } + + // 370 ns + inline unsigned Write(unsigned char * stream, unsigned size, int64 data, unsigned version = 0) + { + if (size < sizeof(int64)) + return 0; +#if (defined(WIN32) || defined(linux)) + uint32 low = *(uint32 *)(&data); + uint32 high = *((uint32 *)&data+1); +#elif defined(sparc) + uint32 low = *((uint32 *)&data+1); + uint32 high = *(uint32 *)(&data); +#endif + stream[BYTE1] = (unsigned char)low&0xff; + stream[BYTE2] = (unsigned char)(low>>8)&0xff; + stream[BYTE3] = (unsigned char)(low>>16)&0xff; + stream[BYTE4] = (unsigned char)(low>>24)&0xff; + stream[BYTE5] = (unsigned char)high&0xff; + stream[BYTE6] = (unsigned char)(high>>8)&0xff; + stream[BYTE7] = (unsigned char)(high>>16)&0xff; + stream[BYTE8] = (unsigned char)(high>>24)&0xff; + return sizeof(int64); + } + + // 370 ns + inline unsigned Write(unsigned char * stream, unsigned size, uint64 data, unsigned version = 0) + { + if (size < sizeof(uint64)) + return 0; +#if (defined(WIN32) || defined(linux)) + uint32 low = *(uint32 *)(&data); + uint32 high = *((uint32 *)&data+1); +#elif defined(sparc) + uint32 low = *((uint32 *)&data+1); + uint32 high = *(uint32 *)(&data); +#endif + stream[BYTE1] = (unsigned char)low&0xff; + stream[BYTE2] = (unsigned char)(low>>8)&0xff; + stream[BYTE3] = (unsigned char)(low>>16)&0xff; + stream[BYTE4] = (unsigned char)(low>>24)&0xff; + stream[BYTE5] = (unsigned char)high&0xff; + stream[BYTE6] = (unsigned char)(high>>8)&0xff; + stream[BYTE7] = (unsigned char)(high>>16)&0xff; + stream[BYTE8] = (unsigned char)(high>>24)&0xff; + return sizeof(uint64); + } + + // 360 ns + inline unsigned Write(unsigned char * stream, unsigned size, float data, unsigned version = 0) + { + uint32 & dataRef = *(uint32 *)(&data); + if (size < sizeof(float)) + return 0; + stream[BYTE1] = dataRef&0xff; + stream[BYTE2] = (dataRef>>8)&0xff; + stream[BYTE3] = (dataRef>>16)&0xff; + stream[BYTE4] = (dataRef>>24)&0xff; + return sizeof(float); + } + + // 370 ns + inline unsigned Write(unsigned char * stream, unsigned size, double data, unsigned version = 0) + { + if (size < sizeof(double)) + return 0; +#if (defined(WIN32) || defined(linux)) + uint32 low = *(uint32 *)(&data); + uint32 high = *((uint32 *)&data+1); +#elif defined(sparc) + uint32 low = *((uint32 *)&data+1); + uint32 high = *(uint32 *)(&data); +#endif + stream[BYTE1] = (unsigned char)low&0xff; + stream[BYTE2] = (unsigned char)(low>>8)&0xff; + stream[BYTE3] = (unsigned char)(low>>16)&0xff; + stream[BYTE4] = (unsigned char)(low>>24)&0xff; + stream[BYTE5] = (unsigned char)high&0xff; + stream[BYTE6] = (unsigned char)(high>>8)&0xff; + stream[BYTE7] = (unsigned char)(high>>16)&0xff; + stream[BYTE8] = (unsigned char)(high>>24)&0xff; + return sizeof(double); + } + + // 361 ns + inline unsigned WriteEncoded(unsigned char * stream, unsigned size, uint32 data) + { + unsigned encoded = ((data & 0xffffffc0) << 2) | (data & 0x3f); + if (data > 0x3fffffff) + { + return 0; + } + else if (data > 0x3fffff && size >= 4) + { + encoded |= 0xc0; + stream[BYTE1] = encoded & 0xff; + stream[BYTE2] = (encoded>>8) & 0xff; + stream[BYTE3] = (encoded>>16) & 0xff; + stream[BYTE4] = (encoded>>24) & 0xff; + return 4; + } + else if (data > 0x3fff) + { + encoded |= 0x80; + stream[BYTE1] = encoded & 0xff; + stream[BYTE2] = (encoded>>8) & 0xff; + stream[BYTE3] = (encoded>>16) & 0xff; + return 3; + } + else if (data > 0x3f) + { + encoded |= 0x40; + stream[BYTE1] = encoded & 0xff; + stream[BYTE2] = (encoded>>8) & 0xff; + return 2; + } + else if (size >= 1) + { + stream[BYTE1] = encoded & 0xff; + return 1; + } + return 0; + } + + // 630 ns + inline unsigned Write(unsigned char * stream, unsigned size, const std::string & data, unsigned version = 0) + { + unsigned lengthBytes = 0; + unsigned bytes = 0; + lengthBytes += WriteEncoded(stream, size, (soe::uint32)data.length()); + if (!lengthBytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write length (%u bytes)", __FUNCTION__, __FILE__, __LINE__, sizeof(soe::uint32))); +#endif + return 0; + } + if (data.length()) + { + bytes = Write(stream+lengthBytes, size-lengthBytes, reinterpret_cast(data.data()), (soe::uint32)data.length()); + if (!bytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write %u string bytes.", __FUNCTION__, __FILE__, __LINE__, data.length())); +#endif + return 0; + } + } + return lengthBytes + bytes; + } + + inline unsigned Read(const unsigned char * stream, unsigned size, unsigned char * data, unsigned dataLen, unsigned version = 0) + { + if (size < dataLen) + return 0; + memcpy(data, stream, dataLen); + return dataLen; + } + + // 355 ns + inline unsigned Read(const unsigned char * stream, unsigned size, bool & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(uint8)) + return 0; + data = stream[BYTE1] != 0; + return sizeof(uint8); + } + + // 355 ns + inline unsigned Read(const unsigned char * stream, unsigned size, int8 & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(int8)) + return 0; + data = stream[BYTE1]; + return sizeof(int8); + } + + // 355 ns + inline unsigned Read(const unsigned char * stream, unsigned size, uint8 & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(uint8)) + return 0; + data = stream[BYTE1]; + return sizeof(uint8); + } + + // 355 ns + inline unsigned Read(const unsigned char * stream, unsigned size, int16 & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(int16)) + return 0; + data = stream[BYTE1]; + data |= stream[BYTE2]<<8; + return sizeof(int16); + } + + // 355 ns + inline unsigned Read(const unsigned char * stream, unsigned size, uint16 & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(uint16)) + return 0; + data = stream[BYTE1]; + data |= stream[BYTE2]<<8; + return sizeof(uint16); + } + + // 360 ns + inline unsigned Read(const unsigned char * stream, unsigned size, int32 & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(int32)) + return 0; + data = stream[BYTE1]; + data |= stream[BYTE2]<<8; + data |= stream[BYTE3]<<16; + data |= stream[BYTE4]<<24; + return sizeof(int32); + } + + // 360 ns + inline unsigned Read(const unsigned char * stream, unsigned size, uint32 & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(uint32)) + return 0; + data = stream[BYTE1]; + data |= stream[BYTE2]<<8; + data |= stream[BYTE3]<<16; + data |= stream[BYTE4]<<24; + return sizeof(uint32); + } + + // 380 ns + inline unsigned Read(const unsigned char * stream, unsigned size, int64 & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(int64)) + return 0; +#if (defined(WIN32) || defined(linux)) + uint32 & low = *(uint32 *)(&data); + uint32 & high = *((uint32 *)&data+1); +#elif defined(sparc) + uint32 & low = *(uint32 *)(&data+1); + uint32 & high = *(uint32 *)(&data); +#endif + low = stream[BYTE1]; + low |= stream[BYTE2]<<8; + low |= stream[BYTE3]<<16; + low |= stream[BYTE4]<<24; + high = stream[BYTE5]; + high |= stream[BYTE6]<<8; + high |= stream[BYTE7]<<16; + high |= stream[BYTE8]<<24; + return sizeof(int64); + } + + // 380 ns + inline unsigned Read(const unsigned char * stream, unsigned size, uint64 & data, unsigned = 1/*unused*/, unsigned version = 0) + { + if (size < sizeof(uint64)) + return 0; +#if (defined(WIN32) || defined(linux)) + uint32 & low = *(uint32 *)(&data); + uint32 & high = *((uint32 *)&data+1); +#elif defined(sparc) + uint32 & low = *(uint32 *)(&data+1); + uint32 & high = *(uint32 *)(&data); +#endif + low = stream[BYTE1]; + low |= stream[BYTE2]<<8; + low |= stream[BYTE3]<<16; + low |= stream[BYTE4]<<24; + high = stream[BYTE5]; + high |= stream[BYTE6]<<8; + high |= stream[BYTE7]<<16; + high |= stream[BYTE8]<<24; + return sizeof(uint64); + } + + // 370 ns + inline unsigned Read(const unsigned char * stream, unsigned size, float & data, unsigned = 1/*unused*/, unsigned version = 0) + { + assert(sizeof(float) == 4); + uint32 & dataRef = *(uint32 *)(&data); + if (size < sizeof(float)) + return 0; + dataRef = stream[BYTE1]; + dataRef |= stream[BYTE2]<<8; + dataRef |= stream[BYTE3]<<16; + dataRef |= stream[BYTE4]<<24; + return sizeof(float); + } + + // 380 ns + inline unsigned Read(const unsigned char * stream, unsigned size, double & data, unsigned = 1/*unused*/, unsigned version = 0) + { + assert(sizeof(double) == 8); + if (size < sizeof(double)) + return 0; +#if (defined(WIN32) || defined(linux)) + uint32 & low = *(uint32 *)(&data); + uint32 & high = *((uint32 *)&data+1); +#elif defined(sparc) + uint32 & low = *(uint32 *)(&data+1); + uint32 & high = *(uint32 *)(&data); +#endif + low = stream[BYTE1]; + low |= stream[BYTE2]<<8; + low |= stream[BYTE3]<<16; + low |= stream[BYTE4]<<24; + high = stream[BYTE5]; + high |= stream[BYTE6]<<8; + high |= stream[BYTE7]<<16; + high |= stream[BYTE8]<<24; + return sizeof(double); + } + + // 360 ns + inline unsigned ReadEncoded(const unsigned char * stream, unsigned size, uint32 & data) + { + if (size < 1) + return 0; + + unsigned encoded = stream[BYTE1]; + unsigned mask = encoded & 0xc0; + if (mask == 0) + { + data = encoded & 0x3f; + return 1; + } + else if (mask == 0x40 && size >=2) + { + data = (stream[BYTE2]<<6) | (encoded & 0x3f); + return 2; + } + else if (mask == 0x80 && size >=3) + { + data = (stream[BYTE3]<<14) | (stream[BYTE2]<<6) | (encoded & 0x3f); + return 3; + } + else if (mask == 0xc0 && size >=4) + { + data = (stream[BYTE4]<<22) | (stream[BYTE3]<<14) | (stream[BYTE2]<<6) | (encoded & 0x3f); + return 4; + } + return 0; + } + + inline unsigned Read(const unsigned char * stream, unsigned size, std::string & data, unsigned maxLen, unsigned version = 0) + { + unsigned bytes = 0; + unsigned length = 0; + bytes += ReadEncoded(stream, size, length); + if (!bytes || size < bytes+length || length > maxLen) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to read string, length = %u, maxLen = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, length, maxLen, size)); +#endif + return 0; + } + data.assign((const char *)stream+bytes, (const char *)stream+bytes+length); // assign would be dangerous if unicode, single-byte characters are ok + return bytes + length; + } + +#ifdef _USE_32BIT_TIME_T + // Seems that VC8 has switched time_t to __int64 so this becomes a signature clash + inline unsigned Read(const unsigned char * stream, unsigned size, time_t & data, unsigned maxLen = 1, unsigned version = 0) + { + soe::uint32 wordData = 0; + unsigned bytesRead = Read(stream, size, wordData, maxLen); + + data = (time_t)wordData; + + return bytesRead; + } + + inline unsigned Write(unsigned char * stream, unsigned size, time_t data, unsigned version = 0) + { + return Write(stream, size, (soe::uint32)data); + } +#endif + + inline bool FailedToPrint(int bytesPrinted, unsigned maxBytes) + { + return ( (bytesPrinted < 0) || (bytesPrinted > (int)maxBytes) ); + } + +#ifdef PRINTABLE_MESSAGES + inline int Print(char * stream, unsigned size, time_t data, unsigned maxDepth=INT_MAX) + { + tm *timeStruct = localtime(&data); + + if (timeStruct) { + size_t bytes = strftime(stream, size, "%Y-%m-%d %H:%M:%S", timeStruct); + + if (bytes > 0) { + return (int)bytes; + } else { + return -1; + } + } else { + return snprintf(stream, size, "(time_t)%u)", (unsigned)data); + } + } + + inline int Print(char * stream, unsigned size, const unsigned char * data, unsigned dataLen, unsigned maxDepth=INT_MAX) + { + int bytes = 0; + int moreBytes = snprintf(stream, size, "[[%u]]", dataLen); + + if ((maxDepth == 0) || (FailedToPrint(moreBytes, size))) { + return moreBytes; + } else { + bytes += moreBytes; + } + + moreBytes = snprintf(stream+bytes, size-bytes, "0x"); + if (FailedToPrint(moreBytes, size-bytes)) { + return moreBytes; + } else { + bytes += moreBytes; + } + + for (unsigned index = 0; index < dataLen; index++) + { + moreBytes = snprintf(stream+bytes, size-bytes, "%x", data[index]); + if (FailedToPrint(moreBytes, size-bytes)) { break; } + bytes += moreBytes; + } + + return FailedToPrint(moreBytes, size-bytes) ? moreBytes : bytes; + } + + inline int Print(char * stream, unsigned size, bool data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%s", data ? "TRUE" : "FALSE"); + } + + inline int Print(char * stream, unsigned size, int8 data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%d", data); + } + + inline int Print(char * stream, unsigned size, uint8 data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%u", data); + } + + inline int Print(char * stream, unsigned size, int16 data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%d", data); + } + + inline int Print(char * stream, unsigned size, uint16 data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%u", data); + } + + inline int Print(char * stream, unsigned size, int32 data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%d", data); + } + + inline int Print(char * stream, unsigned size, uint32 data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%u", data); + } + + inline int Print(char * stream, unsigned size, int64 data, unsigned maxDepth=INT_MAX) + { +# ifdef WIN32 + return snprintf(stream, size, "%I64d", data); +# else + return snprintf(stream, size, "%lld", data); +# endif + } + + inline int Print(char * stream, unsigned size, uint64 data, unsigned maxDepth=INT_MAX) + { +# ifdef WIN32 + return snprintf(stream, size, "%I64u", data); +# else + return snprintf(stream, size, "%llu", data); +# endif + } + + inline int Print(char * stream, unsigned size, float data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%f", data); + } + + inline int Print(char * stream, unsigned size, double data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%f", data); + } + + inline int Print(char * stream, unsigned size, const std::string & data, unsigned maxDepth=INT_MAX) + { + return snprintf(stream, size, "%s", data.c_str()); + } +#endif + + //////////////////////////////////////////////////////////////////////////////// + + class DECLSPEC AutoVariableBase + { + public: + AutoVariableBase() {} + virtual ~AutoVariableBase() {} + + virtual void Copy(AutoVariableBase *pSource) = 0; + + virtual unsigned Write(unsigned char * stream, unsigned size, unsigned version = 0) const = 0; + virtual unsigned Read(const unsigned char * stream, unsigned size, unsigned maxLen = 1, unsigned version = 0) = 0; +#ifdef PRINTABLE_MESSAGES + virtual int Print(char * stream, unsigned size, unsigned maxDepth=INT_MAX) const = 0; +#endif +#if defined(PRINTABLE_MESSAGES) || defined(TRACK_READ_WRITE_FAILURES) + virtual const char * VariableName() const { return "(Unknown)"; } +#endif + }; + + //////////////////////////////////////////////////////////////////////////////// + + enum EVersionEffect + { + eNoEffect, + eDropped, + eAdded, + }; + + inline bool IsMemberIncluded(unsigned messageVersion, unsigned memberVersion, EVersionEffect memberVersionEffect) + { + bool isIncluded = true; + + if (messageVersion) { + switch(memberVersionEffect) + { + case eAdded: + isIncluded = (messageVersion >= memberVersion); + break; + case eDropped: + isIncluded = (messageVersion < memberVersion); + break; + case eNoEffect: + default: + break; + } + } + + return isIncluded; + } + + //////////////////////////////////////////////////////////////////////////////// +} + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeStringVector.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeStringVector.cpp new file mode 100644 index 00000000..bf2f5f46 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeStringVector.cpp @@ -0,0 +1,14 @@ +#include "serializeClasses.h" +#include "serializeStringVector.h" + +namespace soe +{ +/* This file is here for backwards compatability. +*/ + +} + + + + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeStringVector.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeStringVector.h new file mode 100644 index 00000000..52422e3b --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeStringVector.h @@ -0,0 +1,245 @@ +#ifndef SERIALIZESTRINGVECTOR_H +#define SERIALIZESTRINGVECTOR_H + +#ifdef SOE__SERIALIZE_TEMPLATES_H +#error wrong odering, serialzeTemplates.h must be included after this file. +#endif + +#include +#include +#include +#include "serializeClasses.h" + +#define DEFAULT_MAX_LENGTH_STRING 1000 +#define DEFAULT_MAX_NUMBER_ELEMENTS 1000 + + +namespace soe +{ + /////////////////////////////////////////////////////////////////////////////////////////// + // SerializeStringVector + // This class is just a std::vector with extra serialization routines. + // + // This class is designed to handle the serialization of a vector of strings. This was necessary + // because the default serialization routines did not handle the length of the string properly, and + // there was more risk involved in modifying all the serialization routines to handle the + // extra length property for the string as well as the vector. + // + // this is intenede to be used with the serialization macros + // Ex: + // DefineMessageMember(myStringVector, SerializeStringVector) + // ImplementMessageMember(paymentInstrument, SerializeStringVector(DEFAULT_MAX_LENGTH_STRING, DEFAULT_MAX_NUMBER_ELEMENTS)) + /////////////////////////////////////////////////////////////////////////////////////////// + + typedef std::list stringList_t; + typedef std::vector stringVector_t; + + class SerializeStringList : public stringList_t + { + private: + unsigned mMaxStringLength; + unsigned mMaxListLength; + protected: + public: + SerializeStringList() + : mMaxStringLength(DEFAULT_MAX_LENGTH_STRING), mMaxListLength(DEFAULT_MAX_NUMBER_ELEMENTS) + {}; + // writing will use the default values and assume that the vector is valid + //SerializeStringList(stringList_t vStrings); + SerializeStringList(unsigned maxStringLength, unsigned maxListLength) + : mMaxStringLength(maxStringLength), mMaxListLength(maxListLength) + { + } + SerializeStringList::SerializeStringList(stringList_t vStrings) + : mMaxStringLength(DEFAULT_MAX_LENGTH_STRING), mMaxListLength(DEFAULT_MAX_NUMBER_ELEMENTS) + { + for(stringList_t::iterator i = vStrings.begin(); i != vStrings.end(); i++) + { + push_back(*i); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////// + unsigned SerializeStringList::Read(const unsigned char * stream, unsigned size) + { + // fix this: + std::string element; + unsigned bytes = 0; + unsigned elementBytes = 0; + unsigned length = 0; + clear(); + bytes += ReadEncoded(stream, size, length); + if (!bytes || length > mMaxListLength) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to read StringList, length = %u, mMaxListLength = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, length, mMaxListLength, size)); +#endif + return 0; + } + for (unsigned i=0; isize()); + if (!bytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write StringList length (%u bytes)", __FUNCTION__, __FILE__, __LINE__, sizeof(soe::uint32))); +#endif + return 0; + } + for (stringList_t::const_iterator iter = begin(); iter != end(); iter++, index++) + { + elementBytes = soe::Write(stream+bytes, size-bytes, *iter); + if (!elementBytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write StringList element, index = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, index, size)); +#endif + return 0; + } + bytes += elementBytes; + } + + return bytes; + } + + //operator stringList_t () { return *this; } + + //unsigned Read(const unsigned char * stream, unsigned size); + //DECLARE_SCRIBE_MEMBERS + }; + + /////////////////////////////////////////////////////////////////////////////////////////// + inline unsigned Write(unsigned char * stream, unsigned size, const SerializeStringList & data, unsigned version = 0) + { + return data.Write(stream, size); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + inline unsigned Read(const unsigned char * stream, unsigned size, SerializeStringList & data, unsigned, unsigned version = 0) + { + return data.Read(stream, size); + } + + + class SerializeStringVector : public stringVector_t + { + private: + unsigned mMaxStringLength; + unsigned mMaxVectorLength; + protected: + public: + SerializeStringVector() + : mMaxStringLength(DEFAULT_MAX_LENGTH_STRING), mMaxVectorLength(DEFAULT_MAX_NUMBER_ELEMENTS) + {}; + // writing will use the default values and assume that the vector is valid + //SerializeStringVector(stringVector_t vStrings); + SerializeStringVector(unsigned maxStringLength, unsigned maxVectorLength) + : mMaxStringLength(maxStringLength), mMaxVectorLength(maxVectorLength) + { + } + SerializeStringVector::SerializeStringVector(stringVector_t vStrings) + : mMaxStringLength(DEFAULT_MAX_LENGTH_STRING), mMaxVectorLength(DEFAULT_MAX_NUMBER_ELEMENTS) + { + for(stringVector_t::iterator i = vStrings.begin(); i != vStrings.end(); i++) + { + push_back(*i); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////// + unsigned SerializeStringVector::Read(const unsigned char * stream, unsigned size) + { + // fix this: + std::string element; + unsigned bytes = 0; + unsigned elementBytes = 0; + unsigned length = 0; + clear(); + bytes += ReadEncoded(stream, size, length); + if (!bytes || length > mMaxVectorLength) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to read StringVector, length = %u, mMaxListLength = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, length, mMaxVectorLength, size)); +#endif + return 0; + } + for (unsigned i=0; isize()); + if (!bytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write StringVector length (%u bytes)", __FUNCTION__, __FILE__, __LINE__, sizeof(soe::uint32))); +#endif + return 0; + } + for (stringVector_t::const_iterator iter = begin(); iter != end(); iter++, index++) + { + elementBytes = soe::Write(stream+bytes, size-bytes, *iter); + if (!elementBytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write StringVector element, index = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, index, size)); +#endif + return 0; + } + bytes += elementBytes; + } + + return bytes; + } + + operator stringVector_t () { return *this; } + + //unsigned Read(const unsigned char * stream, unsigned size); + //DECLARE_SCRIBE_MEMBERS + }; + + /////////////////////////////////////////////////////////////////////////////////////////// + inline unsigned Write(unsigned char * stream, unsigned size, const SerializeStringVector & data, unsigned version = 0) + { + return data.Write(stream, size); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + inline unsigned Read(const unsigned char * stream, unsigned size, SerializeStringVector & data, unsigned, unsigned version = 0) + { + return data.Read(stream, size); + } + +} // namespace soe + +#endif //SERIALIZESTRINGVECTOR_H + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeTemplates.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeTemplates.h new file mode 100644 index 00000000..5ee58233 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/serializeTemplates.h @@ -0,0 +1,784 @@ +#ifndef SOE__SERIALIZE_TEMPLATES_H +#define SOE__SERIALIZE_TEMPLATES_H + +#ifdef TRACK_READ_WRITE_FAILURES +# include "trackMessageFailures.h" +#endif +#include + +namespace soe +{ + //////////////////////////////////////////////////////////////////////////////// + + template + inline unsigned Write(unsigned char * stream, unsigned size, const std::map & data, unsigned version = 0) + { + unsigned bytes = 0; + unsigned elementBytes = 0; + bytes += WriteEncoded(stream, size, (soe::uint32)data.size()); + if (!bytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write vector length (%u bytes)", __FUNCTION__, __FILE__, __LINE__, sizeof(soe::uint32))); +#endif + return 0; + } + size_t index = 0; + for (typename std::map::const_iterator iter = data.begin(); iter != data.end(); iter++, index++) + { + elementBytes = Write(stream+bytes, size-bytes, iter->second, version); + if (!elementBytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write map element, index = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, index, size)); +#endif + return 0; + } + bytes += elementBytes; + } + + return bytes; + } + + template + inline unsigned Read(const unsigned char * stream, unsigned size, std::map & data, K T::*dataKey, unsigned maxLen, unsigned version = 0) + { + unsigned bytes = 0; + unsigned elementBytes = 0; + unsigned length = 0; + data.clear(); + bytes += ReadEncoded(stream, size, length); + if (!bytes || length > maxLen) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to read vector, length = %u, maxLen = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, length, maxLen, size)); +#endif + return 0; + } + for (unsigned i=0; i + inline unsigned Read(const unsigned char * stream, unsigned size, std::map & data, const K & (T::*dataKeyAccessor)() const, unsigned maxLen, unsigned version = 0) + { + unsigned bytes = 0; + unsigned elementBytes = 0; + unsigned length = 0; + data.clear(); + bytes += ReadEncoded(stream, size, length); + if (!bytes || length > maxLen) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to read vector, length = %u, maxLen = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, length, maxLen, size)); +#endif + return 0; + } + for (unsigned i=0; i + inline unsigned Read(const unsigned char * stream, unsigned size, std::map & data, K (T::*dataKeyAccessor)() const, unsigned maxLen, unsigned version = 0) + { + unsigned bytes = 0; + unsigned elementBytes = 0; + unsigned length = 0; + data.clear(); + bytes += ReadEncoded(stream, size, length); + if (!bytes || length > maxLen) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to read vector, length = %u, maxLen = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, length, maxLen, size)); +#endif + return 0; + } + for (unsigned i=0; i + inline unsigned Write(unsigned char * stream, unsigned size, const std::list & data, unsigned version = 0) + { + unsigned bytes = 0; + unsigned elementBytes = 0; + bytes += WriteEncoded(stream, size, (soe::uint32)data.size()); + if (!bytes) + return 0; + for (typename std::list::const_iterator iter = data.begin(); iter != data.end(); iter++) + { + elementBytes = Write(stream+bytes, size-bytes, *iter, version); + if (!elementBytes) + return 0; + bytes += elementBytes; + } + + return bytes; + } + + template + inline unsigned ReadContainer(const unsigned char * stream, unsigned size, std::list & data, unsigned maxLen, unsigned maxElementLen, unsigned version = 0) + { + unsigned bytes = 0; + unsigned elementBytes = 0; + unsigned length = 0; + data.clear(); + bytes += ReadEncoded(stream, size, length); + if (!bytes || length > maxLen) + return 0; + for (unsigned i=0; i + inline unsigned Read(const unsigned char * stream, unsigned size, std::list & data, unsigned maxLen, unsigned version = 0) + { + return ReadContainer(stream, size, data, maxLen, 1, version); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + inline unsigned Write(unsigned char * stream, unsigned size, const std::vector & data, unsigned version = 0) + { + unsigned bytes = 0; + unsigned elementBytes = 0; + bytes += WriteEncoded(stream, size, (soe::uint32)data.size()); + if (!bytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write vector length (%u bytes)", __FUNCTION__, __FILE__, __LINE__, sizeof(soe::uint32))); +#endif + return 0; + } + for (size_t index = 0; index < data.size(); index++) + { + elementBytes = Write(stream+bytes, size-bytes, data[index], version); + if (!elementBytes) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to write vector element, index = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, index, size)); +#endif + return 0; + } + bytes += elementBytes; + } + + return bytes; + } + + template + inline unsigned ReadContainer(const unsigned char * stream, unsigned size, std::vector & data, unsigned maxLen, unsigned maxElementLen, unsigned version = 0) + { + unsigned bytes = 0; + unsigned elementBytes = 0; + unsigned length = 0; + data.clear(); + bytes += ReadEncoded(stream, size, length); + if (!bytes || length > maxLen) { +#ifdef TRACK_READ_WRITE_FAILURES + PushMessageFailure(PrintToString("%s, %s:%u - failed to read vector, length = %u, maxLen = %u, size = %u", __FUNCTION__, __FILE__, __LINE__, length, maxLen, size)); +#endif + return 0; + } + for (unsigned i=0; i + inline unsigned Read(const unsigned char * stream, unsigned size, std::vector & data, unsigned maxLen, unsigned version = 0) + { + return ReadContainer(stream, size, data, maxLen, 1, version); + } + +#ifdef PRINTABLE_MESSAGES + template + inline int Print(char * stream, unsigned size, const std::map & data, unsigned maxDepth=INT_MAX) + { + int bytes = 0; + int elementBytes = 0; + + if (maxDepth == 0) { + bytes = snprintf(stream, size, "{size(%u)}", data.size()); + return bytes; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "{"); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + + bool printedOne = false; + size_t index = 0; + for (typename std::map::const_iterator iter = data.begin(); iter != data.end(); iter++, index++) + { + if (printedOne) { + elementBytes = snprintf(stream+bytes, size-bytes, ", "); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + } else { + printedOne = true; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "[%u]", index); + if (FailedToPrint(elementBytes, size-bytes)) { + return elementBytes; + } + + bytes += elementBytes; + + elementBytes = Print(stream+bytes, size-bytes, *iter, maxDepth-1); + if (FailedToPrint(elementBytes, size-bytes)) { + return elementBytes; + } + + bytes += elementBytes; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "}"); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + + return bytes; + } + + template + inline int Print(char * stream, unsigned size, const std::list & data, unsigned maxDepth=INT_MAX) + { + int bytes = 0; + int elementBytes = 0; + + if (maxDepth == 0) { + bytes = snprintf(stream, size, "{size(%u)}", data.size()); + return bytes; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "{"); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + + bool printedOne = false; + size_t index = 0; + for (typename std::list::const_iterator iter = data.begin(); iter != data.end(); iter++, index++) + { + if (printedOne) { + elementBytes = snprintf(stream+bytes, size-bytes, ", "); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + } else { + printedOne = true; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "[%u]", index); + if (FailedToPrint(elementBytes, size-bytes)) { + return elementBytes; + } + + bytes += elementBytes; + + elementBytes = Print(stream+bytes, size-bytes, *iter, maxDepth-1); + if (FailedToPrint(elementBytes, size-bytes)) { + return elementBytes; + } + + bytes += elementBytes; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "}"); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + + return bytes; + } + + template + inline int Print(char * stream, unsigned size, const std::vector & data, unsigned maxDepth=INT_MAX) + { + int bytes = 0; + int elementBytes = 0; + + if (maxDepth == 0) { + bytes = snprintf(stream, size, "{size(%u)}", data.size()); + return bytes; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "{"); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + + bool printedOne = false; + size_t index = 0; + for (typename std::vector::const_iterator iter = data.begin(); iter != data.end(); iter++, index++) + { + if (printedOne) { + elementBytes = snprintf(stream+bytes, size-bytes, ", "); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + } else { + printedOne = true; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "[%u]", index); + if (FailedToPrint(elementBytes, size-bytes)) { + return elementBytes; + } + + bytes += elementBytes; + + elementBytes = Print(stream+bytes, size-bytes, *iter, maxDepth-1); + if (FailedToPrint(elementBytes, size-bytes)) { + return elementBytes; + } + + bytes += elementBytes; + } + + elementBytes = snprintf(stream+bytes, size-bytes, "}"); + + if (FailedToPrint(elementBytes, size-bytes)) { + bytes = elementBytes; + return bytes; + } else { + bytes += elementBytes; + } + + return bytes; + } +#endif + + //////////////////////////////////////////////////////////////////////////////// + + template + class DECLSPEC AutoVariable : public AutoVariableBase + { + public: + //AutoVariable() : AutoVariableBase(), mValue() {} + AutoVariable() : AutoVariableBase() {} // rls - removed redundant default constructor for mValue + explicit AutoVariable(const ValueType & source) : AutoVariableBase(), mValue(source) {} + AutoVariable(const AutoVariable & copy) : AutoVariableBase(), mValue(copy.mValue) {} + virtual ~AutoVariable() {} + + const ValueType & Get() const { return mValue; } + ValueType & Get() { return mValue; } + void Set(const ValueType & rhs) { mValue = rhs; } + + virtual void Copy(AutoVariableBase *pSource) { AutoVariable *pOther = static_cast *>(pSource); mValue = pOther->mValue; } + + virtual unsigned Write(unsigned char * stream, unsigned size, unsigned version = 0) const { return soe::Write(stream, size, mValue, version); } + virtual unsigned Read(const unsigned char * stream, unsigned size, unsigned maxLen, unsigned version = 0) { return soe::Read(stream, size, mValue, maxLen, version); } +#ifdef PRINTABLE_MESSAGES + virtual int Print(char * stream, unsigned size, unsigned maxDepth=INT_MAX) const + { + int bytes = snprintf(stream, size, "%s(", this->VariableName()); + int bytesTotal = 0; + + if (FailedToPrint(bytes, size)) { + return bytes; + } + + bytesTotal += bytes; + bytes = soe::Print(stream+bytesTotal, size-bytesTotal, mValue, maxDepth); + + if (FailedToPrint(bytes, size-bytesTotal)) { + return bytes; + } + + bytesTotal += bytes; + bytes = snprintf(stream+bytesTotal, size-bytesTotal, ")"); + + if (FailedToPrint(bytes, size-bytesTotal)) { + return bytes; + } + + bytesTotal += bytes; + + return bytesTotal; + } +#endif + + private: + ValueType mValue; + }; + + + //////////////////////////////////////////////////////////////////////////////// + + template + class DECLSPEC MemberScribeBase + { + public: + + MemberScribeBase(const char * variableName, unsigned version = 0, EVersionEffect effect = eNoEffect) + : mVariableName(variableName) + , mVersion(version) + , mEffect(effect) + {} + + virtual ~MemberScribeBase() {} + + virtual unsigned Read(const unsigned char * stream, unsigned size, ClassType & data, unsigned version = 0) const = 0; + virtual unsigned Write(unsigned char * stream, unsigned size, const ClassType & data, unsigned version = 0) const = 0; + virtual int Print(char * stream, unsigned size, const ClassType & data, unsigned maxDepth=INT_MAX) const = 0; + + inline const char * VariableName() const { return mVariableName; } + inline unsigned Version() const { return mVersion; } + inline EVersionEffect Effect() const { return mEffect; } + + protected: + const char *mVariableName; + unsigned mVersion; + EVersionEffect mEffect; + }; + + //////////////////////////////////////////////////////////////////////////////// + + template + class DECLSPEC MemberScribe : public MemberScribeBase + { + public: + typedef MemberType ClassType::*MemberPointer_t; + + MemberScribe(const char * variableName, MemberPointer_t memberPointer, unsigned size, unsigned version, EVersionEffect effect) + : MemberScribeBase(variableName, version, effect) + , mMemberPointer(memberPointer) + , mSize(size) + {} + + virtual ~MemberScribe() {} + + virtual unsigned Read(const unsigned char * stream, unsigned size, ClassType & data, unsigned version = 0) const + { + return soe::Read(stream, size, data.*mMemberPointer, mSize, version); + } + virtual unsigned Write(unsigned char * stream, unsigned size, const ClassType & data, unsigned version = 0) const + { + return soe::Write(stream, size, data.*mMemberPointer, version); + } + virtual int Print(char * stream, unsigned size, const ClassType & data, unsigned maxDepth=INT_MAX) const + { +#ifdef PRINTABLE_MESSAGES + return soe::Print(stream, size, data.*mMemberPointer, maxDepth); +#else + return 0; +#endif + } + + protected: + MemberPointer_t mMemberPointer; + unsigned mSize; + }; + + // For use with list/vector reading with string/vector/list (anything with maxSize) elements + template + class DECLSPEC MemberScribeWithElements : public MemberScribe + { + public: + MemberScribeWithElements(const char * variableName, typename MemberScribe::MemberPointer_t memberPointer, unsigned size, unsigned elementSize, unsigned version = 0, EVersionEffect effect = eNoEffect) + : MemberScribe(variableName, memberPointer, size, version, effect) + , mElementSize(elementSize) + {} + + virtual ~MemberScribeWithElements() {} + + // overrides read function *only*; parent behavior OK for others + virtual unsigned Read(const unsigned char * stream, unsigned size, ClassType & data, unsigned version = 0) const + { + return soe::ReadContainer(stream, size, data.*MemberScribe::mMemberPointer, MemberScribe::mSize, mElementSize, version); + } + protected: + unsigned mElementSize; + }; + + class DECLSPEC ClassScribeBase + { + public: + ClassScribeBase(const char * className) : mClassName(className), mInitialized(false) { } + virtual ~ClassScribeBase() { } + + inline bool IsInitialized() const { return mInitialized; } + inline void SetInitialized(bool initialized) { mInitialized = initialized; } + + const char * ClassName() const { return mClassName; } + + virtual void ClearMembers() = 0; + virtual size_t CountMembers() const = 0; + + protected: + const char * mClassName; + + private: + bool mInitialized; + }; + + template + class DECLSPEC ClassScribe : public ClassScribeBase + { + protected: + typedef MemberScribeBase MemberScribe_t; + typedef std::vector MemberScribeVector_t; + + public: + ClassScribe(const char * className) : ClassScribeBase(className) { } + ~ClassScribe() { } + + unsigned Read(const unsigned char * stream, unsigned size, ClassType & data, unsigned version) const; + unsigned Write(unsigned char * stream, unsigned size, const ClassType & data, unsigned version) const; + int Print(char * stream, unsigned size, const ClassType & data, unsigned maxDepth) const; + + virtual void ClearMembers() + { + typename MemberScribeVector_t::const_iterator memberIter; + for (memberIter = mMemberScribes.begin(); memberIter != mMemberScribes.end(); memberIter++) + { + MemberScribe_t * pMemberScribe = *memberIter; + delete pMemberScribe; + } + mMemberScribes.clear(); + } + + virtual size_t CountMembers() const { return mMemberScribes.size(); } + + template + void AddMember(const char * variableName, MemberType ClassType:: * memberPointer, unsigned size, unsigned version, EVersionEffect effect) + { + MemberScribeBase * pMemberScribe = new MemberScribe(variableName, memberPointer, size, version, effect); + mMemberScribes.push_back(pMemberScribe); + } + + template + void AddMemberContainer(const char * variableName, MemberType ClassType:: * memberPointer, unsigned size , unsigned elementSize, unsigned version, EVersionEffect effect) + { + MemberScribeBase * pMemberScribe = new MemberScribeWithElements(variableName, memberPointer, size, elementSize, version, effect); + mMemberScribes.push_back(pMemberScribe); + } + + private: + MemberScribeVector_t mMemberScribes; + }; + + template + unsigned ClassScribe::Read(const unsigned char * stream, unsigned size, ClassType & data, unsigned version) const + { + unsigned bytesTotal = 0; + typename MemberScribeVector_t::const_iterator memberIter; + + for (memberIter = mMemberScribes.begin(); memberIter != mMemberScribes.end(); memberIter++) + { + MemberScribe_t * pMemberScribe = *memberIter; + + if (IsMemberIncluded(version, pMemberScribe->Version(), pMemberScribe->Effect())) + { + unsigned bytes = pMemberScribe->Read(stream, size, data, version); + if (bytes == 0) + { +#ifdef TRACK_READ_WRITE_FAILURES + soe::PushMessageFailure(soe::PrintToString("%s, %s:%u - failed to read %s.%s, size = %u, version = %u", __FUNCTION__, __FILE__, __LINE__, ClassName(), pMemberScribe->VariableName(), size, version)); +#endif + bytesTotal = 0; + break; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + } + + return bytesTotal; + } + + template + unsigned ClassScribe::Write(unsigned char * stream, unsigned size, const ClassType & data, unsigned version) const + { + unsigned bytesTotal = 0; + typename MemberScribeVector_t::const_iterator memberIter; + + for (memberIter = mMemberScribes.begin(); memberIter != mMemberScribes.end(); memberIter++) + { + const MemberScribe_t * pMemberScribe = *memberIter; + + if (IsMemberIncluded(version, pMemberScribe->Version(), pMemberScribe->Effect())) + { + unsigned bytes = pMemberScribe->Write(stream, size, data, version); + if (bytes == 0) + { +#ifdef TRACK_READ_WRITE_FAILURES + soe::PushMessageFailure(soe::PrintToString("%s, %s:%u - failed to read %s.%s, size = %u, version = %u", __FUNCTION__, __FILE__, __LINE__, ClassName(), pMemberScribe->VariableName(), size, version)); +#endif + bytesTotal = 0; + break; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } + } + + return bytesTotal; + } + + template + int ClassScribe::Print(char * stream, unsigned size, const ClassType & data, unsigned maxDepth) const + { + int bytesTotal = 0; + int bytes = 0; + +#ifdef PRINTABLE_MESSAGES + if (maxDepth == 0) { + bytes = snprintf(stream, size, "%s{mMembers(%u)}", ClassName(), mMemberScribes.size()); + return bytes; + } + + bytes = snprintf(stream, size, "{"); + + if (soe::FailedToPrint(bytes, size)) { + bytesTotal = bytes; + return bytesTotal; + } else { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + + bool printedOne = false; + typename MemberScribeVector_t::const_iterator memberIter; + + for (memberIter = mMemberScribes.begin(); memberIter != mMemberScribes.end(); memberIter++) + { + int bytes = 0; + + if (printedOne) { + bytes = snprintf(stream, size, ", "); + if (soe::FailedToPrint(bytes, size)) { + bytesTotal = bytes; + return bytesTotal; + } else { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } else { + printedOne = true; + } + + const MemberScribe_t * pMemberScribe = *memberIter; + + bytes = pMemberScribe->Print(stream, size, maxDepth-1); + if (soe::FailedToPrint(bytes, size)) + { + bytesTotal = bytes; + return bytesTotal; + } + else + { + stream += bytes; + size -= bytes; + bytesTotal += bytes; + } + } +#endif + + return bytesTotal; + } + + //////////////////////////////////////////////////////////////////////////////// +} + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/stringutils.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/stringutils.cpp new file mode 100644 index 00000000..1400bf5c --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/stringutils.cpp @@ -0,0 +1,174 @@ +#ifdef WIN32 +#pragma warning (disable: 4786) +#endif + +#include "stringutils.h" + +#include +#include + +using namespace std; +#define transform std::transform + +namespace soe +{ + void makeupper(std::string& str) + { + transform(str.begin(), str.end(), str.begin(), toupper); + } + + std::string touppercase(const std::string& str) + { + std::string tmp = str; + transform(tmp.begin(), tmp.end(), tmp.begin(), toupper); + return tmp; + } + + void makelower(std::string& str) + { + transform(str.begin(), str.end(), str.begin(), tolower); + } + + std::string tolowercase(const std::string& str) + { + std::string tmp = str; + transform(tmp.begin(), tmp.end(), tmp.begin(), tolower); + return tmp; + } + + bool isEqualNoCase(const std::string& left, const std::string& right) + { + std::string tmp1 = left; + makeupper(tmp1); + std::string tmp2 = right; + makeupper(tmp2); + return tmp1 == tmp2; + } + + void rtrim(std::string& str) + { + size_t pos; + pos = str.find_last_not_of (" "); + str = str.substr(0, pos + 1); + } + + void ltrim(std::string& str) + { + size_t pos; + pos = str.find_first_not_of (" "); + if (pos != std::string::npos) + str = str.substr(pos); + } + + void trim(std::string& str) + { + rtrim(str); + ltrim(str); + } + + + // crack(dest, base, tok) takes a string and breaks it at the first instance of the token (tok) + // if the token does not exist it is considerd to be past the end of the string. + // dest will contain the portion before the token, base will containe the after the token, the token is discarded + // I want the caller to provide the dest, so we are not at fault if it leaks + // NBM - This is code that I have developed many times before (FYI) + void crack(std::string * dest, std::string * base, const std::string& tok) + { + size_t pos = base->find(tok); + if (pos == std::string::npos) + { + // tok not found take the whole thing + if (dest != NULL) { dest->assign(*base);} + base->clear(); + return; + } + + if (dest != NULL) {dest->assign(base->substr(0,pos));} + base->erase(0,pos + tok.length()); + } + +} + +//********************************************************************* +// +// Unit Tests +// +//********************************************************************* +#ifdef ENABLE_UNIT_TESTS +#include +#include + +SUITE(stringUtilsSuite) +{ + struct stringUtilsFixture + { + stringUtilsFixture() + { + s1 = "AbcD 1&"; + s2 = ""; + s3 = " abcd 1& "; + } + ~stringUtilsFixture() + { + } + + std::string s1, s2, s3; + }; + + TEST_FIXTURE(stringUtilsFixture, MakeUpper) + { + soe::makeupper(s1); + CHECK_EQUAL("ABCD 1&", s1); + soe::makeupper(s2); + CHECK_EQUAL("", s2); + } + + TEST_FIXTURE(stringUtilsFixture, toppercase) + { + CHECK_EQUAL("ABCD 1&", soe::touppercase(s1)); + CHECK_EQUAL("", soe::touppercase(s2)); + } + + TEST_FIXTURE(stringUtilsFixture, MakeLower) + { + soe::makelower(s1); + CHECK_EQUAL("abcd 1&", s1); + soe::makelower(s2); + CHECK_EQUAL("", s2); + } + + TEST_FIXTURE(stringUtilsFixture, tolowercase) + { + CHECK_EQUAL("abcd 1&", soe::tolowercase(s1)); + CHECK_EQUAL("", soe::tolowercase(s2)); + } + + TEST(isEqualNoCase) + { + CHECK(soe::isEqualNoCase("aBcD", "AbCd")); + CHECK(!(soe::isEqualNoCase("aBcD", "AbCx"))); + CHECK(!(soe::isEqualNoCase("aBcD", "AbCx"))); + CHECK(!(soe::isEqualNoCase("aBcD", "AbCdE"))); + CHECK(!(soe::isEqualNoCase("aBcD", ""))); + CHECK(!(soe::isEqualNoCase("", "AbCdE"))); + } + + TEST_FIXTURE(stringUtilsFixture, rtrim) + { + soe::rtrim(s3); + CHECK_EQUAL(" abcd 1&", s3); + } + + TEST_FIXTURE(stringUtilsFixture, ltrim) + { + soe::ltrim(s3); + CHECK_EQUAL("abcd 1& ", s3); + } + + TEST_FIXTURE(stringUtilsFixture, trim) + { + soe::trim(s3); + CHECK_EQUAL("abcd 1&", s3); + } +} // End of stringutils +#endif // ENABLE_UINIT_TESTS diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/stringutils.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/stringutils.h new file mode 100644 index 00000000..f994210c --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/stringutils.h @@ -0,0 +1,64 @@ +#ifndef __STRINGUTILS_H__ +#define __STRINGUTILS_H__ + +#include + +#define LOWER_CASE_STRING_COMPARISON_OPERATOR(__oper_type__) \ + bool operator __oper_type__(const lowerCaseString & rhs) const { return mString __oper_type__ rhs.mString; } + +#define UPPER_CASE_STRING_COMPARISON_OPERATOR(__oper_type__) \ + bool operator __oper_type__(const upperCaseString & rhs) const { return mString __oper_type__ rhs.mString; } + +namespace soe +{ + void makeupper(std::string& str); + std::string touppercase(const std::string& str); + void makelower(std::string& str); + std::string tolowercase(const std::string& str); + bool isEqualNoCase(const std::string& left, const std::string& right); + void rtrim(std::string& str); + void ltrim(std::string& str); + void trim(std::string& str); + void crack(std::string * dest, std::string * base, const std::string& tok); + void inline crack(std::string& dest, std::string& base, const std::string& tok) {crack(&dest, &base, tok);} + void inline crack(std::string& base, const std::string& tok) {crack(NULL, &base, tok);} + + class lowerCaseString + { + public: + lowerCaseString() {} + lowerCaseString(const std::string & src) : mString(src) { makelower(mString); } + lowerCaseString(const char * src) : mString(src ? src : "") { makelower(mString); } + operator std::string() const { return mString; } + + LOWER_CASE_STRING_COMPARISON_OPERATOR(<) + LOWER_CASE_STRING_COMPARISON_OPERATOR(<=) + LOWER_CASE_STRING_COMPARISON_OPERATOR(==) + LOWER_CASE_STRING_COMPARISON_OPERATOR(>=) + LOWER_CASE_STRING_COMPARISON_OPERATOR(>) + + private: + std::string mString; + }; + + class upperCaseString + { + public: + upperCaseString() {} + upperCaseString(const std::string & src) : mString(src) { makeupper(mString); } + upperCaseString(const char * src) : mString(src ? src : "") { makeupper(mString); } + operator std::string() const { return mString; } + + UPPER_CASE_STRING_COMPARISON_OPERATOR(<) + UPPER_CASE_STRING_COMPARISON_OPERATOR(<=) + UPPER_CASE_STRING_COMPARISON_OPERATOR(==) + UPPER_CASE_STRING_COMPARISON_OPERATOR(>=) + UPPER_CASE_STRING_COMPARISON_OPERATOR(>) + + private: + std::string mString; + }; +} + +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/substringSearchTree.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/substringSearchTree.h new file mode 100644 index 00000000..9acfcea7 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/substringSearchTree.h @@ -0,0 +1,795 @@ +#ifndef _SUBSTRING_SEARCH_TREE_H_ +#define _SUBSTRING_SEARCH_TREE_H_ + +#include +#include +#include + +template +class stringTree +{ +public: + typedef std::basic_string string_t; + + stringTree(); + stringTree(const stringTree & src); + virtual ~stringTree(); + + stringTree & operator=(const stringTree & rhs); + +private: + class letterNode; + typedef typename std::vector nodeVector_t; + +public: + class const_iterator + { + public: + friend class stringTree; + + const_iterator() { } + virtual ~const_iterator() { } + + void getKey(string_t & key) const; + inline string_t key() const { string_t value; getKey(value); return value; } + + const_iterator & operator=(const const_iterator & rhs) { mBranch = rhs.mBranch; return *this; } + + const_iterator & operator++(); + const_iterator operator++(int) { const_iterator tmp = *this; ++*this; return tmp; } + const_iterator & operator--(); + const_iterator operator--(int) { const_iterator tmp = *this; --*this; return tmp; } + bool operator==(const const_iterator & rhs) const; + bool operator!=(const const_iterator & rhs) const; + + const data_t & operator*() const; + const data_t * operator->() const; + + protected: + bool advance(); + bool retreat(); + + nodeVector_t mBranch; + }; + + class iterator : public const_iterator + { + public: + friend class stringTree; + + iterator() { } + virtual ~iterator() { } + + iterator & operator=(const iterator & rhs) { *((const_iterator *)this) = rhs; return *this; } + + data_t & operator*() const; + data_t * operator->() const; + }; + + inline const iterator & begin() { if(mBeginDirty) { setBegin(); } return mBegin; } + inline const const_iterator & begin() const { if(mBeginDirty) { setBegin(); } return mBegin; } + inline const iterator & end() { return mEnd; } + inline const const_iterator & end() const { return mEnd; } + + template + iterator find(const iter_t & start, const iter_t & stop); + + template + const_iterator find(const iter_t & start, const iter_t & stop) const; + + template + inline iterator find(const container_t & key) { return find(key.begin(), key.end()); } + + template + inline const_iterator find(const container_t & key) const { return find(key.begin(), key.end()); } + + template + bool seek(iter_t & start, const iter_t & stop, iterator & position); + + template + bool seek(iter_t & start, const iter_t & stop, const_iterator & position) const; + + template + bool search(iter_t & start, iter_t & middle, const iter_t & stop, typename stringTree::const_iterator & position) const; + + template + bool insert(const iter_t & start, const iter_t & stop, data_t data); + inline bool insert(const string_t & key, data_t data) { return insert(key.begin(), key.end(), data); } + + data_t & operator[](const string_t & key); + + template + bool erase(const iter_t & start, const iter_t & stop); + bool erase(const iterator & position); + inline bool erase(const string_t & key) { return erase(key.begin(), key.end()); } + + void clear(); + + size_t merge(const const_iterator & start, const const_iterator & stop); + inline size_t merge(const stringTree & src) { return merge(src.begin(), src.end()); } + + size_t size() const { return mSize; } + bool empty() const { return (mSize == 0); } + size_t nodeCount() const { return mNodeCount; } + size_t depth() const { return mDepthVector.size(); } + size_t width(size_t level) const { return mDepthVector[level]; } + + template + class finder + { + public: + finder(const stringTree & dictionary, const iter_t & start, const iter_t & end) + : mDictionary(dictionary) + , mTextStart(start) + , mTextMiddle(start) + , mTextEnd(end) + { } + + template + finder(const stringTree & dictionary, const container_t & text) + : mDictionary(dictionary) + , mTextStart(text.begin()) + , mTextMiddle(text.begin()) + , mTextEnd(text.end()) + { } + + virtual ~finder() { } + + bool next(); + const data_t * data() const; + + inline const iter_t & start() const { return mTextStart; } + inline const iter_t & middle() const { return mTextMiddle; } + inline const iter_t & end() const { return mTextEnd; } + inline const iter_t & match() const { return mMatchStart; } + + private: + iter_t mTextStart; + iter_t mTextMiddle; + iter_t mTextEnd; + iter_t mMatchStart; + const stringTree & mDictionary; + typename stringTree::const_iterator mDictionaryIter; + }; + +private: + class letterNode + { + public: + letterNode() : mpData(NULL), mpChildren(NULL), mNumChildren(0) { } + letterNode(const letter_t & letter) : mLetter(letter), mpData(NULL), mpChildren(NULL), mNumChildren(0) { } + ~letterNode(); + + inline bool hasData() const { return (mpData != NULL); } + inline data_t & getData() { return *mpData; } + inline const data_t & getData() const { return *mpData; } + inline void setData(const data_t & data) { if (mpData) { *mpData = data; } else { mpData = new data_t(data); } } + inline void removeData() { delete mpData; mpData = NULL; } + + letterNode * get(letter_t index) const; + bool put(letter_t index, letterNode ** tree); + bool take(letter_t index, bool deallocate = true); + + letterNode * firstChild() const; + letterNode * lastChild() const; + letterNode * nextChild(const letterNode * child) const; + letterNode * previousChild(const letterNode * child) const; + + inline bool operator<(const letter_t & rhs) const { return mLetter < rhs; } + friend inline bool operator<(const const_iterator & lhs, const letter_t & rhs) { return lhs.mLetter < rhs; } + inline bool operator==(const letter_t & rhs) const { return mLetter == rhs; } + friend inline bool operator==(const const_iterator & lhs, const letter_t & rhs) { return lhs.mLetter == rhs; } + + inline letter_t & letter() { return mLetter; } + inline const letter_t & letter() const { return mLetter; } + inline bool hasNoChildren() const { return (mpChildren == NULL) || (mNumChildren == 0); } + + private: + letter_t mLetter; + data_t * mpData; + letterNode * mpChildren; + size_t mNumChildren; + }; + +private: + void setBegin() const; + + size_t mSize; + size_t mNodeCount; + std::vector mDepthVector; + letterNode mRoot; + mutable iterator mBegin; + mutable iterator mEnd; + mutable bool mBeginDirty; + bool mIsClearing; +}; + +template +stringTree::stringTree() + : mSize(0) + , mNodeCount(0) + , mBeginDirty(true) + , mIsClearing(false) +{ + setBegin(); + mEnd.mBranch.push_back(&mRoot); +} + +template +stringTree::stringTree(const stringTree & src) + : mSize(0) + , mNodeCount(0) + , mBeginDirty(true) + , mIsClearing(false) +{ + setBegin(); + mEnd.mBranch.push_back(&mRoot); + merge(src); +} + +template +stringTree & stringTree::operator=(const stringTree & rhs) +{ + clear(); + merge(rhs); + + return *this; +} + +template +stringTree::~stringTree() +{ + clear(); +} + +template +template +bool stringTree::search(iter_t & start, iter_t & middle, const iter_t & stop, typename stringTree::const_iterator & position) const +{ + if (seek(middle, stop, position)) { + start++; + } else { + for (; + (position == end()) && (start!= stop); + start++) + { + position = end(); + middle = start; + seek(middle, stop, position); + } + } + + return (position != end()); +} + +template +template +bool stringTree::seek(iter_t & start, const iter_t & stop, typename stringTree::iterator & position) +{ + bool found = false; + + if (position.mBranch.empty()) { + position.mBranch.push_back(&mRoot); + } + + const letterNode * pnode = (start != stop) ? position.mBranch.back() : NULL; + + for (; (start != stop) && ((pnode = pnode->get(*start)) != NULL); start++) + { + position.mBranch.push_back(pnode); + if (pnode->hasData()) { + start++; + break; + } + } + found = pnode && pnode->hasData(); + if (!found) { position = end(); } + + return found; +} + +template +template +bool stringTree::seek(iter_t & start, const iter_t & stop, typename stringTree::const_iterator & position) const +{ + bool found = false; + + if (position.mBranch.empty()) { + position.mBranch.push_back(&mRoot); + } + + const letterNode * pnode = (start != stop) ? position.mBranch.back() : NULL; + + for (; (start != stop) && ((pnode = pnode->get(*start)) != NULL); start++) + { + position.mBranch.push_back(pnode); + if (pnode->hasData()) { + start++; + break; + } + } + found = pnode && pnode->hasData(); + if (!found) { position = end(); } + + return found; +} + +template +template +typename stringTree::iterator stringTree::find(const iter_t & start, const iter_t & stop) +{ + iter_t iter = start; + iterator pos; + + for (seek(iter, stop, pos); + (iter != stop) && (pos != end()); + seek(iter, stop, pos)) + { } + + return pos; +} + +template +template +typename stringTree::const_iterator stringTree::find(const iter_t & start, const iter_t & stop) const +{ + iter_t iter = start; + const_iterator pos; + + for (seek(iter, stop, pos); + (iter != stop) && (pos != end()); + seek(iter, stop, pos)) + { } + + return pos; +} + +template +template +bool stringTree::insert(const iter_t & start, const iter_t & stop, data_t data) +{ + letterNode * pnode = &mRoot; + bool added = false; + size_t depth = 0; + + for (iter_t iter = start; iter != stop; iter++, depth++) + { + bool justAdded = pnode->put(*iter, &pnode); + + if (justAdded) { + if (mDepthVector.size() <= depth) { + mDepthVector.push_back(0); + } + mDepthVector[depth]++; + mNodeCount++; + } + added = added || justAdded; + } + added = added || !pnode->hasData(); + if (added) { mSize++; mBeginDirty = true; } + pnode->setData(data); + + return added; +} + +template +template +bool stringTree::erase(const iter_t & start, const iter_t & stop) +{ + iterator position = find(start, stop); + bool removed = erase(position); + + return removed; +} + +template +bool stringTree::erase(const iterator & position) +{ + bool removed = false; + size_t depth = position.mBranch.size(); + + if (depth > 1) { + const nodeVector_t & trace = position.mBranch; + typename nodeVector_t::const_reverse_iterator trIter; + letter_t lastLetter; + bool lastWasEmpty = false; + + depth--; + const_cast(position.mBranch.back())->removeData(); + for (trIter = trace.rbegin(); trIter != trace.rend(); trIter++, depth--) + { + if (lastWasEmpty) { + const_cast(*trIter)->take(lastLetter, !mIsClearing); + mNodeCount--; + mDepthVector[depth]--; + if (mDepthVector[depth] == 0) { + mDepthVector.pop_back(); + } + } + lastLetter = (*trIter)->letter(); + lastWasEmpty = (*trIter)->hasNoChildren() && !(*trIter)->hasData(); + } + + removed = true; + } + if (removed) { mSize--; mBeginDirty = true; } + + return removed; +} + +template +data_t & stringTree::operator[](const string_t & key) +{ + iterator iter = find(key); + + if (iter == end()) { + insert(key, data_t()); + iter = find(key); + } + + return *iter; +} + +template +stringTree::letterNode::~letterNode() +{ + /* + does not free any memory; that is left up to the owner + */ +} + +template +typename stringTree::letterNode * stringTree::letterNode::get(letter_t index) const +{ + letterNode * tree = NULL; + letterNode * end = mpChildren + mNumChildren; + letterNode * place = std::lower_bound(mpChildren, mpChildren + mNumChildren, index); + + if (place && (place < end) && (place->mLetter == index)) { + tree = place; + } + + return tree; +} + +template +bool stringTree::letterNode::put(letter_t index, typename stringTree::letterNode ** tree) +{ + letterNode * end = mpChildren + mNumChildren; + letterNode * place = std::lower_bound(mpChildren, mpChildren + mNumChildren, index); + bool added = false; + + if (place && (place < end) && (place->mLetter == index)) { + *tree = place; + } else { + mNumChildren++; + + letterNode * children = new letterNode[mNumChildren]; + size_t before = place - mpChildren; + size_t after = end - place; + letterNode * post = children + before + 1; + + memcpy(children, mpChildren, before * sizeof(letterNode)); + memcpy(post, place, after * sizeof(letterNode)); + + place = children + before; + place->mLetter = index; + delete [] mpChildren; + mpChildren = children; + + *tree = place; + added = true; + } + + return added; +} + +template +bool stringTree::letterNode::take(letter_t index, bool deallocate) +{ + letterNode * end = mpChildren + mNumChildren; + letterNode * place = std::lower_bound(mpChildren, end, index); + bool removed = false; + + if (place && (place < end) && (place->mLetter == index)) { + mNumChildren--; + if (!mNumChildren) { deallocate = true; } + + letterNode * children = NULL; + + if (deallocate) { + children = mNumChildren ? new letterNode[mNumChildren] : NULL; + } else { + children = mpChildren; + } + + size_t before = place - mpChildren; + size_t after = end - place - 1; + letterNode * post = children + before; + + if (deallocate) { memcpy(children, mpChildren, before * sizeof(letterNode)); } + memcpy(post, place + 1, after * sizeof(letterNode)); + + if (deallocate) { delete [] mpChildren; } + mpChildren = children; + + removed = true; + } + + return removed; +} + +template +void stringTree::const_iterator::getKey(string_t & key) const +{ + typename nodeVector_t::const_iterator iter = mBranch.begin(); + if (iter != mBranch.end()) { + // skip the root + iter++; + } + for (; iter != mBranch.end(); iter++) + { + key.append(1, (*iter)->letter()); + } +} + +template +typename stringTree::letterNode * stringTree::letterNode::firstChild() const +{ + letterNode * child = NULL; + + if (mpChildren) { + child = mpChildren; + } + + return child; +} + +template +typename stringTree::letterNode * stringTree::letterNode::lastChild() const +{ + letterNode * child = NULL; + + if (mpChildren) { + child = mpChildren + mNumChildren - 1; + } + + return child; +} + +template +typename stringTree::letterNode * stringTree::letterNode::nextChild(const letterNode * child) const +{ + letterNode * next = NULL; + + if (mpChildren) { + size_t index = child - mpChildren; + + index++; + if (index < mNumChildren) { + next = mpChildren + index; + } + } + + return next; +} + +template +typename stringTree::letterNode * stringTree::letterNode::previousChild(const letterNode * child) const +{ + letterNode * previous = NULL; + + if (mpChildren) { + size_t index = child - mpChildren; + + if (index > 0) { + index--; + previous = mpChildren + index; + } + } + + return previous; +} + +template +typename stringTree::const_iterator& stringTree::const_iterator::operator++() +{ + while (advance() && !mBranch.back()->hasData()) + { } + + if (!mBranch.back()->hasData()) { + mBranch.resize(1); + } + + return *this; +} + +template +typename stringTree::const_iterator & stringTree::const_iterator::operator--() +{ + while (retreat() && + !mBranch.back()->hasData() && + (mBranch.size() > 1)) + { } + + if (!mBranch.back()->hasData()) { + mBranch.resize(1); + } + + return *this; +} + +template +bool stringTree::const_iterator::advance() +{ + bool wentForward = false; + const letterNode * terminal = NULL; + const letterNode * penultimate = NULL; + const letterNode * next = NULL; + + if (mBranch.back()->hasNoChildren()) { + // go forward + while ((mBranch.size() >= 2) && (next == NULL)) + { + penultimate = mBranch[mBranch.size() - 2]; + terminal = mBranch[mBranch.size() - 1]; + next = penultimate->nextChild(terminal); + mBranch.pop_back(); + } + } else { + // go down + terminal = mBranch[mBranch.size() - 1]; + next = terminal->firstChild(); + } + + if (next) { + mBranch.push_back(next); + wentForward = true; + } + + return wentForward; +} + +template +bool stringTree::const_iterator::retreat() +{ + bool wentBack = false; + const letterNode * terminal = NULL; + const letterNode * penultimate = NULL; + const letterNode * next = NULL; + + if (mBranch.size() >= 2) { + // go backwards + penultimate = mBranch[mBranch.size() - 2]; + terminal = mBranch[mBranch.size() - 1]; + next = penultimate->previousChild(terminal); + mBranch.pop_back(); + wentBack = true; + } else { + next = mBranch.back(); + mBranch.pop_back(); + } + // go to leaf + for (; next != NULL; next = next->lastChild()) + { + mBranch.push_back(next); + if (next->hasData() && next->hasNoChildren()) { + wentBack = true; + break; + } + } + + return wentBack; +} + +template +bool stringTree::const_iterator::operator==(const typename stringTree::const_iterator & rhs) const +{ + bool isEqual = (mBranch.size() == rhs.mBranch.size()) && + (mBranch.empty() || (mBranch.back() == rhs.mBranch.back())); + return isEqual; +} + +template +bool stringTree::const_iterator::operator!=(const typename stringTree::const_iterator & rhs) const +{ + return (mBranch != rhs.mBranch); +} + +template +const data_t & stringTree::const_iterator::operator*() const +{ + return mBranch.back()->getData(); +} + +template +const data_t * stringTree::const_iterator::operator->() const +{ + return &(mBranch.back()->getData()); +} + +template +data_t & stringTree::iterator::operator*() const +{ + return ((data_t &)**(const_iterator *)this); +} + +template +data_t * stringTree::iterator::operator->() const +{ + return (&**this); +} + +template +void stringTree::setBegin() const +{ + mBegin.mBranch.clear(); + const letterNode * leaf = NULL; + + for (leaf = &mRoot; leaf != NULL; leaf = leaf->firstChild()) + { + mBegin.mBranch.push_back(leaf); + if (leaf->hasData()) { + break; + } + } + if (!mBegin.mBranch.back()->hasData()) { + mBegin.mBranch.resize(1); + } + mBeginDirty = false; +} + +template +void stringTree::clear() +{ + mIsClearing = true; + while (!empty()) + { + setBegin(); + erase(mBegin); + } + mIsClearing = false; +} + +template +size_t stringTree::merge(const const_iterator & start, const const_iterator & stop) +{ + const_iterator iter; + size_t numberInserted = 0; + + for (iter = start; iter != stop; iter++) + { + if (insert(iter.key(), *iter)) { + numberInserted++; + } + } +} + +template +template +bool stringTree::finder::next() +{ + bool found = mDictionary.search(mTextStart, mTextMiddle, mTextEnd, mDictionaryIter); + + if (found) { + mMatchStart = mTextStart - 1; + } + + return found; +} + +template +template +const data_t * stringTree::finder::data() const +{ + const data_t * pData = NULL; + + if (mDictionaryIter != mDictionary.end()) { + pData = &(*mDictionaryIter); + } + + return pData; +} + +#endif // _SUBSTRING_SEARCH_TREE_H_ + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/systemLog.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/systemLog.cpp new file mode 100644 index 00000000..96e33f76 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/systemLog.cpp @@ -0,0 +1,174 @@ +#ifdef WIN32 +#pragma warning (disable: 4786) +#endif + + +#include +#include +#include +#include "systemLog.h" +#include "profile.h" + +#ifdef linux +#include +#endif + +const char * SysLogPriority[] = +{ + "EMERG ", + "ALERT ", + "CRIT ", + "ERR ", + "WARNING", + "NOTICE ", + "INFO ", + "DEBUG " +}; + +SysLog::SysLog(const char * identity, bool systemOutput, bool localOutput, int logopt, int facility) : + mIdentity(), + mSystemOutput(systemOutput), + mLocalOutput(localOutput), + mLogFile(0), + mFileTimeout(0), + mTimeStamp(), + mStampTimeout(0), + mFlushOnEveryLocalWrite(false) +{ + Profile profile("SysLog::SysLog()"); + strncpy((char *)mIdentity, identity, 64); mIdentity[63] = 0; + mTimeStamp[0] = 0; +#ifdef linux + if (logopt == DEFAULT_LOGOPT) + { + logopt = LOG_NDELAY|LOG_PID; + } + if (facility == DEFAULT_FACILITY) + { + facility = LOG_LOCAL5; + } + openlog(mIdentity, logopt, facility); +#endif +} + +SysLog::~SysLog() +{ + Profile profile("SysLog::~SysLog()"); +#ifdef linux + closelog(); +#endif + if (mLogFile) + { + fprintf(mLogFile, "SysLog closed\n"); + fclose(mLogFile); + } +} + +void SysLog::CheckLocalFile(unsigned currentTime) +{ + Profile profile("SysLog::CheckLocalFile()"); + if (mLogFile && mFileTimeout < currentTime) + { + fprintf(mLogFile, "SysLog closed (continue in new file)\n"); + fclose(mLogFile); + mLogFile = 0; + } + if (!mLogFile) + { + char filename[64]; + struct tm timeStruct = *localtime((time_t *)¤tTime); + strftime(filename, 64, "log-%b%d-%H.txt", &timeStruct); + timeStruct.tm_sec = 59; + timeStruct.tm_min = 59; + //timeStruct.tm_hour = 23; + mFileTimeout = mktime(&timeStruct); + mLogFile = fopen(filename, "a+t"); + if (!mLogFile) + { + mLocalOutput = false; + } + else + { + fprintf(mLogFile, "\nSysLog opened\n"); + } + } +} + + +// 4975 ns +void SysLog::Log(int priority, const char *message) +{ + Profile profile("SysLog::Log()"); + if (priority > LOG_DEBUG) + priority = LOG_DEBUG; +#ifdef linux + if (mSystemOutput) + { + Profile profile("syslog()"); + syslog(priority, "%s : %s", SysLogPriority[priority], message); + } +#endif + if (mLocalOutput) + { + unsigned currentTime = (unsigned)time(0); + CheckLocalFile(currentTime); + if (mLogFile) + { + if (mStampTimeout < currentTime) + { + struct tm timeStruct = *localtime((time_t *)¤tTime); + strftime(mTimeStamp, 64, "%b %d %H:%M:%S", &timeStruct); + mStampTimeout = currentTime; + } + Profile profile("fprintf()"); + fprintf(mLogFile, "%s %s: %s : %s\n", mTimeStamp, mIdentity, SysLogPriority[priority], message); + if (mFlushOnEveryLocalWrite) + fflush(mLogFile); + } + } +} + +void SysLog::Log(int priority, const char *message, va_list args) +{ + Profile profile("SysLog::Log(...)"); + if (priority > LOG_DEBUG) + priority = LOG_DEBUG; +#ifdef linux + if (mSystemOutput) + { + char buffer[10240]; + snprintf(buffer, 10240, "%s : %s", SysLogPriority[priority], message); + Profile profile("vsyslog()"); + vsyslog(priority, buffer, args); + } +#endif + if (mLocalOutput) + { + unsigned currentTime = (unsigned)time(0); + CheckLocalFile(currentTime); + if (mLogFile) + { + if (mStampTimeout < currentTime) + { + struct tm timeStruct = *localtime((time_t *)¤tTime); + strftime(mTimeStamp, 64, "%b %d %H:%M:%S", &timeStruct); + mStampTimeout = currentTime; + } + Profile profile("fprintf()"); + fprintf(mLogFile, "%s %s: %s : ", mTimeStamp, mIdentity, SysLogPriority[priority]); + vfprintf(mLogFile, message, args); + fprintf(mLogFile, "\n"); + if (mFlushOnEveryLocalWrite) + fflush(mLogFile); + } + } +} + +void SysLog::vLog(int priority, const char *message, ...) +{ + Profile profile("SysLog::vLog()"); + va_list args; + va_start(args, message); + Log(priority, message, args); + va_end(args); +} diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/systemLog.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/systemLog.h new file mode 100644 index 00000000..13643535 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/systemLog.h @@ -0,0 +1,37 @@ +#ifndef SERVER__SYSTEMLOG_H +#define SERVER__SYSTEMLOG_H + + +#include + + +class SysLog +{ + public: + enum { LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG }; + enum { DEFAULT_LOGOPT = -1, DEFAULT_FACILITY = -1 }; + + SysLog(const char * identity, bool systemOutput = true, bool localOutput = false, int logopt = DEFAULT_LOGOPT, int facility = DEFAULT_FACILITY); + ~SysLog(); + void Log(int priority, const char *message); + void Log(int priority, const char *message, va_list argptr); + void vLog(int priority, const char *message, ...); + + void EnableFlushOnEveryLocalWrite(bool bEnable=true) { mFlushOnEveryLocalWrite=bEnable; } + + private: + void CheckLocalFile(unsigned currentTime); + + private: + char mIdentity[64]; + bool mSystemOutput; + bool mLocalOutput; + bool mFlushOnEveryLocalWrite; + FILE * mLogFile; + unsigned mFileTimeout; + char mTimeStamp[64]; + unsigned mStampTimeout; +}; + + +#endif // #ifndef SERVER__SYSTEMLOG_H diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/thread.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/thread.cpp new file mode 100644 index 00000000..af797230 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/thread.cpp @@ -0,0 +1,413 @@ +#ifdef WIN32 +#pragma warning (disable: 4514 4786) +#endif + +#if !defined(_MT) && !defined(_REENTRANT) +#pragma message ("excluding thread.cpp - requires multi-threaded compile") +#else + +#include "timer.h" +#include "thread.h" +#include +#include +#ifdef WIN32 +#include +#endif + +#ifdef NAMESPACE +namespace NAMESPACE +{ +#endif + +ThreadId_t BeginThread(ThreadProc_t threadproc, void *arglist) +{ + ThreadId_t threadId; +#ifdef WIN32 + threadId = _beginthread(*threadproc,0,arglist); +#else + pthread_create(&threadId,0,threadproc,arglist); +#endif + return threadId; +} + +ThreadId_t GetThreadId() +{ + unsigned threadId; +#ifdef WIN32 + threadId = GetCurrentThreadId(); +#else + threadId = pthread_self(); +#endif + return threadId; +} + +Mutex::Mutex() +{ +#ifdef WIN32 + InitializeCriticalSection(&mMutex); +#else + pthread_mutex_init(&mMutex, 0); +#endif +} + +Mutex::~Mutex() +{ +#ifdef WIN32 + DeleteCriticalSection(&mMutex); +#else + pthread_mutex_destroy(&mMutex); +#endif +} + +void Mutex::Lock() +{ +#ifdef WIN32 + EnterCriticalSection(&mMutex); +#else + pthread_mutex_lock(&mMutex); +#endif +} + +bool Mutex::TryLock() +{ +#ifdef WIN32 + Lock(); + return false; +#else + return pthread_mutex_trylock(&mMutex) != EBUSY; +#endif +} + +void Mutex::Unlock() +{ +#ifdef WIN32 + LeaveCriticalSection(&mMutex); +#else + pthread_mutex_unlock(&mMutex); +#endif +} + +ScopeLock::ScopeLock(Mutex & mutex) : +mMutex(mutex) +{ + mMutex.Lock(); +} + +ScopeLock::~ScopeLock() +{ + mMutex.Unlock(); +} + +SwitchableScopeLock::SwitchableScopeLock(Mutex & mutex, bool isOn) : + mMutex(mutex), + mIsOn(isOn) +{ + if(mIsOn) { mMutex.Lock(); } +} + +SwitchableScopeLock::~SwitchableScopeLock() +{ + if(mIsOn) { mMutex.Unlock(); } +} + +const int EVENT_SIGNALED = -1; + +Event::Event(bool signaled) : +#ifndef WIN32 + mMutex(), + mThreadCount(signaled ? EVENT_SIGNALED : 0), +#endif + mEvent() +{ +#ifdef WIN32 + mEvent = CreateEvent(NULL, FALSE, signaled ? TRUE : FALSE, NULL); +#else + pthread_mutex_init(&mMutex, NULL); + pthread_cond_init(&mCond, NULL); +#endif +} + +Event::~Event() +{ +#ifdef WIN32 + CloseHandle(mEvent); +#else + pthread_cond_destroy(&mCond); + pthread_mutex_destroy(&mMutex); +#endif +} + +// return: true if signaled, false if timeout/error +bool Event::Wait(unsigned timeoutMilliseconds) +{ +#ifdef WIN32 + return WaitForSingleObjectEx(mEvent, timeoutMilliseconds ? timeoutMilliseconds : INFINITE, FALSE) == WAIT_OBJECT_0; +#else + pthread_mutex_lock(&mMutex); + if (mThreadCount == EVENT_SIGNALED) + { + mThreadCount = 0; + pthread_mutex_unlock(&mMutex); + return true; // signaled + } + if (!timeoutMilliseconds) + { + mThreadCount++; + pthread_cond_wait(&mCond, &mMutex); + mThreadCount--; + pthread_mutex_unlock(&mMutex); + return true; // signaled + } + else + { + struct timeval now; + struct timespec abs_timeout; + gettimeofday(&now, NULL); + abs_timeout.tv_sec = now.tv_sec + timeoutMilliseconds/1000; + abs_timeout.tv_nsec = now.tv_usec * 1000 + (timeoutMilliseconds%1000)*1000000; + abs_timeout.tv_sec += abs_timeout.tv_nsec / 1000000000; + abs_timeout.tv_nsec %= 1000000000; + mThreadCount++; + int waitResult = pthread_cond_timedwait(&mCond, &mMutex, &abs_timeout); + mThreadCount--; + pthread_mutex_unlock(&mMutex); + return (waitResult == 0 || waitResult == EINTR); + } +#endif +} + +void Event::Signal() +{ +#ifdef WIN32 + SetEvent(mEvent); +#else + pthread_mutex_lock(&mMutex); + if (mThreadCount == 0) + mThreadCount = EVENT_SIGNALED; + pthread_cond_signal(&mCond); + pthread_mutex_unlock(&mMutex); +#endif +} + + +EventLock::EventLock(bool locked) : +#ifndef WIN32 + mMutex(), + mLocked(locked), +#endif + mEvent() +{ +#ifdef WIN32 + mEvent = CreateEvent(NULL, TRUE, locked ? FALSE : TRUE, NULL); +#else + pthread_mutex_init(&mMutex, NULL); + pthread_cond_init(&mCond, NULL); +#endif +} + +EventLock::~EventLock() +{ +#ifdef WIN32 + CloseHandle(mEvent); +#else + pthread_cond_destroy(&mCond); + pthread_mutex_destroy(&mMutex); +#endif +} + +bool EventLock::IsLocked() +{ +#ifdef WIN32 + return WaitForSingleObjectEx(mEvent, 0, FALSE) != WAIT_OBJECT_0; +#else + return mLocked; +#endif +} + +bool EventLock::Wait(unsigned timeoutMilliseconds) +{ +#ifdef WIN32 + return WaitForSingleObjectEx(mEvent, timeoutMilliseconds ? timeoutMilliseconds : INFINITE, FALSE) == WAIT_OBJECT_0; +#else + pthread_mutex_lock(&mMutex); + if (mLocked) + { + if (!timeoutMilliseconds) + { + pthread_cond_wait(&mCond, &mMutex); + pthread_mutex_unlock(&mMutex); + + return true; // signaled + } + else + { + struct timeval now; + struct timespec abs_timeout; + gettimeofday(&now, NULL); + abs_timeout.tv_sec = now.tv_sec + timeoutMilliseconds/1000; + abs_timeout.tv_nsec = now.tv_usec * 1000 + (timeoutMilliseconds%1000)*1000000; + abs_timeout.tv_sec += abs_timeout.tv_nsec / 1000000000; + abs_timeout.tv_nsec %= 1000000000; + int waitResult = pthread_cond_timedwait(&mCond, &mMutex, &abs_timeout); + pthread_mutex_unlock(&mMutex); + + return (waitResult == 0 || waitResult == EINTR); + } + } else { + pthread_mutex_unlock(&mMutex); + } + + return true; +#endif +} + +void EventLock::Lock() +{ +#ifdef WIN32 + ResetEvent(mEvent); +#else + pthread_mutex_lock(&mMutex); + mLocked = true; + pthread_mutex_unlock(&mMutex); +#endif +} + +void EventLock::Unlock() +{ +#ifdef WIN32 + SetEvent(mEvent); +#else + pthread_mutex_lock(&mMutex); + mLocked = false; + pthread_cond_broadcast(&mCond); + pthread_mutex_unlock(&mMutex); +#endif +} + + +RWLock::RWLock() : + mWriteMutex(), + mReadMutex(), + mReadCount(0), + mReadLock(), + mReadEvent() +{ +} + +RWLock::~RWLock() +{ +} + +void RWLock::ReadLock() +{ + mReadMutex.Lock(); + unsigned short readCount = mReadCount++; + mReadMutex.Unlock(); + if (readCount == 0) + { + mReadEvent.Wait(); + mReadLock.Unlock(); + } + mReadLock.Wait(); +} + +void RWLock::ReadUnlock() +{ + assert(mReadCount); + mReadMutex.Lock(); + unsigned short readCount = --mReadCount; + mReadMutex.Unlock(); + if (readCount == 0) + { + mReadLock.Lock(); + mReadEvent.Signal(); + } + +} + +void RWLock::WriteLock() +{ + mWriteMutex.Lock(); + mReadEvent.Wait(); +} + +void RWLock::WriteUnlock() +{ + mReadEvent.Signal(); + mWriteMutex.Unlock(); +} + + +Thread::Thread() : + mThreadId(0), + mActive(false), + mContinue(false) +{ +} + +Thread::~Thread() +{ + Stop(); +} + +#ifdef WIN32 +void _threadProc(void *threadPtr) +{ + Thread & thread = *((Thread*)threadPtr); + thread.mActive = true; + thread.ThreadProc(); + thread.mActive = false; +} +#else +void* _threadProc(void *threadPtr) +{ + Thread & thread = *((Thread*)threadPtr); + thread.mActive = true; + thread.ThreadProc(); + thread.mActive = false; + return 0; +} +#endif + +void Thread::Start() +{ + mContinue = true; + mThreadId = BeginThread(_threadProc,this); +} + +// return true if thread became inactive before the timeout +bool Thread::Stop(unsigned timeoutSeconds) +{ + mContinue = false; + timeoutSeconds += time(0); + while (mActive && (unsigned)time(0) +#include +#else +#include +#include +#include +#endif + +#ifdef NAMESPACE +namespace NAMESPACE +{ +#endif + +#ifdef WIN32 + typedef unsigned ThreadId_t; + typedef CRITICAL_SECTION Mutex_t; + typedef HANDLE Event_t; + typedef void (*ThreadProc_t)(void *); + void _threadProc(void *); +#else + typedef pthread_t ThreadId_t; + typedef pthread_mutex_t Mutex_t; + typedef pthread_cond_t Event_t; + typedef void* (*ThreadProc_t)(void *); + void* _threadProc(void *); +#endif + +ThreadId_t BeginThread(ThreadProc_t threadproc, void *arglist); +ThreadId_t GetThreadId(); + +class Mutex +{ + public: + Mutex(); + ~Mutex(); + void Lock(); + bool TryLock(); + void Unlock(); + private: + Mutex_t mMutex; +}; + +class ScopeLock +{ + public: + ScopeLock(Mutex & mutex); + ~ScopeLock(); + private: + Mutex & mMutex; +}; + +class SwitchableScopeLock +{ +public: + SwitchableScopeLock(Mutex & mutex, bool isOn); + ~SwitchableScopeLock(); +private: + Mutex & mMutex; + bool mIsOn; +}; + +class Event +{ + public: + Event(bool signaled = false); + ~Event(); + bool Wait(unsigned timeoutMilliseconds = 0); + void Signal(); + private: +#ifndef WIN32 + Mutex_t mMutex; + int mThreadCount; + pthread_cond_t mCond; +#endif + Event_t mEvent; +}; + +class EventLock +{ + public: + EventLock(bool locked = true); + ~EventLock(); + bool Wait(unsigned timeoutMilliseconds = 0); + void Lock(); + void Unlock(); + bool IsLocked(); + private: +#ifndef WIN32 + Mutex_t mMutex; + bool mLocked; + pthread_cond_t mCond; +#endif + Event_t mEvent; +}; + +class RWLock +{ + public: + RWLock(); + ~RWLock(); + void ReadLock(); + void ReadUnlock(); + void WriteLock(); + void WriteUnlock(); + private: + Mutex mWriteMutex; + Mutex mReadMutex; + unsigned short mReadCount; + EventLock mReadLock; + Event mReadEvent; +}; + +class Thread +{ +#ifdef WIN32 + friend void _threadProc(void *); +#else + friend void* _threadProc(void *); +#endif + public: + Thread(); + virtual ~Thread(); + void Start(); + bool Stop(unsigned timeoutSeconds=0); + bool Active() const; + bool GetId(ThreadId_t & threadId) const; + protected: + virtual void ThreadProc() = 0; + bool Continue() const; // query for ThreadProc to continue or exit + bool mContinue; + private: + ThreadId_t mThreadId; + bool mActive; +}; + +#ifdef NAMESPACE +} +#endif + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/timer.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/timer.h new file mode 100644 index 00000000..af234eca --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/timer.h @@ -0,0 +1,93 @@ +#ifndef SOE__TIMER_H +#define SOE__TIMER_H + + +#ifdef WIN32 + +#include + +#elif linux + +#include +#include + +#endif + +#include "types.h" + + +namespace soe +{ + + +#ifdef WIN32 + + + inline uint64 GetTimer(void) + { + uint64 result; + if (!QueryPerformanceCounter(reinterpret_cast(&result))) + result = 0; + return result; + } + + inline int64 GetTimerFrequency(void) + { + int64 result; + if (!QueryPerformanceFrequency(reinterpret_cast(&result))) + result = 0; + return result; + } + + inline void Sleep(uint32 ms) + { + ::Sleep(ms); + } + + +#elif linux + + inline uint64 GetTimer(void) + { + uint64 t; + struct timeval tv; + + gettimeofday(&tv, 0); + t = tv.tv_sec; + t = t * 1000000; + t += tv.tv_usec; + return t; + } + + inline int64 GetTimerFrequency(void) + { + int64 f = 1000000; + return f; + } + + inline void Sleep(uint32 ms) + { + usleep(static_cast(ms * 1000)); + } + + +#endif + + + inline double GetTimerLatency(soe::uint64 startTime, soe::uint64 finishTime=0) + { + soe::int64 requestAge; + soe::uint64 finish = (finishTime ? finishTime : soe::GetTimer()); + if (finish < startTime) + requestAge = (0 - 1) - (startTime - finish) + 1; + else + requestAge = finish - startTime; + return (double)requestAge/soe::GetTimerFrequency(); + } + + +} + + +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/trackMessageFailures.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/trackMessageFailures.cpp new file mode 100644 index 00000000..f6e1791a --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/trackMessageFailures.cpp @@ -0,0 +1,114 @@ +#include "trackMessageFailures.h" + +#ifdef TRACK_READ_WRITE_FAILURES + +# include +# include + +# if defined(_MT) || defined(_REENTRANT) +# include "thread.h" +# endif + +# ifdef WIN32 +# define vsnprintf _vsnprintf +# endif + +# include + +namespace soe +{ + +//////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////// + // Variables for tracking read/write failure + typedef std::list stringList_t; + + stringList_t messageFailureList; + + bool serializeMessageFailures = false; + +#if defined(_MT) || defined(_REENTRANT) + class SwitchableScopeLock + { + public: + SwitchableScopeLock() + { + if (serializeMessageFailures) + { msMutex.Lock(); } + } + ~SwitchableScopeLock() + { + if (serializeMessageFailures) + { msMutex.Unlock(); } + } + private: + static Mutex msMutex; + }; + Mutex SwitchableScopeLock::msMutex; +#else + class SwitchableScopeLock + { + public: + SwitchableScopeLock() {} + ~SwitchableScopeLock() {} + }; +#endif + +//////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////// + // Functions for tracking read/write failure + + std::string PrintToString(const char * format, ...) + { + va_list arg; + + va_start(arg, format); + + char buffer[2048]; + + vsnprintf((char *)buffer, sizeof(buffer), format, arg); + + va_end(arg); + + return buffer; + } + + void SetSerializeMessageFailures(bool serialize) + { + serializeMessageFailures = serialize; + } + + void PushMessageFailure(const std::string & failureDescription) + { + SwitchableScopeLock s; + + messageFailureList.push_front(failureDescription); + } + + void GetMessageFailureStack(std::vector & failureDescriptions) + { + SwitchableScopeLock s; + size_t index = 0; + + failureDescriptions.resize(messageFailureList.size()); + for (stringList_t::const_iterator lit = messageFailureList.begin(); lit != messageFailureList.end(); lit++, index++) + { + failureDescriptions[index] = *lit; + } + } + + void ClearMessageFailureStack() + { + SwitchableScopeLock s; + + messageFailureList.clear(); + } + +//////////////////////////////////////////////////////////////////////////////// + +} + +#endif // TRACK_READ_WRITE_FAILURES + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/trackMessageFailures.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/trackMessageFailures.h new file mode 100644 index 00000000..74a4c386 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/trackMessageFailures.h @@ -0,0 +1,29 @@ +#ifndef TRACK_MESSAGE_FAILURES_H +#define TRACK_MESSAGE_FAILURES_H + +///////////////////////////////////////////////////////////////////////////// +// Define the preprocessor directive below (TRACK_READ_WRITE_FAILURES) // +// in *.vcproj/make file to enable tracking of message read/write failures // +///////////////////////////////////////////////////////////////////////////// + +#ifdef TRACK_READ_WRITE_FAILURES + +#include +#include + +namespace soe +{ + std::string PrintToString(const char * format, ...); + + void PushMessageFailure(const std::string & failureDescription); + + void GetMessageFailureStack(std::vector & failureDescriptions); + + void ClearMessageFailureStack(); + + void SetSerializeMessageFailures(bool serialize); +}; + +#endif // TRACK_READ_WRITE_FAILURES + +#endif // TRACK_MESSAGE_FAILURES_H diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/types.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/types.h new file mode 100644 index 00000000..b0f24d83 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/types.h @@ -0,0 +1,79 @@ +#ifndef SOE__TYPES_H +#define SOE__TYPES_H + + +#define XSTRINGIFY(S) STRINGIFY(S) +#define STRINGIFY(S) #S + +#ifdef linux +#include +#endif + +#if 0 +# pragma warning (disable: 4251) + +# ifndef DECLSPEC +# if defined (IMPORT_DLL) +# define DECLSPEC __declspec(dllimport) +# elif defined (EXPORT_DLL) +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC +# endif +# endif +# define DECLIMPORT __declspec(dllimport) +#else +# define DECLSPEC +# define DECLIMPORT extern +#endif + +#ifdef WIN32 +# define FILENAME (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#else +# define FILENAME __FILE__ +#endif + +// printf format specifier for 64 bit number since Windows and Linux differ +#ifdef WIN32 +#define FMT_INT64 "%I64d" +#define FMT_UINT64 "%I64u" +#define atoi64(x) _atoi64(x) +#elif linux +#define FMT_INT64 "%lld" +#define FMT_UINT64 "%llu" +#define atoi64(x) atoll(x) +#endif + + +namespace soe +{ + typedef char int8; + typedef unsigned char uint8; + typedef short int16; + typedef unsigned short uint16; + +#ifdef WIN32 + typedef int int32; + typedef unsigned uint32; + typedef __int64 int64; + typedef unsigned __int64 uint64; + +#elif linux + + typedef int32_t int32; + typedef u_int32_t uint32; + typedef int64_t int64; + typedef u_int64_t uint64; +//! the previous seem erroneous +// typedef signed int int32; +// typedef unsigned int uint32; +// typedef signed long long int64; +// typedef unsigned long long uint64; +#endif + +} + + +#endif + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/utf8.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/utf8.cpp new file mode 100644 index 00000000..9cc64b43 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/utf8.cpp @@ -0,0 +1,312 @@ +#include +#include "utf8.h" + +namespace soe +{ + + /* + A little bit about UTF8... + + UTF8 stands for UCS Tranfer Format, while UCS stands for Universal Character Set. In + essence, UTF8 is a way to encode all the characters in the UCS. UTF8 attempts to + describe all the characters in the UCS using from 1 to 6 bytes per character. If the + first byte's high bit is not set, then the character is 1 byte long, and corresponds to + the US-ASCII character of the same number. In this way, all current US-ASCII strings are + already UTF8 compatible. If the the first byte's high bit is set, then the number of + high bits set until the first 0 describes the number of bytes in the character, e.g. a + first byte who's bits are 1110xxxx will be followed by 2 more "data bytes" that look like + 10yyyyyy 10zzzzzz, so that the entire character's descriptive ID is x xxxxyyyy yyzzzzzz. + + With this method, all characters in the UCS-4 character space can be mapped to <=6 bytes of + data, and all characters int the UCS-2 character space can be mapped in <=3 bytes + maximum. Since only UCS-2 actually contains character assignments from known languages, + it is expected that a maximum of 3 bytes will be needed for any character encountered + during translation. + + Unfortunately, this method also means that there are sequences of bytes that do not form + valid UTF8 characters/strings. + + In this file, we've placed some string and character validation functions, as well as + replacements for functions where the standard function will not behave + properly on a UTF8 string. + + */ + + // array of character byte-lengths. Index into the array with the first byte of the character & 0x3C then shift down twice. + // NOTE: this only works for multi-byte characters (up to 6). + static char UTF8_charSizeArray[16] = { 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6 }; + + + // UTF8_charSize returns the length of a UTF8 character in bytes (1-6). + // The array UTF8_size has 0's where an invalid header byte is detected. + // This is a "fast" function, so no error checking is done. + size_t UTF8_charSize( char * ptr ) + { + // temp hold of "header" byte + unsigned char ct = *ptr; + // if high bit not set, it's just a normal character, length 1 + if (( ct & 0x80 ) == 0 ) + { + return 1; + } + return (size_t)UTF8_charSizeArray[ ( ct & 0x3C ) >> 2 ]; + } + + + + // takes a pointer to a UTF8 character and fills the 2nd arg with the 16-bit UTF16 corresponding char. + // return value is the length of the UTF8 char converted. + size_t UTF8_convertCharToUTF16( char * ptr , UTF16 * ret ) + { + size_t len = UTF8_charSize( ptr ); + if ( len == 1 ) + { + *ret = *ptr; + } + else if ( len == 2 ) + { + *ret = (UTF16)(((UTF16)( *ptr & 0x1F ) << 6 ) + ( *(ptr+1) & 0x3F )); + } + else if ( len == 3 ) + { + *ret = (UTF16)(((UTF16)( *ptr & 0x0F ) << 12 ) + ((UTF16)( *(ptr+1) & 0x3F ) << 6 ) + (( *(ptr+2) & 0x3F ))); + } + return len; + } + + + // takes a pointer to a UTF8 String and fills the 2nd array with the corresponding UTF16 string, up to + // the limit number of characters. + // return value is the length of the UTF16 string converted. + size_t UTF8_convertToUTF16( char * ptr , UTF16 * ret, size_t limit ) + { + size_t len = 0; + while (( *ptr != 0 ) && ( len < ( limit - 1 ) ))//we use limit -1 to leave room for the null terminator + { + size_t clen = UTF8_convertCharToUTF16( ptr, ret ); + ptr += clen; + ret++; + len++; + } + *ret = 0; + return len; + } + + + // takes a UTF16 character and fills the 2nd arg with the UTF8 corresponding char. + // return value is the size of the UTF8 char converted. + // return value is 0 if the size of the UTF8 char would be bigger than the limit allowed. In this + // case, the return string is not filled. + size_t UTF16_convertCharToUTF8( UTF16 char16, char * ret, size_t limit ) + { + size_t len = 0; + if ( char16 <= 0x0080 ) + { + if ( limit >= 1 ) + { + *ret = (char)char16; + len = 1; + } + } + else if ( char16 <= 0x07FF ) + { + if ( limit >= 2 ) + { + *(ret) = (char)( 0xC0 + (( char16 & 0x07C0 ) >> 6 )); + *(ret+1) = (char)(0x80 + (char)( char16 & 0x003F )); + len = 2; + } + } + else + { + if ( limit >= 3 ) + { + *ret = (char)(0xE0 + (( char16 & 0xF000 ) >> 12 )); + *(ret+1) = (char)(0x80 + (( char16 & 0x0FC0 ) >> 6 )); + *(ret+2) = (char)(0x80 + ( char16 & 0x003F )); + len = 3; + } + } + return len; + } + + // takes a pointer to a UTF16 String and fills the 2nd array with the corresponding UTF8 string, up to + // the limit number of bytes. + // return value is the size of the UTF8 string converted. + size_t UTF16_convertToUTF8( UTF16 *ptr, char * ret, size_t limit ) + { + size_t len = 0; + while (( *ptr != 0 ) && ( limit != 0 )) + { + size_t clen = UTF16_convertCharToUTF8( *ptr, ret, limit ); + if ( clen == 0 ) + { + break; + } + ret += clen; + ptr++; + limit -= clen; + } + *ret = 0; + return len; + } + + + void EnglishToFakeUtf8(char *fakeDestination, char *englishSource, size_t destinationSize) + { + UTF16 utf16[8192]; + + UTF16 *d = utf16; + char *s = englishSource; + bool afterPercent = false; + while (*s != 0) + { + if (afterPercent) + { + *d = *s; + + if (*s == ' ') // assuming everthing between % and white-space is formatting and don't shift it, this may not be the case all the time, but the worst that will happen is we won't shift everything we possibly could have + afterPercent = false; + } + else + { + if ((*s >= 'a' && *s <= 'k') || (*s >= 'A' && *s <= 'Z')) + *d = (UTF16)(*s + 0xfee0); + else + *d = *s; + + if (*s == '%') // we can't be shifting formatting tokens or else sprintf won't work + afterPercent = true; + } + d++; + s++; + } + *d = 0; + + UTF16_convertToUTF8(utf16, fakeDestination, destinationSize); + } + + void FakeUtf8ToEnglish(char *englishDestination, char *fakeSource, size_t destinationSize) + { + UTF16 utf16[8192]; + UTF8_convertToUTF16(fakeSource, utf16, sizeof(utf16)); + + UTF16 *s = utf16; + char *d = englishDestination; + char *endDest = d + destinationSize - 1; + while (*s != 0 && d < endDest) + { + if (*s >= 0xfee0) + *d = (char)(*s - 0xfee0); + else + *d = (char)*s; + d++; + s++; + } + *d = 0; + } + + size_t UTF16_strlen( UTF16 * ptr ) + { + size_t count(0); + while(*ptr != 0) + { + ptr++; + count++; + } + return count; + } + + static const int utf8_lengths[16]= + { + 1,1,1,1,1,1,1,1, // 0000 to 0111 : 1 byte (plain ASCII) + 0,0,0,0, // 1000 to 1011 : not valid + 2,2, // 1100, 1101 : 2 bytes + 3, // 1110 : 3 bytes + 4 // 1111 :4 bytes + }; + + int UTF8_strlen(const char * source) + { + int wchar_length=0; + int code_size; + unsigned char byte; + if (source) + { + int byte_length = (int)strlen(source); + while (byte_length > 0) + { + byte=(unsigned char)*source; + // Use lookup table to determine sequence + // length based on upper 4 bits of first byte + if ((byte <= 0xF7) && (0 != (code_size=utf8_lengths[ byte >> 4]))) + { + // 1 sequence == 1 character + wchar_length++; + if (code_size==4) + { + wchar_length++; + } + source+=code_size; // increment pointer + byte_length-=code_size; // decrement counter + } + else + { + // invalid character encountered + return -1; + } + } + } + return wchar_length; + } + + int UTF8_truncate(char *output, const char *source, size_t limit) + { + int wchar_length=0; //number of utf-8 characters + int code_size; //number of bytes each wchar + int cur_bytes_count = 0; + unsigned char byte; + char *pout = output; + + if(limit <= 0) + return -1; + + if (source) + { + int byte_length = (int)strlen(source); + while (byte_length > 0) + { + byte=(unsigned char)*source; + // Use lookup table to determine sequence + // length based on upper 4 bits of first byte + if ((byte <= 0xF7) && (0 != (code_size=utf8_lengths[ byte >> 4]))) + { + // 1 sequence == 1 character + cur_bytes_count+=code_size; + + if(cur_bytes_count <= limit) + { + strncpy(pout, source, code_size); + } else { + break; + } + + wchar_length++; + if (code_size==4) + { + wchar_length++; + } + source+=code_size; // increment pointer + pout+=code_size; + byte_length-=code_size; // decrement counter + } + else + { + // invalid character encountered + return -1; + } + } + } + return wchar_length; + } +}; diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/utf8.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/utf8.h new file mode 100644 index 00000000..c785489e --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/Base/utf8.h @@ -0,0 +1,33 @@ +#ifndef UTF8_H +#define UTF8_H + +#include +//#include "Unicode.h" + +namespace soe +{ + +typedef char UTF8; +typedef unsigned short int UTF16; + +size_t UTF8_charSize( char * ); + +size_t UTF8_convertCharToUTF16( char * ptr , UTF16 * ret ); +size_t UTF8_convertToUTF16( char * ptr , UTF16 * ret, size_t limit ); +size_t UTF16_convertCharToUTF8( UTF16 ret, char * ptr , size_t limit ); +size_t UTF16_convertToUTF8( UTF16 * ret, char * ptr , size_t limit ); + +void EnglishToFakeUtf8(char *fakeDestination, char *englishSource, size_t destinationSize); +void FakeUtf8ToEnglish(char *englishDestination, char *fakeSource, size_t destinationSize); + +//Plat_Unicode::String UTF8ToUnicode(const char *source); +//void UnicodeToUTF8(Plat_Unicode::String &source, char *dest, int limit); +size_t UTF16_strlen( UTF16 * ptr ); +int UTF8_strlen( const char * ); + +//truncate UTF-8 +int UTF8_truncate( char *, const char*, size_t limit); + +}; +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/Clock.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/Clock.cpp new file mode 100644 index 00000000..2f5f47b4 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/Clock.cpp @@ -0,0 +1,119 @@ +#include "Clock.h" + +#include + +#ifdef WIN32 + #include +#else //WIN32 + #include + #include + #include +#endif + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +Clock::Clock() +: m_lastStart(0), + m_totalRunTime(0) +{ +} + +ClockStamp Clock::getCurTime() +{ +#if defined(WIN32) + static int sClockHigh = 0; + static ClockStamp sClockLast = 0; + + int high = sClockHigh; + DWORD low = GetTickCount(); + ClockStamp holdLast = sClockLast; // this should be interlocked too + ClockStamp ret = ((ClockStamp)high << 32) | low; + + // crazy trick to allow threading to work, by putting in a 1000 second fudge factor, we effective say + // that it is ok to time-slice us at a bad point and we will still handle it, provided that our thread + // gets processing time again within 1000 seconds + if (ret < holdLast - 1000000) + { + sClockHigh = high + 1; + ret = ((ClockStamp)high << 32) | low; + } + + sClockLast = ret; // this really should be interlocked to be totally safe since it is a 64 bit value, but I don't see a way to do that and am not sure it would mess up anything but the one call anyways + + return ret; +#else + struct timeval tv; + int err; + err = gettimeofday(&tv, NULL); + return (static_cast(tv.tv_sec) * 1000 + static_cast(tv.tv_usec / 1000)); +#endif +} + +ClockStamp Clock::getElapsedSinceLastStart() +{ + if (m_lastStart == 0) + { + //hasn't been started + return 0; + } + + ClockStamp elapsed = getCurTime() - m_lastStart; + + if (elapsed > 2000000000) // only time differences up to 23 days can be measured with this function + elapsed = 2000000000; + + return elapsed; +} + +void Clock::start() +{ + if (m_lastStart != 0) + { + //already started + return; + } + + //set last start to curtime + m_lastStart = getCurTime(); +} + +void Clock::stop() +{ + if (m_lastStart == 0) + { + //need to start before stoping + return; + } + + m_totalRunTime += (unsigned)getElapsedSinceLastStart(); //rlsmith - explicit cast to prevent compiler warning + m_lastStart = 0; +} + + +bool Clock::isDone(unsigned runTime) +{ + if (m_lastStart == 0) + { + //never started, so say no + return false; + } + + ClockStamp totalElapsed = getElapsedSinceLastStart() + m_totalRunTime; + + return (totalElapsed >= runTime); +} + +void Clock::reset() +{ + m_lastStart = 0; + m_totalRunTime = 0; +} + + + +#ifdef EXTERNAL_DISTRO +}; +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/Clock.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/Clock.h new file mode 100644 index 00000000..89eb2c2a --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/Clock.h @@ -0,0 +1,71 @@ +#ifndef CLOCK_H +#define CLOCK_H + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + + +#if defined(WIN32) + typedef __int64 ClockStamp; +#else + typedef long long ClockStamp; +#endif + +/** + * @brief A Clock can be used as a millisecond timer. + */ +class Clock +{ +public: + + /** + * @brief Creates a clock, must still be started with Clock::start method. + * + * Once created, a clock can be started, and stoped as often as possible. + */ + Clock(); + + + /** + * @brief Starts the timer running. + */ + void start(); + + /** + * @brief Stops the timer from running (note: can still be started again later). + */ + void stop(); + + /** + * @brief Tells you if the timer has been in the started state for longer than runTime. + * + * @param runTime The amount of time to test if this timer has ran longer than. + * + * @return 'true' if timer has ran for longer than or equal to runTime, false otherwise. + */ + bool isDone(unsigned runTime); + + /** + * @brief Resets this clock (as if it were never started). + */ + void reset(); + +private: + ClockStamp m_lastStart; + unsigned m_totalRunTime; + + ClockStamp getCurTime(); + ClockStamp getElapsedSinceLastStart(); +}; + + +#ifdef EXTERNAL_DISTRO +}; +#endif + + +#endif //CLOCK_H + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/IPAddress.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/IPAddress.cpp new file mode 100644 index 00000000..ae564017 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/IPAddress.cpp @@ -0,0 +1,33 @@ +#include "IPAddress.h" + +#if defined(WIN32) + #include + typedef int socklen_t; +#else // for non-windows platforms (linux) + #include + #include + #include +#endif + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +IPAddress::IPAddress(unsigned int ip) +: m_IP(ip) +{ +} + +char *IPAddress::GetAddress(char *buffer) const +{ + struct sockaddr_in addr; + addr.sin_addr.s_addr = m_IP; + strcpy(buffer, inet_ntoa(addr.sin_addr)); + return(buffer); +} + + +#ifdef EXTERNAL_DISTRO +}; +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/IPAddress.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/IPAddress.h new file mode 100644 index 00000000..f6d5f886 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/IPAddress.h @@ -0,0 +1,51 @@ +#ifndef TCPIPADDRESS_H +#define TCPIPADDRESS_H + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +/** + * @brief Container object for IP Address. + */ +class IPAddress +{ +public: + + /** + * @brief Constructor, sets the ip address if specified. + */ + IPAddress(unsigned int ip = 0); + + /** + * @brief Sets the ip address. + */ + void SetAddress(unsigned int ip){ m_IP = ip; } + + /** + * @brief Returns the unsigned int representation of this address. + */ + unsigned int GetAddress() const { return m_IP; } + + /** + * @brief Used to retreive the the dot-notation represenatatiion of this address. + * + * @param buffer A pointer to the buffer to place the ip address into. + * Must be at least 17 characters long, will be null terminated. + * + * @return A pointer to the buffer the address was placed into. + */ + char *GetAddress(char *buffer) const; + +private: + unsigned int m_IP; +}; + +#ifdef EXTERNAL_DISTRO +}; +#endif + +#endif //TCPIPADDRESS_H + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpBlockAllocator.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpBlockAllocator.cpp new file mode 100644 index 00000000..6d476d78 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpBlockAllocator.cpp @@ -0,0 +1,94 @@ +#include "TcpBlockAllocator.h" + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +TcpBlockAllocator::TcpBlockAllocator(const unsigned initSize, const unsigned initCount) +: m_freeHead(NULL), m_blockCount(initCount), m_blockSize(initSize), m_numAvailBlocks(0) +{ + realloc(); +} + +TcpBlockAllocator::~TcpBlockAllocator() +{ + while(m_freeHead) + { + data_block *tmp = m_freeHead; + m_freeHead = m_freeHead->m_next; + delete[] tmp->m_data; + delete tmp;m_numAvailBlocks--; + } +} + +data_block *TcpBlockAllocator::getBlock() +{ + data_block *tmp; + + if(!m_freeHead) + { + realloc(); + } + + tmp = m_freeHead; + m_freeHead = m_freeHead->m_next; + tmp->m_next = NULL; + m_numAvailBlocks--; + return(tmp); +} + +void TcpBlockAllocator::returnBlock(data_block *b) +{ + b->m_usedSize = 0; + b->m_sentSize = 0; + + if (m_numAvailBlocks >= m_blockCount) + { + delete[] b->m_data; + delete b; + return; + } + + b->m_next = m_freeHead; + m_freeHead = b; m_numAvailBlocks++; +} + +void TcpBlockAllocator::realloc() +{ + data_block *tmp = NULL, *cursor = NULL; + + tmp = new data_block; m_numAvailBlocks++; + cursor = tmp; + memset(cursor, 0, sizeof(data_block)); + cursor->m_data = new char[m_blockSize]; + cursor->m_totalSize = m_blockSize; + + for(unsigned i = 1; i < m_blockCount; i++) + { + cursor->m_next = new data_block; m_numAvailBlocks++; + cursor = cursor->m_next; + memset(cursor, 0, sizeof(data_block)); + cursor->m_data = new char[m_blockSize]; + cursor->m_totalSize = m_blockSize; + } + + if(m_freeHead) + { + cursor->m_next = m_freeHead; + m_freeHead = tmp; + } + else + { + m_freeHead = tmp; + } +} + + +#ifdef EXTERNAL_DISTRO +}; +#endif + + + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpBlockAllocator.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpBlockAllocator.h new file mode 100644 index 00000000..d34509a0 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpBlockAllocator.h @@ -0,0 +1,44 @@ +#ifndef TCPBLOCKALLOCATOR_H +#define TCPBLOCKALLOCATOR_H + +#include + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +struct data_block +{ + unsigned m_usedSize; + unsigned m_sentSize; + unsigned m_totalSize; + char *m_data; + data_block *m_next; +}; + +class TcpBlockAllocator +{ +public: + TcpBlockAllocator(const unsigned initSize, const unsigned initCount); + ~TcpBlockAllocator(); + data_block *getBlock(); + void returnBlock(data_block *); + +private: + void realloc(); + data_block *m_freeHead; + unsigned m_blockCount; + unsigned m_blockSize; + unsigned m_numAvailBlocks; +}; + + +#ifdef EXTERNAL_DISTRO +}; +#endif +#endif //TCPBLOCKALLOCATOR_H + + + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpConnection.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpConnection.cpp new file mode 100644 index 00000000..bbb2a432 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpConnection.cpp @@ -0,0 +1,792 @@ +#include "TcpConnection.h" +#include "TcpManager.h" +#include "Clock.h" +#include + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + + +//used when want to open new connection with this socket +TcpConnection::TcpConnection(TcpManager *tcpManager, TcpBlockAllocator *sendAlloc, TcpManager::TcpParams ¶ms, const IPAddress &destIP, unsigned short destPort, unsigned timeout) +: m_nextConnection(NULL), + m_prevConnection(NULL), + m_socket(INVALID_SOCKET), + m_nextKeepAliveConnection(NULL), + m_prevKeepAliveConnection(NULL), + m_aliveListId(tcpManager->m_aliveList.m_listID), + m_nextRecvDataConnection(NULL), + m_prevRecvDataConnection(NULL), + m_recvDataListId(tcpManager->m_dataList.m_listID), + m_manager(tcpManager), + m_status(StatusNegotiating), + m_handler(NULL), + m_destIP(destIP), + m_destPort(destPort), + m_refCount(0), + m_sendAllocator(sendAlloc), + m_head(NULL), + m_tail(NULL), + m_bytesRead(0), + m_bytesNeeded(0), + m_params(params), + m_recvBuff(NULL), + m_connectTimeout(timeout), + m_connectTimer(), + m_wasConRemovedFromMgr(false), + m_connectionRefused(false) +{ + //start connection timer + m_connectTimer.start(); + + memset(&m_addr, 0, sizeof(m_addr)); + if (m_params.maxRecvMessageSize != 0) + { + m_recvBuff = new char[m_params.maxRecvMessageSize]; + } + + m_socket = socket(AF_INET, SOCK_STREAM, 0); + + + setOptions(); + + + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons(m_destPort); + m_addr.sin_addr.s_addr = m_destIP.GetAddress(); + + int err = connect(m_socket, (sockaddr *)&m_addr, sizeof(m_addr)); + + if(err == SOCKET_ERROR) + { +#ifdef WIN32 + int sockerr = WSAGetLastError(); + if(sockerr != WSAEWOULDBLOCK) + { + //a real error + m_status = StatusDisconnected; + } + else + { + m_status = StatusNegotiating; + } + +#else // UNIX + if (errno != EINPROGRESS) + { + m_status = StatusDisconnected; + } + else + { + m_status = StatusNegotiating; + } +#endif + } + else + { + //we are connected, wow + m_status = StatusConnected; + } + +} + +//used when server mode creates new connection object representing a connect request +TcpConnection::TcpConnection(TcpManager *tcpManager, TcpBlockAllocator *sendAlloc, TcpManager::TcpParams ¶ms, SOCKET socket, const IPAddress &destIP, unsigned short destPort) +: m_nextConnection(NULL), + m_prevConnection(NULL), + m_socket(socket), + m_nextKeepAliveConnection(NULL), + m_prevKeepAliveConnection(NULL), + m_aliveListId(tcpManager->m_aliveList.m_listID), + m_nextRecvDataConnection(NULL), + m_prevRecvDataConnection(NULL), + m_recvDataListId(tcpManager->m_dataList.m_listID), + m_manager(tcpManager), + m_status(StatusConnected), + m_handler(NULL), + m_destIP(destIP), + m_destPort(destPort), + m_refCount(0), + m_sendAllocator(sendAlloc), + m_head(NULL), + m_tail(NULL), + m_bytesRead(0), + m_bytesNeeded(0), + m_params(params), + m_recvBuff(NULL), + m_connectTimeout(0), + m_connectTimer(), + m_wasConRemovedFromMgr(false) +{ + memset(&m_addr, 0, sizeof(m_addr)); + if (m_params.maxRecvMessageSize != 0) + { + m_recvBuff = new char[m_params.maxRecvMessageSize]; + } + + + setOptions(); +} + +void TcpConnection::setOptions() +{ + if (m_socket != INVALID_SOCKET) + { +#if defined(WIN32) + unsigned long isNonBlocking = 1; + int outBufSize = m_params.outgoingBufferSize; + int inBufSize = m_params.incomingBufferSize; + int keepAlive = 1; + int reuseAddr = 1; + struct linger ld; + ld.l_onoff = 0; + ld.l_linger = 0; + + if (ioctlsocket(m_socket, FIONBIO, &isNonBlocking) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&outBufSize, sizeof(outBufSize)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&inBufSize, sizeof(inBufSize)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepAlive, sizeof(keepAlive)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseAddr, sizeof(reuseAddr)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (char *)&ld, sizeof(ld)) != 0 ) + { + //bummer, but no need to crash now.... ? + } + +#else // linux is to remain the default compile mode + unsigned long isNonBlocking = 1; + unsigned long keepAlive = 1; + unsigned long outBufSize = m_params.outgoingBufferSize; + unsigned long inBufSize = m_params.incomingBufferSize; + unsigned long reuseAddr = 1; + struct linger ld; + ld.l_onoff = 0; + ld.l_linger = 0; + + if (ioctl(m_socket, FIONBIO, &isNonBlocking) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, &outBufSize, sizeof(outBufSize)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, &inBufSize, sizeof(inBufSize)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_LINGER, &ld, sizeof(ld)) != 0) + { + //bummer, but no need to crash now.... ? + } +#endif + } + +} + +int TcpConnection::finishConnect() +{ + AddRef(); + int returnVal = 0; + m_connectionRefused = false; + /**< returns < 0 if fatal error and connect will not work, =0 if need more time, >0 if connect completed */ + switch (m_status) + { + case StatusDisconnected: + { + //something went wrong + Disconnect(false); + returnVal = -1; + } + break; + case StatusNegotiating: + { +#ifdef WIN32 + //try to finish connection + fd_set wrSet; + FD_ZERO(&wrSet); + + FD_SET(m_socket, &wrSet); + + timeval t; + t.tv_sec = 0; + t.tv_usec = 0; + + int err = select(m_socket + 1, NULL, &wrSet, NULL, &t); + + if (err == 0) + { + //needs more time + returnVal = 0; + } + else if (err == SOCKET_ERROR) + { + //huhoh, let's hope it needs more time + int sockerr = WSAGetLastError(); + if (sockerr == WSAEINPROGRESS + || sockerr == WSAEWOULDBLOCK + || sockerr == WSAEALREADY + || sockerr == WSAEINVAL) + { + //yep + returnVal = 0; + } + else + { + Disconnect(false); + returnVal = -1; + } + } + else + { + //check if write bit set for socket + if (FD_ISSET(m_socket, &wrSet)) + { + //connection complete + m_status = StatusConnected; + returnVal = 1; + } + else + { + //give it more time?? + returnVal = 0; + } + } + +#else // not WIN32 + int err = connect(m_socket, (sockaddr *)&m_addr, sizeof(m_addr)); + + if(err == SOCKET_ERROR) + { + m_connectionRefused = (errno == ECONNREFUSED); + if (errno != EINPROGRESS && errno != EALREADY) + { + /* if (errno == ECONNREFUSED) */ Disconnect(true); + /* else Disconnect(false); -- Don't do this here; bad! */ + returnVal = -1;//failure + } + else + { + returnVal = 0;//need to wait + } + } + else + { + m_status = StatusConnected; + returnVal = 1;//connect success + } +#endif + } + break; + case StatusConnected: + { + //wierd, shouldn't be trying to do this here + Disconnect(true); + returnVal = -1; + } + break; + } + + if (returnVal == 0 && m_connectTimeout != 0 && m_connectTimer.isDone(m_connectTimeout)) + { + Disconnect(true); + returnVal = -1; + } + else if (returnVal ==1/* && m_connectTimeout != 0*/) + { + //need to give, onConnect callback + if (m_handler) + m_handler->OnConnectRequest(this); + } + + Release(); + return returnVal; + +} + + +TcpConnection::~TcpConnection() +{ + if (m_recvBuff != NULL) + { + delete [] m_recvBuff; + } + + while(m_head != NULL) + { + data_block *tmp = m_head; + m_head = m_head->m_next; + m_sendAllocator->returnBlock(tmp); + } + + //TODO: need to notify app if are currently connected +} + +void TcpConnection::Send(const char *data, unsigned int dataLen) +{ +//add msg to buf + int totalLen = dataLen + sizeof(int); + + if(m_status == StatusDisconnected) + { + return; + } + + if (m_params.keepAliveDelay > 0 && m_aliveListId == m_manager->m_aliveList.m_listID) + { + m_aliveListId = m_manager->m_keepAliveList.m_listID; + + if (m_prevKeepAliveConnection != NULL) + m_prevKeepAliveConnection->m_nextKeepAliveConnection = m_nextKeepAliveConnection; + if (m_nextKeepAliveConnection != NULL) + m_nextKeepAliveConnection->m_prevKeepAliveConnection = m_prevKeepAliveConnection; + if (m_manager->m_keepAliveList.m_beginList == this) + m_manager->m_keepAliveList.m_beginList = m_nextKeepAliveConnection; + + m_nextKeepAliveConnection = m_manager->m_aliveList.m_beginList; + m_prevKeepAliveConnection = NULL; + if (m_manager->m_aliveList.m_beginList != NULL) + m_manager->m_aliveList.m_beginList->m_prevKeepAliveConnection = this; + m_manager->m_aliveList.m_beginList = this; + } + + + data_block *work = NULL; + + // this connection has no send buffer. Get a block + if(!m_tail) + { + m_head = m_sendAllocator->getBlock(); + m_tail = m_head; + } + work = m_tail; + + //send message len first + unsigned nLen = htonl(totalLen); + unsigned lenLength = sizeof(int); + unsigned lenIndex = 0; + while(lenIndex < lenLength) + { + if ((lenLength - lenIndex) <= (work->m_totalSize - work->m_usedSize)) + { + //size will fit in this block + memcpy(work->m_data + work->m_usedSize, (char *)(&nLen) + lenIndex, lenLength - lenIndex); + work->m_usedSize += (lenLength - lenIndex); + lenIndex += (lenLength - lenIndex); + } + else + { + //size will not fit in this block + memcpy(work->m_data + work->m_usedSize, (char *)(&nLen) + lenIndex, work->m_totalSize - work->m_usedSize); + lenIndex += work->m_totalSize - work->m_usedSize; + work->m_usedSize += work->m_totalSize - work->m_usedSize; + work->m_next = m_sendAllocator->getBlock(); + work = work->m_next; + m_tail = work; + } + } + + //now send message payload + unsigned messageIndex = 0; + while(messageIndex < dataLen) + { + if((dataLen - messageIndex) <= (work->m_totalSize - work->m_usedSize)) + { + // data will fit in this block + memcpy(work->m_data + work->m_usedSize, data + messageIndex, (dataLen - messageIndex)); + work->m_usedSize += (dataLen - messageIndex); + messageIndex += (dataLen - messageIndex); + } + else + { + // data will not fit in this block. Fill this block and get another block + memcpy(work->m_data + work->m_usedSize, data + messageIndex, work->m_totalSize - work->m_usedSize); + messageIndex += work->m_totalSize - work->m_usedSize; + work->m_usedSize += work->m_totalSize - work->m_usedSize; + work->m_next = m_sendAllocator->getBlock(); + work = work->m_next; + m_tail = work; + } + } + + + return; +} + + +void TcpConnection::Disconnect(bool notifyApplication) +{ + AddRef(); + m_status = StatusDisconnected; + if (!m_wasConRemovedFromMgr) + { + m_manager->removeConnection(this); + m_wasConRemovedFromMgr = true; + } + + + if(m_socket != INVALID_SOCKET) + { +#if defined(WIN32) + closesocket(m_socket); +#else + close(m_socket); +#endif + m_socket = INVALID_SOCKET; + } + + + if (notifyApplication && m_handler) + m_handler->OnTerminated(this); + + Release(); +} + + +void TcpConnection::AddRef() +{ + m_refCount++; +} + + +void TcpConnection::Release() +{ + if (--m_refCount == 0) + { + //make sure manager knows I'm gone + if (m_status != StatusDisconnected) + { + m_refCount = 1; + Disconnect(false); + m_refCount = 0; + } + delete this; + } +} + +int TcpConnection::processIncoming() +{ +/**< returns < 0 if fatal error and socket has been closed, + =0 if read anything (full or partial message), + >0 if nothing to read now, or would block so shouldn't try again immediately. */ + + if (m_status != StatusConnected) + { + //wait until connect succeeds + return 1; + } + + if (m_params.noDataTimeout > 0 && m_recvDataListId == m_manager->m_dataList.m_listID) + { + m_recvDataListId = m_manager->m_noDataList.m_listID; + + if (m_prevRecvDataConnection != NULL) + m_prevRecvDataConnection->m_nextRecvDataConnection = m_nextRecvDataConnection; + if (m_nextRecvDataConnection != NULL) + m_nextRecvDataConnection->m_prevRecvDataConnection = m_prevRecvDataConnection; + if (m_manager->m_noDataList.m_beginList == this) + m_manager->m_noDataList.m_beginList = m_nextRecvDataConnection; + + m_nextRecvDataConnection = m_manager->m_dataList.m_beginList; + m_prevRecvDataConnection = NULL; + if (m_manager->m_dataList.m_beginList != NULL) + m_manager->m_dataList.m_beginList->m_prevRecvDataConnection = this; + m_manager->m_dataList.m_beginList = this; + } + + + int newMsg = 0; + + + if (m_bytesRead < sizeof(int)) + { + //new msg + newMsg = 1; + //printf("socket: %d\n", m_socket); + int ret = recv(m_socket, ((char *)(&m_bytesNeeded) + m_bytesRead), + 4 - m_bytesRead, 0); + //fprintf(stderr, "READ: %d\n", ret); + if (ret == 0) + { + //We did a select, so there should be data. Socket was closed. + Disconnect(); + return -1; + } + else if (ret == -1) + { + if (translateRecvSocketEror()) + { + //fatal error + return -1; + } + else + { + //need to wait + return 1; + } + } + else + { + m_bytesRead += ret; + if (m_bytesRead < 4) + { + return 1;//need to wait + } + else + { + + m_bytesNeeded = ntohl(m_bytesNeeded); + + //printf("m_bytesNeeded = %i\n", m_bytesNeeded); + if (m_bytesNeeded == sizeof(int)) + { + //keepalive, ignore + m_bytesRead = 0; + m_bytesNeeded = 0; + return 0; + } + else if (m_bytesNeeded < sizeof(int)) + { + //major protocol violation + Disconnect(); + return -1; + } + else if (m_params.maxRecvMessageSize == 0) + { + if (m_recvBuff!=NULL) + delete [] m_recvBuff; + m_recvBuff = new char[m_bytesNeeded-4]; + } + else if (m_params.maxRecvMessageSize != 0 && (m_bytesNeeded-4) > m_params.maxRecvMessageSize) + { + //error, maxRecvMeessageSize exceeded, Disconnect + Disconnect(); + return -1; + } + } + } + } + + int msgBytesRead = m_bytesRead - 4; + int msgBytesNeeded = m_bytesNeeded - 4; + + int ret = recv(m_socket, (char *)(m_recvBuff + msgBytesRead), + msgBytesNeeded - msgBytesRead, 0); + if (ret == 0 && !newMsg) + { + //We did a select, so there should be data. Socket was closed. + Disconnect(); + return -1; + } + if (ret == -1) + { + if (translateRecvSocketEror()) + { + //fatal error + return -1; + } + else + { + //need to wait + return 1; + } + } else + { + m_bytesRead += ret; + } + + if (m_bytesRead == m_bytesNeeded) + { + m_bytesRead = 0; + m_bytesNeeded = 0; + if (m_handler) + { + AddRef();//could get deleted during this callback + m_handler->OnRoutePacket(this, (unsigned char *)m_recvBuff, msgBytesNeeded); + + if (m_status == StatusDisconnected) + { + Release(); + return -1; + } + Release(); + } + + //entire message received + return 0; + } + else + { + return 1;//couldn't get entire msg + } +} + +int TcpConnection::processOutgoing() +{ +/**< returns < 0 if fatal error and socket has been closed, + =0 if sent data, call again immediately if want to, + >0 may have sent data, but calling again would do no good because there is either no more data to send, or would block. */ + if (m_status != StatusConnected) + { + //wait until connect succeeds + return 0; + } + + + + int sendError = 1; + + // If m_head is not null, then this connection has something to send + + + if(m_head) + { + + + int amt = ::send(m_socket, m_head->m_data + m_head->m_sentSize, m_head->m_usedSize - m_head->m_sentSize, 0); + if(amt < 0) + { +#ifdef WIN32 + switch(WSAGetLastError()) + { + case WSAEWOULDBLOCK: + case WSAEINTR: + case WSAEINPROGRESS: + case WSAEALREADY: + case WSA_IO_PENDING: + case WSA_NOT_ENOUGH_MEMORY: + case WSATRY_AGAIN: + //try again + sendError = 1; + break; + default: + //assume broken, Disconnect + Disconnect(); + sendError = -1; + break; + } +#else //not WIN32 + + // error condition, EAGAIN is recoverable, otherwise raise an error condition. Break from loop + switch(errno) + { + case EAGAIN: + //try again + sendError = 1; + break; + default: + //assume broken, Disconnect + Disconnect(); + sendError = -1; + break; + } +#endif + } + else if(static_cast(amt) < (m_head->m_usedSize - m_head->m_sentSize)) + { + // partial send: trying to do anything more now would be a waste of time. Break from loop + m_head->m_sentSize += amt; + sendError = 1; + } + else if(amt == 0) + { + Disconnect(); + sendError = -1; + // client closed connection + } + else + { + // everything was sent from this block. Return it to the pool, advance m_head. Attempt to continue + // sending + data_block *tmp = m_head; + if(m_tail == m_head) + { + m_tail = m_tail->m_next; + m_head = m_head->m_next; + } + else + { + m_head = m_head->m_next; + } + m_sendAllocator->returnBlock(tmp); + + sendError = 0; + } + } + + return sendError; +} + + +bool TcpConnection::translateRecvSocketEror() +{ +/**< returns false if fatal error and socket has been closed, + true if should try again. */ + bool fatalError=false; +#ifdef WIN32 + + int lastErr = WSAGetLastError(); + switch(lastErr) + { + case WSAENOBUFS: + case WSAEINPROGRESS: + case WSAEINTR: + case WSAEWOULDBLOCK: + case WSABASEERR: + fatalError=false; + break; + + case WSANOTINITIALISED: + case WSAENETDOWN: + case WSAEFAULT: + case WSAENOTCONN: + case WSAENETRESET: + case WSAENOTSOCK: + case WSAEOPNOTSUPP: + case WSAESHUTDOWN: + case WSAEMSGSIZE: + case WSAEINVAL: + case WSAECONNABORTED: + case WSAETIMEDOUT: + case WSAECONNRESET: + default: + //fatal + fatalError=true; + Disconnect(); + break; + } + +#else //not WIN32 + + switch(errno) + { + case EWOULDBLOCK: + case EINTR: + case ETIMEDOUT: + case ENOBUFS: + //try later + fatalError=false; + break; + + case EBADF: + case ECONNRESET: + case EFAULT: + case EINVAL: + case ENOTCONN: + case ENOTSOCK: + case EOPNOTSUPP: + case EPIPE: + case EIO: + case ENOMEM: + case ENOSR: + default: + //fatal + fatalError=true; + Disconnect(); + break; + } +#endif + + + return fatalError; +} + +#ifdef EXTERNAL_DISTRO +}; +#endif + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpConnection.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpConnection.h new file mode 100644 index 00000000..36a41e9f --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpConnection.h @@ -0,0 +1,160 @@ +#ifndef TCPCONNECTION_H +#define TCPCONNECTION_H + + +#include "TcpHandlers.h" +#include "TcpManager.h" +#include "IPAddress.h" +#include "TcpBlockAllocator.h" +#include "Clock.h" + +#if defined(WIN32) + #include + typedef int socklen_t; +#else // for non-windows platforms (linux) + #include + #include + #include + #include + #include + #include + #include +#endif + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + + +/** + * @brief Manages a single connection. + */ +class TcpConnection +{ +public: + + /** + * @brief The connection status. + */ + enum Status { + StatusNegotiating, /**< Currently attempting to connect. */ + StatusConnected, /**< Currently connected. */ + StatusDisconnected /**< Currently disconnected. */ + }; + + /** + * @brief Sets the handler object which will receive callback methods. + * + * To have the TcpConnection call your object directly when packets are received, and when the + * connection is disconnected, you simply need to derive your class + * (multiply if necessary) from TcpConnectionHandler, then you can use + * this method to set the object the TcpConnection will call as appropriate. + * default = NULL (no callbacks made) + * + * @param handler The object which will be called for notifications. + */ + void SetHandler(TcpConnectionHandler *handler){ m_handler = handler; } + + /** + * @brief Returns the handler associated with this object. + */ + TcpConnectionHandler *GetHandler(){ return m_handler; } + + /** + * @brief Returns the current status of this connection. + */ + Status GetStatus(){ return m_status; } + + /** + * @brief Queues a message to be sent on this connection. + */ + void Send(const char *data, unsigned dataLen); + + /** + * @brief Disconnects and recycles the socket. + * + * @param notifyApplication primarily used internally, but when set to 'true', it will cause the application + * to be called back via the onTerminated handler due to this call (the callback will not occur if the connection was + * already disconnected) + */ + void Disconnect(bool notifyApplication=true); + + /** + * @brief Returns the ip on the other side of this connection. + */ + IPAddress GetDestinationIp(){ return m_destIP; } + + /** + * @brief Returns the port on the other side of this conection. + */ + unsigned short GetDestinationPort(){ return m_destPort; } + + /** + * @brief Standard AddRef/Release scheme + */ + void AddRef(); + + /** + * @brief Standard AddRef/Release scheme + */ + void Release(); + + bool wasRemovedFromMgr() { return m_wasConRemovedFromMgr; } + void setRemovedFromMgr() { m_wasConRemovedFromMgr = true; } + + bool isConnectionRefused() const { return m_connectionRefused; } + +protected: + friend class TcpManager; + TcpConnection(TcpManager *tcpManager, TcpBlockAllocator *sendAlloc, TcpManager::TcpParams ¶ms, const IPAddress &destIP, unsigned short destPort, unsigned timeout); + int finishConnect();/**< returns < 0 if fatal error and connect will not work, =0 if need more time, >0 if connect completed */ + TcpConnection(TcpManager *tcpManager, TcpBlockAllocator *sendAlloc, TcpManager::TcpParams ¶ms, SOCKET socket, const IPAddress &destIP, unsigned short destPort); + TcpConnection *m_nextConnection; /**< Double linked list imp. */ + TcpConnection *m_prevConnection; /**< Double linked list imp. */ + SOCKET m_socket; + int processOutgoing();/**< returns < 0 if fatal error and socket has been closed, =0 if sent data, call again immediately if want to, >0 may have sent data, but calling again would do no good because there is either no more data to send, or would block. */ + int processIncoming();/**< returns < 0 if fatal error and socket has been closed, =0 if read anything (full or partial message), >0 if nothing to read now, or would block so shouldn't try again immediately. */ + + + TcpConnection *m_nextKeepAliveConnection; /**< Double linked list imp. */ + TcpConnection *m_prevKeepAliveConnection; /**< Double linked list imp. */ + int m_aliveListId; + + TcpConnection *m_nextRecvDataConnection; + TcpConnection *m_prevRecvDataConnection; + int m_recvDataListId; + +private: + ~TcpConnection(); + void setOptions(); + TcpManager *m_manager; + bool translateRecvSocketEror(); + Status m_status; + TcpConnectionHandler *m_handler; + IPAddress m_destIP; + unsigned short m_destPort; + unsigned m_refCount; + TcpBlockAllocator *m_sendAllocator; + data_block *m_head; + data_block *m_tail; + unsigned m_bytesRead; + unsigned m_bytesNeeded; + TcpManager::TcpParams m_params; + char *m_recvBuff; + sockaddr_in m_addr; + unsigned m_connectTimeout; + Clock m_connectTimer; + bool m_connectionRefused; + + bool m_wasConRemovedFromMgr; +}; + +#ifdef EXTERNAL_DISTRO +}; +#endif + +#endif //TCPCONNECTION_H + + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpHandlers.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpHandlers.h new file mode 100644 index 00000000..782b70af --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpHandlers.h @@ -0,0 +1,52 @@ +#ifndef TCPHANDLERS_H +#define TCPHANDLERS_H + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +class TcpConnection; + +/** + * @brief Interface used by TcpManager class for notification to application of connection state/etc. + * + * Note: these callbacks will only be made when during a call to TcpManager::giveTime. + */ +class TcpManagerHandler +{ + public: + /** + * @brief Callback made when a new connection has been established by the manager. + */ + virtual void OnConnectRequest(TcpConnection *con)=0; + +}; + +/** + * @brief Interface used by TcpConnection class for notification to application of connection state/etc. + * + * Note: these callbacks will only be made when during a call to TcpManager::giveTime. + */ +class TcpConnectionHandler +{ + public: + /** + * @brief Callback made when a new message has been received on the specified connection. + */ + virtual void OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen)=0; + + /** + * @brief Callback made when the specified connection has closed, or been closed. + */ + virtual void OnTerminated(TcpConnection *con)=0; + + virtual void OnConnectRequest(TcpConnection *con)=0; +}; + + +#ifdef EXTERNAL_DISTRO +}; +#endif +#endif //TCPHANDLERS_H + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpListener.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpListener.cpp new file mode 100644 index 00000000..8ae72bac --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpListener.cpp @@ -0,0 +1,435 @@ + +//This code is not used by anything intentionally +//The Connection class was getting mistakenly linked in +//instead of the SWG Connection class that is used for +//all of our connections. Removing the whole thing +//to prevent the issue from coming up again. +#if 0 + +#ifdef WIN32 +#pragma warning (disable: 4786) +#endif + +#include +#include "TcpConnection.h" +#include "TcpListener.h" + +#ifdef WIN32 +#define snprintf _snprintf +#endif + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +//////////////////////////////////////////////////////////////////////////////// + +Listener::QueueNode::QueueNode() : + connection(0), + request(0) +{ +} + +Listener::QueueNode::QueueNode(Connection * con, RequestBase * req) : + connection(con), + request(req) +{ +} + +Listener::Listener() : + mParams(), + mTcpManager(0), + mConnections(), + mConnectionCount(0), + mClosedConnections(), + mQueuedRequests(), + mActiveRequests(), + mActiveCount(0), + mActiveMax(0), + mAcceptingNewConnections(true) +{ + //printf("ctor 0x%x Listener\n",this); +} + +Listener::~Listener() +{ + std::set::iterator iterator; + for (iterator = mConnections.begin(); iterator != mConnections.end(); iterator++) + { + Connection * connection = *iterator; + delete connection; + } + mConnections.clear(); + + if (mTcpManager != 0) + { + mTcpManager->Release(); + mTcpManager = 0; + } + + //printf("dtor 0x%x Listener\n",this); +} + +void Listener::RequestSleep(RequestBase * request) +{ + mSleepingRequests.insert(request); +} + +void Listener::RequestWake(RequestBase * request) +{ + if (mSleepingRequests.erase(request)) + { + QueueNode node(request->mConnection, request); + mActiveRequests.push_front(node); + } +} + +void Listener::OnConnectRequest(TcpConnection * connection) +{ + if (IsAcceptingNewConnections()) + { + Connection * connectionObject = new Connection(*this, connection); + + mConnections.insert(connectionObject); + mConnectionCount++; + + OnConnectionOpened(connectionObject); + } +} + +void Listener::QueueRequest(Connection * connection, RequestBase * request) +{ + if (connection) + { + // normal request, internal requests have no connection + connection->NotifyQueuedRequest(request); + } + mQueuedRequests.push_back(QueueNode(connection,request)); +} + +bool Listener::IsIdle() const +{ + return (!IsActive() && !mTcpManager && mQueuedRequests.empty() && !mActiveCount); +} + +bool Listener::IsAcceptingNewConnections() const +{ + return (IsActive() && mAcceptingNewConnections); +} + +unsigned Listener::Process() +{ + //Profile profile("Listener::Process"); + //////////////////////////////////////// + // handle inactive state (with UdpManager) + if (!IsActive() && mTcpManager) + { + // check all connections to see if they are idle + std::set::iterator iterator; + for (iterator = mConnections.begin(); iterator != mConnections.end(); iterator++) + { + Connection * connection = *iterator; + if (connection->IsConnected()) + connection->Disconnect(); + } + // close the UdpManager if all the connections are closed + if (!mConnectionCount) + { + mTcpManager->Release(); + mTcpManager = 0; + OnShutdown(); + } + } + //////////////////////////////////////// + // handle active state (without UdpManager) + else if (IsActive() && !mTcpManager) + { + mParams = GetConnectionParams(); + mActiveMax = GetActiveRequestMax(); + mTcpManager = new TcpManager(mParams); + mTcpManager->SetHandler(this); + if (mTcpManager->BindAsServer()){ + OnStartup(); + }else{ + OnFailedStartup(); + return 0; + } + } + + //////////////////////////////////////// + // process the TcpManager + if (mTcpManager) + { + //Profile subProfile("TcpManager::GiveTime()"); + mTcpManager->GiveTime(); + } + + // check all closed connections to see if they are idle + std::list::iterator closedIterator = mClosedConnections.begin(); + while (closedIterator != mClosedConnections.end()) + { + //Profile profile("Listener::Process (cleanup connection)"); + std::list::iterator current = closedIterator++; + Connection * connection = *current; + if (!connection->GetActiveRequests() && + !connection->GetQueuedRequests()) + { + mClosedConnections.erase(current); + mConnections.erase(connection); + mConnectionCount--; + OnConnectionDestroyed(connection); + delete connection; + } + } + + //////////////////////////////////////// + // process request queue + while (!mQueuedRequests.empty() && (!mActiveMax || mActiveCount < mActiveMax)) + { + //Profile profile("Listener::Process (activate queued request)"); + QueueNode & node = mQueuedRequests.front(); + if (!IsActive()) + { + // If not active, discard queued request + if (node.connection) + { + // normal request, internal requests have no connection + node.connection->NotifyDiscardRequest(node.request); + } + DestroyRequest(node.request); + } + else + { + // Move request to active list + if (node.connection) + { + // normal request, internal requests have no connection + node.connection->NotifyBeginRequest(node.request); + } + mActiveRequests.push_back(node); + mActiveCount++; + } + mQueuedRequests.pop_front(); + } + + //////////////////////////////////////// + // Process active requests + unsigned requestsProcessed = 0; + std::list::iterator iterator = mActiveRequests.begin(); + while (iterator != mActiveRequests.end()) + { + //Profile profile("Listener::Process (process request)"); + std::list::iterator current = iterator++; + RequestBase * request = current->request; + Connection * connection = current->connection; + + if (request->Process()) + { + if (connection) + { + // normal request, internal requests have no connection + connection->NotifyEndRequest(request); + } + DestroyRequest(request); + mActiveRequests.erase(current); + mActiveCount--; + } + else if (mSleepingRequests.find(request) != mSleepingRequests.end()) + { + mActiveRequests.erase(current); + } + requestsProcessed++; + } + return requestsProcessed; +} + +/* +void Listener::GetStats(UdpManagerStatistics & statsStruct) +{ + if (mTcpManager) + mTcpManager->GetStats(&statsStruct); +} + +void Listener::ResetStats() +{ + if (mTcpManager) + mTcpManager->ResetStats(); +} +*/ + +void Listener::OnStartup() +{ +} + +void Listener::OnShutdown() +{ +} + +void Listener::OnFailedStartup() +{ +} + +void Listener::OnConnectionOpened(Connection * connection) +{ +} + +void Listener::OnConnectionClosed(Connection * connection, const char * reason) +{ +} + +void Listener::OnConnectionDestroyed(Connection * connection) +{ +} + +void Listener::OnCrcReject(Connection *connection, const unsigned char * buffer, unsigned size) +{ +} + +/* +void Listener::OnPacketCorrupt(Connection *con, const unsigned char *data, int dataLen, UdpCorruptionReason reason) +{ +} +*/ + +//////////////////////////////////////////////////////////////////////////////// + + +Connection::Connection(Listener & listener, TcpConnection * connection) : + mListener(listener), + mConnection(connection), + mHost(), + mHostIp(0), + mDisconnectReason(0), + mQueuedRequests(0), + mActiveRequests(0) +{ + mConnection->AddRef(); + mConnection->SetHandler(this); + + char buffer[256]; + char addr[32]; + mHostIp = mConnection->GetDestinationIp().GetAddress(); + mConnection->GetDestinationIp().GetAddress(addr); + snprintf(buffer, sizeof(buffer), "%s:%u", addr, mConnection->GetDestinationPort()); + mHost = buffer; + + //printf("ctor 0x%x connection\n",this); +} + +Connection::~Connection() +{ + Disconnect(); + + //printf("dtor 0x%x connection\n",this); +} + +unsigned Connection::Send(const unsigned char * data, unsigned dataLen) +{ + if (mConnection) + { + mConnection->Send((const char*)data, dataLen); + return dataLen; + } + return 0; +} + +void Connection::Disconnect() +{ + if (mConnection) + OnTerminated(mConnection); +} + +bool Connection::IsConnected() const +{ + return mConnection != 0; +} + +const std::string & Connection::GetHost() const +{ + return mHost; +} + +const unsigned Connection::GetHostIP() const +{ + return mHostIp; +} + +unsigned Connection::GetQueuedRequests() const +{ + return mQueuedRequests; +} + +unsigned Connection::GetActiveRequests() const +{ + return mActiveRequests; +} + +void Connection::OnTerminated(TcpConnection *con) +{ + mConnection->SetHandler(0); + mConnection->Disconnect(); + //TcpConnection::DisconnectReason disconnectReason = mConnection->GetDisconnectReason(); + mConnection->Release(); + mConnection = 0; + mListener.mClosedConnections.push_back(this); + mListener.OnConnectionClosed(this, "Test"/*TcpConnection::DisconnectReasonText(disconnectReason)*/); +} + +void Connection::OnRoutePacket(TcpConnection *, const unsigned char * data, int dataLen) +{ + mListener.OnReceive(this, data, dataLen); +} + +void Connection::OnCrcReject(TcpConnection *, const unsigned char * data, int dataLen) +{ + mListener.OnCrcReject(this, data, dataLen); +} + +/* +void Connection::OnPacketCorrupt(TcpConnection *, const uchar *data, int dataLen, UdpCorruptionReason reason) +{ + mListener.OnPacketCorrupt(this, data, dataLen, reason); +} +*/ + +void Connection::NotifyQueuedRequest(RequestBase * request) +{ + mQueuedRequests++; +} + +void Connection::NotifyDiscardRequest(RequestBase * request) +{ + mQueuedRequests--; +} + +void Connection::NotifyBeginRequest(RequestBase * request) +{ + mQueuedRequests--; + mActiveRequests++; +} + +void Connection::NotifyEndRequest(RequestBase * request) +{ + mActiveRequests--; +} + + +//////////////////////////////////////////////////////////////////////////////// + + +RequestBase::RequestBase(Connection * connection, bool isInternal) : + mConnection(connection), + mProcessState(0), + mIsInternal(isInternal) +{ +} + +RequestBase::~RequestBase() +{ +} +#ifdef EXTERNAL_DISTRO +} +#endif + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpListener.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpListener.h new file mode 100644 index 00000000..4b6a680d --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpListener.h @@ -0,0 +1,160 @@ + +//This code is not used by anything intentionally +//The Connection class was getting mistakenly linked in +//instead of the SWG Connection class that is used for +//all of our connections. Removing the whole thing +//to prevent the issue from coming up again. +#if 0 + + +#ifndef TCP_LISTENER_H +#define TCP_LISTENER_H + +#include +#include +#include + +#include "TcpManager.h" + + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +//////////////////////////////////////////////////////////////////////////////// + + class RequestBase; + class Connection; + class Listener : public TcpManagerHandler + { + friend class Connection; + struct QueueNode + { + QueueNode(); + QueueNode(Connection * connection, RequestBase * request); + bool operator<(const QueueNode & rhs) { return request < rhs.request; } + + Connection * connection; + RequestBase * request; + }; + + public: + Listener(); + virtual ~Listener(); + + void QueueRequest(Connection * connection, RequestBase * request); + bool IsIdle() const; + bool IsAcceptingNewConnections() const; + unsigned Process(); + void SetAcceptingNewConnections(bool value) { mAcceptingNewConnections = value; } + //void GetStats(TcpManagerStatistics & statsStruct); + //void ResetStats(); + + unsigned GetNumberQueuedRequests() { return (unsigned)mQueuedRequests.size(); } + + void RequestSleep(RequestBase * request); + void RequestWake(RequestBase * request); + + virtual bool IsActive() const = 0; + virtual unsigned GetActiveRequestMax() = 0; + virtual TcpManager::TcpParams GetConnectionParams() = 0; + + virtual void OnStartup(); + virtual void OnShutdown(); + virtual void OnFailedStartup(); + virtual void OnConnectionOpened(Connection * connection); + virtual void OnConnectionClosed(Connection * connection, const char * reason); + virtual void OnConnectionDestroyed(Connection * connection); + virtual void OnReceive(Connection * connection, const unsigned char * data, unsigned dataLen) = 0; + virtual void OnCrcReject(Connection *connection, const unsigned char * data, unsigned dataLen); + //virtual void OnPacketCorrupt(Connection *con, const unsigned char *data, int dataLen, TcpCorruptionReason reason); + virtual void DestroyRequest(RequestBase * request) = 0; + + virtual void OnConnectRequest(TcpConnection *con); + + protected: + TcpManager::TcpParams mParams; + TcpManager * mTcpManager; + + std::set mConnections; + unsigned mConnectionCount; + std::list mClosedConnections; + + std::list mQueuedRequests; + std::list mActiveRequests; + std::set mSleepingRequests; + unsigned mActiveCount; + unsigned mActiveMax; + bool mAcceptingNewConnections; + }; + +//////////////////////////////////////////////////////////////////////////////// + + class Connection : public TcpConnectionHandler + { + friend class Listener; + public: + Connection(Listener & listener, TcpConnection * connection); + virtual ~Connection(); + + unsigned Send(const unsigned char * data, unsigned dataLen); + void Disconnect(); + bool IsConnected() const; + + const std::string & GetHost() const; + const unsigned GetHostIP() const; + unsigned GetQueuedRequests() const; + unsigned GetActiveRequests() const; + + virtual void OnTerminated(TcpConnection *con); + virtual void OnConnectRequest(TcpConnection *con) {}; + virtual void OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen); + virtual void OnCrcReject(TcpConnection *con, const unsigned char *data, int dataLen); + //virtual void OnPacketCorrupt(TcpConnection *con, const unsigned char *data, int dataLen, TcpCorruptionReason reason); + + protected: + void NotifyQueuedRequest(RequestBase * request); + void NotifyDiscardRequest(RequestBase * request); + void NotifyBeginRequest(RequestBase * request); + void NotifyEndRequest(RequestBase * request); + + protected: + Listener & mListener; + TcpConnection * mConnection; + std::string mHost; + unsigned mHostIp; + const char * mDisconnectReason; + unsigned mQueuedRequests; + unsigned mActiveRequests; + }; + +//////////////////////////////////////////////////////////////////////////////// + + class RequestBase + { + friend class Listener; + public: + RequestBase(Connection * connection, bool isInternal=false); + virtual ~RequestBase(); + + virtual bool Process() = 0; + + Connection * GetConnection() const { return mConnection; } + unsigned GetState() const { return mProcessState; } + + protected: + Connection * mConnection; + unsigned mProcessState; + bool mIsInternal; + }; + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef EXTERNAL_DISTRO +} +#endif + +#endif + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpManager.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpManager.cpp new file mode 100644 index 00000000..53105f3d --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpManager.cpp @@ -0,0 +1,761 @@ +#include "TcpManager.h" +#include +#include "IPAddress.h" +#include "TcpConnection.h" +#include + +#ifndef WIN32 + #include +#endif + + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +const time_t DNS_TIMEOUT = 60 * 5; + +TcpManager::TcpParams::TcpParams() +: port(0), +/* + maxConnections(1000), + incomingBufferSize(6*1024), + outgoingBufferSize(64*1024), + allocatorBlockSize(8*1024), + allocatorBlockCount(16), + maxRecvMessageSize(0), + keepAliveDelay(0), + noDataTimeout(0) + */ + maxConnections(1000), + incomingBufferSize(512*1024), + outgoingBufferSize(512*1024), + allocatorBlockSize(8*1024), + allocatorBlockCount(1024), + maxRecvMessageSize(2048*1024), + keepAliveDelay(0), + noDataTimeout(0) + +{ + memset(bindAddress, 0, sizeof(bindAddress)); +} + +TcpManager::TcpParams::TcpParams(const TcpParams &cpy) +: port(cpy.port), + maxConnections(cpy.maxConnections), + incomingBufferSize(cpy.incomingBufferSize), + outgoingBufferSize(cpy.outgoingBufferSize), + allocatorBlockSize(cpy.allocatorBlockSize), + allocatorBlockCount(cpy.allocatorBlockCount), + maxRecvMessageSize(cpy.maxRecvMessageSize), + keepAliveDelay(cpy.keepAliveDelay), + noDataTimeout(cpy.noDataTimeout) +{ + memset(bindAddress, 0, sizeof(bindAddress)); + strncpy(bindAddress, cpy.bindAddress, sizeof(cpy.bindAddress)); +} + +TcpManager::TcpManager(const TcpParams ¶ms) +: m_handler(NULL), + m_keepAliveList(NULL, 1), + m_aliveList(NULL, 2), + m_noDataList(NULL, 1), + m_dataList(NULL, 2), + m_params(params), + m_refCount(1), + m_connectionList(NULL), + m_connectionListCount(0), + m_socket(INVALID_SOCKET), + m_boundAsServer(false), + m_allocator(params.allocatorBlockSize, params.allocatorBlockCount), + m_keepAliveTimer(), + m_noDataTimer(), + m_dnsMap() +{ + if (params.keepAliveDelay > 0) + m_keepAliveTimer.start(); + + if (params.noDataTimeout > 0) + m_noDataTimer.start(); + +#if defined(WIN32) + WSADATA wsaData; + WSAStartup(MAKEWORD(1,1), &wsaData); + + FD_ZERO(&m_permfds);//select only used on win32 +#endif + +} + + +TcpManager::~TcpManager() +{ +#if defined(WIN32) + WSACleanup(); +#endif + + if (m_boundAsServer) + { +#if defined(WIN32) + closesocket(m_socket); +#else + close(m_socket); +#endif + } + while (m_connectionList != NULL) + { + TcpConnection *con = m_connectionList; + con->AddRef(); + removeConnection(con); + con->Release(); + } +} + + +bool TcpManager::BindAsServer() +{ + + m_socket = socket(AF_INET, SOCK_STREAM, 0); + + if (m_socket != INVALID_SOCKET) + { +#if defined(WIN32) + FD_SET(m_socket, &m_permfds);//the socket this server is listening on + + unsigned long isNonBlocking = 1; + int outBufSize = m_params.outgoingBufferSize; + int inBufSize = m_params.incomingBufferSize; + int keepAlive = 1; + int reuseAddr = 1; + struct linger ld; + ld.l_onoff = 0; + ld.l_linger = 0; + + if (ioctlsocket(m_socket, FIONBIO, &isNonBlocking) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&outBufSize, sizeof(outBufSize)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&inBufSize, sizeof(inBufSize)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepAlive, sizeof(keepAlive)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseAddr, sizeof(reuseAddr)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (char *)&ld, sizeof(ld)) != 0 ) + { + return false; + } +#else // linux is to remain the default compile mode + unsigned long isNonBlocking = 1; + unsigned long keepAlive = 1; + unsigned long outBufSize = m_params.outgoingBufferSize; + unsigned long inBufSize = m_params.incomingBufferSize; + unsigned long reuseAddr = 1; + struct linger ld; + ld.l_onoff = 0; + ld.l_linger = 0; + + if (ioctl(m_socket, FIONBIO, &isNonBlocking) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, &outBufSize, sizeof(outBufSize)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, &inBufSize, sizeof(inBufSize)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) != 0 + || setsockopt(m_socket, SOL_SOCKET, SO_LINGER, &ld, sizeof(ld)) != 0) + { + return false; + } +#endif + } + else + { + return false; + } + + + struct sockaddr_in addr_loc; + addr_loc.sin_family = AF_INET; + addr_loc.sin_port = htons(m_params.port); + addr_loc.sin_addr.s_addr = htonl(INADDR_ANY); + if (m_params.bindAddress[0] != 0) + { + unsigned long address = inet_addr(m_params.bindAddress); + if (address == INADDR_NONE) + { + struct hostent * lphp; + lphp = gethostbyname(m_params.bindAddress); + if (lphp != NULL) + addr_loc.sin_addr.s_addr = ((struct in_addr *)(lphp->h_addr))->s_addr; + } + else + { + addr_loc.sin_addr.s_addr = address; + } + } + + if (bind(m_socket, (struct sockaddr *)&addr_loc, sizeof(addr_loc)) != 0) + { + return false; + } + + if (listen(m_socket, 1000) != 0) + { + return false; + } + + m_boundAsServer = true; + return true; +} + +TcpConnection *TcpManager::acceptClient() +{ + TcpConnection *newConn = NULL; + + if (m_boundAsServer && m_connectionListCount < m_params.maxConnections) + { + + sockaddr_in addr; + int addrLength = sizeof(addr); + SOCKET sock = ::accept(m_socket, (sockaddr *) &addr, (socklen_t *) &addrLength); + + + if (sock != INVALID_SOCKET) + { + newConn = new TcpConnection(this, &m_allocator, m_params, sock, IPAddress(addr.sin_addr.s_addr), ntohs(addr.sin_port)); + addNewConnection(newConn); + if (m_handler != NULL) + { + m_handler->OnConnectRequest(newConn); + } + } + } + + return newConn; +} + +void TcpManager::SetHandler(TcpManagerHandler *handler) +{ + m_handler = handler; +} + +SOCKET TcpManager::getMaxFD() +{ +#ifdef WIN32 + return 0;//this param is not used on win32 for select, only on unix +#else + SOCKET maxfd = 0; + + if (m_boundAsServer) + maxfd = m_socket+1; + + TcpConnection *next = NULL; + for (TcpConnection *con = m_connectionList ; con != NULL ; con = next) + { + next = con->m_nextConnection; + if (con->GetStatus() != TcpConnection::StatusDisconnected && con->m_socket > maxfd) + { + maxfd = con->m_socket + 1; + } + } + return maxfd; +#endif +} + +TcpConnection *TcpManager::getConnection(SOCKET fd) +{ + TcpConnection *next = NULL; + for (TcpConnection *con = m_connectionList ; con != NULL ; con = next) + { + next = con->m_nextConnection; + if (con->m_socket == fd) + { + return con; + } + } + //if get here ,couldn't find it + return NULL; +} + +bool TcpManager::GiveTime(unsigned maxTimeAcceptingConnections,unsigned maxSendTimePerConnection, unsigned maxRecvTimePerConnection) +{ + bool processedIncoming = false; + + if (maxTimeAcceptingConnections == 0 && maxSendTimePerConnection==0 && maxRecvTimePerConnection==0) + { + //they don't want to do anything now + return processedIncoming; + } + + AddRef(); //keep a reference to ourself in case we callback to the application and the application releases us. + + + + //first process outgoing on each connection, and finish establishing connections, if params say to + if (m_connectionListCount != 0 && maxSendTimePerConnection != 0) + { + + // Send output from last heartbeat + TcpConnection *next = NULL; + for (TcpConnection *con = m_connectionList ; con != NULL ; con = next) + { + con->AddRef(); + if (next) next->Release(); + next = con->m_nextConnection; + if (next) next->AddRef(); + if(con->GetStatus() == TcpConnection::StatusConnected) + { + Clock timer; + timer.start(); + while(!timer.isDone(maxSendTimePerConnection)) + { + int err = con->processOutgoing(); + + if (err > 0) + { + //couldn't finish processing last request, don't try more + break; + } + else if (err < 0) + { + break; + } + } + con->Release(); + } + else if (con->GetStatus() == TcpConnection::StatusNegotiating) + { + if (con->finishConnect() < 0) + { + con->Release(); + continue; + } + con->Release(); + } + else //inactive client in client list???? + { + removeConnection(con); + con->Release(); + continue; + } + } + + } + + //process incoming messages (including connect requests) + if (( + m_boundAsServer //if in server mode and want to spend time accepting clients + && maxTimeAcceptingConnections != 0 + ) + || + ( + m_connectionListCount != 0 //if there are connections and want to spend time receiving on them + && maxRecvTimePerConnection != 0 + ) + ) + { +#ifdef WIN32 + SOCKET maxfd = getMaxFD(); //re-calc maxfd every time select on WIN32 + + //select on all fd's + struct timeval timeout; + + fd_set tmpfds; + tmpfds = m_permfds; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + int cnt = select(maxfd, &tmpfds, NULL, NULL, &timeout); // blocks for timeout + + + if (cnt > 0) + { + if (m_boundAsServer && maxTimeAcceptingConnections != 0) + {//activity on our socket means connect requests + + //see if are new incoming clients + if (FD_ISSET(m_socket, &tmpfds)) + { + //yep + Clock timer; + timer.start(); + while (acceptClient() && !timer.isDone(maxTimeAcceptingConnections)) + { + //loop + } + } + } + + + + //process incoming client messages + if (maxRecvTimePerConnection != 0) + { + TcpConnection *next = NULL; + for (TcpConnection *con = m_connectionList ; con != NULL ; con = next) + { + con->AddRef(); + if (next) next->Release(); + next = con->m_nextConnection; + if (next) next->AddRef(); + + SOCKET fd = con->m_socket; + if (fd == INVALID_SOCKET) + { + //invalid socket in list?, check if is connecting, otherwise, Disconnect and discard + if (con->GetStatus() != TcpConnection::StatusNegotiating) + { + removeConnection(con); + con->Release(); + continue; + } + } + + if (FD_ISSET(fd, &tmpfds)) + { + Clock timer; + timer.start(); + while(!timer.isDone(maxRecvTimePerConnection) && con->GetStatus() == TcpConnection::StatusConnected) + { + int err = con->processIncoming(); + if (err >= 0) + { + processedIncoming = true; + } + + if (err > 0) + { + //couldn't finish processing last request, don't try more + break; + } + else if (err < 0) + { + break; + } + + }//while(!timer...) + }//if (FD_ISSET...) + con->Release(); + }//for (...) + } //maxRecvTimePerConnection != 0 + }//cnt > 0 +#else //on UNIX use poll + + int numfds = m_connectionListCount; + int idx = 0; + if (m_boundAsServer) + { + numfds++; + idx++; + } + + struct pollfd pollfds[numfds]; + + if (m_boundAsServer) + { + pollfds[0].fd = m_socket; + pollfds[0].events |= POLLIN; + } + + TcpConnection *next = NULL; + for (TcpConnection *con = m_connectionList ; con != NULL ; con = next, idx++) + { + next = con->m_nextConnection; + pollfds[idx].fd = con->m_socket; + pollfds[idx].events |= POLLIN; + pollfds[idx].events |= POLLHUP; + } + + + int cnt = poll(pollfds, numfds, 1); + + if(cnt == SOCKET_ERROR) + { + //poll not working? + //TODO: need to notify client somehow, don't think we can assume a fatal error here + } + else if (cnt > 0) + { + for (idx = 0; idx < numfds; idx++) + { + //find corresponding TcpConnection + //TODO: optimize, seriously, this is takes linear time, every time + TcpConnection *con = getConnection(pollfds[idx].fd); + + if (pollfds[idx].revents & POLLIN) + { + if (m_boundAsServer && maxTimeAcceptingConnections != 0 && pollfds[idx].fd == m_socket) + { + //new incoming clients + Clock timer; + timer.start(); + while (acceptClient() && !timer.isDone(maxTimeAcceptingConnections)) + { + //loop + } + + continue;//don't try to readmsgs from listening fd + } + + //process regular msg(s) + if (con == NULL) + { + close(pollfds[idx].fd); + continue; + } + + Clock timer; + timer.start(); + con->AddRef();//so it can't get deleted while we are checking it's status + while(!timer.isDone(maxRecvTimePerConnection) && con->GetStatus() == TcpConnection::StatusConnected) + { + int err = con->processIncoming(); + if (err >= 0) + { + processedIncoming = true; + } + + if (err > 0) + { + //couldn't finish processing last request, don't try more + break; + } + else if (err < 0) + { + break; + } + + }//while(!timer....) + con->Release(); + }//if(pollfds[... + else if (pollfds[idx].revents & POLLHUP) + { + if (con == NULL) + { + close(pollfds[idx].fd); + continue; + } + + //Disconnect client + con->Disconnect(); + } + }//for (idx=0.... + }//else if (cnt > 0) + +#endif + }//wanted to process incoming messages or connect requests + + //now process any keepalives, if time to do that + if (m_params.keepAliveDelay > 0 && m_keepAliveTimer.isDone(m_params.keepAliveDelay)) + { + TcpConnection *next = NULL; + for (TcpConnection *con = m_keepAliveList.m_beginList ; con != NULL ; con = next) + { + con->AddRef(); + if (next) next->Release(); + next = con->m_nextKeepAliveConnection; + if (next) next->AddRef(); + + con->Send(NULL, 0); //note: this request will move the connection from the keepAliveList to the aliveList + con->Release(); + } + + //now move the complete alive list over to the keepalive list to reset those timers + m_keepAliveList.m_beginList = m_aliveList.m_beginList; + m_aliveList.m_beginList = NULL; + + //switch id's for those connections that were in the alive list last go - around + int tmpID = m_aliveList.m_listID; + m_aliveList.m_listID = m_keepAliveList.m_listID; + m_keepAliveList.m_listID = tmpID; + + m_keepAliveTimer.reset(); + m_keepAliveTimer.start(); + } + + //now process any noDataCons, if time to do that + if (m_params.noDataTimeout > 0 && m_noDataTimer.isDone(m_params.noDataTimeout)) + { + TcpConnection *next = NULL; + for (TcpConnection *con = m_noDataList.m_beginList ; con != NULL ; con = next) + { + con->AddRef(); + if (next) next->Release(); + next = con->m_nextRecvDataConnection; + if (next) next->AddRef(); + + //time to disconnect this guy + con->Disconnect(); + con->Release(); + } + + //now move the complete data list over to the nodata list to reset those timers + m_noDataList.m_beginList = m_dataList.m_beginList; + m_dataList.m_beginList = NULL; + + //switch id's for those connections that were in the data list last go - around + int tmpID = m_dataList.m_listID; + m_dataList.m_listID = m_noDataList.m_listID; + m_noDataList.m_listID = tmpID; + + m_noDataTimer.reset(); + m_noDataTimer.start(); + } + + Release(); + + return processedIncoming; +} + +TcpConnection *TcpManager::EstablishConnection(const char *serverAddress, unsigned short serverPort, unsigned timeout) +{ + if (m_boundAsServer) + { + //can't open outgoing connections when in server mode + // use a different TcpManager to do that + return NULL; + } + + if (m_connectionListCount >= m_params.maxConnections) + return(NULL); + + // get server address + unsigned long address = inet_addr(serverAddress); + if (address == INADDR_NONE) + { + if (m_dnsMap[serverAddress].timeout >= time(NULL)) + { + address = m_dnsMap[serverAddress].addr; + } + else + { + struct hostent * lphp; + lphp = gethostbyname(serverAddress); + if (lphp == NULL) + return(NULL); + address = ((struct in_addr *)(lphp->h_addr))->s_addr; + + m_dnsMap[serverAddress].addr = address; + m_dnsMap[serverAddress].timeout = time(NULL)+DNS_TIMEOUT; + } + } + IPAddress destIP(address); + + TcpConnection *con = new TcpConnection(this, &m_allocator, m_params, destIP, serverPort, timeout); + con->AddRef();//for the client - to conform to UdpLibrary method + addNewConnection(con); + + return con; +} + +void TcpManager::addNewConnection(TcpConnection *con) +{ + con->AddRef(); +#ifdef WIN32 //uses select + if (con->m_socket != INVALID_SOCKET) + FD_SET(con->m_socket, &m_permfds); +#endif + con->m_nextConnection = m_connectionList; + con->m_prevConnection = NULL; + if (m_connectionList != NULL) + m_connectionList->m_prevConnection = con; + m_connectionList = con; + m_connectionListCount++; + + con->m_nextKeepAliveConnection = m_aliveList.m_beginList; + con->m_prevKeepAliveConnection = NULL; + if (m_aliveList.m_beginList != NULL) + m_aliveList.m_beginList->m_prevKeepAliveConnection = con; + m_aliveList.m_beginList = con; + con->m_aliveListId = m_keepAliveList.m_listID;//start it out thinking it's already in the alive list, since it is + + con->m_nextRecvDataConnection = m_dataList.m_beginList; + con->m_prevRecvDataConnection = NULL; + if (m_dataList.m_beginList != NULL) + m_dataList.m_beginList->m_prevRecvDataConnection = con; + m_dataList.m_beginList = con; + con->m_recvDataListId = m_noDataList.m_listID;//start it out thinking it's already in the data list, since it is +} + +void TcpManager::removeConnection(TcpConnection *con) +{ + if (!con->wasRemovedFromMgr()) + { + con->setRemovedFromMgr(); + m_connectionListCount--; + #ifdef WIN32 //select only used on win32 + if (con->m_socket != INVALID_SOCKET) + { + FD_CLR(con->m_socket, &m_permfds); + } + #endif + if (con->m_prevConnection != NULL) + con->m_prevConnection->m_nextConnection = con->m_nextConnection; + if (con->m_nextConnection != NULL) + con->m_nextConnection->m_prevConnection = con->m_prevConnection; + if (m_connectionList == con) + m_connectionList = con->m_nextConnection; + con->m_nextConnection = NULL; + con->m_prevConnection = NULL; + + if (con->m_prevKeepAliveConnection != NULL) + con->m_prevKeepAliveConnection->m_nextKeepAliveConnection = con->m_nextKeepAliveConnection; + if (con->m_nextKeepAliveConnection != NULL) + con->m_nextKeepAliveConnection->m_prevKeepAliveConnection = con->m_prevKeepAliveConnection; + + if (m_aliveList.m_beginList == con) + m_aliveList.m_beginList = con->m_nextKeepAliveConnection; + else if (m_keepAliveList.m_beginList == con) + m_keepAliveList.m_beginList = con->m_nextKeepAliveConnection; + con->m_nextKeepAliveConnection = NULL; + con->m_prevKeepAliveConnection = NULL; + + + + if (con->m_prevRecvDataConnection != NULL) + con->m_prevRecvDataConnection->m_nextRecvDataConnection = con->m_nextRecvDataConnection; + if (con->m_nextRecvDataConnection != NULL) + con->m_nextRecvDataConnection->m_prevRecvDataConnection = con->m_prevRecvDataConnection; + + if (m_dataList.m_beginList == con) + m_dataList.m_beginList = con->m_nextRecvDataConnection; + else if (m_noDataList.m_beginList == con) + m_noDataList.m_beginList = con->m_nextRecvDataConnection; + con->m_nextRecvDataConnection = NULL; + con->m_prevRecvDataConnection = NULL; + + + + con->Release(); + } +} + +void TcpManager::AddRef() +{ + m_refCount++; +} + +void TcpManager::Release() +{ + if (--m_refCount == 0) + delete this; +} + +IPAddress TcpManager::GetLocalIp() const +{ + struct sockaddr_in addr_self; + memset(&addr_self, 0, sizeof(addr_self)); + socklen_t len = sizeof(addr_self); + getsockname(m_socket, (struct sockaddr *)&addr_self, &len); + return(IPAddress(addr_self.sin_addr.s_addr)); + +} + +unsigned int TcpManager::GetLocalPort() const +{ + struct sockaddr_in addr_self; + memset(&addr_self, 0, sizeof(addr_self)); + socklen_t len = sizeof(addr_self); + getsockname(m_socket, (struct sockaddr *)&addr_self, &len); + return(ntohs(addr_self.sin_port)); +} + + +#ifdef EXTERNAL_DISTRO +}; +#endif + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpManager.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpManager.h new file mode 100644 index 00000000..b2192dff --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/TcpLibrary/TcpManager.h @@ -0,0 +1,300 @@ +#ifndef TCPMANAGER_H +#define TCPMANAGER_H + +#ifdef WIN32 +#pragma warning (disable: 4786) +#endif + +#include "TcpHandlers.h" + +#include "TcpBlockAllocator.h" +#include "IPAddress.h" +#include "Clock.h" +#include +#include + +#if defined(WIN32) + #include + typedef int socklen_t; +#else // for non-windows platforms (linux) + #include + #include + #include + #include + #include + #include + #include + const int INVALID_SOCKET = 0xFFFFFFFF; + const int SOCKET_ERROR = 0xFFFFFFFF; + typedef int SOCKET; +#endif + +#ifdef EXTERNAL_DISTRO +namespace NAMESPACE +{ +#endif + +class TcpConnection; + +struct ConnectionList +{ + ConnectionList(TcpConnection *con, int id) : m_beginList(con), m_listID(id) {} + + TcpConnection *m_beginList; + int m_listID; +}; + +/** + * @brief The purpose of the TcpManager is to manage a set of connections that are coming in on a particular port. + * + */ +class TcpManager +{ +public: + struct AddrTimeout + { + AddrTimeout() : addr(0), timeout(0) {} + long addr; + time_t timeout; + }; + + /** @brief Parameters for the TcpManager. */ + struct TcpParams + { + /** @brief Simple constructor sets default values for members. */ + TcpParams(); + + /** @brief Simple copy constructor. */ + TcpParams(const TcpParams &cpy); + + /** + * @brief Connection port number. + * + * 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 + */ + unsigned short port; + + /** + * @ brief Server bind ip. + * + */ + char bindAddress[64]; + + + /** + * @brief Maximum number of connections that can be established by this manager. + * + * 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 + */ + unsigned maxConnections; + + /** + * @brief 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 + */ + unsigned incomingBufferSize; + + /** + * @brief 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 + */ + unsigned outgoingBufferSize; + + /** + * @brief The block size of a single outgoing buffer memory allocator block. + * + * This param should allways be set at least as high as the maximum message size you + * expect to send (performance will suffer otherwise). + * default = 8K + */ + unsigned allocatorBlockSize; + + /** + * @brief The number of block memory allocator 'blocks' created at a time. + * + * This is the number of blocks created for the buffer allocator for each + * TcpConnection opened by this manager. Since the block size should be + * the max size of an outgoing message, the recommended setting is: greater + * than the number of concurrent connections you expect to normally have open. + * default = 1024 + */ + unsigned allocatorBlockCount; + + /** + * @brief The maximum size that a recvd message is allowed to be. + * + * Really only here for protection, not required. If you set this, you can safeguard + * your client/server from receiving stray oversized messages. If a message on the socket + * specifies it's length at larger than this value, then the message is not read, and the connection + * is terminated. If the value is set to 0, then there is no max message size checking + * on incoming messages (this will also cary a performance hit, since every new message + * recieved will have to have a new buffer created if you don't specify a value here). Be careful + * not to set this too small, if you have messages that could exceed the value you set here + * they will be discarded, and the connection will be terminated without warning. + * default = 0 + */ + unsigned maxRecvMessageSize; + + unsigned keepAliveDelay; + + unsigned noDataTimeout; + }; + + + /** + * @brief + */ + TcpManager(const TcpParams ¶ms); + + /** + * @brief Use to specify a handler object to receive callbacks. + * + * To have the TcpManager call your object directly when connection requests come in, you + * simply need to derive your class (multiply if necessary) from TcpManagerHandler, then you can use + * this method to set the object the TcpManager will call as appropriate. The TcpConnection object + * also has a handler mechanism that replaces the other callback functions below, see TcpConnection::SetHandler + * default = NULL (no callbacks made) + * + * @param handler The object which will be called for manager related notifications. + */ + void SetHandler(TcpManagerHandler *handler); + + /** + * @brief 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, etc. + * + * @param maxTimeAcceptingConnections The max amount of time in milliseconds to spend accepting new client connections. + * This parameter is only used if this manager has been bound as a server (bindAsServer). + * If you set this param to 0, it will not attempt to accept any new connections. + * + * @param giveConnectionsTime + * True if every connection opened on this manager is given time in this call, false if + * no connections are given time. + * + * @param maxSendTimePerConnection Max amount of time in milliseconds to spend on each client processing outgoing messages. + * A max of the specified amount of time will be spent on each and every individual connection. If you set + * this parametrer to 0, it will not process any outgoing messages on any clients. Note also that when attempting + * to establish new connections (via the EstablishConnection method), this parameter must be > 0 in order to + * complete the connection process for any connections that were still negotiating. + * + * @param maxRecvTimePerConnection Max amount of time in milliseconds to spend on each client processing incoming messages. + * A max of the specified amount of time will be spent on each and every individual connection. If you set + * this param to 0, it will not process any incoming messages on any clients. + * 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. + * + * @return true if any incoming packets were processed during this time slice, otherwise returns false + */ + bool GiveTime(unsigned maxTimeAcceptingConnections = 5, unsigned maxSendTimePerConnection = 5, unsigned maxRecvTimePerConnection = 5); + + /** + * @brief 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 TcpConnection object that will be in a StatusNegotiating + * 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 TcpManager::GiveTime and checking to see + * if the status of the returned TcpConnection object is changed from StatusNegotiating. This allows + * the application to look for the ESC key or timeout an attempted connection. + * + * @param serverAddress The address of the server to open a connection to. + * + * @param serverPort The port of the server to open a connection to. + * + * @param timeout How long to attempt connecting to the server (in milliseconds). + * Setting the timeout value to something greater than 0 will cause the TcpConnection object to change + * from a StatusNegotiating state to a StatusDisconnected state after the timeout has expired. It will also cause + * the connect-complete callback to be called if the connection is succesfull. + * + * @return A pointer to a TcpConnection object. + * NULL if the manager object has exceeded its maximum number of connections + * or if the serverAddress cannot be resolved to an IP address. + */ + TcpConnection *EstablishConnection(const char *serverAddress, unsigned short serverPort, unsigned timeout = 0); + + /** + * @brief Binds this manager as a server which will listen for and accept incoming connections. + * + * @return 'true' if manager is able to bind succesfully, false otherwise. + */ + bool BindAsServer(); + + /** + * @brief Standard AddRef/Release scheme + */ + void AddRef(); + + /** + * @brief Standard AddRef/Release scheme + */ + void Release(); + + /** + * @brief Returns the ip address of this machine. If the machine is multi-homed, this value may be blank. + */ + IPAddress GetLocalIp() const; + + + /** + * @brief 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) + */ + unsigned int GetLocalPort() const; + +protected: + friend class TcpConnection; + void removeConnection(TcpConnection *con); + TcpManagerHandler *m_handler; + + ConnectionList m_keepAliveList; + ConnectionList m_aliveList; + + ConnectionList m_noDataList; + ConnectionList m_dataList; + +private: + ~TcpManager(); + TcpParams m_params; + int m_refCount; + TcpConnection *m_connectionList; + unsigned m_connectionListCount; +#ifdef WIN32 + fd_set m_permfds; /**< Used for select on WIN32 if we are in server mode. Keeps track of all clients connected to us. */ +#endif //WIN32 + SOCKET m_socket; + bool m_boundAsServer; + TcpBlockAllocator m_allocator; + Clock m_keepAliveTimer; + Clock m_noDataTimer; + + void addNewConnection(TcpConnection *con); + SOCKET getMaxFD(); + TcpConnection *getConnection(SOCKET fd); + TcpConnection *acceptClient(); + std::map m_dnsMap; +}; + +#ifdef EXTERNAL_DISTRO +}; +#endif + +#endif //TCPMANAGER_H + + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpHandler.hpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpHandler.hpp new file mode 100644 index 00000000..c97f6c6c --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpHandler.hpp @@ -0,0 +1,82 @@ +#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()) + // For the encrypt-functions, the destination buffer will be (sourceLen + userSuppliedEncryptExpansionBytes1) long, + // do not overflow it, the debug version has an assert check that ensures that you do not. + // For the decrypt-functions, the destination buffer is guaranteed to be large enough to hold what the original + // data used to be before it was encrypted. + 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/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpLibrary.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpLibrary.cpp new file mode 100644 index 00000000..a8dc92c4 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpLibrary.cpp @@ -0,0 +1,4380 @@ +#include +#include +#include +#include +#include + +#include "UdpLibrary.hpp" + + +#if defined(WIN32) + #pragma warning(disable : 4710) + #if defined(UDPLIBRARY_WINSOCK2) + #include + #include + #else + #include + #endif +#elif defined(sparc) + #include + #include + #include + #include + #include + #include + #include + #include + #include +#else // for non-windows platforms (linux) + #include + #include + #include + #include + #include + #include + #include + #include // needed by gcc 3.1 for linux +#endif + + +#if defined(WIN32) + typedef int socklen_t; +#elif defined(sparc) + const int INADDR_NONE = -1; + const int INVALID_SOCKET = 0xFFFFFFFF; + const int SOCKET_ERROR = 0xFFFFFFFF; +#else // for non-windows platforms (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; + lingerDelay = 10; + 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; + } + + if (mParams.lingerDelay != 0) + { + UdpMisc::Sleep(mParams.lingerDelay); // sleep momentarily before closing socket to give it a chance to empty the socket buffer if there is something in it + } + + 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) + { + if (mConnectionListCount >= mParams.maxConnections) + return; // can't handle any more connections, so ignore this request entirely + + 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; + mOtherSideProtocolVersion = 0; + + 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, cDisconnectReasonApplicationReleased); + + 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) +{ + if (mDisconnectReason == cDisconnectReasonNone) + { + 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 (mUdpManager == NULL) + return; + + 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) +{ + if (mStatus == cStatusConnected) + { + 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 otherSideProtocolVersion = UdpMisc::GetValue32(data + 2); + 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) + { + mOtherSideProtocolVersion = otherSideProtocolVersion; + 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); + bufPtr += UdpMisc::PutValue32(bufPtr, UdpManager::cProtocolVersion); + 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 into stack variables + Configuration config; + int otherSideProtocolVersion = 0; + + 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 (dataLen > 15 + UdpManager::cEncryptPasses) + { + otherSideProtocolVersion = UdpMisc::GetValue32(data + 15 + UdpManager::cEncryptPasses); + } + + // only actually process the confirm if we are negotiating (expecting it) and the connect-code matches up + if (mStatus == cStatusNegotiating && mConnectCode == connectCode) + { + mConnectionConfig = config; + mOtherSideProtocolVersion = otherSideProtocolVersion; + 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); + if (mUdpManager == NULL) + return; + } + 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 (mUdpManager == NULL) + return; + + // 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"; + sDisconnectReason[cDisconnectReasonApplicationReleased] = "DisconnectReasonApplicationReleased"; + } + + 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 && mCoalescePacket == NULL) + { + // 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 + 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 + assert(safetyMax == dataLen); + 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 +} + + + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpLibrary.hpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpLibrary.hpp new file mode 100644 index 00000000..01281990 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpLibrary.hpp @@ -0,0 +1,2104 @@ +#ifndef UDPLIBRARY_HPP +#define UDPLIBRARY_HPP + +#include +#include "UdpHandler.hpp" +#include "Base/priority.hpp" +#include "Base/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; + + // when the UdpManager is destroyed and the socket is closed, all pending data in the socket is destroyed + // (because we are UDP and non-blocking, there is no lingering time). The problem is, on the client side it + // is often the case that connection and manager are destroyed simultaneously. This can often result in the + // final terminate-packet not making it to the server since the terminate packet will often get thrown away + // when the socket buffer is destroyed. This delay will cause the UdpManager to sleep momentarily (for however + // long is specified in this option) in order to give the OS time to flush out that socket buffer before it + // destroys it. I wish there were a way to select on whether a socket buffer was empty, but there isn't, and there + // is no way to query how full an outgoing socket buffer is. As such, the only way to have a fair-chance of deliver + // of this final packet is to simply give up our time-slice momentarily and hope the OS pumps out that final message + // to the network before we wake up and destroy the socket. By default we will have it sleep for 10ms, which does + // little more than give up our time-slice, but should be enough to get the job done in most cases. Setting this + // value to 0 will cause it to not linger at all. Again, however long you set this value to is how long the + // UdpManager destructor will block. + // default=10 + int lingerDelay; + + + // 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, cDisconnectReasonApplicationReleased + , 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; + int mOtherSideProtocolVersion; + + 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 }; +}; + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // 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); +} + +#endif + diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpListener.cpp b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpListener.cpp new file mode 100644 index 00000000..5b52d6b6 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpListener.cpp @@ -0,0 +1,394 @@ +//This code is not used by anything intentionally +//The Connection class was getting mistakenly linked in +//instead of the SWG Connection class that is used for +//all of our connections. Removing the whole thing +//to prevent the issue from coming up again. +#if 0 + +#ifdef WIN32 +#pragma warning (disable: 4786) +#endif + +#include "Base/timer.h" +#include "UdpListener.h" +#include "Base/profile.h" + + +#ifdef WIN32 +#define snprintf _snprintf +#endif + + +namespace UdpLibrary +{ + //////////////////////////////////////////////////////////////////////////////// + + + Listener::QueueNode::QueueNode() : + connection(0), + request(0) + { + } + + Listener::QueueNode::QueueNode(Connection * con, RequestBase * req) : + connection(con), + request(req) + { + } + + Listener::Listener() : + mParams(), + mUdpManager(0), + mConnections(), + mConnectionCount(0), + mClosedConnections(), + mQueuedRequests(), + mActiveRequests(), + mActiveCount(0), + mActiveMax(0) + { + //printf("ctor 0x%x Listener\n",this); + } + + Listener::~Listener() + { + std::set::iterator iterator; + for (iterator = mConnections.begin(); iterator != mConnections.end(); iterator++) + { + Connection * connection = *iterator; + delete connection; + } + mConnections.clear(); + + if (mUdpManager != 0) + { + mUdpManager->Release(); + mUdpManager = 0; + } + + //printf("dtor 0x%x Listener\n",this); + } + + void Listener::RequestSleep(RequestBase * request) + { + mSleepingRequests.insert(request); + } + + void Listener::RequestWake(RequestBase * request) + { + if (mSleepingRequests.erase(request)) + { + QueueNode node(request->mConnection, request); + mActiveRequests.push_front(node); + } + } + + void Listener::OnConnectRequest(UdpConnection * connection) + { + if (IsActive()) + { + Connection * connectionObject = new Connection(*this, connection); + + mConnections.insert(connectionObject); + mConnectionCount++; + + OnConnectionOpened(connectionObject); + } + } + + void Listener::QueueRequest(Connection * connection, RequestBase * request) + { + connection->NotifyQueuedRequest(request); + mQueuedRequests.push_back(QueueNode(connection,request)); + } + + bool Listener::IsIdle() const + { + return (!IsActive() && !mUdpManager && mQueuedRequests.empty() && !mActiveCount); + } + + unsigned Listener::Process() + { + Profile profile("Listener::Process"); + //////////////////////////////////////// + // handle inactive state (with UdpManager) + if (!IsActive() && mUdpManager) + { + // check all connections to see if they are idle + std::set::iterator iterator; + for (iterator = mConnections.begin(); iterator != mConnections.end(); iterator++) + { + Connection * connection = *iterator; + if (connection->IsConnected()) + connection->Disconnect(); + } + // close the UdpManager if all the connections are closed + if (!mConnectionCount) + { + mUdpManager->Release(); + mUdpManager = 0; + OnShutdown(); + } + } + //////////////////////////////////////// + // handle active state (without UdpManager) + else if (IsActive() && !mUdpManager) + { + mParams = GetConnectionParams(); + mParams.handler = this; + mActiveMax = GetActiveRequestMax(); + mUdpManager = new UdpManager(&mParams); + OnStartup(); + } + + //////////////////////////////////////// + // process the UdpManager + if (mUdpManager) + { + Profile subProfile("UdpManager::GiveTime()"); + mUdpManager->GiveTime(); + } + + // check all closed connections to see if they are idle + std::list::iterator closedIterator = mClosedConnections.begin(); + while (closedIterator != mClosedConnections.end()) + { + Profile profile("Listener::Process (cleanup connection)"); + std::list::iterator current = closedIterator++; + Connection * connection = *current; + if (!connection->GetActiveRequests() && + !connection->GetQueuedRequests()) + { + mClosedConnections.erase(current); + mConnections.erase(connection); + mConnectionCount--; + OnConnectionDestroyed(connection); + delete connection; + } + } + + //////////////////////////////////////// + // process request queue + while (!mQueuedRequests.empty() && (!mActiveMax || mActiveCount < mActiveMax)) + { + Profile profile("Listener::Process (activate queued request)"); + QueueNode & node = mQueuedRequests.front(); + if (!IsActive()) + { + // If not active, discard queued request + node.connection->NotifyDiscardRequest(node.request); + DestroyRequest(node.request); + } + else + { + // Move request to active list + node.connection->NotifyBeginRequest(node.request); + mActiveRequests.push_back(node); + mActiveCount++; + } + mQueuedRequests.pop_front(); + } + + //////////////////////////////////////// + // Process active requests + unsigned requestsProcessed = 0; + std::list::iterator iterator = mActiveRequests.begin(); + while (iterator != mActiveRequests.end()) + { + Profile profile("Listener::Process (process request)"); + std::list::iterator current = iterator++; + RequestBase * request = current->request; + Connection * connection = current->connection; + + if (request->Process()) + { + connection->NotifyEndRequest(request); + DestroyRequest(request); + mActiveRequests.erase(current); + mActiveCount--; + } + else if (mSleepingRequests.find(request) != mSleepingRequests.end()) + { + mActiveRequests.erase(current); + } + requestsProcessed++; + } + return requestsProcessed; + } + + void Listener::GetStats(UdpManagerStatistics & statsStruct) + { + if (mUdpManager) + mUdpManager->GetStats(&statsStruct); + } + + void Listener::ResetStats() + { + if (mUdpManager) + mUdpManager->ResetStats(); + } + + void Listener::OnStartup() + { + } + + void Listener::OnShutdown() + { + } + + void Listener::OnConnectionOpened(Connection * connection) + { + } + + void Listener::OnConnectionClosed(Connection * connection, const char * reason) + { + } + + void Listener::OnConnectionDestroyed(Connection * connection) + { + } + + void Listener::OnCrcReject(Connection *connection, const unsigned char * buffer, unsigned size) + { + } + + void Listener::OnPacketCorrupt(Connection *con, const unsigned char *data, int dataLen, UdpCorruptionReason reason) + { + } + + //////////////////////////////////////////////////////////////////////////////// + + + Connection::Connection(Listener & listener, UdpConnection * connection) : + mListener(listener), + mConnection(connection), + mHost(), + mHostIp(0), + mDisconnectReason(0), + mQueuedRequests(0), + mActiveRequests(0) + { + mConnection->AddRef(); + mConnection->SetHandler(this); + + char buffer[256]; + char addr[32]; + mHostIp = mConnection->GetDestinationIp().GetAddress(); + mConnection->GetDestinationIp().GetAddress(addr); + snprintf(buffer, sizeof(buffer), "%s:%u", addr, mConnection->GetDestinationPort()); + mHost = buffer; + + //printf("ctor 0x%x connection\n",this); + } + + Connection::~Connection() + { + Disconnect(); + + //printf("dtor 0x%x connection\n",this); + } + + unsigned Connection::Send(const unsigned char * data, unsigned dataLen) + { + if (mConnection) + { + mConnection->Send(cUdpChannelReliable1, data, dataLen); + return dataLen; + } + return 0; + } + + void Connection::Disconnect() + { + if (mConnection) + OnTerminated(mConnection); + } + + bool Connection::IsConnected() const + { + return mConnection != 0; + } + + const std::string & Connection::GetHost() const + { + return mHost; + } + + const unsigned Connection::GetHostIP() const + { + return mHostIp; + } + + unsigned Connection::GetQueuedRequests() const + { + return mQueuedRequests; + } + + unsigned Connection::GetActiveRequests() const + { + return mActiveRequests; + } + + void Connection::OnTerminated(UdpConnection *con) + { + mConnection->SetHandler(0); + mConnection->Disconnect(); + UdpConnection::DisconnectReason disconnectReason = mConnection->GetDisconnectReason(); + mConnection->Release(); + mConnection = 0; + mListener.mClosedConnections.push_back(this); + mListener.OnConnectionClosed(this, UdpConnection::DisconnectReasonText(disconnectReason)); + } + + void Connection::OnRoutePacket(UdpConnection *, const unsigned char * data, int dataLen) + { + mListener.OnReceive(this, data, dataLen); + } + + void Connection::OnCrcReject(UdpConnection *, const unsigned char * data, int dataLen) + { + mListener.OnCrcReject(this, data, dataLen); + } + + void Connection::OnPacketCorrupt(UdpConnection *, const uchar *data, int dataLen, UdpCorruptionReason reason) + { + mListener.OnPacketCorrupt(this, data, dataLen, reason); + } + + void Connection::NotifyQueuedRequest(RequestBase * request) + { + mQueuedRequests++; + } + + void Connection::NotifyDiscardRequest(RequestBase * request) + { + mQueuedRequests--; + } + + void Connection::NotifyBeginRequest(RequestBase * request) + { + mQueuedRequests--; + mActiveRequests++; + } + + void Connection::NotifyEndRequest(RequestBase * request) + { + mActiveRequests--; + } + + + //////////////////////////////////////////////////////////////////////////////// + + + RequestBase::RequestBase(Connection * connection) : + mConnection(connection), + mProcessState(0) + { + } + + RequestBase::~RequestBase() + { + } +} + +#endif diff --git a/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpListener.h b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpListener.h new file mode 100644 index 00000000..678b12f0 --- /dev/null +++ b/external/3rd/library/soePlatform/VChatAPI/utils2.0/utils/UdpLibrary/UdpListener.h @@ -0,0 +1,145 @@ +//This code is not used by anything intentionally +//The Connection class was getting mistakenly linked in +//instead of the SWG Connection class that is used for +//all of our connections. Removing the whole thing +//to prevent the issue from coming up again. +#if 0 + +#ifndef COMMON_SERVER__LISTENER_H +#define COMMON_SERVER__LISTENER_H + +#include +#include +#include +#include "UdpLibrary.hpp" + +namespace UdpLibrary +{ + //////////////////////////////////////////////////////////////////////////////// + + class RequestBase; + class Connection; + class Listener : public UdpManagerHandler + { + friend class Connection; + struct QueueNode + { + QueueNode(); + QueueNode(Connection * connection, RequestBase * request); + bool operator<(const QueueNode & rhs) { return request < rhs.request; } + + Connection * connection; + RequestBase * request; + }; + + public: + Listener(); + virtual ~Listener(); + + void QueueRequest(Connection * connection, RequestBase * request); + bool IsIdle() const; + unsigned Process(); + void GetStats(UdpManagerStatistics & statsStruct); + void ResetStats(); + + void RequestSleep(RequestBase * request); + void RequestWake(RequestBase * request); + + virtual bool IsActive() const = 0; + virtual unsigned GetActiveRequestMax() = 0; + virtual UdpManager::Params GetConnectionParams() = 0; + + virtual void OnStartup(); + virtual void OnShutdown(); + virtual void OnConnectionOpened(Connection * connection); + virtual void OnConnectionClosed(Connection * connection, const char * reason); + virtual void OnConnectionDestroyed(Connection * connection); + virtual void OnReceive(Connection * connection, const unsigned char * data, unsigned dataLen) = 0; + virtual void OnCrcReject(Connection *connection, const unsigned char * data, unsigned dataLen); + virtual void OnPacketCorrupt(Connection *con, const unsigned char *data, int dataLen, UdpCorruptionReason reason); + virtual void DestroyRequest(RequestBase * request) = 0; + + virtual void OnConnectRequest(UdpConnection *con); + + protected: + UdpManager::Params mParams; + UdpManager * mUdpManager; + + std::set mConnections; + unsigned mConnectionCount; + std::list mClosedConnections; + + std::list mQueuedRequests; + std::list mActiveRequests; + std::set mSleepingRequests; + unsigned mActiveCount; + unsigned mActiveMax; + }; + + + //////////////////////////////////////////////////////////////////////////////// + + + class Connection : public UdpConnectionHandler + { + friend class Listener; + public: + Connection(Listener & listener, UdpConnection * connection); + virtual ~Connection(); + + unsigned Send(const uchar * data, unsigned dataLen); + void Disconnect(); + bool IsConnected() const; + + const std::string & GetHost() const; + const unsigned GetHostIP() const; + unsigned GetQueuedRequests() const; + unsigned GetActiveRequests() const; + + virtual void OnTerminated(UdpConnection *con); + virtual void OnRoutePacket(UdpConnection *con, const uchar *data, int dataLen); + virtual void OnCrcReject(UdpConnection *con, const uchar *data, int dataLen); + virtual void OnPacketCorrupt(UdpConnection *con, const uchar *data, int dataLen, UdpCorruptionReason reason); + + protected: + void NotifyQueuedRequest(RequestBase * request); + void NotifyDiscardRequest(RequestBase * request); + void NotifyBeginRequest(RequestBase * request); + void NotifyEndRequest(RequestBase * request); + + protected: + Listener & mListener; + UdpConnection * mConnection; + std::string mHost; + unsigned mHostIp; + const char * mDisconnectReason; + unsigned mQueuedRequests; + unsigned mActiveRequests; + }; + + + //////////////////////////////////////////////////////////////////////////////// + + + class RequestBase + { + friend class Listener; + public: + RequestBase(Connection * connection); + virtual ~RequestBase(); + + virtual bool Process() = 0; + + protected: + Connection * mConnection; + unsigned mProcessState; + }; + + + //////////////////////////////////////////////////////////////////////////////// +} + + +#endif + +#endif