Merge branch 'main' into cdclient-rework

This commit is contained in:
Wincent Holm 2022-07-18 16:44:53 +02:00
commit 45ae46f6f1
73 changed files with 1959 additions and 903 deletions

View File

@ -12,18 +12,21 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ windows-2022, ubuntu-20.04 ] os: [ windows-2022, ubuntu-20.04, macos-11 ]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Add msbuild to PATH (windows only) - name: Add msbuild to PATH (Windows only)
if: ${{ matrix.os == 'windows-2022' }} if: ${{ matrix.os == 'windows-2022' }}
uses: microsoft/setup-msbuild@v1.1 uses: microsoft/setup-msbuild@v1.1
with: with:
vs-version: '[17,18)' vs-version: '[17,18)'
msbuild-architecture: x64 msbuild-architecture: x64
- name: Install libssl (Mac Only)
if: ${{ matrix.os == 'macos-11' }}
run: brew install openssl@3
- name: cmake - name: cmake
uses: lukka/run-cmake@v10 uses: lukka/run-cmake@v10
with: with:

View File

@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.14)
project(Darkflame) project(Darkflame)
include(CTest) include(CTest)
set (CMAKE_CXX_STANDARD 17)
# Read variables from file # Read variables from file
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables) FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
@ -10,92 +12,67 @@ string(REPLACE "\n" ";" variables ${variables})
# Set the cmake variables, formatted as "VARIABLE #" in variables # Set the cmake variables, formatted as "VARIABLE #" in variables
foreach(variable ${variables}) foreach(variable ${variables})
# If the string contains a #, skip it # If the string contains a #, skip it
if(NOT "${variable}" MATCHES "#") if(NOT "${variable}" MATCHES "#")
# Split the variable into name and value # Split the variable into name and value
string(REPLACE "=" ";" variable ${variable}) string(REPLACE "=" ";" variable ${variable})
# Check that the length of the variable is 2 (name and value) # Check that the length of the variable is 2 (name and value)
list(LENGTH variable length) list(LENGTH variable length)
if(${length} EQUAL 2) if(${length} EQUAL 2)
list(GET variable 0 variable_name) list(GET variable 0 variable_name)
list(GET variable 1 variable_value) list(GET variable 1 variable_value)
# Set the variable # Set the variable
set(${variable_name} ${variable_value}) set(${variable_name} ${variable_value})
# Add compiler definition # Add compiler definition
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}")
message(STATUS "Variable: ${variable_name} = ${variable_value}") message(STATUS "Variable: ${variable_name} = ${variable_value}")
endif() endif()
endif() endif()
endforeach() endforeach()
# On windows it's better to build this from source, as there's no way FindZLIB is gonna find it
if(NOT WIN32)
find_package(ZLIB REQUIRED)
endif()
# Fetch External (Non-Submodule) Libraries
if(WIN32)
include(FetchContent)
FetchContent_Declare(
zlib
URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip
URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1
)
FetchContent_MakeAvailable(zlib)
set(ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR})
set_target_properties(zlib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIRS}") # Why?
add_library(ZLIB::ZLIB ALIAS zlib) # You're welcome
endif(WIN32)
if(UNIX AND NOT APPLE)
include(FetchContent)
if (__include_backtrace__ AND __compile_backtrace__)
FetchContent_Declare(
backtrace
GIT_REPOSITORY https://github.com/ianlancetaylor/libbacktrace.git
)
FetchContent_MakeAvailable(backtrace)
if (NOT EXISTS ${backtrace_SOURCE_DIR}/.libs)
set(backtrace_make_cmd "${backtrace_SOURCE_DIR}/configure --prefix=\"/usr\" --enable-shared --with-system-libunwind")
execute_process(
COMMAND bash -c "cd ${backtrace_SOURCE_DIR} && ${backtrace_make_cmd} && make && cd ${CMAKE_SOURCE_DIR}"
)
endif()
link_directories(${backtrace_SOURCE_DIR}/.libs/)
include_directories(${backtrace_SOURCE_DIR})
endif()
endif()
# Set the version # Set the version
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
# Echo the version # Echo the version
message(STATUS "Version: ${PROJECT_VERSION}") message(STATUS "Version: ${PROJECT_VERSION}")
set(CMAKE_CXX_STANDARD 17) # Compiler flags:
# Disabled deprecated warnings as the MySQL includes have deprecated code in them.
if(WIN32) # Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
add_compile_definitions(_CRT_SECURE_NO_WARNINGS) # Disabled no-register
endif(WIN32) # Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
if(UNIX)
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC")
endif()
if (__dynamic)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
endif()
if (__ggdb)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
elseif(MSVC)
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
add_compile_options("/wd4267")
elseif(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()
# Our output dir # Our output dir
set(CMAKE_BINARY_DIR ${PROJECT_BINARY_DIR}) set(CMAKE_BINARY_DIR ${PROJECT_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
# Create a /res directory # Create a /res directory
make_directory(${CMAKE_BINARY_DIR}/res) make_directory(${CMAKE_BINARY_DIR}/res)
@ -107,351 +84,172 @@ make_directory(${CMAKE_BINARY_DIR}/locale)
make_directory(${CMAKE_BINARY_DIR}/logs) make_directory(${CMAKE_BINARY_DIR}/logs)
# Copy ini files on first build # Copy ini files on first build
if (NOT EXISTS ${PROJECT_BINARY_DIR}/authconfig.ini) set(INI_FILES "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini")
configure_file( foreach(ini ${INI_FILES})
${CMAKE_SOURCE_DIR}/resources/authconfig.ini ${PROJECT_BINARY_DIR}/authconfig.ini if (NOT EXISTS ${PROJECT_BINARY_DIR}/${ini})
COPYONLY configure_file(
) ${CMAKE_SOURCE_DIR}/resources/${ini} ${PROJECT_BINARY_DIR}/${ini}
endif() COPYONLY
if (NOT EXISTS ${PROJECT_BINARY_DIR}/chatconfig.ini) )
configure_file( endif()
${CMAKE_SOURCE_DIR}/resources/chatconfig.ini ${PROJECT_BINARY_DIR}/chatconfig.ini endforeach()
COPYONLY
) # Copy vanity files on first build
endif() set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
if (NOT EXISTS ${PROJECT_BINARY_DIR}/worldconfig.ini) foreach(file ${VANITY_FILES})
configure_file( configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
${CMAKE_SOURCE_DIR}/resources/worldconfig.ini ${PROJECT_BINARY_DIR}/worldconfig.ini endforeach()
COPYONLY
) # Move our migrations for MasterServer to run
endif() file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/)
if (NOT EXISTS ${PROJECT_BINARY_DIR}/masterconfig.ini) file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
configure_file( foreach(file ${SQL_FILES})
${CMAKE_SOURCE_DIR}/resources/masterconfig.ini ${PROJECT_BINARY_DIR}/masterconfig.ini get_filename_component(file ${file} NAME)
COPYONLY if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/${file})
) configure_file(
${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/${file}
COPYONLY
)
endif()
endforeach()
# Create our list of include directories
set(INCLUDED_DIRECTORIES
"dCommon"
"dChatFilter"
"dGame"
"dGame/dBehaviors"
"dGame/dComponents"
"dGame/dGameMessages"
"dGame/dInventory"
"dGame/dMission"
"dGame/dEntity"
"dGame/dUtilities"
"dPhysics"
"dZoneManager"
"dDatabase"
"dDatabase/Tables"
"dNet"
"dScripts"
"thirdparty/raknet/Source"
"thirdparty/tinyxml2"
"thirdparty/recastnavigation/Recast/Include"
"thirdparty/recastnavigation/Detour/Include"
"thirdparty/SQLite"
"thirdparty/cpplinq"
)
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
if (APPLE)
include_directories("/usr/local/include/")
endif() endif()
# Copy files to output if (WIN32)
configure_file("${CMAKE_SOURCE_DIR}/vanity/CREDITS.md" "${CMAKE_BINARY_DIR}/vanity/CREDITS.md" COPYONLY) set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include")
configure_file("${CMAKE_SOURCE_DIR}/vanity/INFO.md" "${CMAKE_BINARY_DIR}/vanity/INFO.md" COPYONLY) elseif (UNIX)
configure_file("${CMAKE_SOURCE_DIR}/vanity/TESTAMENT.md" "${CMAKE_BINARY_DIR}/vanity/TESTAMENT.md" COPYONLY) set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt")
configure_file("${CMAKE_SOURCE_DIR}/vanity/NPC.xml" "${CMAKE_BINARY_DIR}/vanity/NPC.xml" COPYONLY) set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt")
endif()
# 3rdparty includes
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/raknet/Source/)
if(APPLE)
include_directories(/usr/local/include/)
endif(APPLE)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Recast/Include)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Detour/Include)
include_directories(${ZLIB_INCLUDE_DIRS}) include_directories(${ZLIB_INCLUDE_DIRS})
# Add binary directory as an include directory
# Bcrypt
if (NOT WIN32)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt)
else ()
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include)
endif ()
# Our includes
include_directories(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_BINARY_DIR})
include_directories(${PROJECT_SOURCE_DIR}/dChatFilter/)
include_directories(${PROJECT_SOURCE_DIR}/dCommon/)
include_directories(${PROJECT_SOURCE_DIR}/dGame/)
include_directories(${PROJECT_SOURCE_DIR}/dGame/dBehaviors)
include_directories(${PROJECT_SOURCE_DIR}/dGame/dComponents)
include_directories(${PROJECT_SOURCE_DIR}/dGame/dGameMessages)
include_directories(${PROJECT_SOURCE_DIR}/dGame/dInventory)
include_directories(${PROJECT_SOURCE_DIR}/dGame/dMission)
include_directories(${PROJECT_SOURCE_DIR}/dGame/dEntity)
include_directories(${PROJECT_SOURCE_DIR}/dGame/dUtilities)
include_directories(${PROJECT_SOURCE_DIR}/dPhysics/)
include_directories(${PROJECT_SOURCE_DIR}/dZoneManager/)
include_directories(${PROJECT_SOURCE_DIR}/dDatabase/)
include_directories(${PROJECT_SOURCE_DIR}/dDatabase/Tables/)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/SQLite/)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/cpplinq/)
include_directories(${PROJECT_SOURCE_DIR}/dNet/)
include_directories(${PROJECT_SOURCE_DIR}/dScripts/)
# Lib folders: # Actually include the directories from our list
foreach (dir ${INCLUDED_DIRECTORIES})
include_directories(${PROJECT_SOURCE_DIR}/${dir})
endforeach()
# Add linking directories:
link_directories(${PROJECT_BINARY_DIR}) link_directories(${PROJECT_BINARY_DIR})
# Third-Party libraries # Load all of our third party directories
add_subdirectory(thirdparty) add_subdirectory(thirdparty)
# Source Code # Glob together all headers that need to be precompiled
file( file(
GLOB SOURCES GLOB HEADERS_DDATABASE
LIST_DIRECTORIES false LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" ${PROJECT_SOURCE_DIR}/dDatabase/*.h
${PROJECT_SOURCE_DIR}/dWorldServer/*.cpp ${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
) ${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
# Source Code for AuthServer
file(
GLOB SOURCES_AUTH
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dAuthServer/*.cpp
)
# Source Code for MasterServer
file(
GLOB SOURCES_MASTER
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dMasterServer/*.cpp
)
# Source Code for ChatServer
file(
GLOB SOURCES_CHAT
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dChatServer/*.cpp
)
# Source Code for dCommon
file(
GLOB SOURCES_DCOMMON
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dCommon/*.cpp
)
# Source Code for dChatFilter
file(
GLOB SOURCES_DCHATFILTER
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dChatFilter/*.cpp
)
# Source Code for dDatabase
file(
GLOB SOURCES_DDATABASE
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dDatabase/*.cpp
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.cpp
) )
file( file(
GLOB HEADERS_DDATABASE GLOB HEADERS_DZONEMANAGER
LIST_DIRECTORIES false LIST_DIRECTORIES false
${PROJECT_SOURCE_DIR}/dDatabase/*.h ${PROJECT_SOURCE_DIR}/dZoneManager/*.h
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
) )
file( file(
GLOB HEADERS_DZONEMANAGER GLOB HEADERS_DCOMMON
LIST_DIRECTORIES false LIST_DIRECTORIES false
${PROJECT_SOURCE_DIR}/dZoneManager/*.h ${PROJECT_SOURCE_DIR}/dCommon/*.h
) )
file( file(
GLOB HEADERS_DCOMMON GLOB HEADERS_DGAME
LIST_DIRECTORIES false LIST_DIRECTORIES false
${PROJECT_SOURCE_DIR}/dCommon/*.h ${PROJECT_SOURCE_DIR}/dGame/Entity.h
${PROJECT_SOURCE_DIR}/dGame/dGameMessages/GameMessages.h
${PROJECT_SOURCE_DIR}/dGame/EntityManager.h
${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h
) )
file( # Add our library subdirectories for creation of the library object
GLOB HEADERS_DGAME add_subdirectory(dCommon)
LIST_DIRECTORIES false add_subdirectory(dDatabase)
${PROJECT_SOURCE_DIR}/dGame/Entity.h add_subdirectory(dChatFilter)
${PROJECT_SOURCE_DIR}/dGame/dGameMessages/GameMessages.h add_subdirectory(dNet)
${PROJECT_SOURCE_DIR}/dGame/EntityManager.h add_subdirectory(dScripts) # Add for dGame to use
${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h add_subdirectory(dGame)
) add_subdirectory(dZoneManager)
add_subdirectory(dPhysics)
# Source Code for dNet # Create a list of common libraries shared between all binaries
file( set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp")
GLOB SOURCES_DNET
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dNet/*.cpp
)
# Source Code for dGame # Add platform specific common libraries
file( if (UNIX)
GLOB SOURCES_DGAME set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread")
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dGame/*.cpp
${PROJECT_SOURCE_DIR}/dGame/dBehaviors/*.cpp
${PROJECT_SOURCE_DIR}/dGame/dComponents/*.cpp
${PROJECT_SOURCE_DIR}/dGame/dGameMessages/*.cpp
${PROJECT_SOURCE_DIR}/dGame/dInventory/*.cpp
${PROJECT_SOURCE_DIR}/dGame/dMission/*.cpp
${PROJECT_SOURCE_DIR}/dGame/dEntity/*.cpp
${PROJECT_SOURCE_DIR}/dGame/dUtilities/*.cpp
${PROJECT_SOURCE_DIR}/dScripts/*.cpp
)
# Source Code for dZoneManager if (NOT APPLE AND __include_backtrace__)
file( set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace")
GLOB SOURCES_DZM endif()
LIST_DIRECTORIES false endif()
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dZoneManager/*.cpp
)
# Source Code for dPhysics add_subdirectory(tests)
file(
GLOB SOURCES_DPHYSICS
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${PROJECT_SOURCE_DIR}/dPhysics/*.cpp
)
if(MSVC) # Include all of our binary directories
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now add_subdirectory(dWorldServer)
add_compile_options("/wd4267") add_subdirectory(dAuthServer)
endif(MSVC) add_subdirectory(dChatServer)
add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries
# Our static libraries: # Add our precompiled headers
add_library(dCommon ${SOURCES_DCOMMON})
add_library(dChatFilter ${SOURCES_DCHATFILTER})
add_library(dDatabase ${SOURCES_DDATABASE})
add_library(dNet ${SOURCES_DNET})
add_library(dGame ${SOURCES_DGAME})
add_library(dZoneManager ${SOURCES_DZM})
add_library(dPhysics ${SOURCES_DPHYSICS})
target_link_libraries(dDatabase sqlite3)
target_link_libraries(dNet dCommon) #Needed because otherwise linker errors occur.
target_link_libraries(dCommon ZLIB::ZLIB)
target_link_libraries(dCommon libbcrypt)
target_link_libraries(dDatabase mariadbConnCpp)
target_link_libraries(dNet dDatabase)
target_link_libraries(dGame dDatabase)
target_link_libraries(dChatFilter dDatabase)
if(WIN32)
target_link_libraries(raknet ws2_32)
endif(WIN32)
# Our executables:
add_executable(WorldServer ${SOURCES})
add_executable(AuthServer ${SOURCES_AUTH})
add_executable(MasterServer ${SOURCES_MASTER})
add_executable(ChatServer ${SOURCES_CHAT})
# Add out precompiled headers
target_precompile_headers( target_precompile_headers(
dGame PRIVATE dGame PRIVATE
${HEADERS_DGAME} ${HEADERS_DGAME}
) )
target_precompile_headers( target_precompile_headers(
dZoneManager PRIVATE dZoneManager PRIVATE
${HEADERS_DZONEMANAGER} ${HEADERS_DZONEMANAGER}
) )
# Need to specify to use the CXX compiler language here or else we get errors including <string>. # Need to specify to use the CXX compiler language here or else we get errors including <string>.
target_precompile_headers( target_precompile_headers(
dDatabase PRIVATE dDatabase PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:${HEADERS_DDATABASE}>" "$<$<COMPILE_LANGUAGE:CXX>:${HEADERS_DDATABASE}>"
) )
target_precompile_headers( target_precompile_headers(
dCommon PRIVATE dCommon PRIVATE
${HEADERS_DCOMMON} ${HEADERS_DCOMMON}
) )
target_precompile_headers( target_precompile_headers(
tinyxml2 PRIVATE tinyxml2 PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>" "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
) )
# Target libraries to link to:
target_link_libraries(WorldServer dCommon)
target_link_libraries(WorldServer dChatFilter)
target_link_libraries(WorldServer dDatabase)
target_link_libraries(WorldServer dNet)
target_link_libraries(WorldServer dGame)
target_link_libraries(WorldServer dZoneManager)
target_link_libraries(WorldServer dPhysics)
target_link_libraries(WorldServer detour)
target_link_libraries(WorldServer recast)
target_link_libraries(WorldServer raknet)
target_link_libraries(WorldServer mariadbConnCpp)
if(UNIX)
if(NOT APPLE AND __include_backtrace__)
target_link_libraries(WorldServer backtrace)
target_link_libraries(MasterServer backtrace)
target_link_libraries(AuthServer backtrace)
target_link_libraries(ChatServer backtrace)
endif()
endif(UNIX)
target_link_libraries(WorldServer tinyxml2)
# Target libraries for Auth:
target_link_libraries(AuthServer dCommon)
target_link_libraries(AuthServer dDatabase)
target_link_libraries(AuthServer dNet)
target_link_libraries(AuthServer raknet)
target_link_libraries(AuthServer mariadbConnCpp)
if(UNIX)
target_link_libraries(AuthServer pthread)
target_link_libraries(AuthServer dl)
endif(UNIX)
# Target libraries for Master:
target_link_libraries(MasterServer dCommon)
target_link_libraries(MasterServer dDatabase)
target_link_libraries(MasterServer dNet)
target_link_libraries(MasterServer raknet)
target_link_libraries(MasterServer mariadbConnCpp)
if(UNIX)
target_link_libraries(MasterServer pthread)
target_link_libraries(MasterServer dl)
endif(UNIX)
# Target libraries for Chat:
target_link_libraries(ChatServer dCommon)
target_link_libraries(ChatServer dChatFilter)
target_link_libraries(ChatServer dDatabase)
target_link_libraries(ChatServer dNet)
target_link_libraries(ChatServer raknet)
target_link_libraries(ChatServer mariadbConnCpp)
if(UNIX)
target_link_libraries(ChatServer pthread)
target_link_libraries(ChatServer dl)
endif(UNIX)
# Compiler flags:
# Disabled deprecated warnings as the MySQL includes have deprecated code in them.
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
# Disabled no-register
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
if(UNIX)
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC")
endif()
if (__dynamic)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
endif()
if (__ggdb)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
endif(UNIX)
if(WIN32)
add_dependencies(MasterServer WorldServer)
add_dependencies(MasterServer AuthServer)
add_dependencies(MasterServer ChatServer)
endif()
# Finally, add the tests
add_subdirectory(tests)

View File

@ -19,6 +19,15 @@
"description": "Same as default, Used in GitHub actions workflow", "description": "Same as default, Used in GitHub actions workflow",
"inherits": "default" "inherits": "default"
}, },
{
"name": "ci-macos-11",
"displayName": "CI configure step for MacOS",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "default",
"cacheVariables": {
"OPENSSL_ROOT_DIR": "/usr/local/Cellar/openssl@3/3.0.5/"
}
},
{ {
"name": "ci-windows-2022", "name": "ci-windows-2022",
"displayName": "CI configure step for Windows", "displayName": "CI configure step for Windows",
@ -66,6 +75,13 @@
"displayName": "Linux CI Build", "displayName": "Linux CI Build",
"description": "This preset is used by the CI build on linux", "description": "This preset is used by the CI build on linux",
"jobs": 2 "jobs": 2
},
{
"name": "ci-macos-11",
"configurePreset": "ci-macos-11",
"displayName": "MacOS CI Build",
"description": "This preset is used by the CI build on MacOS",
"jobs": 2
} }
], ],
"testPresets": [ "testPresets": [
@ -81,6 +97,18 @@
"outputOnFailure": true "outputOnFailure": true
} }
}, },
{
"name": "ci-macos-11",
"configurePreset": "ci-macos-11",
"displayName": "CI Tests on MacOS",
"description": "Runs all tests on a Mac configuration",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
}
},
{ {
"name": "ci-windows-2022", "name": "ci-windows-2022",
"configurePreset": "ci-windows-2022", "configurePreset": "ci-windows-2022",

View File

@ -1,6 +1,6 @@
PROJECT_VERSION_MAJOR=1 PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=0 PROJECT_VERSION_MINOR=0
PROJECT_VERSION_PATCH=2 PROJECT_VERSION_PATCH=3
# LICENSE # LICENSE
LICENSE=AGPL-3.0 LICENSE=AGPL-3.0
# The network version. # The network version.

View File

@ -152,13 +152,6 @@ now follow the build section for your system
## Setting up the environment ## Setting up the environment
### Database
Darkflame Universe utilizes a MySQL/MariaDB database for account and character information.
Initial setup can vary drastically based on which operating system or distribution you are running; there are instructions out there for most setups, follow those and come back here when you have a database up and running.
* Create a database for Darkflame Universe to use
* Run each SQL file in the order at which they appear [here](migrations/dlu/) on the database
### Resources ### Resources
**LEGO® Universe 1.10.64** **LEGO® Universe 1.10.64**
@ -202,6 +195,13 @@ certutil -hashfile <file> SHA256
* Move and rename `cdclient.sqlite` into `build/res/CDServer.sqlite` * Move and rename `cdclient.sqlite` into `build/res/CDServer.sqlite`
* Run each SQL file in the order at which they appear [here](migrations/cdserver/) on the SQLite database * Run each SQL file in the order at which they appear [here](migrations/cdserver/) on the SQLite database
### Database
Darkflame Universe utilizes a MySQL/MariaDB database for account and character information.
Initial setup can vary drastically based on which operating system or distribution you are running; there are instructions out there for most setups, follow those and come back here when you have a database up and running.
* Create a database for Darkflame Universe to use
* Use the command `./MasterServer -m` to automatically run them.
**Configuration** **Configuration**
After the server has been built there should be four `ini` files in the build director: `authconfig.ini`, `chatconfig.ini`, `masterconfig.ini`, and `worldconfig.ini`. Go through them and fill in the database credentials and configure other settings if necessary. After the server has been built there should be four `ini` files in the build director: `authconfig.ini`, `chatconfig.ini`, `masterconfig.ini`, and `worldconfig.ini`. Go through them and fill in the database credentials and configure other settings if necessary.

View File

@ -0,0 +1,4 @@
set(DAUTHSERVER_SOURCES "AuthServer.cpp")
add_executable(AuthServer ${DAUTHSERVER_SOURCES})
target_link_libraries(AuthServer ${COMMON_LIBRARIES})

View File

@ -0,0 +1,4 @@
set(DCHATFILTER_SOURCES "dChatFilter.cpp")
add_library(dChatFilter STATIC ${DCHATFILTER_SOURCES})
target_link_libraries(dChatFilter dDatabase)

View File

@ -0,0 +1,6 @@
set(DCHATSERVER_SOURCES "ChatPacketHandler.cpp"
"ChatServer.cpp"
"PlayerContainer.cpp")
add_executable(ChatServer ${DCHATSERVER_SOURCES})
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter)

View File

@ -8,6 +8,10 @@
#include "dServer.h" #include "dServer.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "dLogger.h" #include "dLogger.h"
#include "AddFriendResponseCode.h"
#include "AddFriendResponseType.h"
#include "RakString.h"
#include "dConfig.h"
extern PlayerContainer playerContainer; extern PlayerContainer playerContainer;
@ -21,44 +25,41 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
auto player = playerContainer.GetPlayerData(playerID); auto player = playerContainer.GetPlayerData(playerID);
if (!player) return; if (!player) return;
//Get our friends list from the Db: //Get our friends list from the Db. Using a derived table since the friend of a player can be in either column.
auto stmt = Database::CreatePreppedStmt("SELECT * FROM friends WHERE player_id = ? OR friend_id = ?"); std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt(
stmt->setUInt64(1, playerID); "SELECT fr.requested_player, best_friend, ci.name FROM "
stmt->setUInt64(2, playerID); "(SELECT CASE "
"WHEN player_id = ? THEN friend_id "
"WHEN friend_id = ? THEN player_id "
"END AS requested_player, best_friend FROM friends) AS fr "
"JOIN charinfo AS ci ON ci.id = fr.requested_player "
"WHERE fr.requested_player IS NOT NULL;"));
stmt->setUInt(1, static_cast<uint32_t>(playerID));
stmt->setUInt(2, static_cast<uint32_t>(playerID));
std::vector<FriendData> friends; std::vector<FriendData> friends;
auto res = stmt->executeQuery(); std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
while (res->next()) { while (res->next()) {
FriendData fd; FriendData fd;
fd.isFTP = false; // not a thing in DLU fd.isFTP = false; // not a thing in DLU
fd.friendID = res->getInt64(1); fd.friendID = res->getUInt(1);
if (fd.friendID == playerID) fd.friendID = res->getUInt64(2); GeneralUtils::SetBit(fd.friendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_PERSISTENT));
GeneralUtils::SetBit(fd.friendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_CHARACTER));
fd.isBestFriend = res->getInt(3) == 2; //0 = friends, 1 = requested, 2 = bffs fd.isBestFriend = res->getInt(2) == 3; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
if (fd.isBestFriend) player->countOfBestFriends+=1;
//We need to find their name as well: fd.friendName = res->getString(3);
{
auto stmt = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE id=? limit 1");
stmt->setInt(1, fd.friendID);
auto res = stmt->executeQuery();
while (res->next()) {
fd.friendName = res->getString(1);
}
delete res;
delete stmt;
}
//Now check if they're online: //Now check if they're online:
auto fr = playerContainer.GetPlayerData(fd.friendID); auto fr = playerContainer.GetPlayerData(fd.friendID);
if (fr) { if (fr) {
fd.isOnline = true; fd.isOnline = true;
fd.zoneID = fr->zoneID; fd.zoneID = fr->zoneID;
//Since this friend is online, we need to update them on the fact that we've just logged in: //Since this friend is online, we need to update them on the fact that we've just logged in:
SendFriendUpdate(fr, player, 1); SendFriendUpdate(fr, player, 1, fd.isBestFriend);
} }
else { else {
fd.isOnline = false; fd.isOnline = false;
@ -68,9 +69,6 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
friends.push_back(fd); friends.push_back(fd);
} }
delete res;
delete stmt;
//Now, we need to send the friendlist to the server they came from: //Now, we need to send the friendlist to the server they came from:
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER); PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER);
@ -93,20 +91,150 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
} }
void ChatPacketHandler::HandleFriendRequest(Packet* packet) { void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
auto maxNumberOfBestFriendsAsString = Game::config->GetValue("max_number_of_best_friends");
// If this config option doesn't exist, default to 5 which is what live used.
auto maxNumberOfBestFriends = maxNumberOfBestFriendsAsString != "" ? std::stoi(maxNumberOfBestFriendsAsString) : 5U;
CINSTREAM; CINSTREAM;
LWOOBJID playerID; LWOOBJID requestorPlayerID;
inStream.Read(playerID); inStream.Read(requestorPlayerID);
inStream.Read(playerID); inStream.Read(requestorPlayerID);
std::string playerName = PacketUtils::ReadString(0x14, packet, true); uint32_t spacing{};
//There's another bool here to determine if it's a best friend request, but we're not handling it right now. inStream.Read(spacing);
std::string playerName = "";
uint16_t character;
bool noMoreLettersInName = false;
//PacketUtils::SavePacket("FriendRequest.bin", (char*)inStream.GetData(), inStream.GetNumberOfBytesUsed()); for (uint32_t j = 0; j < 33; j++) {
inStream.Read(character);
//We need to check to see if the player is actually online or not: if (character == '\0') noMoreLettersInName = true;
auto targetData = playerContainer.GetPlayerData(playerName); if (!noMoreLettersInName) playerName.push_back(static_cast<char>(character));
if (targetData) {
SendFriendRequest(targetData, playerContainer.GetPlayerData(playerID));
} }
char isBestFriendRequest{};
inStream.Read(isBestFriendRequest);
auto requestor = playerContainer.GetPlayerData(requestorPlayerID);
std::unique_ptr<PlayerData> requestee(playerContainer.GetPlayerData(playerName));
// Check if player is online first
if (isBestFriendRequest && !requestee) {
for (auto friendDataCandidate : requestor->friends) {
if (friendDataCandidate.friendName == playerName) {
requestee.reset(new PlayerData());
// Setup the needed info since you can add a best friend offline.
requestee->playerID = friendDataCandidate.friendID;
requestee->playerName = friendDataCandidate.friendName;
requestee->zoneID = LWOZONEID();
FriendData requesteeFriendData{};
requesteeFriendData.friendID = requestor->playerID;
requesteeFriendData.friendName = requestor->playerName;
requesteeFriendData.isFTP = false;
requesteeFriendData.isOnline = false;
requesteeFriendData.zoneID = requestor->zoneID;
requestee->friends.push_back(requesteeFriendData);
requestee->sysAddr = UNASSIGNED_SYSTEM_ADDRESS;
break;
}
}
}
// If at this point we dont have a target, then they arent online and we cant send the request.
// Send the response code that corresponds to what the error is.
if (!requestee) {
std::unique_ptr<sql::PreparedStatement> nameQuery(Database::CreatePreppedStmt("SELECT name from charinfo where name = ?;"));
nameQuery->setString(1, playerName);
std::unique_ptr<sql::ResultSet> result(nameQuery->executeQuery());
requestee.reset(new PlayerData());
requestee->playerName = playerName;
SendFriendResponse(requestor, requestee.get(), result->next() ? AddFriendResponseType::NOTONLINE : AddFriendResponseType::INVALIDCHARACTER);
return;
}
if (isBestFriendRequest) {
std::unique_ptr<sql::PreparedStatement> friendUpdate(Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
friendUpdate->setUInt(1, static_cast<uint32_t>(requestorPlayerID));
friendUpdate->setUInt(2, static_cast<uint32_t>(requestee->playerID));
friendUpdate->setUInt(3, static_cast<uint32_t>(requestee->playerID));
friendUpdate->setUInt(4, static_cast<uint32_t>(requestorPlayerID));
std::unique_ptr<sql::ResultSet> result(friendUpdate->executeQuery());
LWOOBJID queryPlayerID = LWOOBJID_EMPTY;
LWOOBJID queryFriendID = LWOOBJID_EMPTY;
uint8_t oldBestFriendStatus{};
uint8_t bestFriendStatus{};
if (result->next()) {
// Get the IDs
queryPlayerID = result->getInt(1);
queryFriendID = result->getInt(2);
oldBestFriendStatus = result->getInt(3);
bestFriendStatus = oldBestFriendStatus;
// Set the bits
GeneralUtils::SetBit(queryPlayerID, static_cast<size_t>(eObjectBits::OBJECT_BIT_CHARACTER));
GeneralUtils::SetBit(queryPlayerID, static_cast<size_t>(eObjectBits::OBJECT_BIT_PERSISTENT));
GeneralUtils::SetBit(queryFriendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_CHARACTER));
GeneralUtils::SetBit(queryFriendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_PERSISTENT));
// Since this player can either be the friend of someone else or be friends with someone else
// their column in the database determines what bit gets set. When the value hits 3, they
// are now best friends with the other player.
if (queryPlayerID == requestorPlayerID) {
bestFriendStatus |= 1ULL << 0;
} else {
bestFriendStatus |= 1ULL << 1;
}
}
// Only do updates if there was a change in the bff status.
if (oldBestFriendStatus != bestFriendStatus) {
if (requestee->countOfBestFriends >= maxNumberOfBestFriends || requestor->countOfBestFriends >= maxNumberOfBestFriends) {
if (requestee->countOfBestFriends >= maxNumberOfBestFriends) {
SendFriendResponse(requestor, requestee.get(), AddFriendResponseType::THEIRFRIENDLISTFULL, false);
}
if (requestor->countOfBestFriends >= maxNumberOfBestFriends) {
SendFriendResponse(requestor, requestee.get(), AddFriendResponseType::YOURFRIENDSLISTFULL, false);
}
} else {
// Then update the database with this new info.
std::unique_ptr<sql::PreparedStatement> updateQuery(Database::CreatePreppedStmt("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
updateQuery->setUInt(1, bestFriendStatus);
updateQuery->setUInt(2, static_cast<uint32_t>(requestorPlayerID));
updateQuery->setUInt(3, static_cast<uint32_t>(requestee->playerID));
updateQuery->setUInt(4, static_cast<uint32_t>(requestee->playerID));
updateQuery->setUInt(5, static_cast<uint32_t>(requestorPlayerID));
updateQuery->executeUpdate();
// Sent the best friend update here if the value is 3
if (bestFriendStatus == 3U) {
requestee->countOfBestFriends+=1;
requestor->countOfBestFriends+=1;
if (requestee->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee.get(), requestor, AddFriendResponseType::ACCEPTED, false, true);
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), AddFriendResponseType::ACCEPTED, false, true);
for (auto& friendData : requestor->friends) {
if (friendData.friendID == requestee->playerID) {
friendData.isBestFriend = true;
}
}
for (auto& friendData : requestee->friends) {
if (friendData.friendID == requestor->playerID) {
friendData.isBestFriend = true;
}
}
}
}
} else {
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), AddFriendResponseType::WAITINGAPPROVAL, true, true);
}
} else {
// Do not send this if we are requesting to be a best friend.
SendFriendRequest(requestee.get(), requestor);
}
// If the player is actually a player and not a ghost one defined above, release it from being deleted.
if (requestee->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) requestee.release();
} }
void ChatPacketHandler::HandleFriendResponse(Packet* packet) { void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
@ -114,30 +242,75 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
LWOOBJID playerID; LWOOBJID playerID;
inStream.Read(playerID); inStream.Read(playerID);
inStream.Read(playerID); inStream.Read(playerID);
uint8_t responseCode = packet->data[0x14]; AddFriendResponseCode clientResponseCode = static_cast<AddFriendResponseCode>(packet->data[0x14]);
std::string friendName = PacketUtils::ReadString(0x15, packet, true); std::string friendName = PacketUtils::ReadString(0x15, packet, true);
Game::logger->Log("ChatPacketHandler", "Friend response code: %i\n", responseCode);
if (responseCode != 0) return; //If we're not accepting the request, end here, do not insert to friends table.
PacketUtils::SavePacket("HandleFriendResponse.bin", (char*)inStream.GetData(), inStream.GetNumberOfBytesUsed());
//Now to try and find both of these: //Now to try and find both of these:
auto goonA = playerContainer.GetPlayerData(playerID); auto requestor = playerContainer.GetPlayerData(playerID);
auto goonB = playerContainer.GetPlayerData(friendName); auto requestee = playerContainer.GetPlayerData(friendName);
if (!goonA || !goonB) return; if (!requestor || !requestee) return;
SendFriendResponse(goonB, goonA, responseCode); AddFriendResponseType serverResponseCode{};
SendFriendResponse(goonA, goonB, responseCode); //Do we need to send it to both? I think so so both get the updated friendlist but... idk. uint8_t isAlreadyBestFriends = 0U;
// We need to convert this response code to one we can actually send back to the client.
switch (clientResponseCode) {
case AddFriendResponseCode::ACCEPTED:
serverResponseCode = AddFriendResponseType::ACCEPTED;
break;
case AddFriendResponseCode::BUSY:
serverResponseCode = AddFriendResponseType::BUSY;
break;
case AddFriendResponseCode::CANCELLED:
serverResponseCode = AddFriendResponseType::CANCELLED;
break;
case AddFriendResponseCode::REJECTED:
serverResponseCode = AddFriendResponseType::DECLINED;
break;
}
auto stmt = Database::CreatePreppedStmt("INSERT INTO `friends`(`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?)"); // Now that we have handled the base cases, we need to check the other cases.
stmt->setUInt64(1, goonA->playerID); if (serverResponseCode == AddFriendResponseType::ACCEPTED) {
stmt->setUInt64(2, goonB->playerID); for (auto friendData : requestor->friends) {
stmt->setInt(3, 0); if (friendData.friendID == requestee->playerID) {
stmt->execute(); serverResponseCode = AddFriendResponseType::ALREADYFRIEND;
delete stmt; if (friendData.isBestFriend) {
isAlreadyBestFriends = 1U;
}
}
}
}
// This message is NOT sent for best friends and is handled differently for those requests.
if (serverResponseCode == AddFriendResponseType::ACCEPTED) {
// Add the each player to the others friend list.
FriendData requestorData;
requestorData.zoneID = requestor->zoneID;
requestorData.friendID = requestor->playerID;
requestorData.friendName = requestor->playerName;
requestorData.isBestFriend = false;
requestorData.isFTP = false;
requestorData.isOnline = true;
requestee->friends.push_back(requestorData);
FriendData requesteeData;
requesteeData.zoneID = requestee->zoneID;
requesteeData.friendID = requestee->playerID;
requesteeData.friendName = requestee->playerName;
requesteeData.isBestFriend = false;
requesteeData.isFTP = false;
requesteeData.isOnline = true;
requestor->friends.push_back(requesteeData);
std::unique_ptr<sql::PreparedStatement> statement(Database::CreatePreppedStmt("INSERT IGNORE INTO `friends` (`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?);"));
statement->setUInt(1, static_cast<uint32_t>(requestor->playerID));
statement->setUInt(2, static_cast<uint32_t>(requestee->playerID));
statement->setInt(3, 0);
statement->execute();
}
if (serverResponseCode != AddFriendResponseType::DECLINED) SendFriendResponse(requestor, requestee, serverResponseCode, isAlreadyBestFriends);
if (serverResponseCode != AddFriendResponseType::ALREADYFRIEND) SendFriendResponse(requestee, requestor, serverResponseCode, isAlreadyBestFriends);
} }
void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
@ -145,50 +318,55 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
LWOOBJID playerID; LWOOBJID playerID;
inStream.Read(playerID); inStream.Read(playerID);
inStream.Read(playerID); inStream.Read(playerID);
std::string friendName = PacketUtils::ReadString(16, packet, true); std::string friendName = PacketUtils::ReadString(0x14, packet, true);
//we'll have to query the db here to find the user, since you can delete them while they're offline. //we'll have to query the db here to find the user, since you can delete them while they're offline.
//First, we need to find their ID: //First, we need to find their ID:
auto stmt = Database::CreatePreppedStmt("select id from charinfo where name=? limit 1;"); std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? LIMIT 1;"));
stmt->setString(1, friendName.c_str()); stmt->setString(1, friendName.c_str());
LWOOBJID friendID = 0; LWOOBJID friendID = 0;
auto res = stmt->executeQuery(); std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
while (res->next()) { while (res->next()) {
friendID = res->getUInt64(1); friendID = res->getUInt(1);
} }
delete res; // Convert friendID to LWOOBJID
delete stmt; GeneralUtils::SetBit(friendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_PERSISTENT));
GeneralUtils::SetBit(friendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_CHARACTER));
//Set our bits to convert to the BIG BOY objectID. std::unique_ptr<sql::PreparedStatement> deletestmt(Database::CreatePreppedStmt("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
friendID = GeneralUtils::ClearBit(friendID, OBJECT_BIT_CHARACTER); deletestmt->setUInt(1, static_cast<uint32_t>(playerID));
friendID = GeneralUtils::ClearBit(friendID, OBJECT_BIT_PERSISTENT); deletestmt->setUInt(2, static_cast<uint32_t>(friendID));
deletestmt->setUInt(3, static_cast<uint32_t>(friendID));
//YEET: deletestmt->setUInt(4, static_cast<uint32_t>(playerID));
auto deletestmt = Database::CreatePreppedStmt("DELETE FROM `friends` WHERE player_id=? AND friend_id=? LIMIT 1");
deletestmt->setUInt64(1, playerID);
deletestmt->setUInt64(2, friendID);
deletestmt->execute(); deletestmt->execute();
delete deletestmt;
//because I'm lazy and they can be reversed:
{
auto deletestmt = Database::CreatePreppedStmt("DELETE FROM `friends` WHERE player_id=? AND friend_id=? LIMIT 1");
deletestmt->setUInt64(1, friendID);
deletestmt->setUInt64(2, playerID);
deletestmt->execute();
delete deletestmt;
}
//Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended: //Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended:
auto goonA = playerContainer.GetPlayerData(playerID); auto goonA = playerContainer.GetPlayerData(playerID);
if (goonA) { if (goonA) {
// Remove the friend from our list of friends
for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) {
if ((*friendData).friendID == friendID) {
if ((*friendData).isBestFriend) --goonA->countOfBestFriends;
goonA->friends.erase(friendData);
break;
}
}
SendRemoveFriend(goonA, friendName, true); SendRemoveFriend(goonA, friendName, true);
} }
auto goonB = playerContainer.GetPlayerData(friendID); auto goonB = playerContainer.GetPlayerData(friendID);
if (!goonB) return; if (!goonB) return;
// Do it again for other person
for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) {
if ((*friendData).friendID == playerID) {
if ((*friendData).isBestFriend) --goonB->countOfBestFriends;
goonB->friends.erase(friendData);
break;
}
}
std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID)); std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID));
SendRemoveFriend(goonB, goonAName, true); SendRemoveFriend(goonB, goonAName, true);
} }
@ -206,7 +384,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet)
if (playerContainer.GetIsMuted(sender)) return; if (playerContainer.GetIsMuted(sender)) return;
const auto senderName = std::string(sender->playerName.C_String()); const auto senderName = std::string(sender->playerName.c_str());
inStream.SetReadOffset(0x14 * 8); inStream.SetReadOffset(0x14 * 8);
@ -217,8 +395,6 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet)
Game::logger->Log("ChatPacketHandler", "Got a message from (%s) [%d]: %s\n", senderName.c_str(), channel, message.c_str()); Game::logger->Log("ChatPacketHandler", "Got a message from (%s) [%d]: %s\n", senderName.c_str(), channel, message.c_str());
//PacketUtils::SavePacket("chat.bin", reinterpret_cast<char*>(packet->data), packet->length);
if (channel != 8) return; if (channel != 8) return;
auto* team = playerContainer.GetTeam(playerID); auto* team = playerContainer.GetTeam(playerID);
@ -231,7 +407,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet)
if (otherMember == nullptr) return; if (otherMember == nullptr) return;
const auto otherName = std::string(otherMember->playerName.C_String()); const auto otherName = std::string(otherMember->playerName.c_str());
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER); PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER);
@ -267,8 +443,8 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
if (playerContainer.GetIsMuted(goonA)) return; if (playerContainer.GetIsMuted(goonA)) return;
std::string goonAName = goonA->playerName.C_String(); std::string goonAName = goonA->playerName.c_str();
std::string goonBName = goonB->playerName.C_String(); std::string goonBName = goonB->playerName.c_str();
//To the sender: //To the sender:
{ {
@ -454,8 +630,6 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet)
playerContainer.RemoveMember(team, kickedId, false, true, false); playerContainer.RemoveMember(team, kickedId, false, true, false);
} }
//PacketUtils::SavePacket("kick.bin", reinterpret_cast<char*>(packet->data), packet->length);
} }
void ChatPacketHandler::HandleTeamPromote(Packet* packet) void ChatPacketHandler::HandleTeamPromote(Packet* packet)
@ -481,8 +655,6 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet)
playerContainer.PromoteMember(team, promoted->playerID); playerContainer.PromoteMember(team, promoted->playerID);
} }
//PacketUtils::SavePacket("promote.bin", reinterpret_cast<char*>(packet->data), packet->length);
} }
void ChatPacketHandler::HandleTeamLootOption(Packet* packet) void ChatPacketHandler::HandleTeamLootOption(Packet* packet)
@ -509,8 +681,6 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet)
playerContainer.UpdateTeamsOnWorld(team, false); playerContainer.UpdateTeamsOnWorld(team, false);
} }
//PacketUtils::SavePacket("option.bin", reinterpret_cast<char*>(packet->data), packet->length);
} }
void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet)
@ -550,7 +720,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet)
playerContainer.TeamStatusUpdate(team); playerContainer.TeamStatusUpdate(team);
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.C_String())); const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.c_str()));
for (const auto memberId : team->memberIDs) for (const auto memberId : team->memberIDs)
{ {
@ -560,7 +730,6 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet)
const auto memberName = playerContainer.GetName(memberId); const auto memberName = playerContainer.GetName(memberId);
//ChatPacketHandler::SendTeamAddPlayer(otherMember, false, false, false, data->playerID, leaderName, data->zoneID);
if (otherMember != nullptr) if (otherMember != nullptr)
{ {
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID); ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID);
@ -581,7 +750,7 @@ void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender)
//portion that will get routed: //portion that will get routed:
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_TEAM_INVITE); PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_TEAM_INVITE);
PacketUtils::WritePacketWString(sender->playerName.C_String(), 33, &bitStream); PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
bitStream.Write(sender->playerID); bitStream.Write(sender->playerID);
SystemAddress sysAddr = receiver->sysAddr; SystemAddress sysAddr = receiver->sysAddr;
@ -745,7 +914,7 @@ void ChatPacketHandler::SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i
SEND_PACKET; SEND_PACKET;
} }
void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType) { void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend) {
/*chat notification is displayed if log in / out and friend is updated in friends list /*chat notification is displayed if log in / out and friend is updated in friends list
[u8] - update type [u8] - update type
Update types Update types
@ -767,7 +936,7 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_UPDATE_FRIEND_NOTIFY); PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_UPDATE_FRIEND_NOTIFY);
bitStream.Write<uint8_t>(notifyType); bitStream.Write<uint8_t>(notifyType);
std::string playerName = playerData->playerName.C_String(); std::string playerName = playerData->playerName.c_str();
PacketUtils::WritePacketWString(playerName, 33, &bitStream); PacketUtils::WritePacketWString(playerName, 33, &bitStream);
@ -783,19 +952,20 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla
bitStream.Write(playerData->zoneID.GetCloneID()); bitStream.Write(playerData->zoneID.GetCloneID());
} }
bitStream.Write<uint8_t>(0); //isBFF bitStream.Write<uint8_t>(isBestFriend); //isBFF
bitStream.Write<uint8_t>(0); //isFTP bitStream.Write<uint8_t>(0); //isFTP
SystemAddress sysAddr = friendData->sysAddr; SystemAddress sysAddr = friendData->sysAddr;
SEND_PACKET; SEND_PACKET;
} }
void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* sender, bool isBFFReq) { void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* sender) {
if (!receiver || !sender) return; if (!receiver || !sender) return;
//Make sure people aren't requesting people that they're already friends with: //Make sure people aren't requesting people that they're already friends with:
for (auto fr : receiver->friends) { for (auto fr : receiver->friends) {
if (fr.friendID == sender->playerID) { if (fr.friendID == sender->playerID) {
SendFriendResponse(sender, receiver, AddFriendResponseType::ALREADYFRIEND, fr.isBestFriend);
return; //we have this player as a friend, yeet this function so it doesn't send another request. return; //we have this player as a friend, yeet this function so it doesn't send another request.
} }
} }
@ -806,30 +976,34 @@ void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* send
//portion that will get routed: //portion that will get routed:
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_ADD_FRIEND_REQUEST); PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_ADD_FRIEND_REQUEST);
PacketUtils::WritePacketWString(sender->playerName.C_String(), 33, &bitStream); PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
bitStream.Write<uint8_t>(0); bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
SystemAddress sysAddr = receiver->sysAddr; SystemAddress sysAddr = receiver->sysAddr;
SEND_PACKET; SEND_PACKET;
} }
void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sender, uint8_t responseCode) { void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sender, AddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
if (!receiver || !sender) return; if (!receiver || !sender) return;
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER); PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID); bitStream.Write(receiver->playerID);
//portion that will get routed: // Portion that will get routed:
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_ADD_FRIEND_RESPONSE); PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_ADD_FRIEND_RESPONSE);
bitStream.Write<uint8_t>(responseCode); bitStream.Write(responseCode);
bitStream.Write<uint8_t>(1); //isOnline // For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
PacketUtils::WritePacketWString(sender->playerName.C_String(), 33, &bitStream); bitStream.Write<uint8_t>(responseCode != AddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender->sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
bitStream.Write(sender->playerID); // Then write the player name
bitStream.Write(sender->zoneID); PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
bitStream.Write<uint8_t>(0); //isBFF // Then if this is an acceptance code, write the following extra info.
bitStream.Write<uint8_t>(0); //isFTP if (responseCode == AddFriendResponseType::ACCEPTED) {
bitStream.Write(sender->playerID);
bitStream.Write(sender->zoneID);
bitStream.Write(isBestFriendRequest); //isBFF
bitStream.Write<uint8_t>(0); //isFTP
}
SystemAddress sysAddr = receiver->sysAddr; SystemAddress sysAddr = receiver->sysAddr;
SEND_PACKET; SEND_PACKET;
} }

View File

@ -4,6 +4,7 @@
#include "BitStream.h" #include "BitStream.h"
struct PlayerData; struct PlayerData;
enum class AddFriendResponseType : uint8_t;
namespace ChatPacketHandler { namespace ChatPacketHandler {
void HandleFriendlistRequest(Packet* packet); void HandleFriendlistRequest(Packet* packet);
@ -31,10 +32,9 @@ namespace ChatPacketHandler {
void SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID); void SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state. //FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType); void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend);
void SendFriendRequest(PlayerData* receiver, PlayerData* sender, bool isBFFReq = false); void SendFriendRequest(PlayerData* receiver, PlayerData* sender);
void SendFriendResponse(PlayerData* receiver, PlayerData* sender, uint8_t responseCode = 3); void SendFriendResponse(PlayerData* receiver, PlayerData* sender, AddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful); void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful);
}; };

View File

@ -20,17 +20,25 @@ PlayerContainer::~PlayerContainer() {
void PlayerContainer::InsertPlayer(Packet* packet) { void PlayerContainer::InsertPlayer(Packet* packet) {
CINSTREAM; CINSTREAM;
PlayerData* data = new PlayerData(); PlayerData* data = new PlayerData();
inStream.SetReadOffset(inStream.GetReadOffset() + 64);
inStream.Read(data->playerID); inStream.Read(data->playerID);
inStream.Read(data->playerID);
inStream.Read(data->playerName); uint32_t len;
inStream.Read<uint32_t>(len);
for (int i = 0; i < len; i++) {
char character; inStream.Read<char>(character);
data->playerName += character;
}
inStream.Read(data->zoneID); inStream.Read(data->zoneID);
inStream.Read(data->muteExpire); inStream.Read(data->muteExpire);
data->sysAddr = packet->systemAddress; data->sysAddr = packet->systemAddress;
mNames[data->playerID] = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.C_String())); mNames[data->playerID] = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.c_str()));
mPlayers.insert(std::make_pair(data->playerID, data)); mPlayers.insert(std::make_pair(data->playerID, data));
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i\n", data->playerName.C_String(), data->playerID, data->zoneID.GetMapID()); Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i\n", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);"); auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
@ -49,26 +57,22 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
inStream.Read(playerID); inStream.Read(playerID);
//Before they get kicked, we need to also send a message to their friends saying that they disconnected. //Before they get kicked, we need to also send a message to their friends saying that they disconnected.
auto player = this->GetPlayerData(playerID); std::unique_ptr<PlayerData> player(this->GetPlayerData(playerID));
if (player == nullptr) { if (player == nullptr) {
return; return;
} }
for (auto& fr : player->friends) { for (auto& fr : player->friends) {
//if (!fr.isOnline) continue;
auto fd = this->GetPlayerData(fr.friendID); auto fd = this->GetPlayerData(fr.friendID);
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0); if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend);
} }
auto* team = GetTeam(playerID); auto* team = GetTeam(playerID);
if (team != nullptr) if (team != nullptr)
{ {
//TeamStatusUpdate(team); const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(player->playerName.c_str()));
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(player->playerName.C_String()));
for (const auto memberId : team->memberIDs) for (const auto memberId : team->memberIDs)
{ {
@ -77,7 +81,6 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
if (otherMember == nullptr) continue; if (otherMember == nullptr) continue;
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, {0, 0, 0}); ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, {0, 0, 0});
//ChatPacketHandler::SendTeamRemovePlayer(otherMember, false, false, true, false, team->leaderID, player->playerID, memberName);
} }
} }
@ -237,16 +240,10 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID)
if (leader == nullptr || member == nullptr) return; if (leader == nullptr || member == nullptr) return;
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(leader->playerName.C_String())); const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(leader->playerName.c_str()));
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(member->playerName.C_String())); const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(member->playerName.c_str()));
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName); ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName);
/*
ChatPacketHandler::SendTeamAddPlayer(member, false, false, false, leader->playerID, leaderName, leader->zoneID);
Game::logger->Log("PlayerContainer", "Team invite successfully accepted, leader: %s, member: %s\n", leader->playerName.C_String(), member->playerName.C_String());
*/
if (!team->local) if (!team->local)
{ {
@ -348,7 +345,7 @@ void PlayerContainer::DisbandTeam(TeamData* team)
if (otherMember == nullptr) continue; if (otherMember == nullptr) continue;
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(otherMember->playerName.C_String())); const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(otherMember->playerName.c_str()));
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName); ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName);
@ -371,7 +368,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team)
if (leader == nullptr) return; if (leader == nullptr) return;
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(leader->playerName.C_String())); const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(leader->playerName.c_str()));
for (const auto memberId : team->memberIDs) for (const auto memberId : team->memberIDs)
{ {
@ -383,10 +380,6 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team)
{ {
ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName); ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName);
} }
else
{
//ChatPacketHandler::SendTeamStatus(otherMember, LWOOBJID_EMPTY, LWOZONEID(0, 0, 0), 1, 0, u"");
}
} }
UpdateTeamsOnWorld(team, false); UpdateTeamsOnWorld(team, false);

