mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-16 01:01:30 +00:00
commit
999677d314
12
.travis.yml
12
.travis.yml
@ -3,15 +3,23 @@ compiler: gcc
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gcc-4.8
|
||||
- g++-4.8
|
||||
- libmysqlclient-dev
|
||||
- libperl-dev
|
||||
- libboost-dev
|
||||
- liblua5.1-0-dev
|
||||
- zlib1g-dev
|
||||
- uuid-dev
|
||||
- libssl-dev
|
||||
install:
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
|
||||
script:
|
||||
- cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON
|
||||
- make -j8
|
||||
- cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LOGIN=ON
|
||||
- make -j2
|
||||
- ./bin/tests
|
||||
branches:
|
||||
only:
|
||||
|
||||
@ -64,10 +64,26 @@ IF(MSVC)
|
||||
SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_x64")
|
||||
SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x64")
|
||||
SET(LUA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luaj_x64")
|
||||
SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include")
|
||||
IF(MSVC_VERSION GREATER 1800)
|
||||
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v140/dynamic")
|
||||
ELSEIF(MSVC_VERSION EQUAL 1800)
|
||||
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v120/dynamic")
|
||||
ELSE()
|
||||
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v110/dynamic")
|
||||
ENDIF()
|
||||
ELSE(CMAKE_CL_64)
|
||||
SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_x86")
|
||||
SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x86")
|
||||
SET(LUA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luaj_x86")
|
||||
SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include")
|
||||
IF(MSVC_VERSION GREATER 1800)
|
||||
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v140/dynamic")
|
||||
ELSEIF(MSVC_VERSION EQUAL 1800)
|
||||
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v120/dynamic")
|
||||
ELSE()
|
||||
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v110/dynamic")
|
||||
ENDIF()
|
||||
ENDIF(CMAKE_CL_64)
|
||||
|
||||
#disable CRT warnings on windows cause they're annoying as shit and we use C functions everywhere
|
||||
@ -254,6 +270,7 @@ ENDIF(EQEMU_ENABLE_BOTS)
|
||||
#What to build
|
||||
OPTION(EQEMU_BUILD_SERVER "Build the game server." ON)
|
||||
OPTION(EQEMU_BUILD_LOGIN "Build the login server." OFF)
|
||||
OPTION(EQEMU_BUILD_HC "Build the headless client." OFF)
|
||||
OPTION(EQEMU_BUILD_TESTS "Build utility tests." OFF)
|
||||
OPTION(EQEMU_BUILD_PERL "Build Perl parser." ON)
|
||||
OPTION(EQEMU_BUILD_LUA "Build Lua parser." ON)
|
||||
@ -315,6 +332,30 @@ IF(EQEMU_BUILD_PERL)
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${PERL_INCLUDE_PATH}")
|
||||
ENDIF(EQEMU_BUILD_PERL)
|
||||
|
||||
SET(SERVER_LIBS common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY} libuv fmt)
|
||||
|
||||
FIND_PACKAGE(Sodium REQUIRED)
|
||||
IF(SODIUM_FOUND)
|
||||
OPTION(EQEMU_ENABLE_SECURITY "Use Encryption For TCP Connections" ON)
|
||||
IF(EQEMU_ENABLE_SECURITY)
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${SODIUM_INCLUDE_DIRS}")
|
||||
ADD_DEFINITIONS(-DENABLE_SECURITY)
|
||||
SET(SERVER_LIBS ${SERVER_LIBS} ${SODIUM_LIBRARIES})
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF(WIN32)
|
||||
SET(SERVER_LIBS ${SERVER_LIBS} "ws2_32" "psapi" "iphlpapi" "userenv")
|
||||
ENDIF()
|
||||
|
||||
IF(UNIX)
|
||||
SET(SERVER_LIBS ${SERVER_LIBS} ${CMAKE_DL_LIBS} "z" "m" "pthread")
|
||||
IF(NOT DARWIN)
|
||||
SET(SERVER_LIBS ${SERVER_LIBS} "rt")
|
||||
ENDIF()
|
||||
SET(SERVER_LIBS ${SERVER_LIBS} "uuid")
|
||||
ENDIF()
|
||||
|
||||
IF(EQEMU_BUILD_LUA)
|
||||
FIND_PACKAGE(EQLua51 REQUIRED)
|
||||
SET(Boost_USE_STATIC_LIBS OFF)
|
||||
@ -323,7 +364,8 @@ IF(EQEMU_BUILD_LUA)
|
||||
SET(BOOST_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/boost")
|
||||
|
||||
FIND_PACKAGE(Boost REQUIRED)
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${LUA_INCLUDE_DIR}" "${Boost_INCLUDE_DIRS}" "luabind")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${LUA_INCLUDE_DIR}" "${Boost_INCLUDE_DIRS}")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/luabind")
|
||||
|
||||
OPTION(EQEMU_SANITIZE_LUA_LIBS "Sanitize Lua Libraries (Remove OS and IO standard libraries from being able to run)." ON)
|
||||
IF(EQEMU_SANITIZE_LUA_LIBS)
|
||||
@ -331,15 +373,18 @@ IF(EQEMU_BUILD_LUA)
|
||||
ENDIF(EQEMU_SANITIZE_LUA_LIBS)
|
||||
ENDIF(EQEMU_BUILD_LUA)
|
||||
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${MySQL_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/common/glm")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${ZLIB_INCLUDE_DIRS}")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${MySQL_INCLUDE_DIR}")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/common/glm")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/cereal")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/libuv/include" )
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/libuv/src")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/format")
|
||||
|
||||
IF(EQEMU_BUILD_LUA)
|
||||
ADD_SUBDIRECTORY(luabind)
|
||||
ENDIF(EQEMU_BUILD_LUA)
|
||||
|
||||
IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS)
|
||||
IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS OR EQEMU_BUILD_HC)
|
||||
ADD_SUBDIRECTORY(common)
|
||||
ENDIF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS)
|
||||
ADD_SUBDIRECTORY(libs)
|
||||
ENDIF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS OR EQEMU_BUILD_HC)
|
||||
IF(EQEMU_BUILD_SERVER)
|
||||
ADD_SUBDIRECTORY(shared_memory)
|
||||
ADD_SUBDIRECTORY(world)
|
||||
@ -352,6 +397,10 @@ IF(EQEMU_BUILD_LOGIN)
|
||||
ADD_SUBDIRECTORY(loginserver)
|
||||
ENDIF(EQEMU_BUILD_LOGIN)
|
||||
|
||||
IF(EQEMU_BUILD_HC)
|
||||
ADD_SUBDIRECTORY(hc)
|
||||
ENDIF(EQEMU_BUILD_HC)
|
||||
|
||||
IF(EQEMU_BUILD_TESTS)
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
ENDIF(EQEMU_BUILD_TESTS)
|
||||
|
||||
@ -1,5 +1,27 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 4/16/2017 ==
|
||||
KLS: Merge eqstream branch
|
||||
-UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs).
|
||||
-TCP Server to Server connection stack completely rewritten.
|
||||
-Server connections reconnect much more reliably and quickly now.
|
||||
-Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/).
|
||||
-Protocol behind the tcp connections has changed (see breaking changes section).
|
||||
-API significantly changed and should be easier to write new servers or handlers for.
|
||||
-Telnet console connection has been separated out from the current port (see breaking changes section).
|
||||
-Because of changes to the TCP stack, lsreconnect and echo have been disabled.
|
||||
-The server tic rate has been changed to be approx 30 fps from 500+ fps.
|
||||
-Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough).
|
||||
|
||||
-Breaking changes:
|
||||
-Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/
|
||||
-To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections:
|
||||
-You should add <legacy>1</legacy> to the login section of your configuration file when connecting to a server that is using the old protocol.
|
||||
-The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section.
|
||||
-Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections.
|
||||
-To enable telnet you need to add a telnet tag in the world section of configuration such as:
|
||||
<telnet ip="0.0.0.0" port="9001" enabled="true"/>
|
||||
|
||||
== 4/1/2017 ==
|
||||
Akkadius: Cleaned up some of the NPC to NPC aggro code, only do aggro checks to other NPC's when the NPC is flagged for it
|
||||
Akkadius: [Performance] Reworked how all log calls are made in the source
|
||||
|
||||
@ -9,28 +9,8 @@ SET(export_headers
|
||||
|
||||
ADD_EXECUTABLE(export_client_files ${export_sources} ${export_headers})
|
||||
|
||||
INSTALL(TARGETS export_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
INSTALL(TARGETS export_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
TARGET_LINK_LIBRARIES(export_client_files common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY})
|
||||
|
||||
IF(MSVC)
|
||||
SET_TARGET_PROPERTIES(export_client_files PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF")
|
||||
TARGET_LINK_LIBRARIES(export_client_files "Ws2_32.lib")
|
||||
ENDIF(MSVC)
|
||||
|
||||
IF(MINGW)
|
||||
TARGET_LINK_LIBRARIES(export_client_files "WS2_32")
|
||||
ENDIF(MINGW)
|
||||
|
||||
IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(export_client_files "${CMAKE_DL_LIBS}")
|
||||
TARGET_LINK_LIBRARIES(export_client_files "z")
|
||||
TARGET_LINK_LIBRARIES(export_client_files "m")
|
||||
IF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(export_client_files "rt")
|
||||
ENDIF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(export_client_files "pthread")
|
||||
ADD_DEFINITIONS(-fPIC)
|
||||
ENDIF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(export_client_files ${SERVER_LIBS})
|
||||
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
@ -9,28 +9,8 @@ SET(import_headers
|
||||
|
||||
ADD_EXECUTABLE(import_client_files ${import_sources} ${import_headers})
|
||||
|
||||
INSTALL(TARGETS import_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
INSTALL(TARGETS import_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
TARGET_LINK_LIBRARIES(import_client_files common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY})
|
||||
|
||||
IF(MSVC)
|
||||
SET_TARGET_PROPERTIES(import_client_files PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF")
|
||||
TARGET_LINK_LIBRARIES(import_client_files "Ws2_32.lib")
|
||||
ENDIF(MSVC)
|
||||
|
||||
IF(MINGW)
|
||||
TARGET_LINK_LIBRARIES(import_client_files "WS2_32")
|
||||
ENDIF(MINGW)
|
||||
|
||||
IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(import_client_files "${CMAKE_DL_LIBS}")
|
||||
TARGET_LINK_LIBRARIES(import_client_files "z")
|
||||
TARGET_LINK_LIBRARIES(import_client_files "m")
|
||||
IF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(import_client_files "rt")
|
||||
ENDIF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(import_client_files "pthread")
|
||||
ADD_DEFINITIONS(-fPIC)
|
||||
ENDIF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(import_client_files ${SERVER_LIBS})
|
||||
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
30
cmake/FindSodium.cmake
Normal file
30
cmake/FindSodium.cmake
Normal file
@ -0,0 +1,30 @@
|
||||
if (NOT MSVC)
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(PC_SODIUM "libsodium")
|
||||
if (NOT PC_SODIUM_FOUND)
|
||||
pkg_check_modules(PC_SODIUM "sodium")
|
||||
endif (NOT PC_SODIUM_FOUND)
|
||||
if (PC_SODIUM_FOUND)
|
||||
set(SODIUM_INCLUDE_HINTS ${PC_SODIUM_INCLUDE_DIRS} ${PC_SODIUM_INCLUDE_DIRS}/*)
|
||||
set(SODIUM_LIBRARY_HINTS ${PC_SODIUM_LIBRARY_DIRS} ${PC_SODIUM_LIBRARY_DIRS}/*)
|
||||
endif()
|
||||
endif (NOT MSVC)
|
||||
|
||||
# some libraries install the headers is a subdirectory of the include dir
|
||||
# returned by pkg-config, so use a wildcard match to improve chances of finding
|
||||
# headers and libraries.
|
||||
find_path(
|
||||
SODIUM_INCLUDE_DIRS
|
||||
NAMES sodium.h
|
||||
HINTS ${SODIUM_INCLUDE_HINTS}
|
||||
)
|
||||
|
||||
find_library(
|
||||
SODIUM_LIBRARIES
|
||||
NAMES libsodium sodium
|
||||
HINTS ${SODIUM_LIBRARY_HINTS}
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SODIUM DEFAULT_MSG SODIUM_LIBRARIES SODIUM_INCLUDE_DIRS)
|
||||
mark_as_advanced(SODIUM_FOUND SODIUM_LIBRARIES SODIUM_INCLUDE_DIRS)
|
||||
@ -16,8 +16,6 @@ SET(common_sources
|
||||
emu_legacy.cpp
|
||||
emu_limits.cpp
|
||||
emu_opcodes.cpp
|
||||
emu_tcp_connection.cpp
|
||||
emu_tcp_server.cpp
|
||||
emu_versions.cpp
|
||||
eqdb.cpp
|
||||
eqdb_res.cpp
|
||||
@ -26,11 +24,10 @@ SET(common_sources
|
||||
eqemu_logsys.cpp
|
||||
eq_limits.cpp
|
||||
eq_packet.cpp
|
||||
eq_stream.cpp
|
||||
eq_stream_factory.cpp
|
||||
eq_stream_ident.cpp
|
||||
eq_stream_proxy.cpp
|
||||
eqtime.cpp
|
||||
event_sub.cpp
|
||||
extprofile.cpp
|
||||
faction.cpp
|
||||
guild_base.cpp
|
||||
@ -40,6 +37,7 @@ SET(common_sources
|
||||
ipc_mutex.cpp
|
||||
item_data.cpp
|
||||
item_instance.cpp
|
||||
json_config.cpp
|
||||
light_source.cpp
|
||||
md5.cpp
|
||||
memory_buffer.cpp
|
||||
@ -68,15 +66,25 @@ SET(common_sources
|
||||
spdat.cpp
|
||||
string_util.cpp
|
||||
struct_strategy.cpp
|
||||
tcp_connection.cpp
|
||||
tcp_server.cpp
|
||||
textures.cpp
|
||||
timeoutmgr.cpp
|
||||
timer.cpp
|
||||
unix.cpp
|
||||
worldconn.cpp
|
||||
xml_parser.cpp
|
||||
platform.cpp
|
||||
event/event_loop.cpp
|
||||
json/jsoncpp.cpp
|
||||
net/console_server.cpp
|
||||
net/console_server_connection.cpp
|
||||
net/crc32.cpp
|
||||
net/daybreak_connection.cpp
|
||||
net/eqstream.cpp
|
||||
net/packet.cpp
|
||||
net/servertalk_client_connection.cpp
|
||||
net/servertalk_legacy_client_connection.cpp
|
||||
net/servertalk_server.cpp
|
||||
net/servertalk_server_connection.cpp
|
||||
net/tcp_connection.cpp
|
||||
net/tcp_server.cpp
|
||||
patches/patches.cpp
|
||||
patches/sod.cpp
|
||||
patches/sod_limits.cpp
|
||||
@ -90,22 +98,12 @@ SET(common_sources
|
||||
patches/titanium_limits.cpp
|
||||
patches/uf.cpp
|
||||
patches/uf_limits.cpp
|
||||
SocketLib/Base64.cpp
|
||||
SocketLib/File.cpp
|
||||
SocketLib/HttpdCookies.cpp
|
||||
SocketLib/HttpdForm.cpp
|
||||
SocketLib/HttpdSocket.cpp
|
||||
SocketLib/HTTPSocket.cpp
|
||||
SocketLib/MemFile.cpp
|
||||
SocketLib/Mime.cpp
|
||||
SocketLib/Parse.cpp
|
||||
SocketLib/socket_include.cpp
|
||||
SocketLib/Utility.cpp
|
||||
StackWalker/StackWalker.cpp
|
||||
tinyxml/tinystr.cpp
|
||||
tinyxml/tinyxml.cpp
|
||||
tinyxml/tinyxmlerror.cpp
|
||||
tinyxml/tinyxmlparser.cpp
|
||||
util/uuid.cpp
|
||||
)
|
||||
|
||||
SET(common_headers
|
||||
@ -127,8 +125,6 @@ SET(common_headers
|
||||
emu_limits.h
|
||||
emu_opcodes.h
|
||||
emu_oplist.h
|
||||
emu_tcp_connection.h
|
||||
emu_tcp_server.h
|
||||
emu_versions.h
|
||||
eq_constants.h
|
||||
eq_packet_structs.h
|
||||
@ -140,15 +136,13 @@ SET(common_headers
|
||||
eqemu_logsys.h
|
||||
eq_limits.h
|
||||
eq_packet.h
|
||||
eq_stream.h
|
||||
eq_stream_factory.h
|
||||
eq_stream_ident.h
|
||||
eq_stream_intf.h
|
||||
eq_stream_locator.h
|
||||
eq_stream_proxy.h
|
||||
eq_stream_type.h
|
||||
eqtime.h
|
||||
errmsg.h
|
||||
event_sub.h
|
||||
extprofile.h
|
||||
faction.h
|
||||
features.h
|
||||
@ -163,6 +157,7 @@ SET(common_headers
|
||||
item_data.h
|
||||
item_fieldlist.h
|
||||
item_instance.h
|
||||
json_config.h
|
||||
languages.h
|
||||
light_source.h
|
||||
linked_list.h
|
||||
@ -201,19 +196,35 @@ SET(common_headers
|
||||
spdat.h
|
||||
string_util.h
|
||||
struct_strategy.h
|
||||
tcp_basic_server.h
|
||||
tcp_connection.h
|
||||
tcp_server.h
|
||||
textures.h
|
||||
timeoutmgr.h
|
||||
timer.h
|
||||
types.h
|
||||
unix.h
|
||||
useperl.h
|
||||
version.h
|
||||
worldconn.h
|
||||
xml_parser.h
|
||||
zone_numbers.h
|
||||
event/background_task.h
|
||||
event/event_loop.h
|
||||
event/timer.h
|
||||
json/json.h
|
||||
json/json-forwards.h
|
||||
net/console_server.h
|
||||
net/console_server_connection.h
|
||||
net/crc32.h
|
||||
net/daybreak_connection.h
|
||||
net/daybreak_structs.h
|
||||
net/dns.h
|
||||
net/endian.h
|
||||
net/eqstream.h
|
||||
net/packet.h
|
||||
net/servertalk_client_connection.h
|
||||
net/servertalk_legacy_client_connection.h
|
||||
net/servertalk_common.h
|
||||
net/servertalk_server.h
|
||||
net/servertalk_server_connection.h
|
||||
net/tcp_connection.h
|
||||
net/tcp_server.h
|
||||
patches/patches.h
|
||||
patches/sod.h
|
||||
patches/sod_limits.h
|
||||
@ -242,21 +253,57 @@ SET(common_headers
|
||||
patches/uf_limits.h
|
||||
patches/uf_ops.h
|
||||
patches/uf_structs.h
|
||||
SocketLib/Base64.h
|
||||
SocketLib/File.h
|
||||
SocketLib/HttpdCookies.h
|
||||
SocketLib/HttpdForm.h
|
||||
SocketLib/HttpdSocket.h
|
||||
SocketLib/HTTPSocket.h
|
||||
SocketLib/IFile.h
|
||||
SocketLib/MemFile.h
|
||||
SocketLib/Mime.h
|
||||
SocketLib/Parse.h
|
||||
SocketLib/socket_include.h
|
||||
SocketLib/Utility.h
|
||||
StackWalker/StackWalker.h
|
||||
tinyxml/tinystr.h
|
||||
tinyxml/tinyxml.h
|
||||
util/memory_stream.h
|
||||
util/uuid.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Event FILES
|
||||
event/background_task.h
|
||||
event/event_loop.cpp
|
||||
event/event_loop.h
|
||||
event/timer.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Json FILES
|
||||
json/json.h
|
||||
json/jsoncpp.cpp
|
||||
json/json-forwards.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Net FILES
|
||||
net/console_server.cpp
|
||||
net/console_server.h
|
||||
net/console_server_connection.cpp
|
||||
net/console_server_connection.h
|
||||
net/crc32.cpp
|
||||
net/crc32.h
|
||||
net/daybreak_connection.cpp
|
||||
net/daybreak_connection.h
|
||||
net/daybreak_structs.h
|
||||
net/dns.h
|
||||
net/endian.h
|
||||
net/eqmq.cpp
|
||||
net/eqmq.h
|
||||
net/eqstream.cpp
|
||||
net/eqstream.h
|
||||
net/packet.cpp
|
||||
net/packet.h
|
||||
net/servertalk_client_connection.cpp
|
||||
net/servertalk_client_connection.h
|
||||
net/servertalk_legacy_client_connection.cpp
|
||||
net/servertalk_legacy_client_connection.h
|
||||
net/servertalk_common.h
|
||||
net/servertalk_server.cpp
|
||||
net/servertalk_server.h
|
||||
net/servertalk_server_connection.cpp
|
||||
net/servertalk_server_connection.h
|
||||
net/tcp_connection.cpp
|
||||
net/tcp_connection.h
|
||||
net/tcp_server.cpp
|
||||
net/tcp_server.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Patches FILES
|
||||
@ -303,32 +350,6 @@ SOURCE_GROUP(Patches FILES
|
||||
patches/uf_limits.cpp
|
||||
)
|
||||
|
||||
SOURCE_GROUP(SocketLib FILES
|
||||
SocketLib/Base64.h
|
||||
SocketLib/File.h
|
||||
SocketLib/HttpdCookies.h
|
||||
SocketLib/HttpdForm.h
|
||||
SocketLib/HttpdSocket.h
|
||||
SocketLib/HTTPSocket.h
|
||||
SocketLib/IFile.h
|
||||
SocketLib/MemFile.h
|
||||
SocketLib/Mime.h
|
||||
SocketLib/Parse.h
|
||||
SocketLib/socket_include.h
|
||||
SocketLib/Utility.h
|
||||
SocketLib/Base64.cpp
|
||||
SocketLib/File.cpp
|
||||
SocketLib/HttpdCookies.cpp
|
||||
SocketLib/HttpdForm.cpp
|
||||
SocketLib/HttpdSocket.cpp
|
||||
SocketLib/HTTPSocket.cpp
|
||||
SocketLib/MemFile.cpp
|
||||
SocketLib/Mime.cpp
|
||||
SocketLib/Parse.cpp
|
||||
SocketLib/socket_include.cpp
|
||||
SocketLib/Utility.cpp
|
||||
)
|
||||
|
||||
SOURCE_GROUP(StackWalker FILES
|
||||
StackWalker/StackWalker.h
|
||||
StackWalker/StackWalker.cpp
|
||||
@ -343,12 +364,17 @@ SOURCE_GROUP(TinyXML FILES
|
||||
tinyxml/tinyxmlparser.cpp
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Util FILES
|
||||
util/memory_stream.h
|
||||
util/uuid.cpp
|
||||
util/uuid.h
|
||||
)
|
||||
|
||||
INCLUDE_DIRECTORIES(Patches SocketLib StackWalker TinyXML)
|
||||
|
||||
ADD_LIBRARY(common ${common_sources} ${common_headers})
|
||||
|
||||
IF(UNIX)
|
||||
ADD_DEFINITIONS(-fPIC)
|
||||
SET_SOURCE_FILES_PROPERTIES("SocketLib/Mime.cpp" PROPERTY COMPILE_FLAGS -Wno-unused-result)
|
||||
SET_SOURCE_FILES_PROPERTIES("patches/sod.cpp" "patches/sof.cpp" "patches/rof.cpp" "patches/rof2.cpp" "patches/uf.cpp" PROPERTIES COMPILE_FLAGS -O0)
|
||||
ENDIF(UNIX)
|
||||
|
||||
@ -35,6 +35,7 @@ typedef enum {
|
||||
BT_Greater_Akheva = 14,
|
||||
BT_Khati_Sha = 15,
|
||||
BT_Seru = 16, //not confirmed....
|
||||
BT_Draz_Nurakk = 18,
|
||||
BT_Zek = 19,
|
||||
BT_Luggald = 20,
|
||||
BT_Animal = 21,
|
||||
|
||||
@ -1,818 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
There are really two or three different objects shoe-hored into this
|
||||
connection object. Sombody really needs to factor out the relay link
|
||||
crap into its own subclass of this object, it will clean things up
|
||||
tremendously.
|
||||
*/
|
||||
|
||||
#include "../common/global_define.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
#include "emu_tcp_connection.h"
|
||||
#include "emu_tcp_server.h"
|
||||
#include "../common/servertalk.h"
|
||||
|
||||
#ifdef FREEBSD //Timothy Whitman - January 7, 2003
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
#define TCPN_DEBUG 0
|
||||
#define TCPN_DEBUG_Console 0
|
||||
#define TCPN_DEBUG_Memory 0
|
||||
#define TCPN_LOG_PACKETS 0
|
||||
#define TCPN_LOG_RAW_DATA_OUT 0
|
||||
#define TCPN_LOG_RAW_DATA_IN 0
|
||||
|
||||
|
||||
//server side case
|
||||
EmuTCPConnection::EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, SOCKET in_socket, uint32 irIP, uint16 irPort, bool iOldFormat)
|
||||
: TCPConnection(ID, in_socket, irIP, irPort),
|
||||
keepalive_timer(SERVER_TIMEOUT),
|
||||
timeout_timer(SERVER_TIMEOUT * 2)
|
||||
{
|
||||
id = 0;
|
||||
Server = nullptr;
|
||||
pOldFormat = iOldFormat;
|
||||
#ifdef MINILOGIN
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeLogin;
|
||||
#else
|
||||
if (pOldFormat)
|
||||
TCPMode = modePacket;
|
||||
else
|
||||
TCPMode = modeConsole;
|
||||
PacketMode = packetModeZone;
|
||||
#endif
|
||||
RelayLink = 0;
|
||||
RelayServer = false;
|
||||
RelayCount = 0;
|
||||
RemoteID = 0;
|
||||
|
||||
}
|
||||
|
||||
//client outgoing connection case (and client side relay)
|
||||
EmuTCPConnection::EmuTCPConnection(bool iOldFormat, EmuTCPServer* iRelayServer, eTCPMode iMode)
|
||||
: TCPConnection(),
|
||||
keepalive_timer(SERVER_TIMEOUT),
|
||||
timeout_timer(SERVER_TIMEOUT * 2)
|
||||
{
|
||||
Server = iRelayServer;
|
||||
if (Server)
|
||||
RelayServer = true;
|
||||
else
|
||||
RelayServer = false;
|
||||
RelayLink = 0;
|
||||
RelayCount = 0;
|
||||
RemoteID = 0;
|
||||
pOldFormat = iOldFormat;
|
||||
TCPMode = iMode;
|
||||
PacketMode = packetModeZone;
|
||||
#if TCPN_DEBUG_Memory >= 7
|
||||
std::cout << "Constructor #1 on outgoing TCP# " << GetID() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
//server side relay case
|
||||
EmuTCPConnection::EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, EmuTCPConnection* iRelayLink, uint32 iRemoteID, uint32 irIP, uint16 irPort)
|
||||
: TCPConnection(ID, 0, irIP, irPort),
|
||||
keepalive_timer(SERVER_TIMEOUT),
|
||||
timeout_timer(SERVER_TIMEOUT * 2)
|
||||
{
|
||||
Server = iServer;
|
||||
RelayLink = iRelayLink;
|
||||
RelayServer = true;
|
||||
RelayCount = 0;
|
||||
RemoteID = iRemoteID;
|
||||
pOldFormat = false;
|
||||
ConnectionType = Incoming;
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeZone;
|
||||
#if TCPN_DEBUG_Memory >= 7
|
||||
std::cout << "Constructor #3 on outgoing TCP# " << GetID() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
EmuTCPConnection::~EmuTCPConnection() {
|
||||
//the queues free their content right now I believe.
|
||||
}
|
||||
|
||||
EmuTCPNetPacket_Struct* EmuTCPConnection::MakePacket(ServerPacket* pack, uint32 iDestination) {
|
||||
int32 size = sizeof(EmuTCPNetPacket_Struct) + pack->size;
|
||||
if (pack->compressed) {
|
||||
size += 4;
|
||||
}
|
||||
if (iDestination) {
|
||||
size += 4;
|
||||
}
|
||||
EmuTCPNetPacket_Struct* tnps = (EmuTCPNetPacket_Struct*) new uchar[size];
|
||||
tnps->size = size;
|
||||
tnps->opcode = pack->opcode;
|
||||
*((uint8*) &tnps->flags) = 0;
|
||||
uchar* buffer = tnps->buffer;
|
||||
if (pack->compressed) {
|
||||
tnps->flags.compressed = 1;
|
||||
*((int32*) buffer) = pack->InflatedSize;
|
||||
buffer += 4;
|
||||
}
|
||||
if (iDestination) {
|
||||
tnps->flags.destination = 1;
|
||||
*((int32*) buffer) = iDestination;
|
||||
buffer += 4;
|
||||
}
|
||||
memcpy(buffer, pack->pBuffer, pack->size);
|
||||
return tnps;
|
||||
}
|
||||
|
||||
SPackSendQueue* EmuTCPConnection::MakeOldPacket(ServerPacket* pack) {
|
||||
SPackSendQueue* spsq = (SPackSendQueue*) new uchar[sizeof(SPackSendQueue) + pack->size + 4];
|
||||
if (pack->pBuffer != 0 && pack->size != 0)
|
||||
memcpy((char *) &spsq->buffer[4], (char *) pack->pBuffer, pack->size);
|
||||
memcpy((char *) &spsq->buffer[0], (char *) &pack->opcode, 2);
|
||||
spsq->size = pack->size+4;
|
||||
memcpy((char *) &spsq->buffer[2], (char *) &spsq->size, 2);
|
||||
return spsq;
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::SendPacket(ServerPacket* pack, uint32 iDestination) {
|
||||
if (!Connected())
|
||||
return false;
|
||||
eTCPMode tmp = GetMode();
|
||||
if (tmp != modePacket && tmp != modeTransition)
|
||||
return false;
|
||||
LockMutex lock(&MState);
|
||||
if (RemoteID)
|
||||
return RelayLink->SendPacket(pack, RemoteID);
|
||||
else if (pOldFormat) {
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (pack && pack->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging outgoing TCP OldPacket. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << pack->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (pack->size >= 32)
|
||||
DumpPacket(pack->pBuffer, 32);
|
||||
else
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
SPackSendQueue* spsq = MakeOldPacket(pack);
|
||||
ServerSendQueuePushEnd(spsq->buffer, spsq->size);
|
||||
safe_delete_array(spsq);
|
||||
}
|
||||
else {
|
||||
EmuTCPNetPacket_Struct* tnps = MakePacket(pack, iDestination);
|
||||
if (tmp == modeTransition) {
|
||||
InModeQueuePush(tnps);
|
||||
}
|
||||
else {
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (pack && pack->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging outgoing TCP packet. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << pack->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (pack->size >= 32)
|
||||
DumpPacket(pack->pBuffer, 32);
|
||||
else
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
ServerSendQueuePushEnd((uchar**) &tnps, tnps->size);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::SendPacket(EmuTCPNetPacket_Struct* tnps) {
|
||||
if (RemoteID)
|
||||
return false;
|
||||
if (!Connected())
|
||||
return false;
|
||||
if (GetMode() != modePacket)
|
||||
return false;
|
||||
|
||||
LockMutex lock(&MState);
|
||||
eTCPMode tmp = GetMode();
|
||||
if (tmp == modeTransition) {
|
||||
EmuTCPNetPacket_Struct* tnps2 = (EmuTCPNetPacket_Struct*) new uchar[tnps->size];
|
||||
memcpy(tnps2, tnps, tnps->size);
|
||||
InModeQueuePush(tnps2);
|
||||
return true;
|
||||
}
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (tnps && tnps->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging outgoing TCP NetPacket. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << tnps->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << tnps->size << " " << inet_ntoa(in) << ":" << GetrPort();
|
||||
if (pOldFormat)
|
||||
std::cout << " (OldFormat)";
|
||||
std::cout << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (tnps->size >= 32)
|
||||
DumpPacket((uchar*) tnps, 32);
|
||||
else
|
||||
DumpPacket((uchar*) tnps, tnps->size);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket((uchar*) tnps, tnps->size);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
ServerSendQueuePushEnd((const uchar*) tnps, tnps->size);
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerPacket* EmuTCPConnection::PopPacket() {
|
||||
ServerPacket* ret;
|
||||
if (!MOutQueueLock.trylock())
|
||||
return nullptr;
|
||||
ret = OutQueue.pop();
|
||||
MOutQueueLock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EmuTCPConnection::InModeQueuePush(EmuTCPNetPacket_Struct* tnps) {
|
||||
MSendQueue.lock();
|
||||
InModeQueue.push(tnps);
|
||||
MSendQueue.unlock();
|
||||
}
|
||||
|
||||
void EmuTCPConnection::OutQueuePush(ServerPacket* pack) {
|
||||
MOutQueueLock.lock();
|
||||
OutQueue.push(pack);
|
||||
MOutQueueLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
bool EmuTCPConnection::LineOutQueuePush(char* line) {
|
||||
#if defined(GOTFRAGS) && 0
|
||||
if (strcmp(line, "**CRASHME**") == 0) {
|
||||
int i = 0;
|
||||
std::cout << (5 / i) << std::endl;
|
||||
}
|
||||
#endif
|
||||
if(line[0] == '*') {
|
||||
if (strcmp(line, "**PACKETMODE**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODE**\r", 16);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeLogin;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
if (strcmp(line, "**PACKETMODEZONE**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODEZONE**\r", 20);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeZone;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
if (strcmp(line, "**PACKETMODELAUNCHER**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODELAUNCHER**\r", 24);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeLauncher;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
if (strcmp(line, "**PACKETMODEUCS**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODEUCS**\r", 19);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeUCS;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
if (strcmp(line, "**PACKETMODEQS**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODEQS**\r", 18);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeQueryServ;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
||||
return(TCPConnection::LineOutQueuePush(line));
|
||||
}
|
||||
|
||||
void EmuTCPConnection::Disconnect(bool iSendRelayDisconnect) {
|
||||
TCPConnection::Disconnect();
|
||||
|
||||
if (RelayLink) {
|
||||
RelayLink->RemoveRelay(this, iSendRelayDisconnect);
|
||||
RelayLink = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::ConnectIP(uint32 irIP, uint16 irPort, char* errbuf) {
|
||||
if(!TCPConnection::ConnectIP(irIP, irPort, errbuf))
|
||||
return(false);
|
||||
|
||||
MSendQueue.lock();
|
||||
#ifdef MINILOGIN
|
||||
TCPMode = modePacket;
|
||||
#else
|
||||
if (pOldFormat) {
|
||||
TCPMode = modePacket;
|
||||
}
|
||||
else if (TCPMode == modePacket || TCPMode == modeTransition) {
|
||||
TCPMode = modeTransition;
|
||||
if(PacketMode == packetModeLauncher) {
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 24;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODELAUNCHER**\r", sendbuf_size);
|
||||
} else if(PacketMode == packetModeLogin) {
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 16;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODE**\r", sendbuf_size);
|
||||
} else if(PacketMode == packetModeUCS) {
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 19;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODEUCS**\r", sendbuf_size);
|
||||
}
|
||||
else if(PacketMode == packetModeQueryServ) {
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 18;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODEQS**\r", sendbuf_size);
|
||||
}
|
||||
else {
|
||||
//default: packetModeZone
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 20;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODEZONE**\r", sendbuf_size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MSendQueue.unlock();
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
void EmuTCPConnection::ClearBuffers() {
|
||||
TCPConnection::ClearBuffers();
|
||||
|
||||
LockMutex lock2(&MOutQueueLock);
|
||||
ServerPacket* pack = 0;
|
||||
while ((pack = OutQueue.pop()))
|
||||
safe_delete(pack);
|
||||
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop()))
|
||||
safe_delete(tnps);
|
||||
|
||||
keepalive_timer.Start();
|
||||
timeout_timer.Start();
|
||||
}
|
||||
|
||||
void EmuTCPConnection::SendNetErrorPacket(const char* reason) {
|
||||
#if TCPC_DEBUG >= 1
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
std::cout "NetError: '";
|
||||
if (reason)
|
||||
std::cout << reason;
|
||||
std::cout << "': " << inet_ntoa(in) << ":" << GetPort() << std::endl;
|
||||
#endif
|
||||
auto pack = new ServerPacket(0);
|
||||
pack->size = 1;
|
||||
if (reason)
|
||||
pack->size += strlen(reason) + 1;
|
||||
pack->pBuffer = new uchar[pack->size];
|
||||
memset(pack->pBuffer, 0, pack->size);
|
||||
pack->pBuffer[0] = 255;
|
||||
strcpy((char *)&pack->pBuffer[1], reason);
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void EmuTCPConnection::RemoveRelay(EmuTCPConnection* relay, bool iSendRelayDisconnect) {
|
||||
if (iSendRelayDisconnect) {
|
||||
auto pack = new ServerPacket(0, 5);
|
||||
pack->pBuffer[0] = 3;
|
||||
*((uint32*) &pack->pBuffer[1]) = relay->GetRemoteID();
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
RelayCount--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool EmuTCPConnection::ProcessReceivedData(char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
timeout_timer.Start();
|
||||
if (!recvbuf)
|
||||
return true;
|
||||
if (TCPMode == modePacket) {
|
||||
if (pOldFormat)
|
||||
return ProcessReceivedDataAsOldPackets(errbuf);
|
||||
else
|
||||
return ProcessReceivedDataAsPackets(errbuf);
|
||||
}
|
||||
//else, use the base class's text processing.
|
||||
bool ret = TCPConnection::ProcessReceivedData(errbuf);
|
||||
//see if we made the transition to packet mode...
|
||||
if(ret && TCPMode == modePacket) {
|
||||
return ProcessReceivedDataAsPackets(errbuf);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool EmuTCPConnection::ProcessReceivedDataAsPackets(char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
int32 base = 0;
|
||||
int32 size = 7;
|
||||
uchar* buffer;
|
||||
ServerPacket* pack = 0;
|
||||
while ((recvbuf_used - base) >= size) {
|
||||
EmuTCPNetPacket_Struct* tnps = (EmuTCPNetPacket_Struct*) &recvbuf[base];
|
||||
buffer = tnps->buffer;
|
||||
size = tnps->size;
|
||||
if (size >= MaxTCPReceiveBuffferSize) {
|
||||
#if TCPN_DEBUG_Memory >= 1
|
||||
std::cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBuffferSize" << std::endl;
|
||||
DumpPacket(&recvbuf[base], 16);
|
||||
#endif
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "EmuTCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBuffferSize");
|
||||
return false;
|
||||
}
|
||||
if ((recvbuf_used - base) >= size) {
|
||||
// ok, we got enough data to make this packet!
|
||||
pack = new ServerPacket;
|
||||
pack->size = size - sizeof(EmuTCPNetPacket_Struct);
|
||||
// read headers
|
||||
pack->opcode = tnps->opcode;
|
||||
if (tnps->flags.compressed) {
|
||||
pack->compressed = true;
|
||||
pack->InflatedSize = *((int32*)buffer);
|
||||
pack->size -= 4;
|
||||
buffer += 4;
|
||||
}
|
||||
if (tnps->flags.destination) {
|
||||
pack->destination = *((int32*)buffer);
|
||||
pack->size -= 4;
|
||||
buffer += 4;
|
||||
}
|
||||
// end read headers
|
||||
if (pack->size > 0) {
|
||||
if (tnps->flags.compressed) {
|
||||
// Lets decompress the packet here
|
||||
pack->compressed = false;
|
||||
pack->pBuffer = new uchar[pack->InflatedSize];
|
||||
pack->size = InflatePacket(buffer, pack->size, pack->pBuffer, pack->InflatedSize);
|
||||
}
|
||||
else {
|
||||
pack->pBuffer = new uchar[pack->size];
|
||||
memcpy(pack->pBuffer, buffer, pack->size);
|
||||
}
|
||||
}
|
||||
if (pack->opcode == 0) {
|
||||
if (pack->size) {
|
||||
#if TCPN_DEBUG >= 2
|
||||
std::cout << "Received TCP Network layer packet" << std::endl;
|
||||
#endif
|
||||
ProcessNetworkLayerPacket(pack);
|
||||
}
|
||||
#if TCPN_DEBUG >= 5
|
||||
else {
|
||||
std::cout << "Received TCP keepalive packet. (opcode=0)" << std::endl;
|
||||
}
|
||||
#endif
|
||||
// keepalive, no need to process
|
||||
safe_delete(pack);
|
||||
}
|
||||
else {
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (pack && pack->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging incoming TCP packet. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << pack->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (pack->size >= 32)
|
||||
DumpPacket(pack->pBuffer, 32);
|
||||
else
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (RelayServer && Server && pack->destination) {
|
||||
EmuTCPConnection* con = Server->FindConnection(pack->destination);
|
||||
if (!con) {
|
||||
#if TCPN_DEBUG >= 1
|
||||
std::cout << "Error relaying packet: con = 0" << std::endl;
|
||||
#endif
|
||||
safe_delete(pack);
|
||||
}
|
||||
else
|
||||
con->OutQueuePush(pack);
|
||||
}
|
||||
else
|
||||
OutQueuePush(pack);
|
||||
}
|
||||
base += size;
|
||||
size = 7;
|
||||
}
|
||||
}
|
||||
if (base != 0) {
|
||||
if (base >= recvbuf_used) {
|
||||
safe_delete_array(recvbuf);
|
||||
} else {
|
||||
auto tmpbuf = new uchar[recvbuf_size - base];
|
||||
memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base);
|
||||
safe_delete_array(recvbuf);
|
||||
recvbuf = tmpbuf;
|
||||
recvbuf_used -= base;
|
||||
recvbuf_size -= base;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::ProcessReceivedDataAsOldPackets(char* errbuf) {
|
||||
int32 base = 0;
|
||||
int32 size = 4;
|
||||
uchar* buffer;
|
||||
ServerPacket* pack = 0;
|
||||
while ((recvbuf_used - base) >= size) {
|
||||
buffer = &recvbuf[base];
|
||||
memcpy(&size, &buffer[2], 2);
|
||||
if (size >= MaxTCPReceiveBuffferSize) {
|
||||
#if TCPN_DEBUG_Memory >= 1
|
||||
std::cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBuffferSize" << std::endl;
|
||||
#endif
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "EmuTCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBuffferSize");
|
||||
return false;
|
||||
}
|
||||
if ((recvbuf_used - base) >= size) {
|
||||
// ok, we got enough data to make this packet!
|
||||
pack = new ServerPacket;
|
||||
memcpy(&pack->opcode, &buffer[0], 2);
|
||||
pack->size = size - 4;
|
||||
/* if () { // TODO: Checksum or size check or something similar
|
||||
// Datastream corruption, get the hell outta here!
|
||||
delete pack;
|
||||
return false;
|
||||
}*/
|
||||
if (pack->size > 0) {
|
||||
pack->pBuffer = new uchar[pack->size];
|
||||
memcpy(pack->pBuffer, &buffer[4], pack->size);
|
||||
}
|
||||
if (pack->opcode == 0) {
|
||||
// keepalive, no need to process
|
||||
safe_delete(pack);
|
||||
}
|
||||
else {
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (pack && pack->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging incoming TCP OldPacket. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << pack->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (pack->size >= 32)
|
||||
DumpPacket(pack->pBuffer, 32);
|
||||
else
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
OutQueuePush(pack);
|
||||
}
|
||||
base += size;
|
||||
size = 4;
|
||||
}
|
||||
}
|
||||
if (base != 0) {
|
||||
if (base >= recvbuf_used) {
|
||||
safe_delete_array(recvbuf);
|
||||
}
|
||||
else {
|
||||
auto tmpbuf = new uchar[recvbuf_size - base];
|
||||
memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base);
|
||||
safe_delete_array(recvbuf);
|
||||
recvbuf = tmpbuf;
|
||||
recvbuf_used -= base;
|
||||
recvbuf_size -= base;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmuTCPConnection::ProcessNetworkLayerPacket(ServerPacket* pack) {
|
||||
uint8 opcode = pack->pBuffer[0];
|
||||
uint8* data = &pack->pBuffer[1];
|
||||
switch (opcode) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case 1: { // Switch to RelayServer mode
|
||||
if (pack->size != 1) {
|
||||
SendNetErrorPacket("New RelayClient: wrong size, expected 1");
|
||||
break;
|
||||
}
|
||||
if (RelayServer) {
|
||||
SendNetErrorPacket("Switch to RelayServer mode when already in RelayServer mode");
|
||||
break;
|
||||
}
|
||||
if (RemoteID) {
|
||||
SendNetErrorPacket("Switch to RelayServer mode by a Relay Client");
|
||||
break;
|
||||
}
|
||||
if (ConnectionType != Incoming) {
|
||||
SendNetErrorPacket("Switch to RelayServer mode on outgoing connection");
|
||||
break;
|
||||
}
|
||||
#if TCPC_DEBUG >= 3
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
std::cout << "Switching to RelayServer mode: " << inet_ntoa(in) << ":" << GetPort() << std::endl;
|
||||
#endif
|
||||
RelayServer = true;
|
||||
break;
|
||||
}
|
||||
case 2: { // New Relay Client
|
||||
if (!RelayServer) {
|
||||
SendNetErrorPacket("New RelayClient when not in RelayServer mode");
|
||||
break;
|
||||
}
|
||||
if (pack->size != 11) {
|
||||
SendNetErrorPacket("New RelayClient: wrong size, expected 11");
|
||||
break;
|
||||
}
|
||||
if (ConnectionType != Incoming) {
|
||||
SendNetErrorPacket("New RelayClient: illegal on outgoing connection");
|
||||
break;
|
||||
}
|
||||
auto con = new EmuTCPConnection(Server->GetNextID(), Server, this, *((uint32 *)data),
|
||||
*((uint32 *)&data[4]), *((uint16 *)&data[8]));
|
||||
Server->AddConnection(con);
|
||||
RelayCount++;
|
||||
break;
|
||||
}
|
||||
case 3: { // Delete Relay Client
|
||||
if (!RelayServer) {
|
||||
SendNetErrorPacket("Delete RelayClient when not in RelayServer mode");
|
||||
break;
|
||||
}
|
||||
if (pack->size != 5) {
|
||||
SendNetErrorPacket("Delete RelayClient: wrong size, expected 5");
|
||||
break;
|
||||
}
|
||||
EmuTCPConnection* con = Server->FindConnection(*((uint32*)data));
|
||||
if (con) {
|
||||
if (ConnectionType == Incoming) {
|
||||
if (con->GetRelayLink() != this) {
|
||||
SendNetErrorPacket("Delete RelayClient: RelayLink != this");
|
||||
break;
|
||||
}
|
||||
}
|
||||
con->Disconnect(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 255: {
|
||||
#if TCPC_DEBUG >= 1
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
std::cout "Received NetError: '";
|
||||
if (pack->size > 1)
|
||||
std::cout << (char*) data;
|
||||
std::cout << "': " << inet_ntoa(in) << ":" << GetPort() << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::SendData(bool &sent_something, char* errbuf) {
|
||||
sent_something = false;
|
||||
if(!TCPConnection::SendData(sent_something, errbuf))
|
||||
return(false);
|
||||
|
||||
if(sent_something)
|
||||
keepalive_timer.Start();
|
||||
else if (TCPMode == modePacket && keepalive_timer.Check()) {
|
||||
auto pack = new ServerPacket(0, 0);
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
#if TCPN_DEBUG >= 5
|
||||
std::cout << "Sending TCP keepalive packet. (timeout=" << timeout_timer.GetRemainingTime() << " remaining)" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::RecvData(char* errbuf) {
|
||||
if(!TCPConnection::RecvData(errbuf)) {
|
||||
if (OutQueue.count())
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
if ((TCPMode == modePacket || TCPMode == modeTransition) && timeout_timer.Check()) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Connection timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
@ -1,104 +0,0 @@
|
||||
#ifndef EmuTCPCONNECTION_H_
|
||||
#define EmuTCPCONNECTION_H_
|
||||
|
||||
#include "tcp_connection.h"
|
||||
#include "timer.h"
|
||||
|
||||
//moved out of TCPConnection:: to be more exportable
|
||||
#pragma pack(1)
|
||||
struct EmuTCPNetPacket_Struct {
|
||||
uint32 size;
|
||||
struct {
|
||||
uint8
|
||||
compressed : 1,
|
||||
destination : 1,
|
||||
flag3 : 1,
|
||||
flag4 : 1,
|
||||
flag5 : 1,
|
||||
flag6 : 1,
|
||||
flag7 : 1,
|
||||
flag8 : 1;
|
||||
} flags;
|
||||
uint16 opcode;
|
||||
uchar buffer[0];
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
struct SPackSendQueue;
|
||||
class EmuTCPServer;
|
||||
class ServerPacket;
|
||||
|
||||
class EmuTCPConnection : public TCPConnection {
|
||||
public:
|
||||
enum eTCPMode { modeConsole, modeTransition, modePacket };
|
||||
enum ePacketMode { packetModeZone, packetModeLauncher, packetModeLogin, packetModeUCS, packetModeQueryServ };
|
||||
|
||||
EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, SOCKET iSock, uint32 irIP, uint16 irPort, bool iOldFormat = false);
|
||||
EmuTCPConnection(bool iOldFormat = false, EmuTCPServer* iRelayServer = 0, eTCPMode iMode = modePacket); // for outgoing connections
|
||||
EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, EmuTCPConnection* iRelayLink, uint32 iRemoteID, uint32 irIP, uint16 irPort); // for relay connections
|
||||
virtual ~EmuTCPConnection();
|
||||
|
||||
virtual bool ConnectIP(uint32 irIP, uint16 irPort, char* errbuf = 0);
|
||||
virtual void Disconnect(bool iSendRelayDisconnect = true);
|
||||
|
||||
static EmuTCPNetPacket_Struct* MakePacket(ServerPacket* pack, uint32 iDestination = 0);
|
||||
static SPackSendQueue* MakeOldPacket(ServerPacket* pack);
|
||||
|
||||
virtual bool SendPacket(ServerPacket* pack, uint32 iDestination = 0);
|
||||
virtual bool SendPacket(EmuTCPNetPacket_Struct* tnps);
|
||||
ServerPacket* PopPacket(); // OutQueuePop()
|
||||
void SetPacketMode(ePacketMode mode) { PacketMode = mode; }
|
||||
|
||||
eTCPMode GetMode() const { return TCPMode; }
|
||||
ePacketMode GetPacketMode() const { return(PacketMode); }
|
||||
|
||||
//relay crap:
|
||||
inline bool IsRelayServer() const { return RelayServer; }
|
||||
inline TCPConnection* GetRelayLink() const { return RelayLink; }
|
||||
inline uint32 GetRemoteID() const { return RemoteID; }
|
||||
|
||||
protected:
|
||||
void OutQueuePush(ServerPacket* pack);
|
||||
void RemoveRelay(EmuTCPConnection* relay, bool iSendRelayDisconnect);
|
||||
|
||||
void SendNetErrorPacket(const char* reason = 0);
|
||||
|
||||
virtual bool SendData(bool &sent_something, char* errbuf = 0);
|
||||
virtual bool RecvData(char* errbuf = 0);
|
||||
|
||||
virtual bool ProcessReceivedData(char* errbuf = 0);
|
||||
bool ProcessReceivedDataAsPackets(char* errbuf = 0);
|
||||
bool ProcessReceivedDataAsOldPackets(char* errbuf = 0);
|
||||
void ProcessNetworkLayerPacket(ServerPacket* pack);
|
||||
|
||||
virtual bool LineOutQueuePush(char* line);
|
||||
virtual void ClearBuffers();
|
||||
|
||||
EmuTCPServer* Server;
|
||||
|
||||
eTCPMode TCPMode;
|
||||
ePacketMode PacketMode;
|
||||
bool pOldFormat;
|
||||
|
||||
Timer keepalive_timer;
|
||||
Timer timeout_timer;
|
||||
|
||||
//relay crap:
|
||||
EmuTCPConnection* RelayLink;
|
||||
int32 RelayCount;
|
||||
bool RelayServer;
|
||||
uint32 RemoteID;
|
||||
|
||||
//input queue...
|
||||
void InModeQueuePush(EmuTCPNetPacket_Struct* tnps);
|
||||
MyQueue<EmuTCPNetPacket_Struct> InModeQueue;
|
||||
|
||||
//output queue...
|
||||
MyQueue<ServerPacket> OutQueue;
|
||||
Mutex MOutQueueLock;
|
||||
};
|
||||
|
||||
#endif /*EmuTCPCONNECTION_H_*/
|
||||
|
||||
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
#include "global_define.h"
|
||||
#include "emu_tcp_server.h"
|
||||
#include "emu_tcp_connection.h"
|
||||
|
||||
EmuTCPServer::EmuTCPServer(uint16 iPort, bool iOldFormat)
|
||||
: TCPServer<EmuTCPConnection>(iPort),
|
||||
pOldFormat(iOldFormat)
|
||||
{
|
||||
}
|
||||
|
||||
EmuTCPServer::~EmuTCPServer() {
|
||||
MInQueue.lock();
|
||||
while(!m_InQueue.empty()) {
|
||||
delete m_InQueue.front();
|
||||
m_InQueue.pop();
|
||||
}
|
||||
MInQueue.unlock();
|
||||
}
|
||||
|
||||
void EmuTCPServer::Process() {
|
||||
CheckInQueue();
|
||||
TCPServer<EmuTCPConnection>::Process();
|
||||
}
|
||||
|
||||
void EmuTCPServer::CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort)
|
||||
{
|
||||
auto conn = new EmuTCPConnection(ID, this, in_socket, irIP, irPort, pOldFormat);
|
||||
AddConnection(conn);
|
||||
}
|
||||
|
||||
|
||||
void EmuTCPServer::SendPacket(ServerPacket* pack) {
|
||||
EmuTCPNetPacket_Struct* tnps = EmuTCPConnection::MakePacket(pack);
|
||||
SendPacket(&tnps);
|
||||
}
|
||||
|
||||
void EmuTCPServer::SendPacket(EmuTCPNetPacket_Struct** tnps) {
|
||||
MInQueue.lock();
|
||||
m_InQueue.push(*tnps);
|
||||
MInQueue.unlock();
|
||||
tnps = nullptr;
|
||||
}
|
||||
|
||||
void EmuTCPServer::CheckInQueue() {
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
|
||||
while (( tnps = InQueuePop() )) {
|
||||
vitr cur, end;
|
||||
cur = m_list.begin();
|
||||
end = m_list.end();
|
||||
for(; cur != end; cur++) {
|
||||
if ((*cur)->GetMode() != EmuTCPConnection::modeConsole && (*cur)->GetRemoteID() == 0)
|
||||
(*cur)->SendPacket(tnps);
|
||||
}
|
||||
safe_delete(tnps);
|
||||
}
|
||||
}
|
||||
|
||||
EmuTCPNetPacket_Struct* EmuTCPServer::InQueuePop() {
|
||||
EmuTCPNetPacket_Struct* ret = nullptr;
|
||||
MInQueue.lock();
|
||||
if(!m_InQueue.empty()) {
|
||||
ret = m_InQueue.front();
|
||||
m_InQueue.pop();
|
||||
}
|
||||
MInQueue.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
EmuTCPConnection *EmuTCPServer::FindConnection(uint32 iID) {
|
||||
vitr cur, end;
|
||||
cur = m_list.begin();
|
||||
end = m_list.end();
|
||||
for(; cur != end; cur++) {
|
||||
if ((*cur)->GetID() == iID)
|
||||
return *cur;
|
||||
}
|
||||
return(nullptr);
|
||||
}
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
#ifndef EmuTCPSERVER_H_
|
||||
#define EmuTCPSERVER_H_
|
||||
|
||||
#include "tcp_server.h"
|
||||
|
||||
class EmuTCPConnection;
|
||||
struct EmuTCPNetPacket_Struct;
|
||||
class ServerPacket;
|
||||
|
||||
class EmuTCPServer : public TCPServer<EmuTCPConnection> {
|
||||
public:
|
||||
EmuTCPServer(uint16 iPort = 0, bool iOldFormat = false);
|
||||
virtual ~EmuTCPServer();
|
||||
|
||||
//packet broadcast routines.
|
||||
void SendPacket(ServerPacket* pack);
|
||||
void SendPacket(EmuTCPNetPacket_Struct** tnps);
|
||||
|
||||
//special crap for relay management
|
||||
EmuTCPConnection *FindConnection(uint32 iID);
|
||||
|
||||
//exposed for some crap we pull. Do not call from outside this object.
|
||||
using TCPServer<EmuTCPConnection>::AddConnection;
|
||||
|
||||
protected:
|
||||
virtual void Process();
|
||||
|
||||
virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort);
|
||||
|
||||
bool pOldFormat;
|
||||
|
||||
//broadcast packet queue..
|
||||
void CheckInQueue();
|
||||
Mutex MInQueue;
|
||||
EmuTCPNetPacket_Struct* InQueuePop(); //returns ownership
|
||||
std::queue<EmuTCPNetPacket_Struct *> m_InQueue;
|
||||
};
|
||||
#endif /*EmuTCPSERVER_H_*/
|
||||
@ -132,7 +132,7 @@ void EQApplicationPacket::build_header_dump(char *buffer) const
|
||||
#ifdef STATIC_OPCODE
|
||||
sprintf(buffer, "[OpCode 0x%04x Size=%u]\n", emu_opcode,size);
|
||||
#else
|
||||
sprintf(buffer, "[OpCode %s Size=%u]",OpcodeManager::EmuToName(emu_opcode),size);
|
||||
sprintf(buffer, "[OpCode %s(0x%04x) Size=%u]",OpcodeManager::EmuToName(emu_opcode), GetProtocolOpcode(), size);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -115,11 +115,14 @@ public:
|
||||
virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const;
|
||||
virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const;
|
||||
|
||||
uint16 GetOpcodeBypass() { return opcode_bypass; }
|
||||
uint16 GetOpcodeBypass() const { return opcode_bypass; }
|
||||
void SetOpcodeBypass(uint16 v) { opcode_bypass = v; }
|
||||
|
||||
uint16 GetProtocolOpcode() const { return protocol_opcode; }
|
||||
void SetProtocolOpcode(uint16 v) { protocol_opcode = v; }
|
||||
protected:
|
||||
|
||||
uint16 protocol_opcode;
|
||||
uint8 app_opcode_size;
|
||||
uint16 opcode_bypass;
|
||||
private:
|
||||
|
||||
@ -529,7 +529,7 @@ void EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req)
|
||||
if(pack->GetOpcodeBypass() != 0) {
|
||||
opcode = pack->GetOpcodeBypass();
|
||||
} else {
|
||||
opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode);
|
||||
opcode = (*OpMgr)->EmuToEQ(pack->GetOpcode());
|
||||
}
|
||||
|
||||
if (!ack_req) {
|
||||
|
||||
@ -241,7 +241,7 @@ class EQStream : public EQStreamInterface {
|
||||
virtual bool CheckState(EQStreamState state) { return GetState() == state; }
|
||||
virtual std::string Describe() const { return("Direct EQStream"); }
|
||||
|
||||
void SetOpcodeManager(OpcodeManager **opm) { OpMgr = opm; }
|
||||
virtual void SetOpcodeManager(OpcodeManager **opm) { OpMgr = opm; }
|
||||
|
||||
void CheckTimeout(uint32 now, uint32 timeout=30);
|
||||
bool HasOutgoingData();
|
||||
@ -250,13 +250,13 @@ class EQStream : public EQStreamInterface {
|
||||
void Write(int eq_fd);
|
||||
|
||||
// whether or not the stream has been assigned (we passed our stream match)
|
||||
void SetActive(bool val) { streamactive = val; }
|
||||
virtual void SetActive(bool val) { streamactive = val; }
|
||||
|
||||
//
|
||||
inline bool IsInUse() { bool flag; MInUse.lock(); flag=(active_users>0); MInUse.unlock(); return flag; }
|
||||
inline void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); }
|
||||
|
||||
inline EQStreamState GetState() { EQStreamState s; MState.lock(); s=State; MState.unlock(); return s; }
|
||||
virtual EQStreamState GetState() { EQStreamState s; MState.lock(); s=State; MState.unlock(); return s; }
|
||||
|
||||
static SeqOrder CompareSequence(uint16 expected_seq , uint16 seq);
|
||||
|
||||
@ -306,19 +306,7 @@ class EQStream : public EQStreamInterface {
|
||||
const uint64 GetPacketsReceived() { return received_packet_count; }
|
||||
|
||||
//used for dynamic stream identification
|
||||
class Signature {
|
||||
public:
|
||||
//this object could get more complicated if needed...
|
||||
uint16 ignore_eq_opcode; //0=dont ignore
|
||||
uint16 first_eq_opcode;
|
||||
uint32 first_length; //0=dont check length
|
||||
};
|
||||
typedef enum {
|
||||
MatchNotReady,
|
||||
MatchSuccessful,
|
||||
MatchFailed
|
||||
} MatchState;
|
||||
MatchState CheckSignature(const Signature *sig);
|
||||
virtual MatchState CheckSignature(const Signature *sig);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
#include "eqemu_logsys.h"
|
||||
#include "eq_stream_ident.h"
|
||||
#include "eq_stream_proxy.h"
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
EQStreamIdentifier::~EQStreamIdentifier() {
|
||||
while(!m_identified.empty()) {
|
||||
@ -26,7 +26,7 @@ EQStreamIdentifier::~EQStreamIdentifier() {
|
||||
}
|
||||
}
|
||||
|
||||
void EQStreamIdentifier::RegisterPatch(const EQStream::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs) {
|
||||
void EQStreamIdentifier::RegisterPatch(const EQStreamInterface::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs) {
|
||||
auto p = new Patch;
|
||||
p->signature = sig;
|
||||
p->name = name;
|
||||
@ -46,9 +46,9 @@ void EQStreamIdentifier::Process() {
|
||||
|
||||
//first see if this stream has expired
|
||||
if(r.expire.Check(false)) {
|
||||
//this stream has failed to match any pattern in our timeframe.
|
||||
Log(Logs::General, Logs::Netcode, "[IDENTIFY] Unable to identify stream from %s:%d before timeout.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()));
|
||||
r.stream->ReleaseFromUse();
|
||||
Log(Logs::General, Logs::Netcode, "[IDENTIFY] Unable to identify stream from %s:%d before timeout.", r.stream->GetRemoteAddr().c_str(), ntohs(r.stream->GetRemotePort()));
|
||||
r.stream->Close();
|
||||
|
||||
cur = m_streams.erase(cur);
|
||||
continue;
|
||||
}
|
||||
@ -98,14 +98,14 @@ void EQStreamIdentifier::Process() {
|
||||
Patch *p = *curp;
|
||||
|
||||
//ask the stream to see if it matches the supplied signature
|
||||
EQStream::MatchState res = r.stream->CheckSignature(&p->signature);
|
||||
EQStreamInterface::MatchState res = r.stream->CheckSignature(&p->signature);
|
||||
switch(res) {
|
||||
case EQStream::MatchNotReady:
|
||||
case EQStreamInterface::MatchNotReady:
|
||||
//the stream has not received enough packets to compare with this signature
|
||||
// Log.LogDebugType(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: Tried patch %s, but stream is not ready for it.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str());
|
||||
all_ready = false;
|
||||
break;
|
||||
case EQStream::MatchSuccessful: {
|
||||
case EQStreamInterface::MatchSuccessful: {
|
||||
//yay, a match.
|
||||
|
||||
Log(Logs::General, Logs::Netcode, "[IDENTIFY] Identified stream %s:%d with signature %s", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str());
|
||||
@ -120,7 +120,7 @@ void EQStreamIdentifier::Process() {
|
||||
found_one = true;
|
||||
break;
|
||||
}
|
||||
case EQStream::MatchFailed:
|
||||
case EQStreamInterface::MatchFailed:
|
||||
//do nothing...
|
||||
Log(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: Tried patch %s, and it did not match.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str());
|
||||
break;
|
||||
@ -144,7 +144,7 @@ void EQStreamIdentifier::Process() {
|
||||
} //end foreach stream
|
||||
}
|
||||
|
||||
void EQStreamIdentifier::AddStream(std::shared_ptr<EQStream> &eqs) {
|
||||
void EQStreamIdentifier::AddStream(std::shared_ptr<EQStreamInterface> eqs) {
|
||||
m_streams.push_back(Record(eqs));
|
||||
eqs = nullptr;
|
||||
}
|
||||
@ -157,7 +157,7 @@ EQStreamInterface *EQStreamIdentifier::PopIdentified() {
|
||||
return(res);
|
||||
}
|
||||
|
||||
EQStreamIdentifier::Record::Record(std::shared_ptr<EQStream> s)
|
||||
EQStreamIdentifier::Record::Record(std::shared_ptr<EQStreamInterface> s)
|
||||
: stream(std::move(s)),
|
||||
expire(STREAM_IDENT_WAIT_MS)
|
||||
{
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
#ifndef EQSTREAMIDENT_H_
|
||||
#define EQSTREAMIDENT_H_
|
||||
|
||||
#include "eq_stream.h"
|
||||
#include "eq_stream_intf.h"
|
||||
#include "timer.h"
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
|
||||
#define STREAM_IDENT_WAIT_MS 10000
|
||||
#define STREAM_IDENT_WAIT_MS 30000
|
||||
|
||||
class OpcodeManager;
|
||||
class StructStrategy;
|
||||
@ -18,11 +18,11 @@ public:
|
||||
~EQStreamIdentifier();
|
||||
|
||||
//registration interface.
|
||||
void RegisterPatch(const EQStream::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs);
|
||||
void RegisterPatch(const EQStreamInterface::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs);
|
||||
|
||||
//main processing interface
|
||||
void Process();
|
||||
void AddStream(std::shared_ptr<EQStream> &eqs);
|
||||
void AddStream(std::shared_ptr<EQStreamInterface> eqs);
|
||||
EQStreamInterface *PopIdentified();
|
||||
|
||||
protected:
|
||||
@ -31,7 +31,7 @@ protected:
|
||||
class Patch {
|
||||
public:
|
||||
std::string name;
|
||||
EQStream::Signature signature;
|
||||
EQStreamInterface::Signature signature;
|
||||
OpcodeManager ** opcodes;
|
||||
const StructStrategy *structs;
|
||||
};
|
||||
@ -40,8 +40,8 @@ protected:
|
||||
//pending streams..
|
||||
class Record {
|
||||
public:
|
||||
Record(std::shared_ptr<EQStream> s);
|
||||
std::shared_ptr<EQStream> stream; //we own this
|
||||
Record(std::shared_ptr<EQStreamInterface> s);
|
||||
std::shared_ptr<EQStreamInterface> stream; //we own this
|
||||
Timer expire;
|
||||
};
|
||||
std::vector<Record> m_streams; //we own these objects, and the streams contained in them.
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include "emu_versions.h"
|
||||
#include "eq_packet.h"
|
||||
|
||||
typedef enum {
|
||||
ESTABLISHED,
|
||||
@ -15,21 +16,40 @@ typedef enum {
|
||||
} EQStreamState;
|
||||
|
||||
class EQApplicationPacket;
|
||||
class OpcodeManager;
|
||||
|
||||
class EQStreamInterface {
|
||||
public:
|
||||
virtual ~EQStreamInterface() {}
|
||||
|
||||
class Signature {
|
||||
public:
|
||||
//this object could get more complicated if needed...
|
||||
uint16 ignore_eq_opcode; //0=dont ignore
|
||||
uint16 first_eq_opcode;
|
||||
uint32 first_length; //0=dont check length
|
||||
};
|
||||
typedef enum {
|
||||
MatchNotReady,
|
||||
MatchSuccessful,
|
||||
MatchFailed
|
||||
} MatchState;
|
||||
|
||||
virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req=true) = 0;
|
||||
virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req=true) = 0;
|
||||
virtual EQApplicationPacket *PopPacket() = 0;
|
||||
virtual void Close() = 0;
|
||||
virtual void ReleaseFromUse() = 0;
|
||||
virtual void RemoveData() = 0;
|
||||
virtual std::string GetRemoteAddr() const = 0;
|
||||
virtual uint32 GetRemoteIP() const = 0;
|
||||
virtual uint16 GetRemotePort() const = 0;
|
||||
virtual bool CheckState(EQStreamState state) = 0;
|
||||
virtual std::string Describe() const = 0;
|
||||
virtual void SetActive(bool val) { }
|
||||
virtual MatchState CheckSignature(const Signature *sig) { return MatchFailed; }
|
||||
virtual EQStreamState GetState() = 0;
|
||||
virtual void SetOpcodeManager(OpcodeManager **opm) = 0;
|
||||
|
||||
virtual const uint32 GetBytesSent() const { return 0; }
|
||||
virtual const uint32 GetBytesRecieved() const { return 0; }
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
|
||||
#include "global_define.h"
|
||||
#include "eq_stream_proxy.h"
|
||||
#include "eq_stream.h"
|
||||
#include "struct_strategy.h"
|
||||
|
||||
|
||||
EQStreamProxy::EQStreamProxy(std::shared_ptr<EQStream> &stream, const StructStrategy *structs, OpcodeManager **opcodes)
|
||||
EQStreamProxy::EQStreamProxy(std::shared_ptr<EQStreamInterface> &stream, const StructStrategy *structs, OpcodeManager **opcodes)
|
||||
: m_stream(stream),
|
||||
m_structs(structs),
|
||||
m_opcodes(opcodes)
|
||||
@ -26,6 +25,16 @@ const EQEmu::versions::ClientVersion EQStreamProxy::ClientVersion() const
|
||||
return m_structs->ClientVersion();
|
||||
}
|
||||
|
||||
EQStreamState EQStreamProxy::GetState()
|
||||
{
|
||||
return m_stream->GetState();
|
||||
}
|
||||
|
||||
void EQStreamProxy::SetOpcodeManager(OpcodeManager **opm)
|
||||
{
|
||||
return m_stream->SetOpcodeManager(opm);
|
||||
}
|
||||
|
||||
void EQStreamProxy::QueuePacket(const EQApplicationPacket *p, bool ack_req) {
|
||||
if(p == nullptr)
|
||||
return;
|
||||
@ -54,6 +63,10 @@ void EQStreamProxy::Close() {
|
||||
m_stream->Close();
|
||||
}
|
||||
|
||||
std::string EQStreamProxy::GetRemoteAddr() const {
|
||||
return(m_stream->GetRemoteAddr());
|
||||
}
|
||||
|
||||
uint32 EQStreamProxy::GetRemoteIP() const {
|
||||
return(m_stream->GetRemoteIP());
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "eq_stream_intf.h"
|
||||
#include "eq_stream.h"
|
||||
#include <memory>
|
||||
|
||||
class StructStrategy;
|
||||
@ -14,7 +13,7 @@ class EQApplicationPacket;
|
||||
class EQStreamProxy : public EQStreamInterface {
|
||||
public:
|
||||
//takes ownership of the stream.
|
||||
EQStreamProxy(std::shared_ptr<EQStream> &stream, const StructStrategy *structs, OpcodeManager **opcodes);
|
||||
EQStreamProxy(std::shared_ptr<EQStreamInterface> &stream, const StructStrategy *structs, OpcodeManager **opcodes);
|
||||
virtual ~EQStreamProxy();
|
||||
|
||||
//EQStreamInterface:
|
||||
@ -22,6 +21,7 @@ public:
|
||||
virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req=true);
|
||||
virtual EQApplicationPacket *PopPacket();
|
||||
virtual void Close();
|
||||
virtual std::string GetRemoteAddr() const;
|
||||
virtual uint32 GetRemoteIP() const;
|
||||
virtual uint16 GetRemotePort() const;
|
||||
virtual void ReleaseFromUse();
|
||||
@ -29,6 +29,8 @@ public:
|
||||
virtual bool CheckState(EQStreamState state);
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQEmu::versions::ClientVersion ClientVersion() const;
|
||||
virtual EQStreamState GetState();
|
||||
virtual void SetOpcodeManager(OpcodeManager **opm);
|
||||
|
||||
virtual const uint32 GetBytesSent() const;
|
||||
virtual const uint32 GetBytesRecieved() const;
|
||||
@ -36,8 +38,8 @@ public:
|
||||
virtual const uint32 GetBytesRecvPerSecond() const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<EQStream> const m_stream; //we own this stream object.
|
||||
const StructStrategy *const m_structs; //we do not own this object.
|
||||
std::shared_ptr<EQStreamInterface> const m_stream; //we own this stream object.
|
||||
const StructStrategy *const m_structs; //we do not own this object.
|
||||
//this is a pointer to a pointer to make it less likely that a packet will
|
||||
//reference an invalid opcode manager when they are being reloaded.
|
||||
OpcodeManager **const m_opcodes; //we do not own this object.
|
||||
|
||||
@ -66,6 +66,10 @@ void EQEmuConfig::do_world(TiXmlElement *ele)
|
||||
if (text) {
|
||||
LoginPort = atoi(text);
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "legacy", true);
|
||||
if (text) {
|
||||
LoginLegacy = atoi(text) > 0 ? true : false;
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "account", true);
|
||||
if (text) {
|
||||
LoginAccount = text;
|
||||
@ -89,6 +93,10 @@ void EQEmuConfig::do_world(TiXmlElement *ele)
|
||||
if (text) {
|
||||
loginconfig->LoginPort = atoi(text);
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "legacy", true);
|
||||
if (text) {
|
||||
loginconfig->LoginLegacy = atoi(text) > 0 ? true : false;
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "account", true);
|
||||
if (text) {
|
||||
loginconfig->LoginAccount = text;
|
||||
@ -117,11 +125,24 @@ void EQEmuConfig::do_world(TiXmlElement *ele)
|
||||
if (text) {
|
||||
WorldTCPPort = atoi(text);
|
||||
}
|
||||
text = sub_ele->Attribute("telnet");
|
||||
if (text && !strcasecmp(text, "enabled")) {
|
||||
}
|
||||
|
||||
sub_ele = ele->FirstChildElement("telnet");
|
||||
if (sub_ele != nullptr) {
|
||||
text = sub_ele->Attribute("ip");
|
||||
if (text) {
|
||||
TelnetIP = text;
|
||||
}
|
||||
text = sub_ele->Attribute("port");
|
||||
if (text) {
|
||||
TelnetTCPPort = atoi(text);
|
||||
}
|
||||
text = sub_ele->Attribute("enabled");
|
||||
if (text && !strcasecmp(text, "true")) {
|
||||
TelnetEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the <http> element
|
||||
sub_ele = ele->FirstChildElement("http");
|
||||
if (sub_ele != nullptr) {
|
||||
@ -370,6 +391,9 @@ std::string EQEmuConfig::GetByName(const std::string &var_name) const
|
||||
if (var_name == "LoginPort") {
|
||||
return (itoa(LoginPort));
|
||||
}
|
||||
if (var_name == "LoginLegacy") {
|
||||
return (itoa(LoginLegacy ? 1 : 0));
|
||||
}
|
||||
if (var_name == "Locked") {
|
||||
return (Locked ? "true" : "false");
|
||||
}
|
||||
@ -379,6 +403,12 @@ std::string EQEmuConfig::GetByName(const std::string &var_name) const
|
||||
if (var_name == "WorldIP") {
|
||||
return (WorldIP);
|
||||
}
|
||||
if (var_name == "TelnetTCPPort") {
|
||||
return (itoa(TelnetTCPPort));
|
||||
}
|
||||
if (var_name == "TelnetIP") {
|
||||
return (TelnetIP);
|
||||
}
|
||||
if (var_name == "TelnetEnabled") {
|
||||
return (TelnetEnabled ? "true" : "false");
|
||||
}
|
||||
@ -495,9 +525,12 @@ void EQEmuConfig::Dump() const
|
||||
std::cout << "LoginAccount = " << LoginAccount << std::endl;
|
||||
std::cout << "LoginPassword = " << LoginPassword << std::endl;
|
||||
std::cout << "LoginPort = " << LoginPort << std::endl;
|
||||
std::cout << "LoginLegacy = " << LoginLegacy << std::endl;
|
||||
std::cout << "Locked = " << Locked << std::endl;
|
||||
std::cout << "WorldTCPPort = " << WorldTCPPort << std::endl;
|
||||
std::cout << "WorldIP = " << WorldIP << std::endl;
|
||||
std::cout << "TelnetTCPPort = " << TelnetTCPPort << std::endl;
|
||||
std::cout << "TelnetIP = " << TelnetIP << std::endl;
|
||||
std::cout << "TelnetEnabled = " << TelnetEnabled << std::endl;
|
||||
std::cout << "WorldHTTPPort = " << WorldHTTPPort << std::endl;
|
||||
std::cout << "WorldHTTPMimeFile = " << WorldHTTPMimeFile << std::endl;
|
||||
|
||||
@ -26,6 +26,7 @@ struct LoginConfig {
|
||||
std::string LoginAccount;
|
||||
std::string LoginPassword;
|
||||
uint16 LoginPort;
|
||||
bool LoginLegacy;
|
||||
};
|
||||
|
||||
class EQEmuConfig : public XMLParser
|
||||
@ -42,11 +43,14 @@ class EQEmuConfig : public XMLParser
|
||||
std::string LoginAccount;
|
||||
std::string LoginPassword;
|
||||
uint16 LoginPort;
|
||||
bool LoginLegacy;
|
||||
uint32 LoginCount;
|
||||
LinkedList<LoginConfig*> loginlist;
|
||||
bool Locked;
|
||||
uint16 WorldTCPPort;
|
||||
std::string WorldIP;
|
||||
uint16 TelnetTCPPort;
|
||||
std::string TelnetIP;
|
||||
bool TelnetEnabled;
|
||||
int32 MaxClients;
|
||||
bool WorldHTTPEnabled;
|
||||
@ -127,11 +131,13 @@ class EQEmuConfig : public XMLParser
|
||||
#include "eqemu_config_elements.h"
|
||||
// Set sane defaults
|
||||
// Login server
|
||||
LoginHost = "eqemulator.net";
|
||||
LoginHost = "login.eqemulator.net";
|
||||
LoginPort = 5998;
|
||||
LoginLegacy = false;
|
||||
// World
|
||||
Locked = false;
|
||||
WorldTCPPort = 9000;
|
||||
TelnetTCPPort = 9001;
|
||||
TelnetEnabled = false;
|
||||
WorldHTTPEnabled = false;
|
||||
WorldHTTPPort = 9080;
|
||||
@ -186,6 +192,7 @@ class EQEmuConfig : public XMLParser
|
||||
DefaultStatus = 0;
|
||||
// For where zones need to connect to.
|
||||
WorldIP = "127.0.0.1";
|
||||
TelnetIP = "127.0.0.1";
|
||||
// Dynamics to start
|
||||
//DynamicCount=5;
|
||||
MaxClients = -1;
|
||||
|
||||
@ -103,6 +103,7 @@ void EQEmuLogSys::LoadLogSettingsDefaults()
|
||||
log_settings[Logs::Crash].log_to_console = Logs::General;
|
||||
log_settings[Logs::MySQLError].log_to_console = Logs::General;
|
||||
log_settings[Logs::Login_Server].log_to_console = Logs::General;
|
||||
log_settings[Logs::Headless_Client].log_to_console = Logs::General;
|
||||
|
||||
/* Set Category enabled status on defaults */
|
||||
log_settings[Logs::World_Server].is_category_enabled = 1;
|
||||
@ -128,6 +129,8 @@ void EQEmuLogSys::LoadLogSettingsDefaults()
|
||||
platform_file_name = "login";
|
||||
else if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformLaunch)
|
||||
platform_file_name = "launcher";
|
||||
else if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformHC)
|
||||
platform_file_name = "hc";
|
||||
}
|
||||
|
||||
std::string EQEmuLogSys::FormatOutMessageString(uint16 log_category, const std::string &in_message)
|
||||
|
||||
@ -20,11 +20,11 @@
|
||||
#ifndef EQEMU_LOGSYS_H
|
||||
#define EQEMU_LOGSYS_H
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <stdio.h>
|
||||
#include <functional>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Logs {
|
||||
@ -85,6 +85,7 @@ enum LogCategory {
|
||||
Client_Server_Packet_With_Dump,
|
||||
Login_Server,
|
||||
Client_Login,
|
||||
Headless_Client,
|
||||
MaxCategoryID /* Don't Remove this*/
|
||||
};
|
||||
|
||||
@ -143,6 +144,11 @@ static const char* LogCategoryName[LogCategory::MaxCategoryID] = {
|
||||
LogSys.Out(debug_level, log_category, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogF(debug_level, log_category, message, ...) do {\
|
||||
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
||||
LogSys.OutF(debug_level, log_category, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
class EQEmuLogSys {
|
||||
public:
|
||||
EQEmuLogSys();
|
||||
@ -164,6 +170,13 @@ public:
|
||||
void SetCurrentTimeStamp(char* time_stamp); /* Used in file logs to prepend a timestamp entry for logs */
|
||||
void StartFileLogs(const std::string &log_name = ""); /* Used to declare the processes file log and to keep it open for later use */
|
||||
|
||||
template <typename... Args>
|
||||
void OutF(Logs::DebugLevel debug_level, uint16 log_category, const char *fmt, const Args&... args)
|
||||
{
|
||||
std::string log_str = fmt::format(fmt, args...);
|
||||
Out(debug_level, log_category, log_str);
|
||||
}
|
||||
|
||||
/*
|
||||
LogSettings Struct
|
||||
|
||||
@ -204,9 +217,9 @@ private:
|
||||
|
||||
uint16 GetWindowsConsoleColorFromCategory(uint16 log_category); /* Windows console color messages mapped by category */
|
||||
|
||||
void ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessConsoleMessage called via Log.Out */
|
||||
void ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessGMSay called via Log.Out */
|
||||
void ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessLogWrite called via Log.Out */
|
||||
void ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessConsoleMessage called via Log */
|
||||
void ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessGMSay called via Log */
|
||||
void ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessLogWrite called via Log */
|
||||
};
|
||||
|
||||
extern EQEmuLogSys LogSys;
|
||||
|
||||
39
common/event/background_task.h
Normal file
39
common/event/background_task.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include "event_loop.h"
|
||||
|
||||
namespace EQ {
|
||||
class BackgroundTask
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(void)> BackgroundTaskFunction;
|
||||
struct BackgroundTaskBaton
|
||||
{
|
||||
BackgroundTaskFunction fn;
|
||||
BackgroundTaskFunction on_finish;
|
||||
};
|
||||
|
||||
BackgroundTask(BackgroundTaskFunction fn, BackgroundTaskFunction on_finish) {
|
||||
uv_work_t *m_work = new uv_work_t;
|
||||
memset(m_work, 0, sizeof(uv_work_t));
|
||||
BackgroundTaskBaton *baton = new BackgroundTaskBaton();
|
||||
baton->fn = fn;
|
||||
baton->on_finish = on_finish;
|
||||
|
||||
m_work->data = baton;
|
||||
uv_queue_work(EventLoop::Get().Handle(), m_work, [](uv_work_t* req) {
|
||||
BackgroundTaskBaton *baton = (BackgroundTaskBaton*)req->data;
|
||||
baton->fn();
|
||||
}, [](uv_work_t* req, int status) {
|
||||
BackgroundTaskBaton *baton = (BackgroundTaskBaton*)req->data;
|
||||
baton->on_finish();
|
||||
delete baton;
|
||||
delete req;
|
||||
});
|
||||
}
|
||||
|
||||
~BackgroundTask() {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
0
common/event/event_loop.cpp
Normal file
0
common/event/event_loop.cpp
Normal file
37
common/event/event_loop.h
Normal file
37
common/event/event_loop.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <uv.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
class EventLoop
|
||||
{
|
||||
public:
|
||||
static EventLoop &Get() {
|
||||
static EventLoop inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
~EventLoop() {
|
||||
uv_loop_close(&m_loop);
|
||||
}
|
||||
|
||||
void Process() {
|
||||
uv_run(&m_loop, UV_RUN_NOWAIT);
|
||||
}
|
||||
|
||||
uv_loop_t* Handle() { return &m_loop; }
|
||||
|
||||
private:
|
||||
EventLoop() {
|
||||
memset(&m_loop, 0, sizeof(uv_loop_t));
|
||||
uv_loop_init(&m_loop);
|
||||
}
|
||||
|
||||
EventLoop(const EventLoop&);
|
||||
EventLoop& operator=(const EventLoop&);
|
||||
|
||||
uv_loop_t m_loop;
|
||||
};
|
||||
}
|
||||
67
common/event/timer.h
Normal file
67
common/event/timer.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include "event_loop.h"
|
||||
|
||||
namespace EQ {
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
Timer(std::function<void(Timer *)> cb)
|
||||
{
|
||||
m_timer = nullptr;
|
||||
m_cb = cb;
|
||||
}
|
||||
|
||||
Timer(uint64_t duration_ms, bool repeats, std::function<void(Timer *)> cb)
|
||||
{
|
||||
m_timer = nullptr;
|
||||
m_cb = cb;
|
||||
Start(duration_ms, repeats);
|
||||
}
|
||||
|
||||
~Timer()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Start(uint64_t duration_ms, bool repeats) {
|
||||
auto loop = EventLoop::Get().Handle();
|
||||
if (!m_timer) {
|
||||
m_timer = new uv_timer_t;
|
||||
memset(m_timer, 0, sizeof(uv_timer_t));
|
||||
uv_timer_init(loop, m_timer);
|
||||
m_timer->data = this;
|
||||
|
||||
if (repeats) {
|
||||
uv_timer_start(m_timer, [](uv_timer_t *handle) {
|
||||
Timer *t = (Timer*)handle->data;
|
||||
t->Execute();
|
||||
}, duration_ms, duration_ms);
|
||||
}
|
||||
else {
|
||||
uv_timer_start(m_timer, [](uv_timer_t *handle) {
|
||||
Timer *t = (Timer*)handle->data;
|
||||
t->Stop();
|
||||
t->Execute();
|
||||
}, duration_ms, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
if (m_timer) {
|
||||
uv_close((uv_handle_t*)m_timer, [](uv_handle_t* handle) {
|
||||
delete handle;
|
||||
});
|
||||
m_timer = nullptr;
|
||||
}
|
||||
}
|
||||
private:
|
||||
void Execute() {
|
||||
m_cb(this);
|
||||
}
|
||||
|
||||
uv_timer_t *m_timer;
|
||||
std::function<void(Timer*)> m_cb;
|
||||
};
|
||||
}
|
||||
22
common/event_sub.cpp
Normal file
22
common/event_sub.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "event_sub.h"
|
||||
#include <string.h>
|
||||
|
||||
void EventSubscriptionWatcher::Subscribe(const std::string &event_name)
|
||||
{
|
||||
m_subs[event_name] = 1;
|
||||
}
|
||||
|
||||
void EventSubscriptionWatcher::Unsubscribe(const std::string &event_name)
|
||||
{
|
||||
m_subs[event_name] = 0;
|
||||
}
|
||||
|
||||
bool EventSubscriptionWatcher::IsSubscribed(const std::string &event_name) const
|
||||
{
|
||||
auto iter = m_subs.find(event_name);
|
||||
if (iter != m_subs.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
28
common/event_sub.h
Normal file
28
common/event_sub.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class EventSubscriptionWatcher
|
||||
{
|
||||
public:
|
||||
~EventSubscriptionWatcher();
|
||||
|
||||
void Subscribe(const std::string &event_name);
|
||||
void Unsubscribe(const std::string &event_name);
|
||||
bool IsSubscribed(const std::string &event_name) const;
|
||||
|
||||
static EventSubscriptionWatcher *Get() {
|
||||
static EventSubscriptionWatcher* inst = nullptr;
|
||||
if(!inst) {
|
||||
inst = new EventSubscriptionWatcher();
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
private:
|
||||
EventSubscriptionWatcher() { }
|
||||
EventSubscriptionWatcher(const EventSubscriptionWatcher&);
|
||||
EventSubscriptionWatcher& operator=(const EventSubscriptionWatcher&);
|
||||
|
||||
std::unordered_map<std::string, bool> m_subs;
|
||||
};
|
||||
@ -160,7 +160,6 @@ enum { //timer settings, all in milliseconds
|
||||
ClientProximity_interval = 150,
|
||||
CombatEventTimer_expire = 12000,
|
||||
Tribute_duration = 600000,
|
||||
ZoneTimerResolution = 3, //sleep time between zone main loop runs (milliseconds)
|
||||
FeignMemoryDuration = 120000, // Duration player must feign death to clear zonewide agro.
|
||||
EnragedTimer = 360000,
|
||||
EnragedDurationTimer = 10000
|
||||
|
||||
330
common/json/json-forwards.h
Normal file
330
common/json/json-forwards.h
Normal file
@ -0,0 +1,330 @@
|
||||
/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
|
||||
/// It is intended to be used with #include "json/json-forwards.h"
|
||||
/// This header provides forward declaration for all JsonCpp types.
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// Beginning of content of file: LICENSE
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
The JsonCpp library's source code, including accompanying documentation,
|
||||
tests and demonstration applications, are licensed under the following
|
||||
conditions...
|
||||
|
||||
The author (Baptiste Lepilleur) explicitly disclaims copyright in all
|
||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||
this software is released into the Public Domain.
|
||||
|
||||
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
|
||||
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
|
||||
released under the terms of the MIT License (see below).
|
||||
|
||||
In jurisdictions which recognize Public Domain property, the user of this
|
||||
software may choose to accept it either as 1) Public Domain, 2) under the
|
||||
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||
Public Domain/MIT License conditions described here, as they choose.
|
||||
|
||||
The MIT License is about as close to Public Domain as a license can get, and is
|
||||
described in clear, concise terms at:
|
||||
|
||||
http://en.wikipedia.org/wiki/MIT_License
|
||||
|
||||
The full text of the MIT License follows:
|
||||
|
||||
========================================================================
|
||||
Copyright (c) 2007-2010 Baptiste Lepilleur
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
========================================================================
|
||||
(END LICENSE TEXT)
|
||||
|
||||
The MIT license is compatible with both the GPL and commercial
|
||||
software, affording one all of the rights of Public Domain with the
|
||||
minor nuisance of being required to keep the above copyright notice
|
||||
and license text in the source code. Note also that by accepting the
|
||||
Public Domain "license" you can re-license your copy using whatever
|
||||
license you like.
|
||||
|
||||
*/
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// End of content of file: LICENSE
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
|
||||
# define JSON_FORWARD_AMALGATED_H_INCLUDED
|
||||
/// If defined, indicates that the source file is amalgated
|
||||
/// to prevent private header inclusion.
|
||||
#define JSON_IS_AMALGAMATION
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// Beginning of content of file: include/json/config.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Distributed under MIT license, or public domain if desired and
|
||||
// recognized in your jurisdiction.
|
||||
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
|
||||
|
||||
#ifndef JSON_CONFIG_H_INCLUDED
|
||||
#define JSON_CONFIG_H_INCLUDED
|
||||
#include <stddef.h>
|
||||
#include <string> //typedef String
|
||||
#include <stdint.h> //typedef int64_t, uint64_t
|
||||
|
||||
/// If defined, indicates that json library is embedded in CppTL library.
|
||||
//# define JSON_IN_CPPTL 1
|
||||
|
||||
/// If defined, indicates that json may leverage CppTL library
|
||||
//# define JSON_USE_CPPTL 1
|
||||
/// If defined, indicates that cpptl vector based map should be used instead of
|
||||
/// std::map
|
||||
/// as Value container.
|
||||
//# define JSON_USE_CPPTL_SMALLMAP 1
|
||||
|
||||
// If non-zero, the library uses exceptions to report bad input instead of C
|
||||
// assertion macros. The default is to use exceptions.
|
||||
#ifndef JSON_USE_EXCEPTION
|
||||
#define JSON_USE_EXCEPTION 1
|
||||
#endif
|
||||
|
||||
/// If defined, indicates that the source file is amalgated
|
||||
/// to prevent private header inclusion.
|
||||
/// Remarks: it is automatically defined in the generated amalgated header.
|
||||
// #define JSON_IS_AMALGAMATION
|
||||
|
||||
#ifdef JSON_IN_CPPTL
|
||||
#include <cpptl/config.h>
|
||||
#ifndef JSON_USE_CPPTL
|
||||
#define JSON_USE_CPPTL 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JSON_IN_CPPTL
|
||||
#define JSON_API CPPTL_API
|
||||
#elif defined(JSON_DLL_BUILD)
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define JSON_API __declspec(dllexport)
|
||||
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
|
||||
#endif // if defined(_MSC_VER)
|
||||
#elif defined(JSON_DLL)
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define JSON_API __declspec(dllimport)
|
||||
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
|
||||
#endif // if defined(_MSC_VER)
|
||||
#endif // ifdef JSON_IN_CPPTL
|
||||
#if !defined(JSON_API)
|
||||
#define JSON_API
|
||||
#endif
|
||||
|
||||
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
|
||||
// integer
|
||||
// Storages, and 64 bits integer support is disabled.
|
||||
// #define JSON_NO_INT64 1
|
||||
|
||||
#if defined(_MSC_VER) // MSVC
|
||||
# if _MSC_VER <= 1200 // MSVC 6
|
||||
// Microsoft Visual Studio 6 only support conversion from __int64 to double
|
||||
// (no conversion from unsigned __int64).
|
||||
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
|
||||
// characters in the debug information)
|
||||
// All projects I've ever seen with VS6 were using this globally (not bothering
|
||||
// with pragma push/pop).
|
||||
# pragma warning(disable : 4786)
|
||||
# endif // MSVC 6
|
||||
|
||||
# if _MSC_VER >= 1500 // MSVC 2008
|
||||
/// Indicates that the following function is deprecated.
|
||||
# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
|
||||
# endif
|
||||
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
// In c++11 the override keyword allows you to explicity define that a function
|
||||
// is intended to override the base-class version. This makes the code more
|
||||
// managable and fixes a set of common hard-to-find bugs.
|
||||
#if __cplusplus >= 201103L
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT noexcept
|
||||
#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT throw()
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT noexcept
|
||||
#else
|
||||
# define JSONCPP_OVERRIDE
|
||||
# define JSONCPP_NOEXCEPT throw()
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_RVALUE_REFERENCES
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // MSVC >= 2010
|
||||
|
||||
#ifdef __clang__
|
||||
#if __has_feature(cxx_rvalue_references)
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // has_feature
|
||||
|
||||
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
|
||||
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // GXX_EXPERIMENTAL
|
||||
|
||||
#endif // __clang__ || __GNUC__
|
||||
|
||||
#endif // not defined JSON_HAS_RVALUE_REFERENCES
|
||||
|
||||
#ifndef JSON_HAS_RVALUE_REFERENCES
|
||||
#define JSON_HAS_RVALUE_REFERENCES 0
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
|
||||
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
|
||||
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
|
||||
# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
|
||||
# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
|
||||
# endif // GNUC version
|
||||
#endif // __clang__ || __GNUC__
|
||||
|
||||
#if !defined(JSONCPP_DEPRECATED)
|
||||
#define JSONCPP_DEPRECATED(message)
|
||||
#endif // if !defined(JSONCPP_DEPRECATED)
|
||||
|
||||
#if __GNUC__ >= 6
|
||||
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
#endif
|
||||
|
||||
#if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
# include "version.h"
|
||||
|
||||
# if JSONCPP_USING_SECURE_MEMORY
|
||||
# include "allocator.h" //typedef Allocator
|
||||
# endif
|
||||
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
namespace Json {
|
||||
typedef int Int;
|
||||
typedef unsigned int UInt;
|
||||
#if defined(JSON_NO_INT64)
|
||||
typedef int LargestInt;
|
||||
typedef unsigned int LargestUInt;
|
||||
#undef JSON_HAS_INT64
|
||||
#else // if defined(JSON_NO_INT64)
|
||||
// For Microsoft Visual use specific types as long long is not supported
|
||||
#if defined(_MSC_VER) // Microsoft Visual Studio
|
||||
typedef __int64 Int64;
|
||||
typedef unsigned __int64 UInt64;
|
||||
#else // if defined(_MSC_VER) // Other platforms, use long long
|
||||
typedef int64_t Int64;
|
||||
typedef uint64_t UInt64;
|
||||
#endif // if defined(_MSC_VER)
|
||||
typedef Int64 LargestInt;
|
||||
typedef UInt64 LargestUInt;
|
||||
#define JSON_HAS_INT64
|
||||
#endif // if defined(JSON_NO_INT64)
|
||||
#if JSONCPP_USING_SECURE_MEMORY
|
||||
#define JSONCPP_STRING std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_OSTREAM std::basic_ostream<char, std::char_traits<char>>
|
||||
#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_ISTREAM std::istream
|
||||
#else
|
||||
#define JSONCPP_STRING std::string
|
||||
#define JSONCPP_OSTRINGSTREAM std::ostringstream
|
||||
#define JSONCPP_OSTREAM std::ostream
|
||||
#define JSONCPP_ISTRINGSTREAM std::istringstream
|
||||
#define JSONCPP_ISTREAM std::istream
|
||||
#endif // if JSONCPP_USING_SECURE_MEMORY
|
||||
} // end namespace Json
|
||||
|
||||
#endif // JSON_CONFIG_H_INCLUDED
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// End of content of file: include/json/config.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// Beginning of content of file: include/json/forwards.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// Distributed under MIT license, or public domain if desired and
|
||||
// recognized in your jurisdiction.
|
||||
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
|
||||
|
||||
#ifndef JSON_FORWARDS_H_INCLUDED
|
||||
#define JSON_FORWARDS_H_INCLUDED
|
||||
|
||||
#if !defined(JSON_IS_AMALGAMATION)
|
||||
#include "config.h"
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
namespace Json {
|
||||
|
||||
// writer.h
|
||||
class FastWriter;
|
||||
class StyledWriter;
|
||||
|
||||
// reader.h
|
||||
class Reader;
|
||||
|
||||
// features.h
|
||||
class Features;
|
||||
|
||||
// value.h
|
||||
typedef unsigned int ArrayIndex;
|
||||
class StaticString;
|
||||
class Path;
|
||||
class PathArgument;
|
||||
class Value;
|
||||
class ValueIteratorBase;
|
||||
class ValueIterator;
|
||||
class ValueConstIterator;
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#endif // JSON_FORWARDS_H_INCLUDED
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// End of content of file: include/json/forwards.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
|
||||
2161
common/json/json.h
Normal file
2161
common/json/json.h
Normal file
File diff suppressed because it is too large
Load Diff
5311
common/json/jsoncpp.cpp
Normal file
5311
common/json/jsoncpp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
89
common/json_config.cpp
Normal file
89
common/json_config.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "json_config.h"
|
||||
#include <fstream>
|
||||
|
||||
EQ::JsonConfigFile::JsonConfigFile()
|
||||
{
|
||||
}
|
||||
|
||||
EQ::JsonConfigFile::JsonConfigFile(const Json::Value &value)
|
||||
{
|
||||
m_root = value;
|
||||
}
|
||||
|
||||
EQ::JsonConfigFile::~JsonConfigFile()
|
||||
{
|
||||
}
|
||||
|
||||
EQ::JsonConfigFile EQ::JsonConfigFile::Load(const std::string &filename)
|
||||
{
|
||||
JsonConfigFile ret;
|
||||
ret.m_root = Json::Value();
|
||||
|
||||
std::ifstream ifs;
|
||||
ifs.open(filename, std::ifstream::in);
|
||||
|
||||
if (!ifs.good()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
try {
|
||||
ifs >> ret.m_root;
|
||||
}
|
||||
catch (std::exception) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string EQ::JsonConfigFile::GetVariableString(const std::string &title, const std::string ¶meter, const std::string &default_value) {
|
||||
try {
|
||||
if (m_root.isMember(title) && m_root[title].isMember(parameter)) {
|
||||
return m_root[title][parameter].asString();
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
int EQ::JsonConfigFile::GetVariableInt(const std::string &title, const std::string ¶meter, const int default_value) {
|
||||
try {
|
||||
if (m_root.isMember(title) && m_root[title].isMember(parameter)) {
|
||||
return m_root[title][parameter].asInt();
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
bool EQ::JsonConfigFile::GetVariableBool(const std::string &title, const std::string ¶meter, const bool default_value) {
|
||||
try {
|
||||
if (m_root.isMember(title) && m_root[title].isMember(parameter)) {
|
||||
return m_root[title][parameter].asBool();
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
double EQ::JsonConfigFile::GetVariableDouble(const std::string &title, const std::string ¶meter, const double default_value) {
|
||||
try {
|
||||
if (m_root.isMember(title) && m_root[title].isMember(parameter)) {
|
||||
return m_root[title][parameter].asDouble();
|
||||
}
|
||||
}
|
||||
catch (std::exception) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return default_value;
|
||||
}
|
||||
26
common/json_config.h
Normal file
26
common/json_config.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "json/json.h"
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
class JsonConfigFile
|
||||
{
|
||||
public:
|
||||
JsonConfigFile(const Json::Value &value);
|
||||
~JsonConfigFile();
|
||||
|
||||
static JsonConfigFile Load(const std::string &filename);
|
||||
|
||||
std::string GetVariableString(const std::string &title, const std::string ¶meter, const std::string &default_value);
|
||||
int GetVariableInt(const std::string &title, const std::string ¶meter, const int default_value);
|
||||
bool GetVariableBool(const std::string &title, const std::string ¶meter, const bool default_value);
|
||||
double GetVariableDouble(const std::string &title, const std::string ¶meter, const double default_value);
|
||||
|
||||
Json::Value& RawHandle() { return m_root; }
|
||||
private:
|
||||
JsonConfigFile();
|
||||
Json::Value m_root;
|
||||
};
|
||||
|
||||
}
|
||||
78
common/net/console_server.cpp
Normal file
78
common/net/console_server.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "console_server.h"
|
||||
#include "../string_util.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
EQ::Net::ConsoleServer::ConsoleServer(const std::string &addr, int port)
|
||||
{
|
||||
m_server.reset(new EQ::Net::TCPServer());
|
||||
m_server->Listen(addr, port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||
ConsoleServerConnection *c = new ConsoleServerConnection(this, connection);
|
||||
m_connections.insert(std::make_pair(c->GetUUID(), std::unique_ptr<ConsoleServerConnection>(c)));
|
||||
});
|
||||
}
|
||||
|
||||
EQ::Net::ConsoleServer::~ConsoleServer()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServer::RegisterCall(const std::string &command, int status_required, const std::string& help_definition, ConsoleServerCallback fn)
|
||||
{
|
||||
m_commands[command] = { fn, status_required, help_definition };
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServer::RegisterLogin(ConsoleServerLoginCallback fn)
|
||||
{
|
||||
m_login = fn;
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServer::ConnectionDisconnected(ConsoleServerConnection *c)
|
||||
{
|
||||
auto iter = m_connections.find(c->GetUUID());
|
||||
if (iter != m_connections.end()) {
|
||||
m_connections.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServer::ProcessCommand(ConsoleServerConnection *c, const std::string &cmd)
|
||||
{
|
||||
auto split = SplitString(cmd, ' ');
|
||||
|
||||
if (split.size() > 0) {
|
||||
auto command = split[0];
|
||||
ToLowerString(command);
|
||||
|
||||
if (command == "help" || command == "?") {
|
||||
c->SendLine("Commands:");
|
||||
for (auto &cmd_def : m_commands) {
|
||||
if (cmd_def.second.status_required <= c->Admin()) {
|
||||
auto display = fmt::format(" {0}", cmd_def.second.help_definition);
|
||||
c->SendLine(display);
|
||||
}
|
||||
}
|
||||
|
||||
c->SendPrompt();
|
||||
return;
|
||||
}
|
||||
|
||||
split.erase(split.begin(), split.begin() + 1);
|
||||
|
||||
auto cmd_def = m_commands.find(command);
|
||||
if (cmd_def != m_commands.end()) {
|
||||
if (c->Admin() >= cmd_def->second.status_required) {
|
||||
cmd_def->second.fn(c, command, split);
|
||||
c->SendPrompt();
|
||||
}
|
||||
else {
|
||||
c->SendLine(fmt::format("Access denied for command: {0}", command));
|
||||
c->SendPrompt();
|
||||
}
|
||||
}
|
||||
else {
|
||||
c->SendLine(fmt::format("Command not found: {0}", command));
|
||||
c->SendPrompt();
|
||||
}
|
||||
}
|
||||
else {
|
||||
c->SendPrompt();
|
||||
}
|
||||
}
|
||||
49
common/net/console_server.h
Normal file
49
common/net/console_server.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "console_server_connection.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
struct ConsoleLoginStatus
|
||||
{
|
||||
int status;
|
||||
int account_id;
|
||||
std::string account_name;
|
||||
};
|
||||
|
||||
class ConsoleServer
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(ConsoleServerConnection*, const std::string&, const std::vector<std::string>&)> ConsoleServerCallback;
|
||||
typedef std::function<struct ConsoleLoginStatus(const std::string&, const std::string&)> ConsoleServerLoginCallback;
|
||||
ConsoleServer(const std::string &addr, int port);
|
||||
~ConsoleServer();
|
||||
|
||||
void RegisterCall(const std::string& command, int status_required, const std::string& help_definition, ConsoleServerCallback fn);
|
||||
void RegisterLogin(ConsoleServerLoginCallback fn);
|
||||
|
||||
private:
|
||||
void ConnectionDisconnected(ConsoleServerConnection *c);
|
||||
void ProcessCommand(ConsoleServerConnection *c, const std::string& cmd);
|
||||
|
||||
std::unique_ptr<TCPServer> m_server;
|
||||
|
||||
std::map<std::string, std::unique_ptr<ConsoleServerConnection>> m_connections;
|
||||
|
||||
struct ConsoleServerCommand
|
||||
{
|
||||
ConsoleServerCallback fn;
|
||||
int status_required;
|
||||
std::string help_definition;
|
||||
};
|
||||
|
||||
std::map<std::string, ConsoleServerCommand> m_commands;
|
||||
ConsoleServerLoginCallback m_login;
|
||||
friend class ConsoleServerConnection;
|
||||
};
|
||||
}
|
||||
}
|
||||
221
common/net/console_server_connection.cpp
Normal file
221
common/net/console_server_connection.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
#include "console_server.h"
|
||||
#include "../common/util/uuid.h"
|
||||
#include "../common/net/packet.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
|
||||
EQ::Net::ConsoleServerConnection::ConsoleServerConnection(ConsoleServer *parent, std::shared_ptr<TCPConnection> connection)
|
||||
{
|
||||
m_parent = parent;
|
||||
m_connection = connection;
|
||||
m_uuid = EQ::Util::UUID::Generate().ToString();
|
||||
m_cursor = 0;
|
||||
memset(m_line, 0, MaxConsoleLineLength);
|
||||
m_accept_messages = false;
|
||||
m_user_id = 0;
|
||||
m_admin = 0;
|
||||
|
||||
m_connection->OnRead(std::bind(&ConsoleServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_connection->OnDisconnect(std::bind(&ConsoleServerConnection::OnDisconnect, this, std::placeholders::_1));
|
||||
m_connection->Start();
|
||||
ClearBuffer();
|
||||
|
||||
auto addr = m_connection->RemoteIP();
|
||||
|
||||
SendLine(fmt::format("Establishing connection from: {0}:{1}", addr, m_connection->RemotePort()));
|
||||
|
||||
if (addr.find("127.0.0.1") != std::string::npos || addr.find("::0") != std::string::npos) {
|
||||
SendLine("Connection established from localhost, assuming admin");
|
||||
m_status = ConsoleStatusLoggedIn;
|
||||
m_admin = 255;
|
||||
SendPrompt();
|
||||
}
|
||||
else {
|
||||
m_status = ConsoleStatusWaitingForLogin;
|
||||
Send("Username: ");
|
||||
}
|
||||
}
|
||||
|
||||
EQ::Net::ConsoleServerConnection::~ConsoleServerConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::SendClear()
|
||||
{
|
||||
EQ::Net::DynamicPacket clear;
|
||||
clear.PutUInt8(0, 0);
|
||||
m_connection->Write((const char*)clear.Data(), clear.Length());
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::Send(const std::string &msg)
|
||||
{
|
||||
m_connection->Write(msg.c_str(), msg.length());
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::SendLine(const std::string &line)
|
||||
{
|
||||
Send(line);
|
||||
SendNewLine();
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::SendNewLine()
|
||||
{
|
||||
EQ::Net::DynamicPacket newline;
|
||||
newline.PutUInt8(0, 10);
|
||||
newline.PutUInt8(1, 13);
|
||||
m_connection->Write((const char*)newline.Data(), newline.Length());
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::QueueMessage(const std::string &msg)
|
||||
{
|
||||
if (!m_accept_messages) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_cursor == 0) {
|
||||
size_t len = m_user.length() + 2;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
Send("\x08");
|
||||
}
|
||||
|
||||
SendLine(msg);
|
||||
SendPrompt();
|
||||
}
|
||||
else {
|
||||
std::string cmd(m_line, m_line + m_cursor);
|
||||
|
||||
size_t len = m_user.length() + 2 + cmd.length();
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
Send("\x08");
|
||||
}
|
||||
|
||||
if (msg.length() < cmd.length()) {
|
||||
Send(msg);
|
||||
size_t blank_spaces = 2 + cmd.length() - msg.length();
|
||||
|
||||
for (size_t i = 0; i < blank_spaces; ++i) {
|
||||
Send(" ");
|
||||
}
|
||||
|
||||
SendNewLine();
|
||||
}
|
||||
else {
|
||||
SendLine(msg);
|
||||
}
|
||||
|
||||
SendPrompt();
|
||||
Send(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz)
|
||||
{
|
||||
for (size_t i = 0; i < sz; ++i) {
|
||||
unsigned char c = data[i];
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
m_cursor = 0;
|
||||
break;
|
||||
case 10: // \n
|
||||
{
|
||||
if (m_cursor > 0) {
|
||||
std::string cmd(m_line, m_line + m_cursor);
|
||||
ProcessCommand(cmd);
|
||||
m_cursor = 0;
|
||||
}
|
||||
else {
|
||||
ProcessCommand("");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 13: // \r
|
||||
break;
|
||||
case 8:
|
||||
if (m_cursor > 0) {
|
||||
m_cursor--;
|
||||
}
|
||||
break;
|
||||
case 255:
|
||||
//255 is always followed by a character
|
||||
i++;
|
||||
if (i < sz) {
|
||||
unsigned char c = data[i];
|
||||
|
||||
if (c == 255) {
|
||||
//Escaped 255
|
||||
if (m_cursor < MaxConsoleLineLength && isprint(c)) {
|
||||
m_line[m_cursor] = c;
|
||||
m_cursor++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == 251 || c == 252 || c == 253 || c == 254) {
|
||||
//Option code is always followed by an extra character
|
||||
|
||||
//We don't really care about negotiation tho.
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
default:
|
||||
if (m_cursor < MaxConsoleLineLength && isprint(c)) {
|
||||
m_line[m_cursor] = c;
|
||||
m_cursor++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::OnDisconnect(TCPConnection *c)
|
||||
{
|
||||
m_parent->ConnectionDisconnected(this);
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::ProcessCommand(const std::string &cmd)
|
||||
{
|
||||
if (m_status == ConsoleStatusWaitingForLogin) {
|
||||
m_user = cmd;
|
||||
m_status = ConsoleStatusWaitingForPassword;
|
||||
Send("Password: ");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_status == ConsoleStatusWaitingForPassword) {
|
||||
auto status = m_parent->m_login(m_user, cmd);
|
||||
if (status.account_id == 0) {
|
||||
m_status = ConsoleStatusFailedLogin;
|
||||
SendLine("Access denied");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.status < ConsoleLoginStatus) {
|
||||
m_status = ConsoleStatusFailedLogin;
|
||||
SendLine("Access denied");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
m_user = status.account_name;
|
||||
m_user_id = status.account_id;
|
||||
m_admin = status.status;
|
||||
m_status = ConsoleStatusLoggedIn;
|
||||
SendPrompt();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_status == ConsoleStatusLoggedIn) {
|
||||
m_parent->ProcessCommand(this, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ConsoleServerConnection::SendPrompt()
|
||||
{
|
||||
Send(fmt::format("{0}> ", m_user));
|
||||
}
|
||||
63
common/net/console_server_connection.h
Normal file
63
common/net/console_server_connection.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "tcp_server.h"
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
enum ConsoleConnectionStatus
|
||||
{
|
||||
ConsoleStatusWaitingForLogin,
|
||||
ConsoleStatusWaitingForPassword,
|
||||
ConsoleStatusLoggedIn,
|
||||
ConsoleStatusFailedLogin
|
||||
};
|
||||
|
||||
const int MaxConsoleLineLength = 512;
|
||||
const int ConsoleLoginStatus = 50;
|
||||
class ConsoleServer;
|
||||
class ConsoleServerConnection
|
||||
{
|
||||
public:
|
||||
ConsoleServerConnection(ConsoleServer *parent, std::shared_ptr<TCPConnection> connection);
|
||||
~ConsoleServerConnection();
|
||||
|
||||
std::string GetUUID() const { return m_uuid; }
|
||||
void ClearBuffer() { m_cursor = 0; }
|
||||
void Close() { m_connection->Disconnect(); }
|
||||
|
||||
void SendClear();
|
||||
void Send(const std::string &msg);
|
||||
void SendLine(const std::string &line);
|
||||
void SendNewLine();
|
||||
void SendPrompt();
|
||||
ConsoleConnectionStatus Status() const { return m_status; }
|
||||
std::string UserName() const { return m_user; }
|
||||
int UserId() const { return m_user_id; }
|
||||
int Admin() const { return m_admin; }
|
||||
|
||||
bool AcceptMessages() const { return m_accept_messages; }
|
||||
void SetAcceptMessages(bool v) { m_accept_messages = v; }
|
||||
void QueueMessage(const std::string &msg);
|
||||
private:
|
||||
void OnRead(TCPConnection* c, const unsigned char* data, size_t sz);
|
||||
void OnDisconnect(TCPConnection* c);
|
||||
void ProcessCommand(const std::string &cmd);
|
||||
|
||||
ConsoleServer *m_parent;
|
||||
std::shared_ptr<TCPConnection> m_connection;
|
||||
std::string m_uuid;
|
||||
ConsoleConnectionStatus m_status;
|
||||
std::string m_user;
|
||||
int m_user_id;
|
||||
int m_admin;
|
||||
bool m_accept_messages;
|
||||
|
||||
size_t m_cursor;
|
||||
char m_line[MaxConsoleLineLength];
|
||||
};
|
||||
}
|
||||
}
|
||||
96
common/net/crc32.cpp
Normal file
96
common/net/crc32.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "crc32.h"
|
||||
#include <memory.h>
|
||||
|
||||
unsigned int CRC32EncodeTable[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 EQ::Crc32(const void * data, int size)
|
||||
{
|
||||
int crc = 0xffffffff;
|
||||
auto buffer = (const uint8_t *)data;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ *&buffer[i]) & 0x000000FFL];
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
int EQ::Crc32(const void * data, int size, int key)
|
||||
{
|
||||
int crc = 0xffffffff;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ ((key >> (i * 8)) & 0xff)) & 0x000000FFL];
|
||||
}
|
||||
|
||||
auto buffer = (const uint8_t *)data;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ *&buffer[i]) & 0x000000FFL];
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
9
common/net/crc32.h
Normal file
9
common/net/crc32.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
int Crc32(const void *data, int size);
|
||||
int Crc32(const void *data, int size, int key);
|
||||
}
|
||||
1399
common/net/daybreak_connection.cpp
Normal file
1399
common/net/daybreak_connection.cpp
Normal file
File diff suppressed because it is too large
Load Diff
285
common/net/daybreak_connection.h
Normal file
285
common/net/daybreak_connection.h
Normal file
@ -0,0 +1,285 @@
|
||||
#pragma once
|
||||
|
||||
#include "../random.h"
|
||||
#include "packet.h"
|
||||
#include "daybreak_structs.h"
|
||||
#include <uv.h>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <list>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
enum DaybreakProtocolOpcode
|
||||
{
|
||||
OP_Padding = 0x00,
|
||||
OP_SessionRequest = 0x01,
|
||||
OP_SessionResponse = 0x02,
|
||||
OP_Combined = 0x03,
|
||||
OP_SessionDisconnect = 0x05,
|
||||
OP_KeepAlive = 0x06,
|
||||
OP_SessionStatRequest = 0x07,
|
||||
OP_SessionStatResponse = 0x08,
|
||||
OP_Packet = 0x09,
|
||||
OP_Packet2 = 0x0a,
|
||||
OP_Packet3 = 0x0b,
|
||||
OP_Packet4 = 0x0c,
|
||||
OP_Fragment = 0x0d,
|
||||
OP_Fragment2 = 0x0e,
|
||||
OP_Fragment3 = 0x0f,
|
||||
OP_Fragment4 = 0x10,
|
||||
OP_OutOfOrderAck = 0x11,
|
||||
OP_OutOfOrderAck2 = 0x12,
|
||||
OP_OutOfOrderAck3 = 0x13,
|
||||
OP_OutOfOrderAck4 = 0x14,
|
||||
OP_Ack = 0x15,
|
||||
OP_Ack2 = 0x16,
|
||||
OP_Ack3 = 0x17,
|
||||
OP_Ack4 = 0x18,
|
||||
OP_AppCombined = 0x19,
|
||||
OP_OutboundPing = 0x1c,
|
||||
OP_OutOfSession = 0x1d
|
||||
};
|
||||
|
||||
enum DbProtocolStatus
|
||||
{
|
||||
StatusConnecting,
|
||||
StatusConnected,
|
||||
StatusDisconnecting,
|
||||
StatusDisconnected
|
||||
};
|
||||
|
||||
enum DaybreakEncodeType
|
||||
{
|
||||
EncodeNone = 0,
|
||||
EncodeCompression = 1,
|
||||
EncodeXOR = 4,
|
||||
};
|
||||
|
||||
enum SequenceOrder
|
||||
{
|
||||
SequenceCurrent,
|
||||
SequenceFuture,
|
||||
SequencePast
|
||||
};
|
||||
|
||||
typedef std::chrono::high_resolution_clock::time_point Timestamp;
|
||||
typedef std::chrono::high_resolution_clock Clock;
|
||||
|
||||
struct DaybreakConnectionStats
|
||||
{
|
||||
DaybreakConnectionStats() {
|
||||
recv_bytes = 0;
|
||||
sent_bytes = 0;
|
||||
recv_packets = 0;
|
||||
sent_packets = 0;
|
||||
min_ping = 0xFFFFFFFFFFFFFFFFUL;
|
||||
max_ping = 0;
|
||||
created = Clock::now();
|
||||
}
|
||||
|
||||
uint64_t recv_bytes;
|
||||
uint64_t sent_bytes;
|
||||
uint64_t recv_packets;
|
||||
uint64_t sent_packets;
|
||||
uint64_t min_ping;
|
||||
uint64_t max_ping;
|
||||
uint64_t last_ping;
|
||||
Timestamp created;
|
||||
};
|
||||
|
||||
class DaybreakConnectionManager;
|
||||
class DaybreakConnection;
|
||||
class DaybreakConnection
|
||||
{
|
||||
public:
|
||||
DaybreakConnection(DaybreakConnectionManager *owner, const DaybreakConnect &connect, const std::string &endpoint, int port);
|
||||
DaybreakConnection(DaybreakConnectionManager *owner, const std::string &endpoint, int port);
|
||||
~DaybreakConnection();
|
||||
|
||||
const std::string& RemoteEndpoint() const { return m_endpoint; }
|
||||
int RemotePort() const { return m_port; }
|
||||
|
||||
void Close();
|
||||
void QueuePacket(Packet &p);
|
||||
void QueuePacket(Packet &p, int stream);
|
||||
void QueuePacket(Packet &p, int stream, bool reliable);
|
||||
const DaybreakConnectionStats& GetStats() const { return m_stats; }
|
||||
void ResetStats();
|
||||
size_t GetRollingPing() const { return m_rolling_ping; }
|
||||
DbProtocolStatus GetStatus() { return m_status; }
|
||||
private:
|
||||
DaybreakConnectionManager *m_owner;
|
||||
std::string m_endpoint;
|
||||
int m_port;
|
||||
uint32_t m_connect_code;
|
||||
uint32_t m_encode_key;
|
||||
uint32_t m_max_packet_size;
|
||||
uint32_t m_crc_bytes;
|
||||
DaybreakEncodeType m_encode_passes[2];
|
||||
|
||||
Timestamp m_last_send;
|
||||
Timestamp m_last_recv;
|
||||
DbProtocolStatus m_status;
|
||||
Timestamp m_hold_time;
|
||||
std::list<DynamicPacket> m_buffered_packets;
|
||||
size_t m_buffered_packets_length;
|
||||
std::unique_ptr<char[]> m_combined;
|
||||
DaybreakConnectionStats m_stats;
|
||||
Timestamp m_last_session_stats;
|
||||
size_t m_resend_delay;
|
||||
size_t m_rolling_ping;
|
||||
Timestamp m_close_time;
|
||||
|
||||
struct DaybreakSentPacket
|
||||
{
|
||||
DynamicPacket packet;
|
||||
Timestamp last_sent;
|
||||
Timestamp first_sent;
|
||||
size_t times_resent;
|
||||
};
|
||||
|
||||
struct DaybreakStream
|
||||
{
|
||||
DaybreakStream() {
|
||||
sequence_in = 0;
|
||||
sequence_out = 0;
|
||||
fragment_current_bytes = 0;
|
||||
fragment_total_bytes = 0;
|
||||
}
|
||||
|
||||
uint16_t sequence_in;
|
||||
uint16_t sequence_out;
|
||||
std::map<uint16_t, Packet*> packet_queue;
|
||||
|
||||
DynamicPacket fragment_packet;
|
||||
uint32_t fragment_current_bytes;
|
||||
uint32_t fragment_total_bytes;
|
||||
|
||||
std::map<uint16_t, DaybreakSentPacket> sent_packets;
|
||||
};
|
||||
|
||||
DaybreakStream m_streams[4];
|
||||
std::weak_ptr<DaybreakConnection> m_self;
|
||||
|
||||
void Process();
|
||||
void ProcessPacket(Packet &p);
|
||||
void ProcessQueue();
|
||||
void RemoveFromQueue(int stream, uint16_t seq);
|
||||
void AddToQueue(int stream, uint16_t seq, const Packet &p);
|
||||
void ProcessDecodedPacket(const Packet &p);
|
||||
void ChangeStatus(DbProtocolStatus new_status);
|
||||
bool ValidateCRC(Packet &p);
|
||||
void AppendCRC(Packet &p);
|
||||
bool PacketCanBeEncoded(Packet &p) const;
|
||||
void Decode(Packet &p, size_t offset, size_t length);
|
||||
void Encode(Packet &p, size_t offset, size_t length);
|
||||
void Decompress(Packet &p, size_t offset, size_t length);
|
||||
void Compress(Packet &p, size_t offset, size_t length);
|
||||
void ProcessResend();
|
||||
void ProcessResend(int stream);
|
||||
void Ack(int stream, uint16_t seq);
|
||||
void OutOfOrderAck(int stream, uint16_t seq);
|
||||
|
||||
void SendConnect();
|
||||
void SendKeepAlive();
|
||||
void SendAck(int stream, uint16_t seq);
|
||||
void SendOutOfOrderAck(int stream, uint16_t seq);
|
||||
void SendDisconnect();
|
||||
void InternalBufferedSend(Packet &p);
|
||||
void InternalSend(Packet &p);
|
||||
void InternalQueuePacket(Packet &p, int stream_id, bool reliable);
|
||||
void FlushBuffer();
|
||||
SequenceOrder CompareSequence(uint16_t expected, uint16_t actual) const;
|
||||
|
||||
friend class DaybreakConnectionManager;
|
||||
};
|
||||
|
||||
struct DaybreakConnectionManagerOptions
|
||||
{
|
||||
DaybreakConnectionManagerOptions() {
|
||||
max_connection_count = 0;
|
||||
keepalive_delay_ms = 9000;
|
||||
resend_delay_ms = 150;
|
||||
resend_delay_factor = 1.5;
|
||||
resend_delay_min = 150;
|
||||
resend_delay_max = 1000;
|
||||
connect_delay_ms = 500;
|
||||
stale_connection_ms = 90000;
|
||||
connect_stale_ms = 5000;
|
||||
crc_length = 2;
|
||||
max_packet_size = 512;
|
||||
encode_passes[0] = DaybreakEncodeType::EncodeNone;
|
||||
encode_passes[1] = DaybreakEncodeType::EncodeNone;
|
||||
port = 0;
|
||||
hold_size = 448;
|
||||
hold_length_ms = 10;
|
||||
simulated_in_packet_loss = 0;
|
||||
simulated_out_packet_loss = 0;
|
||||
tic_rate_hertz = 60.0;
|
||||
resend_timeout = 90000;
|
||||
connection_close_time = 2000;
|
||||
}
|
||||
|
||||
size_t max_packet_size;
|
||||
size_t max_connection_count;
|
||||
size_t keepalive_delay_ms;
|
||||
double resend_delay_factor;
|
||||
size_t resend_delay_ms;
|
||||
size_t resend_delay_min;
|
||||
size_t resend_delay_max;
|
||||
size_t connect_delay_ms;
|
||||
size_t connect_stale_ms;
|
||||
size_t stale_connection_ms;
|
||||
size_t crc_length;
|
||||
size_t hold_size;
|
||||
size_t hold_length_ms;
|
||||
size_t simulated_in_packet_loss;
|
||||
size_t simulated_out_packet_loss;
|
||||
double tic_rate_hertz;
|
||||
size_t resend_timeout;
|
||||
size_t connection_close_time;
|
||||
DaybreakEncodeType encode_passes[2];
|
||||
int port;
|
||||
};
|
||||
|
||||
class DaybreakConnectionManager
|
||||
{
|
||||
public:
|
||||
DaybreakConnectionManager();
|
||||
DaybreakConnectionManager(const DaybreakConnectionManagerOptions &opts);
|
||||
~DaybreakConnectionManager();
|
||||
|
||||
void Connect(const std::string &addr, int port);
|
||||
void Process();
|
||||
void ProcessResend();
|
||||
void OnNewConnection(std::function<void(std::shared_ptr<DaybreakConnection>)> func) { m_on_new_connection = func; }
|
||||
void OnConnectionStateChange(std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
|
||||
void OnPacketRecv(std::function<void(std::shared_ptr<DaybreakConnection>, const Packet &)> func) { m_on_packet_recv = func; }
|
||||
private:
|
||||
void Attach(uv_loop_t *loop);
|
||||
void Detach();
|
||||
|
||||
EQEmu::Random m_rand;
|
||||
uv_timer_t m_timer;
|
||||
uv_udp_t m_socket;
|
||||
uv_loop_t *m_attached;
|
||||
DaybreakConnectionManagerOptions m_options;
|
||||
std::function<void(std::shared_ptr<DaybreakConnection>)> m_on_new_connection;
|
||||
std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
|
||||
std::function<void(std::shared_ptr<DaybreakConnection>, const Packet&)> m_on_packet_recv;
|
||||
std::map<std::pair<std::string, int>, std::shared_ptr<DaybreakConnection>> m_connections;
|
||||
|
||||
void ProcessPacket(const std::string &endpoint, int port, const char *data, size_t size);
|
||||
std::shared_ptr<DaybreakConnection> FindConnectionByEndpoint(std::string addr, int port);
|
||||
void SendDisconnect(const std::string &addr, int port);
|
||||
|
||||
friend class DaybreakConnection;
|
||||
};
|
||||
}
|
||||
}
|
||||
173
common/net/daybreak_structs.h
Normal file
173
common/net/daybreak_structs.h
Normal file
@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cstdint>
|
||||
#include "endian.h"
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
struct DaybreakHeader
|
||||
{
|
||||
static size_t size() { return 2; }
|
||||
uint8_t zero;
|
||||
uint8_t opcode;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(zero,
|
||||
opcode);
|
||||
}
|
||||
};
|
||||
|
||||
struct DaybreakConnect
|
||||
{
|
||||
static size_t size() { return 14; }
|
||||
uint8_t zero;
|
||||
uint8_t opcode;
|
||||
uint32_t protocol_version;
|
||||
uint32_t connect_code;
|
||||
uint32_t max_packet_size;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(zero,
|
||||
opcode,
|
||||
protocol_version,
|
||||
connect_code,
|
||||
max_packet_size);
|
||||
}
|
||||
};
|
||||
|
||||
struct DaybreakConnectReply
|
||||
{
|
||||
static size_t size() { return 17; }
|
||||
uint8_t zero;
|
||||
uint8_t opcode;
|
||||
uint32_t connect_code;
|
||||
uint32_t encode_key;
|
||||
uint8_t crc_bytes;
|
||||
uint8_t encode_pass1;
|
||||
uint8_t encode_pass2;
|
||||
uint32_t max_packet_size;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(zero,
|
||||
opcode,
|
||||
connect_code,
|
||||
encode_key,
|
||||
crc_bytes,
|
||||
encode_pass1,
|
||||
encode_pass2,
|
||||
max_packet_size);
|
||||
}
|
||||
};
|
||||
|
||||
struct DaybreakDisconnect
|
||||
{
|
||||
static size_t size() { return 8; }
|
||||
uint8_t zero;
|
||||
uint8_t opcode;
|
||||
uint32_t connect_code;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(zero,
|
||||
opcode,
|
||||
connect_code);
|
||||
}
|
||||
};
|
||||
|
||||
struct DaybreakReliableHeader
|
||||
{
|
||||
static size_t size() { return 4; }
|
||||
uint8_t zero;
|
||||
uint8_t opcode;
|
||||
uint16_t sequence;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(zero,
|
||||
opcode,
|
||||
sequence);
|
||||
}
|
||||
};
|
||||
|
||||
struct DaybreakReliableFragmentHeader
|
||||
{
|
||||
static size_t size() { return 4 + DaybreakReliableHeader::size(); }
|
||||
DaybreakReliableHeader reliable;
|
||||
uint32_t total_size;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(reliable,
|
||||
total_size);
|
||||
}
|
||||
};
|
||||
|
||||
struct DaybreakSessionStatRequest
|
||||
{
|
||||
static size_t size() { return 40; }
|
||||
uint8_t zero;
|
||||
uint8_t opcode;
|
||||
uint16_t timestamp;
|
||||
uint32_t stat_ping;
|
||||
uint32_t avg_ping;
|
||||
uint32_t min_ping;
|
||||
uint32_t max_ping;
|
||||
uint32_t last_ping;
|
||||
uint64_t packets_sent;
|
||||
uint64_t packets_recv;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(zero,
|
||||
opcode,
|
||||
timestamp,
|
||||
stat_ping,
|
||||
avg_ping,
|
||||
min_ping,
|
||||
max_ping,
|
||||
last_ping,
|
||||
packets_sent,
|
||||
packets_recv);
|
||||
}
|
||||
};
|
||||
|
||||
struct DaybreakSessionStatResponse
|
||||
{
|
||||
static size_t size() { return 40; }
|
||||
uint8_t zero;
|
||||
uint8_t opcode;
|
||||
uint16_t timestamp;
|
||||
uint32_t our_timestamp;
|
||||
uint64_t client_sent;
|
||||
uint64_t client_recv;
|
||||
uint64_t server_sent;
|
||||
uint64_t server_recv;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(zero,
|
||||
opcode,
|
||||
timestamp,
|
||||
our_timestamp,
|
||||
client_sent,
|
||||
client_recv,
|
||||
server_sent,
|
||||
server_recv);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
61
common/net/dns.h
Normal file
61
common/net/dns.h
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "../event/event_loop.h"
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
static void DNSLookup(const std::string &addr, int port, bool ipv6, std::function<void(const std::string&)> cb) {
|
||||
struct DNSBaton
|
||||
{
|
||||
std::function<void(const std::string&)> cb;
|
||||
bool ipv6;
|
||||
};
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(addrinfo));
|
||||
hints.ai_family = PF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
auto loop = EQ::EventLoop::Get().Handle();
|
||||
uv_getaddrinfo_t *resolver = new uv_getaddrinfo_t();
|
||||
memset(resolver, 0, sizeof(uv_getaddrinfo_t));
|
||||
auto port_str = std::to_string(port);
|
||||
DNSBaton *baton = new DNSBaton();
|
||||
baton->cb = cb;
|
||||
baton->ipv6 = ipv6;
|
||||
resolver->data = baton;
|
||||
|
||||
uv_getaddrinfo(loop, resolver, [](uv_getaddrinfo_t* req, int status, addrinfo* res) {
|
||||
DNSBaton *baton = (DNSBaton*)req->data;
|
||||
if (status < 0) {
|
||||
auto cb = baton->cb;
|
||||
delete baton;
|
||||
delete req;
|
||||
cb("");
|
||||
return;
|
||||
}
|
||||
|
||||
char addr[40] = { 0 };
|
||||
|
||||
if (baton->ipv6) {
|
||||
uv_ip6_name((struct sockaddr_in6*)res->ai_addr, addr, 40);
|
||||
}
|
||||
else {
|
||||
uv_ip4_name((struct sockaddr_in*)res->ai_addr, addr, 40);
|
||||
}
|
||||
|
||||
auto cb = baton->cb;
|
||||
delete baton;
|
||||
delete req;
|
||||
uv_freeaddrinfo(res);
|
||||
|
||||
cb(addr);
|
||||
}, addr.c_str(), port_str.c_str(), &hints);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
common/net/endian.h
Normal file
51
common/net/endian.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
inline bool IsLittleEndian() {
|
||||
const int32_t v = 1;
|
||||
return 1 == *(int8_t*)&v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ByteSwap(T in) {
|
||||
static_assert(std::is_integral<T>::value, "Byte swap only works on integer types.");
|
||||
T ret;
|
||||
|
||||
char *first = (char*)∈
|
||||
char *last = (char*)&in + sizeof(in);
|
||||
char *d_first = (char*)&ret;
|
||||
while (first != last) {
|
||||
*(d_first++) = *(--last);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T HostToNetwork(T in) {
|
||||
if (IsLittleEndian()) {
|
||||
return ByteSwap(in);
|
||||
}
|
||||
else {
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T NetworkToHost(T in) {
|
||||
if (IsLittleEndian()) {
|
||||
return ByteSwap(in);
|
||||
}
|
||||
else {
|
||||
return in;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
211
common/net/eqstream.cpp
Normal file
211
common/net/eqstream.cpp
Normal file
@ -0,0 +1,211 @@
|
||||
#include "eqstream.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
|
||||
EQ::Net::EQStreamManager::EQStreamManager(EQStreamManagerOptions &options) : m_daybreak(options.daybreak_options)
|
||||
{
|
||||
m_options = options;
|
||||
|
||||
m_daybreak.OnNewConnection(std::bind(&EQStreamManager::DaybreakNewConnection, this, std::placeholders::_1));
|
||||
m_daybreak.OnConnectionStateChange(std::bind(&EQStreamManager::DaybreakConnectionStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_daybreak.OnPacketRecv(std::bind(&EQStreamManager::DaybreakPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
EQ::Net::EQStreamManager::~EQStreamManager()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::EQStreamManager::DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection)
|
||||
{
|
||||
std::shared_ptr<EQStream> stream(new EQStream(this, connection));
|
||||
m_streams.insert(std::make_pair(connection, stream));
|
||||
if (m_on_new_connection) {
|
||||
m_on_new_connection(stream);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::EQStreamManager::DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to)
|
||||
{
|
||||
auto iter = m_streams.find(connection);
|
||||
if (iter != m_streams.end()) {
|
||||
if (m_on_connection_state_change) {
|
||||
m_on_connection_state_change(iter->second, from, to);
|
||||
}
|
||||
|
||||
if (to == EQ::Net::StatusDisconnected) {
|
||||
m_streams.erase(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::EQStreamManager::DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p)
|
||||
{
|
||||
auto iter = m_streams.find(connection);
|
||||
if (iter != m_streams.end()) {
|
||||
auto &stream = iter->second;
|
||||
std::unique_ptr<EQ::Net::Packet> t(new EQ::Net::DynamicPacket());
|
||||
t->PutPacket(0, p);
|
||||
stream->m_packet_queue.push_back(std::move(t));
|
||||
}
|
||||
}
|
||||
|
||||
EQ::Net::EQStream::EQStream(EQStreamManager *owner, std::shared_ptr<DaybreakConnection> connection)
|
||||
{
|
||||
m_owner = owner;
|
||||
m_connection = connection;
|
||||
m_opcode_manager = nullptr;
|
||||
}
|
||||
|
||||
EQ::Net::EQStream::~EQStream()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req) {
|
||||
if (m_opcode_manager && *m_opcode_manager) {
|
||||
uint16 opcode = 0;
|
||||
if (p->GetOpcodeBypass() != 0) {
|
||||
opcode = p->GetOpcodeBypass();
|
||||
}
|
||||
else {
|
||||
opcode = (*m_opcode_manager)->EmuToEQ(p->GetOpcode());
|
||||
}
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
switch (m_owner->m_options.opcode_size) {
|
||||
case 1:
|
||||
out.PutUInt8(0, opcode);
|
||||
out.PutData(1, p->pBuffer, p->size);
|
||||
break;
|
||||
case 2:
|
||||
out.PutUInt16(0, opcode);
|
||||
out.PutData(2, p->pBuffer, p->size);
|
||||
break;
|
||||
}
|
||||
|
||||
m_connection->QueuePacket(out);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) {
|
||||
QueuePacket(*p, ack_req);
|
||||
delete *p;
|
||||
*p = nullptr;
|
||||
}
|
||||
|
||||
EQApplicationPacket *EQ::Net::EQStream::PopPacket() {
|
||||
if (m_packet_queue.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_opcode_manager != nullptr && *m_opcode_manager != nullptr) {
|
||||
auto &p = m_packet_queue.front();
|
||||
|
||||
uint16 opcode = 0;
|
||||
switch (m_owner->m_options.opcode_size) {
|
||||
case 1:
|
||||
opcode = p->GetUInt8(0);
|
||||
break;
|
||||
case 2:
|
||||
opcode = p->GetUInt16(0);
|
||||
break;
|
||||
}
|
||||
|
||||
EmuOpcode emu_op = (*m_opcode_manager)->EQToEmu(opcode);
|
||||
EQApplicationPacket *ret = new EQApplicationPacket(emu_op, (unsigned char*)p->Data() + m_owner->m_options.opcode_size, p->Length() - m_owner->m_options.opcode_size);
|
||||
ret->SetProtocolOpcode(opcode);
|
||||
m_packet_queue.pop_front();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EQ::Net::EQStream::Close() {
|
||||
m_connection->Close();
|
||||
}
|
||||
|
||||
std::string EQ::Net::EQStream::GetRemoteAddr() const
|
||||
{
|
||||
return RemoteEndpoint();
|
||||
}
|
||||
|
||||
uint32 EQ::Net::EQStream::GetRemoteIP() const {
|
||||
return inet_addr(RemoteEndpoint().c_str());
|
||||
}
|
||||
|
||||
bool EQ::Net::EQStream::CheckState(EQStreamState state) {
|
||||
return GetState() == state;
|
||||
}
|
||||
|
||||
EQStreamInterface::MatchState EQ::Net::EQStream::CheckSignature(const Signature *sig) {
|
||||
if (!m_packet_queue.empty()) {
|
||||
auto p = m_packet_queue.front().get();
|
||||
uint16 opcode = 0;
|
||||
size_t length = p->Length() - m_owner->m_options.opcode_size;
|
||||
switch (m_owner->m_options.opcode_size) {
|
||||
case 1:
|
||||
opcode = p->GetUInt8(0);
|
||||
break;
|
||||
case 2:
|
||||
opcode = p->GetUInt16(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sig->ignore_eq_opcode != 0 && opcode == sig->ignore_eq_opcode) {
|
||||
if (m_packet_queue.size() > 1) {
|
||||
p = m_packet_queue[1].get();
|
||||
opcode = 0;
|
||||
length = p->Length() - m_owner->m_options.opcode_size;
|
||||
switch (m_owner->m_options.opcode_size) {
|
||||
case 1:
|
||||
opcode = p->GetUInt8(0);
|
||||
break;
|
||||
case 2:
|
||||
opcode = p->GetUInt16(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return MatchNotReady;
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == sig->first_eq_opcode) {
|
||||
if (length == sig->first_length) {
|
||||
LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} and length matched {3}",
|
||||
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode, length);
|
||||
return MatchSuccessful;
|
||||
}
|
||||
else if (length == 0) {
|
||||
LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} and length is ignored.",
|
||||
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode);
|
||||
return MatchSuccessful;
|
||||
}
|
||||
else {
|
||||
LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} but length {3} did not match expected {4}",
|
||||
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode, length, sig->first_length);
|
||||
return MatchFailed;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode {1:#x} did not match expected {2:#x}",
|
||||
RemoteEndpoint(), m_connection->RemotePort(), opcode, sig->first_eq_opcode);
|
||||
return MatchFailed;
|
||||
}
|
||||
}
|
||||
|
||||
return MatchNotReady;
|
||||
}
|
||||
|
||||
EQStreamState EQ::Net::EQStream::GetState() {
|
||||
auto status = m_connection->GetStatus();
|
||||
switch (status) {
|
||||
case StatusConnecting:
|
||||
return UNESTABLISHED;
|
||||
case StatusConnected:
|
||||
return ESTABLISHED;
|
||||
case StatusDisconnecting:
|
||||
return DISCONNECTING;
|
||||
default:
|
||||
return CLOSED;
|
||||
}
|
||||
}
|
||||
99
common/net/eqstream.h
Normal file
99
common/net/eqstream.h
Normal file
@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include "../eq_packet.h"
|
||||
#include "../eq_stream_intf.h"
|
||||
#include "../opcodemgr.h"
|
||||
#include "daybreak_connection.h"
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
struct EQStreamManagerOptions
|
||||
{
|
||||
EQStreamManagerOptions() {
|
||||
opcode_size = 2;
|
||||
}
|
||||
|
||||
EQStreamManagerOptions(int port, bool encoded, bool compressed) {
|
||||
opcode_size = 2;
|
||||
|
||||
//World seems to support both compression and xor zone supports one or the others.
|
||||
//Enforce one or the other in the convienence construct
|
||||
//Login I had trouble getting to recognize compression at all
|
||||
//but that might be because it was still a bit buggy when i was testing that.
|
||||
if (compressed) {
|
||||
daybreak_options.encode_passes[0] = EncodeCompression;
|
||||
}
|
||||
else if (encoded) {
|
||||
daybreak_options.encode_passes[0] = EncodeXOR;
|
||||
}
|
||||
|
||||
daybreak_options.port = port;
|
||||
}
|
||||
|
||||
int opcode_size;
|
||||
DaybreakConnectionManagerOptions daybreak_options;
|
||||
};
|
||||
|
||||
class EQStream;
|
||||
class EQStreamManager
|
||||
{
|
||||
public:
|
||||
EQStreamManager(EQStreamManagerOptions &options);
|
||||
~EQStreamManager();
|
||||
|
||||
void OnNewConnection(std::function<void(std::shared_ptr<EQStream>)> func) { m_on_new_connection = func; }
|
||||
void OnConnectionStateChange(std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
|
||||
private:
|
||||
EQStreamManagerOptions m_options;
|
||||
DaybreakConnectionManager m_daybreak;
|
||||
std::function<void(std::shared_ptr<EQStream>)> m_on_new_connection;
|
||||
std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
|
||||
std::map<std::shared_ptr<DaybreakConnection>, std::shared_ptr<EQStream>> m_streams;
|
||||
|
||||
void DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection);
|
||||
void DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to);
|
||||
void DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p);
|
||||
friend class EQStream;
|
||||
};
|
||||
|
||||
class EQStream : public EQStreamInterface
|
||||
{
|
||||
public:
|
||||
EQStream(EQStreamManager *parent, std::shared_ptr<DaybreakConnection> connection);
|
||||
~EQStream();
|
||||
|
||||
virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req = true);
|
||||
virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req = true);
|
||||
virtual EQApplicationPacket *PopPacket();
|
||||
virtual void Close();
|
||||
virtual void ReleaseFromUse() { };
|
||||
virtual void RemoveData() { };
|
||||
virtual std::string GetRemoteAddr() const;
|
||||
virtual uint32 GetRemoteIP() const;
|
||||
virtual uint16 GetRemotePort() const { return m_connection->RemotePort(); }
|
||||
virtual bool CheckState(EQStreamState state);
|
||||
virtual std::string Describe() const { return "Direct EQStream"; }
|
||||
virtual void SetActive(bool val) { }
|
||||
virtual MatchState CheckSignature(const Signature *sig);
|
||||
virtual EQStreamState GetState();
|
||||
virtual void SetOpcodeManager(OpcodeManager **opm) {
|
||||
m_opcode_manager = opm;
|
||||
}
|
||||
|
||||
const std::string& RemoteEndpoint() const { return m_connection->RemoteEndpoint(); }
|
||||
const DaybreakConnectionStats& GetStats() const { return m_connection->GetStats(); }
|
||||
void ResetStats() { m_connection->ResetStats(); }
|
||||
size_t GetRollingPing() const { return m_connection->GetRollingPing(); }
|
||||
private:
|
||||
EQStreamManager *m_owner;
|
||||
std::shared_ptr<DaybreakConnection> m_connection;
|
||||
OpcodeManager **m_opcode_manager;
|
||||
std::deque<std::unique_ptr<EQ::Net::Packet>> m_packet_queue;
|
||||
friend class EQStreamManager;
|
||||
};
|
||||
}
|
||||
}
|
||||
345
common/net/packet.cpp
Normal file
345
common/net/packet.cpp
Normal file
@ -0,0 +1,345 @@
|
||||
#include "packet.h"
|
||||
#include "endian.h"
|
||||
#include <fmt/format.h>
|
||||
#include <cctype>
|
||||
|
||||
void EQ::Net::Packet::PutInt8(size_t offset, int8_t value)
|
||||
{
|
||||
if (Length() < offset + 1) {
|
||||
if (!Resize(offset + 1)) {
|
||||
throw std::out_of_range("Packet::PutInt8(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(int8_t*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutInt16(size_t offset, int16_t value)
|
||||
{
|
||||
if (Length() < offset + 2) {
|
||||
if (!Resize(offset + 2)) {
|
||||
throw std::out_of_range("Packet::PutInt16(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(int16_t*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutInt32(size_t offset, int32_t value)
|
||||
{
|
||||
if (Length() < offset + 4) {
|
||||
if (!Resize(offset + 4)) {
|
||||
throw std::out_of_range("Packet::PutInt32(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(int32_t*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutInt64(size_t offset, int64_t value)
|
||||
{
|
||||
if (Length() < offset + 8) {
|
||||
if (!Resize(offset + 8)) {
|
||||
throw std::out_of_range("Packet::PutInt64(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(int64_t*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutUInt8(size_t offset, uint8_t value)
|
||||
{
|
||||
if (Length() < offset + 1) {
|
||||
if (!Resize(offset + 1)) {
|
||||
throw std::out_of_range("Packet::PutUInt8(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(uint8_t*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutUInt16(size_t offset, uint16_t value)
|
||||
{
|
||||
if (Length() < offset + 2) {
|
||||
if (!Resize(offset + 2)) {
|
||||
throw std::out_of_range("Packet::PutUInt16(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(uint16_t*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutUInt32(size_t offset, uint32_t value)
|
||||
{
|
||||
if (Length() < offset + 4) {
|
||||
if (!Resize(offset + 4)) {
|
||||
throw std::out_of_range("Packet::PutUInt32(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(uint32_t*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutUInt64(size_t offset, uint64_t value)
|
||||
{
|
||||
if (Length() < offset + 8) {
|
||||
if (!Resize(offset + 8)) {
|
||||
throw std::out_of_range("Packet::PutUInt64(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(uint64_t*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutFloat(size_t offset, float value)
|
||||
{
|
||||
if (Length() < offset + 4) {
|
||||
if (!Resize(offset + 4)) {
|
||||
throw std::out_of_range("Packet::PutFloat(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(float*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutDouble(size_t offset, double value)
|
||||
{
|
||||
if (Length() < offset + 8) {
|
||||
if (!Resize(offset + 8)) {
|
||||
throw std::out_of_range("Packet::PutDouble(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
*(double*)((char*)Data() + offset) = value;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutString(size_t offset, const std::string &str)
|
||||
{
|
||||
if (Length() < offset + str.length()) {
|
||||
if (!Resize(offset + str.length())) {
|
||||
throw std::out_of_range("Packet::PutString(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(((char*)Data() + offset), str.c_str(), str.length());
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutCString(size_t offset, const char *str)
|
||||
{
|
||||
size_t sz = strlen(str);
|
||||
if (Length() < offset + sz + 1) {
|
||||
if (!Resize(offset + sz + 1)) {
|
||||
throw std::out_of_range("Packet::PutCString(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(((char*)Data() + offset), str, sz);
|
||||
*((char*)Data() + offset + sz) = 0;
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutPacket(size_t offset, const Packet &p)
|
||||
{
|
||||
if (Length() < offset + p.Length()) {
|
||||
if (!Resize(offset + p.Length())) {
|
||||
throw std::out_of_range("Packet::PutPacket(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(((char*)Data() + offset), p.Data(), p.Length());
|
||||
}
|
||||
|
||||
void EQ::Net::Packet::PutData(size_t offset, void *data, size_t length)
|
||||
{
|
||||
if (Length() < offset + length) {
|
||||
if (!Resize(offset + length)) {
|
||||
throw std::out_of_range("Packet::PutData(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(((char*)Data() + offset), data, length);
|
||||
}
|
||||
|
||||
int8_t EQ::Net::Packet::GetInt8(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 1) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(int8_t*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
int16_t EQ::Net::Packet::GetInt16(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 2) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(int16_t*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
int32_t EQ::Net::Packet::GetInt32(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 4) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(int32_t*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
int64_t EQ::Net::Packet::GetInt64(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 8) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(int64_t*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
uint8_t EQ::Net::Packet::GetUInt8(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 1) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(uint8_t*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
uint16_t EQ::Net::Packet::GetUInt16(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 2) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(uint16_t*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
uint32_t EQ::Net::Packet::GetUInt32(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 4) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(uint32_t*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
uint64_t EQ::Net::Packet::GetUInt64(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 8) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(uint64_t*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
float EQ::Net::Packet::GetFloat(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 4) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(float*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
double EQ::Net::Packet::GetDouble(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 8) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return *(double*)((char*)Data() + offset);
|
||||
}
|
||||
|
||||
std::string EQ::Net::Packet::GetString(size_t offset, size_t length) const
|
||||
{
|
||||
if (Length() < offset + length) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
return std::string((char*)Data() + offset, (char*)Data() + offset + length);
|
||||
}
|
||||
|
||||
std::string EQ::Net::Packet::GetCString(size_t offset) const
|
||||
{
|
||||
if (Length() < offset + 1) {
|
||||
throw std::out_of_range("Packet read out of range.");
|
||||
}
|
||||
|
||||
char *str = ((char*)Data() + offset);
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
char ToSafePrint(unsigned char in) {
|
||||
if (std::isprint(in)) {
|
||||
return in;
|
||||
}
|
||||
|
||||
return '.';
|
||||
}
|
||||
|
||||
std::string EQ::Net::Packet::ToString() const
|
||||
{
|
||||
return ToString(16);
|
||||
}
|
||||
|
||||
std::string EQ::Net::Packet::ToString(size_t line_length) const
|
||||
{
|
||||
if (Length() == 0) {
|
||||
return fmt::format("{:0>5x} |", 0);
|
||||
}
|
||||
|
||||
std::string ret;
|
||||
size_t lines = Length() / line_length;
|
||||
size_t i;
|
||||
|
||||
char *data = (char*)Data();
|
||||
|
||||
for (i = 0; i < lines; ++i) {
|
||||
ret += fmt::format("{:0>5x} |", i * line_length);
|
||||
std::string hex;
|
||||
std::string ascii;
|
||||
for (size_t j = 0; j < line_length; ++j) {
|
||||
hex += fmt::format(" {:0>2x}", (uint8_t)data[(i * line_length) + j]);
|
||||
ascii += fmt::format("{}", ToSafePrint(data[(i * line_length) + j]));
|
||||
}
|
||||
|
||||
ret += hex;
|
||||
ret += " | ";
|
||||
ret += ascii;
|
||||
ret += "\n";
|
||||
}
|
||||
|
||||
if (Length() % line_length > 0) {
|
||||
ret += fmt::format("{:0>5x} |", i * line_length);
|
||||
|
||||
size_t non_blank_count = Length() % line_length;
|
||||
size_t blank_count = line_length - non_blank_count;
|
||||
std::string hex;
|
||||
std::string ascii;
|
||||
|
||||
for (size_t j = 0; j < non_blank_count; ++j) {
|
||||
hex += fmt::format(" {:0>2x}", (uint8_t)data[(i * line_length) + j]);
|
||||
ascii += fmt::format("{}", ToSafePrint(data[(i * line_length) + j]));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < blank_count; ++j) {
|
||||
hex += " ";
|
||||
ascii += " ";
|
||||
}
|
||||
|
||||
ret += hex;
|
||||
ret += " | ";
|
||||
ret += ascii;
|
||||
ret += "\n";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EQ::Net::StaticPacket::Resize(size_t new_size)
|
||||
{
|
||||
if (new_size > m_max_data_length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_data_length = new_size;
|
||||
}
|
||||
130
common/net/packet.h
Normal file
130
common/net/packet.h
Normal file
@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include "../util/memory_stream.h"
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
|
||||
namespace EQ {
|
||||
namespace Net {
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
Packet() : m_stream(std::ios::out | std::ios::binary) { }
|
||||
virtual ~Packet() { }
|
||||
|
||||
virtual const void *Data() const = 0;
|
||||
virtual void *Data() = 0;
|
||||
virtual size_t Length() const = 0;
|
||||
virtual size_t Length() = 0;
|
||||
virtual bool Clear() = 0;
|
||||
virtual bool Resize(size_t new_size) = 0;
|
||||
virtual void Reserve(size_t new_size) = 0;
|
||||
|
||||
template<typename T>
|
||||
T GetSerialize(size_t offset) const
|
||||
{
|
||||
T ret;
|
||||
Util::MemoryStreamReader reader(((char*)Data() + offset), Length());
|
||||
cereal::BinaryInputArchive input(reader);
|
||||
input(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PutSerialize(size_t offset, const T &value) {
|
||||
m_stream.clear();
|
||||
cereal::BinaryOutputArchive output(m_stream);
|
||||
output(value);
|
||||
|
||||
auto str = m_stream.str();
|
||||
if (Length() < offset + str.length()) {
|
||||
if (!Resize(offset + str.length())) {
|
||||
throw std::out_of_range("Packet::PutSerialize(), could not resize packet and would of written past the end.");
|
||||
}
|
||||
}
|
||||
|
||||
memcpy((char*)Data() + offset, &str[0], str.length());
|
||||
}
|
||||
|
||||
void PutInt8(size_t offset, int8_t value);
|
||||
void PutInt16(size_t offset, int16_t value);
|
||||
void PutInt32(size_t offset, int32_t value);
|
||||
void PutInt64(size_t offset, int64_t value);
|
||||
void PutUInt8(size_t offset, uint8_t value);
|
||||
void PutUInt16(size_t offset, uint16_t value);
|
||||
void PutUInt32(size_t offset, uint32_t value);
|
||||
void PutUInt64(size_t offset, uint64_t value);
|
||||
void PutFloat(size_t offset, float value);
|
||||
void PutDouble(size_t offset, double value);
|
||||
void PutString(size_t offset, const std::string &str);
|
||||
void PutCString(size_t offset, const char *str);
|
||||
void PutPacket(size_t offset, const Packet &p);
|
||||
void PutData(size_t offset, void *data, size_t length);
|
||||
|
||||
int8_t GetInt8(size_t offset) const;
|
||||
int16_t GetInt16(size_t offset) const;
|
||||
int32_t GetInt32(size_t offset) const;
|
||||
int64_t GetInt64(size_t offset) const;
|
||||
uint8_t GetUInt8(size_t offset) const;
|
||||
uint16_t GetUInt16(size_t offset) const;
|
||||
uint32_t GetUInt32(size_t offset) const;
|
||||
uint64_t GetUInt64(size_t offset) const;
|
||||
float GetFloat(size_t offset) const;
|
||||
double GetDouble(size_t offset) const;
|
||||
std::string GetString(size_t offset, size_t length) const;
|
||||
std::string GetCString(size_t offset) const;
|
||||
|
||||
std::string ToString() const;
|
||||
std::string ToString(size_t line_length) const;
|
||||
protected:
|
||||
std::stringstream m_stream;
|
||||
};
|
||||
|
||||
class StaticPacket : public Packet
|
||||
{
|
||||
public:
|
||||
StaticPacket(void *data, size_t size) { m_data = data; m_data_length = size; m_max_data_length = size; }
|
||||
virtual ~StaticPacket() { }
|
||||
StaticPacket(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; }
|
||||
StaticPacket& operator=(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; return *this; }
|
||||
StaticPacket(StaticPacket &&o) { m_data = o.m_data; m_data_length = o.m_data_length; }
|
||||
|
||||
virtual const void *Data() const { return m_data; }
|
||||
virtual void *Data() { return m_data; }
|
||||
virtual size_t Length() const { return m_data_length; }
|
||||
virtual size_t Length() { return m_data_length; }
|
||||
virtual bool Clear() { return false; }
|
||||
virtual bool Resize(size_t new_size);
|
||||
virtual void Reserve(size_t new_size) { }
|
||||
|
||||
protected:
|
||||
void *m_data;
|
||||
size_t m_data_length;
|
||||
size_t m_max_data_length;
|
||||
};
|
||||
|
||||
class DynamicPacket : public Packet
|
||||
{
|
||||
public:
|
||||
DynamicPacket() { }
|
||||
virtual ~DynamicPacket() { }
|
||||
DynamicPacket(DynamicPacket &&o) { m_data = std::move(o.m_data); }
|
||||
DynamicPacket(const DynamicPacket &o) { m_data = o.m_data; }
|
||||
DynamicPacket& operator=(const DynamicPacket &o) { m_data = o.m_data; return *this; }
|
||||
|
||||
virtual const void *Data() const { return &m_data[0]; }
|
||||
virtual void *Data() { return &m_data[0]; }
|
||||
virtual size_t Length() const { return m_data.size(); }
|
||||
virtual size_t Length() { return m_data.size(); }
|
||||
virtual bool Clear() { m_data.clear(); return true; }
|
||||
virtual bool Resize(size_t new_size) { m_data.resize(new_size, 0); return true; }
|
||||
virtual void Reserve(size_t new_size) { m_data.reserve(new_size); }
|
||||
protected:
|
||||
std::vector<char> m_data;
|
||||
};
|
||||
}
|
||||
}
|
||||
380
common/net/servertalk_client_connection.cpp
Normal file
380
common/net/servertalk_client_connection.cpp
Normal file
@ -0,0 +1,380 @@
|
||||
#include "servertalk_client_connection.h"
|
||||
#include "dns.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
|
||||
EQ::Net::ServertalkClient::ServertalkClient(const std::string &addr, int port, bool ipv6, const std::string &identifier, const std::string &credentials)
|
||||
: m_timer(std::unique_ptr<EQ::Timer>(new EQ::Timer(100, true, std::bind(&EQ::Net::ServertalkClient::Connect, this))))
|
||||
{
|
||||
m_port = port;
|
||||
m_ipv6 = ipv6;
|
||||
m_identifier = identifier.empty() ? "Unknown" : identifier;
|
||||
m_credentials = credentials;
|
||||
m_connecting = false;
|
||||
DNSLookup(addr, port, false, [this](const std::string &address) {
|
||||
m_addr = address;
|
||||
});
|
||||
}
|
||||
|
||||
EQ::Net::ServertalkClient::~ServertalkClient()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::Send(uint16_t opcode, EQ::Net::Packet &p)
|
||||
{
|
||||
EQ::Net::DynamicPacket out;
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
if (p.Length() == 0) {
|
||||
p.PutUInt8(0, 0);
|
||||
}
|
||||
|
||||
out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES);
|
||||
out.PutUInt16(4, opcode);
|
||||
|
||||
std::unique_ptr<unsigned char[]> cipher(new unsigned char[p.Length() + crypto_secretbox_MACBYTES]);
|
||||
|
||||
crypto_box_easy_afternm(&cipher[0], (unsigned char*)p.Data(), p.Length(), m_nonce_ours, m_shared_key);
|
||||
(*(uint64_t*)&m_nonce_ours[0])++;
|
||||
out.PutData(6, &cipher[0], p.Length() + crypto_secretbox_MACBYTES);
|
||||
}
|
||||
else {
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
}
|
||||
#else
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
#endif
|
||||
InternalSend(ServertalkMessage, out);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::SendPacket(ServerPacket *p)
|
||||
{
|
||||
EQ::Net::DynamicPacket pout;
|
||||
if (p->pBuffer) {
|
||||
pout.PutData(0, p->pBuffer, p->size);
|
||||
}
|
||||
Send(p->opcode, pout);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb)
|
||||
{
|
||||
m_message_callbacks.insert(std::make_pair(opcode, cb));
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb)
|
||||
{
|
||||
m_message_callback = cb;
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::Connect()
|
||||
{
|
||||
if (m_addr.length() == 0 || m_port == 0 || m_connection || m_connecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_connecting = true;
|
||||
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||
if (connection == nullptr) {
|
||||
LogF(Logs::General, Logs::TCP_Connection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
||||
m_connecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
LogF(Logs::General, Logs::TCP_Connection, "Connected to {0}:{1}", m_addr, m_port);
|
||||
m_connection = connection;
|
||||
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
|
||||
LogF(Logs::General, Logs::TCP_Connection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
||||
m_encrypted = false;
|
||||
m_connection.reset();
|
||||
});
|
||||
|
||||
m_connection->OnRead(std::bind(&EQ::Net::ServertalkClient::ProcessData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_connection->Start();
|
||||
|
||||
SendHello();
|
||||
m_connecting = false;
|
||||
});
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length)
|
||||
{
|
||||
m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + length);
|
||||
ProcessReadBuffer();
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::SendHello()
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
InternalSend(ServertalkClientHello, p);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::InternalSend(ServertalkPacketType type, EQ::Net::Packet &p)
|
||||
{
|
||||
if (!m_connection)
|
||||
return;
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutUInt32(0, (uint32_t)p.Length());
|
||||
out.PutUInt8(4, (uint8_t)type);
|
||||
if (p.Length() > 0) {
|
||||
out.PutPacket(5, p);
|
||||
}
|
||||
|
||||
m_connection->Write((const char*)out.Data(), out.Length());
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::ProcessReadBuffer()
|
||||
{
|
||||
size_t current = 0;
|
||||
size_t total = m_buffer.size();
|
||||
|
||||
while (current < total) {
|
||||
auto left = total - current;
|
||||
|
||||
/*
|
||||
//header:
|
||||
//uint32 length;
|
||||
//uint8 type;
|
||||
*/
|
||||
size_t length = 0;
|
||||
uint8_t type = 0;
|
||||
if (left < 5) {
|
||||
break;
|
||||
}
|
||||
|
||||
length = *(uint32_t*)&m_buffer[current];
|
||||
type = *(uint8_t*)&m_buffer[current + 4];
|
||||
|
||||
if (current + 5 + length > total) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
EQ::Net::DynamicPacket p;
|
||||
switch (type) {
|
||||
case ServertalkServerHello:
|
||||
ProcessHello(p);
|
||||
break;
|
||||
case ServertalkMessage:
|
||||
ProcessMessage(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
EQ::Net::StaticPacket p(&m_buffer[current + 5], length);
|
||||
switch (type) {
|
||||
case ServertalkServerHello:
|
||||
ProcessHello(p);
|
||||
break;
|
||||
case ServertalkMessage:
|
||||
ProcessMessage(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current += length + 5;
|
||||
}
|
||||
|
||||
if (current == total) {
|
||||
m_buffer.clear();
|
||||
}
|
||||
else {
|
||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p)
|
||||
{
|
||||
#ifdef ENABLE_SECURITY
|
||||
memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES);
|
||||
memset(m_nonce_ours, 0, crypto_box_NONCEBYTES);
|
||||
memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES);
|
||||
memset(m_shared_key, 0, crypto_box_BEFORENMBYTES);
|
||||
m_encrypted = false;
|
||||
|
||||
try {
|
||||
bool enc = p.GetInt8(0) == 1 ? true : false;
|
||||
|
||||
if (enc) {
|
||||
if (p.Length() == (1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) {
|
||||
memcpy(m_public_key_theirs, (char*)p.Data() + 1, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(m_nonce_theirs, (char*)p.Data() + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES);
|
||||
m_encrypted = true;
|
||||
|
||||
SendHandshake();
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogF(Logs::General, Logs::Error, "Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SendHandshake();
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
try {
|
||||
bool enc = p.GetInt8(0) == 1 ? true : false;
|
||||
|
||||
if (enc) {
|
||||
SendHandshake(true);
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SendHandshake();
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(nullptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p)
|
||||
{
|
||||
try {
|
||||
auto length = p.GetUInt32(0);
|
||||
auto opcode = p.GetUInt16(4);
|
||||
if (length > 0) {
|
||||
auto data = p.GetString(6, length);
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
size_t message_len = length - crypto_secretbox_MACBYTES;
|
||||
std::unique_ptr<unsigned char[]> decrypted_text(new unsigned char[message_len]);
|
||||
if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key))
|
||||
{
|
||||
LogF(Logs::General, Logs::Error, "Error decrypting message from server");
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
return;
|
||||
}
|
||||
|
||||
EQ::Net::StaticPacket decrypted_packet(&decrypted_text[0], message_len);
|
||||
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, decrypted_packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, decrypted_packet);
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, packet);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, packet);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing message from server: {0}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkClient::SendHandshake(bool downgrade)
|
||||
{
|
||||
EQ::Net::DynamicPacket handshake;
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
crypto_box_keypair(m_public_key_ours, m_private_key_ours);
|
||||
randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES);
|
||||
|
||||
crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours);
|
||||
|
||||
handshake.PutData(0, m_public_key_ours, crypto_box_PUBLICKEYBYTES);
|
||||
handshake.PutData(crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES);
|
||||
|
||||
memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES);
|
||||
|
||||
size_t cipher_length = m_identifier.length() + 1 + m_credentials.length() + 1 + crypto_secretbox_MACBYTES;
|
||||
size_t data_length = m_identifier.length() + 1 + m_credentials.length() + 1;
|
||||
|
||||
std::unique_ptr<unsigned char[]> signed_buffer(new unsigned char[cipher_length]);
|
||||
std::unique_ptr<unsigned char[]> data_buffer(new unsigned char[data_length]);
|
||||
|
||||
memset(&data_buffer[0], 0, data_length);
|
||||
memcpy(&data_buffer[0], m_identifier.c_str(), m_identifier.length());
|
||||
memcpy(&data_buffer[1 + m_identifier.length()], m_credentials.c_str(), m_credentials.length());
|
||||
|
||||
crypto_box_easy_afternm(&signed_buffer[0], &data_buffer[0], data_length, m_nonce_ours, m_shared_key);
|
||||
|
||||
(*(uint64_t*)&m_nonce_ours[0])++;
|
||||
|
||||
handshake.PutData(crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, &signed_buffer[0], cipher_length);
|
||||
}
|
||||
else {
|
||||
handshake.PutString(0, m_identifier);
|
||||
handshake.PutString(m_identifier.length() + 1, m_credentials);
|
||||
handshake.PutUInt8(m_identifier.length() + 1 + m_credentials.length(), 0);
|
||||
}
|
||||
#else
|
||||
handshake.PutString(0, m_identifier);
|
||||
handshake.PutString(m_identifier.length() + 1, m_credentials);
|
||||
handshake.PutUInt8(m_identifier.length() + 1 + m_credentials.length(), 0);
|
||||
#endif
|
||||
|
||||
if (downgrade) {
|
||||
InternalSend(ServertalkClientDowngradeSecurityHandshake, handshake);
|
||||
}
|
||||
else {
|
||||
InternalSend(ServertalkClientHandshake, handshake);
|
||||
}
|
||||
}
|
||||
67
common/net/servertalk_client_connection.h
Normal file
67
common/net/servertalk_client_connection.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include "tcp_connection.h"
|
||||
#include "../event/timer.h"
|
||||
#include "servertalk_common.h"
|
||||
#include "packet.h"
|
||||
#ifdef ENABLE_SECURITY
|
||||
#include <sodium.h>
|
||||
#endif
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
class ServertalkClient
|
||||
{
|
||||
public:
|
||||
ServertalkClient(const std::string &addr, int port, bool ipv6, const std::string &identifier, const std::string &credentials);
|
||||
~ServertalkClient();
|
||||
|
||||
void Send(uint16_t opcode, EQ::Net::Packet &p);
|
||||
void SendPacket(ServerPacket *p);
|
||||
void OnConnect(std::function<void(ServertalkClient*)> cb) { m_on_connect_cb = cb; }
|
||||
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
void OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
bool Connected() const { return m_connecting != true; }
|
||||
|
||||
std::shared_ptr<EQ::Net::TCPConnection> Handle() { return m_connection; }
|
||||
private:
|
||||
void Connect();
|
||||
void ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length);
|
||||
void SendHello();
|
||||
void InternalSend(ServertalkPacketType type, EQ::Net::Packet &p);
|
||||
void ProcessReadBuffer();
|
||||
void ProcessHello(EQ::Net::Packet &p);
|
||||
void ProcessMessage(EQ::Net::Packet &p);
|
||||
void SendHandshake() { SendHandshake(false); }
|
||||
void SendHandshake(bool downgrade);
|
||||
|
||||
std::unique_ptr<EQ::Timer> m_timer;
|
||||
|
||||
std::string m_addr;
|
||||
std::string m_identifier;
|
||||
std::string m_credentials;
|
||||
bool m_connecting;
|
||||
int m_port;
|
||||
bool m_ipv6;
|
||||
bool m_encrypted;
|
||||
std::shared_ptr<EQ::Net::TCPConnection> m_connection;
|
||||
std::vector<char> m_buffer;
|
||||
std::unordered_map<uint16_t, std::function<void(uint16_t, EQ::Net::Packet&)>> m_message_callbacks;
|
||||
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
|
||||
std::function<void(ServertalkClient*)> m_on_connect_cb;
|
||||
|
||||
#ifdef ENABLE_SECURITY
|
||||
unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char m_private_key_ours[crypto_box_SECRETKEYBYTES];
|
||||
unsigned char m_nonce_ours[crypto_box_NONCEBYTES];
|
||||
|
||||
unsigned char m_public_key_theirs[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char m_nonce_theirs[crypto_box_NONCEBYTES];
|
||||
|
||||
unsigned char m_shared_key[crypto_box_BEFORENMBYTES];
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
18
common/net/servertalk_common.h
Normal file
18
common/net/servertalk_common.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "../servertalk.h"
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
enum ServertalkPacketType
|
||||
{
|
||||
ServertalkClientHello = 1,
|
||||
ServertalkServerHello,
|
||||
ServertalkClientHandshake,
|
||||
ServertalkClientDowngradeSecurityHandshake,
|
||||
ServertalkMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
154
common/net/servertalk_legacy_client_connection.cpp
Normal file
154
common/net/servertalk_legacy_client_connection.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
#include "servertalk_legacy_client_connection.h"
|
||||
#include "dns.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
|
||||
EQ::Net::ServertalkLegacyClient::ServertalkLegacyClient(const std::string &addr, int port, bool ipv6)
|
||||
: m_timer(std::unique_ptr<EQ::Timer>(new EQ::Timer(100, true, std::bind(&EQ::Net::ServertalkLegacyClient::Connect, this))))
|
||||
{
|
||||
m_port = port;
|
||||
m_ipv6 = ipv6;
|
||||
m_connecting = false;
|
||||
DNSLookup(addr, port, false, [this](const std::string &address) {
|
||||
m_addr = address;
|
||||
});
|
||||
}
|
||||
|
||||
EQ::Net::ServertalkLegacyClient::~ServertalkLegacyClient()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkLegacyClient::Send(uint16_t opcode, EQ::Net::Packet &p)
|
||||
{
|
||||
if (!m_connection)
|
||||
return;
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutUInt16(0, opcode);
|
||||
out.PutUInt16(2, p.Length() + 4);
|
||||
out.PutPacket(4, p);
|
||||
|
||||
m_connection->Write((const char*)out.Data(), out.Length());
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkLegacyClient::SendPacket(ServerPacket *p)
|
||||
{
|
||||
EQ::Net::DynamicPacket pout;
|
||||
if (p->pBuffer) {
|
||||
pout.PutData(0, p->pBuffer, p->size);
|
||||
}
|
||||
Send(p->opcode, pout);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkLegacyClient::OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb)
|
||||
{
|
||||
m_message_callbacks.insert(std::make_pair(opcode, cb));
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkLegacyClient::OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb)
|
||||
{
|
||||
m_message_callback = cb;
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkLegacyClient::Connect()
|
||||
{
|
||||
if (m_addr.length() == 0 || m_port == 0 || m_connection || m_connecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_connecting = true;
|
||||
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||
if (connection == nullptr) {
|
||||
LogF(Logs::General, Logs::TCP_Connection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
||||
m_connecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
LogF(Logs::General, Logs::TCP_Connection, "Connected to {0}:{1}", m_addr, m_port);
|
||||
m_connection = connection;
|
||||
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
|
||||
LogF(Logs::General, Logs::TCP_Connection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
||||
m_connection.reset();
|
||||
});
|
||||
|
||||
m_connection->OnRead(std::bind(&EQ::Net::ServertalkLegacyClient::ProcessData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_connection->Start();
|
||||
m_connecting = false;
|
||||
|
||||
if (m_on_connect_cb) {
|
||||
m_on_connect_cb(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkLegacyClient::ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length)
|
||||
{
|
||||
m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + length);
|
||||
ProcessReadBuffer();
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkLegacyClient::ProcessReadBuffer()
|
||||
{
|
||||
size_t current = 0;
|
||||
size_t total = m_buffer.size();
|
||||
|
||||
while (current < total) {
|
||||
auto left = total - current;
|
||||
|
||||
/*
|
||||
//header:
|
||||
//uint16 opcode;
|
||||
//uint16 length;
|
||||
*/
|
||||
uint16_t length = 0;
|
||||
uint16_t opcode = 0;
|
||||
if (left < 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
opcode = *(uint16_t*)&m_buffer[current];
|
||||
length = *(uint16_t*)&m_buffer[current + 2];
|
||||
if (length < 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
length -= 4;
|
||||
|
||||
if (current + 4 + length > total) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
EQ::Net::DynamicPacket p;
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, p);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, p);
|
||||
}
|
||||
}
|
||||
else {
|
||||
EQ::Net::StaticPacket p(&m_buffer[current + 4], length);
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, p);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, p);
|
||||
}
|
||||
}
|
||||
|
||||
current += length + 4;
|
||||
}
|
||||
|
||||
if (current == total) {
|
||||
m_buffer.clear();
|
||||
}
|
||||
else {
|
||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
|
||||
}
|
||||
}
|
||||
44
common/net/servertalk_legacy_client_connection.h
Normal file
44
common/net/servertalk_legacy_client_connection.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "tcp_connection.h"
|
||||
#include "../event/timer.h"
|
||||
#include "servertalk_common.h"
|
||||
#include "packet.h"
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
class ServertalkLegacyClient
|
||||
{
|
||||
public:
|
||||
ServertalkLegacyClient(const std::string &addr, int port, bool ipv6);
|
||||
~ServertalkLegacyClient();
|
||||
|
||||
void Send(uint16_t opcode, EQ::Net::Packet &p);
|
||||
void SendPacket(ServerPacket *p);
|
||||
void OnConnect(std::function<void(ServertalkLegacyClient*)> cb) { m_on_connect_cb = cb; }
|
||||
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
void OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
bool Connected() const { return m_connecting != true; }
|
||||
|
||||
std::shared_ptr<EQ::Net::TCPConnection> Handle() { return m_connection; }
|
||||
private:
|
||||
void Connect();
|
||||
void ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length);
|
||||
void ProcessReadBuffer();
|
||||
|
||||
std::unique_ptr<EQ::Timer> m_timer;
|
||||
|
||||
std::string m_addr;
|
||||
bool m_connecting;
|
||||
int m_port;
|
||||
bool m_ipv6;
|
||||
std::shared_ptr<EQ::Net::TCPConnection> m_connection;
|
||||
std::vector<char> m_buffer;
|
||||
std::unordered_map<uint16_t, std::function<void(uint16_t, EQ::Net::Packet&)>> m_message_callbacks;
|
||||
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
|
||||
std::function<void(ServertalkLegacyClient*)> m_on_connect_cb;
|
||||
};
|
||||
}
|
||||
}
|
||||
97
common/net/servertalk_server.cpp
Normal file
97
common/net/servertalk_server.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include "servertalk_server.h"
|
||||
|
||||
EQ::Net::ServertalkServer::ServertalkServer()
|
||||
{
|
||||
}
|
||||
|
||||
EQ::Net::ServertalkServer::~ServertalkServer()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::Listen(const ServertalkServerOptions& opts)
|
||||
{
|
||||
m_encrypted = opts.encrypted;
|
||||
m_credentials = opts.credentials;
|
||||
m_allow_downgrade = opts.allow_downgrade;
|
||||
m_server.reset(new EQ::Net::TCPServer());
|
||||
m_server->Listen(opts.port, opts.ipv6, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||
m_unident_connections.push_back(std::make_shared<ServertalkServerConnection>(connection, this, m_encrypted, m_allow_downgrade));
|
||||
});
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::OnConnectionIdentified(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb)
|
||||
{
|
||||
m_on_ident.insert(std::make_pair(type, cb));
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::OnConnectionRemoved(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb)
|
||||
{
|
||||
m_on_disc.insert(std::make_pair(type, cb));
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::ConnectionDisconnected(ServertalkServerConnection *conn)
|
||||
{
|
||||
if (conn->GetIdentifier().empty()) {
|
||||
auto iter = m_unident_connections.begin();
|
||||
while (iter != m_unident_connections.end()) {
|
||||
if (conn == iter->get()) {
|
||||
m_unident_connections.erase(iter);
|
||||
return;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto type = m_ident_connections.find(conn->GetIdentifier());
|
||||
if (type != m_ident_connections.end()) {
|
||||
auto iter = type->second.begin();
|
||||
while (iter != type->second.end()) {
|
||||
if (conn == iter->get()) {
|
||||
auto on_disc = m_on_disc.find(conn->GetIdentifier());
|
||||
if (on_disc != m_on_disc.end()) {
|
||||
on_disc->second(*iter);
|
||||
}
|
||||
type->second.erase(iter);
|
||||
return;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServer::ConnectionIdentified(ServertalkServerConnection *conn)
|
||||
{
|
||||
auto iter = m_unident_connections.begin();
|
||||
while (iter != m_unident_connections.end()) {
|
||||
if (conn == iter->get()) {
|
||||
auto on_ident = m_on_ident.find(conn->GetIdentifier());
|
||||
if (on_ident != m_on_ident.end()) {
|
||||
on_ident->second(*iter);
|
||||
}
|
||||
|
||||
if (m_ident_connections.count(conn->GetIdentifier()) > 0) {
|
||||
auto &vec = m_ident_connections[conn->GetIdentifier()];
|
||||
vec.push_back(*iter);
|
||||
}
|
||||
else {
|
||||
std::vector<std::shared_ptr<EQ::Net::ServertalkServerConnection>> vec;
|
||||
vec.push_back(*iter);
|
||||
m_ident_connections.insert(std::make_pair(conn->GetIdentifier(), vec));
|
||||
}
|
||||
|
||||
m_unident_connections.erase(iter);
|
||||
return;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
bool EQ::Net::ServertalkServer::CheckCredentials(const std::string &credentials)
|
||||
{
|
||||
if (credentials.compare(m_credentials) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
64
common/net/servertalk_server.h
Normal file
64
common/net/servertalk_server.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "tcp_server.h"
|
||||
#include "servertalk_server_connection.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#ifdef ENABLE_SECURITY
|
||||
#include <sodium.h>
|
||||
#endif
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
struct ServertalkServerOptions
|
||||
{
|
||||
int port;
|
||||
bool ipv6;
|
||||
bool encrypted;
|
||||
bool allow_downgrade;
|
||||
std::string credentials;
|
||||
|
||||
ServertalkServerOptions() {
|
||||
#ifdef ENABLE_SECURITY
|
||||
encrypted = true;
|
||||
allow_downgrade = true;
|
||||
#else
|
||||
encrypted = false;
|
||||
allow_downgrade = true;
|
||||
#endif
|
||||
ipv6 = false;
|
||||
}
|
||||
};
|
||||
|
||||
class ServertalkServer
|
||||
{
|
||||
public:
|
||||
ServertalkServer();
|
||||
~ServertalkServer();
|
||||
|
||||
void Listen(const ServertalkServerOptions& opts);
|
||||
void OnConnectionIdentified(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb);
|
||||
void OnConnectionRemoved(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb);
|
||||
|
||||
private:
|
||||
void ConnectionDisconnected(ServertalkServerConnection *conn);
|
||||
void ConnectionIdentified(ServertalkServerConnection *conn);
|
||||
bool CheckCredentials(const std::string &credentials);
|
||||
|
||||
std::unique_ptr<EQ::Net::TCPServer> m_server;
|
||||
std::vector<std::shared_ptr<ServertalkServerConnection>> m_unident_connections;
|
||||
std::map<std::string, std::vector<std::shared_ptr<ServertalkServerConnection>>> m_ident_connections;
|
||||
|
||||
std::map<std::string, std::function<void(std::shared_ptr<ServertalkServerConnection>)>> m_on_ident;
|
||||
std::map<std::string, std::function<void(std::shared_ptr<ServertalkServerConnection>)>> m_on_disc;
|
||||
bool m_encrypted;
|
||||
bool m_allow_downgrade;
|
||||
std::string m_credentials;
|
||||
|
||||
friend class ServertalkServerConnection;
|
||||
};
|
||||
}
|
||||
}
|
||||
348
common/net/servertalk_server_connection.cpp
Normal file
348
common/net/servertalk_server_connection.cpp
Normal file
@ -0,0 +1,348 @@
|
||||
#include "servertalk_server_connection.h"
|
||||
#include "servertalk_server.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
#include "../util/uuid.h"
|
||||
|
||||
EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr<EQ::Net::TCPConnection> c, EQ::Net::ServertalkServer *parent, bool encrypted, bool allow_downgrade)
|
||||
{
|
||||
m_connection = c;
|
||||
m_parent = parent;
|
||||
m_encrypted = encrypted;
|
||||
m_allow_downgrade = allow_downgrade;
|
||||
m_uuid = EQ::Util::UUID::Generate().ToString();
|
||||
m_connection->OnRead(std::bind(&ServertalkServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_connection->OnDisconnect(std::bind(&ServertalkServerConnection::OnDisconnect, this, std::placeholders::_1));
|
||||
m_connection->Start();
|
||||
}
|
||||
|
||||
EQ::Net::ServertalkServerConnection::~ServertalkServerConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p)
|
||||
{
|
||||
EQ::Net::DynamicPacket out;
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
if (p.Length() == 0) {
|
||||
p.PutUInt8(0, 0);
|
||||
}
|
||||
|
||||
out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES);
|
||||
out.PutUInt16(4, opcode);
|
||||
|
||||
std::unique_ptr<unsigned char[]> cipher(new unsigned char[p.Length() + crypto_secretbox_MACBYTES]);
|
||||
crypto_box_easy_afternm(&cipher[0], (unsigned char*)p.Data(), p.Length(), m_nonce_ours, m_shared_key);
|
||||
(*(uint64_t*)&m_nonce_ours[0])++;
|
||||
out.PutData(6, &cipher[0], p.Length() + crypto_secretbox_MACBYTES);
|
||||
}
|
||||
else {
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
}
|
||||
#else
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
#endif
|
||||
InternalSend(ServertalkMessage, out);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::SendPacket(ServerPacket *p)
|
||||
{
|
||||
EQ::Net::DynamicPacket pout;
|
||||
if (p->pBuffer) {
|
||||
pout.PutData(0, p->pBuffer, p->size);
|
||||
}
|
||||
Send(p->opcode, pout);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb)
|
||||
{
|
||||
m_message_callbacks.insert(std::make_pair(opcode, cb));
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb)
|
||||
{
|
||||
m_message_callback = cb;
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz)
|
||||
{
|
||||
m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + sz);
|
||||
ProcessReadBuffer();
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::ProcessReadBuffer()
|
||||
{
|
||||
size_t current = 0;
|
||||
size_t total = m_buffer.size();
|
||||
|
||||
while (current < total) {
|
||||
auto left = total - current;
|
||||
|
||||
/*
|
||||
//header:
|
||||
//uint32 length;
|
||||
//uint8 type;
|
||||
*/
|
||||
size_t length = 0;
|
||||
uint8_t type = 0;
|
||||
if (left < 5) {
|
||||
break;
|
||||
}
|
||||
|
||||
length = *(uint32_t*)&m_buffer[current];
|
||||
type = *(uint8_t*)&m_buffer[current + 4];
|
||||
|
||||
if (current + 5 + length > total) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
EQ::Net::DynamicPacket p;
|
||||
switch (type) {
|
||||
case ServertalkClientHello:
|
||||
{
|
||||
SendHello();
|
||||
}
|
||||
break;
|
||||
case ServertalkClientHandshake:
|
||||
ProcessHandshake(p);
|
||||
break;
|
||||
case ServertalkMessage:
|
||||
ProcessMessage(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
EQ::Net::StaticPacket p(&m_buffer[current + 5], length);
|
||||
switch (type) {
|
||||
case ServertalkClientHello:
|
||||
{
|
||||
SendHello();
|
||||
}
|
||||
break;
|
||||
case ServertalkClientHandshake:
|
||||
ProcessHandshake(p);
|
||||
break;
|
||||
case ServertalkClientDowngradeSecurityHandshake:
|
||||
ProcessHandshake(p, true);
|
||||
break;
|
||||
case ServertalkMessage:
|
||||
ProcessMessage(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current += length + 5;
|
||||
}
|
||||
|
||||
if (current == total) {
|
||||
m_buffer.clear();
|
||||
}
|
||||
else {
|
||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::OnDisconnect(TCPConnection *c)
|
||||
{
|
||||
m_parent->ConnectionDisconnected(this);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::SendHello()
|
||||
{
|
||||
EQ::Net::DynamicPacket hello;
|
||||
|
||||
#ifdef ENABLE_SECURITY
|
||||
memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES);
|
||||
memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES);
|
||||
memset(m_nonce_ours, 0, crypto_box_NONCEBYTES);
|
||||
memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES);
|
||||
|
||||
if (m_encrypted) {
|
||||
hello.PutInt8(0, 1);
|
||||
|
||||
crypto_box_keypair(m_public_key_ours, m_private_key_ours);
|
||||
randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES);
|
||||
|
||||
hello.PutData(1, m_public_key_ours, crypto_box_PUBLICKEYBYTES);
|
||||
hello.PutData(1 + crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES);
|
||||
}
|
||||
else {
|
||||
hello.PutInt8(0, 0);
|
||||
}
|
||||
#else
|
||||
hello.PutInt8(0, 0);
|
||||
#endif
|
||||
|
||||
InternalSend(ServertalkServerHello, hello);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::InternalSend(ServertalkPacketType type, EQ::Net::Packet &p)
|
||||
{
|
||||
if (!m_connection)
|
||||
return;
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutUInt32(0, (uint32_t)p.Length());
|
||||
out.PutUInt8(4, (uint8_t)type);
|
||||
if (p.Length() > 0) {
|
||||
out.PutPacket(5, p);
|
||||
}
|
||||
|
||||
m_connection->Write((const char*)out.Data(), out.Length());
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, bool downgrade_security)
|
||||
{
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (downgrade_security && m_allow_downgrade && m_encrypted) {
|
||||
LogF(Logs::General, Logs::TCP_Connection, "Downgraded encrypted connection to plaintext because otherside didn't support encryption {0}:{1}",
|
||||
m_connection->RemoteIP(), m_connection->RemotePort());
|
||||
m_encrypted = false;
|
||||
}
|
||||
|
||||
if (m_encrypted) {
|
||||
try {
|
||||
if (p.Length() > (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) {
|
||||
memcpy(m_public_key_theirs, (char*)p.Data(), crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(m_nonce_theirs, (char*)p.Data() + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES);
|
||||
|
||||
crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours);
|
||||
|
||||
size_t cipher_len = p.Length() - crypto_box_PUBLICKEYBYTES - crypto_box_NONCEBYTES;
|
||||
size_t message_len = cipher_len - crypto_secretbox_MACBYTES;
|
||||
std::unique_ptr<unsigned char[]> decrypted_text(new unsigned char[message_len]);
|
||||
|
||||
if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)p.Data() + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, cipher_len, m_nonce_theirs, m_shared_key))
|
||||
{
|
||||
LogF(Logs::General, Logs::Error, "Error decrypting handshake from client, dropping connection.");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
m_identifier = (const char*)&decrypted_text[0];
|
||||
std::string credentials = (const char*)&decrypted_text[0] + (m_identifier.length() + 1);
|
||||
|
||||
if (!m_parent->CheckCredentials(credentials)) {
|
||||
LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection.");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
m_parent->ConnectionIdentified(this);
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
m_identifier = p.GetCString(0);
|
||||
auto credentials = p.GetCString(m_identifier.length() + 1);
|
||||
|
||||
if (!m_parent->CheckCredentials(credentials)) {
|
||||
LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection.");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
m_parent->ConnectionIdentified(this);
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
}
|
||||
#else
|
||||
try {
|
||||
m_identifier = p.GetCString(0);
|
||||
auto credentials = p.GetCString(m_identifier.length() + 1);
|
||||
|
||||
if (!m_parent->CheckCredentials(credentials)) {
|
||||
LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection.");
|
||||
m_connection->Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
m_parent->ConnectionIdentified(this);
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what());
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
|
||||
{
|
||||
try {
|
||||
auto length = p.GetUInt32(0);
|
||||
auto opcode = p.GetUInt16(4);
|
||||
if (length > 0) {
|
||||
auto data = p.GetString(6, length);
|
||||
#ifdef ENABLE_SECURITY
|
||||
if (m_encrypted) {
|
||||
size_t message_len = length - crypto_secretbox_MACBYTES;
|
||||
std::unique_ptr<unsigned char[]> decrypted_text(new unsigned char[message_len]);
|
||||
|
||||
if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key))
|
||||
{
|
||||
LogF(Logs::General, Logs::Error, "Error decrypting message from client");
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
return;
|
||||
}
|
||||
|
||||
EQ::Net::StaticPacket decrypted_packet(&decrypted_text[0], message_len);
|
||||
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, decrypted_packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, decrypted_packet);
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, packet);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, packet);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, packet);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogF(Logs::General, Logs::Error, "Error parsing message from client: {0}", ex.what());
|
||||
}
|
||||
}
|
||||
63
common/net/servertalk_server_connection.h
Normal file
63
common/net/servertalk_server_connection.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "tcp_connection.h"
|
||||
#include "servertalk_common.h"
|
||||
#include "packet.h"
|
||||
#include <vector>
|
||||
#ifdef ENABLE_SECURITY
|
||||
#include <sodium.h>
|
||||
#endif
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
class ServertalkServer;
|
||||
class ServertalkServerConnection
|
||||
{
|
||||
public:
|
||||
ServertalkServerConnection(std::shared_ptr<EQ::Net::TCPConnection> c, ServertalkServer *parent, bool encrypted, bool allow_downgrade);
|
||||
~ServertalkServerConnection();
|
||||
|
||||
void Send(uint16_t opcode, EQ::Net::Packet &p);
|
||||
void SendPacket(ServerPacket *p);
|
||||
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
void OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb);
|
||||
|
||||
std::string GetIdentifier() const { return m_identifier; }
|
||||
std::shared_ptr<EQ::Net::TCPConnection> Handle() { return m_connection; }
|
||||
std::string GetUUID() const { return m_uuid; }
|
||||
private:
|
||||
void OnRead(TCPConnection* c, const unsigned char* data, size_t sz);
|
||||
void ProcessReadBuffer();
|
||||
void OnDisconnect(TCPConnection* c);
|
||||
void SendHello();
|
||||
void InternalSend(ServertalkPacketType type, EQ::Net::Packet &p);
|
||||
void ProcessHandshake(EQ::Net::Packet &p) { ProcessHandshake(p, false); }
|
||||
void ProcessHandshake(EQ::Net::Packet &p, bool security_downgrade);
|
||||
void ProcessMessage(EQ::Net::Packet &p);
|
||||
|
||||
std::shared_ptr<EQ::Net::TCPConnection> m_connection;
|
||||
ServertalkServer *m_parent;
|
||||
|
||||
std::vector<char> m_buffer;
|
||||
std::unordered_map<uint16_t, std::function<void(uint16_t, EQ::Net::Packet&)>> m_message_callbacks;
|
||||
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
|
||||
std::string m_identifier;
|
||||
std::string m_uuid;
|
||||
|
||||
bool m_encrypted;
|
||||
bool m_allow_downgrade;
|
||||
#ifdef ENABLE_SECURITY
|
||||
unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char m_private_key_ours[crypto_box_SECRETKEYBYTES];
|
||||
unsigned char m_nonce_ours[crypto_box_NONCEBYTES];
|
||||
|
||||
unsigned char m_public_key_theirs[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char m_nonce_theirs[crypto_box_NONCEBYTES];
|
||||
|
||||
unsigned char m_shared_key[crypto_box_BEFORENMBYTES];
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
238
common/net/tcp_connection.cpp
Normal file
238
common/net/tcp_connection.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
#include "tcp_connection.h"
|
||||
#include "../event/event_loop.h"
|
||||
|
||||
void on_close_handle(uv_handle_t* handle) {
|
||||
delete handle;
|
||||
}
|
||||
|
||||
EQ::Net::TCPConnection::TCPConnection(uv_tcp_t *socket)
|
||||
{
|
||||
m_socket = socket;
|
||||
m_socket->data = this;
|
||||
}
|
||||
|
||||
EQ::Net::TCPConnection::~TCPConnection() {
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
void EQ::Net::TCPConnection::Connect(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb)
|
||||
{
|
||||
struct EQTCPConnectBaton
|
||||
{
|
||||
uv_tcp_t *socket;
|
||||
std::function<void(std::shared_ptr<EQ::Net::TCPConnection>)> cb;
|
||||
};
|
||||
|
||||
auto loop = EQ::EventLoop::Get().Handle();
|
||||
uv_tcp_t *socket = new uv_tcp_t;
|
||||
memset(socket, 0, sizeof(uv_tcp_t));
|
||||
uv_tcp_init(loop, socket);
|
||||
|
||||
sockaddr_storage iaddr;
|
||||
if (ipv6) {
|
||||
uv_ip6_addr(addr.c_str(), port, (sockaddr_in6*)&iaddr);
|
||||
}
|
||||
else {
|
||||
uv_ip4_addr(addr.c_str(), port, (sockaddr_in*)&iaddr);
|
||||
}
|
||||
|
||||
uv_connect_t *connect = new uv_connect_t;
|
||||
memset(connect, 0, sizeof(uv_connect_t));
|
||||
|
||||
EQTCPConnectBaton *baton = new EQTCPConnectBaton;
|
||||
baton->cb = cb;
|
||||
baton->socket = socket;
|
||||
connect->data = baton;
|
||||
uv_tcp_connect(connect, socket, (sockaddr*)&iaddr,
|
||||
[](uv_connect_t* req, int status) {
|
||||
EQTCPConnectBaton *baton = (EQTCPConnectBaton*)req->data;
|
||||
auto socket = baton->socket;
|
||||
auto cb = baton->cb;
|
||||
|
||||
delete baton;
|
||||
|
||||
if (status < 0) {
|
||||
uv_close((uv_handle_t*)socket, on_close_handle);
|
||||
delete req;
|
||||
cb(nullptr);
|
||||
}
|
||||
else {
|
||||
delete req;
|
||||
std::shared_ptr<EQ::Net::TCPConnection> connection(new EQ::Net::TCPConnection(socket));
|
||||
cb(connection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EQ::Net::TCPConnection::Start() {
|
||||
uv_read_start((uv_stream_t*)m_socket, [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
||||
buf->base = new char[suggested_size];
|
||||
buf->len = suggested_size;
|
||||
}, [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
|
||||
|
||||
TCPConnection *connection = (TCPConnection*)stream->data;
|
||||
|
||||
if (nread > 0) {
|
||||
connection->Read(buf->base, nread);
|
||||
|
||||
if (buf->base) {
|
||||
delete[] buf->base;
|
||||
}
|
||||
}
|
||||
else if (nread == UV_EOF) {
|
||||
if (buf->base) {
|
||||
delete[] buf->base;
|
||||
}
|
||||
}
|
||||
else if (nread < 0) {
|
||||
connection->Disconnect();
|
||||
|
||||
if (buf->base) {
|
||||
delete[] buf->base;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EQ::Net::TCPConnection::OnRead(std::function<void(TCPConnection*, const unsigned char*, size_t)> cb)
|
||||
{
|
||||
m_on_read_cb = cb;
|
||||
}
|
||||
|
||||
void EQ::Net::TCPConnection::OnDisconnect(std::function<void(TCPConnection*)> cb)
|
||||
{
|
||||
m_on_disconnect_cb = cb;
|
||||
}
|
||||
|
||||
void EQ::Net::TCPConnection::Disconnect()
|
||||
{
|
||||
if (m_socket) {
|
||||
m_socket->data = this;
|
||||
uv_close((uv_handle_t*)m_socket, [](uv_handle_t* handle) {
|
||||
TCPConnection *connection = (TCPConnection*)handle->data;
|
||||
|
||||
if (connection->m_on_disconnect_cb) {
|
||||
connection->m_on_disconnect_cb(connection);
|
||||
}
|
||||
|
||||
delete handle;
|
||||
});
|
||||
m_socket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::TCPConnection::Read(const char *data, size_t count)
|
||||
{
|
||||
if (m_on_read_cb) {
|
||||
m_on_read_cb(this, (unsigned char*)data, count);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::TCPConnection::Write(const char *data, size_t count)
|
||||
{
|
||||
if (!m_socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct WriteBaton
|
||||
{
|
||||
TCPConnection *connection;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
WriteBaton *baton = new WriteBaton;
|
||||
baton->connection = this;
|
||||
baton->buffer = new char[count];;
|
||||
|
||||
uv_write_t *write_req = new uv_write_t;
|
||||
memset(write_req, 0, sizeof(uv_write_t));
|
||||
write_req->data = baton;
|
||||
uv_buf_t send_buffers[1];
|
||||
|
||||
memcpy(baton->buffer, data, count);
|
||||
send_buffers[0] = uv_buf_init(baton->buffer, count);
|
||||
|
||||
uv_write(write_req, (uv_stream_t*)m_socket, send_buffers, 1, [](uv_write_t* req, int status) {
|
||||
WriteBaton *baton = (WriteBaton*)req->data;
|
||||
delete[] baton->buffer;
|
||||
delete req;
|
||||
|
||||
if (status < 0) {
|
||||
baton->connection->Disconnect();
|
||||
}
|
||||
|
||||
delete baton;
|
||||
});
|
||||
}
|
||||
|
||||
std::string EQ::Net::TCPConnection::LocalIP() const
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
int addr_len = sizeof(addr);
|
||||
uv_tcp_getsockname(m_socket, (sockaddr*)&addr, &addr_len);
|
||||
|
||||
char endpoint[64] = { 0 };
|
||||
if (addr.ss_family == AF_INET) {
|
||||
uv_ip4_name((const sockaddr_in*)&addr, endpoint, 64);
|
||||
}
|
||||
else if (addr.ss_family == AF_INET6) {
|
||||
uv_ip6_name((const sockaddr_in6*)&addr, endpoint, 64);
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
int EQ::Net::TCPConnection::LocalPort() const
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
int addr_len = sizeof(addr);
|
||||
uv_tcp_getsockname(m_socket, (sockaddr*)&addr, &addr_len);
|
||||
|
||||
char endpoint[64] = { 0 };
|
||||
if (addr.ss_family == AF_INET) {
|
||||
sockaddr_in *s = (sockaddr_in*)&addr;
|
||||
return ntohs(s->sin_port);
|
||||
}
|
||||
else if (addr.ss_family == AF_INET6) {
|
||||
sockaddr_in6 *s = (sockaddr_in6*)&addr;
|
||||
return ntohs(s->sin6_port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string EQ::Net::TCPConnection::RemoteIP() const
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
int addr_len = sizeof(addr);
|
||||
uv_tcp_getpeername(m_socket, (sockaddr*)&addr, &addr_len);
|
||||
|
||||
char endpoint[64] = { 0 };
|
||||
if (addr.ss_family == AF_INET) {
|
||||
uv_ip4_name((const sockaddr_in*)&addr, endpoint, 64);
|
||||
}
|
||||
else if(addr.ss_family == AF_INET6) {
|
||||
uv_ip6_name((const sockaddr_in6*)&addr, endpoint, 64);
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
int EQ::Net::TCPConnection::RemotePort() const
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
int addr_len = sizeof(addr);
|
||||
uv_tcp_getpeername(m_socket, (sockaddr*)&addr, &addr_len);
|
||||
|
||||
char endpoint[64] = { 0 };
|
||||
if (addr.ss_family == AF_INET) {
|
||||
sockaddr_in *s = (sockaddr_in*)&addr;
|
||||
return ntohs(s->sin_port);
|
||||
}
|
||||
else if (addr.ss_family == AF_INET6) {
|
||||
sockaddr_in6 *s = (sockaddr_in6*)&addr;
|
||||
return ntohs(s->sin6_port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
39
common/net/tcp_connection.h
Normal file
39
common/net/tcp_connection.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <uv.h>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
class TCPConnection
|
||||
{
|
||||
public:
|
||||
TCPConnection(uv_tcp_t *socket);
|
||||
~TCPConnection();
|
||||
|
||||
static void Connect(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
|
||||
|
||||
void Start();
|
||||
void OnRead(std::function<void(TCPConnection*, const unsigned char *, size_t)> cb);
|
||||
void OnDisconnect(std::function<void(TCPConnection*)> cb);
|
||||
void Disconnect();
|
||||
void Read(const char *data, size_t count);
|
||||
void Write(const char *data, size_t count);
|
||||
|
||||
std::string LocalIP() const;
|
||||
int LocalPort() const;
|
||||
std::string RemoteIP() const;
|
||||
int RemotePort() const;
|
||||
private:
|
||||
TCPConnection();
|
||||
|
||||
uv_tcp_t *m_socket;
|
||||
std::function<void(TCPConnection*, const unsigned char *, size_t)> m_on_read_cb;
|
||||
std::function<void(TCPConnection*)> m_on_disconnect_cb;
|
||||
};
|
||||
}
|
||||
}
|
||||
85
common/net/tcp_server.cpp
Normal file
85
common/net/tcp_server.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
#include "tcp_server.h"
|
||||
#include "../event/event_loop.h"
|
||||
|
||||
void on_close_tcp_server_handle(uv_handle_t* handle) {
|
||||
delete handle;
|
||||
}
|
||||
|
||||
EQ::Net::TCPServer::TCPServer()
|
||||
{
|
||||
m_socket = nullptr;
|
||||
}
|
||||
|
||||
EQ::Net::TCPServer::~TCPServer() {
|
||||
Close();
|
||||
}
|
||||
|
||||
void EQ::Net::TCPServer::Listen(int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb)
|
||||
{
|
||||
if (ipv6) {
|
||||
Listen("::", port, ipv6, cb);
|
||||
}
|
||||
else {
|
||||
Listen("0.0.0.0", port, ipv6, cb);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::TCPServer::Listen(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb)
|
||||
{
|
||||
if (m_socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_on_new_connection = cb;
|
||||
|
||||
auto loop = EQ::EventLoop::Get().Handle();
|
||||
m_socket = new uv_tcp_t;
|
||||
memset(m_socket, 0, sizeof(uv_tcp_t));
|
||||
uv_tcp_init(loop, m_socket);
|
||||
|
||||
sockaddr_storage iaddr;
|
||||
if (ipv6) {
|
||||
uv_ip6_addr(addr.c_str(), port, (sockaddr_in6*)&iaddr);
|
||||
}
|
||||
else {
|
||||
uv_ip4_addr(addr.c_str(), port, (sockaddr_in*)&iaddr);
|
||||
}
|
||||
|
||||
uv_tcp_bind(m_socket, (sockaddr*)&iaddr, 0);
|
||||
m_socket->data = this;
|
||||
|
||||
uv_listen((uv_stream_t*)m_socket, 128, [](uv_stream_t* server, int status) {
|
||||
if (status < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto loop = EQ::EventLoop::Get().Handle();
|
||||
uv_tcp_t *client = new uv_tcp_t;
|
||||
memset(client, 0, sizeof(uv_tcp_t));
|
||||
uv_tcp_init(loop, client);
|
||||
|
||||
if (uv_accept(server, (uv_stream_t*)client) < 0) {
|
||||
delete client;
|
||||
return;
|
||||
}
|
||||
|
||||
EQ::Net::TCPServer *s = (EQ::Net::TCPServer*)server->data;
|
||||
s->AddClient(client);
|
||||
});
|
||||
}
|
||||
|
||||
void EQ::Net::TCPServer::Close()
|
||||
{
|
||||
if (m_socket) {
|
||||
uv_close((uv_handle_t*)m_socket, on_close_tcp_server_handle);
|
||||
m_socket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::TCPServer::AddClient(uv_tcp_t *c)
|
||||
{
|
||||
std::shared_ptr<TCPConnection> client(new TCPConnection(c));
|
||||
if (m_on_new_connection) {
|
||||
m_on_new_connection(client);
|
||||
}
|
||||
}
|
||||
25
common/net/tcp_server.h
Normal file
25
common/net/tcp_server.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "tcp_connection.h"
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
class TCPServer
|
||||
{
|
||||
public:
|
||||
TCPServer();
|
||||
~TCPServer();
|
||||
|
||||
void Listen(int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
|
||||
void Listen(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
|
||||
void Close();
|
||||
void AddClient(uv_tcp_t *c);
|
||||
|
||||
private:
|
||||
std::function<void(std::shared_ptr<TCPConnection>)> m_on_new_connection;
|
||||
uv_tcp_t *m_socket;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -152,6 +152,15 @@ void DumpPacket(const ServerPacket* pack, bool iShowInfo) {
|
||||
DumpPacketHex(pack->pBuffer, pack->size);
|
||||
}
|
||||
|
||||
void DumpPacket(uint16 opcode, const EQ::Net::Packet &p, bool iShowInfo) {
|
||||
if (iShowInfo) {
|
||||
std::cout << "Dumping ServerPacket: 0x" << std::hex << std::setfill('0') << std::setw(4) << opcode << std::dec;
|
||||
std::cout << " size:" << p.Length() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << p.ToString() << std::endl;
|
||||
}
|
||||
|
||||
void DumpPacketBin(const ServerPacket* pack) {
|
||||
DumpPacketBin(pack->pBuffer, pack->size);
|
||||
}
|
||||
|
||||
@ -18,7 +18,8 @@
|
||||
#ifndef PACKET_DUMP_H
|
||||
#define PACKET_DUMP_H
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "types.h"
|
||||
#include "net/packet.h"
|
||||
|
||||
class ServerPacket;
|
||||
|
||||
@ -28,6 +29,7 @@ std::string DumpPacketHexToString(const uchar* buf, uint32 size, uint32 cols = 1
|
||||
void DumpPacketBin(const void* data, uint32 len);
|
||||
void DumpPacket(const uchar* buf, uint32 size);
|
||||
void DumpPacket(const ServerPacket* pack, bool iShowInfo = false);
|
||||
void DumpPacket(uint16 opcode, const EQ::Net::Packet &p, bool iShowInfo = false);
|
||||
void DumpPacketBin(const ServerPacket* pack);
|
||||
void DumpPacketBin(uint32 data);
|
||||
void DumpPacketBin(uint16 data);
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#include "eq_stream.h"
|
||||
#include "eq_stream_intf.h"
|
||||
#include "packet_dump_file.h"
|
||||
|
||||
void FileDumpPacketAscii(const char* filename, const uchar* buf, uint32 size, uint32 cols, uint32 skip) {
|
||||
|
||||
@ -90,7 +90,7 @@ namespace RoF
|
||||
|
||||
//ok, now we have what we need to register.
|
||||
|
||||
EQStream::Signature signature;
|
||||
EQStreamInterface::Signature signature;
|
||||
std::string pname;
|
||||
|
||||
//register our world signature.
|
||||
|
||||
@ -90,7 +90,7 @@ namespace RoF2
|
||||
|
||||
//ok, now we have what we need to register.
|
||||
|
||||
EQStream::Signature signature;
|
||||
EQStreamInterface::Signature signature;
|
||||
std::string pname;
|
||||
|
||||
//register our world signature.
|
||||
|
||||
@ -86,7 +86,7 @@ namespace SoD
|
||||
|
||||
//ok, now we have what we need to register.
|
||||
|
||||
EQStream::Signature signature;
|
||||
EQStreamInterface::Signature signature;
|
||||
std::string pname;
|
||||
|
||||
//register our world signature.
|
||||
|
||||
@ -86,7 +86,7 @@ namespace SoF
|
||||
|
||||
//ok, now we have what we need to register.
|
||||
|
||||
EQStream::Signature signature;
|
||||
EQStreamInterface::Signature signature;
|
||||
std::string pname;
|
||||
|
||||
//register our world signature.
|
||||
|
||||
@ -17,5 +17,5 @@
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define E(x) static void Encode_##x(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req);
|
||||
#define E(x) static void Encode_##x(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req);
|
||||
#define D(x) static void Decode_##x(EQApplicationPacket *p);
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define ENCODE(x) void Strategy::Encode_##x(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req)
|
||||
#define ENCODE(x) void Strategy::Encode_##x(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req)
|
||||
#define DECODE(x) void Strategy::Decode_##x(EQApplicationPacket *__packet)
|
||||
|
||||
#define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1))
|
||||
|
||||
@ -85,7 +85,7 @@ namespace Titanium
|
||||
|
||||
//ok, now we have what we need to register.
|
||||
|
||||
EQStream::Signature signature;
|
||||
EQStreamInterface::Signature signature;
|
||||
std::string pname;
|
||||
|
||||
//register our world signature.
|
||||
|
||||
@ -86,7 +86,7 @@ namespace UF
|
||||
|
||||
//ok, now we have what we need to register.
|
||||
|
||||
EQStream::Signature signature;
|
||||
EQStreamInterface::Signature signature;
|
||||
std::string pname;
|
||||
|
||||
//register our world signature.
|
||||
|
||||
@ -13,7 +13,8 @@ enum EQEmuExePlatform
|
||||
ExePlatformLaunch,
|
||||
ExePlatformSharedMemory,
|
||||
ExePlatformClientImport,
|
||||
ExePlatformClientExport
|
||||
ExePlatformClientExport,
|
||||
ExePlatformHC
|
||||
};
|
||||
|
||||
void RegisterExecutablePlatform(EQEmuExePlatform p);
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
enum { //values for pTimerType
|
||||
enum : int { //values for pTimerType
|
||||
pTimerNegativeItemReuse = -1, // these grow down basically, we will have item ID * -1 for the timer ID
|
||||
pTimerStartAdventureTimer = 1,
|
||||
pTimerSurnameChange = 2,
|
||||
pTimerFeignDeath = 3,
|
||||
|
||||
@ -56,6 +56,7 @@ RULE_REAL(Character, GroupExpMultiplier, 0.5)
|
||||
RULE_REAL(Character, RaidExpMultiplier, 0.2)
|
||||
RULE_BOOL(Character, UseXPConScaling, true)
|
||||
RULE_INT(Character, ShowExpValues, 0) //0 - normal, 1 - Show raw experience values, 2 - Show raw experience values AND percent.
|
||||
RULE_INT(Character, GreenModifier, 20)
|
||||
RULE_INT(Character, LightBlueModifier, 40)
|
||||
RULE_INT(Character, BlueModifier, 90)
|
||||
RULE_INT(Character, WhiteModifier, 100)
|
||||
@ -149,6 +150,7 @@ RULE_INT(Character, AvoidanceCap, 750) // 750 Is a pretty good value, seen peopl
|
||||
RULE_BOOL(Character, AllowMQTarget, false) // Disables putting players in the 'hackers' list for targeting beyond the clip plane or attempting to target something untargetable
|
||||
RULE_BOOL(Character, UseOldBindWound, false) // Uses the original bind wound behavior
|
||||
RULE_BOOL(Character, GrantHoTTOnCreate, false) // Grant Health of Target's Target leadership AA on character creation
|
||||
RULE_BOOL(Character, UseOldConSystem, false) // Grant Health of Target's Target leadership AA on character creation
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
#include "../common/types.h"
|
||||
#include "../common/packet_functions.h"
|
||||
#include "../common/eq_packet_structs.h"
|
||||
#include "../net/packet.h"
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
|
||||
#define SERVER_TIMEOUT 45000 // how often keepalive gets sent
|
||||
#define INTERSERVER_TIMER 10000
|
||||
@ -59,7 +62,7 @@
|
||||
#define ServerOP_ItemStatus 0x002C
|
||||
#define ServerOP_OOCMute 0x002D
|
||||
#define ServerOP_Revoke 0x002E
|
||||
//#define 0x002F
|
||||
#define ServerOP_WebInterfaceCall 0x002F
|
||||
#define ServerOP_GroupIDReq 0x0030
|
||||
#define ServerOP_GroupIDReply 0x0031
|
||||
#define ServerOP_GroupLeave 0x0032 // for disbanding out of zone folks
|
||||
@ -85,6 +88,9 @@
|
||||
#define ServerOP_DepopPlayerCorpse 0x0065
|
||||
#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues
|
||||
#define ServerOP_ChangeSharedMem 0x0067
|
||||
#define ServerOP_WebInterfaceEvent 0x0068
|
||||
#define ServerOP_WebInterfaceSubscribe 0x0069
|
||||
#define ServerOP_WebInterfaceUnsubscribe 0x0070
|
||||
|
||||
#define ServerOP_RaidAdd 0x0100 //in use
|
||||
#define ServerOP_RaidRemove 0x0101 //in use
|
||||
@ -221,6 +227,22 @@ public:
|
||||
_wpos = 0;
|
||||
_rpos = 0;
|
||||
}
|
||||
|
||||
ServerPacket(uint16 in_opcode, const EQ::Net::Packet &p) {
|
||||
this->compressed = false;
|
||||
size = (uint32)p.Length();
|
||||
opcode = in_opcode;
|
||||
if (size == 0) {
|
||||
pBuffer = 0;
|
||||
}
|
||||
else {
|
||||
pBuffer = new uchar[size];
|
||||
memcpy(pBuffer, p.Data(), size);
|
||||
}
|
||||
_wpos = 0;
|
||||
_rpos = 0;
|
||||
}
|
||||
|
||||
ServerPacket* Copy() {
|
||||
if (this == 0) {
|
||||
return 0;
|
||||
@ -232,43 +254,6 @@ public:
|
||||
ret->InflatedSize = this->InflatedSize;
|
||||
return ret;
|
||||
}
|
||||
bool Deflate() {
|
||||
if (compressed)
|
||||
return false;
|
||||
if ((!this->pBuffer) || (!this->size))
|
||||
return false;
|
||||
uchar* tmp = new uchar[this->size + 128];
|
||||
uint32 tmpsize = DeflatePacket(this->pBuffer, this->size, tmp, this->size + 128);
|
||||
if (!tmpsize) {
|
||||
safe_delete_array(tmp);
|
||||
return false;
|
||||
}
|
||||
this->compressed = true;
|
||||
this->InflatedSize = this->size;
|
||||
this->size = tmpsize;
|
||||
uchar* tmpdel = this->pBuffer;
|
||||
this->pBuffer = tmp;
|
||||
safe_delete_array(tmpdel);
|
||||
return true;
|
||||
}
|
||||
bool Inflate() {
|
||||
if (!compressed)
|
||||
return false;
|
||||
if ((!this->pBuffer) || (!this->size))
|
||||
return false;
|
||||
uchar* tmp = new uchar[InflatedSize];
|
||||
uint32 tmpsize = InflatePacket(this->pBuffer, this->size, tmp, InflatedSize);
|
||||
if (!tmpsize) {
|
||||
safe_delete_array(tmp);
|
||||
return false;
|
||||
}
|
||||
compressed = false;
|
||||
this->size = tmpsize;
|
||||
uchar* tmpdel = this->pBuffer;
|
||||
this->pBuffer = tmp;
|
||||
safe_delete_array(tmpdel);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteUInt8(uint8 value) { *(uint8 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8); }
|
||||
void WriteUInt32(uint32 value) { *(uint32 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint32); }
|
||||
@ -524,14 +509,21 @@ struct ServerLSPlayerZoneChange_Struct {
|
||||
uint32 from; // 0 = world
|
||||
uint32 to; // 0 = world
|
||||
};
|
||||
|
||||
struct ClientAuth_Struct {
|
||||
uint32 lsaccount_id; // ID# in login server's db
|
||||
char name[30]; // username in login server's db
|
||||
char key[30]; // the Key the client will present
|
||||
uint8 lsadmin; // login server admin level
|
||||
int16 worldadmin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
||||
uint32 ip;
|
||||
uint8 local; // 1 if the client is from the local network
|
||||
uint32 lsaccount_id; // ID# in login server's db
|
||||
char name[30]; // username in login server's db
|
||||
char key[30]; // the Key the client will present
|
||||
uint8 lsadmin; // login server admin level
|
||||
int16 worldadmin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
||||
uint32 ip;
|
||||
uint8 local; // 1 if the client is from the local network
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(lsaccount_id, name, key, lsadmin, worldadmin, ip, local);
|
||||
}
|
||||
};
|
||||
|
||||
struct ServerSystemwideMessage {
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "string_util.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <windows.h>
|
||||
@ -71,246 +72,6 @@ const std::string StringFormat(const char* format, ...)
|
||||
return output;
|
||||
}
|
||||
|
||||
// normal strncpy doesnt put a null term on copied strings, this one does
|
||||
// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp
|
||||
char* strn0cpy(char* dest, const char* source, uint32 size) {
|
||||
if (!dest)
|
||||
return 0;
|
||||
if (size == 0 || source == 0) {
|
||||
dest[0] = 0;
|
||||
return dest;
|
||||
}
|
||||
strncpy(dest, source, size);
|
||||
dest[size - 1] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
// String N w/null Copy Truncated?
|
||||
// return value =true if entire string(source) fit, false if it was truncated
|
||||
bool strn0cpyt(char* dest, const char* source, uint32 size) {
|
||||
if (!dest)
|
||||
return 0;
|
||||
if (size == 0 || source == 0) {
|
||||
dest[0] = 0;
|
||||
return false;
|
||||
}
|
||||
strncpy(dest, source, size);
|
||||
dest[size - 1] = 0;
|
||||
return (bool) (source[strlen(dest)] == 0);
|
||||
}
|
||||
|
||||
const char *MakeLowerString(const char *source) {
|
||||
static char str[128];
|
||||
if (!source)
|
||||
return nullptr;
|
||||
MakeLowerString(source, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void MakeLowerString(const char *source, char *target) {
|
||||
if (!source || !target) {
|
||||
*target=0;
|
||||
return;
|
||||
}
|
||||
while (*source)
|
||||
{
|
||||
*target = tolower(*source);
|
||||
target++;source++;
|
||||
}
|
||||
*target = 0;
|
||||
}
|
||||
|
||||
int MakeAnyLenString(char** ret, const char* format, ...) {
|
||||
int buf_len = 128;
|
||||
int chars = -1;
|
||||
va_list argptr, tmpargptr;
|
||||
va_start(argptr, format);
|
||||
while (chars == -1 || chars >= buf_len) {
|
||||
safe_delete_array(*ret);
|
||||
if (chars == -1)
|
||||
buf_len *= 2;
|
||||
else
|
||||
buf_len = chars + 1;
|
||||
*ret = new char[buf_len];
|
||||
va_copy(tmpargptr, argptr);
|
||||
chars = vsnprintf(*ret, buf_len, format, tmpargptr);
|
||||
}
|
||||
va_end(argptr);
|
||||
return chars;
|
||||
}
|
||||
|
||||
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) {
|
||||
if (*bufsize == 0)
|
||||
*bufsize = 256;
|
||||
if (*ret == 0)
|
||||
*strlen = 0;
|
||||
int chars = -1;
|
||||
char* oldret = 0;
|
||||
va_list argptr, tmpargptr;
|
||||
va_start(argptr, format);
|
||||
while (chars == -1 || chars >= (int32)(*bufsize-*strlen)) {
|
||||
if (chars == -1)
|
||||
*bufsize += 256;
|
||||
else
|
||||
*bufsize += chars + 25;
|
||||
oldret = *ret;
|
||||
*ret = new char[*bufsize];
|
||||
if (oldret) {
|
||||
if (*strlen)
|
||||
memcpy(*ret, oldret, *strlen);
|
||||
safe_delete_array(oldret);
|
||||
}
|
||||
va_copy(tmpargptr, argptr);
|
||||
chars = vsnprintf(&(*ret)[*strlen], (*bufsize-*strlen), format, tmpargptr);
|
||||
}
|
||||
va_end(argptr);
|
||||
*strlen += chars;
|
||||
return *strlen;
|
||||
}
|
||||
|
||||
uint32 hextoi(const char* num) {
|
||||
if (num == nullptr)
|
||||
return 0;
|
||||
|
||||
int len = strlen(num);
|
||||
if (len < 3)
|
||||
return 0;
|
||||
|
||||
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
|
||||
return 0;
|
||||
|
||||
uint32 ret = 0;
|
||||
int mul = 1;
|
||||
for (int i=len-1; i>=2; i--) {
|
||||
if (num[i] >= 'A' && num[i] <= 'F')
|
||||
ret += ((num[i] - 'A') + 10) * mul;
|
||||
else if (num[i] >= 'a' && num[i] <= 'f')
|
||||
ret += ((num[i] - 'a') + 10) * mul;
|
||||
else if (num[i] >= '0' && num[i] <= '9')
|
||||
ret += (num[i] - '0') * mul;
|
||||
else
|
||||
return 0;
|
||||
mul *= 16;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64 hextoi64(const char* num) {
|
||||
if (num == nullptr)
|
||||
return 0;
|
||||
|
||||
int len = strlen(num);
|
||||
if (len < 3)
|
||||
return 0;
|
||||
|
||||
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
|
||||
return 0;
|
||||
|
||||
uint64 ret = 0;
|
||||
int mul = 1;
|
||||
for (int i=len-1; i>=2; i--) {
|
||||
if (num[i] >= 'A' && num[i] <= 'F')
|
||||
ret += ((num[i] - 'A') + 10) * mul;
|
||||
else if (num[i] >= 'a' && num[i] <= 'f')
|
||||
ret += ((num[i] - 'a') + 10) * mul;
|
||||
else if (num[i] >= '0' && num[i] <= '9')
|
||||
ret += (num[i] - '0') * mul;
|
||||
else
|
||||
return 0;
|
||||
mul *= 16;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool atobool(const char* iBool) {
|
||||
|
||||
if (iBool == nullptr)
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "true"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "false"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "yes"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "no"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "on"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "off"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "enable"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "disable"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "enabled"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "disabled"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "y"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "n"))
|
||||
return false;
|
||||
if (atoi(iBool))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// removes the crap and turns the underscores into spaces.
|
||||
char *CleanMobName(const char *in, char *out)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
for(i = j = 0; i < strlen(in); i++)
|
||||
{
|
||||
// convert _ to space.. any other conversions like this? I *think* this
|
||||
// is the only non alpha char that's not stripped but converted.
|
||||
if(in[i] == '_')
|
||||
{
|
||||
out[j++] = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
if(isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped
|
||||
out[j++] = in[i];
|
||||
}
|
||||
}
|
||||
out[j] = 0; // terimnate the string before returning it
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void RemoveApostrophes(std::string &s)
|
||||
{
|
||||
for(unsigned int i = 0; i < s.length(); ++i)
|
||||
if(s[i] == '\'')
|
||||
s[i] = '_';
|
||||
}
|
||||
|
||||
char *RemoveApostrophes(const char *s)
|
||||
{
|
||||
auto NewString = new char[strlen(s) + 1];
|
||||
|
||||
strcpy(NewString, s);
|
||||
|
||||
for(unsigned int i = 0 ; i < strlen(NewString); ++i)
|
||||
if(NewString[i] == '\'')
|
||||
NewString[i] = '_';
|
||||
|
||||
return NewString;
|
||||
}
|
||||
|
||||
const char *ConvertArray(int input, char *returnchar)
|
||||
{
|
||||
sprintf(returnchar, "%i" ,input);
|
||||
return returnchar;
|
||||
}
|
||||
|
||||
const char *ConvertArrayF(float input, char *returnchar)
|
||||
{
|
||||
sprintf(returnchar, "%0.2f", input);
|
||||
return returnchar;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitString(const std::string &str, char delim) {
|
||||
std::vector<std::string> ret;
|
||||
std::stringstream ss(str);
|
||||
@ -396,16 +157,35 @@ std::string EscapeString(const char *src, size_t sz) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool isAlphaNumeric(const char *text)
|
||||
{
|
||||
for (unsigned int charIndex=0; charIndex<strlen(text); charIndex++) {
|
||||
if ((text[charIndex] < 'a' || text[charIndex] > 'z') &&
|
||||
(text[charIndex] < 'A' || text[charIndex] > 'Z') &&
|
||||
(text[charIndex] < '0' || text[charIndex] > '9'))
|
||||
return false;
|
||||
bool StringIsNumber(const std::string &s) {
|
||||
try {
|
||||
auto r = stod(s);
|
||||
return true;
|
||||
}
|
||||
catch (std::exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ToLowerString(std::string &s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
}
|
||||
|
||||
void ToUpperString(std::string &s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
|
||||
}
|
||||
|
||||
std::string JoinString(const std::vector<std::string>& ar, const std::string &delim) {
|
||||
std::string ret;
|
||||
for (size_t i = 0; i < ar.size(); ++i) {
|
||||
if (i != 0) {
|
||||
ret += delim;
|
||||
}
|
||||
|
||||
ret += ar[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string) {
|
||||
@ -415,3 +195,257 @@ void find_replace(std::string& string_subject, const std::string& search_string,
|
||||
index = string_subject.find_first_of(search_string);
|
||||
}
|
||||
}
|
||||
|
||||
//Const char based
|
||||
|
||||
// normal strncpy doesnt put a null term on copied strings, this one does
|
||||
// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp
|
||||
char* strn0cpy(char* dest, const char* source, uint32 size) {
|
||||
if (!dest)
|
||||
return 0;
|
||||
if (size == 0 || source == 0) {
|
||||
dest[0] = 0;
|
||||
return dest;
|
||||
}
|
||||
strncpy(dest, source, size);
|
||||
dest[size - 1] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
// String N w/null Copy Truncated?
|
||||
// return value =true if entire string(source) fit, false if it was truncated
|
||||
bool strn0cpyt(char* dest, const char* source, uint32 size) {
|
||||
if (!dest)
|
||||
return 0;
|
||||
if (size == 0 || source == 0) {
|
||||
dest[0] = 0;
|
||||
return false;
|
||||
}
|
||||
strncpy(dest, source, size);
|
||||
dest[size - 1] = 0;
|
||||
return (bool)(source[strlen(dest)] == 0);
|
||||
}
|
||||
|
||||
const char *MakeLowerString(const char *source) {
|
||||
static char str[128];
|
||||
if (!source)
|
||||
return nullptr;
|
||||
MakeLowerString(source, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void MakeLowerString(const char *source, char *target) {
|
||||
if (!source || !target) {
|
||||
*target = 0;
|
||||
return;
|
||||
}
|
||||
while (*source)
|
||||
{
|
||||
*target = tolower(*source);
|
||||
target++; source++;
|
||||
}
|
||||
*target = 0;
|
||||
}
|
||||
|
||||
int MakeAnyLenString(char** ret, const char* format, ...) {
|
||||
int buf_len = 128;
|
||||
int chars = -1;
|
||||
va_list argptr, tmpargptr;
|
||||
va_start(argptr, format);
|
||||
while (chars == -1 || chars >= buf_len) {
|
||||
safe_delete_array(*ret);
|
||||
if (chars == -1)
|
||||
buf_len *= 2;
|
||||
else
|
||||
buf_len = chars + 1;
|
||||
*ret = new char[buf_len];
|
||||
va_copy(tmpargptr, argptr);
|
||||
chars = vsnprintf(*ret, buf_len, format, tmpargptr);
|
||||
}
|
||||
va_end(argptr);
|
||||
return chars;
|
||||
}
|
||||
|
||||
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) {
|
||||
if (*bufsize == 0)
|
||||
*bufsize = 256;
|
||||
if (*ret == 0)
|
||||
*strlen = 0;
|
||||
int chars = -1;
|
||||
char* oldret = 0;
|
||||
va_list argptr, tmpargptr;
|
||||
va_start(argptr, format);
|
||||
while (chars == -1 || chars >= (int32)(*bufsize - *strlen)) {
|
||||
if (chars == -1)
|
||||
*bufsize += 256;
|
||||
else
|
||||
*bufsize += chars + 25;
|
||||
oldret = *ret;
|
||||
*ret = new char[*bufsize];
|
||||
if (oldret) {
|
||||
if (*strlen)
|
||||
memcpy(*ret, oldret, *strlen);
|
||||
safe_delete_array(oldret);
|
||||
}
|
||||
va_copy(tmpargptr, argptr);
|
||||
chars = vsnprintf(&(*ret)[*strlen], (*bufsize - *strlen), format, tmpargptr);
|
||||
}
|
||||
va_end(argptr);
|
||||
*strlen += chars;
|
||||
return *strlen;
|
||||
}
|
||||
|
||||
uint32 hextoi(const char* num) {
|
||||
if (num == nullptr)
|
||||
return 0;
|
||||
|
||||
int len = strlen(num);
|
||||
if (len < 3)
|
||||
return 0;
|
||||
|
||||
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
|
||||
return 0;
|
||||
|
||||
uint32 ret = 0;
|
||||
int mul = 1;
|
||||
for (int i = len - 1; i >= 2; i--) {
|
||||
if (num[i] >= 'A' && num[i] <= 'F')
|
||||
ret += ((num[i] - 'A') + 10) * mul;
|
||||
else if (num[i] >= 'a' && num[i] <= 'f')
|
||||
ret += ((num[i] - 'a') + 10) * mul;
|
||||
else if (num[i] >= '0' && num[i] <= '9')
|
||||
ret += (num[i] - '0') * mul;
|
||||
else
|
||||
return 0;
|
||||
mul *= 16;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64 hextoi64(const char* num) {
|
||||
if (num == nullptr)
|
||||
return 0;
|
||||
|
||||
int len = strlen(num);
|
||||
if (len < 3)
|
||||
return 0;
|
||||
|
||||
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
|
||||
return 0;
|
||||
|
||||
uint64 ret = 0;
|
||||
int mul = 1;
|
||||
for (int i = len - 1; i >= 2; i--) {
|
||||
if (num[i] >= 'A' && num[i] <= 'F')
|
||||
ret += ((num[i] - 'A') + 10) * mul;
|
||||
else if (num[i] >= 'a' && num[i] <= 'f')
|
||||
ret += ((num[i] - 'a') + 10) * mul;
|
||||
else if (num[i] >= '0' && num[i] <= '9')
|
||||
ret += (num[i] - '0') * mul;
|
||||
else
|
||||
return 0;
|
||||
mul *= 16;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool atobool(const char* iBool) {
|
||||
|
||||
if (iBool == nullptr)
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "true"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "false"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "yes"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "no"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "on"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "off"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "enable"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "disable"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "enabled"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "disabled"))
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "y"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "n"))
|
||||
return false;
|
||||
if (atoi(iBool))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// removes the crap and turns the underscores into spaces.
|
||||
char *CleanMobName(const char *in, char *out)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
for (i = j = 0; i < strlen(in); i++)
|
||||
{
|
||||
// convert _ to space.. any other conversions like this? I *think* this
|
||||
// is the only non alpha char that's not stripped but converted.
|
||||
if (in[i] == '_')
|
||||
{
|
||||
out[j++] = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped
|
||||
out[j++] = in[i];
|
||||
}
|
||||
}
|
||||
out[j] = 0; // terimnate the string before returning it
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void RemoveApostrophes(std::string &s)
|
||||
{
|
||||
for (unsigned int i = 0; i < s.length(); ++i)
|
||||
if (s[i] == '\'')
|
||||
s[i] = '_';
|
||||
}
|
||||
|
||||
char *RemoveApostrophes(const char *s)
|
||||
{
|
||||
auto NewString = new char[strlen(s) + 1];
|
||||
|
||||
strcpy(NewString, s);
|
||||
|
||||
for (unsigned int i = 0; i < strlen(NewString); ++i)
|
||||
if (NewString[i] == '\'')
|
||||
NewString[i] = '_';
|
||||
|
||||
return NewString;
|
||||
}
|
||||
|
||||
const char *ConvertArray(int input, char *returnchar)
|
||||
{
|
||||
sprintf(returnchar, "%i", input);
|
||||
return returnchar;
|
||||
}
|
||||
|
||||
const char *ConvertArrayF(float input, char *returnchar)
|
||||
{
|
||||
sprintf(returnchar, "%0.2f", input);
|
||||
return returnchar;
|
||||
}
|
||||
|
||||
bool isAlphaNumeric(const char *text)
|
||||
{
|
||||
for (unsigned int charIndex = 0; charIndex<strlen(text); charIndex++) {
|
||||
if ((text[charIndex] < 'a' || text[charIndex] > 'z') &&
|
||||
(text[charIndex] < 'A' || text[charIndex] > 'Z') &&
|
||||
(text[charIndex] < '0' || text[charIndex] > '9'))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -23,34 +23,34 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
//std::string based
|
||||
const std::string StringFormat(const char* format, ...);
|
||||
const std::string vStringFormat(const char* format, va_list args);
|
||||
std::vector<std::string> SplitString(const std::string &s, char delim);
|
||||
std::string EscapeString(const char *src, size_t sz);
|
||||
std::string EscapeString(const std::string &s);
|
||||
bool StringIsNumber(const std::string &s);
|
||||
void ToLowerString(std::string &s);
|
||||
void ToUpperString(std::string &s);
|
||||
std::string JoinString(const std::vector<std::string>& ar, const std::string &delim);
|
||||
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string);
|
||||
|
||||
//const char based
|
||||
|
||||
bool atobool(const char* iBool);
|
||||
bool isAlphaNumeric(const char *text);
|
||||
bool strn0cpyt(char* dest, const char* source, uint32 size);
|
||||
|
||||
char *CleanMobName(const char *in, char *out);
|
||||
char *RemoveApostrophes(const char *s);
|
||||
char* strn0cpy(char* dest, const char* source, uint32 size);
|
||||
|
||||
const char *ConvertArray(int input, char *returnchar);
|
||||
const char *ConvertArrayF(float input, char *returnchar);
|
||||
const char *MakeLowerString(const char *source);
|
||||
const std::string StringFormat(const char* format, ...);
|
||||
const std::string vStringFormat(const char* format, va_list args);
|
||||
|
||||
int MakeAnyLenString(char** ret, const char* format, ...);
|
||||
|
||||
std::string EscapeString(const char *src, size_t sz);
|
||||
std::string EscapeString(const std::string &s);
|
||||
|
||||
std::vector<std::string> SplitString(const std::string &s, char delim);
|
||||
|
||||
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...);
|
||||
uint32 hextoi(const char* num);
|
||||
|
||||
uint64 hextoi64(const char* num);
|
||||
|
||||
void MakeLowerString(const char *source, char *target);
|
||||
void RemoveApostrophes(std::string &s);
|
||||
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string);
|
||||
|
||||
#endif
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
#include "eqemu_logsys.h"
|
||||
#include "struct_strategy.h"
|
||||
|
||||
#include "eq_stream.h"
|
||||
#include "eq_stream_intf.h"
|
||||
#include "opcodemgr.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
@ -18,7 +19,7 @@ StructStrategy::StructStrategy() {
|
||||
}
|
||||
}
|
||||
|
||||
void StructStrategy::Encode(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req) const {
|
||||
void StructStrategy::Encode(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req) const {
|
||||
if((*p)->GetOpcodeBypass() != 0) {
|
||||
PassEncoder(p, dest, ack_req);
|
||||
return;
|
||||
@ -36,7 +37,7 @@ void StructStrategy::Decode(EQApplicationPacket *p) const {
|
||||
}
|
||||
|
||||
|
||||
void StructStrategy::ErrorEncoder(EQApplicationPacket **in_p, std::shared_ptr<EQStream> dest, bool ack_req) {
|
||||
void StructStrategy::ErrorEncoder(EQApplicationPacket **in_p, std::shared_ptr<EQStreamInterface> dest, bool ack_req) {
|
||||
EQApplicationPacket *p = *in_p;
|
||||
*in_p = nullptr;
|
||||
|
||||
@ -50,7 +51,7 @@ void StructStrategy::ErrorDecoder(EQApplicationPacket *p) {
|
||||
p->SetOpcode(OP_Unknown);
|
||||
}
|
||||
|
||||
void StructStrategy::PassEncoder(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req) {
|
||||
void StructStrategy::PassEncoder(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req) {
|
||||
dest->FastQueuePacket(p, ack_req);
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#define STRUCTSTRATEGY_H_
|
||||
|
||||
class EQApplicationPacket;
|
||||
class EQStream;
|
||||
class EQStreamInterface;
|
||||
#include "emu_opcodes.h"
|
||||
#include "emu_versions.h"
|
||||
|
||||
@ -12,7 +12,7 @@ class EQStream;
|
||||
class StructStrategy {
|
||||
public:
|
||||
//the encoder takes ownership of the supplied packet, and may enqueue multiple resulting packets into the stream
|
||||
typedef void(*Encoder)(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req);
|
||||
typedef void(*Encoder)(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req);
|
||||
//the decoder may only edit the supplied packet, producing a single packet for eqemu to consume.
|
||||
typedef void (*Decoder)(EQApplicationPacket *p);
|
||||
|
||||
@ -20,7 +20,7 @@ public:
|
||||
virtual ~StructStrategy() {}
|
||||
|
||||
//this method takes an eqemu struct, and enqueues the produced structs into the stream.
|
||||
void Encode(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req) const;
|
||||
void Encode(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req) const;
|
||||
//this method takes an EQ wire struct, and converts it into an eqemu struct
|
||||
void Decode(EQApplicationPacket *p) const;
|
||||
|
||||
@ -30,10 +30,10 @@ public:
|
||||
protected:
|
||||
//some common coders:
|
||||
//Print an error saying unknown struct/opcode and drop it
|
||||
static void ErrorEncoder(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req);
|
||||
static void ErrorEncoder(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req);
|
||||
static void ErrorDecoder(EQApplicationPacket *p);
|
||||
//pass the packet through without modification (emu == EQ) (default)
|
||||
static void PassEncoder(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req);
|
||||
static void PassEncoder(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req);
|
||||
static void PassDecoder(EQApplicationPacket *p);
|
||||
|
||||
Encoder encoders[_maxEmuOpcode];
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
#ifndef TCPBASICSERVER_H_
|
||||
#define TCPBASICSERVER_H_
|
||||
|
||||
#include "tcp_server.h"
|
||||
#include "tcp_connection.h"
|
||||
|
||||
class TCPBasicServer : public TCPServer<TCPConnection> {
|
||||
public:
|
||||
inline TCPBasicServer(uint16 iPort = 0) : TCPServer<TCPConnection>(iPort) { }
|
||||
inline virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) {
|
||||
TCPConnection *conn = new TCPConnection(ID, in_socket, irIP, irPort);
|
||||
AddConnection(conn);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /*TCPBASICSERVER_H_*/
|
||||
|
||||
@ -1,176 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef TCP_CONNECTION_H
|
||||
#define TCP_CONNECTION_H
|
||||
/*
|
||||
Parent classes for interserver TCP Communication.
|
||||
-Quagmire
|
||||
*/
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#if (!defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER < 1900))
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#define strncasecmp _strnicmp
|
||||
#define strcasecmp _stricmp
|
||||
|
||||
#include <process.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
#include "unix.h"
|
||||
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "mutex.h"
|
||||
#include "queue.h"
|
||||
#include "misc_functions.h"
|
||||
|
||||
|
||||
#define TCPConnection_ErrorBufferSize 1024
|
||||
#define MaxTCPReceiveBuffferSize 524288
|
||||
|
||||
|
||||
#ifndef DEF_eConnectionType
|
||||
#define DEF_eConnectionType
|
||||
enum eConnectionType {Incoming, Outgoing};
|
||||
#endif
|
||||
|
||||
|
||||
class TCPConnection {
|
||||
protected:
|
||||
typedef enum {
|
||||
TCPS_Ready = 0,
|
||||
TCPS_Connecting = 1,
|
||||
TCPS_Connected = 100,
|
||||
TCPS_Disconnecting = 200, //I do not know the difference between Disconnecting and Closing
|
||||
TCPS_Disconnected = 201,
|
||||
TCPS_Closing = 250,
|
||||
TCPS_Error = 255
|
||||
} State_t;
|
||||
|
||||
public:
|
||||
//socket created by a server (incoming)
|
||||
TCPConnection(uint32 ID, SOCKET iSock, uint32 irIP, uint16 irPort);
|
||||
//socket created to connect to a server (outgoing)
|
||||
TCPConnection(); // for outgoing connections
|
||||
|
||||
virtual ~TCPConnection();
|
||||
|
||||
// Functions for outgoing connections
|
||||
bool Connect(const char* irAddress, uint16 irPort, char* errbuf = 0);
|
||||
virtual bool ConnectIP(uint32 irIP, uint16 irPort, char* errbuf = 0);
|
||||
void AsyncConnect(const char* irAddress, uint16 irPort);
|
||||
void AsyncConnect(uint32 irIP, uint16 irPort);
|
||||
virtual void Disconnect();
|
||||
|
||||
bool Send(const uchar* data, int32 size);
|
||||
|
||||
char* PopLine(); //returns ownership of allocated byte array
|
||||
inline uint32 GetrIP() const { return rIP; }
|
||||
inline uint16 GetrPort() const { return rPort; }
|
||||
virtual State_t GetState() const;
|
||||
inline bool Connected() const { return (GetState() == TCPS_Connected); }
|
||||
bool ConnectReady() const;
|
||||
void Free(); // Inform TCPServer that this connection object is no longer referanced
|
||||
|
||||
inline uint32 GetID() const { return id; }
|
||||
|
||||
bool GetEcho();
|
||||
void SetEcho(bool iValue);
|
||||
bool GetSockName(char *host, uint16 *port);
|
||||
|
||||
//should only be used by TCPServer<T>:
|
||||
bool CheckNetActive();
|
||||
inline bool IsFree() const { return pFree; }
|
||||
virtual bool Process();
|
||||
|
||||
protected:
|
||||
friend class BaseTCPServer;
|
||||
void SetState(State_t iState);
|
||||
|
||||
static ThreadReturnType TCPConnectionLoop(void* tmp);
|
||||
// SOCKET sock;
|
||||
bool RunLoop();
|
||||
Mutex MLoopRunning;
|
||||
Mutex MAsyncConnect;
|
||||
bool GetAsyncConnect();
|
||||
bool SetAsyncConnect(bool iValue);
|
||||
char* charAsyncConnect;
|
||||
bool pAsyncConnect; //this flag should really be turned into a state instead.
|
||||
|
||||
virtual bool ProcessReceivedData(char* errbuf = 0);
|
||||
virtual bool SendData(bool &sent_something, char* errbuf = 0);
|
||||
virtual bool RecvData(char* errbuf = 0);
|
||||
|
||||
virtual void ClearBuffers();
|
||||
|
||||
|
||||
bool m_previousLineEnd;
|
||||
|
||||
eConnectionType ConnectionType;
|
||||
Mutex MRunLoop;
|
||||
bool pRunLoop;
|
||||
|
||||
SOCKET connection_socket;
|
||||
uint32 id;
|
||||
uint32 rIP;
|
||||
uint16 rPort; // host byte order
|
||||
bool pFree;
|
||||
|
||||
mutable Mutex MState;
|
||||
State_t pState;
|
||||
|
||||
//text based line out queue.
|
||||
Mutex MLineOutQueue;
|
||||
virtual bool LineOutQueuePush(char* line); //this is really kinda a hack for the transition to packet mode. Returns true to stop processing the output.
|
||||
MyQueue<char> LineOutQueue;
|
||||
|
||||
uchar* recvbuf;
|
||||
int32 recvbuf_size;
|
||||
int32 recvbuf_used;
|
||||
|
||||
int32 recvbuf_echo;
|
||||
volatile bool pEcho;
|
||||
|
||||
Mutex MSendQueue;
|
||||
uchar* sendbuf;
|
||||
int32 sendbuf_size;
|
||||
int32 sendbuf_used;
|
||||
bool ServerSendQueuePop(uchar** data, int32* size);
|
||||
bool ServerSendQueuePopForce(uchar** data, int32* size); //does a lock() instead of a trylock()
|
||||
void ServerSendQueuePushEnd(const uchar* data, int32 size);
|
||||
void ServerSendQueuePushEnd(uchar** data, int32 size);
|
||||
void ServerSendQueuePushFront(uchar* data, int32 size);
|
||||
|
||||
private:
|
||||
void FinishDisconnect();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,135 +0,0 @@
|
||||
#ifndef TCPSERVER_H_
|
||||
#define TCPSERVER_H_
|
||||
|
||||
#include "types.h"
|
||||
#include "mutex.h"
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#define TCPServer_ErrorBufferSize 1024
|
||||
|
||||
//this is the non-connection type specific server.
|
||||
class BaseTCPServer {
|
||||
public:
|
||||
BaseTCPServer(uint16 iPort = 0);
|
||||
virtual ~BaseTCPServer();
|
||||
|
||||
bool Open(uint16 iPort = 0, char* errbuf = 0); // opens the port
|
||||
void Close(); // closes the port
|
||||
bool IsOpen();
|
||||
inline uint16 GetPort() { return pPort; }
|
||||
inline uint32 GetNextID() { return NextID++; }
|
||||
|
||||
protected:
|
||||
static ThreadReturnType TCPServerLoop(void* tmp);
|
||||
|
||||
//factory method:
|
||||
virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) = 0;
|
||||
|
||||
|
||||
virtual void Process();
|
||||
bool RunLoop();
|
||||
Mutex MLoopRunning;
|
||||
|
||||
void StopLoopAndWait();
|
||||
|
||||
void ListenNewConnections();
|
||||
|
||||
uint32 NextID;
|
||||
|
||||
Mutex MRunLoop;
|
||||
bool pRunLoop;
|
||||
|
||||
Mutex MSock;
|
||||
SOCKET sock;
|
||||
uint16 pPort;
|
||||
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class TCPServer : public BaseTCPServer {
|
||||
protected:
|
||||
typedef typename std::vector<T *> vstore;
|
||||
typedef typename std::vector<T *>::iterator vitr;
|
||||
public:
|
||||
TCPServer(uint16 iPort = 0)
|
||||
: BaseTCPServer(iPort) {
|
||||
}
|
||||
|
||||
virtual ~TCPServer() {
|
||||
StopLoopAndWait();
|
||||
|
||||
//im not sure what the right thing to do here is...
|
||||
//we are freeing a connection which somebody likely has a pointer to..
|
||||
//but, we really shouldent ever get called anyhow..
|
||||
vitr cur, end;
|
||||
cur = m_list.begin();
|
||||
end = m_list.end();
|
||||
for(; cur != end; ++cur) {
|
||||
delete *cur;
|
||||
}
|
||||
}
|
||||
|
||||
T * NewQueuePop() {
|
||||
T * ret = nullptr;
|
||||
MNewQueue.lock();
|
||||
if(!m_NewQueue.empty()) {
|
||||
ret = m_NewQueue.front();
|
||||
m_NewQueue.pop();
|
||||
}
|
||||
MNewQueue.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Process() {
|
||||
BaseTCPServer::Process();
|
||||
|
||||
vitr cur;
|
||||
cur = m_list.begin();
|
||||
while(cur != m_list.end()) {
|
||||
T *data = *cur;
|
||||
if (data->IsFree() && (!data->CheckNetActive())) {
|
||||
#if EQN_DEBUG >= 4
|
||||
std::cout << "TCPConnection Connection deleted." << std::endl;
|
||||
#endif
|
||||
delete data;
|
||||
cur = m_list.erase(cur);
|
||||
} else {
|
||||
if (!data->Process())
|
||||
data->Disconnect();
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddConnection(T *con) {
|
||||
m_list.push_back(con);
|
||||
MNewQueue.lock();
|
||||
m_NewQueue.push(con);
|
||||
MNewQueue.unlock();
|
||||
}
|
||||
|
||||
//queue of new connections, for the app to pull from
|
||||
Mutex MNewQueue;
|
||||
std::queue<T *> m_NewQueue;
|
||||
|
||||
vstore m_list;
|
||||
};
|
||||
|
||||
|
||||
#endif /*TCPSERVER_H_*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
31
common/util/memory_stream.h
Normal file
31
common/util/memory_stream.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <streambuf>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Util {
|
||||
class MemoryBuffer : public std::streambuf
|
||||
{
|
||||
public:
|
||||
MemoryBuffer(char* base, size_t size) {
|
||||
setg(base, base, base + size);
|
||||
setp(base, base + size);
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryStreamWriter : virtual MemoryBuffer, public std::ostream
|
||||
{
|
||||
public:
|
||||
MemoryStreamWriter(char* base, size_t size) : MemoryBuffer(base, size), std::ostream(static_cast<std::streambuf*>(this)) { }
|
||||
};
|
||||
|
||||
class MemoryStreamReader : virtual MemoryBuffer, public std::istream
|
||||
{
|
||||
public:
|
||||
MemoryStreamReader(char* base, size_t size) : MemoryBuffer(base, size), std::istream(static_cast<std::streambuf*>(this)) { }
|
||||
};
|
||||
}
|
||||
}
|
||||
189
common/util/uuid.cpp
Normal file
189
common/util/uuid.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
#include "uuid.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CFUUID.h>
|
||||
#endif
|
||||
|
||||
unsigned char hexDigitToChar(char ch)
|
||||
{
|
||||
if (ch > 47 && ch < 58)
|
||||
return ch - 48;
|
||||
|
||||
if (ch > 96 && ch < 103)
|
||||
return ch - 87;
|
||||
|
||||
if (ch > 64 && ch < 71)
|
||||
return ch - 55;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char hexPairToChar(char a, char b)
|
||||
{
|
||||
return hexDigitToChar(a) * 16 + hexDigitToChar(b);
|
||||
}
|
||||
|
||||
EQ::Util::UUID::UUID()
|
||||
{
|
||||
m_bytes = std::vector<char>(16, 0);
|
||||
}
|
||||
|
||||
EQ::Util::UUID::UUID(const unsigned char *bytes)
|
||||
{
|
||||
m_bytes.assign(bytes, bytes + 16);
|
||||
}
|
||||
|
||||
EQ::Util::UUID::UUID(const UUID &o)
|
||||
{
|
||||
m_bytes = o.m_bytes;
|
||||
}
|
||||
|
||||
EQ::Util::UUID::UUID(UUID &&o)
|
||||
{
|
||||
std::swap(m_bytes, o.m_bytes);
|
||||
}
|
||||
|
||||
EQ::Util::UUID& EQ::Util::UUID::operator=(const UUID &o)
|
||||
{
|
||||
m_bytes = o.m_bytes;
|
||||
return *this;
|
||||
}
|
||||
|
||||
EQ::Util::UUID::~UUID()
|
||||
{
|
||||
}
|
||||
|
||||
EQ::Util::UUID EQ::Util::UUID::Generate()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GUID id;
|
||||
CoCreateGuid(&id);
|
||||
|
||||
const unsigned char buffer[16] =
|
||||
{
|
||||
static_cast<unsigned char>((id.Data1 >> 24) & 0xFF),
|
||||
static_cast<unsigned char>((id.Data1 >> 16) & 0xFF),
|
||||
static_cast<unsigned char>((id.Data1 >> 8) & 0xFF),
|
||||
static_cast<unsigned char>((id.Data1) & 0xff),
|
||||
static_cast<unsigned char>((id.Data2 >> 8) & 0xFF),
|
||||
static_cast<unsigned char>((id.Data2) & 0xff),
|
||||
static_cast<unsigned char>((id.Data3 >> 8) & 0xFF),
|
||||
static_cast<unsigned char>((id.Data3) & 0xFF),
|
||||
id.Data4[0],
|
||||
id.Data4[1],
|
||||
id.Data4[2],
|
||||
id.Data4[3],
|
||||
id.Data4[4],
|
||||
id.Data4[5],
|
||||
id.Data4[6],
|
||||
id.Data4[7]
|
||||
};
|
||||
|
||||
return buffer;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
uuid_t id;
|
||||
uuid_generate(id);
|
||||
return id;
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
auto id = CFUUIDCreate(nullptr);
|
||||
auto bytes = CFUUIDGetUUIDBytes(id);
|
||||
|
||||
const unsigned char buffer[16] =
|
||||
{
|
||||
bytes.byte0,
|
||||
bytes.byte1,
|
||||
bytes.byte2,
|
||||
bytes.byte3,
|
||||
bytes.byte4,
|
||||
bytes.byte5,
|
||||
bytes.byte6,
|
||||
bytes.byte7,
|
||||
bytes.byte8,
|
||||
bytes.byte9,
|
||||
bytes.byte10,
|
||||
bytes.byte11,
|
||||
bytes.byte12,
|
||||
bytes.byte13,
|
||||
bytes.byte14,
|
||||
bytes.byte15
|
||||
};
|
||||
|
||||
CFRelease(id);
|
||||
|
||||
return buffer;
|
||||
#endif
|
||||
}
|
||||
|
||||
EQ::Util::UUID EQ::Util::UUID::FromString(const std::string &str)
|
||||
{
|
||||
UUID ret;
|
||||
ret.m_bytes.clear();
|
||||
|
||||
size_t i = 0;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EQ::Util::UUID EQ::Util::UUID::FromByteArray(const char *buffer)
|
||||
{
|
||||
return UUID((unsigned char*)buffer);
|
||||
}
|
||||
|
||||
std::string EQ::Util::UUID::ToString() const
|
||||
{
|
||||
return fmt::format("{:0<2x}{:0<2x}{:0<2x}{:0<2x}-{:0<2x}{:0<2x}-{:0<2x}{:0<2x}-{:0<2x}{:0<2x}-{:0<2x}{:0<2x}{:0<2x}{:0<2x}{:0<2x}{:0<2x}",
|
||||
(unsigned char)m_bytes[0],
|
||||
(unsigned char)m_bytes[1],
|
||||
(unsigned char)m_bytes[2],
|
||||
(unsigned char)m_bytes[3],
|
||||
(unsigned char)m_bytes[4],
|
||||
(unsigned char)m_bytes[5],
|
||||
(unsigned char)m_bytes[6],
|
||||
(unsigned char)m_bytes[7],
|
||||
(unsigned char)m_bytes[8],
|
||||
(unsigned char)m_bytes[9],
|
||||
(unsigned char)m_bytes[10],
|
||||
(unsigned char)m_bytes[11],
|
||||
(unsigned char)m_bytes[12],
|
||||
(unsigned char)m_bytes[13],
|
||||
(unsigned char)m_bytes[14],
|
||||
(unsigned char)m_bytes[15]);
|
||||
}
|
||||
|
||||
const std::vector<char>& EQ::Util::UUID::ToByteArray() const
|
||||
{
|
||||
return m_bytes;
|
||||
}
|
||||
35
common/util/uuid.h
Normal file
35
common/util/uuid.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ios>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Util
|
||||
{
|
||||
class UUID
|
||||
{
|
||||
public:
|
||||
UUID(const UUID &o);
|
||||
UUID(UUID &&o);
|
||||
UUID& operator=(const UUID &o);
|
||||
~UUID();
|
||||
|
||||
static UUID Generate();
|
||||
static UUID FromString(const std::string &str);
|
||||
static UUID FromByteArray(const char *buffer);
|
||||
|
||||
std::string ToString() const;
|
||||
const std::vector<char>& ToByteArray() const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const UUID &id) {
|
||||
return os << id.ToString();
|
||||
}
|
||||
private:
|
||||
UUID();
|
||||
UUID(const unsigned char *bytes);
|
||||
std::vector<char> m_bytes;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,7 @@
|
||||
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9108
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9110
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017
|
||||
#else
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef WORLDCONNECTION_H
|
||||
#define WORLDCONNECTION_H
|
||||
|
||||
#include "../common/emu_tcp_connection.h"
|
||||
#include <string>
|
||||
|
||||
class ServerPacket;
|
||||
|
||||
/*
|
||||
* This object is an arbitrary connection to world.
|
||||
*/
|
||||
class WorldConnection {
|
||||
public:
|
||||
WorldConnection(EmuTCPConnection::ePacketMode mode, const char *password = "");
|
||||
virtual ~WorldConnection();
|
||||
|
||||
virtual void Process();
|
||||
bool SendPacket(ServerPacket* pack);
|
||||
|
||||
uint32 GetIP() const { return tcpc.GetrIP(); }
|
||||
uint16 GetPort() const { return tcpc.GetrPort(); }
|
||||
bool Connected() const { return (pConnected && tcpc.Connected()); }
|
||||
|
||||
void SetPassword(const char *password) { m_password = password; }
|
||||
bool Connect();
|
||||
void AsyncConnect();
|
||||
void Disconnect();
|
||||
inline bool TryReconnect() const { return pTryReconnect; }
|
||||
|
||||
protected:
|
||||
virtual void OnConnected();
|
||||
|
||||
std::string m_password;
|
||||
EmuTCPConnection tcpc;
|
||||
bool pTryReconnect;
|
||||
bool pConnected;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@ -13,28 +13,8 @@ SET(eqlaunch_headers
|
||||
|
||||
ADD_EXECUTABLE(eqlaunch ${eqlaunch_sources} ${eqlaunch_headers})
|
||||
|
||||
INSTALL(TARGETS eqlaunch RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
INSTALL(TARGETS eqlaunch RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
TARGET_LINK_LIBRARIES(eqlaunch common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY})
|
||||
|
||||
IF(MSVC)
|
||||
SET_TARGET_PROPERTIES(eqlaunch PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF")
|
||||
TARGET_LINK_LIBRARIES(eqlaunch "Ws2_32.lib")
|
||||
ENDIF(MSVC)
|
||||
|
||||
IF(MINGW)
|
||||
TARGET_LINK_LIBRARIES(eqlaunch "WS2_32")
|
||||
ENDIF(MINGW)
|
||||
|
||||
IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(eqlaunch "${CMAKE_DL_LIBS}")
|
||||
TARGET_LINK_LIBRARIES(eqlaunch "z")
|
||||
TARGET_LINK_LIBRARIES(eqlaunch "m")
|
||||
IF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(eqlaunch "rt")
|
||||
ENDIF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(eqlaunch "pthread")
|
||||
ADD_DEFINITIONS(-fPIC)
|
||||
ENDIF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(eqlaunch ${SERVER_LIBS})
|
||||
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include "../common/servertalk.h"
|
||||
#include "../common/platform.h"
|
||||
#include "../common/crash.h"
|
||||
#include "../common/unix.h"
|
||||
#include "worldserver.h"
|
||||
#include "zone_launch.h"
|
||||
#include <vector>
|
||||
@ -91,9 +92,6 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
std::map<std::string, ZoneLaunch *> zones;
|
||||
WorldServer world(zones, launcher_name.c_str(), Config);
|
||||
if (!world.Connect()) {
|
||||
Log(Logs::Detail, Logs::Launcher, "worldserver.Connect() FAILED! Will retry.");
|
||||
}
|
||||
|
||||
std::map<std::string, ZoneLaunch *>::iterator zone, zend;
|
||||
std::set<std::string> to_remove;
|
||||
@ -108,11 +106,6 @@ int main(int argc, char *argv[]) {
|
||||
//Advance the timer to our current point in time
|
||||
Timer::SetCurrentTime();
|
||||
|
||||
/*
|
||||
* Process the world connection
|
||||
*/
|
||||
world.Process();
|
||||
|
||||
/*
|
||||
* Let the process manager look for dead children
|
||||
*/
|
||||
@ -143,19 +136,8 @@ int main(int argc, char *argv[]) {
|
||||
zones.erase(rem);
|
||||
}
|
||||
|
||||
|
||||
if (InterserverTimer.Check()) {
|
||||
if (world.TryReconnect() && (!world.Connected()))
|
||||
world.AsyncConnect();
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a nice nap until next cycle
|
||||
*/
|
||||
if(zones.empty())
|
||||
Sleep(5000);
|
||||
else
|
||||
Sleep(2000);
|
||||
EQ::EventLoop::Get().Process();
|
||||
Sleep(5);
|
||||
}
|
||||
|
||||
//try to be semi-nice about this... without waiting too long
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
|
||||
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../common/global_define.h"
|
||||
@ -25,112 +25,104 @@
|
||||
#include "zone_launch.h"
|
||||
|
||||
WorldServer::WorldServer(std::map<std::string, ZoneLaunch *> &zones, const char *name, const EQEmuConfig *config)
|
||||
: WorldConnection(EmuTCPConnection::packetModeLauncher, config->SharedKey.c_str()),
|
||||
m_name(name),
|
||||
: m_name(name),
|
||||
m_config(config),
|
||||
m_zones(zones)
|
||||
{
|
||||
m_connection.reset(new EQ::Net::ServertalkClient(config->WorldIP, config->WorldTCPPort, false, "Launcher", config->SharedKey));
|
||||
m_connection->OnConnect([this](EQ::Net::ServertalkClient *client) {
|
||||
OnConnected();
|
||||
});
|
||||
|
||||
m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
WorldServer::~WorldServer() {
|
||||
}
|
||||
|
||||
void WorldServer::OnConnected() {
|
||||
WorldConnection::OnConnected();
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_LauncherConnectInfo, sizeof(LauncherConnectInfo));
|
||||
LauncherConnectInfo* sci = (LauncherConnectInfo*) pack->pBuffer;
|
||||
LauncherConnectInfo* sci = (LauncherConnectInfo*)pack->pBuffer;
|
||||
strn0cpy(sci->name, m_name, sizeof(sci->name));
|
||||
// sci->port = net.GetZonePort();
|
||||
// strcpy(sci->address, net.GetZoneAddress());
|
||||
SendPacket(pack);
|
||||
m_connection->SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
|
||||
//send status for all zones...
|
||||
std::map<std::string, ZoneLaunch *>::iterator cur, end;
|
||||
cur = m_zones.begin();
|
||||
end = m_zones.end();
|
||||
for(; cur != end; ++cur) {
|
||||
for (; cur != end; ++cur) {
|
||||
cur->second->SendStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldServer::Process() {
|
||||
void WorldServer::HandleMessage(uint16 opcode, EQ::Net::Packet &p) {
|
||||
ServerPacket tpack(opcode, p);
|
||||
ServerPacket *pack = &tpack;
|
||||
|
||||
WorldConnection::Process();
|
||||
|
||||
if (!Connected())
|
||||
return;
|
||||
|
||||
ServerPacket *pack = 0;
|
||||
while((pack = tcpc.PopPacket())) {
|
||||
switch(pack->opcode) {
|
||||
case 0: {
|
||||
switch (opcode) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case ServerOP_EmoteMessage:
|
||||
case ServerOP_KeepAlive: {
|
||||
// ignore this
|
||||
break;
|
||||
}
|
||||
case ServerOP_LauncherZoneRequest: {
|
||||
if (pack->size != sizeof(LauncherZoneRequest)) {
|
||||
Log(Logs::Detail, Logs::Launcher, "Invalid size of LauncherZoneRequest: %d", pack->size);
|
||||
break;
|
||||
}
|
||||
case ServerOP_EmoteMessage:
|
||||
case ServerOP_KeepAlive: {
|
||||
// ignore this
|
||||
break;
|
||||
}
|
||||
case ServerOP_ZAAuthFailed: {
|
||||
Log(Logs::Detail, Logs::Launcher, "World server responded 'Not Authorized', disabling reconnect");
|
||||
pTryReconnect = false;
|
||||
Disconnect();
|
||||
break;
|
||||
}
|
||||
case ServerOP_LauncherZoneRequest: {
|
||||
if(pack->size != sizeof(LauncherZoneRequest)) {
|
||||
Log(Logs::Detail, Logs::Launcher, "Invalid size of LauncherZoneRequest: %d", pack->size);
|
||||
break;
|
||||
}
|
||||
const LauncherZoneRequest *lzr = (const LauncherZoneRequest *) pack->pBuffer;
|
||||
const LauncherZoneRequest *lzr = (const LauncherZoneRequest *)pack->pBuffer;
|
||||
|
||||
switch(ZoneRequestCommands(lzr->command)) {
|
||||
case ZR_Start: {
|
||||
if(m_zones.find(lzr->short_name) != m_zones.end()) {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to start zone %s, but it is already running.", lzr->short_name);
|
||||
} else {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to start zone %s.", lzr->short_name);
|
||||
auto l = new ZoneLaunch(this, m_name, lzr->short_name, lzr->port, m_config);
|
||||
m_zones[lzr->short_name] = l;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZR_Restart: {
|
||||
auto res = m_zones.find(lzr->short_name);
|
||||
if(res == m_zones.end()) {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to restart zone %s, but it is not running.", lzr->short_name);
|
||||
} else {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to restart zone %s.", lzr->short_name);
|
||||
res->second->Restart();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZR_Stop: {
|
||||
auto res = m_zones.find(lzr->short_name);
|
||||
if(res == m_zones.end()) {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to stop zone %s, but it is not running.", lzr->short_name);
|
||||
} else {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to stop zone %s.", lzr->short_name);
|
||||
res->second->Stop();
|
||||
}
|
||||
break;
|
||||
switch (ZoneRequestCommands(lzr->command)) {
|
||||
case ZR_Start: {
|
||||
if (m_zones.find(lzr->short_name) != m_zones.end()) {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to start zone %s, but it is already running.", lzr->short_name);
|
||||
}
|
||||
else {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to start zone %s.", lzr->short_name);
|
||||
auto l = new ZoneLaunch(this, m_name, lzr->short_name, lzr->port, m_config);
|
||||
m_zones[lzr->short_name] = l;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_GroupIDReply: {
|
||||
//ignore this, world is still being dumb
|
||||
case ZR_Restart: {
|
||||
auto res = m_zones.find(lzr->short_name);
|
||||
if (res == m_zones.end()) {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to restart zone %s, but it is not running.", lzr->short_name);
|
||||
}
|
||||
else {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to restart zone %s.", lzr->short_name);
|
||||
res->second->Restart();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZR_Stop: {
|
||||
auto res = m_zones.find(lzr->short_name);
|
||||
if (res == m_zones.end()) {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to stop zone %s, but it is not running.", lzr->short_name);
|
||||
}
|
||||
else {
|
||||
Log(Logs::Detail, Logs::Launcher, "World told us to stop zone %s.", lzr->short_name);
|
||||
res->second->Stop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_GroupIDReply: {
|
||||
//ignore this, world is still being dumb
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
Log(Logs::Detail, Logs::Launcher, "Unknown opcode 0x%x from World of len %d", pack->opcode, pack->size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
safe_delete(pack);
|
||||
default: {
|
||||
Log(Logs::Detail, Logs::Launcher, "Unknown opcode 0x%x from World of len %d", pack->opcode, pack->size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,18 +130,12 @@ void WorldServer::Process() {
|
||||
|
||||
void WorldServer::SendStatus(const char *short_name, uint32 start_count, bool running) {
|
||||
auto pack = new ServerPacket(ServerOP_LauncherZoneStatus, sizeof(LauncherZoneStatus));
|
||||
LauncherZoneStatus* it =(LauncherZoneStatus*) pack->pBuffer;
|
||||
LauncherZoneStatus* it = (LauncherZoneStatus*)pack->pBuffer;
|
||||
|
||||
strn0cpy(it->short_name, short_name, 32);
|
||||
it->start_count = start_count;
|
||||
it->running = running?1:0;
|
||||
it->running = running ? 1 : 0;
|
||||
|
||||
SendPacket(pack);
|
||||
m_connection->SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -18,7 +18,8 @@
|
||||
#ifndef WORLDSERVER_H
|
||||
#define WORLDSERVER_H
|
||||
|
||||
#include "../common/worldconn.h"
|
||||
#include "../common/net/servertalk_client_connection.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
@ -26,18 +27,19 @@
|
||||
class ZoneLaunch;
|
||||
class EQEmuConfig;
|
||||
|
||||
class WorldServer : public WorldConnection {
|
||||
class WorldServer {
|
||||
public:
|
||||
WorldServer(std::map<std::string, ZoneLaunch *> &zones, const char *name, const EQEmuConfig *config);
|
||||
virtual ~WorldServer();
|
||||
~WorldServer();
|
||||
|
||||
virtual void Process();
|
||||
void HandleMessage(uint16 opcode, EQ::Net::Packet &p);
|
||||
|
||||
void SendStatus(const char *short_name, uint32 start_count, bool running);
|
||||
|
||||
private:
|
||||
virtual void OnConnected();
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
|
||||
const char *const m_name;
|
||||
const EQEmuConfig *const m_config;
|
||||
std::map<std::string, ZoneLaunch *> &m_zones;
|
||||
|
||||
26
hc/CMakeLists.txt
Normal file
26
hc/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
|
||||
SET(hc_sources
|
||||
eq.cpp
|
||||
main.cpp
|
||||
login.cpp
|
||||
world.cpp
|
||||
)
|
||||
|
||||
SET(hc_headers
|
||||
eq.h
|
||||
login.h
|
||||
world.h
|
||||
)
|
||||
|
||||
FIND_PACKAGE(OpenSSL REQUIRED)
|
||||
|
||||
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
||||
|
||||
ADD_EXECUTABLE(hc ${hc_sources} ${hc_headers})
|
||||
|
||||
INSTALL(TARGETS hc RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
TARGET_LINK_LIBRARIES(hc ${SERVER_LIBS} ${OPENSSL_LIBRARIES})
|
||||
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
350
hc/eq.cpp
Normal file
350
hc/eq.cpp
Normal file
@ -0,0 +1,350 @@
|
||||
#include "eq.h"
|
||||
#include "../common/net/dns.h"
|
||||
|
||||
const char* eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char* buffer_out, bool enc) {
|
||||
DES_key_schedule k;
|
||||
DES_cblock v;
|
||||
|
||||
memset(&k, 0, sizeof(DES_key_schedule));
|
||||
memset(&v, 0, sizeof(DES_cblock));
|
||||
|
||||
if (!enc && buffer_in_sz && buffer_in_sz % 8 != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DES_ncbc_encrypt((const unsigned char*)buffer_in, (unsigned char*)buffer_out, (long)buffer_in_sz, &k, &v, enc);
|
||||
return buffer_out;
|
||||
}
|
||||
|
||||
EverQuest::EverQuest(const std::string &host, int port, const std::string &user, const std::string &pass, const std::string &server, const std::string &character)
|
||||
{
|
||||
m_host = host;
|
||||
m_port = port;
|
||||
m_user = user;
|
||||
m_pass = pass;
|
||||
m_server = server;
|
||||
m_character = character;
|
||||
m_dbid = 0;
|
||||
|
||||
EQ::Net::DNSLookup(m_host, port, false, [&](const std::string &addr) {
|
||||
if (addr.empty()) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Could not resolve address: {0}", m_host);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
m_host = addr;
|
||||
m_login_connection_manager.reset(new EQ::Net::DaybreakConnectionManager());
|
||||
|
||||
m_login_connection_manager->OnNewConnection(std::bind(&EverQuest::LoginOnNewConnection, this, std::placeholders::_1));
|
||||
m_login_connection_manager->OnConnectionStateChange(std::bind(&EverQuest::LoginOnStatusChangeReconnectEnabled, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_login_connection_manager->OnPacketRecv(std::bind(&EverQuest::LoginOnPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_login_connection_manager->Connect(m_host, m_port);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EverQuest::~EverQuest()
|
||||
{
|
||||
}
|
||||
|
||||
void EverQuest::LoginOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection)
|
||||
{
|
||||
m_login_connection = connection;
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Connecting...");
|
||||
}
|
||||
|
||||
void EverQuest::LoginOnStatusChangeReconnectEnabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
|
||||
{
|
||||
if (to == EQ::Net::StatusConnected) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Login connected.");
|
||||
LoginSendSessionReady();
|
||||
}
|
||||
|
||||
if (to == EQ::Net::StatusDisconnected) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Login connection lost before we got to world, reconnecting.");
|
||||
m_key.clear();
|
||||
m_dbid = 0;
|
||||
m_login_connection.reset();
|
||||
m_login_connection_manager->Connect(m_host, m_port);
|
||||
}
|
||||
}
|
||||
|
||||
void EverQuest::LoginOnStatusChangeReconnectDisabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
|
||||
{
|
||||
if (to == EQ::Net::StatusDisconnected) {
|
||||
m_login_connection.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void EverQuest::LoginOnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet & p)
|
||||
{
|
||||
auto opcode = p.GetUInt16(0);
|
||||
switch (opcode) {
|
||||
case 0x0017: //OP_ChatMessage
|
||||
LoginSendLogin();
|
||||
break;
|
||||
case 0x0018:
|
||||
LoginProcessLoginResponse(p);
|
||||
break;
|
||||
case 0x0019:
|
||||
LoginProcessServerPacketList(p);
|
||||
break;
|
||||
case 0x0022:
|
||||
LoginProcessServerPlayResponse(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EverQuest::LoginSendSessionReady()
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutUInt16(0, 1); //OP_SessionReady
|
||||
p.PutUInt32(2, 2);
|
||||
|
||||
m_login_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void EverQuest::LoginSendLogin()
|
||||
{
|
||||
size_t buffer_len = m_user.length() + m_pass.length() + 2;
|
||||
std::unique_ptr<char[]> buffer(new char[buffer_len]);
|
||||
|
||||
strcpy(&buffer[0], m_user.c_str());
|
||||
strcpy(&buffer[m_user.length() + 1], m_pass.c_str());
|
||||
|
||||
size_t encrypted_len = buffer_len;
|
||||
|
||||
if (encrypted_len % 8 > 0) {
|
||||
encrypted_len = ((encrypted_len / 8) + 1) * 8;
|
||||
}
|
||||
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.Resize(12 + encrypted_len);
|
||||
p.PutUInt16(0, 2); //OP_Login
|
||||
p.PutUInt32(2, 3);
|
||||
|
||||
eqcrypt_block(&buffer[0], buffer_len, (char*)p.Data() + 12, true);
|
||||
|
||||
m_login_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void EverQuest::LoginSendServerRequest()
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutUInt16(0, 4); //OP_ServerListRequest
|
||||
p.PutUInt32(2, 4);
|
||||
|
||||
m_login_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void EverQuest::LoginSendPlayRequest(uint32_t id)
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutUInt16(0, 0x000d);
|
||||
p.PutUInt16(2, 5);
|
||||
p.PutUInt32(4, 0);
|
||||
p.PutUInt32(8, 0);
|
||||
p.PutUInt32(12, id);
|
||||
|
||||
m_login_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void EverQuest::LoginProcessLoginResponse(const EQ::Net::Packet & p)
|
||||
{
|
||||
auto encrypt_size = p.Length() - 12;
|
||||
if (encrypt_size % 8 > 0) {
|
||||
encrypt_size = (encrypt_size / 8) * 8;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> decrypted(new char[encrypt_size]);
|
||||
|
||||
eqcrypt_block((char*)p.Data() + 12, encrypt_size, &decrypted[0], false);
|
||||
|
||||
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
|
||||
auto response_error = sp.GetUInt16(1);
|
||||
|
||||
if (response_error > 101) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Error logging in response code: {0}", response_error);
|
||||
LoginDisableReconnect();
|
||||
}
|
||||
else {
|
||||
m_key = sp.GetCString(12);
|
||||
m_dbid = sp.GetUInt32(8);
|
||||
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Logged in successfully with dbid {0} and key {1}", m_dbid, m_key);
|
||||
LoginSendServerRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void EverQuest::LoginProcessServerPacketList(const EQ::Net::Packet & p)
|
||||
{
|
||||
m_world_servers.clear();
|
||||
auto number_of_servers = p.GetUInt32(18);
|
||||
size_t idx = 22;
|
||||
|
||||
for (auto i = 0U; i < number_of_servers; ++i) {
|
||||
WorldServer ws;
|
||||
ws.address = p.GetCString(idx);
|
||||
idx += (ws.address.length() + 1);
|
||||
|
||||
ws.type = p.GetInt32(idx);
|
||||
idx += 4;
|
||||
|
||||
auto id = p.GetUInt32(idx);
|
||||
idx += 4;
|
||||
|
||||
ws.long_name = p.GetCString(idx);
|
||||
idx += (ws.long_name.length() + 1);
|
||||
|
||||
ws.lang = p.GetCString(idx);
|
||||
idx += (ws.lang.length() + 1);
|
||||
|
||||
ws.region = p.GetCString(idx);
|
||||
idx += (ws.region.length() + 1);
|
||||
|
||||
ws.status = p.GetInt32(idx);
|
||||
idx += 4;
|
||||
|
||||
ws.players = p.GetInt32(idx);
|
||||
idx += 4;
|
||||
|
||||
m_world_servers[id] = ws;
|
||||
}
|
||||
|
||||
for (auto server : m_world_servers) {
|
||||
if (server.second.long_name.compare(m_server) == 0) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Found world server {0}, attempting to login.", m_server);
|
||||
LoginSendPlayRequest(server.first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Got response from login server but could not find world server {0} disconnecting.", m_server);
|
||||
LoginDisableReconnect();
|
||||
}
|
||||
|
||||
void EverQuest::LoginProcessServerPlayResponse(const EQ::Net::Packet &p)
|
||||
{
|
||||
auto allowed = p.GetUInt8(12);
|
||||
|
||||
if (allowed) {
|
||||
auto server = p.GetUInt32(18);
|
||||
auto ws = m_world_servers.find(server);
|
||||
if (ws != m_world_servers.end()) {
|
||||
ConnectToWorld();
|
||||
LoginDisableReconnect();
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto message = p.GetUInt16(13);
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Failed to login to server with message {0}");
|
||||
LoginDisableReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void EverQuest::LoginDisableReconnect()
|
||||
{
|
||||
m_login_connection_manager->OnConnectionStateChange(std::bind(&EverQuest::LoginOnStatusChangeReconnectDisabled, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_login_connection->Close();
|
||||
}
|
||||
|
||||
void EverQuest::ConnectToWorld()
|
||||
{
|
||||
m_world_connection_manager.reset(new EQ::Net::DaybreakConnectionManager());
|
||||
m_world_connection_manager->OnNewConnection(std::bind(&EverQuest::WorldOnNewConnection, this, std::placeholders::_1));
|
||||
m_world_connection_manager->OnConnectionStateChange(std::bind(&EverQuest::WorldOnStatusChangeReconnectEnabled, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_world_connection_manager->OnPacketRecv(std::bind(&EverQuest::WorldOnPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_world_connection_manager->Connect(m_host, 9000);
|
||||
}
|
||||
|
||||
void EverQuest::WorldOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection)
|
||||
{
|
||||
m_world_connection = connection;
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Connecting to world...");
|
||||
}
|
||||
|
||||
void EverQuest::WorldOnStatusChangeReconnectEnabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
|
||||
{
|
||||
if (to == EQ::Net::StatusConnected) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "World connected.");
|
||||
WorldSendClientAuth();
|
||||
}
|
||||
|
||||
if (to == EQ::Net::StatusDisconnected) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "World connection lost, reconnecting.");
|
||||
m_world_connection.reset();
|
||||
m_world_connection_manager->Connect(m_host, 9000);
|
||||
}
|
||||
}
|
||||
|
||||
void EverQuest::WorldOnStatusChangeReconnectDisabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
|
||||
{
|
||||
if (to == EQ::Net::StatusDisconnected) {
|
||||
m_world_connection.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void EverQuest::WorldOnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet & p)
|
||||
{
|
||||
auto opcode = p.GetUInt16(0);
|
||||
switch (opcode) {
|
||||
case 0x00d2:
|
||||
WorldProcessCharacterSelect(p);
|
||||
break;
|
||||
default:
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Unhandled opcode: {0:#x}", opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EverQuest::WorldSendClientAuth()
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.Resize(2 + 464);
|
||||
|
||||
p.PutUInt16(0, 0x7a09U);
|
||||
std::string dbid_str = std::to_string(m_dbid);
|
||||
|
||||
p.PutCString(2, dbid_str.c_str());
|
||||
p.PutCString(2 + dbid_str.length() + 1, m_key.c_str());
|
||||
|
||||
m_world_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void EverQuest::WorldSendEnterWorld(const std::string &character)
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutUInt16(0, 0x578f);
|
||||
p.PutString(2, character);
|
||||
p.PutUInt32(66, 0);
|
||||
p.PutUInt32(70, 0);
|
||||
|
||||
m_world_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void EverQuest::WorldProcessCharacterSelect(const EQ::Net::Packet &p)
|
||||
{
|
||||
auto char_count = p.GetUInt32(2);
|
||||
size_t idx = 6;
|
||||
|
||||
//Log.OutF(Logs::General, Logs::Headless_Client, "{0} characters", char_count);
|
||||
for (uint32_t i = 0; i < char_count; ++i) {
|
||||
auto name = p.GetCString(idx);
|
||||
idx += name.length() + 1;
|
||||
|
||||
auto pclass = p.GetUInt8(idx);
|
||||
auto prace = p.GetUInt32(idx + 1);
|
||||
auto plevel = p.GetUInt8(idx + 5);
|
||||
|
||||
idx += 274;
|
||||
if (m_character.compare(name) == 0) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Found {0}, would attempt to login here.", m_character);
|
||||
WorldSendEnterWorld(m_character);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Could not find {0}, cannot continue to login.", m_character);
|
||||
}
|
||||
|
||||
74
hc/eq.h
Normal file
74
hc/eq.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include "../common/net/daybreak_connection.h"
|
||||
#include "../common/event/timer.h"
|
||||
#include <openssl/des.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
struct WorldServer
|
||||
{
|
||||
std::string long_name;
|
||||
std::string address;
|
||||
int type;
|
||||
std::string lang;
|
||||
std::string region;
|
||||
int status;
|
||||
int players;
|
||||
};
|
||||
|
||||
class EverQuest
|
||||
{
|
||||
public:
|
||||
EverQuest(const std::string &host, int port, const std::string &user, const std::string &pass, const std::string &server, const std::string &character);
|
||||
~EverQuest();
|
||||
|
||||
private:
|
||||
//Login
|
||||
void LoginOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection);
|
||||
void LoginOnStatusChangeReconnectEnabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to);
|
||||
void LoginOnStatusChangeReconnectDisabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to);
|
||||
void LoginOnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p);
|
||||
|
||||
void LoginSendSessionReady();
|
||||
void LoginSendLogin();
|
||||
void LoginSendServerRequest();
|
||||
void LoginSendPlayRequest(uint32_t id);
|
||||
void LoginProcessLoginResponse(const EQ::Net::Packet &p);
|
||||
void LoginProcessServerPacketList(const EQ::Net::Packet &p);
|
||||
void LoginProcessServerPlayResponse(const EQ::Net::Packet &p);
|
||||
|
||||
void LoginDisableReconnect();
|
||||
|
||||
std::unique_ptr<EQ::Net::DaybreakConnectionManager> m_login_connection_manager;
|
||||
std::shared_ptr<EQ::Net::DaybreakConnection> m_login_connection;
|
||||
std::map<uint32_t, WorldServer> m_world_servers;
|
||||
|
||||
//World
|
||||
void ConnectToWorld();
|
||||
|
||||
void WorldOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection);
|
||||
void WorldOnStatusChangeReconnectEnabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to);
|
||||
void WorldOnStatusChangeReconnectDisabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to);
|
||||
void WorldOnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p);
|
||||
|
||||
void WorldSendClientAuth();
|
||||
void WorldSendEnterWorld(const std::string &character);
|
||||
|
||||
void WorldProcessCharacterSelect(const EQ::Net::Packet &p);
|
||||
|
||||
std::unique_ptr<EQ::Net::DaybreakConnectionManager> m_world_connection_manager;
|
||||
std::shared_ptr<EQ::Net::DaybreakConnection> m_world_connection;
|
||||
|
||||
//Variables
|
||||
std::string m_host;
|
||||
int m_port;
|
||||
std::string m_user;
|
||||
std::string m_pass;
|
||||
std::string m_server;
|
||||
std::string m_character;
|
||||
|
||||
std::string m_key;
|
||||
uint32_t m_dbid;
|
||||
};
|
||||
255
hc/login.cpp
Normal file
255
hc/login.cpp
Normal file
@ -0,0 +1,255 @@
|
||||
/*#include "login.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include <openssl/des.h>
|
||||
|
||||
const char* eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char* buffer_out, bool enc) {
|
||||
DES_key_schedule k;
|
||||
DES_cblock v;
|
||||
|
||||
memset(&k, 0, sizeof(DES_key_schedule));
|
||||
memset(&v, 0, sizeof(DES_cblock));
|
||||
|
||||
if (!enc && buffer_in_sz && buffer_in_sz % 8 != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DES_ncbc_encrypt((const unsigned char*)buffer_in, (unsigned char*)buffer_out, (long)buffer_in_sz, &k, &v, enc);
|
||||
return buffer_out;
|
||||
}
|
||||
|
||||
LoginConnection::LoginConnection(const std::string &username, const std::string &password, const std::string &host, int host_port, const std::string &server)
|
||||
{
|
||||
m_connecting = false;
|
||||
m_username = username;
|
||||
m_password = password;
|
||||
m_host = host;
|
||||
m_host_port = host_port;
|
||||
m_server = server;
|
||||
|
||||
m_connection_manager.reset(new EQ::Net::DaybreakConnectionManager());
|
||||
|
||||
m_connection_manager->OnNewConnection(std::bind(&LoginConnection::OnNewConnection, this, std::placeholders::_1));
|
||||
m_connection_manager->OnConnectionStateChange(std::bind(&LoginConnection::OnStatusChangeActive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_connection_manager->OnPacketRecv(std::bind(&LoginConnection::OnPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_connection_manager->Connect(host, host_port);
|
||||
}
|
||||
|
||||
LoginConnection::~LoginConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void LoginConnection::OnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection)
|
||||
{
|
||||
m_connection = connection;
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Connecting...");
|
||||
}
|
||||
|
||||
void LoginConnection::OnStatusChangeActive(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
|
||||
{
|
||||
if (to == EQ::Net::StatusConnected) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Login connected.");
|
||||
SendSessionReady();
|
||||
}
|
||||
|
||||
if (to == EQ::Net::StatusDisconnected) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Login connection lost, reconnecting.");
|
||||
m_key.clear();
|
||||
m_dbid = 0;
|
||||
m_connection.reset();
|
||||
m_connection_manager->Connect(m_host, m_host_port);
|
||||
}
|
||||
}
|
||||
|
||||
void LoginConnection::OnStatusChangeInactive(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
|
||||
{
|
||||
if (to == EQ::Net::StatusDisconnected) {
|
||||
m_key.clear();
|
||||
m_dbid = 0;
|
||||
m_connection.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void LoginConnection::OnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p)
|
||||
{
|
||||
auto opcode = p.GetUInt16(0);
|
||||
switch (opcode) {
|
||||
case 0x0017: //OP_ChatMessage
|
||||
SendLogin();
|
||||
break;
|
||||
case 0x0018:
|
||||
ProcessLoginResponse(p);
|
||||
break;
|
||||
case 0x0019:
|
||||
ProcessServerPacketList(p);
|
||||
break;
|
||||
case 0x0022:
|
||||
ProcessServerPlayResponse(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LoginConnection::Kill()
|
||||
{
|
||||
m_connection_manager->OnConnectionStateChange(std::bind(&LoginConnection::OnStatusChangeInactive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_key.clear();
|
||||
m_dbid = 0;
|
||||
m_connection->Close();
|
||||
}
|
||||
|
||||
void LoginConnection::Start()
|
||||
{
|
||||
m_connection_manager->OnConnectionStateChange(std::bind(&LoginConnection::OnStatusChangeActive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_connection_manager->Connect(m_host, m_host_port);
|
||||
}
|
||||
|
||||
void LoginConnection::SendSessionReady()
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutUInt16(0, 1); //OP_SessionReady
|
||||
p.PutUInt32(2, 2);
|
||||
|
||||
m_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void LoginConnection::SendLogin()
|
||||
{
|
||||
size_t buffer_len = m_username.length() + m_password.length() + 2;
|
||||
std::unique_ptr<char[]> buffer(new char[buffer_len]);
|
||||
|
||||
strcpy(&buffer[0], m_username.c_str());
|
||||
strcpy(&buffer[m_username.length() + 1], m_password.c_str());
|
||||
|
||||
size_t encrypted_len = buffer_len;
|
||||
|
||||
if (encrypted_len % 8 > 0) {
|
||||
encrypted_len = ((encrypted_len / 8) + 1) * 8;
|
||||
}
|
||||
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.Resize(12 + encrypted_len);
|
||||
p.PutUInt16(0, 2); //OP_Login
|
||||
p.PutUInt32(2, 3);
|
||||
|
||||
eqcrypt_block(&buffer[0], buffer_len, (char*)p.Data() + 12, true);
|
||||
|
||||
m_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void LoginConnection::SendServerRequest()
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutUInt16(0, 4); //OP_ServerListRequest
|
||||
p.PutUInt32(2, 4);
|
||||
|
||||
m_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void LoginConnection::SendPlayRequest(uint32_t id)
|
||||
{
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutUInt16(0, 0x000d);
|
||||
p.PutUInt16(2, 5);
|
||||
p.PutUInt32(4, 0);
|
||||
p.PutUInt32(8, 0);
|
||||
p.PutUInt32(12, id);
|
||||
|
||||
m_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void LoginConnection::ProcessLoginResponse(const EQ::Net::Packet &p)
|
||||
{
|
||||
auto encrypt_size = p.Length() - 12;
|
||||
if (encrypt_size % 8 > 0) {
|
||||
encrypt_size = (encrypt_size / 8) * 8;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> decrypted(new char[encrypt_size]);
|
||||
|
||||
eqcrypt_block((char*)p.Data() + 12, encrypt_size, &decrypted[0], false);
|
||||
|
||||
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
|
||||
auto response_error = sp.GetUInt16(1);
|
||||
|
||||
if (response_error > 101) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Error logging in response code: {0}", response_error);
|
||||
Kill();
|
||||
}
|
||||
else {
|
||||
m_key = sp.GetCString(12);
|
||||
m_dbid = sp.GetUInt32(8);
|
||||
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Logged in successfully with dbid {0} and key {1}", m_dbid, m_key);
|
||||
SendServerRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void LoginConnection::ProcessServerPacketList(const EQ::Net::Packet &p)
|
||||
{
|
||||
m_world_servers.clear();
|
||||
auto number_of_servers = p.GetUInt32(18);
|
||||
size_t idx = 22;
|
||||
|
||||
for (auto i = 0U; i < number_of_servers; ++i) {
|
||||
WorldServer ws;
|
||||
ws.address = p.GetCString(idx);
|
||||
idx += (ws.address.length() + 1);
|
||||
|
||||
ws.type = p.GetInt32(idx);
|
||||
idx += 4;
|
||||
|
||||
auto id = p.GetUInt32(idx);
|
||||
idx += 4;
|
||||
|
||||
ws.long_name = p.GetCString(idx);
|
||||
idx += (ws.long_name.length() + 1);
|
||||
|
||||
ws.lang = p.GetCString(idx);
|
||||
idx += (ws.lang.length() + 1);
|
||||
|
||||
ws.region = p.GetCString(idx);
|
||||
idx += (ws.region.length() + 1);
|
||||
|
||||
ws.status = p.GetInt32(idx);
|
||||
idx += 4;
|
||||
|
||||
ws.players = p.GetInt32(idx);
|
||||
idx += 4;
|
||||
|
||||
m_world_servers[id] = ws;
|
||||
}
|
||||
|
||||
for (auto server : m_world_servers) {
|
||||
if (server.second.long_name.compare(m_server) == 0) {
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Found world server {0}, attempting to login.", m_server);
|
||||
SendPlayRequest(server.first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Got response from login server but could not find world server {0} disconnecting.", m_server);
|
||||
Kill();
|
||||
}
|
||||
|
||||
void LoginConnection::ProcessServerPlayResponse(const EQ::Net::Packet &p)
|
||||
{
|
||||
auto allowed = p.GetUInt8(12);
|
||||
|
||||
if (allowed) {
|
||||
auto server = p.GetUInt32(18);
|
||||
auto ws = m_world_servers.find(server);
|
||||
if (ws != m_world_servers.end()) {
|
||||
if (m_on_can_login_world) {
|
||||
m_on_can_login_world(ws->second, m_key, m_dbid);
|
||||
}
|
||||
|
||||
Kill();
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto message = p.GetUInt16(13);
|
||||
Log.OutF(Logs::General, Logs::Headless_Client, "Failed to login to server with message {0}");
|
||||
Kill();
|
||||
}
|
||||
}
|
||||
*/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user