mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-21 11:59:37 -06:00
Compare commits
51 Commits
fix-disman
...
cmake-reor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b819d508b | ||
|
|
de3fe93100 | ||
|
|
af943278fb | ||
|
|
bfe6900c26 | ||
|
|
0d218fc5c7 | ||
|
|
102e3556cf | ||
|
|
33a8efdd22 | ||
|
|
8d54db7851 | ||
|
|
6213aed8e5 | ||
|
|
d57c5101f4 | ||
|
|
6be65569de | ||
|
|
94b9731a2b | ||
|
|
aaf446fe6e | ||
|
|
2c70f1503c | ||
|
|
49b4748ed3 | ||
|
|
ffeb0108d0 | ||
|
|
c3f6ef5a1d | ||
|
|
999995b2fb | ||
|
|
84fff7c380 | ||
|
|
59c4b35479 | ||
|
|
f2d72e7ed5 | ||
|
|
b648b43c4d | ||
|
|
2628470482 | ||
|
|
f82a82f254 | ||
|
|
9400ee1dc0 | ||
|
|
54b8c25754 | ||
|
|
b984cd6a0b | ||
|
|
bcf1058759 | ||
|
|
6ad6e930c7 | ||
|
|
fee0238e79 | ||
|
|
1454fcd003 | ||
|
|
af651f0d63 | ||
| ff38503597 | |||
| 3f22bf5cc0 | |||
| 9d5d2a68ee | |||
| 1a14c29c39 | |||
| 2ef45bd7ee | |||
| b56d077892 | |||
|
|
a54600b41e | ||
|
|
342da927f5 | ||
|
|
01086d05c8 | ||
| 86f335d64b | |||
| 8ca05241f2 | |||
|
|
db2d4f02b5 | ||
|
|
00f36f3f28 | ||
|
|
a50b256689 | ||
|
|
b3548de7da | ||
|
|
387c37505c | ||
|
|
0c4108e730 | ||
|
|
fd1c6ab2ea | ||
|
|
f2bf9a2a28 |
30
.github/workflows/build-and-test.yml
vendored
30
.github/workflows/build-and-test.yml
vendored
@@ -16,12 +16,12 @@ jobs:
|
||||
os: [ windows-2022, ubuntu-22.04, macos-13 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Add msbuild to PATH (Windows only)
|
||||
if: ${{ matrix.os == 'windows-2022' }}
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
vs-version: '[17,18)'
|
||||
msbuild-architecture: x64
|
||||
@@ -33,21 +33,19 @@ jobs:
|
||||
- name: cmake
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
configurePreset: "ci-${{matrix.os}}"
|
||||
buildPreset: "ci-${{matrix.os}}"
|
||||
testPreset: "ci-${{matrix.os}}"
|
||||
workflowPreset: "ci-${{matrix.os}}"
|
||||
- name: artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{matrix.os}}
|
||||
path: |
|
||||
build/*Server*
|
||||
build/*.ini
|
||||
build/*.so
|
||||
build/*.dll
|
||||
build/vanity/
|
||||
build/navmeshes/
|
||||
build/migrations/
|
||||
build/*.dcf
|
||||
!build/*.pdb
|
||||
!build/d*/
|
||||
build/*/*Server*
|
||||
build/*/*.ini
|
||||
build/*/*.so
|
||||
build/*/*.dll
|
||||
build/*/vanity/
|
||||
build/*/navmeshes/
|
||||
build/*/migrations/
|
||||
build/*/*.dcf
|
||||
!build/*/*.pdb
|
||||
!build/*/d*/
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -122,4 +122,7 @@ docker/__pycache__
|
||||
docker-compose.override.yml
|
||||
|
||||
!*Test.bin
|
||||
|
||||
# CMake scripts
|
||||
!cmake/*
|
||||
!cmake/toolchains/*
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(Darkflame)
|
||||
project(Darkflame
|
||||
HOMEPAGE_URL "https://github.com/DarkflameUniverse/DarkflameServer"
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
# check if the path to the source directory contains a space
|
||||
if("${CMAKE_SOURCE_DIR}" MATCHES " ")
|
||||
@@ -8,8 +11,10 @@ endif()
|
||||
|
||||
include(CTest)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
|
||||
@@ -61,35 +66,36 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
# Disabled no-register
|
||||
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
|
||||
if(UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC")
|
||||
add_compile_options("-fPIC")
|
||||
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)
|
||||
|
||||
if(NOT APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -lstdc++fs")
|
||||
# For all except Clang and Apple Clang
|
||||
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options("-static-libgcc" "-lstdc++fs")
|
||||
endif()
|
||||
|
||||
if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
|
||||
add_compile_options("-rdynamic")
|
||||
endif()
|
||||
|
||||
if(${GGDB})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
|
||||
add_compile_options("-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
|
||||
# Also disable non-portable MSVC volatile behavior
|
||||
add_compile_options("/wd4267" "/utf-8" "/volatile:iso")
|
||||
add_compile_options("/wd4267" "/utf-8" "/volatile:iso" "/Zc:inline")
|
||||
elseif(WIN32)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
# Our output dir
|
||||
set(CMAKE_BINARY_DIR ${PROJECT_BINARY_DIR})
|
||||
#set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) # unfortunately, forces all libraries to be built in series, which will slow down the build process
|
||||
|
||||
# TODO make this not have to override the build type directories
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||
@@ -109,31 +115,39 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
|
||||
# Create a /logs directory
|
||||
make_directory(${CMAKE_BINARY_DIR}/logs)
|
||||
|
||||
# Get DLU config directory
|
||||
if(DEFINED ENV{DLU_CONFIG_DIR})
|
||||
set(DLU_CONFIG_DIR $ENV{DLU_CONFIG_DIR})
|
||||
else()
|
||||
set(DLU_CONFIG_DIR ${PROJECT_BINARY_DIR})
|
||||
endif()
|
||||
message(STATUS "Variable: DLU_CONFIG_DIR = ${DLU_CONFIG_DIR}")
|
||||
|
||||
# Copy resource files on first build
|
||||
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
|
||||
message(STATUS "Checking resource file integrity")
|
||||
|
||||
include(Utils)
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/authconfig.ini "port" "auth_server_port")
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/chatconfig.ini "port" "chat_server_port")
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/masterconfig.ini "port" "master_server_port")
|
||||
UpdateConfigOption(${DLU_CONFIG_DIR}/authconfig.ini "port" "auth_server_port")
|
||||
UpdateConfigOption(${DLU_CONFIG_DIR}/chatconfig.ini "port" "chat_server_port")
|
||||
UpdateConfigOption(${DLU_CONFIG_DIR}/masterconfig.ini "port" "master_server_port")
|
||||
|
||||
foreach(resource_file ${RESOURCE_FILES})
|
||||
set(file_size 0)
|
||||
|
||||
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
|
||||
if(EXISTS ${DLU_CONFIG_DIR}/${resource_file})
|
||||
file(SIZE ${DLU_CONFIG_DIR}/${resource_file} file_size)
|
||||
endif()
|
||||
|
||||
if(${file_size} EQUAL 0)
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
|
||||
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${DLU_CONFIG_DIR}/${resource_file}
|
||||
COPYONLY
|
||||
)
|
||||
message(STATUS "Moved " ${resource_file} " to project binary directory")
|
||||
message(STATUS "Moved " ${resource_file} " to DLU config directory")
|
||||
elseif(resource_file MATCHES ".ini")
|
||||
message(STATUS "Checking " ${resource_file} " for missing config options")
|
||||
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents)
|
||||
file(READ ${DLU_CONFIG_DIR}/${resource_file} current_file_contents)
|
||||
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
|
||||
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
|
||||
set(parsed_current_file_contents "")
|
||||
@@ -164,10 +178,10 @@ foreach(resource_file ${RESOURCE_FILES})
|
||||
set(line_to_add ${line_to_add} ${line})
|
||||
|
||||
foreach(line_to_append ${line_to_add})
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
|
||||
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n" ${line_to_append})
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
|
||||
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n")
|
||||
endif()
|
||||
|
||||
set(line_to_add "")
|
||||
@@ -236,14 +250,15 @@ include_directories(
|
||||
"tests/dGameTests"
|
||||
"tests/dGameTests/dComponentsTests"
|
||||
|
||||
SYSTEM "thirdparty/magic_enum/include/magic_enum"
|
||||
SYSTEM "thirdparty/raknet/Source"
|
||||
SYSTEM "thirdparty/tinyxml2"
|
||||
SYSTEM "thirdparty/recastnavigation"
|
||||
SYSTEM "thirdparty/SQLite"
|
||||
SYSTEM "thirdparty/cpplinq"
|
||||
SYSTEM "thirdparty/cpp-httplib"
|
||||
SYSTEM "thirdparty/MD5"
|
||||
SYSTEM
|
||||
"thirdparty/magic_enum/include/magic_enum"
|
||||
"thirdparty/raknet/Source"
|
||||
"thirdparty/tinyxml2"
|
||||
"thirdparty/recastnavigation"
|
||||
"thirdparty/SQLite"
|
||||
"thirdparty/cpplinq"
|
||||
"thirdparty/cpp-httplib"
|
||||
"thirdparty/MD5"
|
||||
)
|
||||
|
||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
||||
@@ -252,10 +267,17 @@ if(APPLE)
|
||||
include_directories("/usr/local/include/")
|
||||
endif()
|
||||
|
||||
# Add linking directories:
|
||||
if (UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast -Werror") # Warning flags
|
||||
# Set warning flags
|
||||
if(MSVC)
|
||||
# add_compile_options("/W4")
|
||||
# Want to enable warnings eventually, but WAY too much noise right now
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||
add_compile_options("-Wuninitialized" "-Wold-style-cast")
|
||||
else()
|
||||
message(WARNING "Unknown compiler: '${CMAKE_CXX_COMPILER_ID}' - No warning flags enabled.")
|
||||
endif()
|
||||
|
||||
# Add linking directories:
|
||||
file(
|
||||
GLOB HEADERS_DZONEMANAGER
|
||||
LIST_DIRECTORIES false
|
||||
|
||||
@@ -1,128 +1,641 @@
|
||||
{
|
||||
"version": 3,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 14,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default configure step",
|
||||
"description": "Use 'build' dir and Unix makefiles",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"generator": "Unix Makefiles"
|
||||
},
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"displayName": "CI configure step for Ubuntu",
|
||||
"description": "Same as default, Used in GitHub actions workflow",
|
||||
"inherits": "default"
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"displayName": "CI configure step for MacOS",
|
||||
"description": "Same as default, Used in GitHub actions workflow",
|
||||
"inherits": "default"
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"displayName": "CI configure step for Windows",
|
||||
"description": "Set architecture to 64-bit (b/c RakNet)",
|
||||
"inherits": "default",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"architecture": {
|
||||
"value": "x64"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-default",
|
||||
"inherits": "ci-windows-2022",
|
||||
"displayName": "Windows only Configure Settings",
|
||||
"description": "Sets build and install directories",
|
||||
"generator": "Ninja",
|
||||
"architecture": {
|
||||
"value": "x64",
|
||||
"strategy": "external"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"displayName": "Default Build",
|
||||
"description": "Default Build",
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"configurePreset": "ci-windows-2022",
|
||||
"displayName": "Windows CI Build",
|
||||
"description": "This preset is used by the CI build on windows",
|
||||
"configuration": "RelWithDebInfo",
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"configurePreset": "ci-ubuntu-22.04",
|
||||
"displayName": "Linux CI Build",
|
||||
"description": "This preset is used by the CI build on linux",
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"configurePreset": "ci-macos-13",
|
||||
"displayName": "MacOS CI Build",
|
||||
"description": "This preset is used by the CI build on MacOS",
|
||||
"jobs": 2
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"configurePreset": "ci-ubuntu-22.04",
|
||||
"displayName": "CI Tests on Linux",
|
||||
"description": "Runs all tests on a linux configuration",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
"version": 6,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 25,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default configure step",
|
||||
"description": "Use 'build' dir and Unix makefiles",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"environment": {
|
||||
"DLU_CONFIG_DIR": "${sourceDir}/build"
|
||||
},
|
||||
"generator": "Unix Makefiles"
|
||||
},
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"configurePreset": "ci-macos-13",
|
||||
"displayName": "CI Tests on MacOS",
|
||||
"description": "Runs all tests on a Mac configuration",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
},
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"configurePreset": "ci-windows-2022",
|
||||
"displayName": "CI Tests on windows",
|
||||
"description": "Runs all tests on a windows configuration",
|
||||
"configuration": "RelWithDebInfo",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
},
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
},
|
||||
"filter": {
|
||||
"exclude": {
|
||||
"name": "((example)|(minigzip))+"
|
||||
{
|
||||
"name": "debug-config",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "relwithdebinfo-config",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release-config",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-config",
|
||||
"hidden": true,
|
||||
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-clang.cmake"
|
||||
},
|
||||
{
|
||||
"name": "gnu-config",
|
||||
"hidden": true,
|
||||
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-gnu.cmake"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc",
|
||||
"inherits": "default",
|
||||
"displayName": "[Multi] Windows (MSVC)",
|
||||
"description": "Set architecture to 64-bit (b/c RakNet)",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"binaryDir": "${sourceDir}/build/msvc",
|
||||
"architecture": {
|
||||
"value": "x64"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-default",
|
||||
"inherits": "windows-msvc",
|
||||
"displayName": "Windows only Configure Settings",
|
||||
"description": "Sets build and install directories",
|
||||
"generator": "Ninja",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"architecture": {
|
||||
"value": "x64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-config",
|
||||
"inherits": "default",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-debug",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"clang-config",
|
||||
"debug-config"
|
||||
],
|
||||
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
|
||||
"description": "Create a debug build using the Clang toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/clang-debug"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-relwithdebinfo",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"clang-config",
|
||||
"relwithdebinfo-config"
|
||||
],
|
||||
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
|
||||
"description": "Create a release build with debug info using the Clang toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/clang-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"clang-config",
|
||||
"release-config"
|
||||
],
|
||||
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
|
||||
"description": "Create a release build using the Clang toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/clang-release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-debug",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"gnu-config",
|
||||
"debug-config"
|
||||
],
|
||||
"displayName": "[Debug] Linux (GNU)",
|
||||
"description": "Create a debug build using the GNU toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/gnu-debug"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-relwithdebinfo",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"gnu-config",
|
||||
"relwithdebinfo-config"
|
||||
],
|
||||
"displayName": "[RelWithDebInfo] Linux (GNU)",
|
||||
"description": "Create a release build with debug info using the GNU toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/gnu-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-release",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"gnu-config",
|
||||
"release-config"
|
||||
],
|
||||
"displayName": "[Release] Linux (GNU)",
|
||||
"description": "Create a release build using the GNU toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/gnu-release"
|
||||
},
|
||||
{
|
||||
"name": "macos",
|
||||
"inherits": "default",
|
||||
"displayName": "[Multi] MacOS",
|
||||
"description": "Create a build for MacOS",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"binaryDir": "${sourceDir}/build/macos"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"displayName": "Default Build",
|
||||
"description": "Default Build",
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[Debug] Windows (MSVC)",
|
||||
"description": "This preset is used to build in debug mode using the MSVC toolchain on Windows",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[RelWithDebInfo] Windows (MSVC)",
|
||||
"description": "This preset is used to build in debug mode using the MSVC toolchain on Windows",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[Release] Windows (MSVC)",
|
||||
"description": "This preset is used to build in release mode using the MSVC toolchain on Windows",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-debug",
|
||||
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
|
||||
"description": "This preset is used to build in debug mode using the Clang toolchain on Linux",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-relwithdebinfo",
|
||||
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
|
||||
"description": "This preset is used to build in release mode with debug info using the Clang toolchain on Linux",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-release",
|
||||
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
|
||||
"description": "This preset is used to build in release mode using the Clang toolchain on Linux",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-debug",
|
||||
"displayName": "[Debug] Linux (GNU)",
|
||||
"description": "This preset is used to build in debug mode using the GNU toolchain on Linux",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] Linux (GNU)",
|
||||
"description": "This preset is used to build in release mode with debug info using the GNU toolchain on Linux",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-release",
|
||||
"displayName": "[Release] Linux (GNU)",
|
||||
"description": "This preset is used to build in release mode using the GNU toolchain on Linux",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "macos-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[Debug] MacOS",
|
||||
"description": "This preset is used to build in debug mode on MacOS",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "macos-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[RelWithDebInfo] MacOS",
|
||||
"description": "This preset is used to build in release mode with debug info on MacOS",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "macos-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[Release] MacOS",
|
||||
"description": "This preset is used to build in release mode on MacOS",
|
||||
"configuration": "Release"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
},
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-test",
|
||||
"inherits": "default",
|
||||
"configurePreset": "windows-msvc",
|
||||
"hidden": true,
|
||||
"filter": {
|
||||
"exclude": {
|
||||
"name": "((example)|(minigzip))+"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-debug",
|
||||
"inherits": "windows-msvc-test",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[Debug] Windows (MSVC)",
|
||||
"description": "Runs all tests on a Windows configuration",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-relwithdebinfo",
|
||||
"inherits": "windows-msvc-test",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[RelWithDebInfo] Windows (MSVC)",
|
||||
"description": "Runs all tests on a Windows configuration",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-release",
|
||||
"inherits": "windows-msvc-test",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[Release] Windows (MSVC)",
|
||||
"description": "Runs all tests on a Windows configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-debug",
|
||||
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
|
||||
"description": "Runs all tests on a Linux Clang configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-relwithdebinfo",
|
||||
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
|
||||
"description": "Runs all tests on a Linux Clang configuration",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-release",
|
||||
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
|
||||
"description": "Runs all tests on a Linux Clang configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-debug",
|
||||
"displayName": "[Debug] Linux (GNU)",
|
||||
"description": "Runs all tests on a Linux GNU configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] Linux (GNU)",
|
||||
"description": "Runs all tests on a Linux GNU configuration",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-release",
|
||||
"displayName": "[Release] Linux (GNU)",
|
||||
"description": "Runs all tests on a Linux GNU configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "macos-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[Debug] MacOS",
|
||||
"description": "Runs all tests on a MacOS configuration",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "macos-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[RelWithDebInfo] MacOS",
|
||||
"description": "Runs all tests on a MacOS configuration",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "macos-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[Release] MacOS",
|
||||
"description": "Runs all tests on a MacOS configuration",
|
||||
"configuration": "Release"
|
||||
}
|
||||
],
|
||||
"workflowPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "default"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "default"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-debug",
|
||||
"displayName": "[Debug] Windows (MSVC)",
|
||||
"description": "MSVC debug workflow preset for Windows",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows-msvc"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows-msvc-debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows-msvc-debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] Windows (MSVC)",
|
||||
"description": "MSVC release with debug info workflow preset for Windows",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows-msvc"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows-msvc-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows-msvc-relwithdebinfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"displayName": "[Release] Windows (MSVC)",
|
||||
"description": "CI workflow preset for Windows",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows-msvc"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows-msvc-release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows-msvc-release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-debug",
|
||||
"displayName": "[Debug] Linux (GNU)",
|
||||
"description": "GNU debug workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-gnu-debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-gnu-debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-gnu-debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] Linux (GNU)",
|
||||
"description": "GNU release with debug info workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-gnu-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-gnu-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-gnu-relwithdebinfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"displayName": "[Release] Linux (GNU)",
|
||||
"description": "CI workflow preset for Ubuntu",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-gnu-release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-gnu-release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-gnu-release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-debug",
|
||||
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
|
||||
"description": "Clang debug workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-clang-debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-clang-debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-clang-debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-relwithdebinfo",
|
||||
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
|
||||
"description": "Clang release with debug info workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-clang-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-clang-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-clang-relwithdebinfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
|
||||
"description": "Clang release workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-clang-release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-clang-release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-clang-release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "macos-debug",
|
||||
"displayName": "[Debug] MacOS",
|
||||
"description": "Release workflow preset for MacOS",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos-debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos-debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "macos-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] MacOS",
|
||||
"description": "Release with debug info workflow preset for MacOS",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos-relwithdebinfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"displayName": "[Release] MacOS",
|
||||
"description": "CI workflow preset for MacOS",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos-release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos-release"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
3
build.sh
3
build.sh
@@ -6,8 +6,7 @@ mkdir -p build
|
||||
cd build
|
||||
|
||||
# Run cmake to generate make files
|
||||
cmake ..
|
||||
cmake -DCMAKE_BUILD_TYPE="Release" ..
|
||||
|
||||
# To build utilizing multiple cores, append `-j` and the amount of cores to utilize, for example `cmake --build . --config Release -j8'
|
||||
cmake --build . --config Release $1
|
||||
|
||||
|
||||
14
cmake/toolchains/linux-clang.cmake
Normal file
14
cmake/toolchains/linux-clang.cmake
Normal file
@@ -0,0 +1,14 @@
|
||||
# Try and find a clang-16 install, falling back to a generic clang install otherwise
|
||||
find_program(CLANG_C_COMPILER clang-16 | clang REQUIRED)
|
||||
find_program(CLANG_CXX_COMPILER clang++-16 | clang++ REQUIRED)
|
||||
|
||||
# Debug messages
|
||||
message(DEBUG "CLANG_C_COMPILER = ${CLANG_C_COMPILER}")
|
||||
message(DEBUG "CLANG_CXX_COMPILER = ${CLANG_CXX_COMPILER}")
|
||||
|
||||
# Set compilers to clang
|
||||
set(CMAKE_C_COMPILER ${CLANG_C_COMPILER})
|
||||
set(CMAKE_CXX_COMPILER ${CLANG_CXX_COMPILER})
|
||||
|
||||
# Set linker to lld
|
||||
add_link_options("-fuse-ld=lld")
|
||||
11
cmake/toolchains/linux-gnu.cmake
Normal file
11
cmake/toolchains/linux-gnu.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
# Try and find a gcc/g++ install
|
||||
find_program(GNU_C_COMPILER cc | gcc REQUIRED)
|
||||
find_program(GNU_CXX_COMPILER c++ | g++ REQUIRED)
|
||||
|
||||
# Debug messages
|
||||
message(DEBUG "GNU_C_COMPILER = ${GNU_C_COMPILER}")
|
||||
message(DEBUG "GNU_CXX_COMPILER = ${GNU_C_COMPILER}")
|
||||
|
||||
# Set compilers to clang
|
||||
set(CMAKE_C_COMPILER ${GNU_C_COMPILER})
|
||||
set(CMAKE_CXX_COMPILER ${GNU_CXX_COMPILER})
|
||||
@@ -179,6 +179,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||
LOG("A server has disconnected, erasing their connected players from the list.");
|
||||
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
||||
|
||||
@@ -36,16 +36,19 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
data.playerID = playerId;
|
||||
|
||||
uint32_t len;
|
||||
inStream.Read<uint32_t>(len);
|
||||
if (!inStream.Read<uint32_t>(len)) return;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
char character; inStream.Read<char>(character);
|
||||
data.playerName += character;
|
||||
if (len > 33) {
|
||||
LOG("Received a really long player name, probably a fake packet %i.", len);
|
||||
return;
|
||||
}
|
||||
|
||||
inStream.Read(data.zoneID);
|
||||
inStream.Read(data.muteExpire);
|
||||
inStream.Read(data.gmLevel);
|
||||
data.playerName.resize(len);
|
||||
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(data.playerName.data()), len);
|
||||
|
||||
if (!inStream.Read(data.zoneID)) return;
|
||||
if (!inStream.Read(data.muteExpire)) return;
|
||||
if (!inStream.Read(data.gmLevel)) return;
|
||||
data.sysAddr = packet->systemAddress;
|
||||
|
||||
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||
@@ -122,6 +125,11 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
|
||||
size_t membersSize = 0;
|
||||
inStream.Read(membersSize);
|
||||
|
||||
if (membersSize >= 4) {
|
||||
LOG("Tried to create a team with more than 4 players");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> members;
|
||||
|
||||
members.reserve(membersSize);
|
||||
|
||||
@@ -201,7 +201,7 @@ void OnTerminate() {
|
||||
}
|
||||
|
||||
void MakeBacktrace() {
|
||||
struct sigaction sigact;
|
||||
struct sigaction sigact{};
|
||||
|
||||
sigact.sa_sigaction = CritErrHdlr;
|
||||
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "dConfig.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#define __EINVENTORYTYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
static const uint8_t NUMBER_OF_INVENTORIES = 17;
|
||||
/**
|
||||
* Represents the different types of inventories an entity may have
|
||||
@@ -56,4 +59,10 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<eInventoryType> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 16;
|
||||
};
|
||||
|
||||
#endif //!__EINVENTORYTYPE__H__
|
||||
|
||||
21
dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h
Normal file
21
dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
#define __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t {
|
||||
SUCCESS,
|
||||
FAIL_GENERIC,
|
||||
FAIL_INV_FULL,
|
||||
FAIL_ITEM_NOT_FOUND,
|
||||
FAIL_CANT_MOVE_TO_THAT_INV_TYPE,
|
||||
FAIL_NOT_NEAR_BANK,
|
||||
FAIL_CANT_SWAP_ITEMS,
|
||||
FAIL_SOURCE_TYPE,
|
||||
FAIL_WRONG_DEST_TYPE,
|
||||
FAIL_SWAP_DEST_TYPE,
|
||||
FAIL_CANT_MOVE_THINKING_HAT,
|
||||
FAIL_DISMOUNT_BEFORE_MOVING
|
||||
};
|
||||
|
||||
#endif //!__EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "IActivityLog.h"
|
||||
#include "IIgnoreList.h"
|
||||
#include "IAccountsRewardCodes.h"
|
||||
#include "IBehaviors.h"
|
||||
|
||||
namespace sql {
|
||||
class Statement;
|
||||
@@ -40,7 +41,8 @@ class GameDatabase :
|
||||
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
|
||||
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
|
||||
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList {
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList,
|
||||
public IBehaviors {
|
||||
public:
|
||||
virtual ~GameDatabase() = default;
|
||||
// TODO: These should be made private.
|
||||
|
||||
@@ -33,6 +33,9 @@ public:
|
||||
|
||||
// Add a new account to the database.
|
||||
virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0;
|
||||
|
||||
// Update the GameMaster level of an account.
|
||||
virtual void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IACCOUNTS__H__
|
||||
|
||||
22
dDatabase/GameDatabase/ITables/IBehaviors.h
Normal file
22
dDatabase/GameDatabase/ITables/IBehaviors.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef IBEHAVIORS_H
|
||||
#define IBEHAVIORS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
class IBehaviors {
|
||||
public:
|
||||
struct Info {
|
||||
int32_t behaviorId{};
|
||||
uint32_t characterId{};
|
||||
std::string behaviorInfo;
|
||||
};
|
||||
|
||||
// This Add also takes care of updating if it exists.
|
||||
virtual void AddBehavior(const Info& info) = 0;
|
||||
virtual std::string GetBehavior(const int32_t behaviorId) = 0;
|
||||
virtual void RemoveBehavior(const int32_t behaviorId) = 0;
|
||||
};
|
||||
|
||||
#endif //!IBEHAVIORS_H
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef __IPROPERTIESCONTENTS__H__
|
||||
#define __IPROPERTIESCONTENTS__H__
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
@@ -16,6 +17,7 @@ public:
|
||||
LWOOBJID id{};
|
||||
LOT lot{};
|
||||
uint32_t ugcId{};
|
||||
std::array<int32_t, 5> behaviors{};
|
||||
};
|
||||
|
||||
// Inserts a new UGC model into the database.
|
||||
@@ -32,7 +34,7 @@ public:
|
||||
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
|
||||
|
||||
// Update the model position and rotation for the given property id.
|
||||
virtual void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) = 0;
|
||||
virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
|
||||
|
||||
// Remove the model for the given property id.
|
||||
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "Logger.h"
|
||||
#include "dPlatforms.h"
|
||||
|
||||
namespace {
|
||||
std::string databaseName;
|
||||
@@ -39,14 +40,13 @@ void MySQLDatabase::Connect() {
|
||||
properties["autoReconnect"] = "true";
|
||||
|
||||
databaseName = Game::config->GetValue("mysql_database").c_str();
|
||||
|
||||
// `connect(const Properties& props)` segfaults in windows debug, but
|
||||
// `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly
|
||||
if (properties.find("localSocket") != properties.end() || properties.find("pipe") != properties.end()) {
|
||||
con = driver->connect(properties);
|
||||
} else {
|
||||
#if defined(DARKFLAME_PLATFORM_WIN32) && defined(_DEBUG)
|
||||
con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str());
|
||||
}
|
||||
#else
|
||||
con = driver->connect(properties);
|
||||
#endif
|
||||
con->setSchema(databaseName.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||
void RemoveUnreferencedUgcModels() override;
|
||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
||||
void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) override;
|
||||
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void RemoveModel(const LWOOBJID& modelId) override;
|
||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||
@@ -108,6 +108,10 @@ public:
|
||||
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
|
||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
||||
void AddBehavior(const IBehaviors::Info& info) override;
|
||||
std::string GetBehavior(const int32_t behaviorId) override;
|
||||
void RemoveBehavior(const int32_t characterId) override;
|
||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||
private:
|
||||
|
||||
// Generic query functions that can be used for any query.
|
||||
|
||||
@@ -35,3 +35,7 @@ void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::s
|
||||
void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
|
||||
ExecuteInsert("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);", username, bcryptpassword, static_cast<int32_t>(eGameMasterLevel::OPERATOR));
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
|
||||
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
|
||||
}
|
||||
|
||||
19
dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Normal file
19
dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "IBehaviors.h"
|
||||
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO behaviors (behavior_info, character_id, behavior_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE behavior_info = ?",
|
||||
info.behaviorInfo, info.characterId, info.behaviorId, info.behaviorInfo
|
||||
);
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveBehavior(const int32_t behaviorId) {
|
||||
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||
}
|
||||
|
||||
std::string MySQLDatabase::GetBehavior(const int32_t behaviorId) {
|
||||
auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||
return result->next() ? result->getString("behavior_info").c_str() : "";
|
||||
}
|
||||
@@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"Accounts.cpp"
|
||||
"AccountsRewardCodes.cpp"
|
||||
"ActivityLog.cpp"
|
||||
"Behaviors.cpp"
|
||||
"BugReports.cpp"
|
||||
"CharInfo.cpp"
|
||||
"CharXml.cpp"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
|
||||
auto result = ExecuteSelect("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;", propertyId);
|
||||
auto result = ExecuteSelect(
|
||||
"SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id, "
|
||||
"behavior_1, behavior_2, behavior_3, behavior_4, behavior_5 "
|
||||
"FROM properties_contents WHERE property_id = ?;", propertyId);
|
||||
|
||||
std::vector<IPropertyContents::Model> toReturn;
|
||||
toReturn.reserve(result->rowsCount());
|
||||
@@ -17,6 +20,12 @@ std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWO
|
||||
model.rotation.y = result->getFloat("ry");
|
||||
model.rotation.z = result->getFloat("rz");
|
||||
model.ugcId = result->getUInt64("ugc_id");
|
||||
model.behaviors[0] = result->getInt("behavior_1");
|
||||
model.behaviors[1] = result->getInt("behavior_2");
|
||||
model.behaviors[2] = result->getInt("behavior_3");
|
||||
model.behaviors[3] = result->getInt("behavior_4");
|
||||
model.behaviors[4] = result->getInt("behavior_5");
|
||||
|
||||
toReturn.push_back(std::move(model));
|
||||
}
|
||||
return toReturn;
|
||||
@@ -32,21 +41,23 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
|
||||
model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<uint32_t>(model.lot),
|
||||
model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w,
|
||||
name, "", // Model description. TODO implement this.
|
||||
0, // behavior 1. TODO implement this.
|
||||
0, // behavior 2. TODO implement this.
|
||||
0, // behavior 3. TODO implement this.
|
||||
0, // behavior 4. TODO implement this.
|
||||
0 // behavior 5. TODO implement this.
|
||||
model.behaviors[0], // behavior 1
|
||||
model.behaviors[1], // behavior 2
|
||||
model.behaviors[2], // behavior 3
|
||||
model.behaviors[3], // behavior 4
|
||||
model.behaviors[4] // behavior 5
|
||||
);
|
||||
} catch (sql::SQLException& e) {
|
||||
LOG("Error inserting new property model: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) {
|
||||
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
ExecuteUpdate(
|
||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;",
|
||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId);
|
||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
||||
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
|
||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
|
||||
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||
|
||||
@@ -27,6 +27,8 @@ Character::Character(uint32_t id, User* parentUser) {
|
||||
m_ID = id;
|
||||
m_ParentUser = parentUser;
|
||||
m_OurEntity = nullptr;
|
||||
m_GMLevel = eGameMasterLevel::CIVILIAN;
|
||||
m_PermissionMap = static_cast<ePermissionMap>(0);
|
||||
}
|
||||
|
||||
Character::~Character() {
|
||||
|
||||
@@ -464,22 +464,22 @@ private:
|
||||
/**
|
||||
* The ID of this character. First 32 bits of the ObjectID.
|
||||
*/
|
||||
uint32_t m_ID;
|
||||
uint32_t m_ID{};
|
||||
|
||||
/**
|
||||
* The 64-bit unique ID used in the game.
|
||||
*/
|
||||
LWOOBJID m_ObjectID;
|
||||
LWOOBJID m_ObjectID{ LWOOBJID_EMPTY };
|
||||
|
||||
/**
|
||||
* The user that owns this character.
|
||||
*/
|
||||
User* m_ParentUser;
|
||||
User* m_ParentUser{};
|
||||
|
||||
/**
|
||||
* If the character is in game, this is the entity that it represents, else nullptr.
|
||||
*/
|
||||
Entity* m_OurEntity;
|
||||
Entity* m_OurEntity{};
|
||||
|
||||
/**
|
||||
* 0-9, the Game Master level of this character.
|
||||
@@ -506,17 +506,17 @@ private:
|
||||
/**
|
||||
* Whether the custom name of this character is rejected
|
||||
*/
|
||||
bool m_NameRejected;
|
||||
bool m_NameRejected{};
|
||||
|
||||
/**
|
||||
* The current amount of coins of this character
|
||||
*/
|
||||
int64_t m_Coins;
|
||||
int64_t m_Coins{};
|
||||
|
||||
/**
|
||||
* Whether the character is building
|
||||
*/
|
||||
bool m_BuildMode;
|
||||
bool m_BuildMode{};
|
||||
|
||||
/**
|
||||
* The items equipped by the character on world load
|
||||
@@ -583,7 +583,7 @@ private:
|
||||
/**
|
||||
* The ID of the properties of this character
|
||||
*/
|
||||
uint32_t m_PropertyCloneID;
|
||||
uint32_t m_PropertyCloneID{};
|
||||
|
||||
/**
|
||||
* The XML data for this character, stored as string
|
||||
@@ -613,7 +613,7 @@ private:
|
||||
/**
|
||||
* The last time this character logged in
|
||||
*/
|
||||
uint64_t m_LastLogin;
|
||||
uint64_t m_LastLogin{};
|
||||
|
||||
/**
|
||||
* The gameplay flags this character has (not just true values)
|
||||
|
||||
@@ -225,7 +225,7 @@ void Entity::Initialize() {
|
||||
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
|
||||
AddComponent<ModelComponent>();
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
|
||||
AddComponent<RenderComponent>();
|
||||
|
||||
@@ -649,7 +649,7 @@ void Entity::Initialize() {
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
|
||||
AddComponent<ModelComponent>();
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
|
||||
auto* destroyableComponent = AddComponent<DestroyableComponent>();
|
||||
destroyableComponent->SetHealth(1);
|
||||
|
||||
@@ -12,6 +12,7 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
|
||||
m_AccountID = 0;
|
||||
m_Username = "";
|
||||
m_SessionKey = "";
|
||||
m_MuteExpire = 0;
|
||||
|
||||
m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters
|
||||
m_LastCharID = 0;
|
||||
|
||||
@@ -105,7 +105,7 @@ void BehaviorContext::ExecuteUpdates() {
|
||||
this->scheduledUpdates.clear();
|
||||
}
|
||||
|
||||
void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
|
||||
bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
|
||||
BehaviorSyncEntry entry;
|
||||
auto found = false;
|
||||
|
||||
@@ -128,7 +128,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
|
||||
if (!found) {
|
||||
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* behavior = entry.behavior;
|
||||
@@ -137,10 +137,11 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
|
||||
if (behavior == nullptr) {
|
||||
LOG("Invalid behavior for sync id (%i)!", syncId);
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
behavior->Sync(this, bitStream, branch);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +199,26 @@ void BehaviorContext::UpdatePlayerSyncs(float deltaTime) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this->skillUId != 0 && !clientInitalized) {
|
||||
EchoSyncSkill echo;
|
||||
echo.bDone = true;
|
||||
echo.uiSkillHandle = this->skillUId;
|
||||
echo.uiBehaviorHandle = entry.handle;
|
||||
|
||||
RakNet::BitStream bitStream{};
|
||||
entry.behavior->SyncCalculation(this, bitStream, entry.branchContext);
|
||||
|
||||
echo.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed());
|
||||
|
||||
RakNet::BitStream message;
|
||||
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
|
||||
message.Write(this->originator);
|
||||
echo.Serialize(message);
|
||||
|
||||
Game::server->Send(message, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
|
||||
this->syncEntries.erase(this->syncEntries.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ struct BehaviorContext
|
||||
|
||||
void ExecuteUpdates();
|
||||
|
||||
void SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
|
||||
bool SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
|
||||
|
||||
void Update(float deltaTime);
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
destroyable->SetIsShielded(true);
|
||||
|
||||
context->RegisterTimerBehavior(this, branch, target->GetObjectID());
|
||||
|
||||
Game::entityManager->SerializeEntity(target);
|
||||
}
|
||||
|
||||
void DamageAbsorptionBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
||||
@@ -52,7 +54,13 @@ void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchCon
|
||||
|
||||
const auto toRemove = std::min(present, this->m_absorbAmount);
|
||||
|
||||
destroyable->SetDamageToAbsorb(present - toRemove);
|
||||
const auto remaining = present - toRemove;
|
||||
|
||||
destroyable->SetDamageToAbsorb(remaining);
|
||||
|
||||
destroyable->SetIsShielded(remaining > 0);
|
||||
|
||||
Game::entityManager->SerializeEntity(target);
|
||||
}
|
||||
|
||||
void DamageAbsorptionBehavior::Load() {
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
SetAiState(AiState::spawn);
|
||||
m_DirtyStateOrTarget = true;
|
||||
m_State = AiState::spawn;
|
||||
m_Timer = 1.0f;
|
||||
m_StartPosition = parent->GetPosition();
|
||||
m_MovementAI = nullptr;
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
#include "CDScriptComponentTable.h"
|
||||
#include "CDObjectSkillsTable.h"
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "StringifiedEnum.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
|
||||
this->m_Dirty = true;
|
||||
@@ -492,6 +495,11 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const groups = inventoryElement->FirstChildElement("grps");
|
||||
if (groups) {
|
||||
LoadGroupXml(*groups);
|
||||
}
|
||||
|
||||
m_Consumable = inventoryElement->IntAttribute("csl", LOT_NULL);
|
||||
|
||||
auto* bag = bags->FirstChildElement();
|
||||
@@ -630,6 +638,15 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
|
||||
bags->LinkEndChild(bag);
|
||||
}
|
||||
|
||||
auto* groups = inventoryElement->FirstChildElement("grps");
|
||||
if (groups) {
|
||||
groups->DeleteChildren();
|
||||
} else {
|
||||
groups = inventoryElement->InsertNewChildElement("grps");
|
||||
}
|
||||
|
||||
UpdateGroupXml(*groups);
|
||||
|
||||
auto* items = inventoryElement->FirstChildElement("items");
|
||||
|
||||
if (items == nullptr) {
|
||||
@@ -875,8 +892,6 @@ void InventoryComponent::UnEquipItem(Item* item) {
|
||||
|
||||
RemoveSlot(item->GetInfo().equipLocation);
|
||||
|
||||
PurgeProxies(item);
|
||||
|
||||
UnequipScripts(item);
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
@@ -886,6 +901,8 @@ void InventoryComponent::UnEquipItem(Item* item) {
|
||||
PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
|
||||
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
|
||||
}
|
||||
|
||||
PurgeProxies(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -1505,10 +1522,10 @@ void InventoryComponent::PurgeProxies(Item* item) {
|
||||
const auto root = item->GetParent();
|
||||
|
||||
if (root != LWOOBJID_EMPTY) {
|
||||
item = FindItemById(root);
|
||||
Item* itemRoot = FindItemById(root);
|
||||
|
||||
if (item != nullptr) {
|
||||
UnEquipItem(item);
|
||||
if (itemRoot != nullptr) {
|
||||
UnEquipItem(itemRoot);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -1603,3 +1620,110 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
|
||||
m_Skills.insert_or_assign(slot, skillId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void InventoryComponent::UpdateGroup(const GroupUpdate& groupUpdate) {
|
||||
if (groupUpdate.groupId.empty()) return;
|
||||
|
||||
if (groupUpdate.inventory != eInventoryType::BRICKS && groupUpdate.inventory != eInventoryType::MODELS) {
|
||||
LOG("Invalid inventory type for grouping %s", StringifiedEnum::ToString(groupUpdate.inventory).data());
|
||||
return;
|
||||
}
|
||||
|
||||
auto& groups = m_Groups[groupUpdate.inventory];
|
||||
auto groupItr = std::ranges::find_if(groups, [&groupUpdate](const Group& group) {
|
||||
return group.groupId == groupUpdate.groupId;
|
||||
});
|
||||
|
||||
if (groupUpdate.command != GroupUpdateCommand::ADD && groupItr == groups.end()) {
|
||||
LOG("Group %s not found in inventory %s. Cannot process command.", groupUpdate.groupId.c_str(), StringifiedEnum::ToString(groupUpdate.inventory).data());
|
||||
return;
|
||||
}
|
||||
|
||||
if (groupUpdate.command == GroupUpdateCommand::ADD && groups.size() >= MaximumGroupCount) {
|
||||
LOG("Cannot add group to inventory %s. Maximum group count reached.", StringifiedEnum::ToString(groupUpdate.inventory).data());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (groupUpdate.command) {
|
||||
case GroupUpdateCommand::ADD: {
|
||||
auto& group = groups.emplace_back();
|
||||
group.groupId = groupUpdate.groupId;
|
||||
group.groupName = groupUpdate.groupName;
|
||||
break;
|
||||
}
|
||||
case GroupUpdateCommand::ADD_LOT: {
|
||||
groupItr->lots.insert(groupUpdate.lot);
|
||||
break;
|
||||
}
|
||||
case GroupUpdateCommand::REMOVE: {
|
||||
groups.erase(groupItr);
|
||||
break;
|
||||
}
|
||||
case GroupUpdateCommand::REMOVE_LOT: {
|
||||
groupItr->lots.erase(groupUpdate.lot);
|
||||
break;
|
||||
}
|
||||
case GroupUpdateCommand::MODIFY: {
|
||||
groupItr->groupName = groupUpdate.groupName;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG("Invalid group update command %i", groupUpdate.command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::UpdateGroupXml(tinyxml2::XMLElement& groups) const {
|
||||
for (const auto& [inventory, groupsData] : m_Groups) {
|
||||
for (const auto& group : groupsData) {
|
||||
auto* const groupElement = groups.InsertNewChildElement("grp");
|
||||
|
||||
groupElement->SetAttribute("id", group.groupId.c_str());
|
||||
groupElement->SetAttribute("n", group.groupName.c_str());
|
||||
groupElement->SetAttribute("t", static_cast<uint32_t>(inventory));
|
||||
groupElement->SetAttribute("u", 0);
|
||||
std::ostringstream lots;
|
||||
bool first = true;
|
||||
for (const auto lot : group.lots) {
|
||||
if (!first) lots << ' ';
|
||||
first = false;
|
||||
|
||||
lots << lot;
|
||||
}
|
||||
groupElement->SetAttribute("l", lots.str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::LoadGroupXml(const tinyxml2::XMLElement& groups) {
|
||||
auto* groupElement = groups.FirstChildElement("grp");
|
||||
|
||||
while (groupElement) {
|
||||
const char* groupId = nullptr;
|
||||
const char* groupName = nullptr;
|
||||
const char* lots = nullptr;
|
||||
uint32_t inventory = eInventoryType::INVALID;
|
||||
|
||||
groupElement->QueryStringAttribute("id", &groupId);
|
||||
groupElement->QueryStringAttribute("n", &groupName);
|
||||
groupElement->QueryStringAttribute("l", &lots);
|
||||
groupElement->QueryAttribute("t", &inventory);
|
||||
|
||||
if (!groupId || !groupName || !lots) {
|
||||
LOG("Failed to load group from xml id %i name %i lots %i",
|
||||
groupId == nullptr, groupName == nullptr, lots == nullptr);
|
||||
} else {
|
||||
auto& group = m_Groups[static_cast<eInventoryType>(inventory)].emplace_back();
|
||||
group.groupId = groupId;
|
||||
group.groupName = groupName;
|
||||
|
||||
for (const auto& lotStr : GeneralUtils::SplitString(lots, ' ')) {
|
||||
auto lot = GeneralUtils::TryParse<LOT>(lotStr);
|
||||
if (lot) group.lots.insert(*lot);
|
||||
}
|
||||
}
|
||||
|
||||
groupElement = groupElement->NextSiblingElement("grp");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,35 @@ enum class eItemType : int32_t;
|
||||
*/
|
||||
class InventoryComponent final : public Component {
|
||||
public:
|
||||
struct Group {
|
||||
// Generated ID for the group. The ID is sent by the client and has the format user_group + Math.random() * UINT_MAX.
|
||||
std::string groupId;
|
||||
// Custom name assigned by the user.
|
||||
std::string groupName;
|
||||
// All the lots the user has in the group.
|
||||
std::set<LOT> lots;
|
||||
};
|
||||
|
||||
enum class GroupUpdateCommand {
|
||||
ADD,
|
||||
ADD_LOT,
|
||||
MODIFY,
|
||||
REMOVE,
|
||||
REMOVE_LOT,
|
||||
};
|
||||
|
||||
// Based on the command, certain fields will be used or not used.
|
||||
// for example, ADD_LOT wont use groupName, MODIFY wont use lots, etc.
|
||||
struct GroupUpdate {
|
||||
std::string groupId;
|
||||
std::string groupName;
|
||||
LOT lot;
|
||||
eInventoryType inventory;
|
||||
GroupUpdateCommand command;
|
||||
};
|
||||
|
||||
static constexpr uint32_t MaximumGroupCount = 50;
|
||||
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
||||
InventoryComponent(Entity* parent);
|
||||
|
||||
@@ -367,14 +396,23 @@ public:
|
||||
*/
|
||||
void UnequipScripts(Item* unequippedItem);
|
||||
|
||||
std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
|
||||
std::map<BehaviorSlot, uint32_t> GetSkills() { return m_Skills; };
|
||||
|
||||
bool SetSkill(int slot, uint32_t skillId);
|
||||
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
|
||||
|
||||
void UpdateGroup(const GroupUpdate& groupUpdate);
|
||||
void RemoveGroup(const std::string& groupId);
|
||||
|
||||
~InventoryComponent() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The key is the inventory the group belongs to, the value maps' key is the id for the group.
|
||||
* This is only used for bricks and model inventories.
|
||||
*/
|
||||
std::map<eInventoryType, std::vector<Group>> m_Groups{ { eInventoryType::BRICKS, {} }, { eInventoryType::MODELS, {} } };
|
||||
|
||||
/**
|
||||
* All the inventory this entity possesses
|
||||
*/
|
||||
@@ -477,6 +515,9 @@ private:
|
||||
* @param document the xml doc to load from
|
||||
*/
|
||||
void UpdatePetXml(tinyxml2::XMLDocument& document);
|
||||
|
||||
void LoadGroupXml(const tinyxml2::XMLElement& groups);
|
||||
void UpdateGroupXml(tinyxml2::XMLElement& groups) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include "BehaviorStates.h"
|
||||
#include "ControlBehaviorMsgs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
#include "Database.h"
|
||||
|
||||
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
||||
@@ -14,6 +17,33 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
||||
}
|
||||
|
||||
void ModelComponent::LoadBehaviors() {
|
||||
auto behaviors = GeneralUtils::SplitString(m_Parent->GetVar<std::string>(u"userModelBehaviors"), ',');
|
||||
for (const auto& behavior : behaviors) {
|
||||
if (behavior.empty()) continue;
|
||||
|
||||
const auto behaviorId = GeneralUtils::TryParse<int32_t>(behavior);
|
||||
if (!behaviorId.has_value() || behaviorId.value() == 0) continue;
|
||||
|
||||
LOG_DEBUG("Loading behavior %d", behaviorId.value());
|
||||
auto& inserted = m_Behaviors.emplace_back();
|
||||
inserted.SetBehaviorId(*behaviorId);
|
||||
|
||||
const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value());
|
||||
|
||||
tinyxml2::XMLDocument behaviorXml;
|
||||
auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size());
|
||||
LOG_DEBUG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str());
|
||||
|
||||
const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior");
|
||||
if (!behaviorRoot) {
|
||||
LOG("Failed to load behavior %d due to missing behavior root", behaviorId.value());
|
||||
continue;
|
||||
}
|
||||
inserted.Deserialize(*behaviorRoot);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||
// ItemComponent Serialization. Pets do not get this serialization.
|
||||
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
|
||||
@@ -72,3 +102,23 @@ void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) {
|
||||
m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex());
|
||||
// TODO move to the inventory
|
||||
}
|
||||
|
||||
std::array<std::pair<int32_t, std::string>, 5> ModelComponent::GetBehaviorsForSave() const {
|
||||
std::array<std::pair<int32_t, std::string>, 5> toReturn{};
|
||||
for (auto i = 0; i < m_Behaviors.size(); i++) {
|
||||
const auto& behavior = m_Behaviors.at(i);
|
||||
if (behavior.GetBehaviorId() == -1) continue;
|
||||
auto& [id, behaviorData] = toReturn[i];
|
||||
id = behavior.GetBehaviorId();
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
auto* root = doc.NewElement("Behavior");
|
||||
behavior.Serialize(*root);
|
||||
doc.InsertFirstChild(root);
|
||||
|
||||
tinyxml2::XMLPrinter printer(0, true, 0);
|
||||
doc.Print(&printer);
|
||||
behaviorData = printer.CStr();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
@@ -28,6 +29,8 @@ public:
|
||||
|
||||
ModelComponent(Entity* parent);
|
||||
|
||||
void LoadBehaviors();
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
/**
|
||||
@@ -109,6 +112,8 @@ public:
|
||||
|
||||
void VerifyBehaviors();
|
||||
|
||||
std::array<std::pair<int32_t, std::string>, 5> GetBehaviorsForSave() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The behaviors of the model
|
||||
|
||||
@@ -795,8 +795,6 @@ void PetComponent::Wander() {
|
||||
}
|
||||
|
||||
void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
|
||||
AddDrainImaginationTimer(item, fromTaming);
|
||||
|
||||
m_ItemId = item->GetId();
|
||||
m_DatabaseId = item->GetSubKey();
|
||||
|
||||
@@ -807,6 +805,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
|
||||
inventoryComponent->DespawnPet();
|
||||
|
||||
m_Owner = inventoryComponent->GetParent()->GetObjectID();
|
||||
AddDrainImaginationTimer(fromTaming);
|
||||
|
||||
auto* owner = GetOwner();
|
||||
|
||||
@@ -859,17 +858,14 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
|
||||
}
|
||||
}
|
||||
|
||||
void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
|
||||
void PetComponent::AddDrainImaginationTimer(bool fromTaming) {
|
||||
if (Game::config->GetValue("pets_take_imagination") != "1") return;
|
||||
|
||||
auto playerInventory = item->GetInventory();
|
||||
if (!playerInventory) return;
|
||||
|
||||
auto playerInventoryComponent = playerInventory->GetComponent();
|
||||
if (!playerInventoryComponent) return;
|
||||
|
||||
auto playerEntity = playerInventoryComponent->GetParent();
|
||||
if (!playerEntity) return;
|
||||
auto* playerEntity = Game::entityManager->GetEntity(m_Owner);
|
||||
if (!playerEntity) {
|
||||
LOG("owner was null or didnt exist!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto playerDestroyableComponent = playerEntity->GetComponent<DestroyableComponent>();
|
||||
if (!playerDestroyableComponent) return;
|
||||
@@ -878,12 +874,16 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
|
||||
if (!fromTaming) playerDestroyableComponent->Imagine(-1);
|
||||
|
||||
// Set this to a variable so when this is called back from the player the timer doesn't fire off.
|
||||
m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() {
|
||||
if (!playerDestroyableComponent) {
|
||||
LOG("No petComponent and/or no playerDestroyableComponent");
|
||||
m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [this]() {
|
||||
const auto* owner = Game::entityManager->GetEntity(m_Owner);
|
||||
if (!owner) {
|
||||
LOG("owner was null or didnt exist!");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* playerDestroyableComponent = owner->GetComponent<DestroyableComponent>();
|
||||
if (!playerDestroyableComponent) return;
|
||||
|
||||
// If we are out of imagination despawn the pet.
|
||||
if (playerDestroyableComponent->GetImagination() == 0) {
|
||||
this->Deactivate();
|
||||
@@ -893,15 +893,13 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
|
||||
GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet);
|
||||
}
|
||||
|
||||
this->AddDrainImaginationTimer(item);
|
||||
this->AddDrainImaginationTimer();
|
||||
});
|
||||
}
|
||||
|
||||
void PetComponent::Deactivate() {
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true);
|
||||
|
||||
GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress());
|
||||
|
||||
activePets.erase(m_Owner);
|
||||
|
||||
m_Parent->Kill();
|
||||
@@ -910,6 +908,8 @@ void PetComponent::Deactivate() {
|
||||
|
||||
if (owner == nullptr) return;
|
||||
|
||||
GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, owner->GetSystemAddress());
|
||||
|
||||
GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress());
|
||||
|
||||
GameMessages::SendRegisterPetID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress());
|
||||
@@ -1034,6 +1034,7 @@ Entity* PetComponent::GetParentEntity() const {
|
||||
}
|
||||
|
||||
PetComponent::~PetComponent() {
|
||||
m_Owner = LWOOBJID_EMPTY;
|
||||
}
|
||||
|
||||
void PetComponent::SetPetNameForModeration(const std::string& petName) {
|
||||
|
||||
@@ -205,7 +205,7 @@ public:
|
||||
*
|
||||
* @param item The item that represents this pet in the inventory.
|
||||
*/
|
||||
void AddDrainImaginationTimer(Item* item, bool fromTaming = false);
|
||||
void AddDrainImaginationTimer(bool fromTaming = false);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
#include "eObjectBits.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "ModelComponent.h"
|
||||
|
||||
#include <vector>
|
||||
#include "CppScripts.h"
|
||||
#include <ranges>
|
||||
|
||||
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
|
||||
|
||||
@@ -593,6 +595,20 @@ void PropertyManagementComponent::Load() {
|
||||
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
||||
}
|
||||
|
||||
std::ostringstream userModelBehavior;
|
||||
bool firstAdded = false;
|
||||
for (auto behavior : databaseModel.behaviors) {
|
||||
if (behavior < 0) {
|
||||
LOG("Invalid behavior ID: %d, removing behavior reference from model", behavior);
|
||||
behavior = 0;
|
||||
}
|
||||
if (firstAdded) userModelBehavior << ",";
|
||||
userModelBehavior << behavior;
|
||||
firstAdded = true;
|
||||
}
|
||||
|
||||
settings.push_back(new LDFData<std::string>(u"userModelBehaviors", userModelBehavior.str()));
|
||||
|
||||
node->config = settings;
|
||||
|
||||
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
|
||||
@@ -610,6 +626,12 @@ void PropertyManagementComponent::Save() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* const owner = GetOwner();
|
||||
if (!owner) return;
|
||||
|
||||
const auto* const character = owner->GetCharacter();
|
||||
if (!character) return;
|
||||
|
||||
auto present = Database::Get()->GetPropertyModels(propertyId);
|
||||
|
||||
std::vector<LWOOBJID> modelIds;
|
||||
@@ -624,6 +646,20 @@ void PropertyManagementComponent::Save() {
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
auto* modelComponent = entity->GetComponent<ModelComponent>();
|
||||
if (!modelComponent) continue;
|
||||
const auto modelBehaviors = modelComponent->GetBehaviorsForSave();
|
||||
|
||||
// save the behaviors of the model
|
||||
for (const auto& [behaviorId, behaviorStr] : modelBehaviors) {
|
||||
if (behaviorStr.empty() || behaviorId == -1 || behaviorId == 0) continue;
|
||||
IBehaviors::Info info {
|
||||
.behaviorId = behaviorId,
|
||||
.characterId = character->GetID(),
|
||||
.behaviorInfo = behaviorStr
|
||||
};
|
||||
Database::Get()->AddBehavior(info);
|
||||
}
|
||||
|
||||
const auto position = entity->GetPosition();
|
||||
const auto rotation = entity->GetRotation();
|
||||
@@ -635,10 +671,13 @@ void PropertyManagementComponent::Save() {
|
||||
model.position = position;
|
||||
model.rotation = rotation;
|
||||
model.ugcId = 0;
|
||||
for (auto i = 0; i < model.behaviors.size(); i++) {
|
||||
model.behaviors[i] = modelBehaviors[i].first;
|
||||
}
|
||||
|
||||
Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name");
|
||||
} else {
|
||||
Database::Get()->UpdateModelPositionRotation(id, position, rotation);
|
||||
Database::Get()->UpdateModel(id, position, rotation, modelBehaviors);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ RacingControlComponent::RacingControlComponent(Entity* parent)
|
||||
m_LoadedPlayers = 0;
|
||||
m_LoadTimer = 0;
|
||||
m_Finished = 0;
|
||||
m_StartTime = 0;
|
||||
m_EmptyTimer = 0;
|
||||
m_SoloRacing = Game::config->GetValue("solo_racing") == "1";
|
||||
|
||||
@@ -398,25 +397,6 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
|
||||
GameMessages::SendNotifyRacingClient(
|
||||
m_Parent->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"",
|
||||
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent == nullptr) return;
|
||||
|
||||
missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
|
||||
missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.
|
||||
|
||||
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
|
||||
if (m_SoloRacing || m_LoadedPlayers > 2) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, data->finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
|
||||
if (data->finished == 1) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
|
||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
|
||||
}
|
||||
if (data->finished == m_LoadedPlayers) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
|
||||
}
|
||||
}
|
||||
} else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) {
|
||||
auto* vehicle = Game::entityManager->GetEntity(data->vehicleID);
|
||||
|
||||
@@ -446,9 +426,9 @@ void RacingControlComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
|
||||
outBitStream.Write(player.playerID);
|
||||
|
||||
outBitStream.Write(player.data[0]);
|
||||
if (player.finished != 0) outBitStream.Write<float>(player.raceTime);
|
||||
if (player.finished != 0) outBitStream.Write<float>(player.raceTime.count() / 1000.0f);
|
||||
else outBitStream.Write(player.data[1]);
|
||||
if (player.finished != 0) outBitStream.Write<float>(player.bestLapTime);
|
||||
if (player.finished != 0) outBitStream.Write<float>(player.bestLapTime.count() / 1000.0f);
|
||||
else outBitStream.Write(player.data[2]);
|
||||
if (player.finished == 1) outBitStream.Write<float>(1.0f);
|
||||
else outBitStream.Write(player.data[3]);
|
||||
@@ -509,8 +489,8 @@ void RacingControlComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
|
||||
if (player.finished == 0) continue;
|
||||
outBitStream.Write1(); // Has more data
|
||||
outBitStream.Write(player.playerID);
|
||||
outBitStream.Write<float>(player.bestLapTime);
|
||||
outBitStream.Write<float>(player.raceTime);
|
||||
outBitStream.Write<float>(player.bestLapTime.count() / 1000.0f);
|
||||
outBitStream.Write<float>(player.raceTime.count() / 1000.0f);
|
||||
}
|
||||
|
||||
outBitStream.Write0(); // No more data
|
||||
@@ -740,7 +720,7 @@ void RacingControlComponent::Update(float deltaTime) {
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
m_StartTime = std::time(nullptr);
|
||||
m_StartTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
m_StartTimer += deltaTime;
|
||||
@@ -829,46 +809,60 @@ void RacingControlComponent::Update(float deltaTime) {
|
||||
|
||||
// Reached the start point, lapped
|
||||
if (respawnIndex == 0) {
|
||||
time_t lapTime = std::time(nullptr) - (player.lap == 0 ? m_StartTime : player.lapTime);
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto lapTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - (player.lap == 0 ? m_StartTime : player.lapTime));
|
||||
|
||||
// Cheating check
|
||||
if (lapTime < 40) {
|
||||
if (lapTime.count() < 40000) {
|
||||
continue;
|
||||
}
|
||||
|
||||
player.lap++;
|
||||
player.lapTime = now;
|
||||
|
||||
player.lapTime = std::time(nullptr);
|
||||
|
||||
if (player.bestLapTime == 0 || player.bestLapTime > lapTime) {
|
||||
if (player.bestLapTime > lapTime || player.lap == 0) {
|
||||
player.bestLapTime = lapTime;
|
||||
|
||||
LOG("Best lap time (%llu)", lapTime);
|
||||
}
|
||||
|
||||
player.lap++;
|
||||
|
||||
auto* missionComponent =
|
||||
playerEntity->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent != nullptr) {
|
||||
|
||||
// Progress lap time tasks
|
||||
missionComponent->Progress(eMissionTaskType::RACING, (lapTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
|
||||
missionComponent->Progress(eMissionTaskType::RACING, lapTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
|
||||
|
||||
if (player.lap == 3) {
|
||||
m_Finished++;
|
||||
player.finished = m_Finished;
|
||||
|
||||
const auto raceTime =
|
||||
(std::time(nullptr) - m_StartTime);
|
||||
const auto raceTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_StartTime);
|
||||
|
||||
player.raceTime = raceTime;
|
||||
|
||||
LOG("Completed time %llu, %llu",
|
||||
raceTime, raceTime * 1000);
|
||||
LOG("Completed time %llums %fs", raceTime.count(), raceTime.count() / 1000.0f);
|
||||
|
||||
LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast<float>(player.raceTime), static_cast<float>(player.bestLapTime), static_cast<float>(player.finished == 1));
|
||||
LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast<float>(player.raceTime.count()) / 1000, static_cast<float>(player.bestLapTime.count()) / 1000, static_cast<float>(player.finished == 1));
|
||||
// Entire race time
|
||||
missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));
|
||||
missionComponent->Progress(eMissionTaskType::RACING, player.raceTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));
|
||||
|
||||
missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
|
||||
missionComponent->Progress(eMissionTaskType::RACING, player.smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.
|
||||
|
||||
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
|
||||
if (m_SoloRacing || m_RacingPlayers.size() > 2) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, player.finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
|
||||
if (player.finished == 1) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
|
||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
|
||||
}
|
||||
if (player.finished == m_RacingPlayers.size()) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
|
||||
}
|
||||
}
|
||||
|
||||
auto* characterComponent = playerEntity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
@@ -877,8 +871,8 @@ void RacingControlComponent::Update(float deltaTime) {
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Lapped (%i) in (%llu)", player.lap,
|
||||
lapTime);
|
||||
LOG("Lapped (%i) in (%llums %fs)", player.lap,
|
||||
lapTime.count(), lapTime.count() / 1000.0f);
|
||||
}
|
||||
|
||||
LOG("Reached point (%i)/(%i)", player.respawnIndex,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include <chrono>
|
||||
|
||||
/**
|
||||
* Information for each player in the race
|
||||
@@ -72,12 +73,12 @@ struct RacingPlayerInfo {
|
||||
/**
|
||||
* The fastest lap time of the player
|
||||
*/
|
||||
time_t bestLapTime = 0;
|
||||
std::chrono::milliseconds bestLapTime;
|
||||
|
||||
/**
|
||||
* The current lap time of the player
|
||||
*/
|
||||
time_t lapTime = 0;
|
||||
std::chrono::high_resolution_clock::time_point lapTime;
|
||||
|
||||
/**
|
||||
* The number of times this player smashed their car
|
||||
@@ -97,7 +98,7 @@ struct RacingPlayerInfo {
|
||||
/**
|
||||
* Unused
|
||||
*/
|
||||
time_t raceTime = 0;
|
||||
std::chrono::milliseconds raceTime;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -231,7 +232,7 @@ private:
|
||||
/**
|
||||
* The time the race was started
|
||||
*/
|
||||
time_t m_StartTime;
|
||||
std::chrono::high_resolution_clock::time_point m_StartTime;
|
||||
|
||||
/**
|
||||
* Timer for tracking how long a player was alone in this race
|
||||
|
||||
@@ -38,7 +38,7 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
|
||||
|
||||
context->skillID = skillID;
|
||||
|
||||
this->m_managedBehaviors.insert_or_assign(skillUid, context);
|
||||
this->m_managedBehaviors.insert({ skillUid, context });
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
||||
@@ -52,17 +52,24 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
|
||||
}
|
||||
|
||||
void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syncId, RakNet::BitStream& bitStream) {
|
||||
const auto index = this->m_managedBehaviors.find(skillUid);
|
||||
const auto index = this->m_managedBehaviors.equal_range(skillUid);
|
||||
|
||||
if (index == this->m_managedBehaviors.end()) {
|
||||
if (index.first == this->m_managedBehaviors.end()) {
|
||||
LOG("Failed to find skill with uid (%i)!", skillUid, syncId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto* context = index->second;
|
||||
bool foundSyncId = false;
|
||||
for (auto it = index.first; it != index.second && !foundSyncId; ++it) {
|
||||
const auto& context = it->second;
|
||||
|
||||
context->SyncBehavior(syncId, bitStream);
|
||||
foundSyncId = context->SyncBehavior(syncId, bitStream);
|
||||
}
|
||||
|
||||
if (!foundSyncId) {
|
||||
LOG("Failed to find sync id (%i) for skill with uid (%i)!", syncId, skillUid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +145,7 @@ void SkillComponent::Update(const float deltaTime) {
|
||||
for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime);
|
||||
}
|
||||
|
||||
std::map<uint32_t, BehaviorContext*> keep{};
|
||||
std::multimap<uint32_t, BehaviorContext*> keep{};
|
||||
|
||||
for (const auto& pair : this->m_managedBehaviors) {
|
||||
auto* context = pair.second;
|
||||
@@ -176,7 +183,7 @@ void SkillComponent::Update(const float deltaTime) {
|
||||
}
|
||||
}
|
||||
|
||||
keep.insert_or_assign(pair.first, context);
|
||||
keep.insert({ pair.first, context });
|
||||
}
|
||||
|
||||
this->m_managedBehaviors = keep;
|
||||
@@ -285,7 +292,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
this->m_managedBehaviors.insert_or_assign(context->skillUId, context);
|
||||
this->m_managedBehaviors.insert({ context->skillUId, context });
|
||||
|
||||
if (!clientInitalized) {
|
||||
// Echo start skill
|
||||
|
||||
@@ -188,7 +188,7 @@ private:
|
||||
/**
|
||||
* All of the active skills mapped by their unique ID.
|
||||
*/
|
||||
std::map<uint32_t, BehaviorContext*> m_managedBehaviors;
|
||||
std::multimap<uint32_t, BehaviorContext*> m_managedBehaviors;
|
||||
|
||||
/**
|
||||
* All active projectiles.
|
||||
|
||||
@@ -685,6 +685,13 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
||||
case eGameMessageType::REQUEST_VENDOR_STATUS_UPDATE:
|
||||
GameMessages::SendVendorStatusUpdate(entity, sysAddr, true);
|
||||
break;
|
||||
case eGameMessageType::UPDATE_INVENTORY_GROUP:
|
||||
GameMessages::HandleUpdateInventoryGroup(inStream, entity, sysAddr);
|
||||
break;
|
||||
case eGameMessageType::UPDATE_INVENTORY_GROUP_CONTENTS:
|
||||
GameMessages::HandleUpdateInventoryGroupContents(inStream, entity, sysAddr);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
|
||||
break;
|
||||
|
||||
@@ -99,9 +99,11 @@
|
||||
#include "ActivityManager.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "eVendorTransactionResult.h"
|
||||
#include "eReponseMoveItemBetweenInventoryTypeCode.h"
|
||||
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDObjectsTable.h"
|
||||
#include "eItemType.h"
|
||||
|
||||
void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) {
|
||||
CBITSTREAM;
|
||||
@@ -4564,16 +4566,31 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
|
||||
if (inStream.ReadBit()) inStream.Read(itemLOT);
|
||||
|
||||
if (invTypeDst == invTypeSrc) {
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent != nullptr) {
|
||||
if (inventoryComponent) {
|
||||
if (itemID != LWOOBJID_EMPTY) {
|
||||
auto* item = inventoryComponent->FindItemById(itemID);
|
||||
|
||||
if (!item) return;
|
||||
if (!item) {
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_ITEM_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->GetLot() == 6086) { // Thinking hat
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_CANT_MOVE_THINKING_HAT);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destInv = inventoryComponent->GetInventory(invTypeDst);
|
||||
if (destInv && destInv->GetEmptySlots() == 0) {
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_INV_FULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Despawn the pet if we are moving that pet to the vault.
|
||||
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
|
||||
@@ -4582,10 +4599,32 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
|
||||
}
|
||||
|
||||
inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot);
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::SUCCESS);
|
||||
}
|
||||
} else {
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||
}
|
||||
}
|
||||
|
||||
void GameMessages::SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(objectId);
|
||||
bitStream.Write(eGameMessageType::RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES);
|
||||
|
||||
bitStream.Write(inventoryTypeDestination != eInventoryType::ITEMS);
|
||||
if (inventoryTypeDestination != eInventoryType::ITEMS) bitStream.Write(inventoryTypeDestination);
|
||||
|
||||
bitStream.Write(inventoryTypeSource != eInventoryType::ITEMS);
|
||||
if (inventoryTypeSource != eInventoryType::ITEMS) bitStream.Write(inventoryTypeSource);
|
||||
|
||||
bitStream.Write(response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||
if (response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC) bitStream.Write(response);
|
||||
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
|
||||
void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) {
|
||||
CBITSTREAM;
|
||||
@@ -5352,7 +5391,8 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, En
|
||||
iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount);
|
||||
|
||||
if (bConfirmed) {
|
||||
if (eInvType == eInventoryType::MODELS) {
|
||||
const auto itemType = static_cast<eItemType>(item->GetInfo().itemType);
|
||||
if (itemType == eItemType::MODEL || itemType == eItemType::LOOT_MODEL) {
|
||||
item->DisassembleModel(iStackCount);
|
||||
}
|
||||
auto lot = item->GetLot();
|
||||
@@ -6210,3 +6250,81 @@ void GameMessages::SendSlashCommandFeedbackText(Entity* entity, std::u16string t
|
||||
auto sysAddr = entity->GetSystemAddress();
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void GameMessages::HandleUpdateInventoryGroup(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
||||
std::string action;
|
||||
std::u16string groupName;
|
||||
InventoryComponent::GroupUpdate groupUpdate;
|
||||
bool locked{}; // All groups are locked by default
|
||||
|
||||
uint32_t size{};
|
||||
if (!inStream.Read(size)) return;
|
||||
action.resize(size);
|
||||
if (!inStream.Read(action.data(), size)) return;
|
||||
|
||||
if (!inStream.Read(size)) return;
|
||||
groupUpdate.groupId.resize(size);
|
||||
if (!inStream.Read(groupUpdate.groupId.data(), size)) return;
|
||||
|
||||
if (!inStream.Read(size)) return;
|
||||
groupName.resize(size);
|
||||
if (!inStream.Read(reinterpret_cast<char*>(groupName.data()), size * 2)) return;
|
||||
|
||||
if (!inStream.Read(groupUpdate.inventory)) return;
|
||||
if (!inStream.Read(locked)) return;
|
||||
|
||||
groupUpdate.groupName = GeneralUtils::UTF16ToWTF8(groupName);
|
||||
|
||||
if (action == "ADD") groupUpdate.command = InventoryComponent::GroupUpdateCommand::ADD;
|
||||
else if (action == "MODIFY") groupUpdate.command = InventoryComponent::GroupUpdateCommand::MODIFY;
|
||||
else if (action == "REMOVE") groupUpdate.command = InventoryComponent::GroupUpdateCommand::REMOVE;
|
||||
else {
|
||||
LOG("Invalid action %s", action.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
if (inventoryComponent) inventoryComponent->UpdateGroup(groupUpdate);
|
||||
}
|
||||
|
||||
void GameMessages::HandleUpdateInventoryGroupContents(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
||||
std::string action;
|
||||
InventoryComponent::GroupUpdate groupUpdate;
|
||||
|
||||
uint32_t size{};
|
||||
if (!inStream.Read(size)) return;
|
||||
action.resize(size);
|
||||
if (!inStream.Read(action.data(), size)) return;
|
||||
|
||||
if (action == "ADD") groupUpdate.command = InventoryComponent::GroupUpdateCommand::ADD_LOT;
|
||||
else if (action == "REMOVE") groupUpdate.command = InventoryComponent::GroupUpdateCommand::REMOVE_LOT;
|
||||
else {
|
||||
LOG("Invalid action %s", action.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inStream.Read(size)) return;
|
||||
groupUpdate.groupId.resize(size);
|
||||
if (!inStream.Read(groupUpdate.groupId.data(), size)) return;
|
||||
|
||||
if (!inStream.Read(groupUpdate.inventory)) return;
|
||||
if (!inStream.Read(groupUpdate.lot)) return;
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
if (inventoryComponent) inventoryComponent->UpdateGroup(groupUpdate);
|
||||
}
|
||||
|
||||
void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(entity->GetObjectID());
|
||||
bitStream.Write(eGameMessageType::FORCE_CAMERA_TARGET_CYCLE);
|
||||
bitStream.Write(bForceCycling);
|
||||
bitStream.Write(cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES);
|
||||
if (cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES) bitStream.Write(cyclingMode);
|
||||
bitStream.Write(optionalTargetID);
|
||||
|
||||
auto sysAddr = entity->GetSystemAddress();
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,12 @@ enum class eQuickBuildFailReason : uint32_t;
|
||||
enum class eQuickBuildState : uint32_t;
|
||||
enum class BehaviorSlot : int32_t;
|
||||
enum class eVendorTransactionResult : uint32_t;
|
||||
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t;
|
||||
|
||||
enum class eCameraTargetCyclingMode : int32_t {
|
||||
ALLOW_CYCLE_TEAMMATES,
|
||||
DISALLOW_CYCLING
|
||||
};
|
||||
|
||||
namespace GameMessages {
|
||||
class PropertyDataMessage;
|
||||
@@ -589,6 +595,7 @@ namespace GameMessages {
|
||||
//NT:
|
||||
|
||||
void HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
void SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response);
|
||||
|
||||
void SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr);
|
||||
|
||||
@@ -666,6 +673,10 @@ namespace GameMessages {
|
||||
void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity);
|
||||
|
||||
void SendSlashCommandFeedbackText(Entity* entity, std::u16string text);
|
||||
|
||||
void HandleUpdateInventoryGroup(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
void HandleUpdateInventoryGroupContents(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID);
|
||||
};
|
||||
|
||||
#endif // GAMEMESSAGES_H
|
||||
|
||||
@@ -186,7 +186,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
|
||||
|
||||
if (count < 0) {
|
||||
if (mission->IsMission() && type == eMissionTaskType::GATHER && InAllTargets(value)) {
|
||||
if (parameters.size() > 0 && (parameters[0] & 1) != 0) {
|
||||
if (parameters.size() > 0 && (parameters[0] & 4) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "Action.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
#include "tinyxml2.h"
|
||||
|
||||
Action::Action(const AMFArrayValue& arguments) {
|
||||
for (const auto& [paramName, paramValue] : arguments.GetAssociative()) {
|
||||
if (paramName == "Type") {
|
||||
@@ -32,3 +34,46 @@ void Action::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
actionArgs->Insert(m_ValueParameterName, m_ValueParameterDouble);
|
||||
}
|
||||
}
|
||||
|
||||
void Action::Serialize(tinyxml2::XMLElement& action) const {
|
||||
action.SetAttribute("Type", m_Type.c_str());
|
||||
|
||||
if (m_ValueParameterName.empty()) return;
|
||||
|
||||
action.SetAttribute("ValueParameterName", m_ValueParameterName.c_str());
|
||||
|
||||
if (m_ValueParameterName == "Message") {
|
||||
action.SetAttribute("Value", m_ValueParameterString.c_str());
|
||||
} else {
|
||||
action.SetAttribute("Value", m_ValueParameterDouble);
|
||||
}
|
||||
}
|
||||
|
||||
void Action::Deserialize(const tinyxml2::XMLElement& action) {
|
||||
const char* type = nullptr;
|
||||
action.QueryAttribute("Type", &type);
|
||||
if (!type) {
|
||||
LOG("No type found for an action?");
|
||||
return;
|
||||
}
|
||||
|
||||
m_Type = type;
|
||||
|
||||
const char* valueParameterName = nullptr;
|
||||
action.QueryAttribute("ValueParameterName", &valueParameterName);
|
||||
if (valueParameterName) {
|
||||
m_ValueParameterName = valueParameterName;
|
||||
|
||||
if (m_ValueParameterName == "Message") {
|
||||
const char* value = nullptr;
|
||||
action.QueryAttribute("Value", &value);
|
||||
if (value) {
|
||||
m_ValueParameterString = value;
|
||||
} else {
|
||||
LOG("No value found for an action message?");
|
||||
}
|
||||
} else {
|
||||
action.QueryDoubleAttribute("Value", &m_ValueParameterDouble);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
};
|
||||
|
||||
class AMFArrayValue;
|
||||
|
||||
/**
|
||||
@@ -20,6 +24,8 @@ public:
|
||||
|
||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& action) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& action);
|
||||
private:
|
||||
double m_ValueParameterDouble{ 0.0 };
|
||||
std::string m_Type{ "" };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "StripUiPosition.h"
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
StripUiPosition::StripUiPosition(const AMFArrayValue& arguments, const std::string& uiKeyName) {
|
||||
const auto* const uiArray = arguments.GetArray(uiKeyName);
|
||||
@@ -21,3 +22,13 @@ void StripUiPosition::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
uiArgs->Insert("x", m_XPosition);
|
||||
uiArgs->Insert("y", m_YPosition);
|
||||
}
|
||||
|
||||
void StripUiPosition::Serialize(tinyxml2::XMLElement& position) const {
|
||||
position.SetAttribute("x", m_XPosition);
|
||||
position.SetAttribute("y", m_YPosition);
|
||||
}
|
||||
|
||||
void StripUiPosition::Deserialize(const tinyxml2::XMLElement& position) {
|
||||
position.QueryDoubleAttribute("x", &m_XPosition);
|
||||
position.QueryDoubleAttribute("y", &m_YPosition);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
class AMFArrayValue;
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The position of the first Action in a Strip
|
||||
*
|
||||
@@ -15,6 +19,8 @@ public:
|
||||
[[nodiscard]] double GetX() const noexcept { return m_XPosition; }
|
||||
[[nodiscard]] double GetY() const noexcept { return m_YPosition; }
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& position) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& position);
|
||||
private:
|
||||
double m_XPosition{ 0.0 };
|
||||
double m_YPosition{ 0.0 };
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Amf3.h"
|
||||
#include "BehaviorStates.h"
|
||||
#include "ControlBehaviorMsgs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
PropertyBehavior::PropertyBehavior() {
|
||||
m_LastEditedState = BehaviorState::HOME_STATE;
|
||||
@@ -124,3 +125,31 @@ void PropertyBehavior::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
|
||||
// TODO Serialize the execution state of the behavior
|
||||
}
|
||||
|
||||
void PropertyBehavior::Serialize(tinyxml2::XMLElement& behavior) const {
|
||||
behavior.SetAttribute("id", m_BehaviorId);
|
||||
behavior.SetAttribute("name", m_Name.c_str());
|
||||
behavior.SetAttribute("isLocked", isLocked);
|
||||
behavior.SetAttribute("isLoot", isLoot);
|
||||
|
||||
for (const auto& [stateId, state] : m_States) {
|
||||
if (state.IsEmpty()) continue;
|
||||
auto* const stateElement = behavior.InsertNewChildElement("State");
|
||||
stateElement->SetAttribute("id", static_cast<uint32_t>(stateId));
|
||||
state.Serialize(*stateElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) {
|
||||
m_Name = behavior.Attribute("name");
|
||||
behavior.QueryBoolAttribute("isLocked", &isLocked);
|
||||
behavior.QueryBoolAttribute("isLoot", &isLoot);
|
||||
|
||||
for (const auto* stateElement = behavior.FirstChildElement("State"); stateElement; stateElement = stateElement->NextSiblingElement("State")) {
|
||||
int32_t stateId = -1;
|
||||
stateElement->QueryIntAttribute("id", &stateId);
|
||||
if (stateId < 0 || stateId > 5) continue;
|
||||
m_States[static_cast<BehaviorState>(stateId)].Deserialize(*stateElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "State.h"
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
enum class BehaviorState : uint32_t;
|
||||
|
||||
class AMFArrayValue;
|
||||
@@ -25,6 +29,8 @@ public:
|
||||
[[nodiscard]] int32_t GetBehaviorId() const noexcept { return m_BehaviorId; }
|
||||
void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; }
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& behavior) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& behavior);
|
||||
private:
|
||||
|
||||
// The states this behavior has.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "ControlBehaviorMsgs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
template <>
|
||||
void State::HandleMsg(AddStripMessage& msg) {
|
||||
@@ -134,4 +135,20 @@ void State::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
|
||||
strip.SendBehaviorBlocksToClient(*stripArgs);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void State::Serialize(tinyxml2::XMLElement& state) const {
|
||||
for (const auto& strip : m_Strips) {
|
||||
if (strip.IsEmpty()) continue;
|
||||
|
||||
auto* const stripElement = state.InsertNewChildElement("Strip");
|
||||
strip.Serialize(*stripElement);
|
||||
}
|
||||
}
|
||||
|
||||
void State::Deserialize(const tinyxml2::XMLElement& state) {
|
||||
for (const auto* stripElement = state.FirstChildElement("Strip"); stripElement; stripElement = stripElement->NextSiblingElement("Strip")) {
|
||||
auto& strip = m_Strips.emplace_back();
|
||||
strip.Deserialize(*stripElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "Strip.h"
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
class AMFArrayValue;
|
||||
|
||||
class State {
|
||||
@@ -13,6 +17,8 @@ public:
|
||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||
bool IsEmpty() const;
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& state) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& state);
|
||||
private:
|
||||
std::vector<Strip> m_Strips;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "ControlBehaviorMsgs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
template <>
|
||||
void Strip::HandleMsg(AddStripMessage& msg) {
|
||||
@@ -83,4 +84,25 @@ void Strip::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
for (const auto& action : m_Actions) {
|
||||
action.SendBehaviorBlocksToClient(*actions);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void Strip::Serialize(tinyxml2::XMLElement& strip) const {
|
||||
auto* const positionElement = strip.InsertNewChildElement("Position");
|
||||
m_Position.Serialize(*positionElement);
|
||||
for (const auto& action : m_Actions) {
|
||||
auto* const actionElement = strip.InsertNewChildElement("Action");
|
||||
action.Serialize(*actionElement);
|
||||
}
|
||||
}
|
||||
|
||||
void Strip::Deserialize(const tinyxml2::XMLElement& strip) {
|
||||
const auto* positionElement = strip.FirstChildElement("Position");
|
||||
if (positionElement) {
|
||||
m_Position.Deserialize(*positionElement);
|
||||
}
|
||||
|
||||
for (const auto* actionElement = strip.FirstChildElement("Action"); actionElement; actionElement = actionElement->NextSiblingElement("Action")) {
|
||||
auto& action = m_Actions.emplace_back();
|
||||
action.Deserialize(*actionElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
class AMFArrayValue;
|
||||
|
||||
class Strip {
|
||||
@@ -16,6 +20,8 @@ public:
|
||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||
bool IsEmpty() const noexcept { return m_Actions.empty(); }
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& strip) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& strip);
|
||||
private:
|
||||
std::vector<Action> m_Actions;
|
||||
StripUiPosition m_Position;
|
||||
|
||||
@@ -26,7 +26,8 @@ Precondition::Precondition(const uint32_t condition) {
|
||||
if (result.eof()) {
|
||||
this->type = PreconditionType::ItemEquipped;
|
||||
this->count = 1;
|
||||
this->values = { 0 };
|
||||
this->values.clear();
|
||||
this->values.push_back(0);
|
||||
|
||||
LOG("Failed to find precondition of id (%i)!", condition);
|
||||
|
||||
@@ -138,21 +139,20 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
|
||||
case PreconditionType::DoesNotHaveItem:
|
||||
return inventoryComponent->IsEquipped(value) < count;
|
||||
case PreconditionType::HasAchievement:
|
||||
mission = missionComponent->GetMission(value);
|
||||
|
||||
return mission == nullptr || mission->GetMissionState() >= eMissionState::COMPLETE;
|
||||
if (missionComponent == nullptr) return false;
|
||||
return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
|
||||
case PreconditionType::MissionAvailable:
|
||||
mission = missionComponent->GetMission(value);
|
||||
|
||||
return mission == nullptr || mission->GetMissionState() >= eMissionState::AVAILABLE;
|
||||
if (missionComponent == nullptr) return false;
|
||||
return missionComponent->GetMissionState(value) == eMissionState::AVAILABLE || missionComponent->GetMissionState(value) == eMissionState::COMPLETE_AVAILABLE;
|
||||
case PreconditionType::OnMission:
|
||||
mission = missionComponent->GetMission(value);
|
||||
|
||||
return mission == nullptr || mission->GetMissionState() >= eMissionState::ACTIVE;
|
||||
if (missionComponent == nullptr) return false;
|
||||
return missionComponent->GetMissionState(value) == eMissionState::ACTIVE ||
|
||||
missionComponent->GetMissionState(value) == eMissionState::COMPLETE_ACTIVE ||
|
||||
missionComponent->GetMissionState(value) == eMissionState::READY_TO_COMPLETE ||
|
||||
missionComponent->GetMissionState(value) == eMissionState::COMPLETE_READY_TO_COMPLETE;
|
||||
case PreconditionType::MissionComplete:
|
||||
mission = missionComponent->GetMission(value);
|
||||
|
||||
return mission == nullptr ? false : mission->GetMissionState() >= eMissionState::COMPLETE;
|
||||
if (missionComponent == nullptr) return false;
|
||||
return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
|
||||
case PreconditionType::PetDeployed:
|
||||
return false; // TODO
|
||||
case PreconditionType::HasFlag:
|
||||
|
||||
@@ -31,7 +31,6 @@ void SlashCommandHandler::RegisterCommand(Command command) {
|
||||
}
|
||||
|
||||
for (const auto& alias : command.aliases) {
|
||||
LOG_DEBUG("Registering command %s", alias.c_str());
|
||||
auto [_, success] = RegisteredCommands.try_emplace(alias, command);
|
||||
// Don't allow duplicate commands
|
||||
if (!success) {
|
||||
@@ -929,6 +928,15 @@ void SlashCommandHandler::Startup() {
|
||||
};
|
||||
RegisterCommand(FindPlayerCommand);
|
||||
|
||||
Command SpectateCommand{
|
||||
.help = "Spectate a player",
|
||||
.info = "Specify a player name to spectate. They must be in the same world as you. Leave blank to stop spectating",
|
||||
.aliases = { "spectate", "follow" },
|
||||
.handle = GMGreaterThanZeroCommands::Spectate,
|
||||
.requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR
|
||||
};
|
||||
RegisterCommand(SpectateCommand);
|
||||
|
||||
// Register GM Zero Commands
|
||||
|
||||
Command HelpCommand{
|
||||
|
||||
@@ -322,4 +322,19 @@ namespace GMGreaterThanZeroCommands {
|
||||
request.Serialize(bitStream);
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
}
|
||||
|
||||
void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
if (args.empty()) {
|
||||
GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, entity->GetObjectID());
|
||||
return;
|
||||
}
|
||||
|
||||
auto player = PlayerManager::GetPlayer(args);
|
||||
if (!player) {
|
||||
GameMessages::SendSlashCommandFeedbackText(entity, u"Player not found");
|
||||
return;
|
||||
}
|
||||
GameMessages::SendSlashCommandFeedbackText(entity, u"Spectating Player");
|
||||
GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, player->GetObjectID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace GMGreaterThanZeroCommands {
|
||||
void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
}
|
||||
|
||||
#endif //!GMGREATERTHANZEROCOMMANDS_H
|
||||
|
||||
@@ -102,7 +102,7 @@ void VanityUtilities::SpawnVanity() {
|
||||
}
|
||||
|
||||
LWOOBJID SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location) {
|
||||
SceneObject obj;
|
||||
SceneObject obj{};
|
||||
obj.lot = object.m_LOT;
|
||||
// guratantee we have no collisions
|
||||
do {
|
||||
@@ -268,7 +268,7 @@ void ParseXml(const std::string& file) {
|
||||
|
||||
for (auto* location = locations->FirstChildElement("location"); location != nullptr;
|
||||
location = location->NextSiblingElement("location")) {
|
||||
|
||||
|
||||
// Get the location data
|
||||
auto zoneID = GeneralUtils::TryParse<uint32_t>(location->Attribute("zone"));
|
||||
auto x = GeneralUtils::TryParse<float>(location->Attribute("x"));
|
||||
@@ -285,7 +285,6 @@ void ParseXml(const std::string& file) {
|
||||
}
|
||||
|
||||
if (zoneID.value() != currentZoneID) {
|
||||
LOG_DEBUG("Skipping (%s) %i location because it is in %i and not the current zone (%i)", name, lot, zoneID.value(), currentZoneID);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ set(DMASTERSERVER_SOURCES
|
||||
|
||||
add_library(dMasterServer ${DMASTERSERVER_SOURCES})
|
||||
add_executable(MasterServer "MasterServer.cpp")
|
||||
add_compile_definitions(MasterServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
||||
target_compile_definitions(MasterServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
||||
target_include_directories(dMasterServer PUBLIC "."
|
||||
"${PROJECT_SOURCE_DIR}/dZoneManager" # InstanceManager.h uses dZMCommon.h
|
||||
${PROJECT_SOURCE_DIR}/dServer/ # BinaryPathFinder.h
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "BitStreamUtils.h"
|
||||
#include "Start.h"
|
||||
#include "Server.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
@@ -186,15 +188,20 @@ int main(int argc, char** argv) {
|
||||
std::cout << "Enter a username: ";
|
||||
std::cin >> username;
|
||||
|
||||
const auto checkIsAdmin = []() {
|
||||
std::string admin;
|
||||
std::cout << "What level of privilege should this account have? Please enter a number between 0 (Player) and 9 (Admin) inclusive. No entry will default to 0." << std::endl;
|
||||
std::cin >> admin;
|
||||
return admin;
|
||||
};
|
||||
|
||||
auto accountId = Database::Get()->GetAccountInfo(username);
|
||||
if (accountId) {
|
||||
if (accountId && accountId->id != 0) {
|
||||
LOG("Account with name \"%s\" already exists", username.c_str());
|
||||
std::cout << "Do you want to change the password of that account? [y/n]?";
|
||||
std::string prompt = "";
|
||||
std::cin >> prompt;
|
||||
if (prompt == "y" || prompt == "yes") {
|
||||
if (accountId->id == 0) return EXIT_FAILURE;
|
||||
|
||||
//Read the password from the console without echoing it.
|
||||
#ifdef __linux__
|
||||
//This function is obsolete, but it only meant to be used by the
|
||||
@@ -219,6 +226,20 @@ int main(int argc, char** argv) {
|
||||
} else {
|
||||
LOG("Account \"%s\" was not updated.", username.c_str());
|
||||
}
|
||||
|
||||
std::cout << "Update admin privileges? [y/n]? ";
|
||||
std::string admin;
|
||||
std::cin >> admin;
|
||||
bool updateAdmin = admin == "y" || admin == "yes";
|
||||
if (updateAdmin) {
|
||||
auto gmLevel = GeneralUtils::TryParse<int32_t>(checkIsAdmin()).value_or(0);
|
||||
if (gmLevel > 9 || gmLevel < 0) {
|
||||
LOG("Invalid admin level. Defaulting to 0");
|
||||
gmLevel = 0;
|
||||
}
|
||||
Database::Get()->UpdateAccountGmLevel(accountId->id, static_cast<eGameMasterLevel>(gmLevel));
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -249,6 +270,17 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
LOG("Account created successfully!");
|
||||
|
||||
accountId = Database::Get()->GetAccountInfo(username);
|
||||
if (accountId) {
|
||||
auto gmLevel = GeneralUtils::TryParse<int32_t>(checkIsAdmin()).value_or(0);
|
||||
if (gmLevel > 9 || gmLevel < 0) {
|
||||
LOG("Invalid admin level. Defaulting to 0");
|
||||
gmLevel = 0;
|
||||
}
|
||||
Database::Get()->UpdateAccountGmLevel(accountId->id, static_cast<eGameMasterLevel>(gmLevel));
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -277,6 +309,17 @@ int main(int argc, char** argv) {
|
||||
PersistentIDManager::Initialize();
|
||||
Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
|
||||
|
||||
//Get CDClient initial information
|
||||
try {
|
||||
CDClientManager::LoadValuesFromDatabase();
|
||||
} catch (CppSQLite3Exception& e) {
|
||||
LOG("Failed to initialize CDServer SQLite Database");
|
||||
LOG("May be caused by corrupted file: %s", (Game::assetManager->GetResPath() / "CDServer.sqlite").string().c_str());
|
||||
LOG("Error: %s", e.errorMessage());
|
||||
LOG("Error Code: %i", e.errorCode());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//Depending on the config, start up servers:
|
||||
if (Game::config->GetValue("prestart_servers") != "0") {
|
||||
StartChatServer();
|
||||
@@ -382,6 +425,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) {
|
||||
LOG("A server has disconnected");
|
||||
|
||||
@@ -545,7 +589,7 @@ void HandlePacket(Packet* packet) {
|
||||
inStream.Read(sessionKey);
|
||||
LUString username;
|
||||
inStream.Read(username);
|
||||
|
||||
|
||||
for (auto it : activeSessions) {
|
||||
if (it.second == username.string) {
|
||||
activeSessions.erase(it.first);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "MessageIdentifiers.h"
|
||||
#include "BitStream.h"
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
enum class eConnectionType : uint16_t;
|
||||
|
||||
|
||||
@@ -359,7 +359,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
//Warning if we ran slow
|
||||
if (deltaTime > currentFrameDelta) {
|
||||
LOG("We're running behind, dT: %f > %f (framerate %i)", deltaTime, currentFrameDelta, currentFramerate);
|
||||
LOG("We're running behind, dT: %f > %i (framerate %i)", deltaTime, currentFrameDelta, currentFramerate);
|
||||
}
|
||||
|
||||
//Check if we're still connected to master:
|
||||
@@ -529,6 +529,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
void HandlePacketChat(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||
LOG("Lost our connection to chat, zone(%i), instance(%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
|
||||
|
||||
@@ -542,7 +543,7 @@ void HandlePacketChat(Packet* packet) {
|
||||
chatConnected = true;
|
||||
}
|
||||
|
||||
if (packet->data[0] == ID_USER_PACKET_ENUM) {
|
||||
if (packet->data[0] == ID_USER_PACKET_ENUM && packet->length >= 4) {
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
|
||||
switch (static_cast<eChatMessageType>(packet->data[3])) {
|
||||
case eChatMessageType::WORLD_ROUTE_PACKET: {
|
||||
@@ -557,8 +558,9 @@ void HandlePacketChat(Packet* packet) {
|
||||
|
||||
//Write our stream outwards:
|
||||
CBITSTREAM;
|
||||
for (BitSize_t i = 0; i < inStream.GetNumberOfBytesUsed(); i++) {
|
||||
bitStream.Write(packet->data[i + 16]); //16 bytes == header + playerID to skip
|
||||
unsigned char data;
|
||||
while (inStream.Read(data)) {
|
||||
bitStream.Write(data);
|
||||
}
|
||||
|
||||
SEND_PACKET; //send routed packet to player
|
||||
@@ -659,7 +661,7 @@ void HandlePacketChat(Packet* packet) {
|
||||
}
|
||||
|
||||
void HandleMasterPacket(Packet* packet) {
|
||||
|
||||
if (packet->length < 2) return;
|
||||
if (static_cast<eConnectionType>(packet->data[1]) != eConnectionType::MASTER || packet->length < 4) return;
|
||||
switch (static_cast<eMasterMessageType>(packet->data[3])) {
|
||||
case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: {
|
||||
@@ -785,6 +787,7 @@ void HandleMasterPacket(Packet* packet) {
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||
auto user = UserManager::Instance()->GetUser(packet->systemAddress);
|
||||
if (!user) return;
|
||||
@@ -998,7 +1001,6 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
case eWorldMessageType::CHARACTER_DELETE_REQUEST: {
|
||||
UserManager::Instance()->DeleteCharacter(packet->systemAddress, packet);
|
||||
UserManager::Instance()->RequestCharacterList(packet->systemAddress);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1207,8 +1209,8 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
//Now write the rest of the data:
|
||||
auto data = inStream.GetData();
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
bitStream.Write(data[i + 23]);
|
||||
for (uint32_t i = 23; i - 23 < size && i < packet->length; ++i) {
|
||||
bitStream.Write(data[i]);
|
||||
}
|
||||
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false);
|
||||
|
||||
3
migrations/dlu/15_behavior_owner.sql
Normal file
3
migrations/dlu/15_behavior_owner.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE behaviors ADD COLUMN character_id BIGINT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE behaviors ADD COLUMN behavior_id BIGINT NOT NULL PRIMARY KEY;
|
||||
ALTER TABLE behaviors DROP COLUMN id;
|
||||
1
migrations/dlu/16_big_behaviors.sql
Normal file
1
migrations/dlu/16_big_behaviors.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE behaviors MODIFY behavior_info LONGTEXT DEFAULT NULL;
|
||||
Binary file not shown.
@@ -4,8 +4,6 @@ enable_testing()
|
||||
find_package(GoogleTest REQUIRED)
|
||||
include(GoogleTest)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH True)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH True)
|
||||
|
||||
@@ -176,7 +176,7 @@ TEST(dCommonTests, AMFDeserializeAMFArrayTest) {
|
||||
/**
|
||||
* @brief This test checks that if we recieve an unimplemented eAmf
|
||||
* we correctly throw an error and can actch it.
|
||||
* Yes this leaks memory.
|
||||
* Yes this leaks memory.
|
||||
*/
|
||||
TEST(dCommonTests, AMFDeserializeUnimplementedValuesTest) {
|
||||
std::vector<eAmf> unimplementedValues = {
|
||||
@@ -363,7 +363,7 @@ TEST(dCommonTests, AMFBadConversionTest) {
|
||||
ASSERT_EQ(result->Get<double>("BehaviorID"), nullptr);
|
||||
|
||||
// Does not exist in the associative portion
|
||||
ASSERT_EQ(result->Get<nullptr_t>("DOES_NOT_EXIST"), nullptr);
|
||||
ASSERT_EQ(result->Get<std::nullptr_t>("DOES_NOT_EXIST"), nullptr);
|
||||
|
||||
result->Push(true);
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ TEST(dCommonTests, AMF3InsertionAssociativeTest) {
|
||||
ASSERT_EQ(array.Get<int32_t>("Integer")->GetValueType(), eAmf::Integer);
|
||||
ASSERT_EQ(array.Get<double>("Double")->GetValueType(), eAmf::Double);
|
||||
ASSERT_EQ(array.GetArray("Array")->GetValueType(), eAmf::Array);
|
||||
ASSERT_EQ(array.Get<nullptr_t>("Null")->GetValueType(), eAmf::Null);
|
||||
ASSERT_EQ(array.Get<std::nullptr_t>("Null")->GetValueType(), eAmf::Null);
|
||||
ASSERT_EQ(array.Get<std::vector<uint32_t>>("Undefined")->GetValueType(), eAmf::Undefined);
|
||||
}
|
||||
|
||||
@@ -101,6 +101,6 @@ TEST(dCommonTests, AMF3InsertionDenseTest) {
|
||||
ASSERT_EQ(array.Get<int32_t>(4)->GetValueType(), eAmf::Integer);
|
||||
ASSERT_EQ(array.Get<double>(5)->GetValueType(), eAmf::Double);
|
||||
ASSERT_EQ(array.GetArray(6)->GetValueType(), eAmf::Array);
|
||||
ASSERT_EQ(array.Get<nullptr_t>(7)->GetValueType(), eAmf::Null);
|
||||
ASSERT_EQ(array.Get<std::nullptr_t>(7)->GetValueType(), eAmf::Null);
|
||||
ASSERT_EQ(array.Get<std::vector<uint32_t>>(8)->GetValueType(), eAmf::Undefined);
|
||||
}
|
||||
|
||||
5
thirdparty/CMakeLists.txt
vendored
5
thirdparty/CMakeLists.txt
vendored
@@ -23,6 +23,11 @@ if(NOT WIN32)
|
||||
target_include_directories(bcrypt PRIVATE "libbcrypt/include/bcrypt")
|
||||
endif()
|
||||
|
||||
# Need to define this on Clang and GNU for 'strdup' support
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||
target_compile_definitions(bcrypt PRIVATE "_POSIX_C_SOURCE=200809L")
|
||||
endif()
|
||||
|
||||
target_include_directories(bcrypt INTERFACE "libbcrypt/include")
|
||||
target_include_directories(bcrypt PRIVATE "libbcrypt/src")
|
||||
|
||||
|
||||
2
thirdparty/SQLite/CMakeLists.txt
vendored
2
thirdparty/SQLite/CMakeLists.txt
vendored
@@ -11,7 +11,7 @@ if(UNIX)
|
||||
|
||||
# -Wno-unused-result -Wno-unknown-pragmas -fpermissive
|
||||
target_compile_options(sqlite3 PRIVATE)
|
||||
if(NOT APPLE)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(sqlite3 PRIVATE "-Wno-return-local-addr" "-Wno-maybe-uninitialized")
|
||||
else()
|
||||
target_compile_options(sqlite3 PRIVATE "-Wno-return-stack-address" "-Wno-uninitialized" "-Wno-deprecated-declarations")
|
||||
|
||||
Reference in New Issue
Block a user