Compare commits

...

11 Commits

Author SHA1 Message Date
eb1137f664 Merge branch 'windows-clang' of https://github.com/DarkflameUniverse/DarkflameServer into windows-clang 2024-12-08 20:50:25 +00:00
10e64a5d20 re-add testPresets 2024-12-08 20:48:36 +00:00
Jett
9d52c0fb84 Merge branch 'main' into windows-clang 2024-12-08 20:40:40 +00:00
83e08f63bc remove test and workflow presets 2024-12-08 20:31:27 +00:00
ece0e29577 I made this mistake years ago, my bad 2024-12-05 20:03:17 +00:00
6eea3f3662 Fix up ZLib a little more and remove more configuration into CMakeLists 2024-12-05 19:45:21 +00:00
6c8bb743af Remove user-specific clang configurations
Also replace zlib with zlib-ng
2024-12-04 22:38:05 +00:00
jadebenn
f4082cc538 formatting 2024-12-03 21:18:31 -06:00
jadebenn
edc7109e19 update presets 2024-12-03 21:14:24 -06:00
jadebenn
da45152b43 clang cl toolset 2024-12-03 20:41:23 -06:00
jadebenn
8b9a0768a2 trying to enable clang for windows msvc 2024-12-03 20:30:09 -06:00
35 changed files with 41920 additions and 116928 deletions

View File