View File

@ -9,11 +9,12 @@
struct PlayerData { struct PlayerData {
LWOOBJID playerID; LWOOBJID playerID;
RakNet::RakString playerName; std::string playerName;
SystemAddress sysAddr; SystemAddress sysAddr;
LWOZONEID zoneID; LWOZONEID zoneID;
std::vector<FriendData> friends; std::vector<FriendData> friends;
time_t muteExpire; time_t muteExpire;
uint8_t countOfBestFriends = 0;
}; };
struct TeamData { struct TeamData {
@ -45,7 +46,7 @@ public:
PlayerData* GetPlayerData(const std::string& playerName) { PlayerData* GetPlayerData(const std::string& playerName) {
for (auto player : mPlayers) { for (auto player : mPlayers) {
if (player.second) { if (player.second) {
std::string pn = player.second->playerName.C_String(); std::string pn = player.second->playerName.c_str();
if (pn == playerName) return player.second; if (pn == playerName) return player.second;
} }
} }

View File

@ -0,0 +1,15 @@
#pragma once
#ifndef __ADDFRIENDRESPONSECODE__H__
#define __ADDFRIENDRESPONSECODE__H__
#include <cstdint>
enum class AddFriendResponseCode : uint8_t {
ACCEPTED = 0,
REJECTED,
BUSY,
CANCELLED
};
#endif //!__ADDFRIENDRESPONSECODE__H__

View File

@ -0,0 +1,24 @@
#pragma once
#ifndef __ADDFRIENDRESPONSETYPE__H__
#define __ADDFRIENDRESPONSETYPE__H__
#include <cstdint>
enum class AddFriendResponseType : uint8_t {
ACCEPTED = 0,
ALREADYFRIEND,
INVALIDCHARACTER,
GENERALERROR,
YOURFRIENDSLISTFULL,
THEIRFRIENDLISTFULL,
DECLINED,
BUSY,
NOTONLINE,
WAITINGAPPROVAL,
MYTHRAN,
CANCELLED,
FRIENDISFREETRIAL
};
#endif //!__ADDFRIENDRESPONSETYPE__H__

26
dCommon/CMakeLists.txt Normal file
View File

@ -0,0 +1,26 @@
set(DCOMMON_SOURCES "AMFFormat.cpp"
"AMFFormat_BitStream.cpp"
"BinaryIO.cpp"
"dConfig.cpp"
"Diagnostics.cpp"
"dLogger.cpp"
"GeneralUtils.cpp"
"LDFFormat.cpp"
"MD5.cpp"
"Metrics.cpp"
"NiPoint3.cpp"
"NiQuaternion.cpp"
"SHA512.cpp"
"Type.cpp"
"ZCompression.cpp")
include_directories(${PROJECT_SOURCE_DIR}/dCommon/)
add_library(dCommon STATIC ${DCOMMON_SOURCES})
target_link_libraries(dCommon libbcrypt)
if (UNIX)
find_package(ZLIB REQUIRED)
target_link_libraries(dCommon ZLIB::ZLIB)
endif()

View File

@ -188,3 +188,53 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream *inStream) {
return string; return string;
} }
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
std::vector<std::string> GeneralUtils::GetFileNamesFromFolder(const std::string& folder)
{
std::vector<std::string> names;
std::string search_path = folder + "/*.*";
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
names.push_back(fd.cFileName);
}
} while (::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
return names;
}
#else
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <iostream>
#include <vector>
#include <cstring>
std::vector<std::string> GeneralUtils::GetFileNamesFromFolder(const std::string& folder) {
std::vector<std::string> names;
struct dirent* entry;
DIR* dir = opendir(folder.c_str());
if (dir == NULL) {
return names;
}
while ((entry = readdir(dir)) != NULL) {
std::string value(entry->d_name, strlen(entry->d_name));
if (value == "." || value == "..") {
continue;
}
names.push_back(value);
}
closedir(dir);
return names;
}
#endif

View File

@ -128,6 +128,8 @@ namespace GeneralUtils {
std::vector<std::string> SplitString(const std::string& str, char delimiter); std::vector<std::string> SplitString(const std::string& str, char delimiter);
std::vector<std::string> GetFileNamesFromFolder(const std::string& folder);
template <typename T> template <typename T>
T Parse(const char* value); T Parse(const char* value);

View File

@ -1,5 +1,7 @@
#include "ZCompression.h" #include "ZCompression.h"
#ifndef _WIN32
#include <zlib.h> #include <zlib.h>
namespace ZCompression namespace ZCompression
@ -70,4 +72,6 @@ namespace ZCompression
return(nRet); // -1 or len of output return(nRet); // -1 or len of output
*/ */
} }
} }
#endif