@@ -43,7 +43,6 @@ jobs:
build/*/*.ini build/*/*.ini
build/*/*.so build/*/*.so
build/*/*.dll build/*/*.dll
build/*/*.dylib
build/*/vanity/ build/*/vanity/
build/*/navmeshes/ build/*/navmeshes/
build/*/migrations/ build/*/migrations/

2
.gitignore vendored
View File

@@ -112,6 +112,8 @@ CMakeFiles/TargetDirectories.txt
*.sln *.sln
*.recipe *.recipe
CMakeUserPresets.json
# clangd # clangd
.cache .cache
thirdparty/zlib-1.2.11/ thirdparty/zlib-1.2.11/

View File

@@ -19,6 +19,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debuggi
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects 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 set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: Debug, Release, RelWithDebInfo, MinSizeRel")
set(DLU_CONFIG_DIR ${CMAKE_SOURCE_DIR}/build CACHE PATH "The directory where the server configuration files are stored")
# Read variables from file # Read variables from file
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables) FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
@@ -66,13 +69,12 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
# Disabled no-register # Disabled no-register
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas. # Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
if(UNIX) if(UNIX)
add_link_options("-Wl,-rpath,$ORIGIN/")
add_compile_options("-fPIC") add_compile_options("-fPIC")
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0) add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)
# For all except Clang and Apple Clang # For all except Clang and Apple Clang
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options("-static-libgcc" "-lstdc++fs") add_compile_options("-static-libgcc" "-lstdc++fs" "-Wno-error=implicit-function-declaration" "-Wno-error=int-conversion")
endif() endif()
if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@@ -90,10 +92,11 @@ elseif(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS) add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif() endif()
# Our output dir # Set the output directories
#set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) # unfortunately, forces all libraries to be built in series, which will slow down the build process # ./build/<platform + architecture (x64, x86, aarch64)>/<compiler>/<build_mode>/
set(CMAKE_BINARY_DIR ${CMAKE_BINARY_DIR}/${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}/${CMAKE_CXX_COMPILER_ID}/${CMAKE_BUILD_TYPE})
# TODO make this not have to override the build type directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_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_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
@@ -108,6 +111,15 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
# Get DLU config directory
if(DEFINED ENV{DLU_CONFIG_DIR})
set(DLU_CONFIG_DIR $ENV{DLU_CONFIG_DIR})
else()
set(DLU_CONFIG_DIR ${CMAKE_BINARY_DIR})
endif()
message(STATUS "Configuration Directory is ${DLU_CONFIG_DIR}, and the build directory is ${CMAKE_BINARY_DIR}")
find_package(MariaDB) find_package(MariaDB)
# Create a /resServer directory # Create a /resServer directory
@@ -116,18 +128,11 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
# Create a /logs directory # Create a /logs directory
make_directory(${CMAKE_BINARY_DIR}/logs) 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 # Copy resource files on first build
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf") set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
message(STATUS "Checking resource file integrity") message(STATUS "Checking resource file integrity")
include(Utils) include(Utils)
UpdateConfigOption(${DLU_CONFIG_DIR}/authconfig.ini "port" "auth_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}/chatconfig.ini "port" "chat_server_port")
@@ -313,7 +318,7 @@ add_subdirectory(dPhysics)
add_subdirectory(dServer) add_subdirectory(dServer)
# Create a list of common libraries shared between all binaries # Create a list of common libraries shared between all binaries
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "magic_enum") set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "MariaDB::ConnCpp" "magic_enum")
# Add platform specific common libraries # Add platform specific common libraries
if(UNIX) if(UNIX)

View File

@@ -1,638 +1,191 @@
{ {
"version": 6, "version": 6,
"cmakeMinimumRequired": { "cmakeMinimumRequired": {
"major": 3, "major": 3,
"minor": 25, "minor": 25,
"patch": 0 "patch": 0
}, },
"configurePresets": [ "configurePresets": [
{ {
"name": "default", "name": "default",
"displayName": "Default configure step", "displayName": "Default configure step",
"description": "Use 'build' dir and Unix makefiles", "description": "Use 'build' dir and Unix makefiles",
"binaryDir": "${sourceDir}/build", "binaryDir": "${sourceDir}/build",
"generator": "Unix Makefiles" "generator": "Unix Makefiles",
}, "hidden": true
{ },
"name": "debug-config", {
"hidden": true, "name": "windows-default",
"cacheVariables": { "inherits": "default",
"CMAKE_BUILD_TYPE": "Debug" "displayName": "Windows Default Configure Settings",
} "description": "Sets build and install directories",
}, "generator": "Visual Studio 17 2022",
{ "condition": {
"name": "relwithdebinfo-config", "type": "equals",
"hidden": true, "lhs": "${hostSystemName}",
"cacheVariables": { "rhs": "Windows"
"CMAKE_BUILD_TYPE": "RelWithDebInfo" },
} "hidden": true
}, },
{ {
"name": "release-config", "name": "windows-msvc",
"hidden": true, "inherits": "windows-default",
"cacheVariables": { "displayName": "Windows (MSVC)",
"CMAKE_BUILD_TYPE": "Release" "description": "Create a build using MSVC"
} },
}, {
{ "name": "windows-clang",
"name": "clang-config", "inherits": "windows-default",
"hidden": true, "displayName": "EXPERIMENTAL - Windows (Clang)",
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-clang.cmake" "description": "Create a build using Clang",
}, "toolset": "ClangCL"
{ },
"name": "gnu-config", {
"hidden": true, "name": "linux-default",
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-gnu.cmake" "inherits": "default",
}, "hidden": true,
{ "condition": {
"name": "windows-msvc", "type": "equals",
"inherits": "default", "lhs": "${hostSystemName}",
"displayName": "[Multi] Windows (MSVC)", "rhs": "Linux"
"description": "Set architecture to 64-bit (b/c RakNet)", }
"generator": "Visual Studio 17 2022", },
"binaryDir": "${sourceDir}/build/msvc", {
"architecture": { "name": "linux-clang",
"value": "x64" "inherits": "linux-default",
}, "toolchainFile": "${sourceDir}/cmake/toolchains/linux-clang.cmake",
"condition": { "displayName": "Linux (Clang)",
"type": "equals", "description": "Create a build using the Clang toolchain for Linux"
"lhs": "${hostSystemName}", },
"rhs": "Windows" {
} "name": "linux-gnu",
}, "inherits": "linux-default",
{ "toolchainFile": "${sourceDir}/cmake/toolchains/linux-gnu.cmake",
"name": "windows-default", "displayName": "Linux (GNU)",
"inherits": "windows-msvc", "description": "Create a build using the GNU toolchain for Linux"
"displayName": "Windows only Configure Settings", },
"description": "Sets build and install directories", {
"generator": "Ninja", "name": "macos",
"condition": { "inherits": "default",
"type": "equals", "displayName": "MacOS",
"lhs": "${hostSystemName}", "description": "Create a build for MacOS",
"rhs": "Windows" "condition": {
}, "type": "equals",
"architecture": { "lhs": "${hostSystemName}",
"value": "x64" "rhs": "Darwin"
} }
}, }
{ ],
"name": "linux-config", "buildPresets": [
"inherits": "default", {
"hidden": true, "name": "default",
"condition": { "configurePreset": "default",
"type": "equals", "displayName": "Default Build",
"lhs": "${hostSystemName}", "description": "Default Build",
"rhs": "Linux" "jobs": 2
} }
}, ],
{ "testPresets": [
"name": "linux-clang-debug", {
"inherits": [ "name": "ci-ubuntu-22.04",
"linux-config", "configurePreset": "ci-ubuntu-22.04",
"clang-config", "displayName": "CI Tests on Linux",
"debug-config" "description": "Runs all tests on a linux configuration",
], "execution": {
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)", "jobs": 2
"description": "Create a debug build using the Clang toolchain for Linux", },
"binaryDir": "${sourceDir}/build/clang-debug" "output": {
}, "outputOnFailure": true
{ }
"name": "linux-clang-relwithdebinfo", },
"inherits": [ {
"linux-config", "name": "ci-macos-13",
"clang-config", "configurePreset": "ci-macos-13",
"relwithdebinfo-config" "displayName": "CI Tests on MacOS",
], "description": "Runs all tests on a Mac configuration",
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)", "execution": {
"description": "Create a release build with debug info using the Clang toolchain for Linux", "jobs": 2
"binaryDir": "${sourceDir}/build/clang-relwithdebinfo" },
}, "output": {
{ "outputOnFailure": true
"name": "linux-clang-release", }
"inherits": [ },
"linux-config", {
"clang-config", "name": "ci-windows-2022",
"release-config" "configurePreset": "ci-windows-2022",
], "displayName": "CI Tests on windows",
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)", "description": "Runs all tests on a windows configuration",
"description": "Create a release build using the Clang toolchain for Linux", "configuration": "RelWithDebInfo",
"binaryDir": "${sourceDir}/build/clang-release" "execution": {
}, "jobs": 2
{ },
"name": "linux-gnu-debug", "output": {
"inherits": [ "outputOnFailure": true
"linux-config", },
"gnu-config", "filter": {
"debug-config" "exclude": {
], "name": "((example)|(minigzip))+"
"displayName": "[Debug] Linux (GNU)", }
"description": "Create a debug build using the GNU toolchain for Linux", }
"binaryDir": "${sourceDir}/build/gnu-debug" }
}, ],
{ "workflowPresets": [
"name": "linux-gnu-relwithdebinfo", {
"inherits": [ "name": "ci-windows-2022",
"linux-config", "displayName": "[Release] Windows (MSVC)",
"gnu-config", "description": "CI workflow preset for Windows",
"relwithdebinfo-config" "steps": [
], {
"displayName": "[RelWithDebInfo] Linux (GNU)", "type": "configure",
"description": "Create a release build with debug info using the GNU toolchain for Linux", "name": "windows-msvc"
"binaryDir": "${sourceDir}/build/gnu-relwithdebinfo" },
}, {
{ "type": "build",
"name": "linux-gnu-release", "name": "default"
"inherits": [ },
"linux-config", {
"gnu-config", "type": "test",
"release-config" "name": "default"
], }
"displayName": "[Release] Linux (GNU)", ]
"description": "Create a release build using the GNU toolchain for Linux", },
"binaryDir": "${sourceDir}/build/gnu-release" {
}, "name": "ci-ubuntu-22.04",
{ "displayName": "[Release] Linux (GNU)",
"name": "macos", "description": "CI workflow preset for Ubuntu",
"inherits": "default", "steps": [
"displayName": "[Multi] MacOS", {
"description": "Create a build for MacOS", "type": "configure",
"condition": { "name": "linux-gnu"
"type": "equals", },
"lhs": "${hostSystemName}", {
"rhs": "Darwin" "type": "build",
}, "name": "default"
"binaryDir": "${sourceDir}/build/macos" },
} {
], "type": "test",
"buildPresets": [ "name": "ci-ubuntu-22.04"
{ }
"name": "default", ]
"configurePreset": "default", },
"displayName": "Default Build", {
"description": "Default Build", "name": "ci-macos-13",
"jobs": 2 "displayName": "[Release] MacOS",
}, "description": "CI workflow preset for MacOS",
{ "steps": [
"name": "windows-msvc-debug", {
"inherits": "default", "type": "configure",
"configurePreset": "windows-msvc", "name": "macos"
"displayName": "[Debug] Windows (MSVC)", },
"description": "This preset is used to build in debug mode using the MSVC toolchain on Windows", {
"configuration": "Debug" "type": "build",
}, "name": "default"
{ },
"name": "windows-msvc-relwithdebinfo", {
"inherits": "default", "type": "test",
"configurePreset": "windows-msvc", "name": "default"
"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"
}
]
}
]
}

View File

@@ -60,7 +60,7 @@ int main(int argc, char** argv) {
try { try {
Database::Connect(); Database::Connect();
} catch (std::exception& ex) { } catch (sql::SQLException& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("AuthServer"); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;

View File

@@ -81,7 +81,7 @@ int main(int argc, char** argv) {
//Connect to the MySQL Database //Connect to the MySQL Database
try { try {
Database::Connect(); Database::Connect();
} catch (std::exception& ex) { } catch (sql::SQLException& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("ChatServer"); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;

View File

@@ -123,7 +123,7 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
Database::Get()->UpdateUgcModelData(model.id, outputStringStream); Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
LOG("Updated model %i to sd0", model.id); LOG("Updated model %i to sd0", model.id);
updatedModels++; updatedModels++;
} catch (std::exception& exception) { } catch (sql::SQLException exception) {
LOG("Failed to update model %i. This model should be inspected manually to see why." LOG("Failed to update model %i. This model should be inspected manually to see why."
"The database error is %s", model.id, exception.what()); "The database error is %s", model.id, exception.what());
} }

View File

@@ -45,11 +45,14 @@ if (UNIX)
elseif (WIN32) elseif (WIN32)
include(FetchContent) include(FetchContent)
set(WITH_GTEST OFF CACHE BOOL "" FORCE)
set(ZLIB_ENABLE_TESTS OFF CACHE BOOL "" FORCE)
# TODO Keep an eye on the zlib repository for an update to disable testing. Don't forget to update CMakePresets # TODO Keep an eye on the zlib repository for an update to disable testing. Don't forget to update CMakePresets
FetchContent_Declare( FetchContent_Declare(
zlib zlib
URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip URL https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.2.2.zip
URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1 URL_HASH MD5=2cf9199fb785ea579a2a9905a75c38b3
) )
# Disable warning about no project version. # Disable warning about no project version.
@@ -60,7 +63,6 @@ elseif (WIN32)
FetchContent_MakeAvailable(zlib) FetchContent_MakeAvailable(zlib)
set(ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR}) set(ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR})
set_target_properties(zlib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIRS}")
add_library(ZLIB::ZLIB ALIAS zlib) add_library(ZLIB::ZLIB ALIAS zlib)
else () else ()
message( message(

View File

@@ -1,6 +1,6 @@
#include "ZCompression.h" #include "ZCompression.h"
#include "zlib.h" #include "zlib-ng.h"
namespace ZCompression { namespace ZCompression {
int32_t GetMaxCompressedLength(int32_t nLenSrc) { int32_t GetMaxCompressedLength(int32_t nLenSrc) {
@@ -9,41 +9,41 @@ namespace ZCompression {
} }
int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst) { int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst) {
z_stream zInfo = { 0 }; zng_stream zInfo = { 0 };
zInfo.total_in = zInfo.avail_in = nLenSrc; zInfo.total_in = zInfo.avail_in = nLenSrc;
zInfo.total_out = zInfo.avail_out = nLenDst; zInfo.total_out = zInfo.avail_out = nLenDst;
zInfo.next_in = const_cast<Bytef*>(abSrc); zInfo.next_in = const_cast<Bytef*>(abSrc);
zInfo.next_out = abDst; zInfo.next_out = abDst;
int nErr, nRet = -1; int nErr, nRet = -1;
nErr = deflateInit(&zInfo, Z_DEFAULT_COMPRESSION); // zlib function nErr = zng_deflateInit(&zInfo, Z_DEFAULT_COMPRESSION); // zlib function
if (nErr == Z_OK) { if (nErr == Z_OK) {
nErr = deflate(&zInfo, Z_FINISH); // zlib function nErr = zng_deflate(&zInfo, Z_FINISH); // zlib function
if (nErr == Z_STREAM_END) { if (nErr == Z_STREAM_END) {
nRet = zInfo.total_out; nRet = zInfo.total_out;
} }
} }
deflateEnd(&zInfo); // zlib function zng_deflateEnd(&zInfo); // zlib function
return(nRet); return(nRet);
} }
int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr) { int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr) {
// Get the size of the decompressed data // Get the size of the decompressed data
z_stream zInfo = { 0 }; zng_stream zInfo = { 0 };
zInfo.total_in = zInfo.avail_in = nLenSrc; zInfo.total_in = zInfo.avail_in = nLenSrc;
zInfo.total_out = zInfo.avail_out = nLenDst; zInfo.total_out = zInfo.avail_out = nLenDst;
zInfo.next_in = const_cast<Bytef*>(abSrc); zInfo.next_in = const_cast<Bytef*>(abSrc);
zInfo.next_out = abDst; zInfo.next_out = abDst;
int nRet = -1; int nRet = -1;
nErr = inflateInit(&zInfo); // zlib function nErr = zng_inflateInit(&zInfo); // zlib function
if (nErr == Z_OK) { if (nErr == Z_OK) {
nErr = inflate(&zInfo, Z_FINISH); // zlib function nErr = zng_inflate(&zInfo, Z_FINISH); // zlib function
if (nErr == Z_STREAM_END) { if (nErr == Z_STREAM_END) {
nRet = zInfo.total_out; nRet = zInfo.total_out;
} }
} }
inflateEnd(&zInfo); // zlib function zng_inflateEnd(&zInfo); // zlib function
return(nRet); return(nRet);
} }
} }

View File

@@ -4,8 +4,6 @@
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "Logger.h"
#include "zlib.h"
AssetManager::AssetManager(const std::filesystem::path& path) { AssetManager::AssetManager(const std::filesystem::path& path) {
if (!std::filesystem::is_directory(path)) { if (!std::filesystem::is_directory(path)) {
throw std::runtime_error("Attempted to load asset bundle (" + path.string() + ") however it is not a valid directory."); throw std::runtime_error("Attempted to load asset bundle (" + path.string() + ") however it is not a valid directory.");
@@ -82,7 +80,7 @@ bool AssetManager::HasFile(const char* name) {
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName; if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size()); uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4); crc = crc32b(crc, reinterpret_cast<uint8_t*>(const_cast<char*>("\0\0\0\0")), 4);
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) { for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
if (item.m_Crc == crc) { if (item.m_Crc == crc) {
@@ -130,7 +128,7 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
} }
int32_t packIndex = -1; int32_t packIndex = -1;
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size()); uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4); crc = crc32b(crc, reinterpret_cast<uint8_t*>(const_cast<char*>("\0\0\0\0")), 4);
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) { for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
if (item.m_Crc == crc) { if (item.m_Crc == crc) {

View File

@@ -2,12 +2,6 @@ add_subdirectory(CDClientDatabase)
add_subdirectory(GameDatabase) add_subdirectory(GameDatabase)
add_library(dDatabase STATIC "MigrationRunner.cpp") add_library(dDatabase STATIC "MigrationRunner.cpp")
add_custom_target(conncpp_dylib
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR})
add_dependencies(dDatabase conncpp_dylib)
target_include_directories(dDatabase PUBLIC ".") target_include_directories(dDatabase PUBLIC ".")
target_link_libraries(dDatabase target_link_libraries(dDatabase
PUBLIC dDatabaseCDClient dDatabaseGame) PUBLIC dDatabaseCDClient dDatabaseGame)

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <conncpp.hpp>
#include "GameDatabase.h" #include "GameDatabase.h"

View File

@@ -24,7 +24,6 @@
#include "IIgnoreList.h" #include "IIgnoreList.h"
#include "IAccountsRewardCodes.h" #include "IAccountsRewardCodes.h"
#include "IBehaviors.h" #include "IBehaviors.h"
#include "IUgcModularBuild.h"
namespace sql { namespace sql {
class Statement; class Statement;
@@ -43,7 +42,7 @@ class GameDatabase :
public IPropertyContents, public IProperty, public IPetNames, public ICharXml, public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo, 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 IUgcModularBuild { public IBehaviors {
public: public:
virtual ~GameDatabase() = default; virtual ~GameDatabase() = default;
// TODO: These should be made private. // TODO: These should be made private.

View File

@@ -3,44 +3,12 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include <string>
#include <vector>
class ILeaderboard { class ILeaderboard {
public: public:
struct Entry {
uint32_t charId{};
uint32_t lastPlayedTimestamp{};
float primaryScore{};
float secondaryScore{};
uint32_t tertiaryScore{};
uint32_t numWins{};
uint32_t numTimesPlayed{};
uint32_t ranking{};
std::string name{};
};
struct Score {
auto operator<=>(const Score& rhs) const = default;
float primaryScore{ 0.0f };
float secondaryScore{ 0.0f };
float tertiaryScore{ 0.0f };
};
// Get the donation total for the given activity id. // Get the donation total for the given activity id.
virtual std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) = 0; virtual std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) = 0;
virtual std::optional<Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) = 0;
virtual void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) = 0;
virtual void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) = 0;
virtual void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) = 0;
}; };
#endif //!__ILEADERBOARD__H__ #endif //!__ILEADERBOARD__H__

View File

@@ -1,14 +0,0 @@
#ifndef IUGCMODULARBUILD_H
#define IUGCMODULARBUILD_H
#include <cstdint>
#include <optional>
#include <string>
class IUgcModularBuild {
public:
virtual void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) = 0;
virtual void DeleteUgcBuild(const LWOOBJID bigId) = 0;
};
#endif //!IUGCMODULARBUILD_H

View File

@@ -7,7 +7,6 @@
#include "GameDatabase.h" #include "GameDatabase.h"
typedef std::unique_ptr<sql::PreparedStatement>& UniquePreppedStmtRef; typedef std::unique_ptr<sql::PreparedStatement>& UniquePreppedStmtRef;
typedef std::unique_ptr<sql::ResultSet> UniqueResultSet;
// Purposefully no definition for this to provide linker errors in the case someone tries to // Purposefully no definition for this to provide linker errors in the case someone tries to
// bind a parameter to a type that isn't defined. // bind a parameter to a type that isn't defined.
@@ -114,16 +113,6 @@ public:
void RemoveBehavior(const int32_t characterId) override; void RemoveBehavior(const int32_t characterId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override; void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override; std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) override;
void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
std::optional<ILeaderboard::Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) override;
void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) override;
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
void DeleteUgcBuild(const LWOOBJID bigId) override;
private: private:
// Generic query functions that can be used for any query. // Generic query functions that can be used for any query.

View File

@@ -20,7 +20,6 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"PropertyContents.cpp" "PropertyContents.cpp"
"Servers.cpp" "Servers.cpp"
"Ugc.cpp" "Ugc.cpp"
"UgcModularBuild.cpp"
PARENT_SCOPE PARENT_SCOPE
) )

View File

@@ -1,9 +1,5 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
#include "Game.h"
#include "Logger.h"
#include "dConfig.h"
std::optional<uint32_t> MySQLDatabase::GetDonationTotal(const uint32_t activityId) { std::optional<uint32_t> MySQLDatabase::GetDonationTotal(const uint32_t activityId) {
auto donation_total = ExecuteSelect("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;", activityId); auto donation_total = ExecuteSelect("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;", activityId);
@@ -13,75 +9,3 @@ std::optional<uint32_t> MySQLDatabase::GetDonationTotal(const uint32_t activityI
return donation_total->getUInt("donation_total"); return donation_total->getUInt("donation_total");
} }
std::vector<ILeaderboard::Entry> ProcessQuery(UniqueResultSet& rows) {
std::vector<ILeaderboard::Entry> entries;
entries.reserve(rows->rowsCount());
while (rows->next()) {
auto& entry = entries.emplace_back();
entry.charId = rows->getUInt("character_id");
entry.lastPlayedTimestamp = rows->getUInt("lp_unix");
entry.primaryScore = rows->getFloat("primaryScore");
entry.secondaryScore = rows->getFloat("secondaryScore");
entry.tertiaryScore = rows->getFloat("tertiaryScore");
entry.numWins = rows->getUInt("numWins");
entry.numTimesPlayed = rows->getUInt("timesPlayed");
entry.name = rows->getString("char_name");
// entry.ranking is never set because its calculated in leaderboard in code.
}
return entries;
}
std::vector<ILeaderboard::Entry> MySQLDatabase::GetDescendingLeaderboard(const uint32_t activityId) {
auto leaderboard = ExecuteSelect("SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore DESC, tertiaryScore DESC, last_played ASC;", activityId);
return ProcessQuery(leaderboard);
}
std::vector<ILeaderboard::Entry> MySQLDatabase::GetAscendingLeaderboard(const uint32_t activityId) {
auto leaderboard = ExecuteSelect("SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore ASC, secondaryscore ASC, tertiaryScore ASC, last_played ASC;", activityId);
return ProcessQuery(leaderboard);
}
std::vector<ILeaderboard::Entry> MySQLDatabase::GetAgsLeaderboard(const uint32_t activityId) {
auto query = Game::config->GetValue("classic_survival_scoring") != "1" ?
"SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore DESC, tertiaryScore DESC, last_played ASC;" :
"SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY secondaryscore DESC, primaryscore DESC, tertiaryScore DESC, last_played ASC;";
auto leaderboard = ExecuteSelect(query, activityId);
return ProcessQuery(leaderboard);
}
std::vector<ILeaderboard::Entry> MySQLDatabase::GetNsLeaderboard(const uint32_t activityId) {
auto leaderboard = ExecuteSelect("SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore ASC, tertiaryScore DESC, last_played ASC;", activityId);
return ProcessQuery(leaderboard);
}
void MySQLDatabase::SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) {
ExecuteInsert("INSERT leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, character_id = ?, game_id = ?;",
score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId);
}
void MySQLDatabase::UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) {
ExecuteInsert("UPDATE leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;",
score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId);
}
std::optional<ILeaderboard::Score> MySQLDatabase::GetPlayerScore(const uint32_t playerId, const uint32_t gameId) {
std::optional<ILeaderboard::Score> toReturn = std::nullopt;
auto res = ExecuteSelect("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;", playerId, gameId);
if (res->next()) {
toReturn = ILeaderboard::Score{
.primaryScore = res->getFloat("primaryScore"),
.secondaryScore = res->getFloat("secondaryScore"),
.tertiaryScore = res->getFloat("tertiaryScore")
};
}
return toReturn;
}
void MySQLDatabase::IncrementNumWins(const uint32_t playerId, const uint32_t gameId) {
ExecuteUpdate("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;", playerId, gameId);
}

View File

@@ -1,9 +0,0 @@
#include "MySQLDatabase.h"
void MySQLDatabase::InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) {
ExecuteInsert("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)", bigId, modules, characterId);
}
void MySQLDatabase::DeleteUgcBuild(const LWOOBJID bigId) {
ExecuteDelete("DELETE FROM ugc_modular_build WHERE ugc_id = ?;", bigId);
}

View File

@@ -91,16 +91,6 @@ class TestSQLDatabase : public GameDatabase {
void RemoveBehavior(const int32_t behaviorId) override; void RemoveBehavior(const int32_t behaviorId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override; void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override { return {}; }; std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override { return {}; };
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override { return {}; };
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override { return {}; };
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override { return {}; };
std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) override { return {}; };
void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override {};
void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override {};
std::optional<ILeaderboard::Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) override { return {}; };
void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) override {};
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override {};
void DeleteUgcBuild(const LWOOBJID bigId) override {};
}; };
#endif //!TESTSQLDATABASE_H #endif //!TESTSQLDATABASE_H

View File

@@ -34,7 +34,7 @@ Migration LoadMigration(std::string path) {
void MigrationRunner::RunMigrations() { void MigrationRunner::RunMigrations() {
Database::Get()->CreateMigrationHistoryTable(); Database::Get()->CreateMigrationHistoryTable();
std::string finalSQL = ""; sql::SQLString finalSQL = "";
bool runSd0Migrations = false; bool runSd0Migrations = false;
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/").string())) { for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/").string())) {
auto migration = LoadMigration("dlu/" + entry); auto migration = LoadMigration("dlu/" + entry);
@@ -61,12 +61,12 @@ void MigrationRunner::RunMigrations() {
} }
if (!finalSQL.empty()) { if (!finalSQL.empty()) {
auto migration = GeneralUtils::SplitString(finalSQL, ';'); auto migration = GeneralUtils::SplitString(static_cast<std::string>(finalSQL), ';');
for (auto& query : migration) { for (auto& query : migration) {
try { try {
if (query.empty()) continue; if (query.empty()) continue;
Database::Get()->ExecuteCustomQuery(query); Database::Get()->ExecuteCustomQuery(query.c_str());
} catch (std::exception& e) { } catch (sql::SQLException& e) {
LOG("Encountered error running migration: %s", e.what()); LOG("Encountered error running migration: %s", e.what());
} }
} }

View File

@@ -1351,11 +1351,6 @@ void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) {
callback(other); callback(other);
} }
SwitchComponent* switchComp = GetComponent<SwitchComponent>();
if (switchComp) {
switchComp->OnUse(other);
}
TriggerEvent(eTriggerEventType::ENTER, other); TriggerEvent(eTriggerEventType::ENTER, other);
// POI system // POI system

View File

@@ -1,6 +1,5 @@
#include "LeaderboardManager.h" #include "LeaderboardManager.h"
#include <ranges>
#include <sstream> #include <sstream>
#include <utility> #include <utility>
@@ -73,191 +72,197 @@ void Leaderboard::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write0(); bitStream.Write0();
} }
// Takes the resulting query from a leaderboard lookup and converts it to the LDF we need void Leaderboard::QueryToLdf(std::unique_ptr<sql::ResultSet>& rows) {
// to send it to a client. Clear();
void QueryToLdf(Leaderboard& leaderboard, const std::vector<ILeaderboard::Entry>& leaderboardEntries) { if (rows->rowsCount() == 0) return;
using enum Leaderboard::Type;
leaderboard.Clear();
if (leaderboardEntries.empty()) return;
for (const auto& leaderboardEntry : leaderboardEntries) { this->entries.reserve(rows->rowsCount());
while (rows->next()) {
constexpr int32_t MAX_NUM_DATA_PER_ROW = 9; constexpr int32_t MAX_NUM_DATA_PER_ROW = 9;
auto& entry = leaderboard.PushBackEntry(); this->entries.push_back(std::vector<LDFBaseData*>());
auto& entry = this->entries.back();
entry.reserve(MAX_NUM_DATA_PER_ROW); entry.reserve(MAX_NUM_DATA_PER_ROW);
entry.push_back(new LDFData<uint64_t>(u"CharacterID", leaderboardEntry.charId)); entry.push_back(new LDFData<uint64_t>(u"CharacterID", rows->getInt("character_id")));
entry.push_back(new LDFData<uint64_t>(u"LastPlayed", leaderboardEntry.lastPlayedTimestamp)); entry.push_back(new LDFData<uint64_t>(u"LastPlayed", rows->getUInt64("lastPlayed")));
entry.push_back(new LDFData<int32_t>(u"NumPlayed", leaderboardEntry.numTimesPlayed)); entry.push_back(new LDFData<int32_t>(u"NumPlayed", rows->getInt("timesPlayed")));
entry.push_back(new LDFData<std::u16string>(u"name", GeneralUtils::ASCIIToUTF16(leaderboardEntry.name))); entry.push_back(new LDFData<std::u16string>(u"name", GeneralUtils::ASCIIToUTF16(rows->getString("name").c_str())));
entry.push_back(new LDFData<uint64_t>(u"RowNumber", leaderboardEntry.ranking)); entry.push_back(new LDFData<uint64_t>(u"RowNumber", rows->getInt("ranking")));
switch (leaderboard.GetLeaderboardType()) { switch (leaderboardType) {
case ShootingGallery: case Type::ShootingGallery:
entry.push_back(new LDFData<int32_t>(u"Score", leaderboardEntry.primaryScore)); entry.push_back(new LDFData<int32_t>(u"Score", rows->getInt("primaryScore")));
// Score:1 // Score:1
entry.push_back(new LDFData<int32_t>(u"Streak", leaderboardEntry.secondaryScore)); entry.push_back(new LDFData<int32_t>(u"Streak", rows->getInt("secondaryScore")));
// Streak:1 // Streak:1
entry.push_back(new LDFData<float>(u"HitPercentage", (leaderboardEntry.tertiaryScore / 100.0f))); entry.push_back(new LDFData<float>(u"HitPercentage", (rows->getInt("tertiaryScore") / 100.0f)));
// HitPercentage:3 between 0 and 1 // HitPercentage:3 between 0 and 1
break; break;
case Racing: case Type::Racing:
entry.push_back(new LDFData<float>(u"BestTime", leaderboardEntry.primaryScore)); entry.push_back(new LDFData<float>(u"BestTime", rows->getDouble("primaryScore")));
// BestLapTime:3 // BestLapTime:3
entry.push_back(new LDFData<float>(u"BestLapTime", leaderboardEntry.secondaryScore)); entry.push_back(new LDFData<float>(u"BestLapTime", rows->getDouble("secondaryScore")));
// BestTime:3 // BestTime:3
entry.push_back(new LDFData<int32_t>(u"License", 1)); entry.push_back(new LDFData<int32_t>(u"License", 1));
// License:1 - 1 if player has completed mission 637 and 0 otherwise // License:1 - 1 if player has completed mission 637 and 0 otherwise
entry.push_back(new LDFData<int32_t>(u"NumWins", leaderboardEntry.numWins)); entry.push_back(new LDFData<int32_t>(u"NumWins", rows->getInt("numWins")));
// NumWins:1 // NumWins:1
break; break;
case UnusedLeaderboard4: case Type::UnusedLeaderboard4:
entry.push_back(new LDFData<int32_t>(u"Points", leaderboardEntry.primaryScore)); entry.push_back(new LDFData<int32_t>(u"Points", rows->getInt("primaryScore")));
// Points:1 // Points:1
break; break;
case MonumentRace: case Type::MonumentRace:
entry.push_back(new LDFData<int32_t>(u"Time", leaderboardEntry.primaryScore)); entry.push_back(new LDFData<int32_t>(u"Time", rows->getInt("primaryScore")));
// Time:1(?) // Time:1(?)
break; break;
case FootRace: case Type::FootRace:
entry.push_back(new LDFData<int32_t>(u"Time", leaderboardEntry.primaryScore)); entry.push_back(new LDFData<int32_t>(u"Time", rows->getInt("primaryScore")));
// Time:1 // Time:1
break; break;
case Survival: case Type::Survival:
entry.push_back(new LDFData<int32_t>(u"Points", leaderboardEntry.primaryScore)); entry.push_back(new LDFData<int32_t>(u"Points", rows->getInt("primaryScore")));
// Points:1 // Points:1
entry.push_back(new LDFData<int32_t>(u"Time", leaderboardEntry.secondaryScore)); entry.push_back(new LDFData<int32_t>(u"Time", rows->getInt("secondaryScore")));
// Time:1 // Time:1
break; break;
case SurvivalNS: case Type::SurvivalNS:
entry.push_back(new LDFData<int32_t>(u"Wave", leaderboardEntry.primaryScore)); entry.push_back(new LDFData<int32_t>(u"Wave", rows->getInt("primaryScore")));
// Wave:1 // Wave:1
entry.push_back(new LDFData<int32_t>(u"Time", leaderboardEntry.secondaryScore)); entry.push_back(new LDFData<int32_t>(u"Time", rows->getInt("secondaryScore")));
// Time:1 // Time:1
break; break;
case Donations: case Type::Donations:
entry.push_back(new LDFData<int32_t>(u"Score", leaderboardEntry.primaryScore)); entry.push_back(new LDFData<int32_t>(u"Score", rows->getInt("primaryScore")));
// Score:1 // Score:1
break; break;
case None: case Type::None:
[[fallthrough]]; // This type is included here simply to resolve a compiler warning on mac about unused enum types
break;
default: default:
break; break;
} }
} }
} }
std::vector<ILeaderboard::Entry> FilterTo10(const std::vector<ILeaderboard::Entry>& leaderboard, const uint32_t relatedPlayer, const Leaderboard::InfoType infoType) { const std::string_view Leaderboard::GetOrdering(Leaderboard::Type leaderboardType) {
std::vector<ILeaderboard::Entry> toReturn; // Use a switch case and return desc for all 3 columns if higher is better and asc if lower is better
int32_t index = 0;
// for friends and top, we dont need to find this players index.
if (infoType == Leaderboard::InfoType::MyStanding || infoType == Leaderboard::InfoType::Friends) {
for (; index < leaderboard.size(); index++) {
if (leaderboard[index].charId == relatedPlayer) break;
}
}
if (leaderboard.size() < 10) {
toReturn.assign(leaderboard.begin(), leaderboard.end());
index = 0;
} else if (index < 10) {
toReturn.assign(leaderboard.begin(), leaderboard.begin() + 10); // get the top 10 since we are in the top 10
index = 0;
} else if (index > leaderboard.size() - 10) {
toReturn.assign(leaderboard.end() - 10, leaderboard.end()); // get the bottom 10 since we are in the bottom 10
index = leaderboard.size() - 10;
} else {
toReturn.assign(leaderboard.begin() + index - 5, leaderboard.begin() + index + 5); // get the 5 above and below
index -= 5;
}
int32_t i = index;
for (auto& entry : toReturn) {
entry.ranking = ++i;
}
return toReturn;
}
std::vector<ILeaderboard::Entry> FilterWeeklies(const std::vector<ILeaderboard::Entry>& leaderboard) {
// Filter the leaderboard to only include entries from the last week
const auto currentTime = std::chrono::system_clock::now();
auto epochTime = currentTime.time_since_epoch().count();
constexpr auto SECONDS_IN_A_WEEK = 60 * 60 * 24 * 7; // if you think im taking leap seconds into account thats cute.
std::vector<ILeaderboard::Entry> weeklyLeaderboard;
for (const auto& entry : leaderboard) {
if (epochTime - entry.lastPlayedTimestamp < SECONDS_IN_A_WEEK) {
weeklyLeaderboard.push_back(entry);
}
}
return weeklyLeaderboard;
}
std::vector<ILeaderboard::Entry> FilterFriends(const std::vector<ILeaderboard::Entry>& leaderboard, const uint32_t relatedPlayer) {
// Filter the leaderboard to only include friends of the player
auto friendOfPlayer = Database::Get()->GetFriendsList(relatedPlayer);
std::vector<ILeaderboard::Entry> friendsLeaderboard;
for (const auto& entry : leaderboard) {
const auto res = std::ranges::find_if(friendOfPlayer, [&entry, relatedPlayer](const FriendData& data) {
return entry.charId == data.friendID || entry.charId == relatedPlayer;
});
if (res != friendOfPlayer.cend()) {
friendsLeaderboard.push_back(entry);
}
}
return friendsLeaderboard;
}
std::vector<ILeaderboard::Entry> ProcessLeaderboard(
const std::vector<ILeaderboard::Entry>& leaderboard,
const bool weekly,
const Leaderboard::InfoType infoType,
const uint32_t relatedPlayer) {
std::vector<ILeaderboard::Entry> toReturn;
if (infoType == Leaderboard::InfoType::Friends) {
const auto friendsLeaderboard = FilterFriends(leaderboard, relatedPlayer);
toReturn = FilterTo10(weekly ? FilterWeeklies(friendsLeaderboard) : friendsLeaderboard, relatedPlayer, infoType);
} else {
toReturn = FilterTo10(weekly ? FilterWeeklies(leaderboard) : leaderboard, relatedPlayer, infoType);
}
return toReturn;
}
void Leaderboard::SetupLeaderboard(bool weekly) {
const auto leaderboardType = LeaderboardManager::GetLeaderboardType(gameID);
std::vector<ILeaderboard::Entry> leaderboardRes;
switch (leaderboardType) { switch (leaderboardType) {
case Type::SurvivalNS:
leaderboardRes = Database::Get()->GetNsLeaderboard(gameID);
break;
case Type::Survival:
leaderboardRes = Database::Get()->GetAgsLeaderboard(gameID);
break;
case Type::Racing: case Type::Racing:
[[fallthrough]];
case Type::MonumentRace: case Type::MonumentRace:
leaderboardRes = Database::Get()->GetAscendingLeaderboard(gameID); return "primaryScore ASC, secondaryScore ASC, tertiaryScore ASC";
break; case Type::Survival:
return Game::config->GetValue("classic_survival_scoring") == "1" ?
"secondaryScore DESC, primaryScore DESC, tertiaryScore DESC" :
"primaryScore DESC, secondaryScore DESC, tertiaryScore DESC";
case Type::SurvivalNS:
return "primaryScore DESC, secondaryScore ASC, tertiaryScore DESC";
case Type::ShootingGallery: case Type::ShootingGallery:
[[fallthrough]];
case Type::FootRace: case Type::FootRace:
[[fallthrough]]; case Type::UnusedLeaderboard4:
case Type::Donations: case Type::Donations:
[[fallthrough]];
case Type::None: case Type::None:
[[fallthrough]];
default: default:
leaderboardRes = Database::Get()->GetDescendingLeaderboard(gameID); return "primaryScore DESC, secondaryScore DESC, tertiaryScore DESC";
break;
} }
}
const auto processedLeaderboard = ProcessLeaderboard(leaderboardRes, weekly, infoType, relatedPlayer); void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t resultEnd) {
resultStart++;
resultEnd++;
// We need everything except 1 column so i'm selecting * from leaderboard
const std::string queryBase =
R"QUERY(
WITH leaderboardsRanked AS (
SELECT leaderboard.*, charinfo.name,
RANK() OVER
(
ORDER BY %s, UNIX_TIMESTAMP(last_played) ASC, id DESC
) AS ranking
FROM leaderboard JOIN charinfo on charinfo.id = leaderboard.character_id
WHERE game_id = ? %s
),
myStanding AS (
SELECT
ranking as myRank
FROM leaderboardsRanked
WHERE id = ?
),
lowestRanking AS (
SELECT MAX(ranking) AS lowestRank
FROM leaderboardsRanked
)
SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
WHERE leaderboardsRanked.ranking
BETWEEN
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), CAST(lowestRanking.lowestRank AS SIGNED) - 9)
AND
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
ORDER BY ranking ASC;
)QUERY";
QueryToLdf(*this, processedLeaderboard); std::string friendsFilter =
R"QUERY(
AND (
character_id IN (
SELECT fr.requested_player FROM (
SELECT CASE
WHEN player_id = ? THEN friend_id
WHEN friend_id = ? THEN player_id
END AS requested_player
FROM friends
) AS fr
JOIN charinfo AS ci
ON ci.id = fr.requested_player
WHERE fr.requested_player IS NOT NULL
)
OR character_id = ?
)
)QUERY";
std::string weeklyFilter = " AND UNIX_TIMESTAMP(last_played) BETWEEN UNIX_TIMESTAMP(date_sub(now(),INTERVAL 1 WEEK)) AND UNIX_TIMESTAMP(now()) ";
std::string filter;
// Setup our filter based on the query type
if (this->infoType == InfoType::Friends) filter += friendsFilter;
if (this->weekly) filter += weeklyFilter;
const auto orderBase = GetOrdering(this->leaderboardType);
// For top query, we want to just rank all scores, but for all others we need the scores around a specific player
std::string baseLookup;
if (this->infoType == InfoType::Top) {
baseLookup = "SELECT id, last_played FROM leaderboard WHERE game_id = ? " + (this->weekly ? weeklyFilter : std::string("")) + " ORDER BY ";
baseLookup += orderBase.data();
} else {
baseLookup = "SELECT id, last_played FROM leaderboard WHERE game_id = ? " + (this->weekly ? weeklyFilter : std::string("")) + " AND character_id = ";
baseLookup += std::to_string(static_cast<uint32_t>(this->relatedPlayer));
}
baseLookup += " LIMIT 1";
LOG_DEBUG("query is %s", baseLookup.c_str());
std::unique_ptr<sql::PreparedStatement> baseQuery(Database::Get()->CreatePreppedStmt(baseLookup));
baseQuery->setInt(1, this->gameID);
std::unique_ptr<sql::ResultSet> baseResult(baseQuery->executeQuery());
if (!baseResult->next()) return; // In this case, there are no entries in the leaderboard for this game.
uint32_t relatedPlayerLeaderboardId = baseResult->getInt("id");
// Create and execute the actual save here. Using a heap allocated buffer to avoid stack overflow
constexpr uint16_t STRING_LENGTH = 4096;
std::unique_ptr<char[]> lookupBuffer = std::make_unique<char[]>(STRING_LENGTH);
int32_t res = snprintf(lookupBuffer.get(), STRING_LENGTH, queryBase.c_str(), orderBase.data(), filter.c_str(), resultStart, resultEnd);
DluAssert(res != -1);
std::unique_ptr<sql::PreparedStatement> query(Database::Get()->CreatePreppedStmt(lookupBuffer.get()));
LOG_DEBUG("Query is %s vars are %i %i %i", lookupBuffer.get(), this->gameID, this->relatedPlayer, relatedPlayerLeaderboardId);
query->setInt(1, this->gameID);
if (this->infoType == InfoType::Friends) {
query->setInt(2, this->relatedPlayer);
query->setInt(3, this->relatedPlayer);
query->setInt(4, this->relatedPlayer);
query->setInt(5, relatedPlayerLeaderboardId);
} else {
query->setInt(2, relatedPlayerLeaderboardId);
}
std::unique_ptr<sql::ResultSet> result(query->executeQuery());
QueryToLdf(result);
} }
void Leaderboard::Send(const LWOOBJID targetID) const { void Leaderboard::Send(const LWOOBJID targetID) const {
@@ -267,41 +272,129 @@ void Leaderboard::Send(const LWOOBJID targetID) const {
} }
} }
std::string FormatInsert(const Leaderboard::Type& type, const Score& score, const bool useUpdate) {
std::string insertStatement;
if (useUpdate) {
insertStatement =
R"QUERY(
UPDATE leaderboard
SET primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;
)QUERY";
} else {
insertStatement =
R"QUERY(
INSERT leaderboard SET
primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
character_id = ?, game_id = ?;
)QUERY";
}
constexpr uint16_t STRING_LENGTH = 400;
// Then fill in our score
char finishedQuery[STRING_LENGTH];
int32_t res = snprintf(finishedQuery, STRING_LENGTH, insertStatement.c_str(), score.GetPrimaryScore(), score.GetSecondaryScore(), score.GetTertiaryScore());
DluAssert(res != -1);
return finishedQuery;
}
void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) { void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) {
const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId); const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId);
const auto oldScore = Database::Get()->GetPlayerScore(playerID, activityId); std::unique_ptr<sql::PreparedStatement> query(Database::Get()->CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;"));
query->setInt(1, playerID);
query->setInt(2, activityId);
std::unique_ptr<sql::ResultSet> myScoreResult(query->executeQuery());
ILeaderboard::Score newScore{ .primaryScore = primaryScore, .secondaryScore = secondaryScore, .tertiaryScore = tertiaryScore }; std::string saveQuery("UPDATE leaderboard SET timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;");
if (oldScore.has_value()) { Score newScore(primaryScore, secondaryScore, tertiaryScore);
bool lowerScoreBetter = leaderboardType == Leaderboard::Type::Racing || leaderboardType == Leaderboard::Type::MonumentRace; if (myScoreResult->next()) {
Score oldScore;
bool lowerScoreBetter = false;
switch (leaderboardType) {
// Higher score better
case Leaderboard::Type::ShootingGallery: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
oldScore.SetSecondaryScore(myScoreResult->getInt("secondaryScore"));
oldScore.SetTertiaryScore(myScoreResult->getInt("tertiaryScore"));
break;
}
case Leaderboard::Type::FootRace: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
break;
}
case Leaderboard::Type::Survival: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
oldScore.SetSecondaryScore(myScoreResult->getInt("secondaryScore"));
break;
}
case Leaderboard::Type::SurvivalNS: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
oldScore.SetSecondaryScore(myScoreResult->getInt("secondaryScore"));
break;
}
case Leaderboard::Type::UnusedLeaderboard4:
case Leaderboard::Type::Donations: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
newScore.SetPrimaryScore(oldScore.GetPrimaryScore() + newScore.GetPrimaryScore());
break;
}
case Leaderboard::Type::Racing: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
oldScore.SetSecondaryScore(myScoreResult->getInt("secondaryScore"));
// For wins we dont care about the score, just the time, so zero out the tertiary.
// Wins are updated later.
oldScore.SetTertiaryScore(0);
newScore.SetTertiaryScore(0);
lowerScoreBetter = true;
break;
}
case Leaderboard::Type::MonumentRace: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
lowerScoreBetter = true;
// Do score checking here
break;
}
case Leaderboard::Type::None:
default:
LOG("Unknown leaderboard type %i for game %i. Cannot save score!", leaderboardType, activityId);
return;
}
bool newHighScore = lowerScoreBetter ? newScore < oldScore : newScore > oldScore; bool newHighScore = lowerScoreBetter ? newScore < oldScore : newScore > oldScore;
// Nimbus station has a weird leaderboard where we need a custom scoring system // Nimbus station has a weird leaderboard where we need a custom scoring system
if (leaderboardType == Leaderboard::Type::SurvivalNS) { if (leaderboardType == Leaderboard::Type::SurvivalNS) {
newHighScore = newScore.primaryScore > oldScore->primaryScore || newHighScore = newScore.GetPrimaryScore() > oldScore.GetPrimaryScore() ||
(newScore.primaryScore == oldScore->primaryScore && newScore.secondaryScore < oldScore->secondaryScore); (newScore.GetPrimaryScore() == oldScore.GetPrimaryScore() && newScore.GetSecondaryScore() < oldScore.GetSecondaryScore());
} else if (leaderboardType == Leaderboard::Type::Survival && Game::config->GetValue("classic_survival_scoring") == "1") { } else if (leaderboardType == Leaderboard::Type::Survival && Game::config->GetValue("classic_survival_scoring") == "1") {
ILeaderboard::Score oldScoreFlipped{oldScore->secondaryScore, oldScore->primaryScore, oldScore->tertiaryScore}; Score oldScoreFlipped(oldScore.GetSecondaryScore(), oldScore.GetPrimaryScore());
ILeaderboard::Score newScoreFlipped{newScore.secondaryScore, newScore.primaryScore, newScore.tertiaryScore}; Score newScoreFlipped(newScore.GetSecondaryScore(), newScore.GetPrimaryScore());
newHighScore = newScoreFlipped > oldScoreFlipped; newHighScore = newScoreFlipped > oldScoreFlipped;
} }
if (newHighScore) { if (newHighScore) {
Database::Get()->UpdateScore(playerID, activityId, newScore); saveQuery = FormatInsert(leaderboardType, newScore, true);
} }
} else { } else {
Database::Get()->SaveScore(playerID, activityId, newScore); saveQuery = FormatInsert(leaderboardType, newScore, false);
} }
LOG("save query %s %i %i", saveQuery.c_str(), playerID, activityId);
std::unique_ptr<sql::PreparedStatement> saveStatement(Database::Get()->CreatePreppedStmt(saveQuery));
saveStatement->setInt(1, playerID);
saveStatement->setInt(2, activityId);
saveStatement->execute();
// track wins separately // track wins separately
if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) { if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) {
Database::Get()->IncrementNumWins(playerID, activityId); std::unique_ptr<sql::PreparedStatement> winUpdate(Database::Get()->CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;"));
winUpdate->setInt(1, playerID);
winUpdate->setInt(2, activityId);
winUpdate->execute();
} }
} }
void LeaderboardManager::SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID) { void LeaderboardManager::SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID, const uint32_t resultStart, const uint32_t resultEnd) {
Leaderboard leaderboard(gameID, infoType, weekly, playerID, GetLeaderboardType(gameID)); Leaderboard leaderboard(gameID, infoType, weekly, playerID, GetLeaderboardType(gameID));
leaderboard.SetupLeaderboard(weekly); leaderboard.SetupLeaderboard(weekly, resultStart, resultEnd);
leaderboard.Send(targetID); leaderboard.Send(targetID);
} }

View File

@@ -9,10 +9,46 @@
#include "dCommonVars.h" #include "dCommonVars.h"
#include "LDFFormat.h" #include "LDFFormat.h"
namespace sql {
class ResultSet;
};
namespace RakNet { namespace RakNet {
class BitStream; class BitStream;
}; };
class Score {
public:
Score() {
primaryScore = 0;
secondaryScore = 0;
tertiaryScore = 0;
}
Score(const float primaryScore, const float secondaryScore = 0, const float tertiaryScore = 0) {
this->primaryScore = primaryScore;
this->secondaryScore = secondaryScore;
this->tertiaryScore = tertiaryScore;
}
bool operator<(const Score& rhs) const {
return primaryScore < rhs.primaryScore || (primaryScore == rhs.primaryScore && secondaryScore < rhs.secondaryScore) || (primaryScore == rhs.primaryScore && secondaryScore == rhs.secondaryScore && tertiaryScore < rhs.tertiaryScore);
}
bool operator>(const Score& rhs) const {
return primaryScore > rhs.primaryScore || (primaryScore == rhs.primaryScore && secondaryScore > rhs.secondaryScore) || (primaryScore == rhs.primaryScore && secondaryScore == rhs.secondaryScore && tertiaryScore > rhs.tertiaryScore);
}
void SetPrimaryScore(const float score) { primaryScore = score; }
float GetPrimaryScore() const { return primaryScore; }
void SetSecondaryScore(const float score) { secondaryScore = score; }
float GetSecondaryScore() const { return secondaryScore; }
void SetTertiaryScore(const float score) { tertiaryScore = score; }
float GetTertiaryScore() const { return tertiaryScore; }
private:
float primaryScore;
float secondaryScore;
float tertiaryScore;
};
using GameID = uint32_t; using GameID = uint32_t;
class Leaderboard { class Leaderboard {
@@ -43,7 +79,7 @@ public:
/** /**
* @brief Resets the leaderboard state and frees its allocated memory * @brief Resets the leaderboard state and frees its allocated memory
* *
*/ */
void Clear(); void Clear();
@@ -60,16 +96,20 @@ public:
* @param resultStart The index to start the leaderboard at. Zero indexed. * @param resultStart The index to start the leaderboard at. Zero indexed.
* @param resultEnd The index to end the leaderboard at. Zero indexed. * @param resultEnd The index to end the leaderboard at. Zero indexed.
*/ */
void SetupLeaderboard(bool weekly); void SetupLeaderboard(bool weekly, uint32_t resultStart = 0, uint32_t resultEnd = 10);
/** /**
* Sends the leaderboard to the client specified by targetID. * Sends the leaderboard to the client specified by targetID.
*/ */
void Send(const LWOOBJID targetID) const; void Send(const LWOOBJID targetID) const;
// Helper function to get the columns, ordering and insert format for a leaderboard
static const std::string_view GetOrdering(Type leaderboardType);
private: private:
// Takes the resulting query from a leaderboard lookup and converts it to the LDF we need
// to send it to a client.
void QueryToLdf(std::unique_ptr<sql::ResultSet>& rows);
using LeaderboardEntry = std::vector<LDFBaseData*>; using LeaderboardEntry = std::vector<LDFBaseData*>;
using LeaderboardEntries = std::vector<LeaderboardEntry>; using LeaderboardEntries = std::vector<LeaderboardEntry>;
@@ -79,18 +119,10 @@ private:
InfoType infoType; InfoType infoType;
Leaderboard::Type leaderboardType; Leaderboard::Type leaderboardType;
bool weekly; bool weekly;
public:
LeaderboardEntry& PushBackEntry() {
return entries.emplace_back();
}
Type GetLeaderboardType() const {
return leaderboardType;
}
}; };
namespace LeaderboardManager { namespace LeaderboardManager {
void SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID); void SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID, const uint32_t resultStart = 0, const uint32_t resultEnd = 10);
void SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore = 0, const float tertiaryScore = 0); void SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore = 0, const float tertiaryScore = 0);

View File

@@ -82,6 +82,7 @@ void SwitchComponent::EntityEnter(Entity* entity) {
RenderComponent::PlayAnimation(m_Parent, u"engaged"); RenderComponent::PlayAnimation(m_Parent, u"engaged");
m_PetBouncer->SetPetBouncerEnabled(true); m_PetBouncer->SetPetBouncerEnabled(true);
} else { } else {
GameMessages::SendKnockback(entity->GetObjectID(), m_Parent->GetObjectID(), m_Parent->GetObjectID(), 0.0f, NiPoint3(0.0f, 17.0f, 0.0f));
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }

View File

@@ -1691,7 +1691,7 @@ void GameMessages::HandleRequestActivitySummaryLeaderboardData(RakNet::BitStream
bool weekly = inStream.ReadBit(); bool weekly = inStream.ReadBit();
LeaderboardManager::SendLeaderboard(gameID, queryType, weekly, entity->GetObjectID(), entity->GetObjectID()); LeaderboardManager::SendLeaderboard(gameID, queryType, weekly, entity->GetObjectID(), entity->GetObjectID(), resultsStart, resultsEnd);
} }
void GameMessages::HandleActivityStateChangeRequest(RakNet::BitStream& inStream, Entity* entity) { void GameMessages::HandleActivityStateChangeRequest(RakNet::BitStream& inStream, Entity* entity) {
@@ -5066,7 +5066,9 @@ void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream& inStream, E
item->Disassemble(TEMP_MODELS); item->Disassemble(TEMP_MODELS);
Database::Get()->DeleteUgcBuild(item->GetSubKey()); std::unique_ptr<sql::PreparedStatement> stmt(Database::Get()->CreatePreppedStmt("DELETE FROM ugc_modular_build where ugc_id = ?"));
stmt->setUInt64(1, item->GetSubKey());
stmt->execute();
item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::QUICKBUILD); item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::QUICKBUILD);
} }
@@ -5392,8 +5394,6 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, En
const auto itemType = static_cast<eItemType>(item->GetInfo().itemType); const auto itemType = static_cast<eItemType>(item->GetInfo().itemType);
if (itemType == eItemType::MODEL || itemType == eItemType::LOOT_MODEL) { if (itemType == eItemType::MODEL || itemType == eItemType::LOOT_MODEL) {
item->DisassembleModel(iStackCount); item->DisassembleModel(iStackCount);
} else if (itemType == eItemType::VEHICLE) {
Database::Get()->DeleteUgcBuild(item->GetSubKey());
} }
auto lot = item->GetLot(); auto lot = item->GetLot();
item->SetCount(item->GetCount() - iStackCount, true); item->SetCount(item->GetCount() - iStackCount, true);
@@ -5569,8 +5569,12 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream& inStream, Entity*
inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig); inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
} }
std::unique_ptr<sql::PreparedStatement> stmt(Database::Get()->CreatePreppedStmt("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)"));
stmt->setUInt64(1, newIdBig);
stmt->setString(2, GeneralUtils::UTF16ToWTF8(modules).c_str());
auto* pCharacter = character->GetCharacter(); auto* pCharacter = character->GetCharacter();
Database::Get()->InsertUgcBuild(GeneralUtils::UTF16ToWTF8(modules), newIdBig, pCharacter ? std::optional(character->GetCharacter()->GetID()) : std::nullopt); pCharacter ? stmt->setUInt(3, pCharacter->GetID()) : stmt->setNull(3, sql::DataType::BIGINT);
stmt->execute();
auto* missionComponent = character->GetComponent<MissionComponent>(); auto* missionComponent = character->GetComponent<MissionComponent>();