View File

@ -2,6 +2,10 @@
#include <cstdint> #include <cstdint>
#include "dPlatforms.h"
#ifndef DARKFLAME_PLATFORM_WIN32
namespace ZCompression namespace ZCompression
{ {
int32_t GetMaxCompressedLength(int32_t nLenSrc); int32_t GetMaxCompressedLength(int32_t nLenSrc);
@ -10,3 +14,5 @@ namespace ZCompression
int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr); int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr);
} }
#endif

View File

@ -440,33 +440,6 @@ enum eInventoryType : uint32_t {
INVALID // made up, for internal use!!! INVALID // made up, for internal use!!!
}; };
enum eItemType : int32_t {
ITEM_TYPE_UNKNOWN = -1, //!< An unknown item type
ITEM_TYPE_BRICK = 1, //!< A brick
ITEM_TYPE_HAT = 2, //!< A hat / head item
ITEM_TYPE_HAIR = 3, //!< A hair item
ITEM_TYPE_NECK = 4, //!< A neck item
ITEM_TYPE_LEFT_HAND = 5, //!< A left handed item
ITEM_TYPE_RIGHT_HAND = 6, //!< A right handed item
ITEM_TYPE_LEGS = 7, //!< A pants item
ITEM_TYPE_LEFT_TRINKET = 8, //!< A left handled trinket item
ITEM_TYPE_RIGHT_TRINKET = 9, //!< A right handed trinket item
ITEM_TYPE_BEHAVIOR = 10, //!< A behavior
ITEM_TYPE_PROPERTY = 11, //!< A property
ITEM_TYPE_MODEL = 12, //!< A model
ITEM_TYPE_COLLECTIBLE = 13, //!< A collectible item
ITEM_TYPE_CONSUMABLE = 14, //!< A consumable item
ITEM_TYPE_CHEST = 15, //!< A chest item
ITEM_TYPE_EGG = 16, //!< An egg
ITEM_TYPE_PET_FOOD = 17, //!< A pet food item
ITEM_TYPE_QUEST_OBJECT = 18, //!< A quest item
ITEM_TYPE_PET_INVENTORY_ITEM = 19, //!< A pet inventory item
ITEM_TYPE_PACKAGE = 20, //!< A package
ITEM_TYPE_LOOT_MODEL = 21, //!< A loot model
ITEM_TYPE_VEHICLE = 22, //!< A vehicle
ITEM_TYPE_CURRENCY = 23 //!< Currency
};
enum eRebuildState : uint32_t { enum eRebuildState : uint32_t {
REBUILD_OPEN, REBUILD_OPEN,
REBUILD_COMPLETED = 2, REBUILD_COMPLETED = 2,

29
dCommon/dPlatforms.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#if defined(_WIN32)
#define DARKFLAME_PLATFORM_WIN32
#elif defined(__APPLE__) && defined(__MACH__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#define DARKFLAME_PLATFORM_IOS
#elif TARGET_OS_MAC
#define DARKFLAME_PLATFORM_MACOS
#else
#error unknown Apple operating system
#endif
#elif defined(__unix__)
#define DARKFLAME_PLATFORM_UNIX
#if defined(__ANDROID__)
#define DARKFLAME_PLATFORM_ANDROID
#elif defined(__linux__)
#define DARKFLAME_PLATFORM_LINUX
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define DARKFLAME_PLATFORM_FREEBSD
#elif defined(__CYGWIN__)
#define DARKFLAME_PLATFORM_CYGWIN
#else
#error unknown unix operating system
#endif
#else
#error unknown operating system
#endif

36
dCommon/eItemType.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#ifndef __EITEMTYPE__H__
#define __EITEMTYPE__H__
#include <cstdint>
enum class eItemType : int32_t {
ITEM_TYPE_UNKNOWN = -1, //!< An unknown item type
ITEM_TYPE_BRICK = 1, //!< A brick
ITEM_TYPE_HAT = 2, //!< A hat / head item
ITEM_TYPE_HAIR = 3, //!< A hair item
ITEM_TYPE_NECK = 4, //!< A neck item
ITEM_TYPE_LEFT_HAND = 5, //!< A left handed item
ITEM_TYPE_RIGHT_HAND = 6, //!< A right handed item
ITEM_TYPE_LEGS = 7, //!< A pants item
ITEM_TYPE_LEFT_TRINKET = 8, //!< A left handled trinket item
ITEM_TYPE_RIGHT_TRINKET = 9, //!< A right handed trinket item
ITEM_TYPE_BEHAVIOR = 10, //!< A behavior
ITEM_TYPE_PROPERTY = 11, //!< A property
ITEM_TYPE_MODEL = 12, //!< A model
ITEM_TYPE_COLLECTIBLE = 13, //!< A collectible item
ITEM_TYPE_CONSUMABLE = 14, //!< A consumable item
ITEM_TYPE_CHEST = 15, //!< A chest item
ITEM_TYPE_EGG = 16, //!< An egg
ITEM_TYPE_PET_FOOD = 17, //!< A pet food item
ITEM_TYPE_QUEST_OBJECT = 18, //!< A quest item
ITEM_TYPE_PET_INVENTORY_ITEM = 19, //!< A pet inventory item
ITEM_TYPE_PACKAGE = 20, //!< A package
ITEM_TYPE_LOOT_MODEL = 21, //!< A loot model
ITEM_TYPE_VEHICLE = 22, //!< A vehicle
ITEM_TYPE_CURRENCY = 23, //!< Currency
ITEM_TYPE_MOUNT = 24 //!< A Mount
};
#endif //!__EITEMTYPE__H__

13
dDatabase/CMakeLists.txt Normal file
View File

@ -0,0 +1,13 @@
set(DDATABASE_SOURCES "CDClientDatabase.cpp"
"CDClientManager.cpp"
"Database.cpp"
"MigrationRunner.cpp")
add_subdirectory(Tables)
foreach(file ${DDATABASE_TABLES_SOURCES})
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}")
endforeach()
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)

View File

@ -8,6 +8,8 @@ using namespace std;
sql::Driver * Database::driver; sql::Driver * Database::driver;
sql::Connection * Database::con; sql::Connection * Database::con;
sql::Properties Database::props;
std::string Database::database;
void Database::Connect(const string& host, const string& database, const string& username, const string& password) { void Database::Connect(const string& host, const string& database, const string& username, const string& password) {
@ -25,14 +27,26 @@ void Database::Connect(const string& host, const string& database, const string&
properties["user"] = szUsername; properties["user"] = szUsername;
properties["password"] = szPassword; properties["password"] = szPassword;
properties["autoReconnect"] = "true"; properties["autoReconnect"] = "true";
con = driver->connect(properties);
con->setSchema(szDatabase);
} //Connect
void Database::Destroy(std::string source) { Database::props = properties;
Database::database = database;
Database::Connect();
}
void Database::Connect() {
con = driver->connect(Database::props);
con->setSchema(Database::database);
}
void Database::Destroy(std::string source, bool log) {
if (!con) return; if (!con) return;
if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!\n", source.c_str());
else Game::logger->Log("Database", "Destroying MySQL connection!\n"); if (log) {
if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!\n", source.c_str());
else Game::logger->Log("Database", "Destroying MySQL connection!\n");
}
con->close(); con->close();
delete con; delete con;
} //Destroy } //Destroy
@ -48,13 +62,7 @@ sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) {
sql::SQLString str(test, size); sql::SQLString str(test, size);
if (!con) { if (!con) {
//Connect to the MySQL Database Connect();
std::string mysql_host = Game::config->GetValue("mysql_host");
std::string mysql_database = Game::config->GetValue("mysql_database");
std::string mysql_username = Game::config->GetValue("mysql_username");
std::string mysql_password = Game::config->GetValue("mysql_password");
Connect(mysql_host, mysql_database, mysql_username, mysql_password);
Game::logger->Log("Database", "Trying to reconnect to MySQL\n"); Game::logger->Log("Database", "Trying to reconnect to MySQL\n");
} }
@ -64,13 +72,7 @@ sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) {
con = nullptr; con = nullptr;
//Connect to the MySQL Database Connect();
std::string mysql_host = Game::config->GetValue("mysql_host");
std::string mysql_database = Game::config->GetValue("mysql_database");
std::string mysql_username = Game::config->GetValue("mysql_username");
std::string mysql_password = Game::config->GetValue("mysql_password");
Connect(mysql_host, mysql_database, mysql_username, mysql_password);
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection\n"); Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection\n");
} }
@ -79,3 +81,6 @@ sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) {
return stmt; return stmt;
} //CreatePreppedStmt } //CreatePreppedStmt
void Database::Commit() {
Database::con->commit();
}

View File

@ -13,10 +13,17 @@ class Database {
private: private:
static sql::Driver *driver; static sql::Driver *driver;
static sql::Connection *con; static sql::Connection *con;
static sql::Properties props;
static std::string database;
public: public:
static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password); static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
static void Destroy(std::string source=""); static void Connect();
static void Destroy(std::string source = "", bool log = true);
static sql::Statement* CreateStmt(); static sql::Statement* CreateStmt();
static sql::PreparedStatement* CreatePreppedStmt(const std::string& query); static sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
static void Commit();
static std::string GetDatabase() { return database; }
static sql::Properties GetProperties() { return props; }
}; };

View File

@ -0,0 +1,78 @@
#include "MigrationRunner.h"
#include "GeneralUtils.h"
#include <fstream>
#include <algorithm>
#include <thread>
void MigrationRunner::RunMigrations() {
auto stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
stmt->executeQuery();
delete stmt;
sql::SQLString finalSQL = "";
Migration checkMigration{};
for (const auto& entry : GeneralUtils::GetFileNamesFromFolder("./migrations/")) {
auto migration = LoadMigration(entry);
if (migration.data.empty()) {
continue;
}
checkMigration = migration;
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
stmt->setString(1, migration.name);
auto res = stmt->executeQuery();
bool doExit = res->next();
delete res;
delete stmt;
if (doExit) continue;
Game::logger->Log("MigrationRunner", "Running migration: " + migration.name + "\n");
finalSQL.append(migration.data);
finalSQL.append('\n');
stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
stmt->setString(1, entry);
stmt->execute();
delete stmt;
}
if (!finalSQL.empty()) {
try {
auto simpleStatement = Database::CreateStmt();
simpleStatement->execute(finalSQL);
delete simpleStatement;
}
catch (sql::SQLException e) {
Game::logger->Log("MigrationRunner", std::string("Encountered error running migration: ") + e.what() + "\n");
}
}
}
Migration MigrationRunner::LoadMigration(std::string path) {
Migration migration{};
std::ifstream file("./migrations/" + path);
if (file.is_open()) {
std::hash<std::string> hash;
std::string line;
std::string total = "";
while (std::getline(file, line)) {
total += line;
}
file.close();
migration.name = path;
migration.data = total;
}
return migration;
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "Database.h"
#include "dCommonVars.h"
#include "Game.h"
#include "dCommonVars.h"
#include "dLogger.h"
struct Migration {
std::string data;
std::string name;
};
class MigrationRunner {
public:
static void RunMigrations();
static Migration LoadMigration(std::string path);
};

View File

@ -24,7 +24,13 @@ CDBehaviorTemplateTable::CDBehaviorTemplateTable(void) {
entry.behaviorID = tableData.getIntField(0, -1); entry.behaviorID = tableData.getIntField(0, -1);
entry.templateID = tableData.getIntField(1, -1); entry.templateID = tableData.getIntField(1, -1);
entry.effectID = tableData.getIntField(2, -1); entry.effectID = tableData.getIntField(2, -1);
entry.effectHandle = tableData.getStringField(3, ""); auto candidateToAdd = tableData.getStringField(3, "");
auto parameter = m_EffectHandles.find(candidateToAdd);
if (parameter != m_EffectHandles.end()) {
entry.effectHandle = parameter;
} else {
entry.effectHandle = m_EffectHandles.insert(candidateToAdd).first;
}
this->entries.push_back(entry); this->entries.push_back(entry);
this->entriesMappedByBehaviorID.insert(std::make_pair(entry.behaviorID, entry)); this->entriesMappedByBehaviorID.insert(std::make_pair(entry.behaviorID, entry));
@ -62,7 +68,8 @@ const CDBehaviorTemplate CDBehaviorTemplateTable::GetByBehaviorID(uint32_t behav
if (entry == this->entriesMappedByBehaviorID.end()) { if (entry == this->entriesMappedByBehaviorID.end()) {
CDBehaviorTemplate entryToReturn; CDBehaviorTemplate entryToReturn;
entryToReturn.behaviorID = 0; entryToReturn.behaviorID = 0;
entryToReturn.effectHandle = ""; entryToReturn.effectHandle = m_EffectHandles.end();
entryToReturn.effectID = 0;
return entryToReturn; return entryToReturn;
} else { } else {
return entry->second; return entry->second;

View File

@ -3,6 +3,7 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
/*! /*!
\file CDBehaviorTemplateTable.hpp \file CDBehaviorTemplateTable.hpp
@ -11,10 +12,10 @@
//! BehaviorTemplate Entry Struct //! BehaviorTemplate Entry Struct
struct CDBehaviorTemplate { struct CDBehaviorTemplate {
unsigned int behaviorID; //!< The Behavior ID unsigned int behaviorID; //!< The Behavior ID
unsigned int templateID; //!< The Template ID (LOT) unsigned int templateID; //!< The Template ID (LOT)
unsigned int effectID; //!< The Effect ID attached unsigned int effectID; //!< The Effect ID attached
std::string effectHandle; //!< The effect handle std::unordered_set<std::string>::iterator effectHandle; //!< The effect handle
}; };
@ -23,6 +24,7 @@ class CDBehaviorTemplateTable : public CDTable {
private: private:
std::vector<CDBehaviorTemplate> entries; std::vector<CDBehaviorTemplate> entries;
std::unordered_map<uint32_t, CDBehaviorTemplate> entriesMappedByBehaviorID; std::unordered_map<uint32_t, CDBehaviorTemplate> entriesMappedByBehaviorID;
std::unordered_set<std::string> m_EffectHandles;
public: public:
//! Constructor //! Constructor

View File

@ -0,0 +1,38 @@
set(DDATABASE_TABLES_SOURCES "CDActivitiesTable.cpp"
"CDActivityRewardsTable.cpp"
"CDAnimationsTable.cpp"
"CDBehaviorParameterTable.cpp"
"CDBehaviorTemplateTable.cpp"
"CDBrickIDTableTable.cpp"
"CDComponentsRegistryTable.cpp"
"CDCurrencyTableTable.cpp"
"CDDestructibleComponentTable.cpp"
"CDEmoteTable.cpp"
"CDFeatureGatingTable.cpp"
"CDInventoryComponentTable.cpp"
"CDItemComponentTable.cpp"
"CDItemSetSkillsTable.cpp"
"CDItemSetsTable.cpp"
"CDLevelProgressionLookupTable.cpp"
"CDLootMatrixTable.cpp"
"CDLootTableTable.cpp"
"CDMissionEmailTable.cpp"
"CDMissionNPCComponentTable.cpp"
"CDMissionsTable.cpp"
"CDMissionTasksTable.cpp"
"CDMovementAIComponentTable.cpp"
"CDObjectSkillsTable.cpp"
"CDObjectsTable.cpp"
"CDPackageComponentTable.cpp"
"CDPhysicsComponentTable.cpp"
"CDPropertyEntranceComponentTable.cpp"
"CDPropertyTemplateTable.cpp"
"CDProximityMonitorComponentTable.cpp"
"CDRailActivatorComponent.cpp"
"CDRarityTableTable.cpp"
"CDRebuildComponentTable.cpp"
"CDRewardsTable.cpp"
"CDScriptComponentTable.cpp"
"CDSkillBehaviorTable.cpp"
"CDVendorComponentTable.cpp"
"CDZoneTableTable.cpp" PARENT_SCOPE)

59
dGame/CMakeLists.txt Normal file
View File

@ -0,0 +1,59 @@
set(DGAME_SOURCES "Character.cpp"
"Entity.cpp"
"EntityManager.cpp"
"LeaderboardManager.cpp"
"Player.cpp"
"TeamManager.cpp"
"TradingManager.cpp"
"User.cpp"
"UserManager.cpp")
add_subdirectory(dBehaviors)
foreach(file ${DGAME_DBEHAVIORS_SOURCES})
set(DGAME_SOURCES ${DGAME_SOURCES} "dBehaviors/${file}")
endforeach()
add_subdirectory(dComponents)
foreach(file ${DGAME_DCOMPONENTS_SOURCES})
set(DGAME_SOURCES ${DGAME_SOURCES} "dComponents/${file}")
endforeach()
add_subdirectory(dEntity)
foreach(file ${DGAME_DENTITY_SOURCES})
set(DGAME_SOURCES ${DGAME_SOURCES} "dEntity/${file}")
endforeach()
add_subdirectory(dGameMessages)
foreach(file ${DGAME_DGAMEMESSAGES_SOURCES})
set(DGAME_SOURCES ${DGAME_SOURCES} "dGameMessages/${file}")
endforeach()
add_subdirectory(dInventory)
foreach(file ${DGAME_DINVENTORY_SOURCES})
set(DGAME_SOURCES ${DGAME_SOURCES} "dInventory/${file}")
endforeach()
add_subdirectory(dMission)
foreach(file ${DGAME_DMISSION_SOURCES})
set(DGAME_SOURCES ${DGAME_SOURCES} "dMission/${file}")
endforeach()
add_subdirectory(dUtilities)
foreach(file ${DGAME_DUTILITIES_SOURCES})
set(DGAME_SOURCES ${DGAME_SOURCES} "dUtilities/${file}")
endforeach()
foreach(file ${DSCRIPT_SOURCES})
set(DGAME_SOURCES ${DGAME_SOURCES} "${PROJECT_SOURCE_DIR}/dScripts/${file}")
endforeach()
add_library(dGame STATIC ${DGAME_SOURCES})
target_link_libraries(dGame dDatabase)

View File

@ -109,6 +109,11 @@ Entity::~Entity() {
m_Components.erase(pair.first); m_Components.erase(pair.first);
} }
for (auto child : m_ChildEntities) {
if (child) child->RemoveParent();
}
if (m_ParentEntity) { if (m_ParentEntity) {
m_ParentEntity->RemoveChild(this); m_ParentEntity->RemoveChild(this);
} }
@ -1201,17 +1206,21 @@ void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) {
} }
void Entity::Update(const float deltaTime) { void Entity::Update(const float deltaTime) {
for (int i = 0; i < m_Timers.size(); i++) { uint32_t timerPosition;
m_Timers[i]->Update(deltaTime); timerPosition = 0;
if (m_Timers[i]->GetTime() <= 0) { while (timerPosition < m_Timers.size()) {
const auto timerName = m_Timers[i]->GetName(); m_Timers[timerPosition]->Update(deltaTime);
if (m_Timers[timerPosition]->GetTime() <= 0) {
const auto timerName = m_Timers[timerPosition]->GetName();
delete m_Timers[i]; delete m_Timers[timerPosition];
m_Timers.erase(m_Timers.begin() + i); m_Timers.erase(m_Timers.begin() + timerPosition);
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
script->OnTimerDone(this, timerName); script->OnTimerDone(this, timerName);
} }
} else {
timerPosition++;
} }
} }
@ -1223,6 +1232,14 @@ void Entity::Update(const float deltaTime) {
m_CallbackTimers.erase(m_CallbackTimers.begin() + i); m_CallbackTimers.erase(m_CallbackTimers.begin() + i);
} }
} }
// Add pending timers to the list of timers so they start next tick.
if (m_PendingTimers.size() > 0) {
for (auto namedTimer : m_PendingTimers) {
m_Timers.push_back(namedTimer);
}
m_PendingTimers.clear();
}
if (IsSleeping()) if (IsSleeping())
{ {
@ -1650,18 +1667,24 @@ void Entity::AddChild(Entity* child) {
void Entity::RemoveChild(Entity* child) { void Entity::RemoveChild(Entity* child) {
if (!child) return; if (!child) return;
for (auto entity = m_ChildEntities.begin(); entity != m_ChildEntities.end(); entity++) { uint32_t entityPosition = 0;
if (*entity && (*entity)->GetObjectID() == child->GetObjectID()) { while (entityPosition < m_ChildEntities.size()) {
if (!m_ChildEntities[entityPosition] || (m_ChildEntities[entityPosition])->GetObjectID() == child->GetObjectID()) {
m_IsParentChildDirty = true; m_IsParentChildDirty = true;
m_ChildEntities.erase(entity); m_ChildEntities.erase(m_ChildEntities.begin() + entityPosition);
return; } else {
entityPosition++;
} }
} }
} }
void Entity::RemoveParent() {
this->m_ParentEntity = nullptr;
}
void Entity::AddTimer(std::string name, float time) { void Entity::AddTimer(std::string name, float time) {
EntityTimer* timer = new EntityTimer(name, time); EntityTimer* timer = new EntityTimer(name, time);
m_Timers.push_back(timer); m_PendingTimers.push_back(timer);
} }
void Entity::AddCallbackTimer(float time, std::function<void()> callback) { void Entity::AddCallbackTimer(float time, std::function<void()> callback) {

View File

@ -144,6 +144,7 @@ public:
void AddChild(Entity* child); void AddChild(Entity* child);
void RemoveChild(Entity* child); void RemoveChild(Entity* child);
void RemoveParent();
void AddTimer(std::string name, float time); void AddTimer(std::string name, float time);
void AddCallbackTimer(float time, std::function<void()> callback); void AddCallbackTimer(float time, std::function<void()> callback);
bool HasTimer(const std::string& name); bool HasTimer(const std::string& name);
@ -309,6 +310,7 @@ protected:
std::unordered_map<int32_t, Component*> m_Components; //The int is the ID of the component std::unordered_map<int32_t, Component*> m_Components; //The int is the ID of the component
std::vector<EntityTimer*> m_Timers; std::vector<EntityTimer*> m_Timers;
std::vector<EntityTimer*> m_PendingTimers;
std::vector<EntityCallbackTimer*> m_CallbackTimers; std::vector<EntityCallbackTimer*> m_CallbackTimers;
bool m_ShouldDestroyAfterUpdate = false; bool m_ShouldDestroyAfterUpdate = false;

View File

@ -401,7 +401,7 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
Game::server->Send(&stream, sysAddr, false); Game::server->Send(&stream, sysAddr, false);
} }
PacketUtils::SavePacket("[24]_"+std::to_string(entity->GetObjectID()) + "_" + std::to_string(m_SerializationCounter) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed()); // PacketUtils::SavePacket("[24]_"+std::to_string(entity->GetObjectID()) + "_" + std::to_string(m_SerializationCounter) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed());
if (entity->IsPlayer()) if (entity->IsPlayer())
{ {

View File

@ -369,10 +369,8 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
} }
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet); LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
objectID = GeneralUtils::ClearBit(objectID, OBJECT_BIT_CHARACTER); uint32_t charID = static_cast<uint32_t>(objectID);
objectID = GeneralUtils::ClearBit(objectID, OBJECT_BIT_PERSISTENT);
uint32_t charID = static_cast<uint32_t>(objectID);
Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)\n", objectID, charID); Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)\n", objectID, charID);
//Check if this user has this character: //Check if this user has this character:
@ -402,10 +400,14 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
} }
{ {
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;"); sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;");
stmt->setUInt64(1, charID); stmt->setUInt(1, charID);
stmt->setUInt64(2, charID); stmt->setUInt(2, charID);
stmt->execute(); stmt->execute();
delete stmt; delete stmt;
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_PLAYER_REMOVED_NOTIFICATION);
bitStream.Write(objectID);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} }
{ {
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;"); sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;");

View File

@ -409,7 +409,7 @@ Behavior::Behavior(const uint32_t behaviorId)
{ {
auto behaviorTemplateTable = CDClientManager::Instance()->GetTable<CDBehaviorTemplateTable>("BehaviorTemplate"); auto behaviorTemplateTable = CDClientManager::Instance()->GetTable<CDBehaviorTemplateTable>("BehaviorTemplate");
CDBehaviorTemplate templateInDatabase; CDBehaviorTemplate templateInDatabase{};
if (behaviorTemplateTable) { if (behaviorTemplateTable) {
auto templateEntry = behaviorTemplateTable->GetByBehaviorID(behaviorId); auto templateEntry = behaviorTemplateTable->GetByBehaviorID(behaviorId);
@ -445,7 +445,7 @@ Behavior::Behavior(const uint32_t behaviorId)
this->m_effectId = templateInDatabase.effectID; this->m_effectId = templateInDatabase.effectID;
this->m_effectHandle = templateInDatabase.effectHandle != "" ? new std::string(templateInDatabase.effectHandle) : nullptr; this->m_effectHandle = *templateInDatabase.effectHandle != "" ? new std::string(*templateInDatabase.effectHandle) : nullptr;
} }

View File

@ -0,0 +1,51 @@
set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
"AndBehavior.cpp"
"ApplyBuffBehavior.cpp"
"AreaOfEffectBehavior.cpp"
"AttackDelayBehavior.cpp"
"BasicAttackBehavior.cpp"
"Behavior.cpp"
"BehaviorBranchContext.cpp"
"BehaviorContext.cpp"
"BehaviorTemplates.cpp"
"BlockBehavior.cpp"
"BuffBehavior.cpp"
"CarBoostBehavior.cpp"
"ChainBehavior.cpp"
"ChangeOrientationBehavior.cpp"
"ChargeUpBehavior.cpp"
"ClearTargetBehavior.cpp"
"DamageAbsorptionBehavior.cpp"
"DamageReductionBehavior.cpp"
"DurationBehavior.cpp"
"EmptyBehavior.cpp"
"EndBehavior.cpp"
"ForceMovementBehavior.cpp"
"HealBehavior.cpp"
"ImaginationBehavior.cpp"
"ImmunityBehavior.cpp"
"InterruptBehavior.cpp"
"JetPackBehavior.cpp"
"KnockbackBehavior.cpp"
"LootBuffBehavior.cpp"
"MovementSwitchBehavior.cpp"
"NpcCombatSkillBehavior.cpp"
"OverTimeBehavior.cpp"
"PlayEffectBehavior.cpp"
"ProjectileAttackBehavior.cpp"
"PullToPointBehavior.cpp"
"RepairBehavior.cpp"
"SkillCastFailedBehavior.cpp"
"SkillEventBehavior.cpp"
"SpawnBehavior.cpp"
"SpawnQuickbuildBehavior.cpp"
"SpeedBehavior.cpp"
"StartBehavior.cpp"
"StunBehavior.cpp"
"SwitchBehavior.cpp"
"SwitchMultipleBehavior.cpp"
"TacArcBehavior.cpp"
"TargetCasterBehavior.cpp"
"TauntBehavior.cpp"
"VentureVisionBehavior.cpp"
"VerifyBehavior.cpp" PARENT_SCOPE)

View File

@ -0,0 +1,40 @@
set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"BouncerComponent.cpp"
"BuffComponent.cpp"
"BuildBorderComponent.cpp"
"CharacterComponent.cpp"
"Component.cpp"
"ControllablePhysicsComponent.cpp"
"DestroyableComponent.cpp"
"InventoryComponent.cpp"
"LUPExhibitComponent.cpp"
"MissionComponent.cpp"
"MissionOfferComponent.cpp"
"ModelComponent.cpp"
"ModuleAssemblyComponent.cpp"
"MovementAIComponent.cpp"
"MovingPlatformComponent.cpp"
"PetComponent.cpp"
"PhantomPhysicsComponent.cpp"
"PossessableComponent.cpp"
"PossessorComponent.cpp"
"PropertyComponent.cpp"
"PropertyEntranceComponent.cpp"
"PropertyManagementComponent.cpp"
"PropertyVendorComponent.cpp"
"ProximityMonitorComponent.cpp"
"RacingControlComponent.cpp"
"RailActivatorComponent.cpp"
"RebuildComponent.cpp"
"RenderComponent.cpp"
"RigidbodyPhantomPhysicsComponent.cpp"
"RocketLaunchLupComponent.cpp"
"RocketLaunchpadControlComponent.cpp"
"ScriptedActivityComponent.cpp"
"ShootingGalleryComponent.cpp"
"SimplePhysicsComponent.cpp"
"SkillComponent.cpp"
"SoundTriggerComponent.cpp"
"SwitchComponent.cpp"
"VehiclePhysicsComponent.cpp"
"VendorComponent.cpp" PARENT_SCOPE)

View File

@ -179,6 +179,18 @@ public:
*/ */
void SetLastRocketItemID(LWOOBJID lastRocketItemID) { m_LastRocketItemID = lastRocketItemID; } void SetLastRocketItemID(LWOOBJID lastRocketItemID) { m_LastRocketItemID = lastRocketItemID; }
/**
* Gets the object ID of the mount item that is being used
* @return the object ID of the mount item that is being used
*/
LWOOBJID GetMountItemID() const { return m_MountItemID; }
/**
* Sets the object ID of the mount item that is being used
* @param m_MountItemID the object ID of the mount item that is being used
*/
void SetMountItemID(LWOOBJID mountItemID) { m_MountItemID = mountItemID; }
/** /**
* Gives the player rewards for the last level that they leveled up from * Gives the player rewards for the last level that they leveled up from
*/ */
@ -579,6 +591,11 @@ private:
* ID of the last rocket used * ID of the last rocket used
*/ */
LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY; LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY;
/**
* Mount Item ID
*/
LWOOBJID m_MountItemID = LWOOBJID_EMPTY;
}; };
#endif // CHARACTERCOMPONENT_H #endif // CHARACTERCOMPONENT_H

View File

@ -25,6 +25,7 @@
#include "PropertyManagementComponent.h" #include "PropertyManagementComponent.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "dConfig.h" #include "dConfig.h"
#include "eItemType.h"
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent)
{ {
@ -1024,13 +1025,13 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
return; return;
} }
if (type == ITEM_TYPE_LOOT_MODEL || type == ITEM_TYPE_VEHICLE) if (type == eItemType::ITEM_TYPE_LOOT_MODEL || type == eItemType::ITEM_TYPE_VEHICLE)
{ {
return; return;
} }
} }
if (type != ITEM_TYPE_LOOT_MODEL && type != ITEM_TYPE_MODEL) if (type != eItemType::ITEM_TYPE_LOOT_MODEL && type != eItemType::ITEM_TYPE_MODEL)
{ {
if (!item->GetBound() && !item->GetPreconditionExpression()->Check(m_Parent)) if (!item->GetBound() && !item->GetPreconditionExpression()->Check(m_Parent))
{ {
@ -1411,15 +1412,15 @@ void InventoryComponent::RemoveDatabasePet(LWOOBJID id)
BehaviorSlot InventoryComponent::FindBehaviorSlot(const eItemType type) BehaviorSlot InventoryComponent::FindBehaviorSlot(const eItemType type)
{ {
switch (type) { switch (type) {
case ITEM_TYPE_HAT: case eItemType::ITEM_TYPE_HAT:
return BehaviorSlot::Head; return BehaviorSlot::Head;
case ITEM_TYPE_NECK: case eItemType::ITEM_TYPE_NECK:
return BehaviorSlot::Neck; return BehaviorSlot::Neck;
case ITEM_TYPE_LEFT_HAND: case eItemType::ITEM_TYPE_LEFT_HAND:
return BehaviorSlot::Offhand; return BehaviorSlot::Offhand;
case ITEM_TYPE_RIGHT_HAND: case eItemType::ITEM_TYPE_RIGHT_HAND:
return BehaviorSlot::Primary; return BehaviorSlot::Primary;
case ITEM_TYPE_CONSUMABLE: case eItemType::ITEM_TYPE_CONSUMABLE:
return BehaviorSlot::Consumable; return BehaviorSlot::Consumable;
default: default:
return BehaviorSlot::Invalid; return BehaviorSlot::Invalid;

View File

@ -1,4 +1,4 @@
#pragma once #pragma once
#ifndef INVENTORYCOMPONENT_H #ifndef INVENTORYCOMPONENT_H
#define INVENTORYCOMPONENT_H #define INVENTORYCOMPONENT_H
@ -25,6 +25,8 @@ class ItemSet;
typedef std::map<std::string, EquippedItem> EquipmentMap; typedef std::map<std::string, EquippedItem> EquipmentMap;
enum class eItemType : int32_t;
/** /**
* Handles the inventory of entity, including the items they possess and have equipped. An entity can have inventories * Handles the inventory of entity, including the items they possess and have equipped. An entity can have inventories
* of different types, each type representing a different group of items, see `eInventoryType` for a list of * of different types, each type representing a different group of items, see `eInventoryType` for a list of

View File

@ -1,10 +1,13 @@
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessorComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "Inventory.h"
#include "Item.h" #include "Item.h"
PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) : Component(parent){ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) : Component(parent){
m_Possessor = LWOOBJID_EMPTY; m_Possessor = LWOOBJID_EMPTY;
CDItemComponent item = Inventory::FindItemComponent(m_Parent->GetLOT());
m_AnimationFlag = static_cast<eAnimationFlags>(item.animationFlag);
// Get the possession Type from the CDClient // Get the possession Type from the CDClient
auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit FROM PossessableComponent WHERE id = ?;"); auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit FROM PossessableComponent WHERE id = ?;");

View File

@ -54,6 +54,18 @@ class PossessableComponent : public Component {
*/ */
void ForceDepossess() { m_ImmediatelyDepossess = true; m_DirtyPossessable = true;}; void ForceDepossess() { m_ImmediatelyDepossess = true; m_DirtyPossessable = true;};
/**
* Set if the parent entity was spawned from an item
* @param value if the parent entity was spawned from an item
*/
void SetItemSpawned(bool value) { m_ItemSpawned = value;};
/**
* Returns if the parent entity was spawned from an item
* @return if the parent entity was spawned from an item
*/
LWOOBJID GetItemSpawned() const { return m_ItemSpawned; };
/** /**
* Handles an OnUsed event by some other entity, if said entity has a Possessor it becomes the possessor * Handles an OnUsed event by some other entity, if said entity has a Possessor it becomes the possessor
* of this entity * of this entity
@ -95,4 +107,9 @@ class PossessableComponent : public Component {
*/ */
bool m_ImmediatelyDepossess = false; bool m_ImmediatelyDepossess = false;
/**
* @brief Whether the parent entity was spawned from an item
*
*/
bool m_ItemSpawned = false;
}; };

View File

@ -36,6 +36,18 @@ class PossessorComponent : public Component {
*/ */
LWOOBJID GetPossessable() const { return m_Possessable; } LWOOBJID GetPossessable() const { return m_Possessable; }
/**
* Sets if we are busy mounting or dismounting
* @param value if we are busy mounting or dismounting
*/
void SetIsBusy(bool value) { m_IsBusy = value; }
/**
* Returns if we are busy mounting or dismounting
* @return if we are busy mounting or dismounting
*/
bool GetIsBusy() const { return m_IsBusy; }
/** /**
* Sets the possesible type that's currently used, merely used by the shooting gallery if it's 0 * Sets the possesible type that's currently used, merely used by the shooting gallery if it's 0
* @param value the possesible type to set * @param value the possesible type to set
@ -60,4 +72,10 @@ class PossessorComponent : public Component {
* *
*/ */
bool m_DirtyPossesor = false; bool m_DirtyPossesor = false;
/**
* @brief if the possessor is busy mounting or dismounting
*
*/
bool m_IsBusy = false;
}; };

View File

@ -102,7 +102,7 @@ PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(Propert
return property; return property;
} }
std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, std::string customQuery, bool wantLimits) { std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery, bool wantLimits) {
std::string base; std::string base;
if (customQuery == "") { if (customQuery == "") {
base = baseQueryForProperties; base = baseQueryForProperties;
@ -115,15 +115,13 @@ std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMe
auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;"); auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;");
friendsListQuery->setInt64(1, entity->GetObjectID()); friendsListQuery->setUInt(1, character->GetID());
friendsListQuery->setInt64(2, entity->GetObjectID()); friendsListQuery->setUInt(2, character->GetID());
auto friendsListQueryResult = friendsListQuery->executeQuery(); auto friendsListQueryResult = friendsListQuery->executeQuery();
while (friendsListQueryResult->next()) { while (friendsListQueryResult->next()) {
auto playerIDToConvert = friendsListQueryResult->getInt64(1); auto playerIDToConvert = friendsListQueryResult->getInt(1);
playerIDToConvert = GeneralUtils::ClearBit(playerIDToConvert, OBJECT_BIT_CHARACTER);
playerIDToConvert = GeneralUtils::ClearBit(playerIDToConvert, OBJECT_BIT_PERSISTENT);
friendsList = friendsList + std::to_string(playerIDToConvert) + ","; friendsList = friendsList + std::to_string(playerIDToConvert) + ",";
} }
// Replace trailing comma with the closing parenthesis. // Replace trailing comma with the closing parenthesis.
@ -193,7 +191,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
entries.push_back(playerEntry); entries.push_back(playerEntry);
const auto query = BuildQuery(entity, sortMethod); const auto query = BuildQuery(entity, sortMethod, character);
auto propertyLookup = Database::CreatePreppedStmt(query); auto propertyLookup = Database::CreatePreppedStmt(query);
@ -262,17 +260,17 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
// Query to get friend and best friend fields // Query to get friend and best friend fields
auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)"); auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)");
friendCheck->setInt64(1, entity->GetObjectID()); friendCheck->setUInt(1, character->GetID());
friendCheck->setInt64(2, ownerObjId); friendCheck->setUInt(2, ownerObjId);
friendCheck->setInt64(3, ownerObjId); friendCheck->setUInt(3, ownerObjId);
friendCheck->setInt64(4, entity->GetObjectID()); friendCheck->setUInt(4, character->GetID());
auto friendResult = friendCheck->executeQuery(); auto friendResult = friendCheck->executeQuery();
// If we got a result than the two players are friends. // If we got a result than the two players are friends.
if (friendResult->next()) { if (friendResult->next()) {
isFriend = true; isFriend = true;
if (friendResult->getInt(1) == 2) { if (friendResult->getInt(1) == 3) {
isBestFriend = true; isBestFriend = true;
} }
} }
@ -326,7 +324,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
// Query here is to figure out whether or not to display the button to go to the next page or not. // Query here is to figure out whether or not to display the button to go to the next page or not.
int32_t numberOfProperties = 0; int32_t numberOfProperties = 0;
auto buttonQuery = BuildQuery(entity, sortMethod, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false); auto buttonQuery = BuildQuery(entity, sortMethod, character, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false);
auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery); auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery);
propertiesLeft->setUInt(1, this->m_MapID); propertiesLeft->setUInt(1, this->m_MapID);

View File

@ -60,7 +60,7 @@ class PropertyEntranceComponent : public Component {
PropertySelectQueryProperty SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId = LWOCLONEID_INVALID, std::string ownerName = "", std::string propertyName = "", std::string propertyDescription = "", float reputation = 0, bool isBFF = false, bool isFriend = false, bool isModeratorApproved = false, bool isAlt = false, bool isOwned = false, uint32_t privacyOption = 0, uint32_t timeLastUpdated = 0, float performanceCost = 0.0f); PropertySelectQueryProperty SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId = LWOCLONEID_INVALID, std::string ownerName = "", std::string propertyName = "", std::string propertyDescription = "", float reputation = 0, bool isBFF = false, bool isFriend = false, bool isModeratorApproved = false, bool isAlt = false, bool isOwned = false, uint32_t privacyOption = 0, uint32_t timeLastUpdated = 0, float performanceCost = 0.0f);
std::string BuildQuery(Entity* entity, int32_t sortMethod, std::string customQuery = "", bool wantLimits = true); std::string BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery = "", bool wantLimits = true);
private: private:
/** /**

View File

@ -0,0 +1,2 @@
set(DGAME_DENTITY_SOURCES "EntityCallbackTimer.cpp"
"EntityTimer.cpp" PARENT_SCOPE)

View File

@ -0,0 +1,4 @@
set(DGAME_DGAMEMESSAGES_SOURCES "GameMessageHandler.cpp"
"GameMessages.cpp"
"PropertyDataMessage.cpp"
"PropertySelectQueryProperty.cpp" PARENT_SCOPE)

File diff suppressed because it is too large Load Diff

View File

@ -328,6 +328,34 @@ namespace GameMessages {
void SendDisplayChatBubble(LWOOBJID objectId, const std::u16string& text, const SystemAddress& sysAddr); void SendDisplayChatBubble(LWOOBJID objectId, const std::u16string& text, const SystemAddress& sysAddr);
// Mounts
/**
* @brief Set the Inventory LWOOBJID of the mount
*
* @param entity The entity that is mounting
* @param sysAddr the system address to send game message responses to
* @param objectID LWOOBJID of the item in inventory that is being used
*/
void SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr);
/**
* @brief Handle client dismounting mount
*
* @param inStream Raknet BitStream of incoming data
* @param entity The Entity that is dismounting
* @param sysAddr the system address to send game message responses to
*/
void HandleDismountComplete(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
/**
* @brief Handle acknowledging that the client possessed something
*
* @param inStream Raknet BitStream of incoming data
* @param entity The Entity that is possessing
* @param sysAddr the system address to send game message responses to
*/
void HandleAcknowledgePossession(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
//Racing: //Racing:
void HandleModuleAssemblyQueryData(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); void HandleModuleAssemblyQueryData(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
@ -337,8 +365,6 @@ namespace GameMessages {
void HandleRacingClientReady(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); void HandleRacingClientReady(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
void HandleAcknowledgePossession(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
void HandleRequestDie(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); void HandleRequestDie(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
void HandleVehicleNotifyServerAddPassiveBoostAction(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); void HandleVehicleNotifyServerAddPassiveBoostAction(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);

View File

@ -0,0 +1,5 @@
set(DGAME_DINVENTORY_SOURCES "EquippedItem.cpp"
"Inventory.cpp"
"Item.cpp"
"ItemSet.cpp"
"ItemSetPassiveAbility.cpp" PARENT_SCOPE)

View File

@ -1,7 +1,8 @@
#include "Inventory.h" #include "Inventory.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "Game.h" #include "Game.h"
#include "Item.h" #include "Item.h"
#include "eItemType.h"
std::vector<LOT> Inventory::m_GameMasterRestrictedItems = { std::vector<LOT> Inventory::m_GameMasterRestrictedItems = {
1727, // GM Only - JetPack 1727, // GM Only - JetPack
@ -274,40 +275,41 @@ eInventoryType Inventory::FindInventoryTypeForLot(const LOT lot)
const auto itemType = static_cast<eItemType>(itemComponent.itemType); const auto itemType = static_cast<eItemType>(itemComponent.itemType);
switch (itemType) { switch (itemType) {
case ITEM_TYPE_BRICK: case eItemType::ITEM_TYPE_BRICK:
return BRICKS; return BRICKS;
case ITEM_TYPE_BEHAVIOR: case eItemType::ITEM_TYPE_BEHAVIOR:
return BEHAVIORS; return BEHAVIORS;
case ITEM_TYPE_PROPERTY: case eItemType::ITEM_TYPE_PROPERTY:
return PROPERTY_DEEDS; return PROPERTY_DEEDS;
case ITEM_TYPE_MODEL: case eItemType::ITEM_TYPE_MODEL:
case ITEM_TYPE_VEHICLE: case eItemType::ITEM_TYPE_VEHICLE:
case ITEM_TYPE_LOOT_MODEL: case eItemType::ITEM_TYPE_LOOT_MODEL:
case eItemType::ITEM_TYPE_MOUNT:
return MODELS; return MODELS;
case ITEM_TYPE_HAT: case eItemType::ITEM_TYPE_HAT:
case ITEM_TYPE_HAIR: case eItemType::ITEM_TYPE_HAIR:
case ITEM_TYPE_NECK: case eItemType::ITEM_TYPE_NECK:
case ITEM_TYPE_LEFT_HAND: case eItemType::ITEM_TYPE_LEFT_HAND:
case ITEM_TYPE_RIGHT_HAND: case eItemType::ITEM_TYPE_RIGHT_HAND:
case ITEM_TYPE_LEGS: case eItemType::ITEM_TYPE_LEGS:
case ITEM_TYPE_LEFT_TRINKET: case eItemType::ITEM_TYPE_LEFT_TRINKET:
case ITEM_TYPE_RIGHT_TRINKET: case eItemType::ITEM_TYPE_RIGHT_TRINKET:
case ITEM_TYPE_COLLECTIBLE: case eItemType::ITEM_TYPE_COLLECTIBLE:
case ITEM_TYPE_CONSUMABLE: case eItemType::ITEM_TYPE_CONSUMABLE:
case ITEM_TYPE_CHEST: case eItemType::ITEM_TYPE_CHEST:
case ITEM_TYPE_EGG: case eItemType::ITEM_TYPE_EGG:
case ITEM_TYPE_PET_FOOD: case eItemType::ITEM_TYPE_PET_FOOD:
case ITEM_TYPE_PET_INVENTORY_ITEM: case eItemType::ITEM_TYPE_PET_INVENTORY_ITEM:
case ITEM_TYPE_PACKAGE: case eItemType::ITEM_TYPE_PACKAGE:
case ITEM_TYPE_CURRENCY: case eItemType::ITEM_TYPE_CURRENCY:
return ITEMS; return ITEMS;
case ITEM_TYPE_QUEST_OBJECT: case eItemType::ITEM_TYPE_QUEST_OBJECT:
case ITEM_TYPE_UNKNOWN: case eItemType::ITEM_TYPE_UNKNOWN:
default: default:
return HIDDEN; return HIDDEN;
} }

View File

@ -0,0 +1,3 @@
set(DGAME_DMISSION_SOURCES "Mission.cpp"
"MissionPrerequisites.cpp"
"MissionTask.cpp" PARENT_SCOPE)

View File

@ -0,0 +1,9 @@
set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp"
"dLocale.cpp"
"GameConfig.cpp"
"GUID.cpp"
"Loot.cpp"
"Mail.cpp"
"Preconditions.cpp"
"SlashCommandHandler.cpp"
"VanityUtilities.cpp" PARENT_SCOPE)

View File

@ -2017,11 +2017,15 @@ void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ANNOUNCEMENT); PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ANNOUNCEMENT);
RakNet::RakString rsTitle(title.c_str()); bitStream.Write<uint32_t>(title.size());
RakNet::RakString rsMsg(message.c_str()); for (auto character : title) {
bitStream.Write<char>(character);
}
bitStream.Write(rsTitle); bitStream.Write<uint32_t>(message.size());
bitStream.Write(rsMsg); for (auto character : message) {
bitStream.Write<char>(character);
}
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} }

View File

@ -0,0 +1,10 @@
set(DMASTERSERVER_SOURCES "InstanceManager.cpp"
"MasterServer.cpp"
"ObjectIDManager.cpp")
add_executable(MasterServer ${DMASTERSERVER_SOURCES})
target_link_libraries(MasterServer ${COMMON_LIBRARIES})
if(WIN32)
add_dependencies(MasterServer WorldServer AuthServer ChatServer)
endif()

View File

@ -19,6 +19,7 @@
#include "CDClientDatabase.h" #include "CDClientDatabase.h"
#include "CDClientManager.h" #include "CDClientManager.h"
#include "Database.h" #include "Database.h"
#include "MigrationRunner.h"
#include "Diagnostics.h" #include "Diagnostics.h"
#include "dCommonVars.h" #include "dCommonVars.h"
#include "dConfig.h" #include "dConfig.h"
@ -127,6 +128,13 @@ int main(int argc, char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (argc > 1 && (strcmp(argv[1], "-m") == 0 || strcmp(argv[1], "--migrations") == 0)) {
MigrationRunner::RunMigrations();
Game::logger->Log("MigrationRunner", "Finished running migrations\n");
return EXIT_SUCCESS;
}
//If the first command line argument is -a or --account then make the user //If the first command line argument is -a or --account then make the user
//input a username and password, with the password being hidden. //input a username and password, with the password being hidden.
if (argc > 1 && if (argc > 1 &&
@ -491,7 +499,10 @@ void HandlePacket(Packet* packet) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_NEW_SESSION_ALERT); PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_NEW_SESSION_ALERT);
bitStream.Write(sessionKey); bitStream.Write(sessionKey);
bitStream.Write(RakNet::RakString(username.c_str())); bitStream.Write<uint32_t>(username.size());
for (auto character : username) {
bitStream.Write(character);
}
SEND_PACKET_BROADCAST; SEND_PACKET_BROADCAST;
break; break;
@ -566,14 +577,20 @@ void HandlePacket(Packet* packet) {
uint32_t mapId; uint32_t mapId;
LWOCLONEID cloneId; LWOCLONEID cloneId;
RakNet::RakString password; std::string password;
inStream.Read(mapId); inStream.Read(mapId);
inStream.Read(cloneId); inStream.Read(cloneId);
inStream.Read(password);
Game::im->CreatePrivateInstance(mapId, cloneId, uint32_t len;
password.C_String()); inStream.Read<uint32_t>(len);
for (int i = 0; len > i; i++) {
char character;
inStream.Read<char>(character);
password += character;
}
Game::im->CreatePrivateInstance(mapId, cloneId, password.c_str());
break; break;
} }
@ -585,15 +602,22 @@ void HandlePacket(Packet* packet) {
uint64_t requestID = 0; uint64_t requestID = 0;
uint8_t mythranShift = false; uint8_t mythranShift = false;
RakNet::RakString password; std::string password;
inStream.Read(requestID); inStream.Read(requestID);
inStream.Read(mythranShift); inStream.Read(mythranShift);
inStream.Read(password);
uint32_t len;
inStream.Read<uint32_t>(len);
auto* instance = Game::im->FindPrivateInstance(password.C_String()); for (int i = 0; i < len; i++) {
char character; inStream.Read<char>(character);
password += character;
}
Game::logger->Log( "MasterServer", "Join private zone: %llu %d %s %p\n", requestID, mythranShift, password.C_String(), instance); auto* instance = Game::im->FindPrivateInstance(password.c_str());
Game::logger->Log( "MasterServer", "Join private zone: %llu %d %s %p\n", requestID, mythranShift, password.c_str(), instance);
if (instance == nullptr) { if (instance == nullptr) {
return; return;

11
dNet/CMakeLists.txt Normal file
View File

@ -0,0 +1,11 @@
set(DNET_SOURCES "AuthPackets.cpp"
"ChatPackets.cpp"
"ClientPackets.cpp"
"dServer.cpp"
"MasterPackets.cpp"
"PacketUtils.cpp"
"WorldPackets.cpp"
"ZoneInstanceManager.cpp")
add_library(dNet STATIC ${DNET_SOURCES})
target_link_libraries(dNet dCommon dDatabase)

View File

@ -43,8 +43,10 @@ void MasterPackets::SendZoneCreatePrivate(dServer* server, uint32_t zoneID, uint
bitStream.Write(zoneID); bitStream.Write(zoneID);
bitStream.Write(cloneID); bitStream.Write(cloneID);
RakNet::RakString passwd(password.c_str()); bitStream.Write<uint32_t>(password.size());
bitStream.Write(passwd); for (auto character : password) {
bitStream.Write<char>(character);
}
server->SendToMaster(&bitStream); server->SendToMaster(&bitStream);
} }
@ -56,8 +58,10 @@ void MasterPackets::SendZoneRequestPrivate(dServer* server, uint64_t requestID,
bitStream.Write(requestID); bitStream.Write(requestID);
bitStream.Write(static_cast<uint8_t>(mythranShift)); bitStream.Write(static_cast<uint8_t>(mythranShift));
RakNet::RakString passwd(password.c_str()); bitStream.Write<uint32_t>(password.size());
bitStream.Write(passwd); for (auto character : password) {
bitStream.Write<char>(character);
}
server->SendToMaster(&bitStream); server->SendToMaster(&bitStream);
} }

View File

@ -535,8 +535,10 @@ enum GAME_MSG : unsigned short {
GAME_MSG_REQUEST_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1666, GAME_MSG_REQUEST_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1666,
GAME_MSG_RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1667, GAME_MSG_RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1667,
GAME_MSG_PLAYER_SET_CAMERA_CYCLING_MODE = 1676, GAME_MSG_PLAYER_SET_CAMERA_CYCLING_MODE = 1676,
GAME_MSG_SET_MOUNT_INVENTORY_ID = 1726,
GAME_MSG_NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE = 1734, GAME_MSG_NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE = 1734,
GAME_MSG_NOTIFY_LEVEL_REWARDS = 1735, GAME_MSG_NOTIFY_LEVEL_REWARDS = 1735,
GAME_MSG_DISMOUNT_COMPLETE = 1756,
GAME_MSG_MARK_INVENTORY_ITEM_AS_ACTIVE = 1767, GAME_MSG_MARK_INVENTORY_ITEM_AS_ACTIVE = 1767,
END END
}; };

9
dPhysics/CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
set(DPHYSICS_SOURCES "dpCollisionChecks.cpp"
"dpEntity.cpp"
"dpGrid.cpp"
"dpShapeBase.cpp"
"dpShapeBox.cpp"
"dpShapeSphere.cpp"
"dpWorld.cpp")
add_library(dPhysics STATIC ${DPHYSICS_SOURCES})

View File

@ -169,11 +169,10 @@ void BasePropertyServer::BaseZonePropertyRented(Entity* self, Entity* player) co
EntityManager::Instance()->DestructEntity(plaque); EntityManager::Instance()->DestructEntity(plaque);
} }
if (self->GetVar<int32_t>(brickLinkMissionIDFlag) != 0) { auto brickLinkMissionID = self->GetVar<uint32_t>(brickLinkMissionIDFlag);
auto plaques = EntityManager::Instance()->GetEntitiesInGroup(self->GetVar<std::string>(PropertyPlaqueGroup)); if (brickLinkMissionID != 0) {
for (auto* plaque : plaques) { auto missionComponent = player->GetComponent<MissionComponent>();
EntityManager::Instance()->DestructEntity(plaque); if (missionComponent) missionComponent->CompleteMission(brickLinkMissionID, true);
}
} }
ActivateSpawner(self->GetVar<std::string>(PropObjsSpawner)); ActivateSpawner(self->GetVar<std::string>(PropObjsSpawner));

253
dScripts/CMakeLists.txt Normal file
View File

@ -0,0 +1,253 @@
set(DSCRIPT_SOURCES "ActivityManager.cpp"
"ActMine.cpp"
"ActNinjaTurret.cpp"
"ActParadoxPipeFix.cpp"
"ActPlayerDeathTrigger.cpp"
"ActSharkPlayerDeathTrigger.cpp"
"ActVehicleDeathTrigger.cpp"
"AgBugsprayer.cpp"
"AgBusDoor.cpp"
"AgCagedBricksServer.cpp"
"AgDarkSpiderling.cpp"
"AgFans.cpp"
"AgImagSmashable.cpp"
"AgJetEffectServer.cpp"
"AgLaserSensorServer.cpp"
"AgMonumentBirds.cpp"
"AgMonumentLaserServer.cpp"
"AgMonumentRaceCancel.cpp"
"AgMonumentRaceGoal.cpp"
"AgPicnicBlanket.cpp"
"AgPropGuard.cpp"
"AgPropguards.cpp"
"AgQbElevator.cpp"
"AgSalutingNpcs.cpp"
"AgShipPlayerDeathTrigger.cpp"
"AgShipPlayerShockServer.cpp"
"AgSpaceStuff.cpp"
"AgStagePlatforms.cpp"
"AgStromlingProperty.cpp"
"AgSurvivalBuffStation.cpp"
"AgSurvivalMech.cpp"
"AgSurvivalSpiderling.cpp"
"AgSurvivalStromling.cpp"
"AgTurret.cpp"
"AllCrateChicken.cpp"
"AmBlueX.cpp"
"AmBridge.cpp"
"AmConsoleTeleportServer.cpp"
"AmDarklingDragon.cpp"
"AmDarklingMech.cpp"
"AmDrawBridge.cpp"
"AmDropshipComputer.cpp"
"AmScrollReaderServer.cpp"
"AmShieldGenerator.cpp"
"AmShieldGeneratorQuickbuild.cpp"
"AmSkeletonEngineer.cpp"
"AmSkullkinDrill.cpp"
"AmSkullkinDrillStand.cpp"
"AmSkullkinTower.cpp"
"AmTeapotServer.cpp"
"AmTemplateSkillVolume.cpp"
"AnvilOfArmor.cpp"
"BankInteractServer.cpp"
"BaseConsoleTeleportServer.cpp"
"BaseEnemyApe.cpp"
"BaseEnemyMech.cpp"
"BaseFootRaceManager.cpp"
"BaseInteractDropLootServer.cpp"
"BasePropertyServer.cpp"
"BaseRandomServer.cpp"
"BaseSurvivalServer.cpp"
"BaseWavesGenericEnemy.cpp"
"BaseWavesServer.cpp"
"Binoculars.cpp"
"BootyDigServer.cpp"
"BossSpiderQueenEnemyServer.cpp"
"BuccaneerValiantShip.cpp"
"BurningTile.cpp"
"CatapultBaseServer.cpp"
"CatapultBouncerServer.cpp"
"CauldronOfLife.cpp"
"CavePrisonCage.cpp"
"ChooseYourDestinationNsToNt.cpp"
"ClRing.cpp"
"CppScripts.cpp"
"CrabServer.cpp"
"DamagingPets.cpp"
"Darkitect.cpp"
"DLUVanityNPC.cpp"
"EnemyNjBuff.cpp"
"EnemyRoninSpawner.cpp"
"EnemySkeletonSpawner.cpp"
"EnemySpiderSpawner.cpp"
"ExplodingAsset.cpp"
"FallingTile.cpp"
"FireFirstSkillonStartup.cpp"
"FlameJetServer.cpp"
"ForceVolumeServer.cpp"
"FountainOfImagination.cpp"
"FvBounceOverWall.cpp"
"FvBrickPuzzleServer.cpp"
"FvCandle.cpp"
"FvConsoleLeftQuickbuild.cpp"
"FvConsoleRightQuickbuild.cpp"
"FvDragonSmashingGolemQb.cpp"
"FvFacilityBrick.cpp"
"FvFlyingCreviceDragon.cpp"
"FvFong.cpp"
"FvFreeGfNinjas.cpp"
"FvHorsemenTrigger.cpp"
"FvMaelstromCavalry.cpp"
"FvMaelstromDragon.cpp"
"FvNinjaGuard.cpp"
"FvPandaServer.cpp"
"FvPandaSpawnerServer.cpp"
"FvPassThroughWall.cpp"
"FvRaceSmashEggImagineServer.cpp"
"GfApeSmashingQB.cpp"
"GfBanana.cpp"
"GfBananaCluster.cpp"
"GfCampfire.cpp"
"GfCaptainsCannon.cpp"
"GfJailkeepMission.cpp"
"GfJailWalls.cpp"
"GfOrgan.cpp"
"GfTikiTorch.cpp"
"GrowingFlower.cpp"
"HydrantBroken.cpp"
"HydrantSmashable.cpp"
"ImaginationBackpackHealServer.cpp"
"ImaginationShrineServer.cpp"
"ImgBrickConsoleQB.cpp"
"InstanceExitTransferPlayerToLastNonInstance.cpp"
"InvalidScript.cpp"
"LegoDieRoll.cpp"
"Lieutenant.cpp"
"MaestromExtracticatorServer.cpp"
"MailBoxServer.cpp"
"MastTeleport.cpp"
"MinigameTreasureChestServer.cpp"
"MonCoreNookDoors.cpp"
"MonCoreSmashableDoors.cpp"
"NjColeNPC.cpp"
"NjDragonEmblemChestServer.cpp"
"NjEarthDragonPetServer.cpp"
"NjEarthPetServer.cpp"
"NjGarmadonCelebration.cpp"
"NjhubLavaPlayerDeathTrigger.cpp"
"NjIceRailActivator.cpp"
"NjJayMissionItems.cpp"
"NjMonastryBossInstance.cpp"
"NjNPCMissionSpinjitzuServer.cpp"
"NjNyaMissionitems.cpp"
"NjRailActivatorsServer.cpp"
"NjRailPostServer.cpp"
"NjRailSwitch.cpp"
"NjScrollChestServer.cpp"
"NjWuNPC.cpp"
"NPCAddRemoveItem.cpp"
"NpcAgCourseStarter.cpp"
"NpcCowboyServer.cpp"
"NpcEpsilonServer.cpp"
"NpcNjAssistantServer.cpp"
"NpcNpSpacemanBob.cpp"
"NpcPirateServer.cpp"
"NpcWispServer.cpp"
"NsConcertChoiceBuild.cpp"
"NsConcertChoiceBuildManager.cpp"
"NsConcertInstrument.cpp"
"NsConcertQuickBuild.cpp"
"NsGetFactionMissionServer.cpp"
"NsJohnnyMissionServer.cpp"
"NsLegoClubDoor.cpp"
"NsLupTeleport.cpp"
"NsModularBuild.cpp"
"NsQbImaginationStatue.cpp"
"NsTokenConsoleServer.cpp"
"NtAssemblyTubeServer.cpp"
"NtBeamImaginationCollectors.cpp"
"NtCombatChallengeDummy.cpp"
"NtCombatChallengeExplodingDummy.cpp"
"NtCombatChallengeServer.cpp"
"NtConsoleTeleportServer.cpp"
"NtDarkitectRevealServer.cpp"
"NtDirtCloudServer.cpp"
"NtDukeServer.cpp"
"NtFactionSpyServer.cpp"
"NtHaelServer.cpp"
"NtImagBeamBuffer.cpp"
"NtOverbuildServer.cpp"
"NtParadoxPanelServer.cpp"
"NtParadoxTeleServer.cpp"
"NtSentinelWalkwayServer.cpp"
"NtSleepingGuard.cpp"
"NtVandaServer.cpp"
"NtVentureCannonServer.cpp"
"NtVentureSpeedPadServer.cpp"
"NtXRayServer.cpp"
"PersonalFortress.cpp"
"PetDigBuild.cpp"
"PetDigServer.cpp"
"PetFromDigServer.cpp"
"PetFromObjectServer.cpp"
"PropertyBankInteract.cpp"
"PropertyDeathPlane.cpp"
"PropertyDevice.cpp"
"PropertyFXDamage.cpp"
"PropertyPlatform.cpp"
"PrSeagullFly.cpp"
"PrWhistle.cpp"
"QbEnemyStunner.cpp"
"RaceImagineCrateServer.cpp"
"RaceImaginePowerup.cpp"
"RaceMaelstromGeiser.cpp"
"RaceSmashServer.cpp"
"RainOfArrows.cpp"
"RandomSpawnerFin.cpp"
"RandomSpawnerPit.cpp"
"RandomSpawnerStr.cpp"
"RandomSpawnerZip.cpp"
"RemoveRentalGear.cpp"
"RockHydrantBroken.cpp"
"RockHydrantSmashable.cpp"
"ScriptComponent.cpp"
"ScriptedPowerupSpawner.cpp"
"SGCannon.cpp"
"SpawnGryphonServer.cpp"
"SpawnLionServer.cpp"
"SpawnPetBaseServer.cpp"
"SpawnSaberCatServer.cpp"
"SpawnShrakeServer.cpp"
"SpawnStegoServer.cpp"
"SpecialImaginePowerupSpawner.cpp"
"SpiderBossTreasureChestServer.cpp"
"SsModularBuildServer.cpp"
"StinkyFishTarget.cpp"
"StoryBoxInteractServer.cpp"
"Sunflower.cpp"
"TokenConsoleServer.cpp"
"TouchMissionUpdateServer.cpp"
"TreasureChestDragonServer.cpp"
"TriggerAmbush.cpp"
"VeBricksampleServer.cpp"
"VeEpsilonServer.cpp"
"VeMech.cpp"
"VeMissionConsole.cpp"
"WaveBossApe.cpp"
"WaveBossHammerling.cpp"
"WaveBossHorsemen.cpp"
"WaveBossSpiderling.cpp"
"WhFans.cpp"
"WildAmbients.cpp"
"WishingWellServer.cpp"
"ZoneAgMedProperty.cpp"
"ZoneAgProperty.cpp"
"ZoneAgSpiderQueen.cpp"
"ZoneAgSurvival.cpp"
"ZoneFvProperty.cpp"
"ZoneGfProperty.cpp"
"ZoneNsMedProperty.cpp"
"ZoneNsProperty.cpp"
"ZoneNsWaves.cpp"
"ZoneSGServer.cpp" PARENT_SCOPE)

View File

@ -0,0 +1,6 @@
set(DWORLDSERVER_SOURCES "ObjectIDManager.cpp"
"PerformanceManager.cpp"
"WorldServer.cpp")
add_executable(WorldServer ${DWORLDSERVER_SOURCES})
target_link_libraries(WorldServer ${COMMON_LIBRARIES} dChatFilter dGame dZoneManager dPhysics detour recast tinyxml2)

View File

@ -546,18 +546,31 @@ void HandlePacketChat(Packet* packet) {
LWOOBJID header; LWOOBJID header;
inStream.Read(header); inStream.Read(header);
RakNet::RakString title; std::string title;
RakNet::RakString msg; std::string msg;
inStream.Read(title); uint32_t len;
inStream.Read(msg); inStream.Read<uint32_t>(len);
for (int i = 0; len > i; i++) {
char character;
inStream.Read<char>(character);
title += character;
}
len = 0;
inStream.Read<uint32_t>(len);
for (int i = 0; len > i; i++) {
char character;
inStream.Read<char>(character);
msg += character;
}
//Send to our clients: //Send to our clients:
AMFArrayValue args; AMFArrayValue args;
auto* titleValue = new AMFStringValue(); auto* titleValue = new AMFStringValue();
titleValue->SetStringValue(title.C_String()); titleValue->SetStringValue(title.c_str());
auto* messageValue = new AMFStringValue(); auto* messageValue = new AMFStringValue();
messageValue->SetStringValue(msg.C_String()); messageValue->SetStringValue(msg.c_str());
args.InsertValue("title", titleValue); args.InsertValue("title", titleValue);
args.InsertValue("message", messageValue); args.InsertValue("message", messageValue);
@ -669,10 +682,12 @@ void HandlePacket(Packet* packet) {
Game::logger->Log("WorldServer", "Deleting player %llu\n", entity->GetObjectID()); Game::logger->Log("WorldServer", "Deleting player %llu\n", entity->GetObjectID());
EntityManager::Instance()->DestroyEntity(entity); EntityManager::Instance()->DestroyEntity(entity);
}
{
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_PLAYER_REMOVED_NOTIFICATION); PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_PLAYER_REMOVED_NOTIFICATION);
bitStream.Write(c->GetObjectID()); bitStream.Write(user->GetLoggedInChar());
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} }
@ -802,19 +817,27 @@ void HandlePacket(Packet* packet) {
RakNet::BitStream inStream(packet->data, packet->length, false); RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header); uint64_t header = inStream.Read(header);
uint32_t sessionKey = inStream.Read(sessionKey); uint32_t sessionKey = inStream.Read(sessionKey);
RakNet::RakString username;
inStream.Read(username); std::string username;
uint32_t len;
inStream.Read(len);
for (int i = 0; i < len; i++) {
char character; inStream.Read<char>(character);
username += character;
}
//Find them: //Find them:
User* user = UserManager::Instance()->GetUser(username.C_String()); User* user = UserManager::Instance()->GetUser(username.c_str());
if (!user) { if (!user) {
Game::logger->Log("WorldServer", "Got new session alert for user %s, but they're not logged in.\n", username.C_String()); Game::logger->Log("WorldServer", "Got new session alert for user %s, but they're not logged in.\n", username.c_str());
return; return;
} }
//Check the key: //Check the key:
if (sessionKey != std::atoi(user->GetSessionKey().c_str())) { if (sessionKey != std::atoi(user->GetSessionKey().c_str())) {
Game::logger->Log("WorldServer", "Got new session alert for user %s, but the session key is invalid.\n", username.C_String()); Game::logger->Log("WorldServer", "Got new session alert for user %s, but the session key is invalid.\n", username.c_str());
Game::server->Disconnect(user->GetSystemAddress(), SERVER_DISCON_INVALID_SESSION_KEY); Game::server->Disconnect(user->GetSystemAddress(), SERVER_DISCON_INVALID_SESSION_KEY);
return; return;
} }
@ -930,6 +953,19 @@ void HandlePacket(Packet* packet) {
playerID = GeneralUtils::ClearBit(playerID, OBJECT_BIT_CHARACTER); playerID = GeneralUtils::ClearBit(playerID, OBJECT_BIT_CHARACTER);
playerID = GeneralUtils::ClearBit(playerID, OBJECT_BIT_PERSISTENT); playerID = GeneralUtils::ClearBit(playerID, OBJECT_BIT_PERSISTENT);
auto user = UserManager::Instance()->GetUser(packet->systemAddress);
if (user) {
auto lastCharacter = user->GetLoggedInChar();
// This means we swapped characters and we need to remove the previous player from the container.
if (static_cast<uint32_t>(lastCharacter) != playerID) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_PLAYER_REMOVED_NOTIFICATION);
bitStream.Write(lastCharacter);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
}
UserManager::Instance()->LoginCharacter(packet->systemAddress, static_cast<uint32_t>(playerID)); UserManager::Instance()->LoginCharacter(packet->systemAddress, static_cast<uint32_t>(playerID));
break; break;
} }
@ -1107,14 +1143,12 @@ void HandlePacket(Packet* packet) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_PLAYER_ADDED_NOTIFICATION); PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_PLAYER_ADDED_NOTIFICATION);
bitStream.Write(player->GetObjectID()); bitStream.Write(player->GetObjectID());
bitStream.Write<uint16_t>(playerName.size()); bitStream.Write<uint32_t>(playerName.size());
for (size_t i = 0; i < playerName.size(); i++) for (size_t i = 0; i < playerName.size(); i++)
{ {
bitStream.Write(playerName[i]); bitStream.Write(playerName[i]);
} }
//bitStream.Write(playerName);
auto zone = dZoneManager::Instance()->GetZone()->GetZoneID(); auto zone = dZoneManager::Instance()->GetZone()->GetZoneID();
bitStream.Write(zone.GetMapID()); bitStream.Write(zone.GetMapID());
bitStream.Write(zone.GetInstanceID()); bitStream.Write(zone.GetInstanceID());

View File

@ -0,0 +1,6 @@
set(DZONEMANAGER_SOURCES "dZoneManager.cpp"
"Level.cpp"
"Spawner.cpp"
"Zone.cpp")
add_library(dZoneManager STATIC ${DZONEMANAGER_SOURCES})

View File

@ -1,2 +1 @@
-- File added April 9th, 2022
UPDATE ItemComponent SET itemType = 5 where id = 7082; UPDATE ItemComponent SET itemType = 5 where id = 7082;

View File

@ -1 +1 @@
ALTER TABLE bug_reports ADD reporter_id INT NOT NULL DEFAULT 0; ALTER TABLE bug_reports ADD (reporter_id) INT NOT NULL DEFAULT 0;

View File

@ -0,0 +1 @@
UPDATE friends SET player_id = player_id % 0x100000000, friend_id = friend_id % 0x100000000;

View File

@ -60,3 +60,7 @@ classic_survival_scoring=0
# If this value is 1, pets will consume imagination as they did in live. if 0 they will not consume imagination at all. # If this value is 1, pets will consume imagination as they did in live. if 0 they will not consume imagination at all.
pets_take_imagination=1 pets_take_imagination=1
# If you would like to increase the maximum number of best friends a player can have on the server
# Change the value below to what you would like this to be (5 is live accurate)
max_number_of_best_friends=5

View File

@ -1,17 +1,13 @@
# create the testing file and list of tests # create the testing file and list of tests
create_test_sourcelist (Tests create_test_sourcelist (Tests
CommonCxxTests.cpp CommonCxxTests.cpp
TestNiPoint3.cpp TestNiPoint3.cpp
TestLDFFormat.cpp TestLDFFormat.cpp
) )
# add the executable # add the executable
add_executable (CommonCxxTests ${Tests}) add_executable (CommonCxxTests ${Tests})
target_link_libraries(CommonCxxTests dCommon raknet) target_link_libraries(CommonCxxTests ${COMMON_LIBRARIES})
if(WIN32)
target_link_libraries(CommonCxxTests ws2_32)
endif(WIN32)
# remove the test driver source file # remove the test driver source file
set (TestsToRun ${Tests}) set (TestsToRun ${Tests})
@ -19,7 +15,7 @@ remove (TestsToRun CommonCxxTests.cpp)
# Add all the ADD_TEST for each test # Add all the ADD_TEST for each test
foreach (test ${TestsToRun}) foreach (test ${TestsToRun})
get_filename_component (TName ${test} NAME_WE) get_filename_component (TName ${test} NAME_WE)
add_test (NAME ${TName} COMMAND CommonCxxTests ${TName}) add_test (NAME ${TName} COMMAND CommonCxxTests ${TName})
set_property(TEST ${TName} PROPERTY ENVIRONMENT CTEST_OUTPUT_ON_FAILURE=1) set_property(TEST ${TName} PROPERTY ENVIRONMENT CTEST_OUTPUT_ON_FAILURE=1)
endforeach () endforeach ()

View File

@ -1,50 +1,50 @@
# Source Code for raknet # Source Code for raknet
file( file(
GLOB SOURCES_RAKNET GLOB SOURCES_RAKNET
LIST_DIRECTORIES false LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/raknet/Source/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/raknet/Source/*.cpp
) )
# Source Code for recast # Source Code for recast
file( file(
GLOB SOURCES_RECAST GLOB SOURCES_RECAST
LIST_DIRECTORIES false LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/recastnavigation/Recast/Source/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/recastnavigation/Recast/Source/*.cpp
) )
# Source Code for detour # Source Code for detour
file( file(
GLOB SOURCES_DETOUR GLOB SOURCES_DETOUR
LIST_DIRECTORIES false LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/recastnavigation/Detour/Source/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/recastnavigation/Detour/Source/*.cpp
) )
# Source Code for tinyxml2 # Source Code for tinyxml2
file( file(
GLOB SOURCES_TINYXML2 GLOB SOURCES_TINYXML2
LIST_DIRECTORIES false LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/tinyxml2/tinyxml2.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tinyxml2/tinyxml2.cpp
) )
# Source Code for libbcrypt # Source Code for libbcrypt
file( file(
GLOB SOURCES_LIBBCRYPT GLOB SOURCES_LIBBCRYPT
LIST_DIRECTORIES false LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/*.c ${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/*.c
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/src/*.c ${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/src/*.c
) )
file( file(
GLOB SOURCES_SQLITE3 GLOB SOURCES_SQLITE3
LIST_DIRECTORIES false LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/SQLite/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/SQLite/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/SQLite/*.c ${CMAKE_CURRENT_SOURCE_DIR}/SQLite/*.c
) )
# mariadb connector cpp # mariadb connector cpp
@ -147,7 +147,6 @@ else() # Build from source
set(MARIADB_SHARED_LIBRARY_LOCATION "${BINARY_DIR}/RelWithDebInfo/${MARIADB_SHARED_LIBRARY_NAME}") set(MARIADB_SHARED_LIBRARY_LOCATION "${BINARY_DIR}/RelWithDebInfo/${MARIADB_SHARED_LIBRARY_NAME}")
set(MARIADB_SHARED_LIBRARY_COPY_LOCATION "${PROJECT_BINARY_DIR}/$<CONFIG>") set(MARIADB_SHARED_LIBRARY_COPY_LOCATION "${PROJECT_BINARY_DIR}/$<CONFIG>")
set(MARIADB_PLUGINS_LOCATION "${BINARY_DIR}/libmariadb/RelWithDebInfo") set(MARIADB_PLUGINS_LOCATION "${BINARY_DIR}/libmariadb/RelWithDebInfo")
message(STATUS "1 ${CMAKE_SOURCE_DIR}")
else() else()
set(MARIADB_SHARED_LIBRARY_LOCATION "${BINARY_DIR}/${MARIADB_SHARED_LIBRARY_NAME}") set(MARIADB_SHARED_LIBRARY_LOCATION "${BINARY_DIR}/${MARIADB_SHARED_LIBRARY_NAME}")
set(MARIADB_SHARED_LIBRARY_COPY_LOCATION "${PROJECT_BINARY_DIR}") set(MARIADB_SHARED_LIBRARY_COPY_LOCATION "${PROJECT_BINARY_DIR}")
@ -179,16 +178,19 @@ if(EXISTS "${CMAKE_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/test/CMakeLists.
file(REMOVE "${CMAKE_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/test/CMakeLists.txt") file(REMOVE "${CMAKE_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/test/CMakeLists.txt")
endif() endif()
# Create mariadb connector library object
add_library(mariadbConnCpp SHARED IMPORTED GLOBAL) add_library(mariadbConnCpp SHARED IMPORTED GLOBAL)
set_property(TARGET mariadbConnCpp PROPERTY IMPORTED_LOCATION ${MARIADB_SHARED_LIBRARY_LOCATION}) set_property(TARGET mariadbConnCpp PROPERTY IMPORTED_LOCATION ${MARIADB_SHARED_LIBRARY_LOCATION})
if(WIN32) if(WIN32)
set_property(TARGET mariadbConnCpp PROPERTY IMPORTED_IMPLIB ${MARIADB_IMPLIB_LOCATION}) set_property(TARGET mariadbConnCpp PROPERTY IMPORTED_IMPLIB ${MARIADB_IMPLIB_LOCATION})
endif() endif()
# Add directories to include lists
target_include_directories(mariadbConnCpp INTERFACE ${MARIADB_INCLUDE_DIR}) target_include_directories(mariadbConnCpp INTERFACE ${MARIADB_INCLUDE_DIR})
add_dependencies(mariadbConnCpp mariadb_connector_cpp) add_dependencies(mariadbConnCpp mariadb_connector_cpp)
# 3rdparty static libraries: # Create our third party library objects
#add_library(zlib ${SOURCES_ZLIB})
add_library(raknet ${SOURCES_RAKNET}) add_library(raknet ${SOURCES_RAKNET})
add_library(tinyxml2 ${SOURCES_TINYXML2}) add_library(tinyxml2 ${SOURCES_TINYXML2})
add_library(detour ${SOURCES_DETOUR}) add_library(detour ${SOURCES_DETOUR})
@ -196,11 +198,39 @@ add_library(recast ${SOURCES_RECAST})
add_library(libbcrypt ${SOURCES_LIBBCRYPT}) add_library(libbcrypt ${SOURCES_LIBBCRYPT})
add_library(sqlite3 ${SOURCES_SQLITE3}) add_library(sqlite3 ${SOURCES_SQLITE3})
if(UNIX) if(WIN32)
target_link_libraries(sqlite3 pthread dl m) # Link Win Sockets 2 to RakNet
target_link_libraries(raknet ws2_32)
elseif(UNIX)
# Add warning disable flags and link Unix libraries to sqlite3
target_link_libraries(sqlite3 pthread dl m)
# -Wno-unused-result -Wno-unknown-pragmas -fpermissive # -Wno-unused-result -Wno-unknown-pragmas -fpermissive
target_compile_options(sqlite3 PRIVATE "-Wno-return-local-addr" "-Wno-maybe-uninitialized") target_compile_options(sqlite3 PRIVATE "-Wno-return-local-addr" "-Wno-maybe-uninitialized")
target_compile_options(raknet PRIVATE "-Wno-write-strings" "-Wformat-overflow=0" "-Wformat=0") target_compile_options(raknet PRIVATE "-Wno-write-strings" "-Wformat-overflow=0" "-Wformat=0")
target_compile_options(libbcrypt PRIVATE "-Wno-implicit-function-declaration" "-Wno-int-conversion") target_compile_options(libbcrypt PRIVATE "-Wno-implicit-function-declaration" "-Wno-int-conversion")
endif(UNIX) endif()
# Download Backtrace if configured
if(UNIX AND NOT APPLE)
include(FetchContent)
if (__include_backtrace__ AND __compile_backtrace__)
FetchContent_Declare(
backtrace
GIT_REPOSITORY https://github.com/ianlancetaylor/libbacktrace.git
)
FetchContent_MakeAvailable(backtrace)
if (NOT EXISTS ${backtrace_SOURCE_DIR}/.libs)
set(backtrace_make_cmd "${backtrace_SOURCE_DIR}/configure --prefix=\"/usr\" --enable-shared --with-system-libunwind")
execute_process(
COMMAND bash -c "cd ${backtrace_SOURCE_DIR} && ${backtrace_make_cmd} && make && cd ${CMAKE_SOURCE_DIR}"
)
endif()
link_directories(${backtrace_SOURCE_DIR}/.libs/)
include_directories(${backtrace_SOURCE_DIR})
endif()
endif()

View File

@ -14,6 +14,8 @@
#ifndef CPPLINQ__HEADER_GUARD #ifndef CPPLINQ__HEADER_GUARD
# define CPPLINQ__HEADER_GUARD # define CPPLINQ__HEADER_GUARD
#undef min
#undef max
#define NOMINMAX #define NOMINMAX
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------