View File

@@ -103,7 +103,7 @@ int main(int argc, char** argv) {
//Connect to the MySQL Database //Connect to the MySQL Database
try { try {
Database::Connect(); Database::Connect();
} catch (std::exception& ex) { } catch (sql::SQLException& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Got an error while connecting to the database: %s", ex.what());
LOG("Migrations not run"); LOG("Migrations not run");
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -264,7 +264,7 @@ int main(int argc, char** argv) {
//Create account //Create account
try { try {
Database::Get()->InsertNewAccount(username, std::string(hash, BCRYPT_HASHSIZE)); Database::Get()->InsertNewAccount(username, std::string(hash, BCRYPT_HASHSIZE));
} catch (std::exception& e) { } catch (sql::SQLException& e) {
LOG("A SQL error occurred!:\n %s", e.what()); LOG("A SQL error occurred!:\n %s", e.what());
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@@ -24,9 +24,9 @@ void PersistentIDManager::Initialize() {
LOG("Invalid persistent object ID in database. Aborting to prevent bad id generation."); LOG("Invalid persistent object ID in database. Aborting to prevent bad id generation.");
throw std::runtime_error("Invalid persistent object ID in database. Aborting to prevent bad id generation."); throw std::runtime_error("Invalid persistent object ID in database. Aborting to prevent bad id generation.");
} }
} catch (std::exception& e) { } catch (sql::SQLException& e) {
LOG("Unable to fetch max persistent object ID in use. This will cause issues. Aborting to prevent collisions."); LOG("Unable to fetch max persistent object ID in use. This will cause issues. Aborting to prevent collisions.");
LOG("Error: %s", e.what()); LOG("SQL error: %s", e.what());
throw e; throw e;
} }
} }

View File

@@ -121,7 +121,7 @@ void ActivityManager::GetLeaderboardData(Entity* self, const LWOOBJID playerID,
auto* sac = self->GetComponent<ScriptedActivityComponent>(); auto* sac = self->GetComponent<ScriptedActivityComponent>();
uint32_t gameID = sac != nullptr ? sac->GetActivityID() : self->GetLOT(); uint32_t gameID = sac != nullptr ? sac->GetActivityID() : self->GetLOT();
// Save the new score to the leaderboard and show the leaderboard to the player // Save the new score to the leaderboard and show the leaderboard to the player
LeaderboardManager::SendLeaderboard(activityID, Leaderboard::InfoType::MyStanding, false, playerID, self->GetObjectID()); LeaderboardManager::SendLeaderboard(activityID, Leaderboard::InfoType::MyStanding, false, playerID, self->GetObjectID(), 0, numResults);
} }
void ActivityManager::ActivityTimerStart(Entity* self, const std::string& timerName, const float_t updateInterval, void ActivityManager::ActivityTimerStart(Entity* self, const std::string& timerName, const float_t updateInterval,

View File

@@ -194,7 +194,7 @@ int main(int argc, char** argv) {
//Connect to the MySQL Database: //Connect to the MySQL Database:
try { try {
Database::Connect(); Database::Connect();
} catch (std::exception& ex) { } catch (sql::SQLException& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Got an error while connecting to the database: %s", ex.what());
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@@ -4,6 +4,15 @@ enable_testing()
find_package(GoogleTest REQUIRED) find_package(GoogleTest REQUIRED)
include(GoogleTest) include(GoogleTest)
if(APPLE)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH True)
set(CMAKE_BUILD_WITH_INSTALL_RPATH True)
set(CMAKE_INSTALL_RPATH "@executable_path")
endif()
add_custom_target(conncpp_tests
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# Add the subdirectories # Add the subdirectories
add_subdirectory(dCommonTests) add_subdirectory(dCommonTests)
add_subdirectory(dGameTests) add_subdirectory(dGameTests)

View File

@@ -17,13 +17,18 @@ list(APPEND DCOMMONTEST_SOURCES ${DENUMS_TESTS})
# Set our executable # Set our executable
add_executable(dCommonTests ${DCOMMONTEST_SOURCES}) add_executable(dCommonTests ${DCOMMONTEST_SOURCES})
add_dependencies(dCommonTests conncpp_tests)
# Needs to be in binary dir for ctest # Apple needs some special linkage for the mariadb connector for tests.
if(APPLE) if(APPLE)
add_custom_target(dCommonTestsLink add_custom_command(TARGET dCommonTests POST_BUILD
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${CMAKE_CURRENT_BINARY_DIR}) COMMAND otool ARGS -l dCommonTests
COMMAND otool ARGS -L dCommonTests
add_dependencies(dCommonTests dCommonTestsLink) COMMAND ls
COMMAND otool ARGS -D libmariadbcpp.dylib
COMMAND install_name_tool ARGS -change libmariadbcpp.dylib @rpath/libmariadbcpp.dylib dCommonTests
COMMAND otool ARGS -L dCommonTests
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif() endif()
# Link needed libraries # Link needed libraries

View File

@@ -13,12 +13,14 @@ file(COPY ${COMPONENT_TEST_DATA} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
# Add the executable. Remember to add all tests above this! # Add the executable. Remember to add all tests above this!
add_executable(dGameTests ${DGAMETEST_SOURCES}) add_executable(dGameTests ${DGAMETEST_SOURCES})
add_dependencies(dGameTests conncpp_tests)
# Apple needs some special linkage for the mariadb connector for tests.
if(APPLE) if(APPLE)
add_custom_target(dGameTestsLink add_custom_command(TARGET dGameTests POST_BUILD
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${CMAKE_CURRENT_BINARY_DIR}) COMMAND install_name_tool ARGS -change libmariadbcpp.dylib @rpath/libmariadbcpp.dylib dGameTests
COMMAND otool ARGS -L dGameTests
add_dependencies(dGameTests dGameTestsLink) WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif() endif()
target_link_libraries(dGameTests ${COMMON_LIBRARIES} GTest::gtest_main target_link_libraries(dGameTests ${COMMON_LIBRARIES} GTest::gtest_main

143161
thirdparty/SQLite/sqlite3.c vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff