mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-17 12:04:27 -06:00
Compare commits
22 Commits
windows-cl
...
MM12-Testi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05dc5d6fcd | ||
|
|
c24c756f73 | ||
|
|
bdb380aed2 | ||
|
|
2baef3f198 | ||
|
|
fbf7833b7b | ||
|
|
05c67fd712 | ||
|
|
9d4d618e63 | ||
|
|
6aa7c592a2 | ||
|
|
68187d9f96 | ||
|
|
716e8c646c | ||
|
|
d2cecd0073 | ||
|
|
d97f374a90 | ||
|
|
a6c6474e67 | ||
|
|
dbdf4ac46a | ||
|
|
6ed504c88e | ||
|
|
9be2ab03b6 | ||
|
|
e53e31021f | ||
|
|
41b8762c8f | ||
|
|
da07a70d4d | ||
|
|
d78b2958d3 | ||
|
|
9cfc126842 | ||
|
|
56e3d07669 |
37
.clang-format
Normal file
37
.clang-format
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
BeforeLambdaBody: false
|
||||||
|
BeforeWhile: false
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
ColumnLimit: 0
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '<[[:alnum:].]+\.h>'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '<[[:alnum:].]+>'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '.*/.*'
|
||||||
|
Priority: 3
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 4
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
PointerAlignment: Left
|
||||||
|
...
|
||||||
17
.clang-tidy
Normal file
17
.clang-tidy
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
Checks: '-*,readability-*,performance-*,modernize-*,-modernize-use-trailing-return-type,bugprone-*'
|
||||||
|
WarningsAsErrors: true
|
||||||
|
HeaderFilterRegex: ''
|
||||||
|
FormatStyle: none
|
||||||
|
CheckOptions:
|
||||||
|
- key: readability-identifier-naming.ClassCase
|
||||||
|
value: CamelCase
|
||||||
|
- key: readability-identifier-naming.ClassMethodCase
|
||||||
|
value: CamelCase
|
||||||
|
- key: readability-identifier-naming.ClassMemberPrefix
|
||||||
|
value: m_
|
||||||
|
- key: readability-identifier-naming.ClassMemberCase
|
||||||
|
value: CamelCase
|
||||||
|
- key: readability-identifier-naming.ClassConstantCase
|
||||||
|
value: UPPER_CASE
|
||||||
|
- key: readability-identifier-naming.FunctionCase
|
||||||
|
value: CamelCase
|
||||||
@@ -3,8 +3,8 @@ Dockerfile
|
|||||||
*.md
|
*.md
|
||||||
logo.png
|
logo.png
|
||||||
versions.txt
|
versions.txt
|
||||||
|
build.sh
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
.env
|
.env
|
||||||
docker/__pycache__
|
docker/__pycache__
|
||||||
.env.example
|
.env.example
|
||||||
build
|
|
||||||
@@ -73,4 +73,4 @@ cpp_space_around_assignment_operator=insert
|
|||||||
cpp_space_pointer_reference_alignment=left
|
cpp_space_pointer_reference_alignment=left
|
||||||
cpp_space_around_ternary_operator=insert
|
cpp_space_around_ternary_operator=insert
|
||||||
cpp_wrap_preserve_blocks=one_liners
|
cpp_wrap_preserve_blocks=one_liners
|
||||||
cpp_indent_comment=false
|
cpp_indent_comment=fasle
|
||||||
|
|||||||
11
.env.example
11
.env.example
@@ -1,7 +1,9 @@
|
|||||||
# Full path to the LEGO Universe client
|
# Full path to the LEGO Universe client
|
||||||
CLIENT_PATH=./client
|
CLIENT_PATH=/Users/someuser/LEGO Universe
|
||||||
|
# Can improve build time
|
||||||
|
BUILD_THREADS=1
|
||||||
# Updates NET_VERSION in CMakeVariables.txt
|
# Updates NET_VERSION in CMakeVariables.txt
|
||||||
NET_VERSION=171022
|
BUILD_VERSION=171022
|
||||||
# make sure this is a long random string
|
# make sure this is a long random string
|
||||||
# grab a "SHA 256-bit Key" from here: https://keygen.io/
|
# grab a "SHA 256-bit Key" from here: https://keygen.io/
|
||||||
ACCOUNT_MANAGER_SECRET=
|
ACCOUNT_MANAGER_SECRET=
|
||||||
@@ -10,5 +12,6 @@ EXTERNAL_IP=localhost
|
|||||||
# Database values
|
# Database values
|
||||||
# Be careful with special characters here. It is more safe to use normal characters and/or numbers.
|
# Be careful with special characters here. It is more safe to use normal characters and/or numbers.
|
||||||
MARIADB_USER=darkflame
|
MARIADB_USER=darkflame
|
||||||
MARIADB_PASSWORD=
|
MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME
|
||||||
MARIADB_DATABASE=darkflame
|
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME
|
||||||
|
MARIADB_DATABASE=darkflame
|
||||||
@@ -6,6 +6,3 @@
|
|||||||
|
|
||||||
# convert to unix line endings
|
# convert to unix line endings
|
||||||
72477e01e2711e0f61cdb192ee266e5e21b8846f
|
72477e01e2711e0f61cdb192ee266e5e21b8846f
|
||||||
|
|
||||||
# enum cleanup
|
|
||||||
faf42d2f8cf432df2993b031f079b0b8c6d7dbe7
|
|
||||||
|
|||||||
56
.github/workflows/build-and-push-docker.yml
vendored
56
.github/workflows/build-and-push-docker.yml
vendored
@@ -1,56 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- "main"
|
|
||||||
tags:
|
|
||||||
- "v*.*.*"
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- "main"
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ghcr.io
|
|
||||||
IMAGE_NAME: ${{ github.repository }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
||||||
# generate Docker tags based on the following events/attributes
|
|
||||||
tags: |
|
|
||||||
type=ref,event=pr
|
|
||||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
46
.github/workflows/build-and-test.yml
vendored
46
.github/workflows/build-and-test.yml
vendored
@@ -13,39 +13,45 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ windows-2022, ubuntu-22.04, macos-13 ]
|
os: [ windows-2022, ubuntu-20.04, macos-11 ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Add msbuild to PATH (Windows only)
|
- name: Add msbuild to PATH (Windows only)
|
||||||
if: ${{ matrix.os == 'windows-2022' }}
|
if: ${{ matrix.os == 'windows-2022' }}
|
||||||
uses: microsoft/setup-msbuild@v2
|
uses: microsoft/setup-msbuild@v1.1
|
||||||
with:
|
with:
|
||||||
vs-version: '[17,18)'
|
vs-version: '[17,18)'
|
||||||
msbuild-architecture: x64
|
msbuild-architecture: x64
|
||||||
- name: Install libssl and switch to XCode 15.2 (Mac Only)
|
- name: Install libssl (Mac Only)
|
||||||
if: ${{ matrix.os == 'macos-13' }}
|
if: ${{ matrix.os == 'macos-11' }}
|
||||||
run: |
|
run: brew install openssl@3
|
||||||
brew install openssl@3
|
|
||||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
|
||||||
- name: cmake
|
- name: cmake
|
||||||
uses: lukka/run-cmake@v10
|
uses: lukka/run-cmake@v10
|
||||||
with:
|
with:
|
||||||
workflowPreset: "ci-${{matrix.os}}"
|
configurePreset: "ci-${{matrix.os}}"
|
||||||
|
buildPreset: "ci-${{matrix.os}}"
|
||||||
|
testPreset: "ci-${{matrix.os}}"
|
||||||
- name: artifacts
|
- name: artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
|
if: ${{ github.ref == 'ref/head/main' }}
|
||||||
with:
|
with:
|
||||||
name: build-${{matrix.os}}
|
name: build-${{matrix.os}}
|
||||||
path: |
|
path: |
|
||||||
build/*/*Server*
|
build
|
||||||
build/*/*.ini
|
!build/tests
|
||||||
build/*/*.so
|
!build/Testing
|
||||||
build/*/*.dll
|
!build/CMakeFiles
|
||||||
build/*/vanity/
|
!build/DartConfiguration.tcl
|
||||||
build/*/navmeshes/
|
!build/CTestTestfile.cmake
|
||||||
build/*/migrations/
|
!build/CMakeCache.txt
|
||||||
build/*/*.dcf
|
!build/build.ninja
|
||||||
!build/*/*.pdb
|
!build/_deps
|
||||||
!build/*/d*/
|
!build/cmake_install.cmake
|
||||||
|
!build/*.a
|
||||||
|
!build/*.lib
|
||||||
|
!build/*.dir
|
||||||
|
!build/*.vcxproj
|
||||||
|
!build/*.vcxproj.filters
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -112,8 +112,6 @@ CMakeFiles/TargetDirectories.txt
|
|||||||
*.sln
|
*.sln
|
||||||
*.recipe
|
*.recipe
|
||||||
|
|
||||||
CMakeUserPresets.json
|
|
||||||
|
|
||||||
# clangd
|
# clangd
|
||||||
.cache
|
.cache
|
||||||
thirdparty/zlib-1.2.11/
|
thirdparty/zlib-1.2.11/
|
||||||
@@ -124,7 +122,3 @@ docker/__pycache__
|
|||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
|
||||||
!*Test.bin
|
!*Test.bin
|
||||||
|
|
||||||
# CMake scripts
|
|
||||||
!cmake/*
|
|
||||||
!cmake/toolchains/*
|
|
||||||
|
|||||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -14,6 +14,6 @@
|
|||||||
path = thirdparty/mariadb-connector-cpp
|
path = thirdparty/mariadb-connector-cpp
|
||||||
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
|
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
|
||||||
ignore = dirty
|
ignore = dirty
|
||||||
[submodule "thirdparty/magic_enum"]
|
[submodule "thirdparty/AccountManager"]
|
||||||
path = thirdparty/magic_enum
|
path = thirdparty/AccountManager
|
||||||
url = https://github.com/Neargye/magic_enum.git
|
url = https://github.com/DarkflameUniverse/AccountManager
|
||||||
|
|||||||
374
CMakeLists.txt
374
CMakeLists.txt
@@ -1,27 +1,8 @@
|
|||||||
cmake_minimum_required(VERSION 3.25)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
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 " ")
|
|
||||||
message(FATAL_ERROR "The server cannot build in the path (" ${CMAKE_SOURCE_DIR} ") because it contains a space. Please move the server to a path without spaces.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set (CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
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
|
|
||||||
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)
|
||||||
@@ -33,26 +14,30 @@ string(REPLACE "\n" ";" variables ${variables})
|
|||||||
foreach(variable ${variables})
|
foreach(variable ${variables})
|
||||||
# If the string contains a #, skip it
|
# If the string contains a #, skip it
|
||||||
if(NOT "${variable}" MATCHES "#")
|
if(NOT "${variable}" MATCHES "#")
|
||||||
|
|
||||||
# Split the variable into name and value
|
# Split the variable into name and value
|
||||||
string(REPLACE "=" ";" variable ${variable})
|
string(REPLACE "=" ";" variable ${variable})
|
||||||
|
|
||||||
# Check that the length of the variable is 2 (name and value)
|
# Check that the length of the variable is 2 (name and value)
|
||||||
list(LENGTH variable length)
|
list(LENGTH variable length)
|
||||||
|
|
||||||
if(${length} EQUAL 2)
|
if(${length} EQUAL 2)
|
||||||
|
|
||||||
list(GET variable 0 variable_name)
|
list(GET variable 0 variable_name)
|
||||||
list(GET variable 1 variable_value)
|
list(GET variable 1 variable_value)
|
||||||
|
|
||||||
# Set the variable
|
# Set the variable
|
||||||
set(${variable_name} ${variable_value})
|
set(${variable_name} ${variable_value})
|
||||||
|
|
||||||
|
# Add compiler definition
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}")
|
||||||
|
|
||||||
message(STATUS "Variable: ${variable_name} = ${variable_value}")
|
message(STATUS "Variable: ${variable_name} = ${variable_value}")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# Set the version
|
# Set the version
|
||||||
set(PROJECT_VERSION "\"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"")
|
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||||
|
|
||||||
# Echo the version
|
# Echo the version
|
||||||
message(STATUS "Version: ${PROJECT_VERSION}")
|
message(STATUS "Version: ${PROJECT_VERSION}")
|
||||||
@@ -68,38 +53,31 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
|
|||||||
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
|
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
|
||||||
# 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.
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
add_compile_options("-fPIC")
|
if(APPLE)
|
||||||
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
|
||||||
|
else()
|
||||||
# For all except Clang and Apple Clang
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC -lstdc++fs")
|
||||||
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
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")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
|
||||||
add_link_options("-export-dynamic")
|
|
||||||
endif()
|
endif()
|
||||||
|
if (__ggdb)
|
||||||
if(${GGDB})
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
|
||||||
add_compile_options("-ggdb")
|
|
||||||
endif()
|
endif()
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
|
||||||
elseif(MSVC)
|
elseif(MSVC)
|
||||||
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
|
# 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")
|
||||||
add_compile_options("/wd4267" "/utf-8" "/volatile:iso" "/Zc:inline")
|
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Set the output directories
|
# Our output dir
|
||||||
# ./build/<platform + architecture (x64, x86, aarch64)>/<compiler>/<build_mode>/
|
set(CMAKE_BINARY_DIR ${PROJECT_BINARY_DIR})
|
||||||
|
|
||||||
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_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_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||||
@@ -111,17 +89,6 @@ 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)
|
|
||||||
|
|
||||||
# Create a /resServer directory
|
# Create a /resServer directory
|
||||||
make_directory(${CMAKE_BINARY_DIR}/resServer)
|
make_directory(${CMAKE_BINARY_DIR}/resServer)
|
||||||
|
|
||||||
@@ -129,91 +96,30 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
|
|||||||
make_directory(${CMAKE_BINARY_DIR}/logs)
|
make_directory(${CMAKE_BINARY_DIR}/logs)
|
||||||
|
|
||||||
# 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" "blacklist.dcf")
|
||||||
message(STATUS "Checking resource file integrity")
|
|
||||||
|
|
||||||
|
|
||||||
include(Utils)
|
|
||||||
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})
|
foreach(resource_file ${RESOURCE_FILES})
|
||||||
set(file_size 0)
|
if (NOT EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||||
|
|
||||||
if(EXISTS ${DLU_CONFIG_DIR}/${resource_file})
|
|
||||||
file(SIZE ${DLU_CONFIG_DIR}/${resource_file} file_size)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(${file_size} EQUAL 0)
|
|
||||||
configure_file(
|
configure_file(
|
||||||
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${DLU_CONFIG_DIR}/${resource_file}
|
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
|
||||||
COPYONLY
|
COPYONLY
|
||||||
)
|
)
|
||||||
message(STATUS "Moved " ${resource_file} " to DLU config directory")
|
message("Moved ${resource_file} to project binary directory")
|
||||||
elseif(resource_file MATCHES ".ini")
|
|
||||||
message(STATUS "Checking " ${resource_file} " for missing config options")
|
|
||||||
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 "")
|
|
||||||
|
|
||||||
# Remove comment lines so they do not interfere with the variable parsing
|
|
||||||
foreach(line ${current_file_contents})
|
|
||||||
string(FIND ${line} "#" is_comment)
|
|
||||||
|
|
||||||
if(NOT ${is_comment} EQUAL 0)
|
|
||||||
string(APPEND parsed_current_file_contents ${line})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
file(READ ${CMAKE_SOURCE_DIR}/resources/${resource_file} depot_file_contents)
|
|
||||||
string(REPLACE "\\\n" "" depot_file_contents ${depot_file_contents})
|
|
||||||
string(REPLACE "\n" ";" depot_file_contents ${depot_file_contents})
|
|
||||||
set(line_to_add "")
|
|
||||||
|
|
||||||
foreach(line ${depot_file_contents})
|
|
||||||
string(FIND ${line} "#" is_comment)
|
|
||||||
|
|
||||||
if(NOT ${is_comment} EQUAL 0)
|
|
||||||
string(REPLACE "=" ";" line_split ${line})
|
|
||||||
list(GET line_split 0 variable_name)
|
|
||||||
|
|
||||||
if(NOT ${parsed_current_file_contents} MATCHES ${variable_name})
|
|
||||||
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file})
|
|
||||||
set(line_to_add ${line_to_add} ${line})
|
|
||||||
|
|
||||||
foreach(line_to_append ${line_to_add})
|
|
||||||
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n" ${line_to_append})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(line_to_add "")
|
|
||||||
else()
|
|
||||||
set(line_to_add ${line_to_add} ${line})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
message(STATUS "Resource file integrity check complete")
|
# Copy navmesh data on first build and extract it
|
||||||
|
if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes/)
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
|
||||||
# if navmeshes directory does not exist, create it
|
file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip)
|
||||||
if(NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes)
|
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
|
||||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/navmeshes)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Copy navmesh data on first build and extract it
|
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip COPYONLY)
|
|
||||||
|
|
||||||
file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip DESTINATION ${PROJECT_BINARY_DIR}/navmeshes)
|
|
||||||
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
|
|
||||||
|
|
||||||
# Copy vanity files on first build
|
# Copy vanity files on first build
|
||||||
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "root.xml" "dev-tribute.xml" "atm.xml" "demo.xml")
|
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
|
||||||
|
|
||||||
foreach(file ${VANITY_FILES})
|
foreach(file ${VANITY_FILES})
|
||||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
|
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
|
||||||
endforeach()
|
endforeach()
|
||||||
@@ -221,69 +127,168 @@ endforeach()
|
|||||||
# Move our migrations for MasterServer to run
|
# Move our migrations for MasterServer to run
|
||||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/)
|
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/)
|
||||||
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
|
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
|
||||||
|
|
||||||
foreach(file ${SQL_FILES})
|
foreach(file ${SQL_FILES})
|
||||||
get_filename_component(file ${file} NAME)
|
get_filename_component(file ${file} NAME)
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
|
if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file}
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
|
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
|
||||||
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)
|
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)
|
||||||
|
|
||||||
foreach(file ${SQL_FILES})
|
foreach(file ${SQL_FILES})
|
||||||
get_filename_component(file ${file} NAME)
|
get_filename_component(file ${file} NAME)
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
|
if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
# Create our list of include directories
|
||||||
|
set(INCLUDED_DIRECTORIES
|
||||||
|
"dCommon"
|
||||||
|
"dCommon/dClient"
|
||||||
|
"dCommon/dEnums"
|
||||||
|
"dChatFilter"
|
||||||
|
"dGame"
|
||||||
|
"dGame/dBehaviors"
|
||||||
|
"dGame/dComponents"
|
||||||
|
"dGame/dGameMessages"
|
||||||
|
"dGame/dInventory"
|
||||||
|
"dGame/dMission"
|
||||||
|
"dGame/dEntity"
|
||||||
|
"dGame/dPropertyBehaviors"
|
||||||
|
"dGame/dUtilities"
|
||||||
|
"dPhysics"
|
||||||
|
"dNavigation"
|
||||||
|
"dNavigation/dTerrain"
|
||||||
|
"dZoneManager"
|
||||||
|
"dDatabase"
|
||||||
|
"dDatabase/Tables"
|
||||||
|
"dNet"
|
||||||
|
"dScripts"
|
||||||
|
"dScripts/02_server"
|
||||||
|
"dScripts/ai"
|
||||||
|
"dScripts/client"
|
||||||
|
"dScripts/EquipmentScripts"
|
||||||
|
"dScripts/EquipmentTriggers"
|
||||||
|
"dScripts/zone"
|
||||||
|
"dScripts/02_server/DLU"
|
||||||
|
"dScripts/02_server/Enemy"
|
||||||
|
"dScripts/02_server/Equipment"
|
||||||
|
"dScripts/02_server/Map"
|
||||||
|
"dScripts/02_server/Minigame"
|
||||||
|
"dScripts/02_server/Objects"
|
||||||
|
"dScripts/02_server/Pets"
|
||||||
|
"dScripts/02_server/Enemy/AG"
|
||||||
|
"dScripts/02_server/Enemy/AM"
|
||||||
|
"dScripts/02_server/Enemy/FV"
|
||||||
|
"dScripts/02_server/Enemy/General"
|
||||||
|
"dScripts/02_server/Enemy/Survival"
|
||||||
|
"dScripts/02_server/Enemy/VE"
|
||||||
|
"dScripts/02_server/Enemy/Waves"
|
||||||
|
"dScripts/02_server/Map/AG"
|
||||||
|
"dScripts/02_server/Map/AG_Spider_Queen"
|
||||||
|
"dScripts/02_server/Map/AM"
|
||||||
|
"dScripts/02_server/Map/FV"
|
||||||
|
"dScripts/02_server/Map/General"
|
||||||
|
"dScripts/02_server/Map/GF"
|
||||||
|
"dScripts/02_server/Map/njhub"
|
||||||
|
"dScripts/02_server/Map/NS"
|
||||||
|
"dScripts/02_server/Map/NT"
|
||||||
|
"dScripts/02_server/Map/PR"
|
||||||
|
"dScripts/02_server/Map/Property"
|
||||||
|
"dScripts/02_server/Map/SS"
|
||||||
|
"dScripts/02_server/Map/VE"
|
||||||
|
"dScripts/02_server/Map/FV/Racing"
|
||||||
|
"dScripts/02_server/Map/General/Ninjago"
|
||||||
|
"dScripts/02_server/Map/njhub/boss_instance"
|
||||||
|
"dScripts/02_server/Map/NS/Waves"
|
||||||
|
"dScripts/02_server/Map/Property/AG_Med"
|
||||||
|
"dScripts/02_server/Map/Property/AG_Small"
|
||||||
|
"dScripts/02_server/Map/Property/NS_Med"
|
||||||
|
"dScripts/02_server/Minigame/General"
|
||||||
|
"dScripts/ai/ACT"
|
||||||
|
"dScripts/ai/AG"
|
||||||
|
"dScripts/ai/FV"
|
||||||
|
"dScripts/ai/GENERAL"
|
||||||
|
"dScripts/ai/GF"
|
||||||
|
"dScripts/ai/MINIGAME"
|
||||||
|
"dScripts/ai/NP"
|
||||||
|
"dScripts/ai/NS"
|
||||||
|
"dScripts/ai/PETS"
|
||||||
|
"dScripts/ai/PROPERTY"
|
||||||
|
"dScripts/ai/RACING"
|
||||||
|
"dScripts/ai/SPEC"
|
||||||
|
"dScripts/ai/WILD"
|
||||||
|
"dScripts/ai/ACT/FootRace"
|
||||||
|
"dScripts/ai/MINIGAME/SG_GF"
|
||||||
|
"dScripts/ai/MINIGAME/SG_GF/SERVER"
|
||||||
|
"dScripts/ai/NS/NS_PP_01"
|
||||||
|
"dScripts/ai/NS/WH"
|
||||||
|
"dScripts/ai/PROPERTY/AG"
|
||||||
|
"dScripts/ai/RACING/OBJECTS"
|
||||||
|
"dScripts/client/ai"
|
||||||
|
"dScripts/client/ai/PR"
|
||||||
|
"dScripts/zone/AG"
|
||||||
|
"dScripts/zone/LUPs"
|
||||||
|
"dScripts/zone/PROPERTY"
|
||||||
|
"dScripts/zone/PROPERTY/FV"
|
||||||
|
"dScripts/zone/PROPERTY/GF"
|
||||||
|
"dScripts/zone/PROPERTY/NS"
|
||||||
|
|
||||||
|
"thirdparty/raknet/Source"
|
||||||
|
"thirdparty/tinyxml2"
|
||||||
|
"thirdparty/recastnavigation"
|
||||||
|
"thirdparty/SQLite"
|
||||||
|
"thirdparty/cpplinq"
|
||||||
|
|
||||||
|
"tests"
|
||||||
|
"tests/dCommonTests"
|
||||||
|
"tests/dGameTests"
|
||||||
|
"tests/dGameTests/dComponentsTests"
|
||||||
|
)
|
||||||
|
|
||||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
include_directories("/usr/local/include/")
|
include_directories("/usr/local/include/")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Load all of our third party directories
|
if (WIN32)
|
||||||
add_subdirectory(thirdparty SYSTEM)
|
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include")
|
||||||
|
elseif (UNIX)
|
||||||
# Create our list of include directories
|
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt")
|
||||||
include_directories(
|
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt")
|
||||||
"dPhysics"
|
|
||||||
|
|
||||||
"dNavigation"
|
|
||||||
|
|
||||||
"dNet"
|
|
||||||
|
|
||||||
"tests"
|
|
||||||
"tests/dCommonTests"
|
|
||||||
"tests/dGameTests"
|
|
||||||
"tests/dGameTests/dComponentsTests"
|
|
||||||
|
|
||||||
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)
|
|
||||||
# TODO: Should probably not do this.
|
|
||||||
if(APPLE)
|
|
||||||
include_directories("/usr/local/include/")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Set warning flags
|
# Add binary directory as an include directory
|
||||||
if(MSVC)
|
include_directories(${PROJECT_BINARY_DIR})
|
||||||
# add_compile_options("/W4")
|
|
||||||
# Want to enable warnings eventually, but WAY too much noise right now
|
# Actually include the directories from our list
|
||||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
foreach (dir ${INCLUDED_DIRECTORIES})
|
||||||
add_compile_options("-Wuninitialized" "-Wold-style-cast")
|
include_directories(${PROJECT_SOURCE_DIR}/${dir})
|
||||||
else()
|
endforeach()
|
||||||
message(WARNING "Unknown compiler: '${CMAKE_CXX_COMPILER_ID}' - No warning flags enabled.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Add linking directories:
|
# Add linking directories:
|
||||||
|
link_directories(${PROJECT_BINARY_DIR})
|
||||||
|
|
||||||
|
# Load all of our third party directories
|
||||||
|
add_subdirectory(thirdparty)
|
||||||
|
|
||||||
|
# Glob together all headers that need to be precompiled
|
||||||
|
file(
|
||||||
|
GLOB HEADERS_DDATABASE
|
||||||
|
LIST_DIRECTORIES false
|
||||||
|
${PROJECT_SOURCE_DIR}/dDatabase/*.h
|
||||||
|
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
|
||||||
|
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
|
||||||
|
)
|
||||||
|
|
||||||
file(
|
file(
|
||||||
GLOB HEADERS_DZONEMANAGER
|
GLOB HEADERS_DZONEMANAGER
|
||||||
LIST_DIRECTORIES false
|
LIST_DIRECTORIES false
|
||||||
@@ -315,16 +320,15 @@ add_subdirectory(dGame)
|
|||||||
add_subdirectory(dZoneManager)
|
add_subdirectory(dZoneManager)
|
||||||
add_subdirectory(dNavigation)
|
add_subdirectory(dNavigation)
|
||||||
add_subdirectory(dPhysics)
|
add_subdirectory(dPhysics)
|
||||||
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" "MariaDB::ConnCpp" "magic_enum")
|
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp")
|
||||||
|
|
||||||
# Add platform specific common libraries
|
# Add platform specific common libraries
|
||||||
if(UNIX)
|
if (UNIX)
|
||||||
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread")
|
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread")
|
||||||
|
|
||||||
if(NOT APPLE AND ${INCLUDE_BACKTRACE})
|
if (NOT APPLE AND __include_backtrace__)
|
||||||
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace")
|
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@@ -335,11 +339,23 @@ add_subdirectory(dAuthServer)
|
|||||||
add_subdirectory(dChatServer)
|
add_subdirectory(dChatServer)
|
||||||
add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries
|
add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries
|
||||||
|
|
||||||
|
# Add our precompiled headers
|
||||||
|
target_precompile_headers(
|
||||||
|
dGame PRIVATE
|
||||||
|
${HEADERS_DGAME}
|
||||||
|
)
|
||||||
|
|
||||||
target_precompile_headers(
|
target_precompile_headers(
|
||||||
dZoneManager PRIVATE
|
dZoneManager PRIVATE
|
||||||
${HEADERS_DZONEMANAGER}
|
${HEADERS_DZONEMANAGER}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Need to specify to use the CXX compiler language here or else we get errors including <string>.
|
||||||
|
target_precompile_headers(
|
||||||
|
dDatabase PRIVATE
|
||||||
|
"$<$<COMPILE_LANGUAGE:CXX>:${HEADERS_DDATABASE}>"
|
||||||
|
)
|
||||||
|
|
||||||
target_precompile_headers(
|
target_precompile_headers(
|
||||||
dCommon PRIVATE
|
dCommon PRIVATE
|
||||||
${HEADERS_DCOMMON}
|
${HEADERS_DCOMMON}
|
||||||
@@ -350,6 +366,6 @@ target_precompile_headers(
|
|||||||
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
|
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(${ENABLE_TESTING})
|
if (${__enable_testing__} MATCHES "1")
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,191 +1,128 @@
|
|||||||
{
|
{
|
||||||
"version": 6,
|
"version": 3,
|
||||||
"cmakeMinimumRequired": {
|
"cmakeMinimumRequired": {
|
||||||
"major": 3,
|
"major": 3,
|
||||||
"minor": 25,
|
"minor": 14,
|
||||||
"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": "ci-ubuntu-20.04",
|
||||||
|
"displayName": "CI configure step for Ubuntu",
|
||||||
|
"description": "Same as default, Used in GitHub actions workflow",
|
||||||
|
"inherits": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ci-macos-11",
|
||||||
|
"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": {
|
||||||
"name": "windows-default",
|
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||||
"inherits": "default",
|
|
||||||
"displayName": "Windows Default Configure Settings",
|
|
||||||
"description": "Sets build and install directories",
|
|
||||||
"generator": "Visual Studio 17 2022",
|
|
||||||
"condition": {
|
|
||||||
"type": "equals",
|
|
||||||
"lhs": "${hostSystemName}",
|
|
||||||
"rhs": "Windows"
|
|
||||||
},
|
|
||||||
"hidden": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "windows-msvc",
|
|
||||||
"inherits": "windows-default",
|
|
||||||
"displayName": "Windows (MSVC)",
|
|
||||||
"description": "Create a build using MSVC"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "windows-clang",
|
|
||||||
"inherits": "windows-default",
|
|
||||||
"displayName": "EXPERIMENTAL - Windows (Clang)",
|
|
||||||
"description": "Create a build using Clang",
|
|
||||||
"toolset": "ClangCL"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "linux-default",
|
|
||||||
"inherits": "default",
|
|
||||||
"hidden": true,
|
|
||||||
"condition": {
|
|
||||||
"type": "equals",
|
|
||||||
"lhs": "${hostSystemName}",
|
|
||||||
"rhs": "Linux"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "linux-clang",
|
|
||||||
"inherits": "linux-default",
|
|
||||||
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-clang.cmake",
|
|
||||||
"displayName": "Linux (Clang)",
|
|
||||||
"description": "Create a build using the Clang toolchain for Linux"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "linux-gnu",
|
|
||||||
"inherits": "linux-default",
|
|
||||||
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-gnu.cmake",
|
|
||||||
"displayName": "Linux (GNU)",
|
|
||||||
"description": "Create a build using the GNU toolchain for Linux"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "macos",
|
|
||||||
"inherits": "default",
|
|
||||||
"displayName": "MacOS",
|
|
||||||
"description": "Create a build for MacOS",
|
|
||||||
"condition": {
|
|
||||||
"type": "equals",
|
|
||||||
"lhs": "${hostSystemName}",
|
|
||||||
"rhs": "Darwin"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": [
|
"buildPresets": [
|
||||||
{
|
{
|
||||||
"name": "default",
|
"name": "default",
|
||||||
"configurePreset": "default",
|
"configurePreset": "default",
|
||||||
"displayName": "Default Build",
|
"displayName": "Default Build",
|
||||||
"description": "Default Build",
|
"description": "Default Build",
|
||||||
"jobs": 2
|
"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-20.04",
|
||||||
|
"configurePreset": "ci-ubuntu-20.04",
|
||||||
|
"displayName": "Linux CI Build",
|
||||||
|
"description": "This preset is used by the CI build on linux",
|
||||||
|
"jobs": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ci-macos-11",
|
||||||
|
"configurePreset": "ci-macos-11",
|
||||||
|
"displayName": "MacOS CI Build",
|
||||||
|
"description": "This preset is used by the CI build on MacOS",
|
||||||
|
"jobs": 2
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"testPresets": [
|
"testPresets": [
|
||||||
{
|
{
|
||||||
"name": "ci-ubuntu-22.04",
|
"name": "ci-ubuntu-20.04",
|
||||||
"configurePreset": "ci-ubuntu-22.04",
|
"configurePreset": "ci-ubuntu-20.04",
|
||||||
"displayName": "CI Tests on Linux",
|
"displayName": "CI Tests on Linux",
|
||||||
"description": "Runs all tests on a linux configuration",
|
"description": "Runs all tests on a linux configuration",
|
||||||
"execution": {
|
"execution": {
|
||||||
"jobs": 2
|
"jobs": 2
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
"outputOnFailure": true
|
"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))+"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"workflowPresets": [
|
{
|
||||||
{
|
"name": "ci-macos-11",
|
||||||
"name": "ci-windows-2022",
|
"configurePreset": "ci-macos-11",
|
||||||
"displayName": "[Release] Windows (MSVC)",
|
"displayName": "CI Tests on MacOS",
|
||||||
"description": "CI workflow preset for Windows",
|
"description": "Runs all tests on a Mac configuration",
|
||||||
"steps": [
|
"execution": {
|
||||||
{
|
"jobs": 2
|
||||||
"type": "configure",
|
},
|
||||||
"name": "windows-msvc"
|
"output": {
|
||||||
},
|
"outputOnFailure": true
|
||||||
{
|
|
||||||
"type": "build",
|
|
||||||
"name": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "test",
|
|
||||||
"name": "default"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ci-ubuntu-22.04",
|
|
||||||
"displayName": "[Release] Linux (GNU)",
|
|
||||||
"description": "CI workflow preset for Ubuntu",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"type": "configure",
|
|
||||||
"name": "linux-gnu"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "build",
|
|
||||||
"name": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "test",
|
|
||||||
"name": "ci-ubuntu-22.04"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ci-macos-13",
|
|
||||||
"displayName": "[Release] MacOS",
|
|
||||||
"description": "CI workflow preset for MacOS",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"type": "configure",
|
|
||||||
"name": "macos"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "build",
|
|
||||||
"name": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "test",
|
|
||||||
"name": "default"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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))+"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,24 @@
|
|||||||
PROJECT_VERSION_MAJOR=2
|
PROJECT_VERSION_MAJOR=1
|
||||||
PROJECT_VERSION_MINOR=3
|
PROJECT_VERSION_MINOR=0
|
||||||
PROJECT_VERSION_PATCH=0
|
PROJECT_VERSION_PATCH=4
|
||||||
|
# LICENSE
|
||||||
|
LICENSE=AGPL-3.0
|
||||||
|
# The network version.
|
||||||
|
# 171023 - Darkflame Universe client
|
||||||
|
# 171022 - Unmodded client
|
||||||
|
NET_VERSION=171022
|
||||||
# Debugging
|
# Debugging
|
||||||
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
||||||
DYNAMIC=1
|
__dynamic=1
|
||||||
|
# Set __ggdb to 1 to enable the -ggdb flag for the linker, including more debug info.
|
||||||
# Set GGDB to 1 to enable the -ggdb flag for the linker, including more debug info.
|
# __ggdb=1
|
||||||
# Do note, changing this will re-build the whole server
|
# Set __include_backtrace__ to 1 to includes the backtrace library for better crashlogs.
|
||||||
GGDB=0
|
# __include_backtrace__=1
|
||||||
|
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
|
||||||
# Set INCLUDE_BACKTRACE to 1 to includes the backtrace library for better crashlogs.
|
# __compile_backtrace__=1
|
||||||
# Do note, changing this will re-build the whole server
|
|
||||||
INCLUDE_BACKTRACE=0
|
|
||||||
|
|
||||||
# Set COMPILE_BACKTRACE to 1 to compile the backtrace library instead of using system libraries.
|
|
||||||
# Do note, changing this will re-build the whole server
|
|
||||||
COMPILE_BACKTRACE=0
|
|
||||||
|
|
||||||
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with.
|
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with.
|
||||||
MARIADB_CONNECTOR_COMPILE_JOBS=1
|
__maria_db_connector_compile_jobs__=1
|
||||||
|
|
||||||
# When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
|
# When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
|
||||||
ENABLE_TESTING=1
|
__enable_testing__=1
|
||||||
|
|
||||||
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
|
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
|
||||||
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
|
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
|
||||||
|
|
||||||
# Whether or not to cache the entire CDClient Database into memory instead of lazy loading.
|
|
||||||
# 0 means to lazy load, all other values mean load the entire database.
|
|
||||||
CDCLIENT_CACHE_ALL=0
|
|
||||||
|
|||||||
46
Docker.md
Normal file
46
Docker.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Run the Darkflame Server inside Docker
|
||||||
|
|
||||||
|
## What you need
|
||||||
|
|
||||||
|
- [Docker](https://docs.docker.com/get-docker/) (Docker Desktop or on Linux normal Docker)
|
||||||
|
- [Docker Compose](https://docs.docker.com/compose/install/) (Included in Docker Desktop)
|
||||||
|
- LEGO® Universe Client. Check the main [README](./README.md) for details on this.
|
||||||
|
|
||||||
|
## Run server inside Docker
|
||||||
|
|
||||||
|
1. Copy `.env.example` and save it as `.env` inside the root directory of this repository
|
||||||
|
2. Edit the `.env` file and add your path to the root directory of your LEGO® Universe Client after `CLIENT_PATH=`
|
||||||
|
3. Update other values in the `.env` file as needed (be sure to update passwords!)
|
||||||
|
4. Run `docker compose up -d --build`
|
||||||
|
5. Run `docker compose exec darkflame /app/MasterServer -a` and setup your admin account
|
||||||
|
6. Follow the directions [here](https://github.com/DarkflameUniverse/AccountManager) to setup regular user accounts. The server will be accessible at: `http://<EXTERNAL_IP>:5000`
|
||||||
|
7. Now you can see the output of the server with `docker compose logs -f --tail 100` or `docker compose logs -f --tail 100`. This can help you understand issues and there you can also see when the server finishes it's startup.
|
||||||
|
8. You're ready to connect your client!
|
||||||
|
|
||||||
|
**NOTE #1**: If you're running an older version of Docker, you may need to use the command `docker-compose` instead of `docker compose`.
|
||||||
|
|
||||||
|
**NOTE #2**: To stop the server simply run `docker compose down` and to restart it just run `docker compose up -d` again. No need to run all the steps above every time.
|
||||||
|
|
||||||
|
**NOTE #3**: Docker buildkit needs to be enabled. https://docs.docker.com/develop/develop-images/build_enhancements/#to-enable-buildkit-builds
|
||||||
|
|
||||||
|
**NOTE #4**: Make sure to run the following in the repo root directory after cloning so submodules are also downloaded.
|
||||||
|
```
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
**NOTE #5**: If DarkflameSetup fails due to not having cdclient.fdb, rename CDClient.fdb (in the same folder) to cdclient.fdb
|
||||||
|
|
||||||
|
## Disable brickbuildfix
|
||||||
|
|
||||||
|
If you don't need the http server running on port 80 do this:
|
||||||
|
|
||||||
|
1. Create a file with the name `docker-compose.override.yml` in the root of the repository
|
||||||
|
2. Paste this content:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
services:
|
||||||
|
brickbuildfix:
|
||||||
|
profiles:
|
||||||
|
- donotstart
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Now run `docker compose up -d`
|
||||||
58
Docker_Windows.md
Normal file
58
Docker_Windows.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Installation under Windows
|
||||||
|
## First Run
|
||||||
|
1. Navigate to the [Docker download page](https://www.docker.com/products/docker-desktop) and download docker.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. Once the file has finished downloading, run it and proceed through the installation. Make sure, "Install required Windows components for WSL 2" is checked.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. If necessary, restart your computer.
|
||||||
|
4. After the restart, Docker Desktop will automatically open. If it does not, simply start it like any other program.
|
||||||
|
5. If a window "WSL 2 Installation is incomplete." pops up, follow the link and click "WSL2 Linux kernel update package for x64 machines". Run the downloaded file and once that finishes, click "Restart" in the Docker Desktop window.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
6. Wait until Docker Desktop has started. You may skip the tutorial.
|
||||||
|
7. You may want to disable "Open Docker Dashboard at startup" in _Settings_ -> _General_
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
8. Install [Git for Windows](https://git-scm.com/download/win). During the installation, simply confirming the defaults is sufficient.
|
||||||
|
9. In the folder you wish to save the Server, right click and select "Git Bash Here".
|
||||||
|
10. Type `git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer`
|
||||||
|
11. Once the command has completed (you can see you path again and can enter commands), close the window.
|
||||||
|
12. Inside the downloaded folder, copy `.env.example` and name the copy `.env`
|
||||||
|
13. Open `.env` with Notepad by right-clicking it and selecting _Open With_ -> _More apps_ -> _Notepad_.
|
||||||
|
14. Change the text after `CLIENT_PATH=` to the location of your client. This folder must contain either a folder `client` or `legouniverse.exe`.
|
||||||
|
> If you need the extra performance, place the client files in `\\wsl$\<your linux OS>\...` to avoid working across file systems, see [Docker Best Practices](https://docs.docker.com/desktop/windows/wsl/#best-practices) and [WSL documentation](https://docs.microsoft.com/en-us/windows/wsl/filesystems#file-storage-and-performance-across-file-systems).
|
||||||
|
|
||||||
|
15. Optionally, you can change the number after `BUILD_THREADS=` to the number of cores / threads your processor has. If your computer crashes while building, you can try to reduce this value.
|
||||||
|
16. After `ACCOUNT_MANAGER_SECRET=` paste a "SHA 256-bit Key" from https://keygen.io/
|
||||||
|
17. If you are not only hosting a local server, change the value after `EXTERNAL_IP=` to the external IP address of your computer.
|
||||||
|
18. Change the two values `SECRET_VALUE_CHANGE_ME` to passwords only you know. Save and close the file.
|
||||||
|
19. In the extracted folder hit Shift+Right Click and select "Open PowerShell window here".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
17. In the new window, paste (with right click) or type `docker compose up -d --build` and confirm with enter.
|
||||||
|
18. Once you see the blinking cursor and the path again, setup has finished and the server is already running.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
19. Create an admin account by pasting `docker compose exec darkflame /app/MasterServer -a` and following the prompts.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
20. You can now login with these credentials at `http://your_ip:5000` (replace your_ip with your external IP). There you can create your account for playing as well as generate keys for other people to join; use these at `http://your_ip:5000/activate`
|
||||||
|
|
||||||
|
## Normal Use
|
||||||
|
1. In Docker Desktop you should now see an entry `darkflameserver-main` and when you click on it all containers but `DarkflameSetup` should eventually be green. That means the server is running.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. For troubleshooting, you can check the logs of the various parts by clicking their entry.
|
||||||
|
3. You can start and stop the server with the corresponding buttons. Once all containers are grey, the server has shut down, and when all containers but `DarkflameSetup` are green, the server is running. Note that starting and stopping takes some time, please be patient.
|
||||||
|
|
||||||
|

|
||||||
50
Dockerfile
50
Dockerfile
@@ -1,50 +0,0 @@
|
|||||||
FROM gcc:12 as build
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN set -ex; \
|
|
||||||
apt-get update; \
|
|
||||||
apt-get install -y cmake
|
|
||||||
|
|
||||||
COPY . /app/
|
|
||||||
COPY --chmod=0500 ./build.sh /app/
|
|
||||||
|
|
||||||
RUN sed -i 's/MARIADB_CONNECTOR_COMPILE_JOBS__=.*/MARIADB_CONNECTOR_COMPILE_JOBS__=2/' /app/CMakeVariables.txt
|
|
||||||
|
|
||||||
RUN ./build.sh
|
|
||||||
|
|
||||||
FROM debian:12 as runtime
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \
|
|
||||||
apt update && \
|
|
||||||
apt install -y libssl3 libcurl4 && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Grab libraries and load them
|
|
||||||
COPY --from=build /app/build/mariadbcpp/libmariadbcpp.so /usr/local/lib/
|
|
||||||
RUN ldconfig
|
|
||||||
|
|
||||||
# Server bins
|
|
||||||
COPY --from=build /app/build/*Server /app/
|
|
||||||
|
|
||||||
# Necessary suplimentary files
|
|
||||||
COPY --from=build /app/build/*.ini /app/configs/
|
|
||||||
COPY --from=build /app/build/vanity/*.* /app/vanity/
|
|
||||||
COPY --from=build /app/build/navmeshes /app/navmeshes
|
|
||||||
COPY --from=build /app/build/migrations /app/migrations
|
|
||||||
COPY --from=build /app/build/*.dcf /app/
|
|
||||||
|
|
||||||
# backup of config and vanity files to copy to the host incase
|
|
||||||
# of a mount clobbering the copy from above
|
|
||||||
COPY --from=build /app/build/*.ini /app/default-configs/
|
|
||||||
COPY --from=build /app/build/vanity/*.* /app/default-vanity/
|
|
||||||
|
|
||||||
# needed as the container runs with the root user
|
|
||||||
# and therefore sudo doesn't exist
|
|
||||||
ENV USE_SUDO_AUTH=0
|
|
||||||
ENV DLU_CONFIG_DIR=/app/configs/
|
|
||||||
|
|
||||||
COPY --chmod=0500 ./entrypoint.sh /app/
|
|
||||||
ENTRYPOINT [ "/app/entrypoint.sh" ]
|
|
||||||
138
README.md
138
README.md
@@ -23,9 +23,6 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s
|
|||||||
### Supply of resource files
|
### Supply of resource files
|
||||||
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.
|
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.
|
||||||
|
|
||||||
## Step by step walkthrough for a single-player server
|
|
||||||
If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.
|
|
||||||
|
|
||||||
## Steps to setup server
|
## Steps to setup server
|
||||||
* [Clone this repository](#clone-the-repository)
|
* [Clone this repository](#clone-the-repository)
|
||||||
* [Install dependencies](#install-dependencies)
|
* [Install dependencies](#install-dependencies)
|
||||||
@@ -37,7 +34,6 @@ If you would like a setup for a single player server only on a Windows machine,
|
|||||||
* [Verify your setup](#verify-your-setup)
|
* [Verify your setup](#verify-your-setup)
|
||||||
* [Running the server](#running-the-server)
|
* [Running the server](#running-the-server)
|
||||||
* [User Guide](#user-guide)
|
* [User Guide](#user-guide)
|
||||||
* [Docker](#docker)
|
|
||||||
|
|
||||||
## Clone the repository
|
## Clone the repository
|
||||||
If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win)
|
If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win)
|
||||||
@@ -51,7 +47,7 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
|
|||||||
|
|
||||||
### Windows packages
|
### Windows packages
|
||||||
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
|
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
|
||||||
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!).
|
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.18**</font> or later!).
|
||||||
|
|
||||||
### MacOS packages
|
### MacOS packages
|
||||||
Ensure you have [brew](https://brew.sh) installed.
|
Ensure you have [brew](https://brew.sh) installed.
|
||||||
@@ -73,7 +69,7 @@ sudo apt install build-essential gcc zlib1g-dev libssl-dev openssl mariadb-serve
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Required CMake version
|
#### Required CMake version
|
||||||
This project uses <font size="4">**CMake version 3.25**</font> or higher and as such you will need to ensure you have this version installed.
|
This project uses <font size="4">**CMake version 3.18**</font> or higher and as such you will need to ensure you have this version installed.
|
||||||
You can check your CMake version by using the following command in a terminal.
|
You can check your CMake version by using the following command in a terminal.
|
||||||
```bash
|
```bash
|
||||||
cmake --version
|
cmake --version
|
||||||
@@ -176,14 +172,10 @@ You can either run `build.sh` when in the root folder of the repository:
|
|||||||
|
|
||||||
Or manually run the commands used in [build.sh](build.sh).
|
Or manually run the commands used in [build.sh](build.sh).
|
||||||
|
|
||||||
If you would like to build the server faster, append `-j<number>` where number is the number of simultaneous compile jobs to run at once. It is recommended that you have this number always be 1 less than your core count to prevent slowdowns. The command would look like this if you would build with 4 jobs at once:
|
|
||||||
```bash
|
|
||||||
./build.sh -j4
|
|
||||||
```
|
|
||||||
### Notes
|
### Notes
|
||||||
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
|
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
|
||||||
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
|
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
|
||||||
* If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023.
|
* If you are using a Darkflame Universe client, ensure NET_VERSION is changed to 171023.
|
||||||
|
|
||||||
## Configuring your server
|
## Configuring your server
|
||||||
This server has a few steps that need to be taken to configure the server for your use case.
|
This server has a few steps that need to be taken to configure the server for your use case.
|
||||||
@@ -228,44 +220,6 @@ sudo setcap 'cap_net_bind_service=+ep' AuthServer
|
|||||||
```
|
```
|
||||||
and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0.
|
and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0.
|
||||||
|
|
||||||
### Linux Service
|
|
||||||
If you are running this on a linux based system, it will use your terminal to run the program interactively, preventing you using it for other tasks and requiring it to be open to run the server.
|
|
||||||
_Note: You could use screen or tmux instead for virtual terminals_
|
|
||||||
To run the server non-interactively, we can use a systemctl service by copying the following file:
|
|
||||||
```shell
|
|
||||||
cp ./systemd.example /etc/systemd/system/darkflame.service
|
|
||||||
```
|
|
||||||
|
|
||||||
Make sure to edit the file in `/etc/systemd/system/darkflame.service` and change the:
|
|
||||||
- `User` and `Group` to the user that runs the darkflame server.
|
|
||||||
- `ExecPath` to the full file path of the server executable.
|
|
||||||
|
|
||||||
To register, enable and start the service use the following commands:
|
|
||||||
|
|
||||||
- Reload the systemd manager configuration to make it aware of the new service file:
|
|
||||||
```shell
|
|
||||||
systemctl daemon-reload
|
|
||||||
```
|
|
||||||
- Start the service:
|
|
||||||
```shell
|
|
||||||
systemctl start darkflame.service
|
|
||||||
```
|
|
||||||
- Enable OR disable the service to start on boot using:
|
|
||||||
```shell
|
|
||||||
systemctl enable darkflame.service
|
|
||||||
systemctl disable darkflame.service
|
|
||||||
```
|
|
||||||
- Verify that the service is running without errors:
|
|
||||||
```shell
|
|
||||||
systemctl status darkflame.service
|
|
||||||
```
|
|
||||||
- You can also restart, stop, or check the logs of the service using journalctl
|
|
||||||
```shell
|
|
||||||
systemctl restart darkflame.service
|
|
||||||
systemctl stop darkflame.service
|
|
||||||
journalctl -xeu darkflame.service
|
|
||||||
```
|
|
||||||
|
|
||||||
### First admin user
|
### First admin user
|
||||||
Run `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users!
|
Run `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users!
|
||||||
|
|
||||||
@@ -287,11 +241,30 @@ To connect to a server follow these steps:
|
|||||||
* In the client directory, locate `boot.cfg`
|
* In the client directory, locate `boot.cfg`
|
||||||
* Open it in a text editor and locate where it says `AUTHSERVERIP=0:`
|
* Open it in a text editor and locate where it says `AUTHSERVERIP=0:`
|
||||||
* Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers
|
* Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers
|
||||||
* Next locate the line `UGCUSE3DSERVICES=7:`
|
|
||||||
* Ensure the number after the 7 is a `0`
|
|
||||||
* Launch `legouniverse.exe`, through `wine` if on a Unix-like operating system
|
* Launch `legouniverse.exe`, through `wine` if on a Unix-like operating system
|
||||||
* Note that if you are on WSL2, you will need to configure the public IP in the server and client to be the IP of the WSL2 instance and not localhost, which can be found by running `ifconfig` in the terminal. Windows defaults to WSL1, so this will not apply to most users.
|
* Note that if you are on WSL2, you will need to configure the public IP in the server and client to be the IP of the WSL2 instance and not localhost, which can be found by running `ifconfig` in the terminal. Windows defaults to WSL1, so this will not apply to most users.
|
||||||
|
|
||||||
|
## Brick-By-Brick building
|
||||||
|
Should you choose to do any brick building, you will want to have some form of a http server that returns a 404 error since we are unable to emulate live User Generated Content at the moment. If you attempt to do any brick building without a 404 server running properly, you will be unable to load into your game. Python is the easiest way to do this, but any thing that returns a 404 should work fine.
|
||||||
|
* Note: the client hard codes this request on port 80.
|
||||||
|
|
||||||
|
<font size="4">**If you do not plan on doing any Brick Building, then you can skip this step.**</font>
|
||||||
|
|
||||||
|
The easiest way to do this is to install [python](https://www.python.org/downloads/).
|
||||||
|
|
||||||
|
### Allowing a user to build in Brick-by-Brick mode
|
||||||
|
Brick-By-Brick building requires `PATCHSERVERIP=0:` and `UGCSERVERIP=0:` in the `boot.cfg` to point to a HTTP server which always returns `HTTP 404 - Not Found` for all requests. This can be most easily achieved by pointing both of those variables to `localhost` while having running in the background.
|
||||||
|
Each client must have their own 404 server running if they are using a locally hosted 404 server.
|
||||||
|
```bash
|
||||||
|
# If on linux run this command. Because this is run on a port below 1024, binary network permissions are needed.
|
||||||
|
sudo python3 -m http.server 80
|
||||||
|
|
||||||
|
# If on windows one of the following will work when run through Powershell or Command Prompt assuming python is installed
|
||||||
|
python3 -m http.server 80
|
||||||
|
python http.server 80
|
||||||
|
py -m http.server 80
|
||||||
|
```
|
||||||
|
|
||||||
## Updating your server
|
## Updating your server
|
||||||
To update your server to the latest version navigate to your cloned directory
|
To update your server to the latest version navigate to your cloned directory
|
||||||
```bash
|
```bash
|
||||||
@@ -327,7 +300,6 @@ Below are known good SHA256 checksums of the client:
|
|||||||
* `0d862f71eedcadc4494c4358261669721b40b2131101cbd6ef476c5a6ec6775b` (unpacked client, includes extra locales, rar compressed)
|
* `0d862f71eedcadc4494c4358261669721b40b2131101cbd6ef476c5a6ec6775b` (unpacked client, includes extra locales, rar compressed)
|
||||||
|
|
||||||
If the returned hash matches one of the lines above then you can continue with setting up the server. If you are using a fully downloaded and complete client from live, then it will work, but the hash above may not match. Otherwise you must obtain a full install of LEGO® Universe 1.10.64.
|
If the returned hash matches one of the lines above then you can continue with setting up the server. If you are using a fully downloaded and complete client from live, then it will work, but the hash above may not match. Otherwise you must obtain a full install of LEGO® Universe 1.10.64.
|
||||||
You must also make absolutely sure your LEGO Universe client is not in a Windows OneDrive. DLU is not and will not support a client being stored in a OneDrive, so ensure you have moved the client outside of that location.
|
|
||||||
|
|
||||||
### Darkflame Universe Client
|
### Darkflame Universe Client
|
||||||
Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there.
|
Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there.
|
||||||
@@ -348,66 +320,6 @@ certutil -hashfile <file> SHA1
|
|||||||
Known good *SHA1* checksum of the Darkflame Universe client:
|
Known good *SHA1* checksum of the Darkflame Universe client:
|
||||||
- `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed)
|
- `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed)
|
||||||
|
|
||||||
|
|
||||||
# Docker
|
|
||||||
|
|
||||||
The Darkflame Server is automatically built and published as a Docker Container / [OCI](https://opencontainers.org/) Image to the GitHub Container Registry at:
|
|
||||||
[`ghcr.io/darkflameuniverse/darkflameserver`](https://github.com/DarkflameUniverse/DarkflameServer/pkgs/container/darkflameserver).
|
|
||||||
|
|
||||||
## Compose
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> It seems that Docker Desktop on Windows with the WSL 2 backend has some issues with MariaDB (c.f. [mariadb-docker#331](https://github.com/MariaDB/mariadb-docker/issues/331)) triggered by NexusDashboard
|
|
||||||
> migrations, so this setup may not work for you. If that is the case, please tell us about your setup in [NexusDashboard#92](https://github.com/DarkflameUniverse/NexusDashboard/issues/92).
|
|
||||||
|
|
||||||
You can use the `docker-compose` tool to [setup a MariaDB database](#database-setup), run the Darkflame Server and manage it with [Nexus Dashboard](https://github.com/DarkflameUniverse/NexusDashboard) all
|
|
||||||
at once. For that:
|
|
||||||
|
|
||||||
- [Install Docker Desktop](https://docs.docker.com/get-docker/)
|
|
||||||
- Open the directory that contains your LU Client
|
|
||||||
- If the `legouniverse.exe` is in a subfolder called `client`, you're good to go. There may also be a folder `versions`.
|
|
||||||
- Otherwise, create a new `client` folder and move the exe and everything else (e.g. `res` and `locale`) in there. This is necessary to work around a bug in the client that will prevent that you to log back in after getting disconnected.
|
|
||||||
- Download the [docker-compose.yml](docker-compose.yml) file and place it next to `client`.
|
|
||||||
- Download the [.env.example](.env.example) file and place it next to `client` with the file name `.env`
|
|
||||||
- You may get warnings that this name starts with a dot, acknowledge those, this is intentional. Depending on your operating system, you may need to activate showing hidden files (e.g. Ctrl-H in Gnome on Linux) and/or file extensions ("File name extensions" in the "View" tab on Windows).
|
|
||||||
- Update the `ACCOUNT_MANAGER_SECRET` and `MARIADB_PASSWORD` with strong random passwords.
|
|
||||||
- Use a password generator like <https://keygen.io>
|
|
||||||
- Avoid `:` and `@` characters
|
|
||||||
- Once the database user is created, changing the password will not update it, so the server will just fail to connect.
|
|
||||||
- Set `EXTERNAL_IP` to your LAN IP or public IP if you want to host the game for friends & family
|
|
||||||
- Open a terminal in the folder with the `docker-compose.yml` and `client`
|
|
||||||
- Run `docker compose up -d`
|
|
||||||
- This might require `sudo` on Linux, and a recent version of [docker compose](https://docs.docker.com/compose/install/)
|
|
||||||
- Run `docker exec -it dlu-darkflameserver-1 /app/MasterServer -a` and follow the instructions to create the initial admin account
|
|
||||||
- Open <http://localhost:8000> to access Nexus Dashboard with the admin account to create normal users
|
|
||||||
- Set `AUTHSERVERIP=0:localhost` in `client/boot.cfg`
|
|
||||||
- Replace `localhost` with the value of `EXTERNAL_IP` if you changed that earlier.
|
|
||||||
- Also make sure `UGCUSE3DSERVICES=7:` is set to `0`
|
|
||||||
- Launch `legouniverse.exe`
|
|
||||||
|
|
||||||
## Standalone
|
|
||||||
|
|
||||||
This assumes that you have a database deployed to your host or in another docker container.
|
|
||||||
|
|
||||||
A basic deployment of this contianer would look like:
|
|
||||||
```sh
|
|
||||||
# example docker contianer deployment
|
|
||||||
docker run -it \
|
|
||||||
-v /path/to/configs/:/app/configs \
|
|
||||||
-v /path/to/logs/:/app/logs \
|
|
||||||
-v /path/to/dumps/:/app/dumps \
|
|
||||||
-v /path/to/res:/app/res:ro \
|
|
||||||
-v /path/to/resServer:/app/resServer \
|
|
||||||
-e DUMP_FOLDER=/app/dumps \
|
|
||||||
-p 1001:1001/udp \
|
|
||||||
-p 2005:2005/udp \
|
|
||||||
-p 3000-3300:3000-3300/udp \
|
|
||||||
ghcr.io/darkflameuniverse/darkflameserver:latest
|
|
||||||
```
|
|
||||||
You will need to replace the `/path/to/`'s to reflect the paths on your host.
|
|
||||||
|
|
||||||
Any config option in the `.ini`'s can be overridden with environmental variables: Ex: `log_to_console=1` from `shared_config.ini` would be overidden like `-e LOG_TO_CONSOLE=0`
|
|
||||||
|
|
||||||
# Development Documentation
|
# Development Documentation
|
||||||
This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server
|
This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server
|
||||||
[Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html)
|
[Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html)
|
||||||
@@ -441,7 +353,7 @@ This is a Work in Progress, but below are some quick links to documentaion for s
|
|||||||
## Former Contributors
|
## Former Contributors
|
||||||
* TheMachine
|
* TheMachine
|
||||||
* Matthew
|
* Matthew
|
||||||
* [Raine](https://github.com/uwainium)
|
* [Raine](https://github.com/Rainebannister)
|
||||||
* Bricknave
|
* Bricknave
|
||||||
|
|
||||||
## Special Thanks
|
## Special Thanks
|
||||||
|
|||||||
5
build.sh
5
build.sh
@@ -6,7 +6,8 @@ mkdir -p build
|
|||||||
cd build
|
cd build
|
||||||
|
|
||||||
# Run cmake to generate make files
|
# Run cmake to generate make files
|
||||||
cmake -DCMAKE_BUILD_TYPE="Release" ..
|
cmake ..
|
||||||
|
|
||||||
# To build utilizing multiple cores, append `-j` and the amount of cores to utilize, for example `cmake --build . --config Release -j8'
|
# 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
|
cmake --build . --config Release
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
include(FetchContent)
|
|
||||||
|
|
||||||
message(STATUS "Fetching gtest...")
|
|
||||||
|
|
||||||
FetchContent_Declare(
|
|
||||||
googletest
|
|
||||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
||||||
GIT_TAG release-1.12.1
|
|
||||||
)
|
|
||||||
|
|
||||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
FetchContent_MakeAvailable(GoogleTest)
|
|
||||||
|
|
||||||
message(STATUS "gtest fetched and is now ready.")
|
|
||||||
set(GoogleTest_FOUND TRUE)
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
# mariadb connector cpp
|
|
||||||
# On Windows ClangCL can't compile the connector from source but can link to an msvc compiled one,
|
|
||||||
# so prefer the prebuilt binaries unless MARIADB_BUILD_SOURCE is specified
|
|
||||||
if(WIN32 AND NOT MARIADB_BUILD_SOURCE)
|
|
||||||
set(MARIADB_MSI_DIR "${PROJECT_BINARY_DIR}/msi")
|
|
||||||
set(MARIADB_CONNECTOR_DIR "${PROJECT_BINARY_DIR}/mariadbcpp")
|
|
||||||
set(MARIADB_C_CONNECTOR_DIR "${MARIADB_CONNECTOR_DIR}/MariaDB/MariaDB Connector C 64-bit")
|
|
||||||
set(MARIADB_CPP_CONNECTOR_DIR "${MARIADB_CONNECTOR_DIR}/MariaDB/MariaDB C++ Connector 64-bit")
|
|
||||||
|
|
||||||
file(MAKE_DIRECTORY "${MARIADB_MSI_DIR}")
|
|
||||||
file(MAKE_DIRECTORY "${MARIADB_CONNECTOR_DIR}")
|
|
||||||
|
|
||||||
# These values need to be updated whenever a new minor release replaces an old one
|
|
||||||
# Go to https://mariadb.com/downloads/connectors/ to find the up-to-date URL parts
|
|
||||||
set(MARIADB_CONNECTOR_C_VERSION "3.2.7")
|
|
||||||
set(MARIADB_CONNECTOR_C_BUCKET "2319651")
|
|
||||||
set(MARIADB_CONNECTOR_C_MD5 "f8636d733f1d093af9d4f22f3239f885")
|
|
||||||
set(MARIADB_CONNECTOR_CPP_VERSION "1.0.2")
|
|
||||||
set(MARIADB_CONNECTOR_CPP_BUCKET "2531525")
|
|
||||||
set(MARIADB_CONNECTOR_CPP_MD5 "3034bbd6ca00a0125345f9fd1a178401")
|
|
||||||
|
|
||||||
set(MARIADB_CONNECTOR_C_MSI "mariadb-connector-c-${MARIADB_CONNECTOR_C_VERSION}-win64.msi")
|
|
||||||
set(MARIADB_CONNECTOR_CPP_MSI "mariadb-connector-cpp-${MARIADB_CONNECTOR_CPP_VERSION}-win64.msi")
|
|
||||||
|
|
||||||
if(NOT EXISTS "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}" )
|
|
||||||
message(STATUS "Downloading mariadb connector/c")
|
|
||||||
file(DOWNLOAD https://dlm.mariadb.com/${MARIADB_CONNECTOR_C_BUCKET}/Connectors/c/connector-c-${MARIADB_CONNECTOR_C_VERSION}/${MARIADB_CONNECTOR_C_MSI}
|
|
||||||
"${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}"
|
|
||||||
EXPECTED_HASH MD5=${MARIADB_CONNECTOR_C_MD5})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}" )
|
|
||||||
message(STATUS "Downloading mariadb connector/c++")
|
|
||||||
file(DOWNLOAD https://dlm.mariadb.com/${MARIADB_CONNECTOR_CPP_BUCKET}/Connectors/cpp/connector-cpp-${MARIADB_CONNECTOR_CPP_VERSION}/${MARIADB_CONNECTOR_CPP_MSI}
|
|
||||||
"${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}"
|
|
||||||
EXPECTED_HASH MD5=${MARIADB_CONNECTOR_CPP_MD5})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
file(TO_NATIVE_PATH "${MARIADB_CONNECTOR_DIR}" MSIEXEC_TARGETDIR)
|
|
||||||
# extract msi files without installing to users system
|
|
||||||
if(NOT EXISTS "${MARIADB_C_CONNECTOR_DIR}")
|
|
||||||
file(TO_NATIVE_PATH "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}" MSI_DIR)
|
|
||||||
execute_process(COMMAND msiexec /a ${MSI_DIR} /qn TARGETDIR=${MSIEXEC_TARGETDIR})
|
|
||||||
endif()
|
|
||||||
set(MARIADBC_SHARED_LIBRARY_LOCATION "${MARIADB_C_CONNECTOR_DIR}/lib/libmariadb.dll")
|
|
||||||
|
|
||||||
if(NOT EXISTS "${MARIADB_CPP_CONNECTOR_DIR}")
|
|
||||||
file(TO_NATIVE_PATH "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}" MSI_DIR)
|
|
||||||
execute_process(COMMAND msiexec /a ${MSI_DIR} /qn TARGETDIR=${MSIEXEC_TARGETDIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(MARIADBCPP_SHARED_LIBRARY_LOCATION "${MARIADB_CPP_CONNECTOR_DIR}/mariadbcpp.dll")
|
|
||||||
set(MARIADB_IMPLIB_LOCATION "${MARIADB_CPP_CONNECTOR_DIR}/mariadbcpp.lib")
|
|
||||||
set(MARIADB_INCLUDE_DIR "${MARIADB_CPP_CONNECTOR_DIR}/include/mariadb")
|
|
||||||
|
|
||||||
add_custom_target(mariadb_connector_cpp)
|
|
||||||
add_custom_command(TARGET mariadb_connector_cpp POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
|
||||||
"${MARIADBCPP_SHARED_LIBRARY_LOCATION}"
|
|
||||||
"${MARIADBC_SHARED_LIBRARY_LOCATION}"
|
|
||||||
"${PROJECT_BINARY_DIR}")
|
|
||||||
|
|
||||||
# MariaDB uses plugins that the database needs to load, the prebuilt binaries by default will try to find the libraries in system directories,
|
|
||||||
# so set this define and the servers will set the MARIADB_PLUGIN_DIR environment variable to the appropriate directory.
|
|
||||||
# Plugin directory is determined at dll load time (this will happen before main()) so we need to delay the dll load so that we can set the environment variable
|
|
||||||
add_link_options(/DELAYLOAD:${MARIADBCPP_SHARED_LIBRARY_LOCATION})
|
|
||||||
add_compile_definitions(MARIADB_PLUGIN_DIR_OVERRIDE="${MARIADB_CPP_CONNECTOR_DIR}/plugin")
|
|
||||||
else() # Build from source
|
|
||||||
|
|
||||||
include(ExternalProject)
|
|
||||||
if(WIN32)
|
|
||||||
set(MARIADB_EXTRA_CMAKE_ARGS
|
|
||||||
-DCMAKE_C_FLAGS=/w # disable zlib warnings
|
|
||||||
-DCMAKE_CXX_FLAGS=/EHsc
|
|
||||||
-DWITH_MSI=OFF)
|
|
||||||
elseif(APPLE)
|
|
||||||
set(MARIADB_EXTRA_CMAKE_ARGS
|
|
||||||
-DWITH_EXTERNAL_ZLIB=ON
|
|
||||||
-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}
|
|
||||||
-DCMAKE_C_FLAGS=-w # disable zlib warnings
|
|
||||||
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0)
|
|
||||||
else()
|
|
||||||
set(MARIADB_EXTRA_CMAKE_ARGS
|
|
||||||
-DCMAKE_C_FLAGS=-w # disable zlib warnings
|
|
||||||
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(MARIADBCPP_INSTALL_DIR ${PROJECT_BINARY_DIR}/prefix)
|
|
||||||
set(MARIADBCPP_LIBRARY_DIR ${PROJECT_BINARY_DIR}/mariadbcpp)
|
|
||||||
set(MARIADBCPP_PLUGIN_DIR ${MARIADBCPP_LIBRARY_DIR}/plugin)
|
|
||||||
set(MARIADBCPP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp)
|
|
||||||
set(MARIADB_INCLUDE_DIR "${MARIADBCPP_SOURCE_DIR}/include")
|
|
||||||
ExternalProject_Add(mariadb_connector_cpp
|
|
||||||
PREFIX "${PROJECT_BINARY_DIR}/thirdparty/mariadb-connector-cpp"
|
|
||||||
SOURCE_DIR ${MARIADBCPP_SOURCE_DIR}
|
|
||||||
INSTALL_DIR ${MARIADBCPP_INSTALL_DIR}
|
|
||||||
CMAKE_ARGS -Wno-dev
|
|
||||||
-DWITH_UNIT_TESTS=OFF
|
|
||||||
-DMARIADB_LINK_DYNAMIC=OFF
|
|
||||||
-DCMAKE_BUILD_RPATH_USE_ORIGIN=${CMAKE_BUILD_RPATH_USE_ORIGIN}
|
|
||||||
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
|
||||||
-DINSTALL_LIBDIR=${MARIADBCPP_LIBRARY_DIR}
|
|
||||||
-DINSTALL_PLUGINDIR=${MARIADBCPP_PLUGIN_DIR}
|
|
||||||
${MARIADB_EXTRA_CMAKE_ARGS}
|
|
||||||
BUILD_ALWAYS true
|
|
||||||
)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
set(MARIADB_SHARED_LIBRARY_NAME mariadbcpp.dll)
|
|
||||||
set(MARIADB_PLUGIN_SUFFIX .dll)
|
|
||||||
set(MARIADB_IMPLIB_LOCATION "${MARIADBCPP_LIBRARY_DIR}/mariadbcpp.lib")
|
|
||||||
|
|
||||||
# When built from source windows only seems to check same folder as exe instead specified folder, so use
|
|
||||||
# environment variable to force it
|
|
||||||
add_link_options(/DELAYLOAD:mariadbcpp.dll)
|
|
||||||
add_compile_definitions(MARIADB_PLUGIN_DIR_OVERRIDE="${MARIADBCPP_PLUGIN_DIR}")
|
|
||||||
else()
|
|
||||||
set(MARIADB_SHARED_LIBRARY_NAME libmariadbcpp${CMAKE_SHARED_LIBRARY_SUFFIX})
|
|
||||||
set(MARIADB_PLUGIN_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(MARIADBCPP_SHARED_LIBRARY_LOCATION "${MARIADBCPP_LIBRARY_DIR}/${MARIADB_SHARED_LIBRARY_NAME}")
|
|
||||||
if(WIN32)
|
|
||||||
set(MARIADBC_SHARED_LIBRARY_LOCATION "${MARIADBCPP_LIBRARY_DIR}/libmariadb.lib")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Create mariadb connector library object
|
|
||||||
add_library(MariaDB::ConnCpp SHARED IMPORTED GLOBAL)
|
|
||||||
add_dependencies(MariaDB::ConnCpp mariadb_connector_cpp)
|
|
||||||
set_target_properties(MariaDB::ConnCpp PROPERTIES
|
|
||||||
IMPORTED_LOCATION "${MARIADBCPP_SHARED_LIBRARY_LOCATION}")
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
set_target_properties(MariaDB::ConnCpp PROPERTIES
|
|
||||||
IMPORTED_IMPLIB "${MARIADB_IMPLIB_LOCATION}")
|
|
||||||
elseif(APPLE)
|
|
||||||
set_target_properties(MariaDB::ConnCpp PROPERTIES
|
|
||||||
IMPORTED_SONAME "libmariadbcpp")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Add directories to include lists
|
|
||||||
target_include_directories(MariaDB::ConnCpp SYSTEM INTERFACE ${MARIADB_INCLUDE_DIR})
|
|
||||||
|
|
||||||
set(MariaDB_FOUND TRUE)
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# Parses a config file for a specific option and appends the new option if it does not exist
|
|
||||||
# If the new option does exist, this function will do nothing.
|
|
||||||
# file_name: The name of the file to parse
|
|
||||||
# old_option_name: The name of the option to find
|
|
||||||
# new_option_name: The name of the option to add
|
|
||||||
function(UpdateConfigOption file_name old_option_name new_option_name)
|
|
||||||
string(APPEND old_option_name "=")
|
|
||||||
string(APPEND new_option_name "=")
|
|
||||||
message(STATUS "Checking " ${file_name} " for " ${old_option_name} " and adding " ${new_option_name} " if it does not exist")
|
|
||||||
if(NOT EXISTS ${file_name})
|
|
||||||
message(STATUS ${file_name} " does not exist. Doing nothing")
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
file(READ ${file_name} 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 "")
|
|
||||||
|
|
||||||
# Remove comment lines so they do not interfere with the variable parsing
|
|
||||||
foreach(line ${current_file_contents})
|
|
||||||
string(FIND ${line} "#" is_comment)
|
|
||||||
|
|
||||||
if(NOT ${is_comment} EQUAL 0)
|
|
||||||
string(APPEND parsed_current_file_contents ${line})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
set(found_new_option -1)
|
|
||||||
set(found_old_option -1)
|
|
||||||
set(current_value -1)
|
|
||||||
|
|
||||||
foreach(line ${current_file_contents})
|
|
||||||
string(FIND ${line} ${old_option_name} old_option_in_file)
|
|
||||||
|
|
||||||
if(${old_option_in_file} EQUAL 0)
|
|
||||||
set(found_old_option 1)
|
|
||||||
set(current_value ${line})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
string(FIND ${line} ${new_option_name} found_new_option_in_file)
|
|
||||||
|
|
||||||
if(${found_new_option_in_file} EQUAL 0)
|
|
||||||
set(found_new_option 1)
|
|
||||||
endif()
|
|
||||||
endforeach(line ${current_file_contents})
|
|
||||||
|
|
||||||
if(${found_old_option} EQUAL 1 AND NOT ${found_new_option} EQUAL 1)
|
|
||||||
string(REPLACE ${old_option_name} ${new_option_name} current_value ${current_value})
|
|
||||||
file(APPEND ${file_name} "\n" ${current_value})
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# 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 (need to cache for VSCode tools to work correctly)
|
|
||||||
set(CMAKE_C_COMPILER ${CLANG_C_COMPILER} CACHE STRING "Set C compiler")
|
|
||||||
set(CMAKE_CXX_COMPILER ${CLANG_CXX_COMPILER} CACHE STRING "Set C++ compiler")
|
|
||||||
|
|
||||||
# Set linker to lld
|
|
||||||
add_link_options("-fuse-ld=lld")
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# 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_CXX_COMPILER}")
|
|
||||||
|
|
||||||
# Set compilers to GNU (need to cache for VSCode tools to work correctly)
|
|
||||||
set(CMAKE_C_COMPILER ${GNU_C_COMPILER} CACHE STRING "Set C compiler")
|
|
||||||
set(CMAKE_CXX_COMPILER ${GNU_CXX_COMPILER} CACHE STRING "Set C++ compiler")
|
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <csignal>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
//DLU Includes:
|
//DLU Includes:
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
#include "dServer.h"
|
#include "dServer.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
#include "Diagnostics.h"
|
#include "Diagnostics.h"
|
||||||
@@ -16,26 +15,20 @@
|
|||||||
|
|
||||||
//RakNet includes:
|
//RakNet includes:
|
||||||
#include "RakNetDefines.h"
|
#include "RakNetDefines.h"
|
||||||
#include "MessageIdentifiers.h"
|
|
||||||
|
|
||||||
//Auth includes:
|
//Auth includes:
|
||||||
#include "AuthPackets.h"
|
#include "AuthPackets.h"
|
||||||
#include "eConnectionType.h"
|
#include "dMessageIdentifiers.h"
|
||||||
#include "MessageType/Server.h"
|
|
||||||
#include "MessageType/Auth.h"
|
|
||||||
|
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Server.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace Game {
|
namespace Game {
|
||||||
Logger* logger = nullptr;
|
dLogger* logger = nullptr;
|
||||||
dServer* server = nullptr;
|
dServer* server = nullptr;
|
||||||
dConfig* config = nullptr;
|
dConfig* config = nullptr;
|
||||||
Game::signal_t lastSignal = 0;
|
bool shouldShutdown = false;
|
||||||
std::mt19937 randomEngine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dLogger* SetupLogger();
|
||||||
void HandlePacket(Packet* packet);
|
void HandlePacket(Packet* packet);
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
@@ -45,23 +38,29 @@ int main(int argc, char** argv) {
|
|||||||
Diagnostics::SetProcessFileName(argv[0]);
|
Diagnostics::SetProcessFileName(argv[0]);
|
||||||
Diagnostics::Initialize();
|
Diagnostics::Initialize();
|
||||||
|
|
||||||
std::signal(SIGINT, Game::OnSignal);
|
|
||||||
std::signal(SIGTERM, Game::OnSignal);
|
|
||||||
|
|
||||||
Game::config = new dConfig("authconfig.ini");
|
|
||||||
|
|
||||||
//Create all the objects we need to run our service:
|
//Create all the objects we need to run our service:
|
||||||
Server::SetupLogger("AuthServer");
|
Game::logger = SetupLogger();
|
||||||
if (!Game::logger) return EXIT_FAILURE;
|
if (!Game::logger) return EXIT_FAILURE;
|
||||||
|
|
||||||
LOG("Starting Auth server...");
|
//Read our config:
|
||||||
LOG("Version: %s", PROJECT_VERSION);
|
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string());
|
||||||
LOG("Compiled on: %s", __TIMESTAMP__);
|
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
|
||||||
|
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
|
||||||
|
|
||||||
|
Game::logger->Log("AuthServer", "Starting Auth server...");
|
||||||
|
Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||||
|
Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__);
|
||||||
|
|
||||||
|
//Connect to the MySQL Database
|
||||||
|
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||||
|
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||||
|
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||||
|
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Database::Connect();
|
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||||
} catch (sql::SQLException& ex) {
|
} catch (sql::SQLException& ex) {
|
||||||
LOG("Got an error while connecting to the database: %s", ex.what());
|
Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s", ex.what());
|
||||||
Database::Destroy("AuthServer");
|
Database::Destroy("AuthServer");
|
||||||
delete Game::server;
|
delete Game::server;
|
||||||
delete Game::logger;
|
delete Game::logger;
|
||||||
@@ -71,26 +70,23 @@ int main(int argc, char** argv) {
|
|||||||
//Find out the master's IP:
|
//Find out the master's IP:
|
||||||
std::string masterIP;
|
std::string masterIP;
|
||||||
uint32_t masterPort = 1500;
|
uint32_t masterPort = 1500;
|
||||||
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
auto res = stmt->executeQuery();
|
||||||
if (masterInfo) {
|
while (res->next()) {
|
||||||
masterIP = masterInfo->ip;
|
masterIP = res->getString(1).c_str();
|
||||||
masterPort = masterInfo->port;
|
masterPort = res->getInt(2);
|
||||||
}
|
}
|
||||||
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
|
|
||||||
|
|
||||||
Game::randomEngine = std::mt19937(time(0));
|
delete res;
|
||||||
|
delete stmt;
|
||||||
|
|
||||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||||
std::string ourIP = "localhost";
|
uint32_t maxClients = 50;
|
||||||
const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
|
uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default.
|
||||||
|
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||||
|
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||||
|
|
||||||
//LU client is hardcoded to use this for auth port, so I'm making it the default.
|
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
|
||||||
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("auth_server_port")).value_or(1001);
|
|
||||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
|
||||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
|
||||||
|
|
||||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
|
|
||||||
|
|
||||||
//Run it until server gets a kill message from Master:
|
//Run it until server gets a kill message from Master:
|
||||||
auto t = std::chrono::high_resolution_clock::now();
|
auto t = std::chrono::high_resolution_clock::now();
|
||||||
@@ -101,18 +97,13 @@ int main(int argc, char** argv) {
|
|||||||
uint32_t framesSinceMasterDisconnect = 0;
|
uint32_t framesSinceMasterDisconnect = 0;
|
||||||
uint32_t framesSinceLastSQLPing = 0;
|
uint32_t framesSinceLastSQLPing = 0;
|
||||||
|
|
||||||
AuthPackets::LoadClaimCodes();
|
while (!Game::shouldShutdown) {
|
||||||
|
|
||||||
Game::logger->Flush(); // once immediately before main loop
|
|
||||||
while (!Game::ShouldShutdown()) {
|
|
||||||
//Check if we're still connected to master:
|
//Check if we're still connected to master:
|
||||||
if (!Game::server->GetIsConnectedToMaster()) {
|
if (!Game::server->GetIsConnectedToMaster()) {
|
||||||
framesSinceMasterDisconnect++;
|
framesSinceMasterDisconnect++;
|
||||||
|
|
||||||
if (framesSinceMasterDisconnect >= authFramerate) {
|
if (framesSinceMasterDisconnect >= authFramerate)
|
||||||
LOG("No connection to master!");
|
|
||||||
break; //Exit our loop, shut down.
|
break; //Exit our loop, shut down.
|
||||||
}
|
|
||||||
} else framesSinceMasterDisconnect = 0;
|
} else framesSinceMasterDisconnect = 0;
|
||||||
|
|
||||||
//In world we'd update our other systems here.
|
//In world we'd update our other systems here.
|
||||||
@@ -137,12 +128,16 @@ int main(int argc, char** argv) {
|
|||||||
//Find out the master's IP for absolutely no reason:
|
//Find out the master's IP for absolutely no reason:
|
||||||
std::string masterIP;
|
std::string masterIP;
|
||||||
uint32_t masterPort;
|
uint32_t masterPort;
|
||||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||||
if (masterInfo) {
|
auto res = stmt->executeQuery();
|
||||||
masterIP = masterInfo->ip;
|
while (res->next()) {
|
||||||
masterPort = masterInfo->port;
|
masterIP = res->getString(1).c_str();
|
||||||
|
masterPort = res->getInt(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete res;
|
||||||
|
delete stmt;
|
||||||
|
|
||||||
framesSinceLastSQLPing = 0;
|
framesSinceLastSQLPing = 0;
|
||||||
} else framesSinceLastSQLPing++;
|
} else framesSinceLastSQLPing++;
|
||||||
|
|
||||||
@@ -151,7 +146,6 @@ int main(int argc, char** argv) {
|
|||||||
std::this_thread::sleep_until(t);
|
std::this_thread::sleep_until(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("Exited Main Loop! (signal %d)", Game::lastSignal);
|
|
||||||
//Delete our objects here:
|
//Delete our objects here:
|
||||||
Database::Destroy("AuthServer");
|
Database::Destroy("AuthServer");
|
||||||
delete Game::server;
|
delete Game::server;
|
||||||
@@ -161,16 +155,26 @@ int main(int argc, char** argv) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandlePacket(Packet* packet) {
|
dLogger* SetupLogger() {
|
||||||
if (packet->length < 4) return;
|
std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/AuthServer_" + std::to_string(time(nullptr)) + ".log")).string();
|
||||||
|
bool logToConsole = false;
|
||||||
|
bool logDebugStatements = false;
|
||||||
|
#ifdef _DEBUG
|
||||||
|
logToConsole = true;
|
||||||
|
logDebugStatements = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return new dLogger(logPath, logToConsole, logDebugStatements);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlePacket(Packet* packet) {
|
||||||
if (packet->data[0] == ID_USER_PACKET_ENUM) {
|
if (packet->data[0] == ID_USER_PACKET_ENUM) {
|
||||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::SERVER) {
|
if (packet->data[1] == SERVER) {
|
||||||
if (static_cast<MessageType::Server>(packet->data[3]) == MessageType::Server::VERSION_CONFIRM) {
|
if (packet->data[3] == MSG_SERVER_VERSION_CONFIRM) {
|
||||||
AuthPackets::HandleHandshake(Game::server, packet);
|
AuthPackets::HandleHandshake(Game::server, packet);
|
||||||
}
|
}
|
||||||
} else if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::AUTH) {
|
} else if (packet->data[1] == AUTH) {
|
||||||
if (static_cast<MessageType::Auth>(packet->data[3]) == MessageType::Auth::LOGIN_REQUEST) {
|
if (packet->data[3] == MSG_AUTH_LOGIN_REQUEST) {
|
||||||
AuthPackets::HandleLoginRequest(Game::server, packet);
|
AuthPackets::HandleLoginRequest(Game::server, packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,2 @@
|
|||||||
add_executable(AuthServer "AuthServer.cpp")
|
add_executable(AuthServer "AuthServer.cpp")
|
||||||
|
target_link_libraries(AuthServer ${COMMON_LIBRARIES})
|
||||||
target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer)
|
|
||||||
|
|
||||||
target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer)
|
|
||||||
|
|
||||||
add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
|
||||||
|
|||||||
@@ -8,11 +8,10 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "eGameMasterLevel.h"
|
|
||||||
|
|
||||||
using namespace dChatFilterDCF;
|
using namespace dChatFilterDCF;
|
||||||
|
|
||||||
@@ -27,16 +26,20 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
|
|||||||
ExportWordlistToDCF(filepath + ".dcf", true);
|
ExportWordlistToDCF(filepath + ".dcf", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BinaryIO::DoesFileExist("blocklist.dcf")) {
|
if (BinaryIO::DoesFileExist("blacklist.dcf")) {
|
||||||
ReadWordlistDCF("blocklist.dcf", false);
|
ReadWordlistDCF("blacklist.dcf", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Read player names that are ok as well:
|
//Read player names that are ok as well:
|
||||||
auto approvedNames = Database::Get()->GetApprovedCharacterNames();
|
auto stmt = Database::CreatePreppedStmt("select name from charinfo;");
|
||||||
for (auto& name : approvedNames) {
|
auto res = stmt->executeQuery();
|
||||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower); //Transform to lowercase
|
while (res->next()) {
|
||||||
m_ApprovedWords.push_back(CalculateHash(name));
|
std::string line = res->getString(1).c_str();
|
||||||
|
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
|
||||||
|
m_ApprovedWords.push_back(CalculateHash(line));
|
||||||
}
|
}
|
||||||
|
delete res;
|
||||||
|
delete stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
dChatFilter::~dChatFilter() {
|
dChatFilter::~dChatFilter() {
|
||||||
@@ -44,20 +47,20 @@ dChatFilter::~dChatFilter() {
|
|||||||
m_DeniedWords.clear();
|
m_DeniedWords.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool allowList) {
|
void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool whiteList) {
|
||||||
std::ifstream file(filepath);
|
std::ifstream file(filepath);
|
||||||
if (file) {
|
if (file) {
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(file, line)) {
|
||||||
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
|
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
|
||||||
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
|
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
|
||||||
if (allowList) m_ApprovedWords.push_back(CalculateHash(line));
|
if (whiteList) m_ApprovedWords.push_back(CalculateHash(line));
|
||||||
else m_DeniedWords.push_back(CalculateHash(line));
|
else m_DeniedWords.push_back(CalculateHash(line));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
|
bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
|
||||||
std::ifstream file(filepath, std::ios::binary);
|
std::ifstream file(filepath, std::ios::binary);
|
||||||
if (file) {
|
if (file) {
|
||||||
fileHeader hdr;
|
fileHeader hdr;
|
||||||
@@ -70,13 +73,13 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
|
|||||||
if (hdr.formatVersion == formatVersion) {
|
if (hdr.formatVersion == formatVersion) {
|
||||||
size_t wordsToRead = 0;
|
size_t wordsToRead = 0;
|
||||||
BinaryIO::BinaryRead(file, wordsToRead);
|
BinaryIO::BinaryRead(file, wordsToRead);
|
||||||
if (allowList) m_ApprovedWords.reserve(wordsToRead);
|
if (whiteList) m_ApprovedWords.reserve(wordsToRead);
|
||||||
else m_DeniedWords.reserve(wordsToRead);
|
else m_DeniedWords.reserve(wordsToRead);
|
||||||
|
|
||||||
size_t word = 0;
|
size_t word = 0;
|
||||||
for (size_t i = 0; i < wordsToRead; ++i) {
|
for (size_t i = 0; i < wordsToRead; ++i) {
|
||||||
BinaryIO::BinaryRead(file, word);
|
BinaryIO::BinaryRead(file, word);
|
||||||
if (allowList) m_ApprovedWords.push_back(word);
|
if (whiteList) m_ApprovedWords.push_back(word);
|
||||||
else m_DeniedWords.push_back(word);
|
else m_DeniedWords.push_back(word);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,14 +93,14 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowList) {
|
void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool whiteList) {
|
||||||
std::ofstream file(filepath, std::ios::binary | std::ios_base::out);
|
std::ofstream file(filepath, std::ios::binary | std::ios_base::out);
|
||||||
if (file) {
|
if (file) {
|
||||||
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::header));
|
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::header));
|
||||||
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::formatVersion));
|
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::formatVersion));
|
||||||
BinaryIO::BinaryWrite(file, size_t(allowList ? m_ApprovedWords.size() : m_DeniedWords.size()));
|
BinaryIO::BinaryWrite(file, size_t(whiteList ? m_ApprovedWords.size() : m_DeniedWords.size()));
|
||||||
|
|
||||||
for (size_t word : allowList ? m_ApprovedWords : m_DeniedWords) {
|
for (size_t word : whiteList ? m_ApprovedWords : m_DeniedWords) {
|
||||||
BinaryIO::BinaryWrite(file, word);
|
BinaryIO::BinaryWrite(file, word);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,10 +108,10 @@ void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowLis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) {
|
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, int gmLevel, bool whiteList) {
|
||||||
if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true.
|
if (gmLevel > GAME_MASTER_LEVEL_FORUM_MODERATOR) return { }; //If anything but a forum mod, return true.
|
||||||
if (message.empty()) return { };
|
if (message.empty()) return { };
|
||||||
if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } };
|
if (!whiteList && m_DeniedWords.empty()) return { { 0, message.length() } };
|
||||||
|
|
||||||
std::stringstream sMessage(message);
|
std::stringstream sMessage(message);
|
||||||
std::string segment;
|
std::string segment;
|
||||||
@@ -126,21 +129,21 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
|
|||||||
|
|
||||||
size_t hash = CalculateHash(segment);
|
size_t hash = CalculateHash(segment);
|
||||||
|
|
||||||
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) {
|
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && whiteList) {
|
||||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) {
|
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && whiteList) {
|
||||||
m_UserUnapprovedWordCache.push_back(hash);
|
m_UserUnapprovedWordCache.push_back(hash);
|
||||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) {
|
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !whiteList) {
|
||||||
m_UserUnapprovedWordCache.push_back(hash);
|
m_UserUnapprovedWordCache.push_back(hash);
|
||||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
position += originalSegment.length() + 1;
|
position += segment.length() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return listOfBadSegments;
|
return listOfBadSegments;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
enum class eGameMasterLevel : uint8_t;
|
|
||||||
namespace dChatFilterDCF {
|
namespace dChatFilterDCF {
|
||||||
static const uint32_t header = ('D' + ('C' << 8) + ('F' << 16) + ('B' << 24));
|
static const uint32_t header = ('D' + ('C' << 8) + ('F' << 16) + ('B' << 24));
|
||||||
static const uint32_t formatVersion = 2;
|
static const uint32_t formatVersion = 2;
|
||||||
@@ -21,10 +20,10 @@ public:
|
|||||||
dChatFilter(const std::string& filepath, bool dontGenerateDCF);
|
dChatFilter(const std::string& filepath, bool dontGenerateDCF);
|
||||||
~dChatFilter();
|
~dChatFilter();
|
||||||
|
|
||||||
void ReadWordlistPlaintext(const std::string& filepath, bool allowList);
|
void ReadWordlistPlaintext(const std::string& filepath, bool whiteList);
|
||||||
bool ReadWordlistDCF(const std::string& filepath, bool allowList);
|
bool ReadWordlistDCF(const std::string& filepath, bool whiteList);
|
||||||
void ExportWordlistToDCF(const std::string& filepath, bool allowList);
|
void ExportWordlistToDCF(const std::string& filepath, bool whiteList);
|
||||||
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true);
|
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, int gmLevel, bool whiteList = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_DontGenerateDCF;
|
bool m_DontGenerateDCF;
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
set(DCHATSERVER_SOURCES
|
set(DCHATSERVER_SOURCES
|
||||||
"ChatIgnoreList.cpp"
|
|
||||||
"ChatPacketHandler.cpp"
|
"ChatPacketHandler.cpp"
|
||||||
"PlayerContainer.cpp"
|
"PlayerContainer.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(ChatServer "ChatServer.cpp")
|
add_executable(ChatServer "ChatServer.cpp")
|
||||||
target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter")
|
|
||||||
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
|
||||||
|
|
||||||
add_library(dChatServer ${DCHATSERVER_SOURCES})
|
add_library(dChatServer ${DCHATSERVER_SOURCES})
|
||||||
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
|
|
||||||
|
|
||||||
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
|
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
|
||||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer)
|
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer)
|
||||||
|
|
||||||
|
|||||||
@@ -1,171 +0,0 @@
|
|||||||
#include "ChatIgnoreList.h"
|
|
||||||
#include "PlayerContainer.h"
|
|
||||||
#include "MessageType/Chat.h"
|
|
||||||
#include "BitStreamUtils.h"
|
|
||||||
#include "Game.h"
|
|
||||||
#include "Logger.h"
|
|
||||||
#include "eObjectBits.h"
|
|
||||||
|
|
||||||
#include "Database.h"
|
|
||||||
|
|
||||||
// A note to future readers, The client handles all the actual ignoring logic:
|
|
||||||
// not allowing teams, rejecting DMs, friends requets etc.
|
|
||||||
// The only thing not auto-handled is instance activities force joining the team on the server.
|
|
||||||
|
|
||||||
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
|
||||||
bitStream.Write(receivingPlayer);
|
|
||||||
|
|
||||||
//portion that will get routed:
|
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatIgnoreList::GetIgnoreList(Packet* packet) {
|
|
||||||
CINSTREAM_SKIP_HEADER;
|
|
||||||
LWOOBJID playerId;
|
|
||||||
inStream.Read(playerId);
|
|
||||||
|
|
||||||
auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId);
|
|
||||||
if (!receiver) {
|
|
||||||
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!receiver.ignoredPlayers.empty()) {
|
|
||||||
LOG_DEBUG("Player %llu already has an ignore list, but is requesting it again.", playerId);
|
|
||||||
} else {
|
|
||||||
auto ignoreList = Database::Get()->GetIgnoreList(static_cast<uint32_t>(playerId));
|
|
||||||
if (ignoreList.empty()) {
|
|
||||||
LOG_DEBUG("Player %llu has no ignores", playerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& ignoredPlayer : ignoreList) {
|
|
||||||
receiver.ignoredPlayers.emplace_back(ignoredPlayer.name, ignoredPlayer.id);
|
|
||||||
GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::CHARACTER);
|
|
||||||
GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::PERSISTENT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CBITSTREAM;
|
|
||||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::GET_IGNORE);
|
|
||||||
|
|
||||||
bitStream.Write<uint8_t>(false); // Probably is Is Free Trial, but we don't care about that
|
|
||||||
bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment
|
|
||||||
|
|
||||||
bitStream.Write<uint16_t>(receiver.ignoredPlayers.size());
|
|
||||||
for (const auto& ignoredPlayer : receiver.ignoredPlayers) {
|
|
||||||
bitStream.Write(ignoredPlayer.playerId);
|
|
||||||
bitStream.Write(LUWString(ignoredPlayer.playerName, 36));
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::server->Send(bitStream, packet->systemAddress, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatIgnoreList::AddIgnore(Packet* packet) {
|
|
||||||
CINSTREAM_SKIP_HEADER;
|
|
||||||
LWOOBJID playerId;
|
|
||||||
inStream.Read(playerId);
|
|
||||||
|
|
||||||
auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId);
|
|
||||||
if (!receiver) {
|
|
||||||
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int32_t MAX_IGNORES = 32;
|
|
||||||
if (receiver.ignoredPlayers.size() > MAX_IGNORES) {
|
|
||||||
LOG_DEBUG("Player %llu has too many ignores", playerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
|
|
||||||
|
|
||||||
LUWString toIgnoreName;
|
|
||||||
inStream.Read(toIgnoreName);
|
|
||||||
std::string toIgnoreStr = toIgnoreName.GetAsString();
|
|
||||||
|
|
||||||
CBITSTREAM;
|
|
||||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::ADD_IGNORE);
|
|
||||||
|
|
||||||
// Check if the player exists
|
|
||||||
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
|
|
||||||
if (toIgnoreStr == receiver.playerName || toIgnoreStr.find("[GM]") == 0) {
|
|
||||||
LOG_DEBUG("Player %llu tried to ignore themselves", playerId);
|
|
||||||
|
|
||||||
bitStream.Write(ChatIgnoreList::AddResponse::GENERAL_ERROR);
|
|
||||||
} else if (std::count(receiver.ignoredPlayers.begin(), receiver.ignoredPlayers.end(), toIgnoreStr) > 0) {
|
|
||||||
LOG_DEBUG("Player %llu is already ignoring %s", playerId, toIgnoreStr.c_str());
|
|
||||||
|
|
||||||
bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED);
|
|
||||||
} else {
|
|
||||||
// Get the playerId falling back to query if not online
|
|
||||||
const auto& playerData = Game::playerContainer.GetPlayerData(toIgnoreStr);
|
|
||||||
if (!playerData) {
|
|
||||||
// Fall back to query
|
|
||||||
auto player = Database::Get()->GetCharacterInfo(toIgnoreStr);
|
|
||||||
if (!player || player->name != toIgnoreStr) {
|
|
||||||
LOG_DEBUG("Player %s not found", toIgnoreStr.c_str());
|
|
||||||
} else {
|
|
||||||
ignoredPlayerId = player->id;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ignoredPlayerId = playerData.playerID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoredPlayerId != LWOOBJID_EMPTY) {
|
|
||||||
Database::Get()->AddIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(ignoredPlayerId));
|
|
||||||
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER);
|
|
||||||
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT);
|
|
||||||
|
|
||||||
receiver.ignoredPlayers.emplace_back(toIgnoreStr, ignoredPlayerId);
|
|
||||||
LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str());
|
|
||||||
|
|
||||||
bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS);
|
|
||||||
} else {
|
|
||||||
bitStream.Write(ChatIgnoreList::AddResponse::PLAYER_NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LUWString playerNameSend(toIgnoreStr, 33);
|
|
||||||
bitStream.Write(playerNameSend);
|
|
||||||
bitStream.Write(ignoredPlayerId);
|
|
||||||
|
|
||||||
Game::server->Send(bitStream, packet->systemAddress, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatIgnoreList::RemoveIgnore(Packet* packet) {
|
|
||||||
CINSTREAM_SKIP_HEADER;
|
|
||||||
LWOOBJID playerId;
|
|
||||||
inStream.Read(playerId);
|
|
||||||
|
|
||||||
auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId);
|
|
||||||
if (!receiver) {
|
|
||||||
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
|
|
||||||
|
|
||||||
LUWString removedIgnoreName;
|
|
||||||
inStream.Read(removedIgnoreName);
|
|
||||||
std::string removedIgnoreStr = removedIgnoreName.GetAsString();
|
|
||||||
|
|
||||||
auto toRemove = std::remove(receiver.ignoredPlayers.begin(), receiver.ignoredPlayers.end(), removedIgnoreStr);
|
|
||||||
if (toRemove == receiver.ignoredPlayers.end()) {
|
|
||||||
LOG_DEBUG("Player %llu is not ignoring %s", playerId, removedIgnoreStr.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Database::Get()->RemoveIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(toRemove->playerId));
|
|
||||||
receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end());
|
|
||||||
|
|
||||||
CBITSTREAM;
|
|
||||||
WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::REMOVE_IGNORE);
|
|
||||||
|
|
||||||
bitStream.Write<int8_t>(0);
|
|
||||||
LUWString playerNameSend(removedIgnoreStr, 33);
|
|
||||||
bitStream.Write(playerNameSend);
|
|
||||||
|
|
||||||
Game::server->Send(bitStream, packet->systemAddress, false);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#ifndef __CHATIGNORELIST__H__
|
|
||||||
#define __CHATIGNORELIST__H__
|
|
||||||
|
|
||||||
struct Packet;
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace ChatIgnoreList {
|
|
||||||
void GetIgnoreList(Packet* packet);
|
|
||||||
void AddIgnore(Packet* packet);
|
|
||||||
void RemoveIgnore(Packet* packet);
|
|
||||||
|
|
||||||
enum class Response : uint8_t {
|
|
||||||
ADD_IGNORE = 32,
|
|
||||||
REMOVE_IGNORE = 33,
|
|
||||||
GET_IGNORE = 34,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class AddResponse : uint8_t {
|
|
||||||
SUCCESS,
|
|
||||||
ALREADY_IGNORED,
|
|
||||||
PLAYER_NOT_FOUND,
|
|
||||||
GENERAL_ERROR,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //!__CHATIGNORELIST__H__
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -4,58 +4,16 @@
|
|||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
|
|
||||||
struct PlayerData;
|
struct PlayerData;
|
||||||
|
enum class AddFriendResponseType : uint8_t;
|
||||||
enum class eAddFriendResponseType : uint8_t;
|
|
||||||
|
|
||||||
enum class eChatChannel : uint8_t {
|
|
||||||
SYSTEMNOTIFY = 0,
|
|
||||||
SYSTEMWARNING,
|
|
||||||
SYSTEMERROR,
|
|
||||||
BROADCAST,
|
|
||||||
LOCAL,
|
|
||||||
LOCALNOANIM,
|
|
||||||
EMOTE,
|
|
||||||
PRIVATE_CHAT,
|
|
||||||
TEAM,
|
|
||||||
TEAMLOCAL,
|
|
||||||
GUILD,
|
|
||||||
GUILDNOTIFY,
|
|
||||||
PROPERTY,
|
|
||||||
ADMIN,
|
|
||||||
COMBATDAMAGE,
|
|
||||||
COMBATHEALING,
|
|
||||||
COMBATLOOT,
|
|
||||||
COMBATEXP,
|
|
||||||
COMBATDEATH,
|
|
||||||
GENERAL,
|
|
||||||
TRADE,
|
|
||||||
LFG,
|
|
||||||
USER
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum class eChatMessageResponseCode : uint8_t {
|
|
||||||
SENT = 0,
|
|
||||||
NOTONLINE,
|
|
||||||
GENERALERROR,
|
|
||||||
RECEIVEDNEWWHISPER,
|
|
||||||
NOTFRIENDS,
|
|
||||||
SENDERFREETRIAL,
|
|
||||||
RECEIVERFREETRIAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace ChatPacketHandler {
|
namespace ChatPacketHandler {
|
||||||
void HandleFriendlistRequest(Packet* packet);
|
void HandleFriendlistRequest(Packet* packet);
|
||||||
void HandleFriendRequest(Packet* packet);
|
void HandleFriendRequest(Packet* packet);
|
||||||
void HandleFriendResponse(Packet* packet);
|
void HandleFriendResponse(Packet* packet);
|
||||||
void HandleRemoveFriend(Packet* packet);
|
void HandleRemoveFriend(Packet* packet);
|
||||||
void HandleGMLevelUpdate(Packet* packet);
|
|
||||||
void HandleWho(Packet* packet);
|
|
||||||
void HandleShowAll(Packet* packet);
|
|
||||||
|
|
||||||
void HandleChatMessage(Packet* packet);
|
void HandleChatMessage(Packet* packet);
|
||||||
void HandlePrivateChatMessage(Packet* packet);
|
void HandlePrivateChatMessage(Packet* packet);
|
||||||
void SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode);
|
|
||||||
|
|
||||||
void HandleTeamInvite(Packet* packet);
|
void HandleTeamInvite(Packet* packet);
|
||||||
void HandleTeamInviteResponse(Packet* packet);
|
void HandleTeamInviteResponse(Packet* packet);
|
||||||
@@ -65,18 +23,18 @@ namespace ChatPacketHandler {
|
|||||||
void HandleTeamLootOption(Packet* packet);
|
void HandleTeamLootOption(Packet* packet);
|
||||||
void HandleTeamStatusRequest(Packet* packet);
|
void HandleTeamStatusRequest(Packet* packet);
|
||||||
|
|
||||||
void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender);
|
void SendTeamInvite(PlayerData* receiver, PlayerData* sender);
|
||||||
void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
|
void SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
|
||||||
void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
|
void SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
|
||||||
void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID);
|
void SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID);
|
||||||
void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
|
void SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
|
||||||
void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
|
void SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
|
||||||
void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
|
void SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
|
||||||
|
|
||||||
//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
|
//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
|
||||||
void SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend);
|
void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend);
|
||||||
|
|
||||||
void SendFriendRequest(const PlayerData& receiver, const PlayerData& sender);
|
void SendFriendRequest(PlayerData* receiver, PlayerData* sender);
|
||||||
void SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
|
void SendFriendResponse(PlayerData* receiver, PlayerData* sender, AddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
|
||||||
void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful);
|
void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,41 +6,37 @@
|
|||||||
//DLU Includes:
|
//DLU Includes:
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
#include "dServer.h"
|
#include "dServer.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
|
#include "dMessageIdentifiers.h"
|
||||||
#include "dChatFilter.h"
|
#include "dChatFilter.h"
|
||||||
#include "Diagnostics.h"
|
#include "Diagnostics.h"
|
||||||
#include "AssetManager.h"
|
#include "AssetManager.h"
|
||||||
#include "BinaryPathFinder.h"
|
#include "BinaryPathFinder.h"
|
||||||
#include "eConnectionType.h"
|
|
||||||
#include "PlayerContainer.h"
|
#include "PlayerContainer.h"
|
||||||
#include "ChatPacketHandler.h"
|
#include "ChatPacketHandler.h"
|
||||||
#include "MessageType/Chat.h"
|
|
||||||
#include "MessageType/World.h"
|
|
||||||
#include "ChatIgnoreList.h"
|
|
||||||
#include "StringifiedEnum.h"
|
|
||||||
|
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Server.h"
|
|
||||||
|
|
||||||
//RakNet includes:
|
//RakNet includes:
|
||||||
#include "RakNetDefines.h"
|
#include "RakNetDefines.h"
|
||||||
#include "MessageIdentifiers.h"
|
|
||||||
|
|
||||||
namespace Game {
|
namespace Game {
|
||||||
Logger* logger = nullptr;
|
dLogger* logger = nullptr;
|
||||||
dServer* server = nullptr;
|
dServer* server = nullptr;
|
||||||
dConfig* config = nullptr;
|
dConfig* config = nullptr;
|
||||||
dChatFilter* chatFilter = nullptr;
|
dChatFilter* chatFilter = nullptr;
|
||||||
AssetManager* assetManager = nullptr;
|
AssetManager* assetManager = nullptr;
|
||||||
Game::signal_t lastSignal = 0;
|
bool shouldShutdown = false;
|
||||||
std::mt19937 randomEngine;
|
|
||||||
PlayerContainer playerContainer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dLogger* SetupLogger();
|
||||||
void HandlePacket(Packet* packet);
|
void HandlePacket(Packet* packet);
|
||||||
|
|
||||||
|
PlayerContainer playerContainer;
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
constexpr uint32_t chatFramerate = mediumFramerate;
|
constexpr uint32_t chatFramerate = mediumFramerate;
|
||||||
constexpr uint32_t chatFrameDelta = mediumFrameDelta;
|
constexpr uint32_t chatFrameDelta = mediumFrameDelta;
|
||||||
@@ -48,20 +44,18 @@ int main(int argc, char** argv) {
|
|||||||
Diagnostics::SetProcessFileName(argv[0]);
|
Diagnostics::SetProcessFileName(argv[0]);
|
||||||
Diagnostics::Initialize();
|
Diagnostics::Initialize();
|
||||||
|
|
||||||
std::signal(SIGINT, Game::OnSignal);
|
|
||||||
std::signal(SIGTERM, Game::OnSignal);
|
|
||||||
|
|
||||||
Game::config = new dConfig("chatconfig.ini");
|
|
||||||
|
|
||||||
//Create all the objects we need to run our service:
|
//Create all the objects we need to run our service:
|
||||||
Server::SetupLogger("ChatServer");
|
Game::logger = SetupLogger();
|
||||||
if (!Game::logger) return EXIT_FAILURE;
|
if (!Game::logger) return EXIT_FAILURE;
|
||||||
|
|
||||||
//Read our config:
|
//Read our config:
|
||||||
|
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string());
|
||||||
|
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
|
||||||
|
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
|
||||||
|
|
||||||
LOG("Starting Chat server...");
|
Game::logger->Log("ChatServer", "Starting Chat server...");
|
||||||
LOG("Version: %s", PROJECT_VERSION);
|
Game::logger->Log("ChatServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||||
LOG("Compiled on: %s", __TIMESTAMP__);
|
Game::logger->Log("ChatServer", "Compiled on: %s", __TIMESTAMP__);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::string clientPathStr = Game::config->GetValue("client_location");
|
std::string clientPathStr = Game::config->GetValue("client_location");
|
||||||
@@ -73,16 +67,21 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
Game::assetManager = new AssetManager(clientPath);
|
Game::assetManager = new AssetManager(clientPath);
|
||||||
} catch (std::runtime_error& ex) {
|
} catch (std::runtime_error& ex) {
|
||||||
LOG("Got an error while setting up assets: %s", ex.what());
|
Game::logger->Log("ChatServer", "Got an error while setting up assets: %s", ex.what());
|
||||||
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Connect to the MySQL Database
|
//Connect to the MySQL Database
|
||||||
|
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||||
|
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||||
|
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||||
|
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Database::Connect();
|
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||||
} catch (sql::SQLException& ex) {
|
} catch (sql::SQLException& ex) {
|
||||||
LOG("Got an error while connecting to the database: %s", ex.what());
|
Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s", ex.what());
|
||||||
Database::Destroy("ChatServer");
|
Database::Destroy("ChatServer");
|
||||||
delete Game::server;
|
delete Game::server;
|
||||||
delete Game::logger;
|
delete Game::logger;
|
||||||
@@ -92,26 +91,25 @@ int main(int argc, char** argv) {
|
|||||||
//Find out the master's IP:
|
//Find out the master's IP:
|
||||||
std::string masterIP;
|
std::string masterIP;
|
||||||
uint32_t masterPort = 1000;
|
uint32_t masterPort = 1000;
|
||||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||||
if (masterInfo) {
|
auto res = stmt->executeQuery();
|
||||||
masterIP = masterInfo->ip;
|
while (res->next()) {
|
||||||
masterPort = masterInfo->port;
|
masterIP = res->getString(1).c_str();
|
||||||
|
masterPort = res->getInt(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete res;
|
||||||
|
delete stmt;
|
||||||
|
|
||||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||||
std::string ourIP = "localhost";
|
uint32_t maxClients = 50;
|
||||||
const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
|
uint32_t ourPort = 1501;
|
||||||
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("chat_server_port")).value_or(2005);
|
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
|
||||||
|
|
||||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
|
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
|
||||||
|
|
||||||
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
|
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
|
|
||||||
|
|
||||||
Game::randomEngine = std::mt19937(time(0));
|
|
||||||
|
|
||||||
Game::playerContainer.Initialize();
|
|
||||||
|
|
||||||
//Run it until server gets a kill message from Master:
|
//Run it until server gets a kill message from Master:
|
||||||
auto t = std::chrono::high_resolution_clock::now();
|
auto t = std::chrono::high_resolution_clock::now();
|
||||||
@@ -122,8 +120,7 @@ int main(int argc, char** argv) {
|
|||||||
uint32_t framesSinceMasterDisconnect = 0;
|
uint32_t framesSinceMasterDisconnect = 0;
|
||||||
uint32_t framesSinceLastSQLPing = 0;
|
uint32_t framesSinceLastSQLPing = 0;
|
||||||
|
|
||||||
Game::logger->Flush(); // once immediately before main loop
|
while (!Game::shouldShutdown) {
|
||||||
while (!Game::ShouldShutdown()) {
|
|
||||||
//Check if we're still connected to master:
|
//Check if we're still connected to master:
|
||||||
if (!Game::server->GetIsConnectedToMaster()) {
|
if (!Game::server->GetIsConnectedToMaster()) {
|
||||||
framesSinceMasterDisconnect++;
|
framesSinceMasterDisconnect++;
|
||||||
@@ -154,13 +151,16 @@ int main(int argc, char** argv) {
|
|||||||
//Find out the master's IP for absolutely no reason:
|
//Find out the master's IP for absolutely no reason:
|
||||||
std::string masterIP;
|
std::string masterIP;
|
||||||
uint32_t masterPort;
|
uint32_t masterPort;
|
||||||
|
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
auto res = stmt->executeQuery();
|
||||||
if (masterInfo) {
|
while (res->next()) {
|
||||||
masterIP = masterInfo->ip;
|
masterIP = res->getString(1).c_str();
|
||||||
masterPort = masterInfo->port;
|
masterPort = res->getInt(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete res;
|
||||||
|
delete stmt;
|
||||||
|
|
||||||
framesSinceLastSQLPing = 0;
|
framesSinceLastSQLPing = 0;
|
||||||
} else framesSinceLastSQLPing++;
|
} else framesSinceLastSQLPing++;
|
||||||
|
|
||||||
@@ -178,169 +178,135 @@ int main(int argc, char** argv) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dLogger* SetupLogger() {
|
||||||
|
std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/ChatServer_" + std::to_string(time(nullptr)) + ".log")).string();
|
||||||
|
bool logToConsole = false;
|
||||||
|
bool logDebugStatements = false;
|
||||||
|
#ifdef _DEBUG
|
||||||
|
logToConsole = true;
|
||||||
|
logDebugStatements = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return new dLogger(logPath, logToConsole, logDebugStatements);
|
||||||
|
}
|
||||||
|
|
||||||
void HandlePacket(Packet* packet) {
|
void HandlePacket(Packet* packet) {
|
||||||
if (packet->length < 1) return;
|
|
||||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
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.");
|
Game::logger->Log("ChatServer", "A server has disconnected, erasing their connected players from the list.");
|
||||||
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
}
|
||||||
LOG("A server is connecting, awaiting user list.");
|
|
||||||
} else if (packet->length < 4 || packet->data[0] != ID_USER_PACKET_ENUM) return; // Nothing left to process or not the right packet type
|
|
||||||
|
|
||||||
CINSTREAM;
|
if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
||||||
inStream.SetReadOffset(BYTES_TO_BITS(1));
|
Game::logger->Log("ChatServer", "A server is connecting, awaiting user list.");
|
||||||
|
}
|
||||||
|
|
||||||
eConnectionType connection;
|
if (packet->data[1] == CHAT_INTERNAL) {
|
||||||
MessageType::Chat chatMessageID;
|
switch (packet->data[3]) {
|
||||||
|
case MSG_CHAT_INTERNAL_PLAYER_ADDED_NOTIFICATION:
|
||||||
inStream.Read(connection);
|
playerContainer.InsertPlayer(packet);
|
||||||
if (connection != eConnectionType::CHAT) return;
|
|
||||||
inStream.Read(chatMessageID);
|
|
||||||
|
|
||||||
switch (chatMessageID) {
|
|
||||||
case MessageType::Chat::GM_MUTE:
|
|
||||||
Game::playerContainer.MuteUpdate(packet);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::CREATE_TEAM:
|
case MSG_CHAT_INTERNAL_PLAYER_REMOVED_NOTIFICATION:
|
||||||
Game::playerContainer.CreateTeamServer(packet);
|
playerContainer.RemovePlayer(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::GET_FRIENDS_LIST:
|
case MSG_CHAT_INTERNAL_MUTE_UPDATE:
|
||||||
|
playerContainer.MuteUpdate(packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_CHAT_INTERNAL_CREATE_TEAM:
|
||||||
|
playerContainer.CreateTeamServer(packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_CHAT_INTERNAL_ANNOUNCEMENT: {
|
||||||
|
//we just forward this packet to every connected server
|
||||||
|
CINSTREAM;
|
||||||
|
Game::server->Send(&inStream, packet->systemAddress, true); //send to everyone except origin
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Game::logger->Log("ChatServer", "Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->data[1] == CHAT) {
|
||||||
|
switch (packet->data[3]) {
|
||||||
|
case MSG_CHAT_GET_FRIENDS_LIST:
|
||||||
ChatPacketHandler::HandleFriendlistRequest(packet);
|
ChatPacketHandler::HandleFriendlistRequest(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::GET_IGNORE_LIST:
|
case MSG_CHAT_GET_IGNORE_LIST:
|
||||||
ChatIgnoreList::GetIgnoreList(packet);
|
Game::logger->Log("ChatServer", "Asked for ignore list, but is unimplemented right now.");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::ADD_IGNORE:
|
case MSG_CHAT_TEAM_GET_STATUS:
|
||||||
ChatIgnoreList::AddIgnore(packet);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MessageType::Chat::REMOVE_IGNORE:
|
|
||||||
ChatIgnoreList::RemoveIgnore(packet);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_GET_STATUS:
|
|
||||||
ChatPacketHandler::HandleTeamStatusRequest(packet);
|
ChatPacketHandler::HandleTeamStatusRequest(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::ADD_FRIEND_REQUEST:
|
case MSG_CHAT_ADD_FRIEND_REQUEST:
|
||||||
//this involves someone sending the initial request, the response is below, response as in from the other player.
|
//this involves someone sending the initial request, the response is below, response as in from the other player.
|
||||||
//We basically just check to see if this player is online or not and route the packet.
|
//We basically just check to see if this player is online or not and route the packet.
|
||||||
ChatPacketHandler::HandleFriendRequest(packet);
|
ChatPacketHandler::HandleFriendRequest(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::ADD_FRIEND_RESPONSE:
|
case MSG_CHAT_ADD_FRIEND_RESPONSE:
|
||||||
//This isn't the response a server sent, rather it is a player's response to a received request.
|
//This isn't the response a server sent, rather it is a player's response to a received request.
|
||||||
//Here, we'll actually have to add them to eachother's friend lists depending on the response code.
|
//Here, we'll actually have to add them to eachother's friend lists depending on the response code.
|
||||||
ChatPacketHandler::HandleFriendResponse(packet);
|
ChatPacketHandler::HandleFriendResponse(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::REMOVE_FRIEND:
|
case MSG_CHAT_REMOVE_FRIEND:
|
||||||
ChatPacketHandler::HandleRemoveFriend(packet);
|
ChatPacketHandler::HandleRemoveFriend(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::GENERAL_CHAT_MESSAGE:
|
case MSG_CHAT_GENERAL_CHAT_MESSAGE:
|
||||||
ChatPacketHandler::HandleChatMessage(packet);
|
ChatPacketHandler::HandleChatMessage(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::PRIVATE_CHAT_MESSAGE:
|
case MSG_CHAT_PRIVATE_CHAT_MESSAGE:
|
||||||
//This message is supposed to be echo'd to both the sender and the receiver
|
//This message is supposed to be echo'd to both the sender and the receiver
|
||||||
//BUT: they have to have different responseCodes, so we'll do some of the ol hacky wacky to fix that right up.
|
//BUT: they have to have different responseCodes, so we'll do some of the ol hacky wacky to fix that right up.
|
||||||
ChatPacketHandler::HandlePrivateChatMessage(packet);
|
ChatPacketHandler::HandlePrivateChatMessage(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_INVITE:
|
case MSG_CHAT_TEAM_INVITE:
|
||||||
ChatPacketHandler::HandleTeamInvite(packet);
|
ChatPacketHandler::HandleTeamInvite(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_INVITE_RESPONSE:
|
case MSG_CHAT_TEAM_INVITE_RESPONSE:
|
||||||
ChatPacketHandler::HandleTeamInviteResponse(packet);
|
ChatPacketHandler::HandleTeamInviteResponse(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_LEAVE:
|
case MSG_CHAT_TEAM_LEAVE:
|
||||||
ChatPacketHandler::HandleTeamLeave(packet);
|
ChatPacketHandler::HandleTeamLeave(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_SET_LEADER:
|
case MSG_CHAT_TEAM_SET_LEADER:
|
||||||
ChatPacketHandler::HandleTeamPromote(packet);
|
ChatPacketHandler::HandleTeamPromote(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_KICK:
|
case MSG_CHAT_TEAM_KICK:
|
||||||
ChatPacketHandler::HandleTeamKick(packet);
|
ChatPacketHandler::HandleTeamKick(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageType::Chat::TEAM_SET_LOOT:
|
case MSG_CHAT_TEAM_SET_LOOT:
|
||||||
ChatPacketHandler::HandleTeamLootOption(packet);
|
ChatPacketHandler::HandleTeamLootOption(packet);
|
||||||
break;
|
break;
|
||||||
case MessageType::Chat::GMLEVEL_UPDATE:
|
|
||||||
ChatPacketHandler::HandleGMLevelUpdate(packet);
|
|
||||||
break;
|
|
||||||
case MessageType::Chat::LOGIN_SESSION_NOTIFY:
|
|
||||||
Game::playerContainer.InsertPlayer(packet);
|
|
||||||
break;
|
|
||||||
case MessageType::Chat::GM_ANNOUNCE:{
|
|
||||||
// we just forward this packet to every connected server
|
|
||||||
inStream.ResetReadPointer();
|
|
||||||
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MessageType::Chat::UNEXPECTED_DISCONNECT:
|
|
||||||
Game::playerContainer.RemovePlayer(packet);
|
|
||||||
break;
|
|
||||||
case MessageType::Chat::WHO:
|
|
||||||
ChatPacketHandler::HandleWho(packet);
|
|
||||||
break;
|
|
||||||
case MessageType::Chat::SHOW_ALL:
|
|
||||||
ChatPacketHandler::HandleShowAll(packet);
|
|
||||||
break;
|
|
||||||
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
|
|
||||||
case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
|
|
||||||
case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
|
|
||||||
case MessageType::Chat::WORLD_PARCEL_RESPONSE:
|
|
||||||
case MessageType::Chat::TEAM_MISSED_INVITE_CHECK:
|
|
||||||
case MessageType::Chat::GUILD_CREATE:
|
|
||||||
case MessageType::Chat::GUILD_INVITE:
|
|
||||||
case MessageType::Chat::GUILD_INVITE_RESPONSE:
|
|
||||||
case MessageType::Chat::GUILD_LEAVE:
|
|
||||||
case MessageType::Chat::GUILD_KICK:
|
|
||||||
case MessageType::Chat::GUILD_GET_STATUS:
|
|
||||||
case MessageType::Chat::GUILD_GET_ALL:
|
|
||||||
case MessageType::Chat::BLUEPRINT_MODERATED:
|
|
||||||
case MessageType::Chat::BLUEPRINT_MODEL_READY:
|
|
||||||
case MessageType::Chat::PROPERTY_READY_FOR_APPROVAL:
|
|
||||||
case MessageType::Chat::PROPERTY_MODERATION_CHANGED:
|
|
||||||
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED:
|
|
||||||
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED_REPORT:
|
|
||||||
case MessageType::Chat::MAIL:
|
|
||||||
case MessageType::Chat::WORLD_INSTANCE_LOCATION_REQUEST:
|
|
||||||
case MessageType::Chat::REPUTATION_UPDATE:
|
|
||||||
case MessageType::Chat::SEND_CANNED_TEXT:
|
|
||||||
case MessageType::Chat::CHARACTER_NAME_CHANGE_REQUEST:
|
|
||||||
case MessageType::Chat::CSR_REQUEST:
|
|
||||||
case MessageType::Chat::CSR_REPLY:
|
|
||||||
case MessageType::Chat::GM_KICK:
|
|
||||||
case MessageType::Chat::WORLD_ROUTE_PACKET:
|
|
||||||
case MessageType::Chat::GET_ZONE_POPULATIONS:
|
|
||||||
case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE:
|
|
||||||
case MessageType::Chat::MATCH_REQUEST:
|
|
||||||
case MessageType::Chat::UGCMANIFEST_REPORT_MISSING_FILE:
|
|
||||||
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_FILE:
|
|
||||||
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
|
|
||||||
case MessageType::Chat::UGCC_REQUEST:
|
|
||||||
case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
|
|
||||||
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
|
|
||||||
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
|
||||||
case MessageType::Chat::PLAYER_READY:
|
|
||||||
case MessageType::Chat::GET_DONATION_TOTAL:
|
|
||||||
case MessageType::Chat::UPDATE_DONATION:
|
|
||||||
case MessageType::Chat::PRG_CSR_COMMAND:
|
|
||||||
case MessageType::Chat::HEARTBEAT_REQUEST_FROM_WORLD:
|
|
||||||
case MessageType::Chat::UPDATE_FREE_TRIAL_STATUS:
|
|
||||||
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
LOG("Unknown CHAT Message id: %i", chatMessageID);
|
Game::logger->Log("ChatServer", "Unknown CHAT id: %i", int(packet->data[3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->data[1] == WORLD) {
|
||||||
|
switch (packet->data[3]) {
|
||||||
|
case MSG_WORLD_CLIENT_ROUTE_PACKET: {
|
||||||
|
Game::logger->Log("ChatServer", "Routing packet from world");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Game::logger->Log("ChatServer", "Unknown World id: %i", int(packet->data[3]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,133 +3,127 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
#include "ChatPacketHandler.h"
|
#include "ChatPacketHandler.h"
|
||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
#include "BitStreamUtils.h"
|
#include "dMessageIdentifiers.h"
|
||||||
|
#include "PacketUtils.h"
|
||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "eConnectionType.h"
|
|
||||||
#include "ChatPackets.h"
|
|
||||||
#include "dConfig.h"
|
|
||||||
#include "MessageType/Chat.h"
|
|
||||||
|
|
||||||
void PlayerContainer::Initialize() {
|
PlayerContainer::PlayerContainer() {
|
||||||
m_MaxNumberOfBestFriends =
|
|
||||||
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends")).value_or(m_MaxNumberOfBestFriends);
|
|
||||||
m_MaxNumberOfFriends =
|
|
||||||
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends")).value_or(m_MaxNumberOfFriends);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamData::TeamData() {
|
PlayerContainer::~PlayerContainer() {
|
||||||
lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
|
mPlayers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::InsertPlayer(Packet* packet) {
|
void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||||
CINSTREAM_SKIP_HEADER;
|
CINSTREAM;
|
||||||
LWOOBJID playerId;
|
PlayerData* data = new PlayerData();
|
||||||
if (!inStream.Read(playerId)) {
|
inStream.SetReadOffset(inStream.GetReadOffset() + 64);
|
||||||
LOG("Failed to read player ID");
|
inStream.Read(data->playerID);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& data = m_Players[playerId];
|
|
||||||
data.playerID = playerId;
|
|
||||||
|
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
if (!inStream.Read<uint32_t>(len)) return;
|
inStream.Read<uint32_t>(len);
|
||||||
|
|
||||||
if (len > 33) {
|
for (int i = 0; i < len; i++) {
|
||||||
LOG("Received a really long player name, probably a fake packet %i.", len);
|
char character; inStream.Read<char>(character);
|
||||||
return;
|
data->playerName += character;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.playerName.resize(len);
|
inStream.Read(data->zoneID);
|
||||||
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(data.playerName.data()), len);
|
inStream.Read(data->muteExpire);
|
||||||
|
data->sysAddr = packet->systemAddress;
|
||||||
|
|
||||||
if (!inStream.Read(data.zoneID)) return;
|
mNames[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
|
||||||
if (!inStream.Read(data.muteExpire)) return;
|
|
||||||
if (!inStream.Read(data.gmLevel)) return;
|
|
||||||
data.sysAddr = packet->systemAddress;
|
|
||||||
|
|
||||||
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
mPlayers.insert(std::make_pair(data->playerID, data));
|
||||||
m_PlayerCount++;
|
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
|
||||||
|
|
||||||
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
|
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||||
|
|
||||||
Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID());
|
insertLog->setInt(1, data->playerID);
|
||||||
|
insertLog->setInt(2, 0);
|
||||||
|
insertLog->setUInt64(3, time(nullptr));
|
||||||
|
insertLog->setInt(4, data->zoneID.GetMapID());
|
||||||
|
|
||||||
|
insertLog->executeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::RemovePlayer(Packet* packet) {
|
void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||||
CINSTREAM_SKIP_HEADER;
|
CINSTREAM;
|
||||||
LWOOBJID playerID;
|
LWOOBJID playerID;
|
||||||
|
inStream.Read(playerID); //skip header
|
||||||
inStream.Read(playerID);
|
inStream.Read(playerID);
|
||||||
|
|
||||||
//Before they get kicked, we need to also send a message to their friends saying that they disconnected.
|
//Before they get kicked, we need to also send a message to their friends saying that they disconnected.
|
||||||
const auto& player = GetPlayerData(playerID);
|
std::unique_ptr<PlayerData> player(this->GetPlayerData(playerID));
|
||||||
|
|
||||||
if (!player) {
|
if (player == nullptr) {
|
||||||
LOG("Failed to find user: %llu", playerID);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& fr : player.friends) {
|
for (auto& fr : player->friends) {
|
||||||
const auto& fd = this->GetPlayerData(fr.friendID);
|
auto fd = this->GetPlayerData(fr.friendID);
|
||||||
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend);
|
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* team = GetTeam(playerID);
|
auto* team = GetTeam(playerID);
|
||||||
|
|
||||||
if (team != nullptr) {
|
if (team != nullptr) {
|
||||||
const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName);
|
const auto memberName = GeneralUtils::UTF8ToUTF16(std::string(player->playerName.c_str()));
|
||||||
|
|
||||||
for (const auto memberId : team->memberIDs) {
|
for (const auto memberId : team->memberIDs) {
|
||||||
const auto& otherMember = GetPlayerData(memberId);
|
auto* otherMember = GetPlayerData(memberId);
|
||||||
|
|
||||||
if (!otherMember) continue;
|
if (otherMember == nullptr) continue;
|
||||||
|
|
||||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
|
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_PlayerCount--;
|
Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID);
|
||||||
LOG("Removed user: %llu", playerID);
|
mPlayers.erase(playerID);
|
||||||
m_Players.erase(playerID);
|
|
||||||
|
|
||||||
Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player.zoneID.GetMapID());
|
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||||
|
|
||||||
|
insertLog->setInt(1, playerID);
|
||||||
|
insertLog->setInt(2, 1);
|
||||||
|
insertLog->setUInt64(3, time(nullptr));
|
||||||
|
insertLog->setInt(4, player->zoneID.GetMapID());
|
||||||
|
|
||||||
|
insertLog->executeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::MuteUpdate(Packet* packet) {
|
void PlayerContainer::MuteUpdate(Packet* packet) {
|
||||||
CINSTREAM_SKIP_HEADER;
|
CINSTREAM;
|
||||||
LWOOBJID playerID;
|
LWOOBJID playerID;
|
||||||
|
inStream.Read(playerID); //skip header
|
||||||
inStream.Read(playerID);
|
inStream.Read(playerID);
|
||||||
time_t expire = 0;
|
time_t expire = 0;
|
||||||
inStream.Read(expire);
|
inStream.Read(expire);
|
||||||
|
|
||||||
auto& player = this->GetPlayerDataMutable(playerID);
|
auto* player = this->GetPlayerData(playerID);
|
||||||
|
|
||||||
if (!player) {
|
if (player == nullptr) {
|
||||||
LOG("Failed to find user: %llu", playerID);
|
Game::logger->Log("PlayerContainer", "Failed to find user: %llu", playerID);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.muteExpire = expire;
|
player->muteExpire = expire;
|
||||||
|
|
||||||
BroadcastMuteUpdate(playerID, expire);
|
BroadcastMuteUpdate(playerID, expire);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::CreateTeamServer(Packet* packet) {
|
void PlayerContainer::CreateTeamServer(Packet* packet) {
|
||||||
CINSTREAM_SKIP_HEADER;
|
CINSTREAM;
|
||||||
LWOOBJID playerID;
|
LWOOBJID playerID;
|
||||||
|
inStream.Read(playerID); //skip header
|
||||||
inStream.Read(playerID);
|
inStream.Read(playerID);
|
||||||
size_t membersSize = 0;
|
size_t membersSize = 0;
|
||||||
inStream.Read(membersSize);
|
inStream.Read(membersSize);
|
||||||
|
|
||||||
if (membersSize >= 4) {
|
|
||||||
LOG("Tried to create a team with more than 4 players");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<LWOOBJID> members;
|
std::vector<LWOOBJID> members;
|
||||||
|
|
||||||
members.reserve(membersSize);
|
members.reserve(membersSize);
|
||||||
@@ -148,18 +142,19 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
|
|||||||
|
|
||||||
if (team != nullptr) {
|
if (team != nullptr) {
|
||||||
team->zoneId = zoneId;
|
team->zoneId = zoneId;
|
||||||
UpdateTeamsOnWorld(team, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateTeamsOnWorld(team, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_MUTE);
|
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_MUTE_UPDATE);
|
||||||
|
|
||||||
bitStream.Write(player);
|
bitStream.Write(player);
|
||||||
bitStream.Write(time);
|
bitStream.Write(time);
|
||||||
|
|
||||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
||||||
@@ -193,7 +188,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
|||||||
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
|
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
|
||||||
auto* team = new TeamData();
|
auto* team = new TeamData();
|
||||||
|
|
||||||
team->teamID = ++m_TeamIDCounter;
|
team->teamID = ++mTeamIDCounter;
|
||||||
team->leaderID = leader;
|
team->leaderID = leader;
|
||||||
team->local = local;
|
team->local = local;
|
||||||
|
|
||||||
@@ -215,32 +210,24 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
|
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
|
||||||
if (team->memberIDs.size() >= 4) {
|
|
||||||
LOG("Tried to add player to team that already had 4 players");
|
|
||||||
const auto& player = GetPlayerData(playerID);
|
|
||||||
if (!player) return;
|
|
||||||
ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
|
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
|
||||||
|
|
||||||
if (index != team->memberIDs.end()) return;
|
if (index != team->memberIDs.end()) return;
|
||||||
|
|
||||||
team->memberIDs.push_back(playerID);
|
team->memberIDs.push_back(playerID);
|
||||||
|
|
||||||
const auto& leader = GetPlayerData(team->leaderID);
|
auto* leader = GetPlayerData(team->leaderID);
|
||||||
const auto& member = GetPlayerData(playerID);
|
auto* member = GetPlayerData(playerID);
|
||||||
|
|
||||||
if (!leader || !member) return;
|
if (leader == nullptr || member == nullptr) return;
|
||||||
|
|
||||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
|
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName);
|
||||||
const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName);
|
const auto memberName = GeneralUtils::UTF8ToUTF16(member->playerName);
|
||||||
|
|
||||||
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader.playerID, leader.zoneID, team->lootFlag, 0, 0, leaderName);
|
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName);
|
||||||
|
|
||||||
if (!team->local) {
|
if (!team->local) {
|
||||||
ChatPacketHandler::SendTeamSetLeader(member, leader.playerID);
|
ChatPacketHandler::SendTeamSetLeader(member, leader->playerID);
|
||||||
} else {
|
} else {
|
||||||
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
|
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
|
||||||
}
|
}
|
||||||
@@ -248,16 +235,16 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
|
|||||||
UpdateTeamsOnWorld(team, false);
|
UpdateTeamsOnWorld(team, false);
|
||||||
|
|
||||||
for (const auto memberId : team->memberIDs) {
|
for (const auto memberId : team->memberIDs) {
|
||||||
const auto& otherMember = GetPlayerData(memberId);
|
auto* otherMember = GetPlayerData(memberId);
|
||||||
|
|
||||||
if (otherMember == member) continue;
|
if (otherMember == member) continue;
|
||||||
|
|
||||||
const auto otherMemberName = GetName(memberId);
|
const auto otherMemberName = GetName(memberId);
|
||||||
|
|
||||||
ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
|
ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0));
|
||||||
|
|
||||||
if (otherMember) {
|
if (otherMember != nullptr) {
|
||||||
ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID);
|
ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member->playerID, memberName, member->zoneID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,9 +254,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba
|
|||||||
|
|
||||||
if (index == team->memberIDs.end()) return;
|
if (index == team->memberIDs.end()) return;
|
||||||
|
|
||||||
const auto& member = GetPlayerData(playerID);
|
auto* member = GetPlayerData(playerID);
|
||||||
|
|
||||||
if (member && !silent) {
|
if (member != nullptr && !silent) {
|
||||||
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
|
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,9 +267,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& otherMember = GetPlayerData(memberId);
|
auto* otherMember = GetPlayerData(memberId);
|
||||||
|
|
||||||
if (!otherMember) continue;
|
if (otherMember == nullptr) continue;
|
||||||
|
|
||||||
ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName);
|
ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName);
|
||||||
}
|
}
|
||||||
@@ -304,9 +291,9 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
|
|||||||
team->leaderID = newLeader;
|
team->leaderID = newLeader;
|
||||||
|
|
||||||
for (const auto memberId : team->memberIDs) {
|
for (const auto memberId : team->memberIDs) {
|
||||||
const auto& otherMember = GetPlayerData(memberId);
|
auto* otherMember = GetPlayerData(memberId);
|
||||||
|
|
||||||
if (!otherMember) continue;
|
if (otherMember == nullptr) continue;
|
||||||
|
|
||||||
ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader);
|
ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader);
|
||||||
}
|
}
|
||||||
@@ -318,14 +305,14 @@ void PlayerContainer::DisbandTeam(TeamData* team) {
|
|||||||
if (index == mTeams.end()) return;
|
if (index == mTeams.end()) return;
|
||||||
|
|
||||||
for (const auto memberId : team->memberIDs) {
|
for (const auto memberId : team->memberIDs) {
|
||||||
const auto& otherMember = GetPlayerData(memberId);
|
auto* otherMember = GetPlayerData(memberId);
|
||||||
|
|
||||||
if (!otherMember) continue;
|
if (otherMember == nullptr) continue;
|
||||||
|
|
||||||
const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember.playerName);
|
const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember->playerName);
|
||||||
|
|
||||||
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
|
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
|
||||||
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember.playerID, memberName);
|
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateTeamsOnWorld(team, true);
|
UpdateTeamsOnWorld(team, true);
|
||||||
@@ -340,19 +327,19 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
|||||||
|
|
||||||
if (index == mTeams.end()) return;
|
if (index == mTeams.end()) return;
|
||||||
|
|
||||||
const auto& leader = GetPlayerData(team->leaderID);
|
auto* leader = GetPlayerData(team->leaderID);
|
||||||
|
|
||||||
if (!leader) return;
|
if (leader == nullptr) return;
|
||||||
|
|
||||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
|
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName);
|
||||||
|
|
||||||
for (const auto memberId : team->memberIDs) {
|
for (const auto memberId : team->memberIDs) {
|
||||||
const auto& otherMember = GetPlayerData(memberId);
|
auto* otherMember = GetPlayerData(memberId);
|
||||||
|
|
||||||
if (!otherMember) continue;
|
if (otherMember == nullptr) continue;
|
||||||
|
|
||||||
if (!team->local) {
|
if (!team->local) {
|
||||||
ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader.zoneID, team->lootFlag, 0, leaderName);
|
ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,59 +348,40 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
|||||||
|
|
||||||
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::TEAM_GET_STATUS);
|
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_TEAM_UPDATE);
|
||||||
|
|
||||||
bitStream.Write(team->teamID);
|
bitStream.Write(team->teamID);
|
||||||
bitStream.Write(deleteTeam);
|
bitStream.Write(deleteTeam);
|
||||||
|
|
||||||
if (!deleteTeam) {
|
if (!deleteTeam) {
|
||||||
bitStream.Write(team->lootFlag);
|
bitStream.Write(team->lootFlag);
|
||||||
bitStream.Write<char>(team->memberIDs.size());
|
bitStream.Write(static_cast<char>(team->memberIDs.size()));
|
||||||
for (const auto memberID : team->memberIDs) {
|
for (const auto memberID : team->memberIDs) {
|
||||||
bitStream.Write(memberID);
|
bitStream.Write(memberID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
|
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
|
||||||
const auto iter = m_Names.find(playerID);
|
const auto& pair = mNames.find(playerID);
|
||||||
|
|
||||||
if (iter == m_Names.end()) return u"";
|
if (pair == mNames.end()) return u"";
|
||||||
|
|
||||||
return iter->second;
|
return pair->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
|
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
|
||||||
LWOOBJID toReturn = LWOOBJID_EMPTY;
|
for (const auto& pair : mNames) {
|
||||||
|
if (pair.second == playerName) {
|
||||||
for (const auto& [id, name] : m_Names) {
|
return pair.first;
|
||||||
if (name == playerName) {
|
|
||||||
toReturn = id;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return toReturn;
|
return LWOOBJID_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
|
bool PlayerContainer::GetIsMuted(PlayerData* data) {
|
||||||
return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY];
|
return data->muteExpire == 1 || data->muteExpire > time(NULL);
|
||||||
}
|
|
||||||
|
|
||||||
PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) {
|
|
||||||
for (auto& [id, player] : m_Players) {
|
|
||||||
if (!player) continue;
|
|
||||||
if (player.playerName == playerName) return player;
|
|
||||||
}
|
|
||||||
return m_Players[LWOOBJID_EMPTY];
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlayerData& PlayerContainer::GetPlayerData(const LWOOBJID& playerID) {
|
|
||||||
return GetPlayerDataMutable(playerID);
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) {
|
|
||||||
return GetPlayerDataMutable(playerName);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,49 +7,17 @@
|
|||||||
#include "dServer.h"
|
#include "dServer.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
enum class eGameMasterLevel : uint8_t;
|
|
||||||
|
|
||||||
struct IgnoreData {
|
|
||||||
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
|
|
||||||
inline bool operator==(const std::string& other) const noexcept {
|
|
||||||
return playerName == other;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(const LWOOBJID& other) const noexcept {
|
|
||||||
return playerId == other;
|
|
||||||
}
|
|
||||||
|
|
||||||
LWOOBJID playerId = LWOOBJID_EMPTY;
|
|
||||||
std::string playerName;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PlayerData {
|
struct PlayerData {
|
||||||
operator bool() const noexcept {
|
LWOOBJID playerID;
|
||||||
return playerID != LWOOBJID_EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const PlayerData& other) const noexcept {
|
|
||||||
return playerID == other.playerID;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetIsMuted() const {
|
|
||||||
return muteExpire == 1 || muteExpire > time(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemAddress sysAddr{};
|
|
||||||
LWOZONEID zoneID{};
|
|
||||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
|
||||||
time_t muteExpire = 0;
|
|
||||||
uint8_t countOfBestFriends = 0;
|
|
||||||
std::string playerName;
|
std::string playerName;
|
||||||
|
SystemAddress sysAddr;
|
||||||
|
LWOZONEID zoneID;
|
||||||
std::vector<FriendData> friends;
|
std::vector<FriendData> friends;
|
||||||
std::vector<IgnoreData> ignoredPlayers;
|
time_t muteExpire;
|
||||||
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
|
uint8_t countOfBestFriends = 0;
|
||||||
bool isFTP = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TeamData {
|
struct TeamData {
|
||||||
TeamData();
|
|
||||||
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
|
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
|
||||||
LWOOBJID leaderID = LWOOBJID_EMPTY;
|
LWOOBJID leaderID = LWOOBJID_EMPTY;
|
||||||
std::vector<LWOOBJID> memberIDs{};
|
std::vector<LWOOBJID> memberIDs{};
|
||||||
@@ -60,20 +28,31 @@ struct TeamData {
|
|||||||
|
|
||||||
class PlayerContainer {
|
class PlayerContainer {
|
||||||
public:
|
public:
|
||||||
void Initialize();
|
PlayerContainer();
|
||||||
|
~PlayerContainer();
|
||||||
|
|
||||||
void InsertPlayer(Packet* packet);
|
void InsertPlayer(Packet* packet);
|
||||||
void RemovePlayer(Packet* packet);
|
void RemovePlayer(Packet* packet);
|
||||||
void MuteUpdate(Packet* packet);
|
void MuteUpdate(Packet* packet);
|
||||||
void CreateTeamServer(Packet* packet);
|
void CreateTeamServer(Packet* packet);
|
||||||
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
||||||
|
|
||||||
const PlayerData& GetPlayerData(const LWOOBJID& playerID);
|
PlayerData* GetPlayerData(const LWOOBJID& playerID) {
|
||||||
const PlayerData& GetPlayerData(const std::string& playerName);
|
auto it = mPlayers.find(playerID);
|
||||||
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
if (it != mPlayers.end()) return it->second;
|
||||||
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
return nullptr;
|
||||||
uint32_t GetPlayerCount() { return m_PlayerCount; };
|
}
|
||||||
uint32_t GetSimCount() { return m_SimCount; };
|
|
||||||
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
|
PlayerData* GetPlayerData(const std::string& playerName) {
|
||||||
|
for (auto player : mPlayers) {
|
||||||
|
if (player.second) {
|
||||||
|
std::string pn = player.second->playerName.c_str();
|
||||||
|
if (pn == playerName) return player.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
||||||
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
||||||
@@ -86,17 +65,14 @@ public:
|
|||||||
void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
|
void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
|
||||||
std::u16string GetName(LWOOBJID playerID);
|
std::u16string GetName(LWOOBJID playerID);
|
||||||
LWOOBJID GetId(const std::u16string& playerName);
|
LWOOBJID GetId(const std::u16string& playerName);
|
||||||
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
bool GetIsMuted(PlayerData* data);
|
||||||
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
|
||||||
|
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return mPlayers; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LWOOBJID m_TeamIDCounter = 0;
|
LWOOBJID mTeamIDCounter = 0;
|
||||||
std::map<LWOOBJID, PlayerData> m_Players;
|
std::map<LWOOBJID, PlayerData*> mPlayers;
|
||||||
std::vector<TeamData*> mTeams;
|
std::vector<TeamData*> mTeams;
|
||||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
std::unordered_map<LWOOBJID, std::u16string> mNames;
|
||||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
|
||||||
uint32_t m_MaxNumberOfFriends = 50;
|
|
||||||
uint32_t m_PlayerCount = 0;
|
|
||||||
uint32_t m_SimCount = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +1,89 @@
|
|||||||
#include "AMFDeserialize.h"
|
#include "AMFDeserialize.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include "AMFFormat.h"
|
||||||
|
|
||||||
#include "Amf3.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf
|
* AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf
|
||||||
* AMF3 Deserializer written by EmosewaMC
|
* AMF3 Deserializer written by EmosewaMC
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::unique_ptr<AMFBaseValue> AMFDeserialize::Read(RakNet::BitStream& inStream) {
|
AMFValue* AMFDeserialize::Read(RakNet::BitStream* inStream) {
|
||||||
|
if (!inStream) return nullptr;
|
||||||
|
AMFValue* returnValue = nullptr;
|
||||||
// Read in the value type from the bitStream
|
// Read in the value type from the bitStream
|
||||||
eAmf marker;
|
int8_t marker;
|
||||||
inStream.Read(marker);
|
inStream->Read(marker);
|
||||||
// Based on the typing, create the value associated with that and return the base value class
|
// Based on the typing, create the value associated with that and return the base value class
|
||||||
switch (marker) {
|
switch (marker) {
|
||||||
case eAmf::Undefined:
|
case AMFValueType::AMFUndefined: {
|
||||||
return std::make_unique<AMFBaseValue>();
|
returnValue = new AMFUndefinedValue();
|
||||||
case eAmf::Null:
|
break;
|
||||||
return std::make_unique<AMFNullValue>();
|
|
||||||
case eAmf::False:
|
|
||||||
return std::make_unique<AMFBoolValue>(false);
|
|
||||||
case eAmf::True:
|
|
||||||
return std::make_unique<AMFBoolValue>(true);
|
|
||||||
case eAmf::Integer:
|
|
||||||
return ReadAmfInteger(inStream);
|
|
||||||
case eAmf::Double:
|
|
||||||
return ReadAmfDouble(inStream);
|
|
||||||
case eAmf::String:
|
|
||||||
return ReadAmfString(inStream);
|
|
||||||
case eAmf::Array:
|
|
||||||
return ReadAmfArray(inStream);
|
|
||||||
|
|
||||||
// These values are unimplemented in the live client and will remain unimplemented
|
|
||||||
// unless someone modifies the client to allow serializing of these values.
|
|
||||||
case eAmf::XMLDoc:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::Date:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::Object:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::XML:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::ByteArray:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::VectorInt:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::VectorUInt:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::VectorDouble:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::VectorObject:
|
|
||||||
[[fallthrough]];
|
|
||||||
case eAmf::Dictionary:
|
|
||||||
throw marker;
|
|
||||||
default:
|
|
||||||
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AMFValueType::AMFNull: {
|
||||||
|
returnValue = new AMFNullValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFValueType::AMFFalse: {
|
||||||
|
returnValue = new AMFFalseValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFValueType::AMFTrue: {
|
||||||
|
returnValue = new AMFTrueValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFValueType::AMFInteger: {
|
||||||
|
returnValue = ReadAmfInteger(inStream);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFValueType::AMFDouble: {
|
||||||
|
returnValue = ReadAmfDouble(inStream);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFValueType::AMFString: {
|
||||||
|
returnValue = ReadAmfString(inStream);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFValueType::AMFArray: {
|
||||||
|
returnValue = ReadAmfArray(inStream);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO We do not need these values, but if someone wants to implement them
|
||||||
|
// then please do so and add the corresponding unit tests.
|
||||||
|
case AMFValueType::AMFXMLDoc:
|
||||||
|
case AMFValueType::AMFDate:
|
||||||
|
case AMFValueType::AMFObject:
|
||||||
|
case AMFValueType::AMFXML:
|
||||||
|
case AMFValueType::AMFByteArray:
|
||||||
|
case AMFValueType::AMFVectorInt:
|
||||||
|
case AMFValueType::AMFVectorUInt:
|
||||||
|
case AMFValueType::AMFVectorDouble:
|
||||||
|
case AMFValueType::AMFVectorObject:
|
||||||
|
case AMFValueType::AMFDictionary: {
|
||||||
|
throw static_cast<AMFValueType>(marker);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw static_cast<AMFValueType>(marker);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AMFDeserialize::ReadU29(RakNet::BitStream& inStream) {
|
uint32_t AMFDeserialize::ReadU29(RakNet::BitStream* inStream) {
|
||||||
bool byteFlag = true;
|
bool byteFlag = true;
|
||||||
uint32_t actualNumber{};
|
uint32_t actualNumber{};
|
||||||
uint8_t numberOfBytesRead{};
|
uint8_t numberOfBytesRead{};
|
||||||
while (byteFlag && numberOfBytesRead < 4) {
|
while (byteFlag && numberOfBytesRead < 4) {
|
||||||
uint8_t byte{};
|
uint8_t byte{};
|
||||||
inStream.Read(byte);
|
inStream->Read(byte);
|
||||||
// Parse the byte
|
// Parse the byte
|
||||||
if (numberOfBytesRead < 3) {
|
if (numberOfBytesRead < 3) {
|
||||||
byteFlag = byte & static_cast<uint8_t>(1 << 7);
|
byteFlag = byte & static_cast<uint8_t>(1 << 7);
|
||||||
@@ -81,7 +99,7 @@ uint32_t AMFDeserialize::ReadU29(RakNet::BitStream& inStream) {
|
|||||||
return actualNumber;
|
return actualNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string AMFDeserialize::ReadString(RakNet::BitStream& inStream) {
|
std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
|
||||||
auto length = ReadU29(inStream);
|
auto length = ReadU29(inStream);
|
||||||
// Check if this is a reference
|
// Check if this is a reference
|
||||||
bool isReference = length % 2 == 1;
|
bool isReference = length % 2 == 1;
|
||||||
@@ -89,45 +107,54 @@ const std::string AMFDeserialize::ReadString(RakNet::BitStream& inStream) {
|
|||||||
length = length >> 1;
|
length = length >> 1;
|
||||||
if (isReference) {
|
if (isReference) {
|
||||||
std::string value(length, 0);
|
std::string value(length, 0);
|
||||||
inStream.Read(&value[0], length);
|
inStream->Read(&value[0], length);
|
||||||
// Empty strings are never sent by reference
|
// Empty strings are never sent by reference
|
||||||
if (!value.empty()) accessedElements.push_back(value);
|
if (!value.empty()) accessedElements.push_back(value);
|
||||||
return value;
|
return value;
|
||||||
} else {
|
} else {
|
||||||
// Length is a reference to a previous index - use that as the read in value
|
// Length is a reference to a previous index - use that as the read in value
|
||||||
return accessedElements.at(length);
|
return accessedElements[length];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AMFDoubleValue> AMFDeserialize::ReadAmfDouble(RakNet::BitStream& inStream) {
|
AMFValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream* inStream) {
|
||||||
|
auto doubleValue = new AMFDoubleValue();
|
||||||
double value;
|
double value;
|
||||||
inStream.Read<double>(value);
|
inStream->Read<double>(value);
|
||||||
return std::make_unique<AMFDoubleValue>(value);
|
doubleValue->SetDoubleValue(value);
|
||||||
|
return doubleValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AMFArrayValue> AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
|
AMFValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream* inStream) {
|
||||||
auto arrayValue = std::make_unique<AMFArrayValue>();
|
auto arrayValue = new AMFArrayValue();
|
||||||
|
|
||||||
// Read size of dense array
|
// Read size of dense array
|
||||||
const auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
|
auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
|
||||||
// Then read associative portion
|
|
||||||
|
// Then read Key'd portion
|
||||||
while (true) {
|
while (true) {
|
||||||
const auto key = ReadString(inStream);
|
auto key = ReadString(inStream);
|
||||||
// No more associative values when we encounter an empty string key
|
// No more values when we encounter an empty string
|
||||||
if (key.size() == 0) break;
|
if (key.size() == 0) break;
|
||||||
arrayValue->Insert(key, Read(inStream));
|
arrayValue->InsertValue(key, Read(inStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally read dense portion
|
// Finally read dense portion
|
||||||
for (uint32_t i = 0; i < sizeOfDenseArray; i++) {
|
for (uint32_t i = 0; i < sizeOfDenseArray; i++) {
|
||||||
arrayValue->Insert(i, Read(inStream));
|
arrayValue->PushBackValue(Read(inStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
return arrayValue;
|
return arrayValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AMFStringValue> AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) {
|
AMFValue* AMFDeserialize::ReadAmfString(RakNet::BitStream* inStream) {
|
||||||
return std::make_unique<AMFStringValue>(ReadString(inStream));
|
auto stringValue = new AMFStringValue();
|
||||||
|
stringValue->SetStringValue(ReadString(inStream));
|
||||||
|
return stringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AMFIntValue> AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) {
|
AMFValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream* inStream) {
|
||||||
return std::make_unique<AMFIntValue>(ReadU29(inStream)); // NOTE: NARROWING CONVERSION FROM UINT TO INT. IS THIS INTENDED?
|
auto integerValue = new AMFIntegerValue();
|
||||||
|
integerValue->SetIntegerValue(ReadU29(inStream));
|
||||||
|
return integerValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Amf3.h"
|
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
class AMFValue;
|
||||||
class AMFDeserialize {
|
class AMFDeserialize {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +14,7 @@ public:
|
|||||||
* @param inStream inStream to read value from.
|
* @param inStream inStream to read value from.
|
||||||
* @return Returns an AMFValue with all the information from the bitStream in it.
|
* @return Returns an AMFValue with all the information from the bitStream in it.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<AMFBaseValue> Read(RakNet::BitStream& inStream);
|
AMFValue* Read(RakNet::BitStream* inStream);
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Private method to read a U29 integer from a bitstream
|
* @brief Private method to read a U29 integer from a bitstream
|
||||||
@@ -23,7 +22,7 @@ private:
|
|||||||
* @param inStream bitstream to read data from
|
* @param inStream bitstream to read data from
|
||||||
* @return The number as an unsigned 29 bit integer
|
* @return The number as an unsigned 29 bit integer
|
||||||
*/
|
*/
|
||||||
static uint32_t ReadU29(RakNet::BitStream& inStream);
|
uint32_t ReadU29(RakNet::BitStream* inStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reads a string from a bitstream
|
* @brief Reads a string from a bitstream
|
||||||
@@ -31,7 +30,7 @@ private:
|
|||||||
* @param inStream bitStream to read data from
|
* @param inStream bitStream to read data from
|
||||||
* @return The read string
|
* @return The read string
|
||||||
*/
|
*/
|
||||||
const std::string ReadString(RakNet::BitStream& inStream);
|
std::string ReadString(RakNet::BitStream* inStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read an AMFDouble value from a bitStream
|
* @brief Read an AMFDouble value from a bitStream
|
||||||
@@ -39,7 +38,7 @@ private:
|
|||||||
* @param inStream bitStream to read data from
|
* @param inStream bitStream to read data from
|
||||||
* @return Double value represented as an AMFValue
|
* @return Double value represented as an AMFValue
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<AMFDoubleValue> ReadAmfDouble(RakNet::BitStream& inStream);
|
AMFValue* ReadAmfDouble(RakNet::BitStream* inStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read an AMFArray from a bitStream
|
* @brief Read an AMFArray from a bitStream
|
||||||
@@ -47,7 +46,7 @@ private:
|
|||||||
* @param inStream bitStream to read data from
|
* @param inStream bitStream to read data from
|
||||||
* @return Array value represented as an AMFValue
|
* @return Array value represented as an AMFValue
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<AMFArrayValue> ReadAmfArray(RakNet::BitStream& inStream);
|
AMFValue* ReadAmfArray(RakNet::BitStream* inStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read an AMFString from a bitStream
|
* @brief Read an AMFString from a bitStream
|
||||||
@@ -55,7 +54,7 @@ private:
|
|||||||
* @param inStream bitStream to read data from
|
* @param inStream bitStream to read data from
|
||||||
* @return String value represented as an AMFValue
|
* @return String value represented as an AMFValue
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<AMFStringValue> ReadAmfString(RakNet::BitStream& inStream);
|
AMFValue* ReadAmfString(RakNet::BitStream* inStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read an AMFInteger from a bitStream
|
* @brief Read an AMFInteger from a bitStream
|
||||||
@@ -63,7 +62,7 @@ private:
|
|||||||
* @param inStream bitStream to read data from
|
* @param inStream bitStream to read data from
|
||||||
* @return Integer value represented as an AMFValue
|
* @return Integer value represented as an AMFValue
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<AMFIntValue> ReadAmfInteger(RakNet::BitStream& inStream);
|
AMFValue* ReadAmfInteger(RakNet::BitStream* inStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of strings read so far saved to be read by reference.
|
* List of strings read so far saved to be read by reference.
|
||||||
|
|||||||
156
dCommon/AMFFormat.cpp
Normal file
156
dCommon/AMFFormat.cpp
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#include "AMFFormat.h"
|
||||||
|
|
||||||
|
// AMFInteger
|
||||||
|
void AMFIntegerValue::SetIntegerValue(uint32_t value) {
|
||||||
|
this->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AMFIntegerValue::GetIntegerValue() {
|
||||||
|
return this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFDouble
|
||||||
|
void AMFDoubleValue::SetDoubleValue(double value) {
|
||||||
|
this->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
double AMFDoubleValue::GetDoubleValue() {
|
||||||
|
return this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFString
|
||||||
|
void AMFStringValue::SetStringValue(const std::string& value) {
|
||||||
|
this->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AMFStringValue::GetStringValue() {
|
||||||
|
return this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFXMLDoc
|
||||||
|
void AMFXMLDocValue::SetXMLDocValue(const std::string& value) {
|
||||||
|
this->xmlData = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AMFXMLDocValue::GetXMLDocValue() {
|
||||||
|
return this->xmlData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFDate
|
||||||
|
void AMFDateValue::SetDateValue(uint64_t value) {
|
||||||
|
this->millisecondTimestamp = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t AMFDateValue::GetDateValue() {
|
||||||
|
return this->millisecondTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Insert Value
|
||||||
|
void AMFArrayValue::InsertValue(const std::string& key, AMFValue* value) {
|
||||||
|
this->associative.insert(std::make_pair(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Remove Value
|
||||||
|
void AMFArrayValue::RemoveValue(const std::string& key) {
|
||||||
|
_AMFArrayMap_::iterator it = this->associative.find(key);
|
||||||
|
if (it != this->associative.end()) {
|
||||||
|
this->associative.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Get Associative Iterator Begin
|
||||||
|
_AMFArrayMap_::iterator AMFArrayValue::GetAssociativeIteratorValueBegin() {
|
||||||
|
return this->associative.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Get Associative Iterator End
|
||||||
|
_AMFArrayMap_::iterator AMFArrayValue::GetAssociativeIteratorValueEnd() {
|
||||||
|
return this->associative.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Push Back Value
|
||||||
|
void AMFArrayValue::PushBackValue(AMFValue* value) {
|
||||||
|
this->dense.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Pop Back Value
|
||||||
|
void AMFArrayValue::PopBackValue() {
|
||||||
|
this->dense.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Get Dense List Size
|
||||||
|
uint32_t AMFArrayValue::GetDenseValueSize() {
|
||||||
|
return (uint32_t)this->dense.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Get Dense Iterator Begin
|
||||||
|
_AMFArrayList_::iterator AMFArrayValue::GetDenseIteratorBegin() {
|
||||||
|
return this->dense.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFArray Get Dense Iterator End
|
||||||
|
_AMFArrayList_::iterator AMFArrayValue::GetDenseIteratorEnd() {
|
||||||
|
return this->dense.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
AMFArrayValue::~AMFArrayValue() {
|
||||||
|
for (auto valueToDelete : GetDenseArray()) {
|
||||||
|
if (valueToDelete) delete valueToDelete;
|
||||||
|
}
|
||||||
|
for (auto valueToDelete : GetAssociativeMap()) {
|
||||||
|
if (valueToDelete.second) delete valueToDelete.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFObject Constructor
|
||||||
|
AMFObjectValue::AMFObjectValue(std::vector<std::pair<std::string, AMFValueType>> traits) {
|
||||||
|
this->traits.reserve(traits.size());
|
||||||
|
std::vector<std::pair<std::string, AMFValueType>>::iterator it = traits.begin();
|
||||||
|
while (it != traits.end()) {
|
||||||
|
this->traits.insert(std::make_pair(it->first, std::make_pair(it->second, new AMFNullValue())));
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFObject Set Value
|
||||||
|
void AMFObjectValue::SetTraitValue(const std::string& trait, AMFValue* value) {
|
||||||
|
if (value) {
|
||||||
|
_AMFObjectTraits_::iterator it = this->traits.find(trait);
|
||||||
|
if (it != this->traits.end()) {
|
||||||
|
if (it->second.first == value->GetValueType()) {
|
||||||
|
it->second.second = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFObject Get Value
|
||||||
|
AMFValue* AMFObjectValue::GetTraitValue(const std::string& trait) {
|
||||||
|
_AMFObjectTraits_::iterator it = this->traits.find(trait);
|
||||||
|
if (it != this->traits.end()) {
|
||||||
|
return it->second.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFObject Get Trait Iterator Begin
|
||||||
|
_AMFObjectTraits_::iterator AMFObjectValue::GetTraitsIteratorBegin() {
|
||||||
|
return this->traits.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFObject Get Trait Iterator End
|
||||||
|
_AMFObjectTraits_::iterator AMFObjectValue::GetTraitsIteratorEnd() {
|
||||||
|
return this->traits.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMFObject Get Trait Size
|
||||||
|
uint32_t AMFObjectValue::GetTraitArrayCount() {
|
||||||
|
return (uint32_t)this->traits.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
AMFObjectValue::~AMFObjectValue() {
|
||||||
|
for (auto valueToDelete = GetTraitsIteratorBegin(); valueToDelete != GetTraitsIteratorEnd(); valueToDelete++) {
|
||||||
|
if (valueToDelete->second.second) delete valueToDelete->second.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
413
dCommon/AMFFormat.h
Normal file
413
dCommon/AMFFormat.h
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Custom Classes
|
||||||
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
|
// C++
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\file AMFFormat.hpp
|
||||||
|
\brief A class for managing AMF values
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AMFValue; // Forward declaration
|
||||||
|
|
||||||
|
// Definitions
|
||||||
|
#define _AMFArrayMap_ std::unordered_map<std::string, AMFValue*>
|
||||||
|
#define _AMFArrayList_ std::vector<AMFValue*>
|
||||||
|
|
||||||
|
#define _AMFObjectTraits_ std::unordered_map<std::string, std::pair<AMFValueType, AMFValue*>>
|
||||||
|
#define _AMFObjectDynamicTraits_ std::unordered_map<std::string, AMFValue*>
|
||||||
|
|
||||||
|
//! An enum for each AMF value type
|
||||||
|
enum AMFValueType : unsigned char {
|
||||||
|
AMFUndefined = 0x00, //!< An undefined AMF Value
|
||||||
|
AMFNull = 0x01, //!< A null AMF value
|
||||||
|
AMFFalse = 0x02, //!< A false AMF value
|
||||||
|
AMFTrue = 0x03, //!< A true AMF value
|
||||||
|
AMFInteger = 0x04, //!< An integer AMF value
|
||||||
|
AMFDouble = 0x05, //!< A double AMF value
|
||||||
|
AMFString = 0x06, //!< A string AMF value
|
||||||
|
AMFXMLDoc = 0x07, //!< An XML Doc AMF value
|
||||||
|
AMFDate = 0x08, //!< A date AMF value
|
||||||
|
AMFArray = 0x09, //!< An array AMF value
|
||||||
|
AMFObject = 0x0A, //!< An object AMF value
|
||||||
|
AMFXML = 0x0B, //!< An XML AMF value
|
||||||
|
AMFByteArray = 0x0C, //!< A byte array AMF value
|
||||||
|
AMFVectorInt = 0x0D, //!< An integer vector AMF value
|
||||||
|
AMFVectorUInt = 0x0E, //!< An unsigned integer AMF value
|
||||||
|
AMFVectorDouble = 0x0F, //!< A double vector AMF value
|
||||||
|
AMFVectorObject = 0x10, //!< An object vector AMF value
|
||||||
|
AMFDictionary = 0x11 //!< A dictionary AMF value
|
||||||
|
};
|
||||||
|
|
||||||
|
//! An enum for the object value types
|
||||||
|
enum AMFObjectValueType : unsigned char {
|
||||||
|
AMFObjectAnonymous = 0x01,
|
||||||
|
AMFObjectTyped = 0x02,
|
||||||
|
AMFObjectDynamic = 0x03,
|
||||||
|
AMFObjectExternalizable = 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The base AMF value class
|
||||||
|
class AMFValue {
|
||||||
|
public:
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
virtual AMFValueType GetValueType() = 0;
|
||||||
|
virtual ~AMFValue() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
//! A typedef for a pointer to an AMF value
|
||||||
|
typedef AMFValue* NDGFxValue;
|
||||||
|
|
||||||
|
|
||||||
|
// The various AMF value types
|
||||||
|
|
||||||
|
//! The undefined value AMF type
|
||||||
|
class AMFUndefinedValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFUndefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The null value AMF type
|
||||||
|
class AMFNullValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFNull;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The false value AMF type
|
||||||
|
class AMFFalseValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFFalse;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The true value AMF type
|
||||||
|
class AMFTrueValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFTrue;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The integer value AMF type
|
||||||
|
class AMFIntegerValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
uint32_t value; //!< The value of the AMF type
|
||||||
|
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFInteger;
|
||||||
|
//! Sets the integer value
|
||||||
|
/*!
|
||||||
|
\param value The value to set
|
||||||
|
*/
|
||||||
|
void SetIntegerValue(uint32_t value);
|
||||||
|
|
||||||
|
//! Gets the integer value
|
||||||
|
/*!
|
||||||
|
\return The integer value
|
||||||
|
*/
|
||||||
|
uint32_t GetIntegerValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The double value AMF type
|
||||||
|
class AMFDoubleValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
double value; //!< The value of the AMF type
|
||||||
|
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFDouble;
|
||||||
|
//! Sets the double value
|
||||||
|
/*!
|
||||||
|
\param value The value to set to
|
||||||
|
*/
|
||||||
|
void SetDoubleValue(double value);
|
||||||
|
|
||||||
|
//! Gets the double value
|
||||||
|
/*!
|
||||||
|
\return The double value
|
||||||
|
*/
|
||||||
|
double GetDoubleValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The string value AMF type
|
||||||
|
class AMFStringValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
std::string value; //!< The value of the AMF type
|
||||||
|
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFString;
|
||||||
|
//! Sets the string value
|
||||||
|
/*!
|
||||||
|
\param value The string value to set to
|
||||||
|
*/
|
||||||
|
void SetStringValue(const std::string& value);
|
||||||
|
|
||||||
|
//! Gets the string value
|
||||||
|
/*!
|
||||||
|
\return The string value
|
||||||
|
*/
|
||||||
|
std::string GetStringValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The XML doc value AMF type
|
||||||
|
class AMFXMLDocValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
std::string xmlData; //!< The value of the AMF type
|
||||||
|
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFXMLDoc;
|
||||||
|
//! Sets the XML Doc value
|
||||||
|
/*!
|
||||||
|
\param value The value to set to
|
||||||
|
*/
|
||||||
|
void SetXMLDocValue(const std::string& value);
|
||||||
|
|
||||||
|
//! Gets the XML Doc value
|
||||||
|
/*!
|
||||||
|
\return The XML Doc value
|
||||||
|
*/
|
||||||
|
std::string GetXMLDocValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The date value AMF type
|
||||||
|
class AMFDateValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
uint64_t millisecondTimestamp; //!< The time in milliseconds since the ephoch
|
||||||
|
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFDate;
|
||||||
|
//! Sets the date time
|
||||||
|
/*!
|
||||||
|
\param value The value to set to
|
||||||
|
*/
|
||||||
|
void SetDateValue(uint64_t value);
|
||||||
|
|
||||||
|
//! Gets the date value
|
||||||
|
/*!
|
||||||
|
\return The date value in milliseconds since the epoch
|
||||||
|
*/
|
||||||
|
uint64_t GetDateValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The array value AMF type
|
||||||
|
// This object will manage it's own memory map and list. Do not delete its values.
|
||||||
|
class AMFArrayValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
_AMFArrayMap_ associative; //!< The array map (associative part)
|
||||||
|
_AMFArrayList_ dense; //!< The array list (dense part)
|
||||||
|
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFArray;
|
||||||
|
|
||||||
|
~AMFArrayValue() override;
|
||||||
|
//! Inserts an item into the array map for a specific key
|
||||||
|
/*!
|
||||||
|
\param key The key to set
|
||||||
|
\param value The value to add
|
||||||
|
*/
|
||||||
|
void InsertValue(const std::string& key, AMFValue* value);
|
||||||
|
|
||||||
|
//! Removes an item for a specific key
|
||||||
|
/*!
|
||||||
|
\param key The key to remove
|
||||||
|
*/
|
||||||
|
void RemoveValue(const std::string& key);
|
||||||
|
|
||||||
|
//! Finds an AMF value
|
||||||
|
/*!
|
||||||
|
\return The AMF value if found, nullptr otherwise
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
T* FindValue(const std::string& key) const {
|
||||||
|
_AMFArrayMap_::const_iterator it = this->associative.find(key);
|
||||||
|
if (it != this->associative.end() && T::ValueType == it->second->GetValueType()) {
|
||||||
|
return dynamic_cast<T*>(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Returns where the associative iterator begins
|
||||||
|
/*!
|
||||||
|
\return Where the array map iterator begins
|
||||||
|
*/
|
||||||
|
_AMFArrayMap_::iterator GetAssociativeIteratorValueBegin();
|
||||||
|
|
||||||
|
//! Returns where the associative iterator ends
|
||||||
|
/*!
|
||||||
|
\return Where the array map iterator ends
|
||||||
|
*/
|
||||||
|
_AMFArrayMap_::iterator GetAssociativeIteratorValueEnd();
|
||||||
|
|
||||||
|
//! Pushes back a value into the array list
|
||||||
|
/*!
|
||||||
|
\param value The value to push back
|
||||||
|
*/
|
||||||
|
void PushBackValue(AMFValue* value);
|
||||||
|
|
||||||
|
//! Pops back the last value in the array list
|
||||||
|
void PopBackValue();
|
||||||
|
|
||||||
|
//! Gets the count of the dense list
|
||||||
|
/*!
|
||||||
|
\return The dense list size
|
||||||
|
*/
|
||||||
|
uint32_t GetDenseValueSize();
|
||||||
|
|
||||||
|
//! Gets a specific value from the list for the specified index
|
||||||
|
/*!
|
||||||
|
\param index The index to get
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
T* GetValueAt(uint32_t index) {
|
||||||
|
if (index >= this->dense.size()) return nullptr;
|
||||||
|
AMFValue* foundValue = this->dense.at(index);
|
||||||
|
return T::ValueType == foundValue->GetValueType() ? dynamic_cast<T*>(foundValue) : nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Returns where the dense iterator begins
|
||||||
|
/*!
|
||||||
|
\return Where the iterator begins
|
||||||
|
*/
|
||||||
|
_AMFArrayList_::iterator GetDenseIteratorBegin();
|
||||||
|
|
||||||
|
//! Returns where the dense iterator ends
|
||||||
|
/*!
|
||||||
|
\return Where the iterator ends
|
||||||
|
*/
|
||||||
|
_AMFArrayList_::iterator GetDenseIteratorEnd();
|
||||||
|
|
||||||
|
//! Returns the associative map
|
||||||
|
/*!
|
||||||
|
\return The associative map
|
||||||
|
*/
|
||||||
|
_AMFArrayMap_ GetAssociativeMap() { return this->associative; };
|
||||||
|
|
||||||
|
//! Returns the dense array
|
||||||
|
/*!
|
||||||
|
\return The dense array
|
||||||
|
*/
|
||||||
|
_AMFArrayList_ GetDenseArray() { return this->dense; };
|
||||||
|
};
|
||||||
|
|
||||||
|
//! The anonymous object value AMF type
|
||||||
|
class AMFObjectValue : public AMFValue {
|
||||||
|
private:
|
||||||
|
_AMFObjectTraits_ traits; //!< The object traits
|
||||||
|
|
||||||
|
//! Returns the AMF value type
|
||||||
|
/*!
|
||||||
|
\return The AMF value type
|
||||||
|
*/
|
||||||
|
AMFValueType GetValueType() { return ValueType; }
|
||||||
|
~AMFObjectValue() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const AMFValueType ValueType = AMFObject;
|
||||||
|
//! Constructor
|
||||||
|
/*!
|
||||||
|
\param traits The traits to set
|
||||||
|
*/
|
||||||
|
AMFObjectValue(std::vector<std::pair<std::string, AMFValueType>> traits);
|
||||||
|
|
||||||
|
//! Gets the object value type
|
||||||
|
/*!
|
||||||
|
\return The object value type
|
||||||
|
*/
|
||||||
|
virtual AMFObjectValueType GetObjectValueType() { return AMFObjectAnonymous; }
|
||||||
|
|
||||||
|
//! Sets the value of a trait
|
||||||
|
/*!
|
||||||
|
\param trait The trait to set the value for
|
||||||
|
\param value The AMF value to set
|
||||||
|
*/
|
||||||
|
void SetTraitValue(const std::string& trait, AMFValue* value);
|
||||||
|
|
||||||
|
//! Gets a trait value
|
||||||
|
/*!
|
||||||
|
\param trait The trait to get the value for
|
||||||
|
\return The trait value
|
||||||
|
*/
|
||||||
|
AMFValue* GetTraitValue(const std::string& trait);
|
||||||
|
|
||||||
|
//! Gets the beginning of the object traits iterator
|
||||||
|
/*!
|
||||||
|
\return The AMF trait array iterator begin
|
||||||
|
*/
|
||||||
|
_AMFObjectTraits_::iterator GetTraitsIteratorBegin();
|
||||||
|
|
||||||
|
//! Gets the end of the object traits iterator
|
||||||
|
/*!
|
||||||
|
\return The AMF trait array iterator begin
|
||||||
|
*/
|
||||||
|
_AMFObjectTraits_::iterator GetTraitsIteratorEnd();
|
||||||
|
|
||||||
|
//! Gets the amount of traits
|
||||||
|
/*!
|
||||||
|
\return The amount of traits
|
||||||
|
*/
|
||||||
|
uint32_t GetTraitArrayCount();
|
||||||
|
};
|
||||||
250
dCommon/AMFFormat_BitStream.cpp
Normal file
250
dCommon/AMFFormat_BitStream.cpp
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
#include "AMFFormat_BitStream.h"
|
||||||
|
|
||||||
|
// Writes an AMFValue pointer to a RakNet::BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFValue*>(AMFValue* value) {
|
||||||
|
if (value != nullptr) {
|
||||||
|
AMFValueType type = value->GetValueType();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case AMFUndefined: {
|
||||||
|
AMFUndefinedValue* v = (AMFUndefinedValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFNull: {
|
||||||
|
AMFNullValue* v = (AMFNullValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFFalse: {
|
||||||
|
AMFFalseValue* v = (AMFFalseValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFTrue: {
|
||||||
|
AMFTrueValue* v = (AMFTrueValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFInteger: {
|
||||||
|
AMFIntegerValue* v = (AMFIntegerValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFDouble: {
|
||||||
|
AMFDoubleValue* v = (AMFDoubleValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFString: {
|
||||||
|
AMFStringValue* v = (AMFStringValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFXMLDoc: {
|
||||||
|
AMFXMLDocValue* v = (AMFXMLDocValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFDate: {
|
||||||
|
AMFDateValue* v = (AMFDateValue*)value;
|
||||||
|
this->Write(*v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AMFArray: {
|
||||||
|
this->Write((AMFArrayValue*)value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A private function to write an value to a RakNet::BitStream
|
||||||
|
* RakNet writes in the correct byte order - do not reverse this.
|
||||||
|
*/
|
||||||
|
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||||
|
unsigned char b4 = (unsigned char)v;
|
||||||
|
if (v < 0x00200000) {
|
||||||
|
b4 = b4 & 0x7F;
|
||||||
|
if (v > 0x7F) {
|
||||||
|
unsigned char b3;
|
||||||
|
v = v >> 7;
|
||||||
|
b3 = ((unsigned char)(v)) | 0x80;
|
||||||
|
if (v > 0x7F) {
|
||||||
|
unsigned char b2;
|
||||||
|
v = v >> 7;
|
||||||
|
b2 = ((unsigned char)(v)) | 0x80;
|
||||||
|
bs->Write(b2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bs->Write(b3);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsigned char b1;
|
||||||
|
unsigned char b2;
|
||||||
|
unsigned char b3;
|
||||||
|
|
||||||
|
v = v >> 8;
|
||||||
|
b3 = ((unsigned char)(v)) | 0x80;
|
||||||
|
v = v >> 7;
|
||||||
|
b2 = ((unsigned char)(v)) | 0x80;
|
||||||
|
v = v >> 7;
|
||||||
|
b1 = ((unsigned char)(v)) | 0x80;
|
||||||
|
|
||||||
|
bs->Write(b1);
|
||||||
|
bs->Write(b2);
|
||||||
|
bs->Write(b3);
|
||||||
|
}
|
||||||
|
|
||||||
|
bs->Write(b4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a flag number to a RakNet::BitStream
|
||||||
|
* RakNet writes in the correct byte order - do not reverse this.
|
||||||
|
*/
|
||||||
|
void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
|
||||||
|
v = (v << 1) | 0x01;
|
||||||
|
WriteUInt29(bs, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an AMFString to a RakNet::BitStream
|
||||||
|
*
|
||||||
|
* RakNet writes in the correct byte order - do not reverse this.
|
||||||
|
*/
|
||||||
|
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
|
||||||
|
WriteFlagNumber(bs, (uint32_t)str.size());
|
||||||
|
bs->Write(str.c_str(), (uint32_t)str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an U16 to a bitstream
|
||||||
|
*
|
||||||
|
* RakNet writes in the correct byte order - do not reverse this.
|
||||||
|
*/
|
||||||
|
void WriteAMFU16(RakNet::BitStream* bs, uint16_t value) {
|
||||||
|
bs->Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an U32 to a bitstream
|
||||||
|
*
|
||||||
|
* RakNet writes in the correct byte order - do not reverse this.
|
||||||
|
*/
|
||||||
|
void WriteAMFU32(RakNet::BitStream* bs, uint32_t value) {
|
||||||
|
bs->Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an U64 to a bitstream
|
||||||
|
*
|
||||||
|
* RakNet writes in the correct byte order - do not reverse this.
|
||||||
|
*/
|
||||||
|
void WriteAMFU64(RakNet::BitStream* bs, uint64_t value) {
|
||||||
|
bs->Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Writes an AMFUndefinedValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFUndefinedValue>(AMFUndefinedValue value) {
|
||||||
|
this->Write(AMFUndefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFNullValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFNullValue>(AMFNullValue value) {
|
||||||
|
this->Write(AMFNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFFalseValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFFalseValue>(AMFFalseValue value) {
|
||||||
|
this->Write(AMFFalse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFTrueValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFTrueValue>(AMFTrueValue value) {
|
||||||
|
this->Write(AMFTrue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFIntegerValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFIntegerValue>(AMFIntegerValue value) {
|
||||||
|
this->Write(AMFInteger);
|
||||||
|
WriteUInt29(this, value.GetIntegerValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFDoubleValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFDoubleValue>(AMFDoubleValue value) {
|
||||||
|
this->Write(AMFDouble);
|
||||||
|
double d = value.GetDoubleValue();
|
||||||
|
WriteAMFU64(this, *((unsigned long long*) & d));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFStringValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFStringValue>(AMFStringValue value) {
|
||||||
|
this->Write(AMFString);
|
||||||
|
std::string v = value.GetStringValue();
|
||||||
|
WriteAMFString(this, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFXMLDocValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFXMLDocValue>(AMFXMLDocValue value) {
|
||||||
|
this->Write(AMFXMLDoc);
|
||||||
|
std::string v = value.GetXMLDocValue();
|
||||||
|
WriteAMFString(this, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFDateValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFDateValue>(AMFDateValue value) {
|
||||||
|
this->Write(AMFDate);
|
||||||
|
uint64_t date = value.GetDateValue();
|
||||||
|
WriteAMFU64(this, date);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes an AMFArrayValue to BitStream
|
||||||
|
template<>
|
||||||
|
void RakNet::BitStream::Write<AMFArrayValue*>(AMFArrayValue* value) {
|
||||||
|
this->Write(AMFArray);
|
||||||
|
uint32_t denseSize = value->GetDenseValueSize();
|
||||||
|
WriteFlagNumber(this, denseSize);
|
||||||
|
|
||||||
|
_AMFArrayMap_::iterator it = value->GetAssociativeIteratorValueBegin();
|
||||||
|
_AMFArrayMap_::iterator end = value->GetAssociativeIteratorValueEnd();
|
||||||
|
|
||||||
|
while (it != end) {
|
||||||
|
WriteAMFString(this, it->first);
|
||||||
|
this->Write(it->second);
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->Write(AMFNull);
|
||||||
|
|
||||||
|
if (denseSize > 0) {
|
||||||
|
_AMFArrayList_::iterator it2 = value->GetDenseIteratorBegin();
|
||||||
|
_AMFArrayList_::iterator end2 = value->GetDenseIteratorEnd();
|
||||||
|
|
||||||
|
while (it2 != end2) {
|
||||||
|
this->Write(*it2);
|
||||||
|
it2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
dCommon/AMFFormat_BitStream.h
Normal file
92
dCommon/AMFFormat_BitStream.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Custom Classes
|
||||||
|
#include "AMFFormat.h"
|
||||||
|
|
||||||
|
// RakNet
|
||||||
|
#include <BitStream.h>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\file AMFFormat_BitStream.h
|
||||||
|
\brief A class that implements native writing of AMF values to RakNet::BitStream
|
||||||
|
*/
|
||||||
|
|
||||||
|
// We are using the RakNet namespace
|
||||||
|
namespace RakNet {
|
||||||
|
//! Writes an AMFValue pointer to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFValue*>(AMFValue* value);
|
||||||
|
|
||||||
|
//! Writes an AMFUndefinedValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFUndefinedValue>(AMFUndefinedValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFNullValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFNullValue>(AMFNullValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFFalseValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFFalseValue>(AMFFalseValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFTrueValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFTrueValue>(AMFTrueValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFIntegerValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFIntegerValue>(AMFIntegerValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFDoubleValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFDoubleValue>(AMFDoubleValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFStringValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFStringValue>(AMFStringValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFXMLDocValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFXMLDocValue>(AMFXMLDocValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFDateValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFDateValue>(AMFDateValue value);
|
||||||
|
|
||||||
|
//! Writes an AMFArrayValue to a RakNet::BitStream
|
||||||
|
/*!
|
||||||
|
\param value The value to write
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
void RakNet::BitStream::Write<AMFArrayValue*>(AMFArrayValue* value);
|
||||||
|
} // namespace RakNet
|
||||||
359
dCommon/Amf3.h
359
dCommon/Amf3.h
@@ -1,359 +0,0 @@
|
|||||||
#ifndef __AMF3__H__
|
|
||||||
#define __AMF3__H__
|
|
||||||
|
|
||||||
#include "dCommonVars.h"
|
|
||||||
#include "Logger.h"
|
|
||||||
#include "Game.h"
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
enum class eAmf : uint8_t {
|
|
||||||
Undefined = 0x00, // An undefined AMF Value
|
|
||||||
Null = 0x01, // A null AMF value
|
|
||||||
False = 0x02, // A false AMF value
|
|
||||||
True = 0x03, // A true AMF value
|
|
||||||
Integer = 0x04, // An integer AMF value
|
|
||||||
Double = 0x05, // A double AMF value
|
|
||||||
String = 0x06, // A string AMF value
|
|
||||||
XMLDoc = 0x07, // Unused in the live client and cannot be serialized without modification. An XML Doc AMF value
|
|
||||||
Date = 0x08, // Unused in the live client and cannot be serialized without modification. A date AMF value
|
|
||||||
Array = 0x09, // An array AMF value
|
|
||||||
Object = 0x0A, // Unused in the live client and cannot be serialized without modification. An object AMF value
|
|
||||||
XML = 0x0B, // Unused in the live client and cannot be serialized without modification. An XML AMF value
|
|
||||||
ByteArray = 0x0C, // Unused in the live client and cannot be serialized without modification. A byte array AMF value
|
|
||||||
VectorInt = 0x0D, // Unused in the live client and cannot be serialized without modification. An integer vector AMF value
|
|
||||||
VectorUInt = 0x0E, // Unused in the live client and cannot be serialized without modification. An unsigned integer AMF value
|
|
||||||
VectorDouble = 0x0F, // Unused in the live client and cannot be serialized without modification. A double vector AMF value
|
|
||||||
VectorObject = 0x10, // Unused in the live client and cannot be serialized without modification. An object vector AMF value
|
|
||||||
Dictionary = 0x11 // Unused in the live client and cannot be serialized without modification. A dictionary AMF value
|
|
||||||
};
|
|
||||||
|
|
||||||
class AMFBaseValue {
|
|
||||||
public:
|
|
||||||
[[nodiscard]] constexpr virtual eAmf GetValueType() const noexcept { return eAmf::Undefined; }
|
|
||||||
constexpr AMFBaseValue() noexcept = default;
|
|
||||||
constexpr virtual ~AMFBaseValue() noexcept = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
// AMFValue template class instantiations
|
|
||||||
template <typename ValueType>
|
|
||||||
class AMFValue : public AMFBaseValue {
|
|
||||||
public:
|
|
||||||
AMFValue() = default;
|
|
||||||
AMFValue(const ValueType value) : m_Data{ value } {}
|
|
||||||
|
|
||||||
virtual ~AMFValue() override = default;
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override;
|
|
||||||
|
|
||||||
[[nodiscard]] const ValueType& GetValue() const { return m_Data; }
|
|
||||||
|
|
||||||
void SetValue(const ValueType value) { m_Data = value; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ValueType m_Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Explicit template class instantiations
|
|
||||||
template class AMFValue<std::nullptr_t>;
|
|
||||||
template class AMFValue<bool>;
|
|
||||||
template class AMFValue<int32_t>;
|
|
||||||
template class AMFValue<uint32_t>;
|
|
||||||
template class AMFValue<std::string>;
|
|
||||||
template class AMFValue<double>;
|
|
||||||
|
|
||||||
// AMFValue template class member function instantiations
|
|
||||||
template <> [[nodiscard]] constexpr eAmf AMFValue<std::nullptr_t>::GetValueType() const noexcept { return eAmf::Null; }
|
|
||||||
template <> [[nodiscard]] constexpr eAmf AMFValue<bool>::GetValueType() const noexcept { return m_Data ? eAmf::True : eAmf::False; }
|
|
||||||
template <> [[nodiscard]] constexpr eAmf AMFValue<int32_t>::GetValueType() const noexcept { return eAmf::Integer; }
|
|
||||||
template <> [[nodiscard]] constexpr eAmf AMFValue<uint32_t>::GetValueType() const noexcept { return eAmf::Integer; }
|
|
||||||
template <> [[nodiscard]] constexpr eAmf AMFValue<std::string>::GetValueType() const noexcept { return eAmf::String; }
|
|
||||||
template <> [[nodiscard]] constexpr eAmf AMFValue<double>::GetValueType() const noexcept { return eAmf::Double; }
|
|
||||||
|
|
||||||
template <typename ValueType>
|
|
||||||
[[nodiscard]] constexpr eAmf AMFValue<ValueType>::GetValueType() const noexcept { return eAmf::Undefined; }
|
|
||||||
|
|
||||||
// As a string this is much easier to write and read from a BitStream.
|
|
||||||
template <>
|
|
||||||
class AMFValue<const char*> : public AMFBaseValue {
|
|
||||||
public:
|
|
||||||
AMFValue() = default;
|
|
||||||
AMFValue(const char* value) { m_Data = value; }
|
|
||||||
virtual ~AMFValue() override = default;
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::String; }
|
|
||||||
|
|
||||||
[[nodiscard]] const std::string& GetValue() const { return m_Data; }
|
|
||||||
void SetValue(const std::string& value) { m_Data = value; }
|
|
||||||
protected:
|
|
||||||
std::string m_Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
using AMFNullValue = AMFValue<std::nullptr_t>;
|
|
||||||
using AMFBoolValue = AMFValue<bool>;
|
|
||||||
using AMFIntValue = AMFValue<int32_t>;
|
|
||||||
using AMFStringValue = AMFValue<std::string>;
|
|
||||||
using AMFDoubleValue = AMFValue<double>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The AMFArrayValue object holds 2 types of lists:
|
|
||||||
* An associative list where a key maps to a value
|
|
||||||
* A Dense list where elements are stored back to back
|
|
||||||
*
|
|
||||||
* Objects that are Registered are owned by this object
|
|
||||||
* and are not to be deleted by a caller.
|
|
||||||
*/
|
|
||||||
class AMFArrayValue : public AMFBaseValue {
|
|
||||||
using AMFAssociative =
|
|
||||||
std::unordered_map<std::string, std::unique_ptr<AMFBaseValue>, GeneralUtils::transparent_string_hash, std::equal_to<>>;
|
|
||||||
|
|
||||||
using AMFDense = std::vector<std::unique_ptr<AMFBaseValue>>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Associative portion of the object
|
|
||||||
*/
|
|
||||||
[[nodiscard]] inline const AMFAssociative& GetAssociative() const noexcept { return m_Associative; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the dense portion of the object
|
|
||||||
*/
|
|
||||||
[[nodiscard]] inline const AMFDense& GetDense() const noexcept { return m_Dense; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts an AMFValue into the associative portion with the given key.
|
|
||||||
* If a duplicate is attempted to be inserted, it is ignored and the
|
|
||||||
* first value with that key is kept in the map.
|
|
||||||
*
|
|
||||||
* These objects are not to be deleted by the caller as they are owned by
|
|
||||||
* the AMFArray object which manages its own memory.
|
|
||||||
*
|
|
||||||
* @param key The key to associate with the value
|
|
||||||
* @param value The value to insert
|
|
||||||
*
|
|
||||||
* @return The inserted element if the type matched,
|
|
||||||
* or nullptr if a key existed and was not the same type
|
|
||||||
*/
|
|
||||||
template <typename ValueType>
|
|
||||||
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string_view key, const ValueType value) {
|
|
||||||
const auto element = m_Associative.find(key);
|
|
||||||
AMFValue<ValueType>* val = nullptr;
|
|
||||||
bool found = true;
|
|
||||||
if (element == m_Associative.cend()) {
|
|
||||||
auto newVal = std::make_unique<AMFValue<ValueType>>(value);
|
|
||||||
val = newVal.get();
|
|
||||||
m_Associative.emplace(key, std::move(newVal));
|
|
||||||
} else {
|
|
||||||
val = dynamic_cast<AMFValue<ValueType>*>(element->second.get());
|
|
||||||
found = false;
|
|
||||||
}
|
|
||||||
return std::make_pair(val, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Associates an array with a string key
|
|
||||||
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string_view key) {
|
|
||||||
const auto element = m_Associative.find(key);
|
|
||||||
AMFArrayValue* val = nullptr;
|
|
||||||
bool found = true;
|
|
||||||
if (element == m_Associative.cend()) {
|
|
||||||
auto newVal = std::make_unique<AMFArrayValue>();
|
|
||||||
val = newVal.get();
|
|
||||||
m_Associative.emplace(key, std::move(newVal));
|
|
||||||
} else {
|
|
||||||
val = dynamic_cast<AMFArrayValue*>(element->second.get());
|
|
||||||
found = false;
|
|
||||||
}
|
|
||||||
return std::make_pair(val, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Associates an array with an integer key
|
|
||||||
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) {
|
|
||||||
bool inserted = false;
|
|
||||||
if (index >= m_Dense.size()) {
|
|
||||||
m_Dense.resize(index + 1);
|
|
||||||
m_Dense.at(index) = std::make_unique<AMFArrayValue>();
|
|
||||||
inserted = true;
|
|
||||||
}
|
|
||||||
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index).get()), inserted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Inserts an AMFValue into the AMFArray key'd by index.
|
|
||||||
* Attempting to insert the same key to the same value twice overwrites
|
|
||||||
* the previous value with the new one.
|
|
||||||
*
|
|
||||||
* @param index The index to associate with the value
|
|
||||||
* @param value The value to insert
|
|
||||||
* @return The inserted element, or nullptr if the type did not match
|
|
||||||
* what was at the index.
|
|
||||||
*/
|
|
||||||
template <typename ValueType>
|
|
||||||
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) {
|
|
||||||
bool inserted = false;
|
|
||||||
if (index >= m_Dense.size()) {
|
|
||||||
m_Dense.resize(index + 1);
|
|
||||||
m_Dense.at(index) = std::make_unique<AMFValue<ValueType>>(value);
|
|
||||||
inserted = true;
|
|
||||||
}
|
|
||||||
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index).get()), inserted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts an AMFValue into the associative portion with the given key.
|
|
||||||
* If a duplicate is attempted to be inserted, it replaces the original
|
|
||||||
*
|
|
||||||
* The inserted element is now owned by this object and is not to be deleted
|
|
||||||
*
|
|
||||||
* @param key The key to associate with the value
|
|
||||||
* @param value The value to insert
|
|
||||||
*/
|
|
||||||
void Insert(const std::string_view key, std::unique_ptr<AMFBaseValue> value) {
|
|
||||||
const auto element = m_Associative.find(key);
|
|
||||||
if (element != m_Associative.cend() && element->second) {
|
|
||||||
element->second = std::move(value);
|
|
||||||
} else {
|
|
||||||
m_Associative.emplace(key, std::move(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts an AMFValue into the associative portion with the given index.
|
|
||||||
* If a duplicate is attempted to be inserted, it replaces the original
|
|
||||||
*
|
|
||||||
* The inserted element is now owned by this object and is not to be deleted
|
|
||||||
*
|
|
||||||
* @param key The key to associate with the value
|
|
||||||
* @param value The value to insert
|
|
||||||
*/
|
|
||||||
void Insert(const size_t index, std::unique_ptr<AMFBaseValue> value) {
|
|
||||||
if (index >= m_Dense.size()) {
|
|
||||||
m_Dense.resize(index + 1);
|
|
||||||
}
|
|
||||||
m_Dense.at(index) = std::move(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes an AMFValue into the back of the dense portion.
|
|
||||||
*
|
|
||||||
* These objects are not to be deleted by the caller as they are owned by
|
|
||||||
* the AMFArray object which manages its own memory.
|
|
||||||
*
|
|
||||||
* @param value The value to insert
|
|
||||||
*
|
|
||||||
* @return The inserted pointer, or nullptr should the key already be in use.
|
|
||||||
*/
|
|
||||||
template <typename ValueType>
|
|
||||||
[[maybe_unused]] inline AMFValue<ValueType>* Push(const ValueType value) {
|
|
||||||
return Insert(m_Dense.size(), value).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the key from the associative portion
|
|
||||||
*
|
|
||||||
* The pointer removed is now no longer managed by this container
|
|
||||||
*
|
|
||||||
* @param key The key to remove from the associative portion
|
|
||||||
*/
|
|
||||||
void Remove(const std::string& key, const bool deleteValue = true) {
|
|
||||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
|
||||||
if (it != m_Associative.cend()) {
|
|
||||||
if (deleteValue) m_Associative.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pops the last element in the dense portion, deleting it in the process.
|
|
||||||
*/
|
|
||||||
void Remove(const size_t index) {
|
|
||||||
if (!m_Dense.empty() && index < m_Dense.size()) {
|
|
||||||
const auto itr = m_Dense.cbegin() + index;
|
|
||||||
m_Dense.erase(itr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pop() {
|
|
||||||
if (!m_Dense.empty()) Remove(m_Dense.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] AMFArrayValue* GetArray(const std::string_view key) const {
|
|
||||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
|
||||||
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second.get()) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] AMFArrayValue* GetArray(const size_t index) const {
|
|
||||||
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index).get()) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string_view key) {
|
|
||||||
return static_cast<AMFArrayValue*>(Insert(key).first);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[maybe_unused]] inline AMFArrayValue* InsertArray(const size_t index) {
|
|
||||||
return static_cast<AMFArrayValue*>(Insert(index).first);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[maybe_unused]] inline AMFArrayValue* PushArray() {
|
|
||||||
return static_cast<AMFArrayValue*>(Insert(m_Dense.size()).first);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an AMFValue by the key from the associative portion and converts it
|
|
||||||
* to the AmfValue template type. If the key did not exist, it is inserted.
|
|
||||||
*
|
|
||||||
* @tparam The target object type
|
|
||||||
* @param key The key to lookup
|
|
||||||
*
|
|
||||||
* @return The AMFValue
|
|
||||||
*/
|
|
||||||
template <typename AmfType>
|
|
||||||
[[nodiscard]] AMFValue<AmfType>* Get(const std::string_view key) const {
|
|
||||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
|
||||||
return it != m_Associative.cend() ?
|
|
||||||
dynamic_cast<AMFValue<AmfType>*>(it->second.get()) :
|
|
||||||
nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get from the array but dont cast it
|
|
||||||
[[nodiscard]] AMFBaseValue* Get(const std::string_view key) const {
|
|
||||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
|
||||||
return it != m_Associative.cend() ? it->second.get() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get an AMFValue object at a position in the dense portion.
|
|
||||||
* Gets an AMFValue by the index from the dense portion and converts it
|
|
||||||
* to the AmfValue template type. If the index did not exist, it is inserted.
|
|
||||||
*
|
|
||||||
* @tparam The target object type
|
|
||||||
* @param index The index to get
|
|
||||||
* @return The casted object, or nullptr.
|
|
||||||
*/
|
|
||||||
template <typename AmfType>
|
|
||||||
[[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const {
|
|
||||||
return index < m_Dense.size() ?
|
|
||||||
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index).get()) :
|
|
||||||
nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get from the dense but dont cast it
|
|
||||||
[[nodiscard]] AMFBaseValue* Get(const size_t index) const {
|
|
||||||
return index < m_Dense.size() ? m_Dense.at(index).get() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* The associative portion. These values are key'd with strings to an AMFValue.
|
|
||||||
*/
|
|
||||||
AMFAssociative m_Associative;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The dense portion. These AMFValue's are stored one after
|
|
||||||
* another with the most recent addition being at the back.
|
|
||||||
*/
|
|
||||||
AMFDense m_Dense;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //!__AMF3__H__
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
#include "AmfSerialize.h"
|
|
||||||
|
|
||||||
#include "Game.h"
|
|
||||||
#include "Logger.h"
|
|
||||||
|
|
||||||
// Writes an AMFValue pointer to a RakNet::BitStream
|
|
||||||
template<>
|
|
||||||
void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
|
|
||||||
eAmf type = value.GetValueType();
|
|
||||||
this->Write(type);
|
|
||||||
switch (type) {
|
|
||||||
case eAmf::Integer: {
|
|
||||||
this->Write<AMFIntValue&>(*static_cast<AMFIntValue*>(&value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case eAmf::Double: {
|
|
||||||
this->Write<AMFDoubleValue&>(*static_cast<AMFDoubleValue*>(&value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case eAmf::String: {
|
|
||||||
this->Write<AMFStringValue&>(*static_cast<AMFStringValue*>(&value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case eAmf::Array: {
|
|
||||||
this->Write<AMFArrayValue&>(*static_cast<AMFArrayValue*>(&value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
LOG("Encountered unwritable AMFType %i!", type);
|
|
||||||
}
|
|
||||||
case eAmf::Undefined:
|
|
||||||
case eAmf::Null:
|
|
||||||
case eAmf::False:
|
|
||||||
case eAmf::True:
|
|
||||||
case eAmf::Date:
|
|
||||||
case eAmf::Object:
|
|
||||||
case eAmf::XML:
|
|
||||||
case eAmf::XMLDoc:
|
|
||||||
case eAmf::ByteArray:
|
|
||||||
case eAmf::VectorInt:
|
|
||||||
case eAmf::VectorUInt:
|
|
||||||
case eAmf::VectorDouble:
|
|
||||||
case eAmf::VectorObject:
|
|
||||||
case eAmf::Dictionary:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A private function to write an value to a RakNet::BitStream
|
|
||||||
* RakNet writes in the correct byte order - do not reverse this.
|
|
||||||
*/
|
|
||||||
void WriteUInt29(RakNet::BitStream& bs, uint32_t v) {
|
|
||||||
unsigned char b4 = static_cast<unsigned char>(v);
|
|
||||||
if (v < 0x00200000) {
|
|
||||||
b4 = b4 & 0x7F;
|
|
||||||
if (v > 0x7F) {
|
|
||||||
unsigned char b3;
|
|
||||||
v = v >> 7;
|
|
||||||
b3 = static_cast<unsigned char>(v) | 0x80;
|
|
||||||
if (v > 0x7F) {
|
|
||||||
unsigned char b2;
|
|
||||||
v = v >> 7;
|
|
||||||
b2 = static_cast<unsigned char>(v) | 0x80;
|
|
||||||
bs.Write(b2);
|
|
||||||
}
|
|
||||||
|
|
||||||
bs.Write(b3);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unsigned char b1;
|
|
||||||
unsigned char b2;
|
|
||||||
unsigned char b3;
|
|
||||||
|
|
||||||
v = v >> 8;
|
|
||||||
b3 = static_cast<unsigned char>(v) | 0x80;
|
|
||||||
v = v >> 7;
|
|
||||||
b2 = static_cast<unsigned char>(v) | 0x80;
|
|
||||||
v = v >> 7;
|
|
||||||
b1 = static_cast<unsigned char>(v) | 0x80;
|
|
||||||
|
|
||||||
bs.Write(b1);
|
|
||||||
bs.Write(b2);
|
|
||||||
bs.Write(b3);
|
|
||||||
}
|
|
||||||
|
|
||||||
bs.Write(b4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a flag number to a RakNet::BitStream
|
|
||||||
* RakNet writes in the correct byte order - do not reverse this.
|
|
||||||
*/
|
|
||||||
void WriteFlagNumber(RakNet::BitStream& bs, uint32_t v) {
|
|
||||||
v = (v << 1) | 0x01;
|
|
||||||
WriteUInt29(bs, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes an AMFString to a RakNet::BitStream
|
|
||||||
*
|
|
||||||
* RakNet writes in the correct byte order - do not reverse this.
|
|
||||||
*/
|
|
||||||
void WriteAMFString(RakNet::BitStream& bs, const std::string& str) {
|
|
||||||
WriteFlagNumber(bs, static_cast<uint32_t>(str.size()));
|
|
||||||
bs.Write(str.c_str(), static_cast<uint32_t>(str.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes an U16 to a bitstream
|
|
||||||
*
|
|
||||||
* RakNet writes in the correct byte order - do not reverse this.
|
|
||||||
*/
|
|
||||||
void WriteAMFU16(RakNet::BitStream& bs, uint16_t value) {
|
|
||||||
bs.Write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes an U32 to a bitstream
|
|
||||||
*
|
|
||||||
* RakNet writes in the correct byte order - do not reverse this.
|
|
||||||
*/
|
|
||||||
void WriteAMFU32(RakNet::BitStream& bs, uint32_t value) {
|
|
||||||
bs.Write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes an U64 to a bitstream
|
|
||||||
*
|
|
||||||
* RakNet writes in the correct byte order - do not reverse this.
|
|
||||||
*/
|
|
||||||
void WriteAMFU64(RakNet::BitStream& bs, uint64_t value) {
|
|
||||||
bs.Write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes an AMFIntegerValue to BitStream
|
|
||||||
template<>
|
|
||||||
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value) {
|
|
||||||
WriteUInt29(*this, value.GetValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes an AMFDoubleValue to BitStream
|
|
||||||
template<>
|
|
||||||
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value) {
|
|
||||||
double d = value.GetValue();
|
|
||||||
WriteAMFU64(*this, *reinterpret_cast<uint64_t*>(&d));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes an AMFStringValue to BitStream
|
|
||||||
template<>
|
|
||||||
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value) {
|
|
||||||
WriteAMFString(*this, value.GetValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes an AMFArrayValue to BitStream
|
|
||||||
template<>
|
|
||||||
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value) {
|
|
||||||
uint32_t denseSize = value.GetDense().size();
|
|
||||||
WriteFlagNumber(*this, denseSize);
|
|
||||||
|
|
||||||
auto it = value.GetAssociative().begin();
|
|
||||||
auto end = value.GetAssociative().end();
|
|
||||||
|
|
||||||
while (it != end) {
|
|
||||||
WriteAMFString(*this, it->first);
|
|
||||||
this->Write<AMFBaseValue&>(*it->second);
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->Write(eAmf::Null);
|
|
||||||
|
|
||||||
if (denseSize > 0) {
|
|
||||||
auto it2 = value.GetDense().begin();
|
|
||||||
auto end2 = value.GetDense().end();
|
|
||||||
|
|
||||||
while (it2 != end2) {
|
|
||||||
this->Write<AMFBaseValue&>(**it2);
|
|
||||||
it2++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// Custom Classes
|
|
||||||
#include "Amf3.h"
|
|
||||||
|
|
||||||
// RakNet
|
|
||||||
#include "BitStream.h"
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\file AmfSerialize.h
|
|
||||||
\brief A class that implements native writing of AMF values to RakNet::BitStream
|
|
||||||
*/
|
|
||||||
|
|
||||||
// We are using the RakNet namespace
|
|
||||||
namespace RakNet {
|
|
||||||
//! Writes an AMFValue pointer to a RakNet::BitStream
|
|
||||||
/*!
|
|
||||||
\param value The value to write
|
|
||||||
*/
|
|
||||||
template <>
|
|
||||||
void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value);
|
|
||||||
|
|
||||||
//! Writes an AMFIntegerValue to a RakNet::BitStream
|
|
||||||
/*!
|
|
||||||
\param value The value to write
|
|
||||||
*/
|
|
||||||
template <>
|
|
||||||
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value);
|
|
||||||
|
|
||||||
//! Writes an AMFDoubleValue to a RakNet::BitStream
|
|
||||||
/*!
|
|
||||||
\param value The value to write
|
|
||||||
*/
|
|
||||||
template <>
|
|
||||||
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value);
|
|
||||||
|
|
||||||
//! Writes an AMFStringValue to a RakNet::BitStream
|
|
||||||
/*!
|
|
||||||
\param value The value to write
|
|
||||||
*/
|
|
||||||
template <>
|
|
||||||
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value);
|
|
||||||
|
|
||||||
//! Writes an AMFArrayValue to a RakNet::BitStream
|
|
||||||
/*!
|
|
||||||
\param value The value to write
|
|
||||||
*/
|
|
||||||
template <>
|
|
||||||
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value);
|
|
||||||
} // namespace RakNet
|
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
#include "BinaryIO.h"
|
#include "BinaryIO.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
void BinaryIO::WriteString(const std::string& stringToWrite, std::ofstream& outstream) {
|
||||||
|
//BinaryWrite(outstream, uint32_t(stringToWrite.length()));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size_t(stringToWrite.length()); ++i) {
|
||||||
|
BinaryIO::BinaryWrite(outstream, stringToWrite[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//For reading null-terminated strings
|
//For reading null-terminated strings
|
||||||
std::string BinaryIO::ReadString(std::istream& instream) {
|
std::string BinaryIO::ReadString(std::istream& instream) {
|
||||||
std::string toReturn;
|
std::string toReturn;
|
||||||
@@ -15,3 +23,36 @@ std::string BinaryIO::ReadString(std::istream& instream) {
|
|||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//For reading strings of a specific size
|
||||||
|
std::string BinaryIO::ReadString(std::istream& instream, size_t size) {
|
||||||
|
std::string toReturn;
|
||||||
|
char buffer;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
BinaryIO::BinaryRead(instream, buffer);
|
||||||
|
toReturn += buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BinaryIO::ReadWString(std::istream& instream) {
|
||||||
|
size_t size;
|
||||||
|
BinaryRead(instream, size);
|
||||||
|
//toReturn.resize(size);
|
||||||
|
std::string test;
|
||||||
|
unsigned char buf;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
//instream.ignore(1);
|
||||||
|
BinaryRead(instream, buf);
|
||||||
|
test += buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("%s\n", test.c_str());
|
||||||
|
|
||||||
|
//instream.read((char*)&toReturn[0], size * 2);
|
||||||
|
//std::string str(toReturn.begin(), toReturn.end());
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef __BINARYIO__H__
|
|
||||||
#define __BINARYIO__H__
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "Game.h"
|
|
||||||
#include "Logger.h"
|
|
||||||
|
|
||||||
namespace BinaryIO {
|
namespace BinaryIO {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::ostream& BinaryWrite(std::ostream& stream, const T& value) {
|
std::ostream& BinaryWrite(std::ostream& stream, const T& value) {
|
||||||
return stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
|
return stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
|
||||||
@@ -24,51 +15,13 @@ namespace BinaryIO {
|
|||||||
return stream.read(reinterpret_cast<char*>(&value), sizeof(T));
|
return stream.read(reinterpret_cast<char*>(&value), sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ReadType : int8_t {
|
void WriteString(const std::string& stringToWrite, std::ofstream& outstream);
|
||||||
WideString = 0,
|
|
||||||
String = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename SizeType>
|
|
||||||
inline void ReadString(std::istream& stream, std::u16string& value) {
|
|
||||||
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
|
|
||||||
|
|
||||||
SizeType size;
|
|
||||||
BinaryRead(stream, size);
|
|
||||||
|
|
||||||
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
|
|
||||||
value.resize(size);
|
|
||||||
stream.read(reinterpret_cast<char*>(value.data()), size * sizeof(uint16_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename SizeType>
|
|
||||||
inline void ReadString(std::istream& stream, std::string& value, ReadType readType) {
|
|
||||||
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
|
|
||||||
|
|
||||||
SizeType size;
|
|
||||||
BinaryRead(stream, size);
|
|
||||||
|
|
||||||
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
|
|
||||||
value.resize(size);
|
|
||||||
if (readType == ReadType::WideString) {
|
|
||||||
uint16_t wideChar;
|
|
||||||
|
|
||||||
// Faster to do this than to read a u16string and convert it to a string since we only go through allocator once
|
|
||||||
for (SizeType i = 0; i < size; ++i) {
|
|
||||||
BinaryRead(stream, wideChar);
|
|
||||||
value[i] = static_cast<char>(wideChar);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stream.read(value.data(), size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ReadString(std::istream& instream);
|
std::string ReadString(std::istream& instream);
|
||||||
|
std::string ReadString(std::istream& instream, size_t size);
|
||||||
|
std::string ReadWString(std::istream& instream);
|
||||||
|
|
||||||
inline bool DoesFileExist(const std::string& name) {
|
inline bool DoesFileExist(const std::string& name) {
|
||||||
std::ifstream f(name.c_str());
|
std::ifstream f(name.c_str());
|
||||||
return f.good();
|
return f.good();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //!__BINARYIO__H__
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
#ifndef __BRICK__H__
|
|
||||||
#define __BRICK__H__
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
struct Brick {
|
|
||||||
uint32_t designerID;
|
|
||||||
uint32_t materialID;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //!__BRICK__H__
|
|
||||||
@@ -9,12 +9,13 @@
|
|||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "ZCompression.h"
|
#include "ZCompression.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
|
|
||||||
//! Forward declarations
|
//! Forward declarations
|
||||||
|
|
||||||
|
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
|
||||||
void WriteSd0Magic(char* input, uint32_t chunkSize);
|
void WriteSd0Magic(char* input, uint32_t chunkSize);
|
||||||
bool CheckSd0Magic(std::istream& streamToCheck);
|
bool CheckSd0Magic(sql::Blob* streamToCheck);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Truncates all models with broken data from the database.
|
* @brief Truncates all models with broken data from the database.
|
||||||
@@ -23,24 +24,28 @@ bool CheckSd0Magic(std::istream& streamToCheck);
|
|||||||
*/
|
*/
|
||||||
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||||
uint32_t modelsTruncated{};
|
uint32_t modelsTruncated{};
|
||||||
auto modelsToTruncate = Database::Get()->GetAllUgcModels();
|
auto modelsToTruncate = GetModelsFromDatabase();
|
||||||
bool previousCommitValue = Database::Get()->GetAutoCommit();
|
bool previousCommitValue = Database::GetAutoCommit();
|
||||||
Database::Get()->SetAutoCommit(false);
|
Database::SetAutoCommit(false);
|
||||||
for (auto& model : modelsToTruncate) {
|
while (modelsToTruncate->next()) {
|
||||||
|
std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;"));
|
||||||
|
std::unique_ptr<sql::PreparedStatement> pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;"));
|
||||||
std::string completeUncompressedModel{};
|
std::string completeUncompressedModel{};
|
||||||
uint32_t chunkCount{};
|
uint32_t chunkCount{};
|
||||||
|
uint64_t modelId = modelsToTruncate->getInt(1);
|
||||||
|
std::unique_ptr<sql::Blob> modelAsSd0(modelsToTruncate->getBlob(2));
|
||||||
// Check that header is sd0 by checking for the sd0 magic.
|
// Check that header is sd0 by checking for the sd0 magic.
|
||||||
if (CheckSd0Magic(model.lxfmlData)) {
|
if (CheckSd0Magic(modelAsSd0.get())) {
|
||||||
while (true) {
|
while (true) {
|
||||||
uint32_t chunkSize{};
|
uint32_t chunkSize{};
|
||||||
model.lxfmlData.read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream
|
modelAsSd0->read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream
|
||||||
|
|
||||||
// Check if good here since if at the end of an sd0 file, this will have eof flagged.
|
// Check if good here since if at the end of an sd0 file, this will have eof flagged.
|
||||||
if (!model.lxfmlData.good()) break;
|
if (!modelAsSd0->good()) break;
|
||||||
|
|
||||||
std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]);
|
std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]);
|
||||||
for (uint32_t i = 0; i < chunkSize; i++) {
|
for (uint32_t i = 0; i < chunkSize; i++) {
|
||||||
compressedChunk[i] = model.lxfmlData.get();
|
compressedChunk[i] = modelAsSd0->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
|
// Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
|
||||||
@@ -51,17 +56,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
|||||||
|
|
||||||
if (actualUncompressedSize != -1) {
|
if (actualUncompressedSize != -1) {
|
||||||
uint32_t previousSize = completeUncompressedModel.size();
|
uint32_t previousSize = completeUncompressedModel.size();
|
||||||
completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get()));
|
completeUncompressedModel.append((char*)uncompressedChunk.get());
|
||||||
completeUncompressedModel.resize(previousSize + actualUncompressedSize);
|
completeUncompressedModel.resize(previousSize + actualUncompressedSize);
|
||||||
} else {
|
} else {
|
||||||
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err);
|
Game::logger->Log("BrickByBrickFix", "Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, modelId, err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
chunkCount++;
|
chunkCount++;
|
||||||
}
|
}
|
||||||
std::unique_ptr<tinyxml2::XMLDocument> document = std::make_unique<tinyxml2::XMLDocument>();
|
std::unique_ptr<tinyxml2::XMLDocument> document = std::make_unique<tinyxml2::XMLDocument>();
|
||||||
if (!document) {
|
if (!document) {
|
||||||
LOG("Failed to initialize tinyxml document. Aborting.");
|
Game::logger->Log("BrickByBrickFix", "Failed to initialize tinyxml document. Aborting.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,20 +75,28 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
|||||||
"</LXFML>",
|
"</LXFML>",
|
||||||
completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos
|
completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos
|
||||||
) {
|
) {
|
||||||
LOG("Brick-by-brick model %llu will be deleted!", model.id);
|
Game::logger->Log("BrickByBrickFix",
|
||||||
Database::Get()->DeleteUgcModelData(model.id);
|
"Brick-by-brick model %llu will be deleted!", modelId);
|
||||||
|
ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
|
||||||
|
pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
|
||||||
|
ugcModelToDelete->execute();
|
||||||
|
pcModelToDelete->execute();
|
||||||
modelsTruncated++;
|
modelsTruncated++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG("Brick-by-brick model %llu will be deleted!", model.id);
|
Game::logger->Log("BrickByBrickFix",
|
||||||
Database::Get()->DeleteUgcModelData(model.id);
|
"Brick-by-brick model %llu will be deleted!", modelId);
|
||||||
|
ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
|
||||||
|
pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
|
||||||
|
ugcModelToDelete->execute();
|
||||||
|
pcModelToDelete->execute();
|
||||||
modelsTruncated++;
|
modelsTruncated++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Database::Get()->Commit();
|
Database::Commit();
|
||||||
Database::Get()->SetAutoCommit(previousCommitValue);
|
Database::SetAutoCommit(previousCommitValue);
|
||||||
return modelsTruncated;
|
return modelsTruncated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,17 +108,21 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
|||||||
*/
|
*/
|
||||||
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||||
uint32_t updatedModels = 0;
|
uint32_t updatedModels = 0;
|
||||||
auto modelsToUpdate = Database::Get()->GetAllUgcModels();
|
auto modelsToUpdate = GetModelsFromDatabase();
|
||||||
auto previousAutoCommitState = Database::Get()->GetAutoCommit();
|
auto previousAutoCommitState = Database::GetAutoCommit();
|
||||||
Database::Get()->SetAutoCommit(false);
|
Database::SetAutoCommit(false);
|
||||||
for (auto& model : modelsToUpdate) {
|
std::unique_ptr<sql::PreparedStatement> insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;"));
|
||||||
|
while (modelsToUpdate->next()) {
|
||||||
|
int64_t modelId = modelsToUpdate->getInt64(1);
|
||||||
|
std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2));
|
||||||
// Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib)
|
// Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib)
|
||||||
// If it does, convert it to sd0.
|
// If it does, convert it to sd0.
|
||||||
if (model.lxfmlData.get() == 0x78 && model.lxfmlData.get() == 0xDA) {
|
if (oldLxfml->get() == 0x78 && oldLxfml->get() == 0xDA) {
|
||||||
|
|
||||||
// Get and save size of zlib compressed chunk.
|
// Get and save size of zlib compressed chunk.
|
||||||
model.lxfmlData.seekg(0, std::ios::end);
|
oldLxfml->seekg(0, std::ios::end);
|
||||||
uint32_t oldLxfmlSize = static_cast<uint32_t>(model.lxfmlData.tellg());
|
uint32_t oldLxfmlSize = static_cast<uint32_t>(oldLxfml->tellg());
|
||||||
model.lxfmlData.seekg(0);
|
oldLxfml->seekg(0);
|
||||||
|
|
||||||
// Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size.
|
// Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size.
|
||||||
uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9;
|
uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9;
|
||||||
@@ -113,27 +130,36 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
|||||||
|
|
||||||
WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize);
|
WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize);
|
||||||
for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) {
|
for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) {
|
||||||
sd0ConvertedModel.get()[i] = model.lxfmlData.get();
|
sd0ConvertedModel.get()[i] = oldLxfml->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
|
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
|
||||||
std::istringstream outputStringStream(outputString);
|
std::istringstream outputStringStream(outputString);
|
||||||
|
|
||||||
|
insertionStatement->setBlob(1, static_cast<std::istream*>(&outputStringStream));
|
||||||
|
insertionStatement->setInt64(2, modelId);
|
||||||
try {
|
try {
|
||||||
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
|
insertionStatement->executeUpdate();
|
||||||
LOG("Updated model %i to sd0", model.id);
|
Game::logger->Log("BrickByBrickFix", "Updated model %i to sd0", modelId);
|
||||||
updatedModels++;
|
updatedModels++;
|
||||||
} catch (sql::SQLException exception) {
|
} catch (sql::SQLException exception) {
|
||||||
LOG("Failed to update model %i. This model should be inspected manually to see why."
|
Game::logger->Log(
|
||||||
"The database error is %s", model.id, exception.what());
|
"BrickByBrickFix",
|
||||||
|
"Failed to update model %i. This model should be inspected manually to see why."
|
||||||
|
"The database error is %s", modelId, exception.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Database::Get()->Commit();
|
Database::Commit();
|
||||||
Database::Get()->SetAutoCommit(previousAutoCommitState);
|
Database::SetAutoCommit(previousAutoCommitState);
|
||||||
return updatedModels;
|
return updatedModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase() {
|
||||||
|
std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;"));
|
||||||
|
return std::unique_ptr<sql::ResultSet>(modelsRawDataQuery->executeQuery());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Writes sd0 magic at the front of a char*
|
* @brief Writes sd0 magic at the front of a char*
|
||||||
*
|
*
|
||||||
@@ -149,6 +175,6 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) {
|
|||||||
*reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array
|
*reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckSd0Magic(std::istream& streamToCheck) {
|
bool CheckSd0Magic(sql::Blob* streamToCheck) {
|
||||||
return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF;
|
return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,46 @@
|
|||||||
set(DCOMMON_SOURCES
|
set(DCOMMON_SOURCES "AMFFormat.cpp"
|
||||||
"AMFDeserialize.cpp"
|
"AMFDeserialize.cpp"
|
||||||
"AmfSerialize.cpp"
|
"AMFFormat_BitStream.cpp"
|
||||||
"BinaryIO.cpp"
|
"BinaryIO.cpp"
|
||||||
"dConfig.cpp"
|
"dConfig.cpp"
|
||||||
"Diagnostics.cpp"
|
"Diagnostics.cpp"
|
||||||
"Logger.cpp"
|
"dLogger.cpp"
|
||||||
"Game.cpp"
|
|
||||||
"GeneralUtils.cpp"
|
"GeneralUtils.cpp"
|
||||||
"LDFFormat.cpp"
|
"LDFFormat.cpp"
|
||||||
|
"MD5.cpp"
|
||||||
"Metrics.cpp"
|
"Metrics.cpp"
|
||||||
"NiPoint3.cpp"
|
"NiPoint3.cpp"
|
||||||
"NiQuaternion.cpp"
|
"NiQuaternion.cpp"
|
||||||
"Demangler.cpp"
|
"SHA512.cpp"
|
||||||
|
"Type.cpp"
|
||||||
"ZCompression.cpp"
|
"ZCompression.cpp"
|
||||||
"BrickByBrickFix.cpp"
|
"BrickByBrickFix.cpp"
|
||||||
"BinaryPathFinder.cpp"
|
"BinaryPathFinder.cpp"
|
||||||
"FdbToSqlite.cpp"
|
"FdbToSqlite.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible.
|
|
||||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
||||||
set_source_files_properties("FdbToSqlite.cpp" PROPERTIES COMPILE_FLAGS "-Wno-stringop-overflow")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_subdirectory(dClient)
|
add_subdirectory(dClient)
|
||||||
|
|
||||||
foreach(file ${DCOMMON_DCLIENT_SOURCES})
|
foreach(file ${DCOMMON_DCLIENT_SOURCES})
|
||||||
set(DCOMMON_SOURCES ${DCOMMON_SOURCES} "dClient/${file}")
|
set(DCOMMON_SOURCES ${DCOMMON_SOURCES} "dClient/${file}")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/dCommon/)
|
||||||
|
|
||||||
add_library(dCommon STATIC ${DCOMMON_SOURCES})
|
add_library(dCommon STATIC ${DCOMMON_SOURCES})
|
||||||
target_include_directories(dCommon
|
|
||||||
PUBLIC "." "dClient" "dEnums"
|
target_link_libraries(dCommon bcrypt dDatabase tinyxml2)
|
||||||
PRIVATE
|
|
||||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
|
|
||||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
|
|
||||||
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
|
|
||||||
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
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/zlib-ng/zlib-ng/archive/refs/tags/2.2.2.zip
|
URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip
|
||||||
URL_HASH MD5=2cf9199fb785ea579a2a9905a75c38b3
|
URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1
|
||||||
)
|
)
|
||||||
|
|
||||||
# Disable warning about no project version.
|
# Disable warning about no project version.
|
||||||
@@ -63,6 +51,7 @@ 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(
|
||||||
@@ -71,6 +60,4 @@ else ()
|
|||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_link_libraries(dCommon
|
target_link_libraries(dCommon ZLIB::ZLIB)
|
||||||
PRIVATE ZLIB::ZLIB bcrypt tinyxml2
|
|
||||||
INTERFACE dDatabase)
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
#include "Demangler.h"
|
|
||||||
#ifdef __GNUG__
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cxxabi.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
std::string Demangler::Demangle(const char* name) {
|
|
||||||
// some arbitrary value to eliminate the compiler warning
|
|
||||||
// -4 is not a valid return value for __cxa_demangle so we'll use that.
|
|
||||||
int status = -4;
|
|
||||||
|
|
||||||
// __cxa_demangle requires that we free the returned char*
|
|
||||||
std::unique_ptr<char, void (*)(void*)> res{
|
|
||||||
abi::__cxa_demangle(name, NULL, NULL, &status),
|
|
||||||
std::free
|
|
||||||
};
|
|
||||||
|
|
||||||
return (status == 0) ? res.get() : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // __GNUG__
|
|
||||||
|
|
||||||
// does nothing if not g++
|
|
||||||
std::string Demangler::Demangle(const char* name) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __GNUG__
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Demangler {
|
|
||||||
// Given a char* containing a mangled name, return a std::string containing the demangled name.
|
|
||||||
// If the function fails for any reason, it returns an empty string.
|
|
||||||
std::string Demangle(const char* name);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "Diagnostics.h"
|
#include "Diagnostics.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
|
|
||||||
// If we're on Win32, we'll include our minidump writer
|
// If we're on Win32, we'll include our minidump writer
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <Dbghelp.h>
|
#include <Dbghelp.h>
|
||||||
|
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
|
|
||||||
void make_minidump(EXCEPTION_POINTERS* e) {
|
void make_minidump(EXCEPTION_POINTERS* e) {
|
||||||
auto hDbgHelp = LoadLibraryA("dbghelp");
|
auto hDbgHelp = LoadLibraryA("dbghelp");
|
||||||
@@ -28,7 +28,7 @@ void make_minidump(EXCEPTION_POINTERS* e) {
|
|||||||
"_%4d%02d%02d_%02d%02d%02d.dmp",
|
"_%4d%02d%02d_%02d%02d%02d.dmp",
|
||||||
t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
|
t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
|
||||||
}
|
}
|
||||||
LOG("Creating crash dump %s", name);
|
Game::logger->Log("Diagnostics", "Creating crash dump %s", name);
|
||||||
auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
if (hFile == INVALID_HANDLE_VALUE)
|
if (hFile == INVALID_HANDLE_VALUE)
|
||||||
return;
|
return;
|
||||||
@@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) {
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
#if defined(INCLUDE_BACKTRACE)
|
#if defined(__include_backtrace__)
|
||||||
#include <backtrace.h>
|
#include <backtrace.h>
|
||||||
|
|
||||||
#include <backtrace-supported.h>
|
#include <backtrace-supported.h>
|
||||||
@@ -83,7 +83,7 @@ struct bt_ctx {
|
|||||||
|
|
||||||
static inline void Bt(struct backtrace_state* state) {
|
static inline void Bt(struct backtrace_state* state) {
|
||||||
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
|
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
|
||||||
LOG("backtrace is enabled, crash dump located at %s", fileName.c_str());
|
Game::logger->Log("Diagnostics", "backtrace is enabled, crash dump located at %s", fileName.c_str());
|
||||||
FILE* file = fopen(fileName.c_str(), "w+");
|
FILE* file = fopen(fileName.c_str(), "w+");
|
||||||
if (file != nullptr) {
|
if (file != nullptr) {
|
||||||
backtrace_print(state, 2, file);
|
backtrace_print(state, 2, file);
|
||||||
@@ -107,76 +107,66 @@ static void ErrorCallback(void* data, const char* msg, int errnum) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Demangler.h"
|
#include "Type.h"
|
||||||
|
|
||||||
void GenerateDump() {
|
void GenerateDump() {
|
||||||
std::string cmd = "sudo gcore " + std::to_string(getpid());
|
std::string cmd = "sudo gcore " + std::to_string(getpid());
|
||||||
int ret = system(cmd.c_str()); // Saving a return just to prevent warning
|
system(cmd.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatchUnhandled(int sig) {
|
void CatchUnhandled(int sig) {
|
||||||
std::exception_ptr eptr = std::current_exception();
|
#ifndef __include_backtrace__
|
||||||
try {
|
|
||||||
if (eptr) std::rethrow_exception(eptr);
|
|
||||||
} catch(const std::exception& e) {
|
|
||||||
LOG("Caught exception: '%s'", e.what());
|
|
||||||
} catch (...) {
|
|
||||||
LOG("Caught unknown exception.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef INCLUDE_BACKTRACE
|
|
||||||
|
|
||||||
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
|
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
|
||||||
LOG("Encountered signal %i, creating crash dump %s", sig, fileName.c_str());
|
Game::logger->Log("Diagnostics", "Encountered signal %i, creating crash dump %s", sig, fileName.c_str());
|
||||||
if (Diagnostics::GetProduceMemoryDump()) {
|
if (Diagnostics::GetProduceMemoryDump()) {
|
||||||
GenerateDump();
|
GenerateDump();
|
||||||
}
|
}
|
||||||
constexpr uint8_t MaxStackTrace = 32;
|
|
||||||
void* array[MaxStackTrace];
|
void* array[10];
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
// get void*'s for all entries on the stack
|
// get void*'s for all entries on the stack
|
||||||
size = backtrace(array, MaxStackTrace);
|
size = backtrace(array, 10);
|
||||||
|
|
||||||
# if defined(__GNUG__)
|
#if defined(__GNUG__) and defined(__dynamic)
|
||||||
|
|
||||||
// Loop through the returned addresses, and get the symbols to be demangled
|
// Loop through the returned addresses, and get the symbols to be demangled
|
||||||
char** strings = backtrace_symbols(array, size);
|
char** strings = backtrace_symbols(array, size);
|
||||||
|
|
||||||
FILE* file = fopen(fileName.c_str(), "w+");
|
|
||||||
if (file != NULL) {
|
|
||||||
fprintf(file, "Error: signal %d:\n", sig);
|
|
||||||
}
|
|
||||||
// Print the stack trace
|
// Print the stack trace
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
|
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' and extract the function name
|
||||||
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
|
|
||||||
std::string functionName = strings[i];
|
std::string functionName = strings[i];
|
||||||
std::string::size_type start = functionName.find('(');
|
std::string::size_type start = functionName.find('(');
|
||||||
std::string::size_type end = functionName.find('+');
|
std::string::size_type end = functionName.find('+');
|
||||||
if (start != std::string::npos && end != std::string::npos) {
|
if (start != std::string::npos && end != std::string::npos) {
|
||||||
std::string demangled = functionName.substr(start + 1, end - start - 1);
|
std::string demangled = functionName.substr(start + 1, end - start - 1);
|
||||||
|
|
||||||
demangled = Demangler::Demangle(demangled.c_str());
|
demangled = demangle(functionName.c_str());
|
||||||
|
|
||||||
// If the demangled string is not empty, then we can replace the mangled string with the demangled one
|
if (demangled.empty()) {
|
||||||
if (!demangled.empty()) {
|
Game::logger->Log("Diagnostics", "[%02zu] %s", i, demangled.c_str());
|
||||||
demangled.push_back('(');
|
} else {
|
||||||
demangled += functionName.substr(end);
|
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
|
||||||
functionName = demangled;
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
|
||||||
LOG("[%02zu] %s", i, functionName.c_str());
|
|
||||||
if (file != NULL) {
|
|
||||||
fprintf(file, "[%02zu] %s\n", i, functionName.c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# else // defined(__GNUG__)
|
#else
|
||||||
backtrace_symbols_fd(array, size, STDOUT_FILENO);
|
backtrace_symbols_fd(array, size, STDOUT_FILENO);
|
||||||
# endif // defined(__GNUG__)
|
#endif
|
||||||
|
|
||||||
#else // INCLUDE_BACKTRACE
|
FILE* file = fopen(fileName.c_str(), "w+");
|
||||||
|
if (file != NULL) {
|
||||||
|
// print out all the frames to stderr
|
||||||
|
fprintf(file, "Error: signal %d:\n", sig);
|
||||||
|
backtrace_symbols_fd(array, size, fileno(file));
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
struct backtrace_state* state = backtrace_create_state(
|
struct backtrace_state* state = backtrace_create_state(
|
||||||
Diagnostics::GetProcessFileName().c_str(),
|
Diagnostics::GetProcessFileName().c_str(),
|
||||||
@@ -187,7 +177,7 @@ void CatchUnhandled(int sig) {
|
|||||||
struct bt_ctx ctx = { state, 0 };
|
struct bt_ctx ctx = { state, 0 };
|
||||||
Bt(state);
|
Bt(state);
|
||||||
|
|
||||||
#endif // INCLUDE_BACKTRACE
|
#endif
|
||||||
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -201,15 +191,15 @@ void OnTerminate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MakeBacktrace() {
|
void MakeBacktrace() {
|
||||||
struct sigaction sigact{};
|
struct sigaction sigact;
|
||||||
|
|
||||||
sigact.sa_sigaction = CritErrHdlr;
|
sigact.sa_sigaction = CritErrHdlr;
|
||||||
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||||
|
|
||||||
if (sigaction(SIGSEGV, &sigact, nullptr) != 0 ||
|
if (sigaction(SIGSEGV, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||||
sigaction(SIGFPE, &sigact, nullptr) != 0 ||
|
sigaction(SIGFPE, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||||
sigaction(SIGABRT, &sigact, nullptr) != 0 ||
|
sigaction(SIGABRT, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||||
sigaction(SIGILL, &sigact, nullptr) != 0) {
|
sigaction(SIGILL, &sigact, (struct sigaction*)nullptr) != 0) {
|
||||||
fprintf(stderr, "error setting signal handler for %d (%s)\n",
|
fprintf(stderr, "error setting signal handler for %d (%s)\n",
|
||||||
SIGSEGV,
|
SIGSEGV,
|
||||||
strsignal(SIGSEGV));
|
strsignal(SIGSEGV));
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
#ifndef __DLUASSERT__H__
|
|
||||||
#define __DLUASSERT__H__
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
# define DluAssert(expression) assert(expression)
|
|
||||||
#else
|
|
||||||
# define DluAssert(expression)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif //!__DLUASSERT__H__
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "CDClientDatabase.h"
|
#include "CDClientDatabase.h"
|
||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
#include "AssetManager.h"
|
#include "AssetManager.h"
|
||||||
|
|
||||||
#include "eSqliteDataType.h"
|
#include "eSqliteDataType.h"
|
||||||
@@ -28,21 +28,23 @@ FdbToSqlite::Convert::Convert(std::string binaryOutPath) {
|
|||||||
this->m_BinaryOutPath = binaryOutPath;
|
this->m_BinaryOutPath = binaryOutPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FdbToSqlite::Convert::ConvertDatabase(AssetStream& buffer) {
|
bool FdbToSqlite::Convert::ConvertDatabase(AssetMemoryBuffer& buffer) {
|
||||||
if (m_ConversionStarted) return false;
|
if (m_ConversionStarted) return false;
|
||||||
|
|
||||||
|
std::istream cdClientBuffer(&buffer);
|
||||||
|
|
||||||
this->m_ConversionStarted = true;
|
this->m_ConversionStarted = true;
|
||||||
try {
|
try {
|
||||||
CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite");
|
CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite");
|
||||||
|
|
||||||
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
|
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
|
||||||
|
|
||||||
int32_t numberOfTables = ReadInt32(buffer);
|
int32_t numberOfTables = ReadInt32(cdClientBuffer);
|
||||||
ReadTables(numberOfTables, buffer);
|
ReadTables(numberOfTables, cdClientBuffer);
|
||||||
|
|
||||||
CDClientDatabase::ExecuteQuery("COMMIT;");
|
CDClientDatabase::ExecuteQuery("COMMIT;");
|
||||||
} catch (CppSQLite3Exception& e) {
|
} catch (CppSQLite3Exception& e) {
|
||||||
LOG("Encountered error %s converting FDB to SQLite", e.errorMessage());
|
Game::logger->Log("FdbToSqlite", "Encountered error %s converting FDB to SQLite", e.errorMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "AssetManager.h"
|
class AssetMemoryBuffer;
|
||||||
|
|
||||||
enum class eSqliteDataType : int32_t;
|
enum class eSqliteDataType : int32_t;
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ namespace FdbToSqlite {
|
|||||||
*
|
*
|
||||||
* @return true if the database was converted properly, false otherwise.
|
* @return true if the database was converted properly, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool ConvertDatabase(AssetStream& buffer);
|
bool ConvertDatabase(AssetMemoryBuffer& buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reads a 32 bit int from the fdb file.
|
* @brief Reads a 32 bit int from the fdb file.
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
#include "Game.h"
|
|
||||||
|
|
||||||
namespace Game {
|
|
||||||
void OnSignal(int signal) {
|
|
||||||
lastSignal = signal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <csignal>
|
|
||||||
|
|
||||||
class dServer;
|
class dServer;
|
||||||
class Logger;
|
class dLogger;
|
||||||
class InstanceManager;
|
class InstanceManager;
|
||||||
class dChatFilter;
|
class dChatFilter;
|
||||||
class dConfig;
|
class dConfig;
|
||||||
class RakPeerInterface;
|
class RakPeerInterface;
|
||||||
class AssetManager;
|
class AssetManager;
|
||||||
struct SystemAddress;
|
struct SystemAddress;
|
||||||
class EntityManager;
|
|
||||||
class dZoneManager;
|
|
||||||
class PlayerContainer;
|
|
||||||
|
|
||||||
namespace Game {
|
namespace Game {
|
||||||
using signal_t = volatile std::sig_atomic_t;
|
extern dLogger* logger;
|
||||||
extern Logger* logger;
|
|
||||||
extern dServer* server;
|
extern dServer* server;
|
||||||
extern InstanceManager* im;
|
extern InstanceManager* im;
|
||||||
extern dChatFilter* chatFilter;
|
extern dChatFilter* chatFilter;
|
||||||
@@ -27,14 +21,5 @@ namespace Game {
|
|||||||
extern RakPeerInterface* chatServer;
|
extern RakPeerInterface* chatServer;
|
||||||
extern AssetManager* assetManager;
|
extern AssetManager* assetManager;
|
||||||
extern SystemAddress chatSysAddr;
|
extern SystemAddress chatSysAddr;
|
||||||
extern signal_t lastSignal;
|
extern bool shouldShutdown;
|
||||||
extern EntityManager* entityManager;
|
|
||||||
extern dZoneManager* zoneManager;
|
|
||||||
extern PlayerContainer playerContainer;
|
|
||||||
extern std::string projectVersion;
|
|
||||||
|
|
||||||
inline bool ShouldShutdown() {
|
|
||||||
return lastSignal != 0;
|
|
||||||
}
|
|
||||||
void OnSignal(int signal);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,23 +8,23 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
|
inline size_t MinSize(size_t size, const std::basic_string_view<T>& string) {
|
||||||
if (size == SIZE_MAX || size > string.size()) {
|
if (size == size_t(-1) || size > string.size()) {
|
||||||
return string.size();
|
return string.size();
|
||||||
} else {
|
} else {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsLeadSurrogate(const char16_t c) {
|
inline bool IsLeadSurrogate(char16_t c) {
|
||||||
return (0xD800 <= c) && (c <= 0xDBFF);
|
return (0xD800 <= c) && (c <= 0xDBFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsTrailSurrogate(const char16_t c) {
|
inline bool IsTrailSurrogate(char16_t c) {
|
||||||
return (0xDC00 <= c) && (c <= 0xDFFF);
|
return (0xDC00 <= c) && (c <= 0xDFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void PushUTF8CodePoint(std::string& ret, const char32_t cp) {
|
inline void PushUTF8CodePoint(std::string& ret, char32_t cp) {
|
||||||
if (cp <= 0x007F) {
|
if (cp <= 0x007F) {
|
||||||
ret.push_back(static_cast<uint8_t>(cp));
|
ret.push_back(static_cast<uint8_t>(cp));
|
||||||
} else if (cp <= 0x07FF) {
|
} else if (cp <= 0x07FF) {
|
||||||
@@ -46,16 +46,16 @@ inline void PushUTF8CodePoint(std::string& ret, const char32_t cp) {
|
|||||||
|
|
||||||
constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
|
constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
|
||||||
|
|
||||||
bool static _IsSuffixChar(const uint8_t c) {
|
bool _IsSuffixChar(uint8_t c) {
|
||||||
return (c & 0xC0) == 0x80;
|
return (c & 0xC0) == 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||||
const size_t rem = slice.length();
|
size_t rem = slice.length();
|
||||||
if (slice.empty()) return false;
|
if (slice.empty()) return false;
|
||||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
|
const uint8_t* bytes = (const uint8_t*)&slice.front();
|
||||||
if (rem > 0) {
|
if (rem > 0) {
|
||||||
const uint8_t first = bytes[0];
|
uint8_t first = bytes[0];
|
||||||
if (first < 0x80) { // 1 byte character
|
if (first < 0x80) { // 1 byte character
|
||||||
out = static_cast<uint32_t>(first & 0x7F);
|
out = static_cast<uint32_t>(first & 0x7F);
|
||||||
slice.remove_prefix(1);
|
slice.remove_prefix(1);
|
||||||
@@ -64,7 +64,7 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
|
|||||||
// middle byte, not valid at start, fall through
|
// middle byte, not valid at start, fall through
|
||||||
} else if (first < 0xE0) { // two byte character
|
} else if (first < 0xE0) { // two byte character
|
||||||
if (rem > 1) {
|
if (rem > 1) {
|
||||||
const uint8_t second = bytes[1];
|
uint8_t second = bytes[1];
|
||||||
if (_IsSuffixChar(second)) {
|
if (_IsSuffixChar(second)) {
|
||||||
out = (static_cast<uint32_t>(first & 0x1F) << 6)
|
out = (static_cast<uint32_t>(first & 0x1F) << 6)
|
||||||
+ static_cast<uint32_t>(second & 0x3F);
|
+ static_cast<uint32_t>(second & 0x3F);
|
||||||
@@ -74,8 +74,8 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
|
|||||||
}
|
}
|
||||||
} else if (first < 0xF0) { // three byte character
|
} else if (first < 0xF0) { // three byte character
|
||||||
if (rem > 2) {
|
if (rem > 2) {
|
||||||
const uint8_t second = bytes[1];
|
uint8_t second = bytes[1];
|
||||||
const uint8_t third = bytes[2];
|
uint8_t third = bytes[2];
|
||||||
if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
|
if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
|
||||||
out = (static_cast<uint32_t>(first & 0x0F) << 12)
|
out = (static_cast<uint32_t>(first & 0x0F) << 12)
|
||||||
+ (static_cast<uint32_t>(second & 0x3F) << 6)
|
+ (static_cast<uint32_t>(second & 0x3F) << 6)
|
||||||
@@ -86,9 +86,9 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
|
|||||||
}
|
}
|
||||||
} else if (first < 0xF8) { // four byte character
|
} else if (first < 0xF8) { // four byte character
|
||||||
if (rem > 3) {
|
if (rem > 3) {
|
||||||
const uint8_t second = bytes[1];
|
uint8_t second = bytes[1];
|
||||||
const uint8_t third = bytes[2];
|
uint8_t third = bytes[2];
|
||||||
const uint8_t fourth = bytes[3];
|
uint8_t fourth = bytes[3];
|
||||||
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
|
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
|
||||||
out = (static_cast<uint32_t>(first & 0x07) << 18)
|
out = (static_cast<uint32_t>(first & 0x07) << 18)
|
||||||
+ (static_cast<uint32_t>(second & 0x3F) << 12)
|
+ (static_cast<uint32_t>(second & 0x3F) << 12)
|
||||||
@@ -107,7 +107,7 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1>
|
/// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1>
|
||||||
bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t size) {
|
bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
|
||||||
if (output.length() >= size) return false;
|
if (output.length() >= size) return false;
|
||||||
if (U < 0x10000) {
|
if (U < 0x10000) {
|
||||||
// If U < 0x10000, encode U as a 16-bit unsigned integer and terminate.
|
// If U < 0x10000, encode U as a 16-bit unsigned integer and terminate.
|
||||||
@@ -120,7 +120,7 @@ bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t s
|
|||||||
// Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
|
// Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
|
||||||
// U' must be less than or equal to 0xFFFFF. That is, U' can be
|
// U' must be less than or equal to 0xFFFFF. That is, U' can be
|
||||||
// represented in 20 bits.
|
// represented in 20 bits.
|
||||||
const uint32_t Ut = U - 0x10000;
|
uint32_t Ut = U - 0x10000;
|
||||||
|
|
||||||
// Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
|
// Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
|
||||||
// 0xDC00, respectively. These integers each have 10 bits free to
|
// 0xDC00, respectively. These integers each have 10 bits free to
|
||||||
@@ -141,25 +141,25 @@ bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t s
|
|||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view string, const size_t size) {
|
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view& string, size_t size) {
|
||||||
const size_t newSize = MinSize(size, string);
|
size_t newSize = MinSize(size, string);
|
||||||
std::u16string output;
|
std::u16string output;
|
||||||
output.reserve(newSize);
|
output.reserve(newSize);
|
||||||
std::string_view iterator = string;
|
std::string_view iterator = string;
|
||||||
|
|
||||||
uint32_t c;
|
uint32_t c;
|
||||||
while (details::_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
|
while (_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Converts an std::string (ASCII) to UCS-2 / UTF-16
|
//! Converts an std::string (ASCII) to UCS-2 / UTF-16
|
||||||
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const size_t size) {
|
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t size) {
|
||||||
const size_t newSize = MinSize(size, string);
|
size_t newSize = MinSize(size, string);
|
||||||
std::u16string ret;
|
std::u16string ret;
|
||||||
ret.reserve(newSize);
|
ret.reserve(newSize);
|
||||||
|
|
||||||
for (size_t i = 0; i < newSize; ++i) {
|
for (size_t i = 0; i < newSize; i++) {
|
||||||
const char c = string[i];
|
char c = string[i];
|
||||||
// Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t
|
// Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t
|
||||||
ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER);
|
ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER);
|
||||||
}
|
}
|
||||||
@@ -169,18 +169,18 @@ std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const s
|
|||||||
|
|
||||||
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
|
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
|
||||||
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
|
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
|
||||||
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) {
|
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t size) {
|
||||||
const size_t newSize = MinSize(size, string);
|
size_t newSize = MinSize(size, string);
|
||||||
std::string ret;
|
std::string ret;
|
||||||
ret.reserve(newSize);
|
ret.reserve(newSize);
|
||||||
|
|
||||||
for (size_t i = 0; i < newSize; ++i) {
|
for (size_t i = 0; i < newSize; i++) {
|
||||||
const char16_t u = string[i];
|
char16_t u = string[i];
|
||||||
if (IsLeadSurrogate(u) && (i + 1) < newSize) {
|
if (IsLeadSurrogate(u) && (i + 1) < newSize) {
|
||||||
const char16_t next = string[i + 1];
|
char16_t next = string[i + 1];
|
||||||
if (IsTrailSurrogate(next)) {
|
if (IsTrailSurrogate(next)) {
|
||||||
i += 1;
|
i += 1;
|
||||||
const char32_t cp = 0x10000
|
char32_t cp = 0x10000
|
||||||
+ ((static_cast<char32_t>(u) - 0xD800) << 10)
|
+ ((static_cast<char32_t>(u) - 0xD800) << 10)
|
||||||
+ (static_cast<char32_t>(next) - 0xDC00);
|
+ (static_cast<char32_t>(next) - 0xDC00);
|
||||||
PushUTF8CodePoint(ret, cp);
|
PushUTF8CodePoint(ret, cp);
|
||||||
@@ -195,40 +195,40 @@ std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const si
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b) {
|
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string& a, const std::string& b) {
|
||||||
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
|
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Bits
|
// MARK: Bits
|
||||||
|
|
||||||
//! Sets a specific bit in a signed 64-bit integer
|
//! Sets a specific bit in a signed 64-bit integer
|
||||||
int64_t GeneralUtils::SetBit(int64_t value, const uint32_t index) {
|
int64_t GeneralUtils::SetBit(int64_t value, uint32_t index) {
|
||||||
return value |= 1ULL << index;
|
return value |= 1ULL << index;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Clears a specific bit in a signed 64-bit integer
|
//! Clears a specific bit in a signed 64-bit integer
|
||||||
int64_t GeneralUtils::ClearBit(int64_t value, const uint32_t index) {
|
int64_t GeneralUtils::ClearBit(int64_t value, uint32_t index) {
|
||||||
return value &= ~(1ULL << index);
|
return value &= ~(1ULL << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Checks a specific bit in a signed 64-bit integer
|
//! Checks a specific bit in a signed 64-bit integer
|
||||||
bool GeneralUtils::CheckBit(int64_t value, const uint32_t index) {
|
bool GeneralUtils::CheckBit(int64_t value, uint32_t index) {
|
||||||
return value & (1ULL << index);
|
return value & (1ULL << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneralUtils::ReplaceInString(std::string& str, const std::string_view from, const std::string_view to) {
|
bool GeneralUtils::ReplaceInString(std::string& str, const std::string& from, const std::string& to) {
|
||||||
const size_t start_pos = str.find(from);
|
size_t start_pos = str.find(from);
|
||||||
if (start_pos == std::string::npos)
|
if (start_pos == std::string::npos)
|
||||||
return false;
|
return false;
|
||||||
str.replace(start_pos, from.length(), to);
|
str.replace(start_pos, from.length(), to);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str, const wchar_t delimiter) {
|
std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t delimiter) {
|
||||||
std::vector<std::wstring> vector = std::vector<std::wstring>();
|
std::vector<std::wstring> vector = std::vector<std::wstring>();
|
||||||
std::wstring current;
|
std::wstring current;
|
||||||
|
|
||||||
for (const wchar_t c : str) {
|
for (const auto& c : str) {
|
||||||
if (c == delimiter) {
|
if (c == delimiter) {
|
||||||
vector.push_back(current);
|
vector.push_back(current);
|
||||||
current = L"";
|
current = L"";
|
||||||
@@ -237,15 +237,15 @@ std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector.push_back(std::move(current));
|
vector.push_back(current);
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view str, const char16_t delimiter) {
|
std::vector<std::u16string> GeneralUtils::SplitString(std::u16string& str, char16_t delimiter) {
|
||||||
std::vector<std::u16string> vector = std::vector<std::u16string>();
|
std::vector<std::u16string> vector = std::vector<std::u16string>();
|
||||||
std::u16string current;
|
std::u16string current;
|
||||||
|
|
||||||
for (const char16_t c : str) {
|
for (const auto& c : str) {
|
||||||
if (c == delimiter) {
|
if (c == delimiter) {
|
||||||
vector.push_back(current);
|
vector.push_back(current);
|
||||||
current = u"";
|
current = u"";
|
||||||
@@ -254,15 +254,17 @@ std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector.push_back(std::move(current));
|
vector.push_back(current);
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, const char delimiter) {
|
std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char delimiter) {
|
||||||
std::vector<std::string> vector = std::vector<std::string>();
|
std::vector<std::string> vector = std::vector<std::string>();
|
||||||
std::string current = "";
|
std::string current = "";
|
||||||
|
|
||||||
for (const char c : str) {
|
for (size_t i = 0; i < str.length(); i++) {
|
||||||
|
char c = str[i];
|
||||||
|
|
||||||
if (c == delimiter) {
|
if (c == delimiter) {
|
||||||
vector.push_back(current);
|
vector.push_back(current);
|
||||||
current = "";
|
current = "";
|
||||||
@@ -271,70 +273,49 @@ std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector.push_back(std::move(current));
|
vector.push_back(current);
|
||||||
|
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
|
std::u16string GeneralUtils::ReadWString(RakNet::BitStream* inStream) {
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
inStream.Read<uint32_t>(length);
|
inStream->Read<uint32_t>(length);
|
||||||
|
|
||||||
std::u16string string;
|
std::u16string string;
|
||||||
for (uint32_t i = 0; i < length; ++i) {
|
for (auto i = 0; i < length; i++) {
|
||||||
uint16_t c;
|
uint16_t c;
|
||||||
inStream.Read(c);
|
inStream->Read(c);
|
||||||
string.push_back(c);
|
string.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string_view folder) {
|
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string& folder) {
|
||||||
// Because we dont know how large the initial number before the first _ is we need to make it a map like so.
|
// Because we dont know how large the initial number before the first _ is we need to make it a map like so.
|
||||||
std::map<uint32_t, std::string> filenames{};
|
std::map<uint32_t, std::string> filenames{};
|
||||||
for (const auto& t : std::filesystem::directory_iterator(folder)) {
|
for (auto& t : std::filesystem::directory_iterator(folder)) {
|
||||||
auto filename = t.path().filename().string();
|
auto filename = t.path().filename().string();
|
||||||
const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
|
auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
|
||||||
filenames.emplace(index, std::move(filename));
|
filenames.insert(std::make_pair(index, filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now sort the map by the oldest migration.
|
// Now sort the map by the oldest migration.
|
||||||
std::vector<std::string> sortedFiles{};
|
std::vector<std::string> sortedFiles{};
|
||||||
auto fileIterator = filenames.cbegin();
|
auto fileIterator = filenames.begin();
|
||||||
auto oldest = filenames.cbegin();
|
std::map<uint32_t, std::string>::iterator oldest = filenames.begin();
|
||||||
while (!filenames.empty()) {
|
while (!filenames.empty()) {
|
||||||
if (fileIterator == filenames.cend()) {
|
if (fileIterator == filenames.end()) {
|
||||||
sortedFiles.push_back(oldest->second);
|
sortedFiles.push_back(oldest->second);
|
||||||
filenames.erase(oldest);
|
filenames.erase(oldest);
|
||||||
fileIterator = filenames.cbegin();
|
fileIterator = filenames.begin();
|
||||||
oldest = filenames.cbegin();
|
oldest = filenames.begin();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (oldest->first > fileIterator->first) oldest = fileIterator;
|
if (oldest->first > fileIterator->first) oldest = fileIterator;
|
||||||
++fileIterator;
|
fileIterator++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sortedFiles;
|
return sortedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
|
||||||
|
|
||||||
// MacOS floating-point parse function specializations
|
|
||||||
namespace GeneralUtils::details {
|
|
||||||
template <>
|
|
||||||
[[nodiscard]] float _parse<float>(const std::string_view str, size_t& parseNum) {
|
|
||||||
return std::stof(std::string{ str }, &parseNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
[[nodiscard]] double _parse<double>(const std::string_view str, size_t& parseNum) {
|
|
||||||
return std::stod(std::string{ str }, &parseNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
[[nodiscard]] long double _parse<long double>(const std::string_view str, size_t& parseNum) {
|
|
||||||
return std::stold(std::string{ str }, &parseNum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -1,27 +1,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// C++
|
// C++
|
||||||
#include <charconv>
|
#include <stdint.h>
|
||||||
#include <cstdint>
|
|
||||||
#include <ctime>
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <span>
|
#include <time.h>
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <functional>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <BitStream.h>
|
||||||
|
|
||||||
#include "BitStream.h"
|
|
||||||
#include "NiPoint3.h"
|
|
||||||
#include "dPlatforms.h"
|
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
|
|
||||||
enum eInventoryType : uint32_t;
|
enum eInventoryType : uint32_t;
|
||||||
enum class eObjectBits : size_t;
|
|
||||||
enum class eReplicaComponentType : uint32_t;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\file GeneralUtils.hpp
|
\file GeneralUtils.hpp
|
||||||
@@ -33,31 +26,29 @@ namespace GeneralUtils {
|
|||||||
//! Converts a plain ASCII string to a UTF-16 string
|
//! Converts a plain ASCII string to a UTF-16 string
|
||||||
/*!
|
/*!
|
||||||
\param string The string to convert
|
\param string The string to convert
|
||||||
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
\param size A size to trim the string to. Default is -1 (No trimming)
|
||||||
\return An UTF-16 representation of the string
|
\return An UTF-16 representation of the string
|
||||||
*/
|
*/
|
||||||
std::u16string ASCIIToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
|
std::u16string ASCIIToUTF16(const std::string_view& string, size_t size = -1);
|
||||||
|
|
||||||
//! Converts a UTF-8 String to a UTF-16 string
|
//! Converts a UTF-8 String to a UTF-16 string
|
||||||
/*!
|
/*!
|
||||||
\param string The string to convert
|
\param string The string to convert
|
||||||
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
\param size A size to trim the string to. Default is -1 (No trimming)
|
||||||
\return An UTF-16 representation of the string
|
\return An UTF-16 representation of the string
|
||||||
*/
|
*/
|
||||||
std::u16string UTF8ToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
|
std::u16string UTF8ToUTF16(const std::string_view& string, size_t size = -1);
|
||||||
|
|
||||||
namespace details {
|
//! Internal, do not use
|
||||||
//! Internal, do not use
|
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
|
||||||
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Converts a UTF-16 string to a UTF-8 string
|
//! Converts a UTF-16 string to a UTF-8 string
|
||||||
/*!
|
/*!
|
||||||
\param string The string to convert
|
\param string The string to convert
|
||||||
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
\param size A size to trim the string to. Default is -1 (No trimming)
|
||||||
\return An UTF-8 representation of the string
|
\return An UTF-8 representation of the string
|
||||||
*/
|
*/
|
||||||
std::string UTF16ToWTF8(const std::u16string_view string, const size_t size = SIZE_MAX);
|
std::string UTF16ToWTF8(const std::u16string_view& string, size_t size = -1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two basic strings but does so ignoring case sensitivity
|
* Compares two basic strings but does so ignoring case sensitivity
|
||||||
@@ -65,7 +56,7 @@ namespace GeneralUtils {
|
|||||||
* \param b the second string to compare against the first string
|
* \param b the second string to compare against the first string
|
||||||
* @return if the two strings are equal
|
* @return if the two strings are equal
|
||||||
*/
|
*/
|
||||||
bool CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b);
|
bool CaseInsensitiveStringCompare(const std::string& a, const std::string& b);
|
||||||
|
|
||||||
// MARK: Bits
|
// MARK: Bits
|
||||||
|
|
||||||
@@ -73,9 +64,9 @@ namespace GeneralUtils {
|
|||||||
|
|
||||||
//! Sets a bit on a numerical value
|
//! Sets a bit on a numerical value
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void SetBit(T& value, const eObjectBits bits) {
|
void SetBit(T& value, size_t index) {
|
||||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||||
const auto index = static_cast<size_t>(bits);
|
|
||||||
if (index > (sizeof(T) * 8) - 1) {
|
if (index > (sizeof(T) * 8) - 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -85,9 +76,9 @@ namespace GeneralUtils {
|
|||||||
|
|
||||||
//! Clears a bit on a numerical value
|
//! Clears a bit on a numerical value
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void ClearBit(T& value, const eObjectBits bits) {
|
void ClearBit(T& value, size_t index) {
|
||||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||||
const auto index = static_cast<size_t>(bits);
|
|
||||||
if (index > (sizeof(T) * 8 - 1)) {
|
if (index > (sizeof(T) * 8 - 1)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -100,14 +91,14 @@ namespace GeneralUtils {
|
|||||||
\param value The value to set the bit for
|
\param value The value to set the bit for
|
||||||
\param index The index of the bit
|
\param index The index of the bit
|
||||||
*/
|
*/
|
||||||
int64_t SetBit(int64_t value, const uint32_t index);
|
int64_t SetBit(int64_t value, uint32_t index);
|
||||||
|
|
||||||
//! Clears a specific bit in a signed 64-bit integer
|
//! Clears a specific bit in a signed 64-bit integer
|
||||||
/*!
|
/*!
|
||||||
\param value The value to clear the bit from
|
\param value The value to clear the bit from
|
||||||
\param index The index of the bit
|
\param index The index of the bit
|
||||||
*/
|
*/
|
||||||
int64_t ClearBit(int64_t value, const uint32_t index);
|
int64_t ClearBit(int64_t value, uint32_t index);
|
||||||
|
|
||||||
//! Checks a specific bit in a signed 64-bit integer
|
//! Checks a specific bit in a signed 64-bit integer
|
||||||
/*!
|
/*!
|
||||||
@@ -115,154 +106,7 @@ namespace GeneralUtils {
|
|||||||
\param index The index of the bit
|
\param index The index of the bit
|
||||||
\return Whether or not the bit is set
|
\return Whether or not the bit is set
|
||||||
*/
|
*/
|
||||||
bool CheckBit(int64_t value, const uint32_t index);
|
bool CheckBit(int64_t value, uint32_t index);
|
||||||
|
|
||||||
bool ReplaceInString(std::string& str, const std::string_view from, const std::string_view to);
|
|
||||||
|
|
||||||
std::u16string ReadWString(RakNet::BitStream& inStream);
|
|
||||||
|
|
||||||
std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
|
|
||||||
|
|
||||||
std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
|
|
||||||
|
|
||||||
std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
|
|
||||||
|
|
||||||
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transparent string hasher - used to allow string_view key lookups for maps storing std::string keys
|
|
||||||
* https://www.reddit.com/r/cpp_questions/comments/12xw3sn/find_stdstring_view_in_unordered_map_with/jhki225/
|
|
||||||
* https://godbolt.org/z/789xv8Eeq
|
|
||||||
*/
|
|
||||||
template <typename... Bases>
|
|
||||||
struct overload : Bases... {
|
|
||||||
using is_transparent = void;
|
|
||||||
using Bases::operator() ... ;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct char_pointer_hash {
|
|
||||||
auto operator()(const char* const ptr) const noexcept {
|
|
||||||
return std::hash<std::string_view>{}(ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using transparent_string_hash = overload<
|
|
||||||
std::hash<std::string>,
|
|
||||||
std::hash<std::string_view>,
|
|
||||||
char_pointer_hash
|
|
||||||
>;
|
|
||||||
|
|
||||||
// Concept constraining to enum types
|
|
||||||
template <typename T>
|
|
||||||
concept Enum = std::is_enum_v<T>;
|
|
||||||
|
|
||||||
// Concept constraining to numeric types
|
|
||||||
template <typename T>
|
|
||||||
concept Numeric = std::integral<T> || Enum<T> || std::floating_point<T>;
|
|
||||||
|
|
||||||
// Concept trickery to enable parsing underlying numeric types
|
|
||||||
template <Numeric T>
|
|
||||||
struct numeric_parse { using type = T; };
|
|
||||||
|
|
||||||
// If an enum, present an alias to its underlying type for parsing
|
|
||||||
template <Numeric T> requires Enum<T>
|
|
||||||
struct numeric_parse<T> { using type = std::underlying_type_t<T>; };
|
|
||||||
|
|
||||||
// If a boolean, present an alias to an intermediate integral type for parsing
|
|
||||||
template <Numeric T> requires std::same_as<T, bool>
|
|
||||||
struct numeric_parse<T> { using type = uint8_t; };
|
|
||||||
|
|
||||||
// Shorthand type alias
|
|
||||||
template <Numeric T>
|
|
||||||
using numeric_parse_t = numeric_parse<T>::type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For numeric values: Parses a string_view and returns an optional variable depending on the result.
|
|
||||||
* @param str The string_view to be evaluated
|
|
||||||
* @returns An std::optional containing the desired value if it is equivalent to the string
|
|
||||||
*/
|
|
||||||
template <Numeric T>
|
|
||||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str) {
|
|
||||||
numeric_parse_t<T> result;
|
|
||||||
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
|
||||||
|
|
||||||
const char* const strEnd = str.data() + str.size();
|
|
||||||
const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result);
|
|
||||||
const bool isParsed = parseEnd == strEnd && ec == std::errc{};
|
|
||||||
|
|
||||||
return isParsed ? static_cast<T>(result) : std::optional<T>{};
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
|
||||||
|
|
||||||
// MacOS floating-point parse helper function specializations
|
|
||||||
namespace details {
|
|
||||||
template <std::floating_point T>
|
|
||||||
[[nodiscard]] T _parse(const std::string_view str, size_t& parseNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For floating-point values: Parses a string_view and returns an optional variable depending on the result.
|
|
||||||
* Note that this function overload is only included for MacOS, as from_chars will fulfill its purpose otherwise.
|
|
||||||
* @param str The string_view to be evaluated
|
|
||||||
* @returns An std::optional containing the desired value if it is equivalent to the string
|
|
||||||
*/
|
|
||||||
template <std::floating_point T>
|
|
||||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
|
|
||||||
try {
|
|
||||||
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
|
||||||
|
|
||||||
size_t parseNum;
|
|
||||||
const T result = details::_parse<T>(str, parseNum);
|
|
||||||
const bool isParsed = str.length() == parseNum;
|
|
||||||
|
|
||||||
return isParsed ? result : std::optional<T>{};
|
|
||||||
} catch (...) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The TryParse overload for handling NiPoint3 by passing 3 seperate string references
|
|
||||||
* @param strX The string representing the X coordinate
|
|
||||||
* @param strY The string representing the Y coordinate
|
|
||||||
* @param strZ The string representing the Z coordinate
|
|
||||||
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
|
|
||||||
const auto x = TryParse<float>(strX);
|
|
||||||
if (!x) return std::nullopt;
|
|
||||||
|
|
||||||
const auto y = TryParse<float>(strY);
|
|
||||||
if (!y) return std::nullopt;
|
|
||||||
|
|
||||||
const auto z = TryParse<float>(strZ);
|
|
||||||
return z ? std::make_optional<NiPoint3>(x.value(), y.value(), z.value()) : std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The TryParse overload for handling NiPoint3 by passing a span of three strings
|
|
||||||
* @param str The string vector representing the X, Y, and Z coordinates
|
|
||||||
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::span<const std::string> str) {
|
|
||||||
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::u16string to_u16string(const T value) {
|
|
||||||
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// From boost::hash_combine
|
|
||||||
template <class T>
|
|
||||||
constexpr void hash_combine(std::size_t& s, const T& v) {
|
|
||||||
std::hash<T> h;
|
|
||||||
s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Random Number Generation
|
// MARK: Random Number Generation
|
||||||
|
|
||||||
@@ -272,7 +116,7 @@ namespace GeneralUtils {
|
|||||||
\param max The maximum to generate to
|
\param max The maximum to generate to
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T GenerateRandomNumber(const std::size_t min, const std::size_t max) {
|
inline T GenerateRandomNumber(std::size_t min, std::size_t max) {
|
||||||
// Make sure it is a numeric type
|
// Make sure it is a numeric type
|
||||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||||
|
|
||||||
@@ -287,28 +131,86 @@ namespace GeneralUtils {
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to);
|
||||||
* Casts the value of an enum entry to its underlying type
|
|
||||||
* @param entry Enum entry to cast
|
|
||||||
* @returns The enum entry's value in its underlying type
|
|
||||||
*/
|
|
||||||
template <Enum eType>
|
|
||||||
constexpr std::underlying_type_t<eType> ToUnderlying(const eType entry) noexcept {
|
|
||||||
return static_cast<std::underlying_type_t<eType>>(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// on Windows we need to undef these or else they conflict with our numeric limits calls
|
std::u16string ReadWString(RakNet::BitStream* inStream);
|
||||||
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
|
|
||||||
#ifdef _WIN32
|
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter);
|
||||||
#undef min
|
|
||||||
#undef max
|
std::vector<std::u16string> SplitString(std::u16string& str, char16_t delimiter);
|
||||||
#endif
|
|
||||||
|
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
||||||
|
|
||||||
|
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T GenerateRandomNumber() {
|
T Parse(const char* value);
|
||||||
// Make sure it is a numeric type
|
|
||||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
|
||||||
|
|
||||||
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
template <>
|
||||||
|
inline int32_t Parse(const char* value) {
|
||||||
|
return std::stoi(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int64_t Parse(const char* value) {
|
||||||
|
return std::stoll(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline float Parse(const char* value) {
|
||||||
|
return std::stof(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline double Parse(const char* value) {
|
||||||
|
return std::stod(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline uint32_t Parse(const char* value) {
|
||||||
|
return std::stoul(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline uint64_t Parse(const char* value) {
|
||||||
|
return std::stoull(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline eInventoryType Parse(const char* value) {
|
||||||
|
return static_cast<eInventoryType>(std::stoul(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool TryParse(const char* value, T& dst) {
|
||||||
|
try {
|
||||||
|
dst = Parse<T>(value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T Parse(const std::string& value) {
|
||||||
|
return Parse<T>(value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool TryParse(const std::string& value, T& dst) {
|
||||||
|
return TryParse<T>(value.c_str(), dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::u16string to_u16string(T value) {
|
||||||
|
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// From boost::hash_combine
|
||||||
|
template <class T>
|
||||||
|
void hash_combine(std::size_t& s, const T& v) {
|
||||||
|
std::hash<T> h;
|
||||||
|
s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
#ifndef __IMPLEMENTATION_H__
|
|
||||||
#define __IMPLEMENTATION_H__
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A way to defer the implementation of an action.
|
|
||||||
*
|
|
||||||
* @tparam R The result of the action.
|
|
||||||
* @tparam T The types of the arguments that the implementation requires.
|
|
||||||
*/
|
|
||||||
template <typename R, typename... T>
|
|
||||||
class Implementation {
|
|
||||||
public:
|
|
||||||
typedef std::function<std::optional<R>(T...)> ImplementationFunction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sets the implementation of the action.
|
|
||||||
*
|
|
||||||
* @param implementation The implementation of the action.
|
|
||||||
*/
|
|
||||||
void SetImplementation(const ImplementationFunction& implementation) {
|
|
||||||
this->implementation = implementation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clears the implementation of the action.
|
|
||||||
*/
|
|
||||||
void ClearImplementation() {
|
|
||||||
implementation.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if the implementation is set.
|
|
||||||
*
|
|
||||||
* @return true If the implementation is set.
|
|
||||||
* @return false If the implementation is not set.
|
|
||||||
*/
|
|
||||||
bool IsSet() const {
|
|
||||||
return implementation.has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Executes the implementation if it is set.
|
|
||||||
*
|
|
||||||
* @param args The arguments to pass to the implementation.
|
|
||||||
* @return std::optional<R> The optional result of the implementation. If the result is not set, it indicates that the default action should be taken.
|
|
||||||
*/
|
|
||||||
std::optional<R> Execute(T... args) const {
|
|
||||||
return IsSet() ? implementation.value()(args...) : std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Exectues the implementation if it is set, otherwise returns a default value.
|
|
||||||
*
|
|
||||||
* @param args The arguments to pass to the implementation.
|
|
||||||
* @param defaultValue The default value to return if the implementation is not set or should not be deferred.
|
|
||||||
*/
|
|
||||||
R ExecuteWithDefault(T... args, const R& defaultValue) const {
|
|
||||||
return Execute(args...).value_or(defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* = operator overload.
|
|
||||||
*/
|
|
||||||
Implementation& operator=(const Implementation& other) {
|
|
||||||
implementation = other.implementation;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* = operator overload.
|
|
||||||
*/
|
|
||||||
Implementation& operator=(const ImplementationFunction& implementation) {
|
|
||||||
this->implementation = implementation;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* () operator overload.
|
|
||||||
*/
|
|
||||||
std::optional<R> operator()(T... args) {
|
|
||||||
return !IsSet() ? std::nullopt : implementation(args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<ImplementationFunction> implementation;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //!__IMPLEMENTATION_H__
|
|
||||||
@@ -3,170 +3,122 @@
|
|||||||
// Custom Classes
|
// Custom Classes
|
||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
|
|
||||||
#include "Game.h"
|
|
||||||
#include "Logger.h"
|
|
||||||
|
|
||||||
// C++
|
// C++
|
||||||
#include <string_view>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using LDFKey = std::string_view;
|
|
||||||
using LDFTypeAndValue = std::string_view;
|
|
||||||
|
|
||||||
using LDFType = std::string_view;
|
|
||||||
using LDFValue = std::string_view;
|
|
||||||
|
|
||||||
//! Returns a pointer to a LDFData value based on string format
|
//! Returns a pointer to a LDFData value based on string format
|
||||||
LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
|
LDFBaseData* LDFBaseData::DataFromString(const std::string& format) {
|
||||||
// A valid LDF must be at least 3 characters long (=0:) is the shortest valid LDF (empty UTF-16 key with no initial value)
|
|
||||||
if (format.empty() || format.length() <= 2) return nullptr;
|
|
||||||
auto equalsPosition = format.find('=');
|
|
||||||
// You can have an empty key, just make sure the type and value might exist
|
|
||||||
if (equalsPosition == std::string::npos || equalsPosition == (format.size() - 1)) return nullptr;
|
|
||||||
|
|
||||||
std::pair<LDFKey, LDFTypeAndValue> keyValue;
|
// First, check the format
|
||||||
keyValue.first = format.substr(0, equalsPosition);
|
std::istringstream ssFormat(format);
|
||||||
keyValue.second = format.substr(equalsPosition + 1, format.size());
|
std::string token;
|
||||||
|
|
||||||
std::u16string key = GeneralUtils::ASCIIToUTF16(keyValue.first);
|
std::vector<std::string> keyValueArray;
|
||||||
|
while (std::getline(ssFormat, token, '=')) {
|
||||||
auto colonPosition = keyValue.second.find(':');
|
keyValueArray.push_back(token);
|
||||||
|
|
||||||
// If : is the first thing after an =, then this is an invalid LDF since
|
|
||||||
// we dont have a type to use.
|
|
||||||
if (colonPosition == std::string::npos || colonPosition == 0) return nullptr;
|
|
||||||
|
|
||||||
std::pair<LDFType, LDFValue> ldfTypeAndValue;
|
|
||||||
ldfTypeAndValue.first = keyValue.second.substr(0, colonPosition);
|
|
||||||
ldfTypeAndValue.second = keyValue.second.substr(colonPosition + 1, keyValue.second.size());
|
|
||||||
|
|
||||||
// Only allow empty values for string values.
|
|
||||||
if (ldfTypeAndValue.second.size() == 0 && !(ldfTypeAndValue.first == "0" || ldfTypeAndValue.first == "13")) return nullptr;
|
|
||||||
|
|
||||||
eLDFType type;
|
|
||||||
char* storage;
|
|
||||||
try {
|
|
||||||
type = static_cast<eLDFType>(strtol(ldfTypeAndValue.first.data(), &storage, 10));
|
|
||||||
} catch (std::exception) {
|
|
||||||
LOG("Attempted to process invalid ldf type (%s) from string (%s)", ldfTypeAndValue.first.data(), format.data());
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LDFBaseData* returnValue = nullptr;
|
if (keyValueArray.size() == 2) {
|
||||||
switch (type) {
|
std::u16string key = GeneralUtils::ASCIIToUTF16(keyValueArray[0]);
|
||||||
case LDF_TYPE_UTF_16: {
|
|
||||||
std::u16string data = GeneralUtils::UTF8ToUTF16(ldfTypeAndValue.second);
|
|
||||||
returnValue = new LDFData<std::u16string>(key, data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LDF_TYPE_S32: {
|
std::vector<std::string> dataArray;
|
||||||
const auto data = GeneralUtils::TryParse<int32_t>(ldfTypeAndValue.second);
|
std::istringstream ssData(keyValueArray[1]);
|
||||||
if (!data) {
|
while (std::getline(ssData, token, ':')) {
|
||||||
LOG("Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
dataArray.push_back(token);
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
returnValue = new LDFData<int32_t>(key, data.value());
|
|
||||||
|
|
||||||
break;
|
if (dataArray.size() > 2) { // hacky fix for strings with colons in them
|
||||||
}
|
std::vector<std::string> newDataArray;
|
||||||
|
newDataArray.push_back(dataArray[0]);
|
||||||
case LDF_TYPE_FLOAT: {
|
std::string value = "";
|
||||||
const auto data = GeneralUtils::TryParse<float>(ldfTypeAndValue.second);
|
for (size_t i = 1; i < dataArray.size(); ++i) {
|
||||||
if (!data) {
|
value += dataArray[i] + ':';
|
||||||
LOG("Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
}
|
||||||
return nullptr;
|
value.pop_back(); // remove last colon
|
||||||
|
newDataArray.push_back(value);
|
||||||
|
dataArray = newDataArray;
|
||||||
}
|
}
|
||||||
returnValue = new LDFData<float>(key, data.value());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LDF_TYPE_DOUBLE: {
|
if ((dataArray[0] == "0" || dataArray[0] == "13") && dataArray.size() == 1) {
|
||||||
const auto data = GeneralUtils::TryParse<double>(ldfTypeAndValue.second);
|
dataArray.push_back("");
|
||||||
if (!data) {
|
|
||||||
LOG("Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
returnValue = new LDFData<double>(key, data.value());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LDF_TYPE_U32:
|
if (dataArray.size() == 2) {
|
||||||
{
|
eLDFType type = static_cast<eLDFType>(stoi(dataArray[0]));
|
||||||
uint32_t data;
|
|
||||||
|
|
||||||
if (ldfTypeAndValue.second == "true") {
|
switch (type) {
|
||||||
data = 1;
|
case LDF_TYPE_UTF_16: {
|
||||||
} else if (ldfTypeAndValue.second == "false") {
|
std::u16string data = GeneralUtils::UTF8ToUTF16(dataArray[1]);
|
||||||
data = 0;
|
return new LDFData<std::u16string>(key, data);
|
||||||
} else {
|
}
|
||||||
const auto dataOptional = GeneralUtils::TryParse<uint32_t>(ldfTypeAndValue.second);
|
|
||||||
if (!dataOptional) {
|
case LDF_TYPE_S32: {
|
||||||
LOG("Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
int32_t data = static_cast<int32_t>(stoull(dataArray[1]));
|
||||||
|
return new LDFData<int32_t>(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LDF_TYPE_FLOAT: {
|
||||||
|
float data = static_cast<float>(stof(dataArray[1]));
|
||||||
|
return new LDFData<float>(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LDF_TYPE_DOUBLE: {
|
||||||
|
double data = static_cast<float>(stod(dataArray[1]));
|
||||||
|
return new LDFData<double>(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LDF_TYPE_U32:
|
||||||
|
{
|
||||||
|
uint32_t data;
|
||||||
|
|
||||||
|
if (dataArray[1] == "true") {
|
||||||
|
data = 1;
|
||||||
|
} else if (dataArray[1] == "false") {
|
||||||
|
data = 0;
|
||||||
|
} else {
|
||||||
|
data = static_cast<uint32_t>(stoul(dataArray[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LDFData<uint32_t>(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LDF_TYPE_BOOLEAN: {
|
||||||
|
bool data;
|
||||||
|
|
||||||
|
if (dataArray[1] == "true") {
|
||||||
|
data = true;
|
||||||
|
} else if (dataArray[1] == "false") {
|
||||||
|
data = false;
|
||||||
|
} else {
|
||||||
|
data = static_cast<bool>(stoi(dataArray[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LDFData<bool>(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LDF_TYPE_U64: {
|
||||||
|
uint64_t data = static_cast<uint64_t>(stoull(dataArray[1]));
|
||||||
|
return new LDFData<uint64_t>(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LDF_TYPE_OBJID: {
|
||||||
|
LWOOBJID data = static_cast<LWOOBJID>(stoll(dataArray[1]));
|
||||||
|
return new LDFData<LWOOBJID>(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LDF_TYPE_UTF_8: {
|
||||||
|
std::string data = dataArray[1];
|
||||||
|
return new LDFData<std::string>(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LDF_TYPE_UNKNOWN: {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
data = dataOptional.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
returnValue = new LDFData<uint32_t>(key, data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LDF_TYPE_BOOLEAN: {
|
|
||||||
bool data;
|
|
||||||
|
|
||||||
if (ldfTypeAndValue.second == "true") {
|
|
||||||
data = true;
|
|
||||||
} else if (ldfTypeAndValue.second == "false") {
|
|
||||||
data = false;
|
|
||||||
} else {
|
|
||||||
const auto dataOptional = GeneralUtils::TryParse<bool>(ldfTypeAndValue.second);
|
|
||||||
if (!dataOptional) {
|
|
||||||
LOG("Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
data = dataOptional.value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
returnValue = new LDFData<bool>(key, data);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case LDF_TYPE_U64: {
|
return nullptr;
|
||||||
const auto data = GeneralUtils::TryParse<uint64_t>(ldfTypeAndValue.second);
|
|
||||||
if (!data) {
|
|
||||||
LOG("Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
returnValue = new LDFData<uint64_t>(key, data.value());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LDF_TYPE_OBJID: {
|
|
||||||
const auto data = GeneralUtils::TryParse<LWOOBJID>(ldfTypeAndValue.second);
|
|
||||||
if (!data) {
|
|
||||||
LOG("Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
returnValue = new LDFData<LWOOBJID>(key, data.value());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LDF_TYPE_UTF_8: {
|
|
||||||
std::string data = ldfTypeAndValue.second.data();
|
|
||||||
returnValue = new LDFData<std::string>(key, data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LDF_TYPE_UNKNOWN: {
|
|
||||||
LOG("Warning: Attempted to process invalid unknown value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
LOG("Warning: Attempted to process invalid LDF type (%d) from string (%s)", type, format.data());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#ifndef __LDFFORMAT__H__
|
#pragma once
|
||||||
#define __LDFFORMAT__H__
|
|
||||||
|
|
||||||
// Custom Classes
|
// Custom Classes
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
@@ -7,12 +6,18 @@
|
|||||||
|
|
||||||
// C++
|
// C++
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
// RakNet
|
// RakNet
|
||||||
#include "BitStream.h"
|
|
||||||
|
|
||||||
|
#include "../thirdparty/raknet/Source/BitStream.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\file LDFFormat.hpp
|
||||||
|
\brief A collection of LDF format classes
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! An enum for LDF Data Types
|
||||||
enum eLDFType {
|
enum eLDFType {
|
||||||
LDF_TYPE_UNKNOWN = -1, //!< Unknown data type
|
LDF_TYPE_UNKNOWN = -1, //!< Unknown data type
|
||||||
LDF_TYPE_UTF_16 = 0, //!< UTF-16 wstring data type
|
LDF_TYPE_UTF_16 = 0, //!< UTF-16 wstring data type
|
||||||
@@ -26,53 +31,72 @@ enum eLDFType {
|
|||||||
LDF_TYPE_UTF_8 = 13, //!< UTF-8 string data type
|
LDF_TYPE_UTF_8 = 13, //!< UTF-8 string data type
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! A base class for the LDF data
|
||||||
class LDFBaseData {
|
class LDFBaseData {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual ~LDFBaseData() {}
|
//! Destructor
|
||||||
|
virtual ~LDFBaseData(void) {}
|
||||||
|
|
||||||
virtual void WriteToPacket(RakNet::BitStream& packet) const = 0;
|
//! Writes the data to a packet
|
||||||
|
/*!
|
||||||
virtual const std::u16string& GetKey() const = 0;
|
\param packet The packet
|
||||||
|
|
||||||
virtual eLDFType GetValueType() const = 0;
|
|
||||||
|
|
||||||
/** Gets a string from the key/value pair
|
|
||||||
* @param includeKey Whether or not to include the key in the data
|
|
||||||
* @param includeTypeId Whether or not to include the type id in the data
|
|
||||||
* @return The string representation of the data
|
|
||||||
*/
|
*/
|
||||||
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) const = 0;
|
virtual void WriteToPacket(RakNet::BitStream* packet) = 0;
|
||||||
|
|
||||||
virtual std::string GetValueAsString() const = 0;
|
//! Gets the key
|
||||||
|
/*!
|
||||||
virtual LDFBaseData* Copy() const = 0;
|
\return The key
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an input string, return the data as a LDF key.
|
|
||||||
*/
|
*/
|
||||||
static LDFBaseData* DataFromString(const std::string_view& format);
|
virtual const std::u16string& GetKey(void) = 0;
|
||||||
|
|
||||||
|
//! Gets the value type
|
||||||
|
/*!
|
||||||
|
\return The value type
|
||||||
|
*/
|
||||||
|
virtual eLDFType GetValueType(void) = 0;
|
||||||
|
|
||||||
|
//! Gets a string from the key/value pair
|
||||||
|
/*!
|
||||||
|
\param includeKey Whether or not to include the key in the data
|
||||||
|
\param includeTypeId Whether or not to include the type id in the data
|
||||||
|
\return The string representation of the data
|
||||||
|
*/
|
||||||
|
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) = 0;
|
||||||
|
|
||||||
|
virtual std::string GetValueAsString() = 0;
|
||||||
|
|
||||||
|
virtual LDFBaseData* Copy() = 0;
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
//! Returns a pointer to a LDFData value based on string format
|
||||||
|
/*!
|
||||||
|
\param format The format
|
||||||
|
*/
|
||||||
|
static LDFBaseData* DataFromString(const std::string& format);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! A structure for an LDF key-value pair
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class LDFData: public LDFBaseData {
|
class LDFData : public LDFBaseData {
|
||||||
private:
|
private:
|
||||||
std::u16string key;
|
std::u16string key;
|
||||||
T value;
|
T value;
|
||||||
|
|
||||||
//! Writes the key to the packet
|
//! Writes the key to the packet
|
||||||
void WriteKey(RakNet::BitStream& packet) const {
|
void WriteKey(RakNet::BitStream* packet) {
|
||||||
packet.Write<uint8_t>(this->key.length() * sizeof(uint16_t));
|
packet->Write(static_cast<uint8_t>(this->key.length() * sizeof(uint16_t)));
|
||||||
for (uint32_t i = 0; i < this->key.length(); ++i) {
|
for (uint32_t i = 0; i < this->key.length(); ++i) {
|
||||||
packet.Write<uint16_t>(this->key[i]);
|
packet->Write(static_cast<uint16_t>(this->key[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Writes the value to the packet
|
//! Writes the value to the packet
|
||||||
void WriteValue(RakNet::BitStream& packet) const {
|
void WriteValue(RakNet::BitStream* packet) {
|
||||||
packet.Write<uint8_t>(this->GetValueType());
|
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||||
packet.Write(this->value);
|
packet->Write(this->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -90,7 +114,7 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The value
|
\return The value
|
||||||
*/
|
*/
|
||||||
const T& GetValue(void) const { return this->value; }
|
const T& GetValue(void) { return this->value; }
|
||||||
|
|
||||||
//! Sets the value
|
//! Sets the value
|
||||||
/*!
|
/*!
|
||||||
@@ -102,13 +126,13 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The value string
|
\return The value string
|
||||||
*/
|
*/
|
||||||
std::string GetValueString(void) const { return ""; }
|
std::string GetValueString(void) { return ""; }
|
||||||
|
|
||||||
//! Writes the data to a packet
|
//! Writes the data to a packet
|
||||||
/*!
|
/*!
|
||||||
\param packet The packet
|
\param packet The packet
|
||||||
*/
|
*/
|
||||||
void WriteToPacket(RakNet::BitStream& packet) const override {
|
void WriteToPacket(RakNet::BitStream* packet) override {
|
||||||
this->WriteKey(packet);
|
this->WriteKey(packet);
|
||||||
this->WriteValue(packet);
|
this->WriteValue(packet);
|
||||||
}
|
}
|
||||||
@@ -117,13 +141,13 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The key
|
\return The key
|
||||||
*/
|
*/
|
||||||
const std::u16string& GetKey(void) const override { return this->key; }
|
const std::u16string& GetKey(void) override { return this->key; }
|
||||||
|
|
||||||
//! Gets the LDF Type
|
//! Gets the LDF Type
|
||||||
/*!
|
/*!
|
||||||
\return The LDF value type
|
\return The LDF value type
|
||||||
*/
|
*/
|
||||||
eLDFType GetValueType(void) const override { return LDF_TYPE_UNKNOWN; }
|
eLDFType GetValueType(void) override { return LDF_TYPE_UNKNOWN; }
|
||||||
|
|
||||||
//! Gets the string data
|
//! Gets the string data
|
||||||
/*!
|
/*!
|
||||||
@@ -131,7 +155,7 @@ public:
|
|||||||
\param includeTypeId Whether or not to include the type id in the data
|
\param includeTypeId Whether or not to include the type id in the data
|
||||||
\return The string representation of the data
|
\return The string representation of the data
|
||||||
*/
|
*/
|
||||||
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) const override {
|
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) override {
|
||||||
if (GetValueType() == -1) {
|
if (GetValueType() == -1) {
|
||||||
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
|
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
|
||||||
}
|
}
|
||||||
@@ -140,11 +164,15 @@ public:
|
|||||||
|
|
||||||
if (includeKey) {
|
if (includeKey) {
|
||||||
const std::string& sKey = GeneralUtils::UTF16ToWTF8(this->key, this->key.size());
|
const std::string& sKey = GeneralUtils::UTF16ToWTF8(this->key, this->key.size());
|
||||||
stream << sKey << '=';
|
|
||||||
|
stream << sKey << "=";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeTypeId) {
|
if (includeTypeId) {
|
||||||
stream << this->GetValueType() << ':';
|
const std::string& sType = std::to_string(this->GetValueType());
|
||||||
|
|
||||||
|
|
||||||
|
stream << sType << ":";
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& sData = this->GetValueString();
|
const std::string& sData = this->GetValueString();
|
||||||
@@ -154,70 +182,72 @@ public:
|
|||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetValueAsString() const override {
|
std::string GetValueAsString() override {
|
||||||
return this->GetValueString();
|
return this->GetValueString();
|
||||||
}
|
}
|
||||||
|
|
||||||
LDFBaseData* Copy() const override {
|
LDFBaseData* Copy() override {
|
||||||
return new LDFData<T>(key, value);
|
return new LDFData<T>(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static const T Default = {};
|
inline static T Default = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
// LDF Types
|
// LDF Types
|
||||||
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) const { return LDF_TYPE_UTF_16; };
|
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) { return LDF_TYPE_UTF_16; };
|
||||||
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) const { return LDF_TYPE_S32; };
|
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) { return LDF_TYPE_S32; };
|
||||||
template<> inline eLDFType LDFData<float>::GetValueType(void) const { return LDF_TYPE_FLOAT; };
|
template<> inline eLDFType LDFData<float>::GetValueType(void) { return LDF_TYPE_FLOAT; };
|
||||||
template<> inline eLDFType LDFData<double>::GetValueType(void) const { return LDF_TYPE_DOUBLE; };
|
template<> inline eLDFType LDFData<double>::GetValueType(void) { return LDF_TYPE_DOUBLE; };
|
||||||
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) const { return LDF_TYPE_U32; };
|
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) { return LDF_TYPE_U32; };
|
||||||
template<> inline eLDFType LDFData<bool>::GetValueType(void) const { return LDF_TYPE_BOOLEAN; };
|
template<> inline eLDFType LDFData<bool>::GetValueType(void) { return LDF_TYPE_BOOLEAN; };
|
||||||
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) const { return LDF_TYPE_U64; };
|
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) { return LDF_TYPE_U64; };
|
||||||
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) const { return LDF_TYPE_OBJID; };
|
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) { return LDF_TYPE_OBJID; };
|
||||||
template<> inline eLDFType LDFData<std::string>::GetValueType(void) const { return LDF_TYPE_UTF_8; };
|
template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF_TYPE_UTF_8; };
|
||||||
|
|
||||||
// The specialized version for std::u16string (UTF-16)
|
// The specialized version for std::u16string (UTF-16)
|
||||||
template<>
|
template<>
|
||||||
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) const {
|
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream* packet) {
|
||||||
packet.Write<uint8_t>(this->GetValueType());
|
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||||
|
|
||||||
packet.Write<uint32_t>(this->value.length());
|
packet->Write(static_cast<uint32_t>(this->value.length()));
|
||||||
for (uint32_t i = 0; i < this->value.length(); ++i) {
|
for (uint32_t i = 0; i < this->value.length(); ++i) {
|
||||||
packet.Write<uint16_t>(this->value[i]);
|
packet->Write(static_cast<uint16_t>(this->value[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The specialized version for bool
|
// The specialized version for bool
|
||||||
template<>
|
template<>
|
||||||
inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) const {
|
inline void LDFData<bool>::WriteValue(RakNet::BitStream* packet) {
|
||||||
packet.Write<uint8_t>(this->GetValueType());
|
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||||
|
|
||||||
packet.Write<uint8_t>(this->value);
|
packet->Write(static_cast<uint8_t>(this->value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The specialized version for std::string (UTF-8)
|
// The specialized version for std::string (UTF-8)
|
||||||
template<>
|
template<>
|
||||||
inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) const {
|
inline void LDFData<std::string>::WriteValue(RakNet::BitStream* packet) {
|
||||||
packet.Write<uint8_t>(this->GetValueType());
|
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||||
|
|
||||||
packet.Write<uint32_t>(this->value.length());
|
packet->Write(static_cast<uint32_t>(this->value.length()));
|
||||||
for (uint32_t i = 0; i < this->value.length(); ++i) {
|
for (uint32_t i = 0; i < this->value.length(); ++i) {
|
||||||
packet.Write<uint8_t>(this->value[i]);
|
packet->Write(static_cast<uint8_t>(this->value[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline std::string LDFData<std::u16string>::GetValueString() const {
|
// MARK: String Data
|
||||||
|
template<> inline std::string LDFData<std::u16string>::GetValueString(void) {
|
||||||
|
//std::string toReturn(this->value.begin(), this->value.end());
|
||||||
|
//return toReturn;
|
||||||
|
|
||||||
return GeneralUtils::UTF16ToWTF8(this->value, this->value.size());
|
return GeneralUtils::UTF16ToWTF8(this->value, this->value.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline std::string LDFData<int32_t>::GetValueString() const { return std::to_string(this->value); }
|
template<> inline std::string LDFData<int32_t>::GetValueString(void) { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<float>::GetValueString() const { return std::to_string(this->value); }
|
template<> inline std::string LDFData<float>::GetValueString(void) { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<double>::GetValueString() const { return std::to_string(this->value); }
|
template<> inline std::string LDFData<double>::GetValueString(void) { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<uint32_t>::GetValueString() const { return std::to_string(this->value); }
|
template<> inline std::string LDFData<uint32_t>::GetValueString(void) { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<bool>::GetValueString() const { return std::to_string(this->value); }
|
template<> inline std::string LDFData<bool>::GetValueString(void) { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<uint64_t>::GetValueString() const { return std::to_string(this->value); }
|
template<> inline std::string LDFData<uint64_t>::GetValueString(void) { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<LWOOBJID>::GetValueString() const { return std::to_string(this->value); }
|
template<> inline std::string LDFData<LWOOBJID>::GetValueString(void) { return std::to_string(this->value); }
|
||||||
|
|
||||||
template<> inline std::string LDFData<std::string>::GetValueString() const { return this->value; }
|
template<> inline std::string LDFData<std::string>::GetValueString(void) { return this->value; }
|
||||||
|
|
||||||
#endif //!__LDFFORMAT__H__
|
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
#include "Logger.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <ctime>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
Writer::~Writer() {
|
|
||||||
// Flush before we close
|
|
||||||
Flush();
|
|
||||||
// Dont try to close stdcout...
|
|
||||||
if (!m_Outfile || m_IsConsoleWriter) return;
|
|
||||||
|
|
||||||
fclose(m_Outfile);
|
|
||||||
m_Outfile = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Writer::Log(const char* time, const char* message) {
|
|
||||||
if (!m_Outfile || !m_Enabled) return;
|
|
||||||
|
|
||||||
fputs(time, m_Outfile);
|
|
||||||
fputs(message, m_Outfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Writer::Flush() {
|
|
||||||
if (!m_Outfile) return;
|
|
||||||
fflush(m_Outfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileWriter::FileWriter(const char* outpath) {
|
|
||||||
m_Outfile = fopen(outpath, "wt");
|
|
||||||
if (!m_Outfile) printf("Couldn't open %s for writing!\n", outpath);
|
|
||||||
m_Outpath = outpath;
|
|
||||||
m_IsConsoleWriter = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleWriter::ConsoleWriter(bool enabled) {
|
|
||||||
m_Enabled = enabled;
|
|
||||||
m_Outfile = stdout;
|
|
||||||
m_IsConsoleWriter = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::Logger(const std::string& outpath, bool logToConsole, bool logDebugStatements) {
|
|
||||||
m_logDebugStatements = logDebugStatements;
|
|
||||||
std::filesystem::path outpathPath(outpath);
|
|
||||||
if (!std::filesystem::exists(outpathPath.parent_path())) std::filesystem::create_directories(outpathPath.parent_path());
|
|
||||||
m_Writers.push_back(std::make_unique<FileWriter>(outpath));
|
|
||||||
m_Writers.push_back(std::make_unique<ConsoleWriter>(logToConsole));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::vLog(const char* format, va_list args) {
|
|
||||||
time_t t = time(NULL);
|
|
||||||
struct tm* time = localtime(&t);
|
|
||||||
char timeStr[70];
|
|
||||||
strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time);
|
|
||||||
char message[2048];
|
|
||||||
vsnprintf(message, 2048, format, args);
|
|
||||||
for (const auto& writer : m_Writers) {
|
|
||||||
writer->Log(timeStr, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::Log(const char* className, const char* format, ...) {
|
|
||||||
va_list args;
|
|
||||||
std::string log = std::string(className) + "] " + std::string(format) + "\n";
|
|
||||||
va_start(args, format);
|
|
||||||
vLog(log.c_str(), args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::LogDebug(const char* className, const char* format, ...) {
|
|
||||||
if (!m_logDebugStatements) return;
|
|
||||||
va_list args;
|
|
||||||
std::string log = std::string(className) + "] " + std::string(format) + "\n";
|
|
||||||
va_start(args, format);
|
|
||||||
vLog(log.c_str(), args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::Flush() {
|
|
||||||
for (const auto& writer : m_Writers) {
|
|
||||||
writer->Flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::SetLogToConsole(bool logToConsole) {
|
|
||||||
for (const auto& writer : m_Writers) {
|
|
||||||
if (writer->IsConsoleWriter()) writer->SetEnabled(logToConsole);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Logger::GetLogToConsole() const {
|
|
||||||
bool toReturn = false;
|
|
||||||
for (const auto& writer : m_Writers) {
|
|
||||||
if (writer->IsConsoleWriter()) toReturn |= writer->GetEnabled();
|
|
||||||
}
|
|
||||||
return toReturn;
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define STRINGIFY_IMPL(x) #x
|
|
||||||
|
|
||||||
#define STRINGIFY(x) STRINGIFY_IMPL(x)
|
|
||||||
|
|
||||||
#define GET_FILE_NAME(x, y) GetFileNameFromAbsolutePath(__FILE__ x y)
|
|
||||||
|
|
||||||
#define FILENAME_AND_LINE GET_FILE_NAME(":", STRINGIFY(__LINE__))
|
|
||||||
|
|
||||||
// Calculate the filename at compile time from the path.
|
|
||||||
// We just do this by scanning the path for the last '/' or '\' character and returning the string after it.
|
|
||||||
constexpr const char* GetFileNameFromAbsolutePath(const char* path) {
|
|
||||||
const char* file = path;
|
|
||||||
while (*path) {
|
|
||||||
char nextChar = *path++;
|
|
||||||
if (nextChar == '/' || nextChar == '\\') {
|
|
||||||
file = path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These have to have a constexpr variable to store the filename_and_line result in a local variable otherwise
|
|
||||||
// they will not be valid constexpr and will be evaluated at runtime instead of compile time!
|
|
||||||
// The full string is still stored in the binary, however the offset of the filename in the absolute paths
|
|
||||||
// is used in the instruction instead of the start of the absolute path.
|
|
||||||
#define LOG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->Log(str, message, ##__VA_ARGS__); } while(0)
|
|
||||||
#define LOG_DEBUG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->LogDebug(str, message, ##__VA_ARGS__); } while(0)
|
|
||||||
|
|
||||||
// Writer class for writing data to files.
|
|
||||||
class Writer {
|
|
||||||
public:
|
|
||||||
Writer(bool enabled = true) : m_Enabled(enabled) {};
|
|
||||||
virtual ~Writer();
|
|
||||||
|
|
||||||
virtual void Log(const char* time, const char* message);
|
|
||||||
virtual void Flush();
|
|
||||||
|
|
||||||
void SetEnabled(bool disabled) { m_Enabled = disabled; }
|
|
||||||
bool GetEnabled() const { return m_Enabled; }
|
|
||||||
|
|
||||||
bool IsConsoleWriter() { return m_IsConsoleWriter; }
|
|
||||||
public:
|
|
||||||
bool m_Enabled = true;
|
|
||||||
bool m_IsConsoleWriter = false;
|
|
||||||
FILE* m_Outfile;
|
|
||||||
};
|
|
||||||
|
|
||||||
// FileWriter class for writing data to a file on a disk.
|
|
||||||
class FileWriter : public Writer {
|
|
||||||
public:
|
|
||||||
FileWriter(const char* outpath);
|
|
||||||
FileWriter(const std::string& outpath) : FileWriter(outpath.c_str()) {};
|
|
||||||
private:
|
|
||||||
std::string m_Outpath;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ConsoleWriter class for writing data to the console.
|
|
||||||
class ConsoleWriter : public Writer {
|
|
||||||
public:
|
|
||||||
ConsoleWriter(bool enabled);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Logger {
|
|
||||||
public:
|
|
||||||
Logger() = delete;
|
|
||||||
Logger(const std::string& outpath, bool logToConsole, bool logDebugStatements);
|
|
||||||
|
|
||||||
void Log(const char* filenameAndLine, const char* format, ...);
|
|
||||||
void LogDebug(const char* filenameAndLine, const char* format, ...);
|
|
||||||
|
|
||||||
void Flush();
|
|
||||||
|
|
||||||
bool GetLogToConsole() const;
|
|
||||||
void SetLogToConsole(bool logToConsole);
|
|
||||||
|
|
||||||
void SetLogDebugStatements(bool logDebugStatements) { m_logDebugStatements = logDebugStatements; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void vLog(const char* format, va_list args);
|
|
||||||
|
|
||||||
bool m_logDebugStatements;
|
|
||||||
std::vector<std::unique_ptr<Writer>> m_Writers;
|
|
||||||
};
|
|
||||||
@@ -107,7 +107,7 @@ void Metrics::EndMeasurement(MetricVariable variable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float Metrics::ToMiliseconds(int64_t nanoseconds) {
|
float Metrics::ToMiliseconds(int64_t nanoseconds) {
|
||||||
return static_cast<float>(nanoseconds) / 1e6;
|
return (float)nanoseconds / 1e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Metrics::MetricVariableToString(MetricVariable variable) {
|
std::string Metrics::MetricVariableToString(MetricVariable variable) {
|
||||||
@@ -193,34 +193,34 @@ size_t Metrics::GetPeakRSS() {
|
|||||||
/* Windows -------------------------------------------------- */
|
/* Windows -------------------------------------------------- */
|
||||||
PROCESS_MEMORY_COUNTERS info;
|
PROCESS_MEMORY_COUNTERS info;
|
||||||
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
||||||
return static_cast<size_t>(info.PeakWorkingSetSize);
|
return (size_t)info.PeakWorkingSetSize;
|
||||||
|
|
||||||
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
|
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
|
||||||
/* AIX and Solaris ------------------------------------------ */
|
/* AIX and Solaris ------------------------------------------ */
|
||||||
struct psinfo psinfo;
|
struct psinfo psinfo;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
|
if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
|
||||||
return static_cast<size_t>(0L); /* Can't open? */
|
return (size_t)0L; /* Can't open? */
|
||||||
if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
|
if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return static_cast<size_t>(0L); /* Can't read? */
|
return (size_t)0L; /* Can't read? */
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
return static_cast<size_t>(psinfo.pr_rssize * 1024L);
|
return (size_t)(psinfo.pr_rssize * 1024L);
|
||||||
|
|
||||||
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
|
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
|
||||||
/* BSD, Linux, and OSX -------------------------------------- */
|
/* BSD, Linux, and OSX -------------------------------------- */
|
||||||
struct rusage rusage;
|
struct rusage rusage;
|
||||||
getrusage(RUSAGE_SELF, &rusage);
|
getrusage(RUSAGE_SELF, &rusage);
|
||||||
#if defined(__APPLE__) && defined(__MACH__)
|
#if defined(__APPLE__) && defined(__MACH__)
|
||||||
return static_cast<size_t>(rusage.ru_maxrss);
|
return (size_t)rusage.ru_maxrss;
|
||||||
#else
|
#else
|
||||||
return static_cast<size_t>(rusage.ru_maxrss * 1024L);
|
return (size_t)(rusage.ru_maxrss * 1024L);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/* Unknown OS ----------------------------------------------- */
|
/* Unknown OS ----------------------------------------------- */
|
||||||
return static_cast<size_t>(0L); /* Unsupported. */
|
return (size_t)0L; /* Unsupported. */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -234,33 +234,33 @@ size_t Metrics::GetCurrentRSS() {
|
|||||||
/* Windows -------------------------------------------------- */
|
/* Windows -------------------------------------------------- */
|
||||||
PROCESS_MEMORY_COUNTERS info;
|
PROCESS_MEMORY_COUNTERS info;
|
||||||
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
||||||
return static_cast<size_t>(info.WorkingSetSize);
|
return (size_t)info.WorkingSetSize;
|
||||||
|
|
||||||
#elif defined(__APPLE__) && defined(__MACH__)
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
/* OSX ------------------------------------------------------ */
|
/* OSX ------------------------------------------------------ */
|
||||||
struct mach_task_basic_info info;
|
struct mach_task_basic_info info;
|
||||||
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
|
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
|
||||||
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
|
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
|
||||||
reinterpret_cast<task_info_t>(&info), &infoCount) != KERN_SUCCESS)
|
(task_info_t)&info, &infoCount) != KERN_SUCCESS)
|
||||||
return static_cast<size_t>(0L); /* Can't access? */
|
return (size_t)0L; /* Can't access? */
|
||||||
return static_cast<size_t>(info.resident_size);
|
return (size_t)info.resident_size;
|
||||||
|
|
||||||
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
|
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
|
||||||
/* Linux ---------------------------------------------------- */
|
/* Linux ---------------------------------------------------- */
|
||||||
long rss = 0L;
|
long rss = 0L;
|
||||||
FILE* fp = NULL;
|
FILE* fp = NULL;
|
||||||
if ((fp = fopen("/proc/self/statm", "r")) == NULL)
|
if ((fp = fopen("/proc/self/statm", "r")) == NULL)
|
||||||
return static_cast<size_t>(0L); /* Can't open? */
|
return (size_t)0L; /* Can't open? */
|
||||||
if (fscanf(fp, "%*s%ld", &rss) != 1) {
|
if (fscanf(fp, "%*s%ld", &rss) != 1) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return static_cast<size_t>(0L); /* Can't read? */
|
return (size_t)0L; /* Can't read? */
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return static_cast<size_t>(rss) * static_cast<size_t>(sysconf(_SC_PAGESIZE));
|
return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/* AIX, BSD, Solaris, and Unknown OS ------------------------ */
|
/* AIX, BSD, Solaris, and Unknown OS ------------------------ */
|
||||||
return static_cast<size_t>(0L); /* Unsupported. */
|
return (size_t)0L; /* Unsupported. */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,195 @@
|
|||||||
#include "NiPoint3.h"
|
#include "NiPoint3.h"
|
||||||
|
#include "NiQuaternion.h"
|
||||||
|
|
||||||
// C++
|
// C++
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
// MARK: Member Functions
|
// Static Variables
|
||||||
|
const NiPoint3 NiPoint3::ZERO(0.0f, 0.0f, 0.0f);
|
||||||
|
const NiPoint3 NiPoint3::UNIT_X(1.0f, 0.0f, 0.0f);
|
||||||
|
const NiPoint3 NiPoint3::UNIT_Y(0.0f, 1.0f, 0.0f);
|
||||||
|
const NiPoint3 NiPoint3::UNIT_Z(0.0f, 0.0f, 1.0f);
|
||||||
|
const NiPoint3 NiPoint3::UNIT_ALL(1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
//! Initializer
|
||||||
|
NiPoint3::NiPoint3(void) {
|
||||||
|
this->x = 0;
|
||||||
|
this->y = 0;
|
||||||
|
this->z = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Initializer
|
||||||
|
NiPoint3::NiPoint3(float x, float y, float z) {
|
||||||
|
this->x = x;
|
||||||
|
this->y = y;
|
||||||
|
this->z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Copy Constructor
|
||||||
|
NiPoint3::NiPoint3(const NiPoint3& point) {
|
||||||
|
this->x = point.x;
|
||||||
|
this->y = point.y;
|
||||||
|
this->z = point.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Destructor
|
||||||
|
NiPoint3::~NiPoint3(void) {}
|
||||||
|
|
||||||
|
// MARK: Getters / Setters
|
||||||
|
|
||||||
|
//! Gets the X coordinate
|
||||||
|
float NiPoint3::GetX(void) const {
|
||||||
|
return this->x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the X coordinate
|
||||||
|
void NiPoint3::SetX(float x) {
|
||||||
|
this->x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets the Y coordinate
|
||||||
|
float NiPoint3::GetY(void) const {
|
||||||
|
return this->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the Y coordinate
|
||||||
|
void NiPoint3::SetY(float y) {
|
||||||
|
this->y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets the Z coordinate
|
||||||
|
float NiPoint3::GetZ(void) const {
|
||||||
|
return this->z;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the Z coordinate
|
||||||
|
void NiPoint3::SetZ(float z) {
|
||||||
|
this->z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
//! Gets the length of the vector
|
//! Gets the length of the vector
|
||||||
float NiPoint3::Length() const {
|
float NiPoint3::Length(void) const {
|
||||||
return std::sqrt(x * x + y * y + z * z);
|
return sqrt(x * x + y * y + z * z);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets the squared length of a vector
|
||||||
|
float NiPoint3::SquaredLength(void) const {
|
||||||
|
return (x * x + y * y + z * z);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns the dot product of the vector dotted with another vector
|
||||||
|
float NiPoint3::DotProduct(const Vector3& vec) const {
|
||||||
|
return ((this->x * vec.x) + (this->y * vec.y) + (this->z * vec.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns the cross product of the vector crossed with another vector
|
||||||
|
Vector3 NiPoint3::CrossProduct(const Vector3& vec) const {
|
||||||
|
return Vector3(((this->y * vec.z) - (this->z * vec.y)),
|
||||||
|
((this->z * vec.x) - (this->x * vec.z)),
|
||||||
|
((this->x * vec.y) - (this->y * vec.x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Unitize the vector
|
//! Unitize the vector
|
||||||
NiPoint3 NiPoint3::Unitize() const {
|
NiPoint3 NiPoint3::Unitize(void) const {
|
||||||
float length = this->Length();
|
float length = this->Length();
|
||||||
|
|
||||||
return length != 0 ? *this / length : NiPoint3Constant::ZERO;
|
return length != 0 ? *this / length : NiPoint3::ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Operators
|
||||||
|
|
||||||
|
//! Operator to check for equality
|
||||||
|
bool NiPoint3::operator==(const NiPoint3& point) const {
|
||||||
|
return point.x == this->x && point.y == this->y && point.z == this->z;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator to check for inequality
|
||||||
|
bool NiPoint3::operator!=(const NiPoint3& point) const {
|
||||||
|
return !(*this == point);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator for subscripting
|
||||||
|
float& NiPoint3::operator[](int i) {
|
||||||
|
float* base = &x;
|
||||||
|
return (float&)base[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator for subscripting
|
||||||
|
const float& NiPoint3::operator[](int i) const {
|
||||||
|
const float* base = &x;
|
||||||
|
return (float&)base[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator for addition of vectors
|
||||||
|
NiPoint3 NiPoint3::operator+(const NiPoint3& point) const {
|
||||||
|
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator for subtraction of vectors
|
||||||
|
NiPoint3 NiPoint3::operator-(const NiPoint3& point) const {
|
||||||
|
return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator for addition of a scalar on all vector components
|
||||||
|
NiPoint3 NiPoint3::operator+(float fScalar) const {
|
||||||
|
return NiPoint3(this->x + fScalar, this->y + fScalar, this->z + fScalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator for subtraction of a scalar on all vector components
|
||||||
|
NiPoint3 NiPoint3::operator-(float fScalar) const {
|
||||||
|
return NiPoint3(this->x - fScalar, this->y - fScalar, this->z - fScalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator for scalar multiplication of a vector
|
||||||
|
NiPoint3 NiPoint3::operator*(float fScalar) const {
|
||||||
|
return NiPoint3(this->x * fScalar, this->y * fScalar, this->z * fScalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator for scalar division of a vector
|
||||||
|
NiPoint3 NiPoint3::operator/(float fScalar) const {
|
||||||
|
float retX = this->x != 0 ? this->x / fScalar : 0;
|
||||||
|
float retY = this->y != 0 ? this->y / fScalar : 0;
|
||||||
|
float retZ = this->z != 0 ? this->z / fScalar : 0;
|
||||||
|
return NiPoint3(retX, retY, retZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Helper Functions
|
// MARK: Helper Functions
|
||||||
|
|
||||||
|
//! Checks to see if the point (or vector) is with an Axis-Aligned Bounding Box
|
||||||
|
bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) {
|
||||||
|
if (this->x < minPoint.x) return false;
|
||||||
|
if (this->x > maxPoint.x) return false;
|
||||||
|
if (this->y < minPoint.y) return false;
|
||||||
|
if (this->y > maxPoint.y) return false;
|
||||||
|
|
||||||
|
return (this->z < maxPoint.z&& this->z > minPoint.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Checks to see if the point (or vector) is within a sphere
|
||||||
|
bool NiPoint3::IsWithinSpehere(const NiPoint3& sphereCenter, float radius) {
|
||||||
|
Vector3 diffVec = Vector3(x - sphereCenter.GetX(), y - sphereCenter.GetY(), z - sphereCenter.GetZ());
|
||||||
|
return (diffVec.SquaredLength() <= (radius * radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
NiPoint3 NiPoint3::ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) {
|
||||||
|
if (a == b) return a;
|
||||||
|
|
||||||
|
const auto pa = p - a;
|
||||||
|
const auto ab = b - a;
|
||||||
|
|
||||||
|
const auto t = pa.DotProduct(ab) / ab.SquaredLength();
|
||||||
|
|
||||||
|
if (t <= 0.0f) return a;
|
||||||
|
|
||||||
|
if (t >= 1.0f) return b;
|
||||||
|
|
||||||
|
return a + ab * t;
|
||||||
|
}
|
||||||
|
|
||||||
float NiPoint3::Angle(const NiPoint3& a, const NiPoint3& b) {
|
float NiPoint3::Angle(const NiPoint3& a, const NiPoint3& b) {
|
||||||
const auto dot = a.DotProduct(b);
|
const auto dot = a.DotProduct(b);
|
||||||
const auto lenA = a.SquaredLength();
|
const auto lenA = a.SquaredLength();
|
||||||
@@ -34,24 +205,47 @@ float NiPoint3::Distance(const NiPoint3& a, const NiPoint3& b) {
|
|||||||
return std::sqrt(dx * dx + dy * dy + dz * dz);
|
return std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
}
|
}
|
||||||
|
|
||||||
NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target, const float maxDistanceDelta) {
|
float NiPoint3::DistanceSquared(const NiPoint3& a, const NiPoint3& b) {
|
||||||
|
const auto dx = a.x - b.x;
|
||||||
|
const auto dy = a.y - b.y;
|
||||||
|
const auto dz = a.z - b.z;
|
||||||
|
|
||||||
|
return dx * dx + dy * dy + dz * dz;
|
||||||
|
}
|
||||||
|
|
||||||
|
NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target, float maxDistanceDelta) {
|
||||||
float dx = target.x - current.x;
|
float dx = target.x - current.x;
|
||||||
float dy = target.y - current.y;
|
float dy = target.y - current.y;
|
||||||
float dz = target.z - current.z;
|
float dz = target.z - current.z;
|
||||||
|
float lengthSquared = (float)((double)dx * (double)dx + (double)dy * (double)dy + (double)dz * (double)dz);
|
||||||
float lengthSquared = static_cast<float>(
|
if ((double)lengthSquared == 0.0 || (double)maxDistanceDelta >= 0.0 && (double)lengthSquared <= (double)maxDistanceDelta * (double)maxDistanceDelta)
|
||||||
static_cast<double>(dx) * static_cast<double>(dx) +
|
|
||||||
static_cast<double>(dy) * static_cast<double>(dy) +
|
|
||||||
static_cast<double>(dz) * static_cast<double>(dz)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (static_cast<double>(lengthSquared) == 0.0
|
|
||||||
|| static_cast<double>(maxDistanceDelta) >= 0.0
|
|
||||||
&& static_cast<double>(lengthSquared)
|
|
||||||
<= static_cast<double>(maxDistanceDelta) * static_cast<double>(maxDistanceDelta)) {
|
|
||||||
return target;
|
return target;
|
||||||
}
|
float length = (float)std::sqrt((double)lengthSquared);
|
||||||
|
|
||||||
float length = std::sqrt(lengthSquared);
|
|
||||||
return NiPoint3(current.x + dx / length * maxDistanceDelta, current.y + dy / length * maxDistanceDelta, current.z + dz / length * maxDistanceDelta);
|
return NiPoint3(current.x + dx / length * maxDistanceDelta, current.y + dy / length * maxDistanceDelta, current.z + dz / length * maxDistanceDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//This code is yoinked from the MS XNA code, so it should be right, even if it's horrible.
|
||||||
|
NiPoint3 NiPoint3::RotateByQuaternion(const NiQuaternion& rotation) {
|
||||||
|
Vector3 vector;
|
||||||
|
float num12 = rotation.x + rotation.x;
|
||||||
|
float num2 = rotation.y + rotation.y;
|
||||||
|
float num = rotation.z + rotation.z;
|
||||||
|
float num11 = rotation.w * num12;
|
||||||
|
float num10 = rotation.w * num2;
|
||||||
|
float num9 = rotation.w * num;
|
||||||
|
float num8 = rotation.x * num12;
|
||||||
|
float num7 = rotation.x * num2;
|
||||||
|
float num6 = rotation.x * num;
|
||||||
|
float num5 = rotation.y * num2;
|
||||||
|
float num4 = rotation.y * num;
|
||||||
|
float num3 = rotation.z * num;
|
||||||
|
|
||||||
|
NiPoint3 value = *this;
|
||||||
|
float num15 = ((value.x * ((1.0f - num5) - num3)) + (value.y * (num7 - num9))) + (value.z * (num6 + num10));
|
||||||
|
float num14 = ((value.x * (num7 + num9)) + (value.y * ((1.0f - num8) - num3))) + (value.z * (num4 - num11));
|
||||||
|
float num13 = ((value.x * (num6 - num10)) + (value.y * (num4 + num11))) + (value.z * ((1.0f - num8) - num5));
|
||||||
|
vector.x = num15;
|
||||||
|
vector.y = num14;
|
||||||
|
vector.z = num13;
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#ifndef __NIPOINT3_H__
|
#pragma once
|
||||||
#define __NIPOINT3_H__
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\file NiPoint3.hpp
|
\file NiPoint3.hpp
|
||||||
@@ -13,13 +12,13 @@ typedef NiPoint3 Vector3; //!< The Vector3 class is technically the NiPoin
|
|||||||
//! A custom class the defines a point in space
|
//! A custom class the defines a point in space
|
||||||
class NiPoint3 {
|
class NiPoint3 {
|
||||||
public:
|
public:
|
||||||
float x{ 0 }; //!< The x position
|
float x; //!< The x position
|
||||||
float y{ 0 }; //!< The y position
|
float y; //!< The y position
|
||||||
float z{ 0 }; //!< The z position
|
float z; //!< The z position
|
||||||
|
|
||||||
|
|
||||||
//! Initializer
|
//! Initializer
|
||||||
constexpr NiPoint3() = default;
|
NiPoint3(void);
|
||||||
|
|
||||||
//! Initializer
|
//! Initializer
|
||||||
/*!
|
/*!
|
||||||
@@ -27,21 +26,23 @@ public:
|
|||||||
\param y The y coordinate
|
\param y The y coordinate
|
||||||
\param z The z coordinate
|
\param z The z coordinate
|
||||||
*/
|
*/
|
||||||
constexpr NiPoint3(const float x, const float y, const float z) noexcept
|
NiPoint3(float x, float y, float z);
|
||||||
: x{ x }
|
|
||||||
, y{ y }
|
|
||||||
, z{ z } {
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Copy Constructor
|
//! Copy Constructor
|
||||||
/*!
|
/*!
|
||||||
\param point The point to copy
|
\param point The point to copy
|
||||||
*/
|
*/
|
||||||
constexpr NiPoint3(const NiPoint3& point) noexcept
|
NiPoint3(const NiPoint3& point);
|
||||||
: x{ point.x }
|
|
||||||
, y{ point.y }
|
//! Destructor
|
||||||
, z{ point.z } {
|
~NiPoint3(void);
|
||||||
}
|
|
||||||
|
// MARK: Constants
|
||||||
|
static const NiPoint3 ZERO; //!< Point(0, 0, 0)
|
||||||
|
static const NiPoint3 UNIT_X; //!< Point(1, 0, 0)
|
||||||
|
static const NiPoint3 UNIT_Y; //!< Point(0, 1, 0)
|
||||||
|
static const NiPoint3 UNIT_Z; //!< Point(0, 0, 1)
|
||||||
|
static const NiPoint3 UNIT_ALL; //!< Point(1, 1, 1)
|
||||||
|
|
||||||
// MARK: Getters / Setters
|
// MARK: Getters / Setters
|
||||||
|
|
||||||
@@ -49,37 +50,38 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The x coordinate
|
\return The x coordinate
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float GetX() const noexcept;
|
float GetX(void) const;
|
||||||
|
|
||||||
//! Sets the X coordinate
|
//! Sets the X coordinate
|
||||||
/*!
|
/*!
|
||||||
\param x The x coordinate
|
\param x The x coordinate
|
||||||
*/
|
*/
|
||||||
constexpr void SetX(const float x) noexcept;
|
void SetX(float x);
|
||||||
|
|
||||||
//! Gets the Y coordinate
|
//! Gets the Y coordinate
|
||||||
/*!
|
/*!
|
||||||
\return The y coordinate
|
\return The y coordinate
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float GetY() const noexcept;
|
float GetY(void) const;
|
||||||
|
|
||||||
//! Sets the Y coordinate
|
//! Sets the Y coordinate
|
||||||
/*!
|
/*!
|
||||||
\param y The y coordinate
|
\param y The y coordinate
|
||||||
*/
|
*/
|
||||||
constexpr void SetY(const float y) noexcept;
|
void SetY(float y);
|
||||||
|
|
||||||
//! Gets the Z coordinate
|
//! Gets the Z coordinate
|
||||||
/*!
|
/*!
|
||||||
\return The z coordinate
|
\return The z coordinate
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float GetZ() const noexcept;
|
float GetZ(void) const;
|
||||||
|
|
||||||
//! Sets the Z coordinate
|
//! Sets the Z coordinate
|
||||||
/*!
|
/*!
|
||||||
\param z The z coordinate
|
\param z The z coordinate
|
||||||
*/
|
*/
|
||||||
constexpr void SetZ(const float z) noexcept;
|
void SetZ(float z);
|
||||||
|
|
||||||
|
|
||||||
// MARK: Member Functions
|
// MARK: Member Functions
|
||||||
|
|
||||||
@@ -87,70 +89,67 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The scalar length of the vector
|
\return The scalar length of the vector
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] float Length() const;
|
float Length(void) const;
|
||||||
|
|
||||||
//! Gets the squared length of a vector
|
//! Gets the squared length of a vector
|
||||||
/*!
|
/*!
|
||||||
\return The squared length of a vector
|
\return The squared length of a vector
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float SquaredLength() const noexcept;
|
float SquaredLength(void) const;
|
||||||
|
|
||||||
//! Returns the dot product of the vector dotted with another vector
|
//! Returns the dot product of the vector dotted with another vector
|
||||||
/*!
|
/*!
|
||||||
\param vec The second vector
|
\param vec The second vector
|
||||||
\return The dot product of the two vectors
|
\return The dot product of the two vectors
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float DotProduct(const Vector3& vec) const noexcept;
|
float DotProduct(const Vector3& vec) const;
|
||||||
|
|
||||||
//! Returns the cross product of the vector crossed with another vector
|
//! Returns the cross product of the vector crossed with another vector
|
||||||
/*!
|
/*!
|
||||||
\param vec The second vector
|
\param vec The second vector
|
||||||
\return The cross product of the two vectors
|
\return The cross product of the two vectors
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr Vector3 CrossProduct(const Vector3& vec) const noexcept;
|
Vector3 CrossProduct(const Vector3& vec) const;
|
||||||
|
|
||||||
//! Unitize the vector
|
//! Unitize the vector
|
||||||
/*!
|
/*!
|
||||||
\returns The current vector
|
\returns The current vector
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] NiPoint3 Unitize() const;
|
NiPoint3 Unitize(void) const;
|
||||||
|
|
||||||
|
|
||||||
// MARK: Operators
|
// MARK: Operators
|
||||||
|
|
||||||
//! Operator to check for equality
|
//! Operator to check for equality
|
||||||
constexpr bool operator==(const NiPoint3& point) const noexcept;
|
bool operator==(const NiPoint3& point) const;
|
||||||
|
|
||||||
//! Operator to check for inequality
|
//! Operator to check for inequality
|
||||||
constexpr bool operator!=(const NiPoint3& point) const noexcept;
|
bool operator!=(const NiPoint3& point) const;
|
||||||
|
|
||||||
//! Operator for subscripting
|
//! Operator for subscripting
|
||||||
constexpr float& operator[](const int i) noexcept;
|
float& operator[](int i);
|
||||||
|
|
||||||
//! Operator for subscripting
|
//! Operator for subscripting
|
||||||
constexpr const float& operator[](const int i) const noexcept;
|
const float& operator[](int i) const;
|
||||||
|
|
||||||
//! Operator for addition of vectors
|
//! Operator for addition of vectors
|
||||||
constexpr NiPoint3 operator+(const NiPoint3& point) const noexcept;
|
NiPoint3 operator+(const NiPoint3& point) const;
|
||||||
|
|
||||||
//! Operator for addition of vectors
|
|
||||||
constexpr NiPoint3& operator+=(const NiPoint3& point) noexcept;
|
|
||||||
|
|
||||||
constexpr NiPoint3& operator*=(const float scalar) noexcept;
|
|
||||||
|
|
||||||
//! Operator for subtraction of vectors
|
//! Operator for subtraction of vectors
|
||||||
constexpr NiPoint3 operator-(const NiPoint3& point) const noexcept;
|
NiPoint3 operator-(const NiPoint3& point) const;
|
||||||
|
|
||||||
//! Operator for addition of a scalar on all vector components
|
//! Operator for addition of a scalar on all vector components
|
||||||
constexpr NiPoint3 operator+(const float fScalar) const noexcept;
|
NiPoint3 operator+(float fScalar) const;
|
||||||
|
|
||||||
//! Operator for subtraction of a scalar on all vector components
|
//! Operator for subtraction of a scalar on all vector components
|
||||||
constexpr NiPoint3 operator-(const float fScalar) const noexcept;
|
NiPoint3 operator-(float fScalar) const;
|
||||||
|
|
||||||
//! Operator for scalar multiplication of a vector
|
//! Operator for scalar multiplication of a vector
|
||||||
constexpr NiPoint3 operator*(const float fScalar) const noexcept;
|
NiPoint3 operator*(float fScalar) const;
|
||||||
|
|
||||||
//! Operator for scalar division of a vector
|
//! Operator for scalar division of a vector
|
||||||
constexpr NiPoint3 operator/(const float fScalar) const noexcept;
|
NiPoint3 operator/(float fScalar) const;
|
||||||
|
|
||||||
|
|
||||||
// MARK: Helper Functions
|
// MARK: Helper Functions
|
||||||
|
|
||||||
@@ -160,14 +159,14 @@ public:
|
|||||||
\param maxPoint The maximum point of the bounding box
|
\param maxPoint The maximum point of the bounding box
|
||||||
\return Whether or not this point lies within the box
|
\return Whether or not this point lies within the box
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr bool IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) noexcept;
|
bool IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint);
|
||||||
|
|
||||||
//! Checks to see if the point (or vector) is within a sphere
|
//! Checks to see if the point (or vector) is within a sphere
|
||||||
/*!
|
/*!
|
||||||
\param sphereCenter The sphere center
|
\param sphereCenter The sphere center
|
||||||
\param radius The radius
|
\param radius The radius
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr bool IsWithinSphere(const NiPoint3& sphereCenter, const float radius) noexcept;
|
bool IsWithinSpehere(const NiPoint3& sphereCenter, float radius);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\param a Start of line
|
\param a Start of line
|
||||||
@@ -175,30 +174,15 @@ public:
|
|||||||
\param p Refrence point
|
\param p Refrence point
|
||||||
\return The point of line AB which is closest to P
|
\return The point of line AB which is closest to P
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] static constexpr NiPoint3 ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) noexcept;
|
static NiPoint3 ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p);
|
||||||
|
|
||||||
[[nodiscard]] static float Angle(const NiPoint3& a, const NiPoint3& b);
|
static float Angle(const NiPoint3& a, const NiPoint3& b);
|
||||||
|
|
||||||
[[nodiscard]] static float Distance(const NiPoint3& a, const NiPoint3& b);
|
static float Distance(const NiPoint3& a, const NiPoint3& b);
|
||||||
|
|
||||||
[[nodiscard]] static constexpr float DistanceSquared(const NiPoint3& a, const NiPoint3& b) noexcept;
|
static float DistanceSquared(const NiPoint3& a, const NiPoint3& b);
|
||||||
|
|
||||||
[[nodiscard]] static NiPoint3 MoveTowards(const NiPoint3& current, const NiPoint3& target, const float maxDistanceDelta);
|
static NiPoint3 MoveTowards(const NiPoint3& current, const NiPoint3& target, float maxDistanceDelta);
|
||||||
|
|
||||||
//This code is yoinked from the MS XNA code, so it should be right, even if it's horrible.
|
NiPoint3 RotateByQuaternion(const NiQuaternion& rotation);
|
||||||
[[nodiscard]] constexpr NiPoint3 RotateByQuaternion(const NiQuaternion& rotation) noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Static Variables
|
|
||||||
namespace NiPoint3Constant {
|
|
||||||
constexpr NiPoint3 ZERO(0.0f, 0.0f, 0.0f);
|
|
||||||
constexpr NiPoint3 UNIT_X(1.0f, 0.0f, 0.0f);
|
|
||||||
constexpr NiPoint3 UNIT_Y(0.0f, 1.0f, 0.0f);
|
|
||||||
constexpr NiPoint3 UNIT_Z(0.0f, 0.0f, 1.0f);
|
|
||||||
constexpr NiPoint3 UNIT_ALL(1.0f, 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// .inl file needed for code organization and to circumvent circular dependency issues
|
|
||||||
#include "NiPoint3.inl"
|
|
||||||
|
|
||||||
#endif // !__NIPOINT3_H__
|
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#ifndef __NIPOINT3_H__
|
|
||||||
#error "This should only be included inline in NiPoint3.h: Do not include directly!"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "NiQuaternion.h"
|
|
||||||
|
|
||||||
// MARK: Getters / Setters
|
|
||||||
|
|
||||||
//! Gets the X coordinate
|
|
||||||
constexpr float NiPoint3::GetX() const noexcept {
|
|
||||||
return this->x;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the X coordinate
|
|
||||||
constexpr void NiPoint3::SetX(const float x) noexcept {
|
|
||||||
this->x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets the Y coordinate
|
|
||||||
constexpr float NiPoint3::GetY() const noexcept {
|
|
||||||
return this->y;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the Y coordinate
|
|
||||||
constexpr void NiPoint3::SetY(const float y) noexcept {
|
|
||||||
this->y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets the Z coordinate
|
|
||||||
constexpr float NiPoint3::GetZ() const noexcept {
|
|
||||||
return this->z;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the Z coordinate
|
|
||||||
constexpr void NiPoint3::SetZ(const float z) noexcept {
|
|
||||||
this->z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Member Functions
|
|
||||||
|
|
||||||
//! Gets the squared length of a vector
|
|
||||||
constexpr float NiPoint3::SquaredLength() const noexcept {
|
|
||||||
return (x * x + y * y + z * z);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Returns the dot product of the vector dotted with another vector
|
|
||||||
constexpr float NiPoint3::DotProduct(const Vector3& vec) const noexcept {
|
|
||||||
return ((this->x * vec.x) + (this->y * vec.y) + (this->z * vec.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Returns the cross product of the vector crossed with another vector
|
|
||||||
constexpr Vector3 NiPoint3::CrossProduct(const Vector3& vec) const noexcept {
|
|
||||||
return Vector3(((this->y * vec.z) - (this->z * vec.y)),
|
|
||||||
((this->z * vec.x) - (this->x * vec.z)),
|
|
||||||
((this->x * vec.y) - (this->y * vec.x)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Operators
|
|
||||||
|
|
||||||
//! Operator to check for equality
|
|
||||||
constexpr bool NiPoint3::operator==(const NiPoint3& point) const noexcept {
|
|
||||||
return point.x == this->x && point.y == this->y && point.z == this->z;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator to check for inequality
|
|
||||||
constexpr bool NiPoint3::operator!=(const NiPoint3& point) const noexcept {
|
|
||||||
return !(*this == point);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for subscripting
|
|
||||||
constexpr float& NiPoint3::operator[](const int i) noexcept {
|
|
||||||
float* base = &x;
|
|
||||||
return base[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for subscripting
|
|
||||||
constexpr const float& NiPoint3::operator[](const int i) const noexcept {
|
|
||||||
const float* base = &x;
|
|
||||||
return base[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for addition of vectors
|
|
||||||
constexpr NiPoint3 NiPoint3::operator+(const NiPoint3& point) const noexcept {
|
|
||||||
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for addition of vectors
|
|
||||||
constexpr NiPoint3& NiPoint3::operator+=(const NiPoint3& point) noexcept {
|
|
||||||
this->x += point.x;
|
|
||||||
this->y += point.y;
|
|
||||||
this->z += point.z;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr NiPoint3& NiPoint3::operator*=(const float scalar) noexcept {
|
|
||||||
this->x *= scalar;
|
|
||||||
this->y *= scalar;
|
|
||||||
this->z *= scalar;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for subtraction of vectors
|
|
||||||
constexpr NiPoint3 NiPoint3::operator-(const NiPoint3& point) const noexcept {
|
|
||||||
return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for addition of a scalar on all vector components
|
|
||||||
constexpr NiPoint3 NiPoint3::operator+(const float fScalar) const noexcept {
|
|
||||||
return NiPoint3(this->x + fScalar, this->y + fScalar, this->z + fScalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for subtraction of a scalar on all vector components
|
|
||||||
constexpr NiPoint3 NiPoint3::operator-(const float fScalar) const noexcept {
|
|
||||||
return NiPoint3(this->x - fScalar, this->y - fScalar, this->z - fScalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for scalar multiplication of a vector
|
|
||||||
constexpr NiPoint3 NiPoint3::operator*(const float fScalar) const noexcept {
|
|
||||||
return NiPoint3(this->x * fScalar, this->y * fScalar, this->z * fScalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator for scalar division of a vector
|
|
||||||
constexpr NiPoint3 NiPoint3::operator/(const float fScalar) const noexcept {
|
|
||||||
float retX = this->x != 0 ? this->x / fScalar : 0;
|
|
||||||
float retY = this->y != 0 ? this->y / fScalar : 0;
|
|
||||||
float retZ = this->z != 0 ? this->z / fScalar : 0;
|
|
||||||
return NiPoint3(retX, retY, retZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Helper Functions
|
|
||||||
|
|
||||||
//! Checks to see if the point (or vector) is with an Axis-Aligned Bounding Box
|
|
||||||
constexpr bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) noexcept {
|
|
||||||
if (this->x < minPoint.x) return false;
|
|
||||||
if (this->x > maxPoint.x) return false;
|
|
||||||
if (this->y < minPoint.y) return false;
|
|
||||||
if (this->y > maxPoint.y) return false;
|
|
||||||
|
|
||||||
return (this->z < maxPoint.z && this->z > minPoint.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Checks to see if the point (or vector) is within a sphere
|
|
||||||
constexpr bool NiPoint3::IsWithinSphere(const NiPoint3& sphereCenter, const float radius) noexcept {
|
|
||||||
Vector3 diffVec = Vector3(x - sphereCenter.GetX(), y - sphereCenter.GetY(), z - sphereCenter.GetZ());
|
|
||||||
return (diffVec.SquaredLength() <= (radius * radius));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr NiPoint3 NiPoint3::ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) noexcept {
|
|
||||||
if (a == b) return a;
|
|
||||||
|
|
||||||
const auto pa = p - a;
|
|
||||||
const auto ab = b - a;
|
|
||||||
|
|
||||||
const auto t = pa.DotProduct(ab) / ab.SquaredLength();
|
|
||||||
|
|
||||||
if (t <= 0.0f) return a;
|
|
||||||
|
|
||||||
if (t >= 1.0f) return b;
|
|
||||||
|
|
||||||
return a + ab * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr float NiPoint3::DistanceSquared(const NiPoint3& a, const NiPoint3& b) noexcept {
|
|
||||||
const auto dx = a.x - b.x;
|
|
||||||
const auto dy = a.y - b.y;
|
|
||||||
const auto dz = a.z - b.z;
|
|
||||||
|
|
||||||
return dx * dx + dy * dy + dz * dz;
|
|
||||||
}
|
|
||||||
|
|
||||||
//This code is yoinked from the MS XNA code, so it should be right, even if it's horrible.
|
|
||||||
constexpr NiPoint3 NiPoint3::RotateByQuaternion(const NiQuaternion& rotation) noexcept {
|
|
||||||
Vector3 vector;
|
|
||||||
float num12 = rotation.x + rotation.x;
|
|
||||||
float num2 = rotation.y + rotation.y;
|
|
||||||
float num = rotation.z + rotation.z;
|
|
||||||
float num11 = rotation.w * num12;
|
|
||||||
float num10 = rotation.w * num2;
|
|
||||||
float num9 = rotation.w * num;
|
|
||||||
float num8 = rotation.x * num12;
|
|
||||||
float num7 = rotation.x * num2;
|
|
||||||
float num6 = rotation.x * num;
|
|
||||||
float num5 = rotation.y * num2;
|
|
||||||
float num4 = rotation.y * num;
|
|
||||||
float num3 = rotation.z * num;
|
|
||||||
|
|
||||||
NiPoint3 value = *this;
|
|
||||||
float num15 = ((value.x * ((1.0f - num5) - num3)) + (value.y * (num7 - num9))) + (value.z * (num6 + num10));
|
|
||||||
float num14 = ((value.x * (num7 + num9)) + (value.y * ((1.0f - num8) - num3))) + (value.z * (num4 - num11));
|
|
||||||
float num13 = ((value.x * (num6 - num10)) + (value.y * (num4 + num11))) + (value.z * ((1.0f - num8) - num5));
|
|
||||||
vector.x = num15;
|
|
||||||
vector.y = num14;
|
|
||||||
vector.z = num13;
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,89 @@
|
|||||||
// C++
|
// C++
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
// Static Variables
|
||||||
|
const NiQuaternion NiQuaternion::IDENTITY(1, 0, 0, 0);
|
||||||
|
|
||||||
|
//! The initializer
|
||||||
|
NiQuaternion::NiQuaternion(void) {
|
||||||
|
this->w = 1;
|
||||||
|
this->x = 0;
|
||||||
|
this->y = 0;
|
||||||
|
this->z = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! The initializer
|
||||||
|
NiQuaternion::NiQuaternion(float w, float x, float y, float z) {
|
||||||
|
this->w = w;
|
||||||
|
this->x = x;
|
||||||
|
this->y = y;
|
||||||
|
this->z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Destructor
|
||||||
|
NiQuaternion::~NiQuaternion(void) {}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Setters / Getters
|
||||||
|
|
||||||
|
//! Gets the W coordinate
|
||||||
|
float NiQuaternion::GetW(void) const {
|
||||||
|
return this->w;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the W coordinate
|
||||||
|
void NiQuaternion::SetW(float w) {
|
||||||
|
this->w = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets the X coordinate
|
||||||
|
float NiQuaternion::GetX(void) const {
|
||||||
|
return this->x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the X coordinate
|
||||||
|
void NiQuaternion::SetX(float x) {
|
||||||
|
this->x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets the Y coordinate
|
||||||
|
float NiQuaternion::GetY(void) const {
|
||||||
|
return this->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the Y coordinate
|
||||||
|
void NiQuaternion::SetY(float y) {
|
||||||
|
this->y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets the Z coordinate
|
||||||
|
float NiQuaternion::GetZ(void) const {
|
||||||
|
return this->z;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the Z coordinate
|
||||||
|
void NiQuaternion::SetZ(float z) {
|
||||||
|
this->z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Member Functions
|
// MARK: Member Functions
|
||||||
|
|
||||||
|
//! Returns the forward vector from the quaternion
|
||||||
|
Vector3 NiQuaternion::GetForwardVector(void) const {
|
||||||
|
return Vector3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns the up vector from the quaternion
|
||||||
|
Vector3 NiQuaternion::GetUpVector(void) const {
|
||||||
|
return Vector3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns the right vector from the quaternion
|
||||||
|
Vector3 NiQuaternion::GetRightVector(void) const {
|
||||||
|
return Vector3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y));
|
||||||
|
}
|
||||||
|
|
||||||
Vector3 NiQuaternion::GetEulerAngles() const {
|
Vector3 NiQuaternion::GetEulerAngles() const {
|
||||||
Vector3 angles;
|
Vector3 angles;
|
||||||
|
|
||||||
@@ -30,9 +111,22 @@ Vector3 NiQuaternion::GetEulerAngles() const {
|
|||||||
return angles;
|
return angles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Operators
|
||||||
|
|
||||||
|
//! Operator to check for equality
|
||||||
|
bool NiQuaternion::operator==(const NiQuaternion& rot) const {
|
||||||
|
return rot.x == this->x && rot.y == this->y && rot.z == this->z && rot.w == this->w;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Operator to check for inequality
|
||||||
|
bool NiQuaternion::operator!=(const NiQuaternion& rot) const {
|
||||||
|
return !(*this == rot);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Helper Functions
|
// MARK: Helper Functions
|
||||||
|
|
||||||
//! Look from a specific point in space to another point in space (Y-locked)
|
//! Look from a specific point in space to another point in space
|
||||||
NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
|
NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
|
||||||
//To make sure we don't orient around the X/Z axis:
|
//To make sure we don't orient around the X/Z axis:
|
||||||
NiPoint3 source = sourcePoint;
|
NiPoint3 source = sourcePoint;
|
||||||
@@ -42,7 +136,7 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d
|
|||||||
|
|
||||||
NiPoint3 forwardVector = NiPoint3(dest - source).Unitize();
|
NiPoint3 forwardVector = NiPoint3(dest - source).Unitize();
|
||||||
|
|
||||||
NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
|
NiPoint3 posZ = NiPoint3::UNIT_Z;
|
||||||
NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize();
|
NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize();
|
||||||
|
|
||||||
float dot = posZ.DotProduct(forwardVector);
|
float dot = posZ.DotProduct(forwardVector);
|
||||||
@@ -54,11 +148,10 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d
|
|||||||
return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle);
|
return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Look from a specific point in space to another point in space
|
|
||||||
NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
|
NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
|
||||||
NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize();
|
NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize();
|
||||||
|
|
||||||
NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
|
NiPoint3 posZ = NiPoint3::UNIT_Z;
|
||||||
NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize();
|
NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize();
|
||||||
|
|
||||||
float dot = posZ.DotProduct(forwardVector);
|
float dot = posZ.DotProduct(forwardVector);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#ifndef __NIQUATERNION_H__
|
#pragma once
|
||||||
#define __NIQUATERNION_H__
|
|
||||||
|
|
||||||
// Custom Classes
|
// Custom Classes
|
||||||
#include "NiPoint3.h"
|
#include "NiPoint3.h"
|
||||||
@@ -15,14 +14,14 @@ typedef NiQuaternion Quaternion; //!< A typedef for a shorthand version o
|
|||||||
//! A class that defines a rotation in space
|
//! A class that defines a rotation in space
|
||||||
class NiQuaternion {
|
class NiQuaternion {
|
||||||
public:
|
public:
|
||||||
float w{ 1 }; //!< The w coordinate
|
float w; //!< The w coordinate
|
||||||
float x{ 0 }; //!< The x coordinate
|
float x; //!< The x coordinate
|
||||||
float y{ 0 }; //!< The y coordinate
|
float y; //!< The y coordinate
|
||||||
float z{ 0 }; //!< The z coordinate
|
float z; //!< The z coordinate
|
||||||
|
|
||||||
|
|
||||||
//! The initializer
|
//! The initializer
|
||||||
constexpr NiQuaternion() = default;
|
NiQuaternion(void);
|
||||||
|
|
||||||
//! The initializer
|
//! The initializer
|
||||||
/*!
|
/*!
|
||||||
@@ -31,12 +30,13 @@ public:
|
|||||||
\param y The y coordinate
|
\param y The y coordinate
|
||||||
\param z The z coordinate
|
\param z The z coordinate
|
||||||
*/
|
*/
|
||||||
constexpr NiQuaternion(const float w, const float x, const float y, const float z) noexcept
|
NiQuaternion(float w, float x, float y, float z);
|
||||||
: w{ w }
|
|
||||||
, x{ x }
|
//! Destructor
|
||||||
, y{ y }
|
~NiQuaternion(void);
|
||||||
, z{ z } {
|
|
||||||
}
|
// MARK: Constants
|
||||||
|
static const NiQuaternion IDENTITY; //!< Quaternion(1, 0, 0, 0)
|
||||||
|
|
||||||
// MARK: Setters / Getters
|
// MARK: Setters / Getters
|
||||||
|
|
||||||
@@ -44,49 +44,50 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The w coordinate
|
\return The w coordinate
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float GetW() const noexcept;
|
float GetW(void) const;
|
||||||
|
|
||||||
//! Sets the W coordinate
|
//! Sets the W coordinate
|
||||||
/*!
|
/*!
|
||||||
\param w The w coordinate
|
\param w The w coordinate
|
||||||
*/
|
*/
|
||||||
constexpr void SetW(const float w) noexcept;
|
void SetW(float w);
|
||||||
|
|
||||||
//! Gets the X coordinate
|
//! Gets the X coordinate
|
||||||
/*!
|
/*!
|
||||||
\return The x coordinate
|
\return The x coordinate
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float GetX() const noexcept;
|
float GetX(void) const;
|
||||||
|
|
||||||
//! Sets the X coordinate
|
//! Sets the X coordinate
|
||||||
/*!
|
/*!
|
||||||
\param x The x coordinate
|
\param x The x coordinate
|
||||||
*/
|
*/
|
||||||
constexpr void SetX(const float x) noexcept;
|
void SetX(float x);
|
||||||
|
|
||||||
//! Gets the Y coordinate
|
//! Gets the Y coordinate
|
||||||
/*!
|
/*!
|
||||||
\return The y coordinate
|
\return The y coordinate
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float GetY() const noexcept;
|
float GetY(void) const;
|
||||||
|
|
||||||
//! Sets the Y coordinate
|
//! Sets the Y coordinate
|
||||||
/*!
|
/*!
|
||||||
\param y The y coordinate
|
\param y The y coordinate
|
||||||
*/
|
*/
|
||||||
constexpr void SetY(const float y) noexcept;
|
void SetY(float y);
|
||||||
|
|
||||||
//! Gets the Z coordinate
|
//! Gets the Z coordinate
|
||||||
/*!
|
/*!
|
||||||
\return The z coordinate
|
\return The z coordinate
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr float GetZ() const noexcept;
|
float GetZ(void) const;
|
||||||
|
|
||||||
//! Sets the Z coordinate
|
//! Sets the Z coordinate
|
||||||
/*!
|
/*!
|
||||||
\param z The z coordinate
|
\param z The z coordinate
|
||||||
*/
|
*/
|
||||||
constexpr void SetZ(const float z) noexcept;
|
void SetZ(float z);
|
||||||
|
|
||||||
|
|
||||||
// MARK: Member Functions
|
// MARK: Member Functions
|
||||||
|
|
||||||
@@ -94,29 +95,31 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The forward vector of the quaternion
|
\return The forward vector of the quaternion
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr Vector3 GetForwardVector() const noexcept;
|
Vector3 GetForwardVector(void) const;
|
||||||
|
|
||||||
//! Returns the up vector from the quaternion
|
//! Returns the up vector from the quaternion
|
||||||
/*!
|
/*!
|
||||||
\return The up vector fo the quaternion
|
\return The up vector fo the quaternion
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr Vector3 GetUpVector() const noexcept;
|
Vector3 GetUpVector(void) const;
|
||||||
|
|
||||||
//! Returns the right vector from the quaternion
|
//! Returns the right vector from the quaternion
|
||||||
/*!
|
/*!
|
||||||
\return The right vector of the quaternion
|
\return The right vector of the quaternion
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr Vector3 GetRightVector() const noexcept;
|
Vector3 GetRightVector(void) const;
|
||||||
|
|
||||||
|
Vector3 GetEulerAngles() const;
|
||||||
|
|
||||||
[[nodiscard]] Vector3 GetEulerAngles() const;
|
|
||||||
|
|
||||||
// MARK: Operators
|
// MARK: Operators
|
||||||
|
|
||||||
//! Operator to check for equality
|
//! Operator to check for equality
|
||||||
constexpr bool operator==(const NiQuaternion& rot) const noexcept;
|
bool operator==(const NiQuaternion& rot) const;
|
||||||
|
|
||||||
//! Operator to check for inequality
|
//! Operator to check for inequality
|
||||||
constexpr bool operator!=(const NiQuaternion& rot) const noexcept;
|
bool operator!=(const NiQuaternion& rot) const;
|
||||||
|
|
||||||
|
|
||||||
// MARK: Helper Functions
|
// MARK: Helper Functions
|
||||||
|
|
||||||
@@ -126,7 +129,7 @@ public:
|
|||||||
\param destPoint The destination location
|
\param destPoint The destination location
|
||||||
\return The Quaternion with the rotation towards the destination
|
\return The Quaternion with the rotation towards the destination
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] static NiQuaternion LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
|
static NiQuaternion LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
|
||||||
|
|
||||||
//! Look from a specific point in space to another point in space
|
//! Look from a specific point in space to another point in space
|
||||||
/*!
|
/*!
|
||||||
@@ -134,7 +137,7 @@ public:
|
|||||||
\param destPoint The destination location
|
\param destPoint The destination location
|
||||||
\return The Quaternion with the rotation towards the destination
|
\return The Quaternion with the rotation towards the destination
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] static NiQuaternion LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
|
static NiQuaternion LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
|
||||||
|
|
||||||
//! Creates a Quaternion from a specific axis and angle relative to that axis
|
//! Creates a Quaternion from a specific axis and angle relative to that axis
|
||||||
/*!
|
/*!
|
||||||
@@ -142,17 +145,7 @@ public:
|
|||||||
\param angle The angle relative to this axis
|
\param angle The angle relative to this axis
|
||||||
\return A quaternion created from the axis and angle
|
\return A quaternion created from the axis and angle
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] static NiQuaternion CreateFromAxisAngle(const Vector3& axis, float angle);
|
static NiQuaternion CreateFromAxisAngle(const Vector3& axis, float angle);
|
||||||
|
|
||||||
[[nodiscard]] static NiQuaternion FromEulerAngles(const NiPoint3& eulerAngles);
|
static NiQuaternion FromEulerAngles(const NiPoint3& eulerAngles);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Static Variables
|
|
||||||
namespace NiQuaternionConstant {
|
|
||||||
constexpr NiQuaternion IDENTITY(1, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include constexpr and inline function definitions in a seperate file for readability
|
|
||||||
#include "NiQuaternion.inl"
|
|
||||||
|
|
||||||
#endif // !__NIQUATERNION_H__
|
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#ifndef __NIQUATERNION_H__
|
|
||||||
#error "This should only be included inline in NiQuaternion.h: Do not include directly!"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: Setters / Getters
|
|
||||||
|
|
||||||
//! Gets the W coordinate
|
|
||||||
constexpr float NiQuaternion::GetW() const noexcept {
|
|
||||||
return this->w;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the W coordinate
|
|
||||||
constexpr void NiQuaternion::SetW(const float w) noexcept {
|
|
||||||
this->w = w;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets the X coordinate
|
|
||||||
constexpr float NiQuaternion::GetX() const noexcept {
|
|
||||||
return this->x;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the X coordinate
|
|
||||||
constexpr void NiQuaternion::SetX(const float x) noexcept {
|
|
||||||
this->x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets the Y coordinate
|
|
||||||
constexpr float NiQuaternion::GetY() const noexcept {
|
|
||||||
return this->y;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the Y coordinate
|
|
||||||
constexpr void NiQuaternion::SetY(const float y) noexcept {
|
|
||||||
this->y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets the Z coordinate
|
|
||||||
constexpr float NiQuaternion::GetZ() const noexcept {
|
|
||||||
return this->z;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Sets the Z coordinate
|
|
||||||
constexpr void NiQuaternion::SetZ(const float z) noexcept {
|
|
||||||
this->z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Member Functions
|
|
||||||
|
|
||||||
//! Returns the forward vector from the quaternion
|
|
||||||
constexpr Vector3 NiQuaternion::GetForwardVector() const noexcept {
|
|
||||||
return Vector3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y));
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Returns the up vector from the quaternion
|
|
||||||
constexpr Vector3 NiQuaternion::GetUpVector() const noexcept {
|
|
||||||
return Vector3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x));
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Returns the right vector from the quaternion
|
|
||||||
constexpr Vector3 NiQuaternion::GetRightVector() const noexcept {
|
|
||||||
return Vector3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y));
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Operators
|
|
||||||
|
|
||||||
//! Operator to check for equality
|
|
||||||
constexpr bool NiQuaternion::operator==(const NiQuaternion& rot) const noexcept {
|
|
||||||
return rot.x == this->x && rot.y == this->y && rot.z == this->z && rot.w == this->w;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Operator to check for inequality
|
|
||||||
constexpr bool NiQuaternion::operator!=(const NiQuaternion& rot) const noexcept {
|
|
||||||
return !(*this == rot);
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#ifndef __OBSERVABLE_H__
|
|
||||||
#define __OBSERVABLE_H__
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief An event which can be observed by multiple observers.
|
|
||||||
*
|
|
||||||
* @tparam T The types of the arguments to be passed to the observers.
|
|
||||||
*/
|
|
||||||
template <typename... T>
|
|
||||||
class Observable {
|
|
||||||
public:
|
|
||||||
typedef std::function<void(T...)> Observer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds an observer to the event.
|
|
||||||
*
|
|
||||||
* @param observer The observer to add.
|
|
||||||
*/
|
|
||||||
void AddObserver(const Observer& observer) {
|
|
||||||
observers.push_back(observer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Removes an observer from the event.
|
|
||||||
*
|
|
||||||
* @param observer The observer to remove.
|
|
||||||
*/
|
|
||||||
void RemoveObserver(const Observer& observer) {
|
|
||||||
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Notifies all observers of the event.
|
|
||||||
*
|
|
||||||
* @param args The arguments to pass to the observers.
|
|
||||||
*/
|
|
||||||
void Notify(T... args) {
|
|
||||||
for (const auto& observer : observers) {
|
|
||||||
observer(args...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* += operator overload.
|
|
||||||
*/
|
|
||||||
Observable& operator+=(const Observer& observer) {
|
|
||||||
AddObserver(observer);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* -= operator overload.
|
|
||||||
*/
|
|
||||||
Observable& operator-=(const Observer& observer) {
|
|
||||||
RemoveObserver(observer);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* () operator overload.
|
|
||||||
*/
|
|
||||||
void operator()(T... args) {
|
|
||||||
Notify(args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Observer> observers;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //!__OBSERVABLE_H__
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#ifndef __POSITIONUPDATE__H__
|
|
||||||
#define __POSITIONUPDATE__H__
|
|
||||||
|
|
||||||
#include "NiPoint3.h"
|
|
||||||
#include "NiQuaternion.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct RemoteInputInfo {
|
|
||||||
bool operator==(const RemoteInputInfo& other) {
|
|
||||||
return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
float m_RemoteInputX = 0;
|
|
||||||
float m_RemoteInputY = 0;
|
|
||||||
bool m_IsPowersliding = false;
|
|
||||||
bool m_IsModified = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LocalSpaceInfo {
|
|
||||||
LWOOBJID objectId = LWOOBJID_EMPTY;
|
|
||||||
NiPoint3 position = NiPoint3Constant::ZERO;
|
|
||||||
NiPoint3 linearVelocity = NiPoint3Constant::ZERO;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PositionUpdate {
|
|
||||||
NiPoint3 position = NiPoint3Constant::ZERO;
|
|
||||||
NiQuaternion rotation = NiQuaternionConstant::IDENTITY;
|
|
||||||
bool onGround = false;
|
|
||||||
bool onRail = false;
|
|
||||||
NiPoint3 velocity = NiPoint3Constant::ZERO;
|
|
||||||
NiPoint3 angularVelocity = NiPoint3Constant::ZERO;
|
|
||||||
LocalSpaceInfo localSpaceInfo;
|
|
||||||
RemoteInputInfo remoteInputInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //!__POSITIONUPDATE__H__
|
|
||||||
152
dCommon/SHA512.cpp
Normal file
152
dCommon/SHA512.cpp
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#include "SHA512.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
const unsigned long long SHA512::sha512_k[80] = //ULL = uint64
|
||||||
|
{ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
|
||||||
|
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
|
||||||
|
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
|
||||||
|
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
|
||||||
|
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
|
||||||
|
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
|
||||||
|
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
|
||||||
|
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
|
||||||
|
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
|
||||||
|
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
|
||||||
|
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
|
||||||
|
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
|
||||||
|
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
|
||||||
|
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
|
||||||
|
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
|
||||||
|
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
|
||||||
|
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
|
||||||
|
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
|
||||||
|
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
|
||||||
|
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
|
||||||
|
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
|
||||||
|
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
|
||||||
|
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
|
||||||
|
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
|
||||||
|
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
|
||||||
|
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
|
||||||
|
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
|
||||||
|
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
|
||||||
|
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
|
||||||
|
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
|
||||||
|
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
|
||||||
|
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
|
||||||
|
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
|
||||||
|
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
|
||||||
|
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
|
||||||
|
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
|
||||||
|
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
|
||||||
|
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
|
||||||
|
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
|
||||||
|
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL };
|
||||||
|
|
||||||
|
void SHA512::transform(const unsigned char* message, unsigned int block_nb) {
|
||||||
|
uint64 w[80];
|
||||||
|
uint64 wv[8];
|
||||||
|
uint64 t1, t2;
|
||||||
|
const unsigned char* sub_block;
|
||||||
|
int i, j;
|
||||||
|
for (i = 0; i < (int)block_nb; i++) {
|
||||||
|
sub_block = message + (i << 7);
|
||||||
|
for (j = 0; j < 16; j++) {
|
||||||
|
SHA2_PACK64(&sub_block[j << 3], &w[j]);
|
||||||
|
}
|
||||||
|
for (j = 16; j < 80; j++) {
|
||||||
|
w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16];
|
||||||
|
}
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
wv[j] = m_h[j];
|
||||||
|
}
|
||||||
|
for (j = 0; j < 80; j++) {
|
||||||
|
t1 = wv[7] + SHA512_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6])
|
||||||
|
+ sha512_k[j] + w[j];
|
||||||
|
t2 = SHA512_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]);
|
||||||
|
wv[7] = wv[6];
|
||||||
|
wv[6] = wv[5];
|
||||||
|
wv[5] = wv[4];
|
||||||
|
wv[4] = wv[3] + t1;
|
||||||
|
wv[3] = wv[2];
|
||||||
|
wv[2] = wv[1];
|
||||||
|
wv[1] = wv[0];
|
||||||
|
wv[0] = t1 + t2;
|
||||||
|
}
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
m_h[j] += wv[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHA512::init() {
|
||||||
|
m_h[0] = 0x6a09e667f3bcc908ULL;
|
||||||
|
m_h[1] = 0xbb67ae8584caa73bULL;
|
||||||
|
m_h[2] = 0x3c6ef372fe94f82bULL;
|
||||||
|
m_h[3] = 0xa54ff53a5f1d36f1ULL;
|
||||||
|
m_h[4] = 0x510e527fade682d1ULL;
|
||||||
|
m_h[5] = 0x9b05688c2b3e6c1fULL;
|
||||||
|
m_h[6] = 0x1f83d9abfb41bd6bULL;
|
||||||
|
m_h[7] = 0x5be0cd19137e2179ULL;
|
||||||
|
m_len = 0;
|
||||||
|
m_tot_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHA512::update(const unsigned char* message, unsigned int len) {
|
||||||
|
unsigned int block_nb;
|
||||||
|
unsigned int new_len, rem_len, tmp_len;
|
||||||
|
const unsigned char* shifted_message;
|
||||||
|
tmp_len = SHA384_512_BLOCK_SIZE - m_len;
|
||||||
|
rem_len = len < tmp_len ? len : tmp_len;
|
||||||
|
memcpy(&m_block[m_len], message, rem_len);
|
||||||
|
if (m_len + len < SHA384_512_BLOCK_SIZE) {
|
||||||
|
m_len += len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_len = len - rem_len;
|
||||||
|
block_nb = new_len / SHA384_512_BLOCK_SIZE;
|
||||||
|
shifted_message = message + rem_len;
|
||||||
|
transform(m_block, 1);
|
||||||
|
transform(shifted_message, block_nb);
|
||||||
|
rem_len = new_len % SHA384_512_BLOCK_SIZE;
|
||||||
|
memcpy(m_block, &shifted_message[block_nb << 7], rem_len);
|
||||||
|
m_len = rem_len;
|
||||||
|
m_tot_len += (block_nb + 1) << 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHA512::final(unsigned char* digest) {
|
||||||
|
unsigned int block_nb;
|
||||||
|
unsigned int pm_len;
|
||||||
|
unsigned int len_b;
|
||||||
|
int i;
|
||||||
|
block_nb = 1 + ((SHA384_512_BLOCK_SIZE - 17)
|
||||||
|
< (m_len % SHA384_512_BLOCK_SIZE));
|
||||||
|
len_b = (m_tot_len + m_len) << 3;
|
||||||
|
pm_len = block_nb << 7;
|
||||||
|
memset(m_block + m_len, 0, pm_len - m_len);
|
||||||
|
m_block[m_len] = 0x80;
|
||||||
|
SHA2_UNPACK32(len_b, m_block + pm_len - 4);
|
||||||
|
transform(m_block, block_nb);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
SHA2_UNPACK64(m_h[i], &digest[i << 3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sha512(std::string input) {
|
||||||
|
unsigned char digest[SHA512::DIGEST_SIZE];
|
||||||
|
memset(digest, 0, SHA512::DIGEST_SIZE);
|
||||||
|
class SHA512 ctx;
|
||||||
|
ctx.init();
|
||||||
|
ctx.update((unsigned char*)input.c_str(), input.length());
|
||||||
|
ctx.final(digest);
|
||||||
|
|
||||||
|
char buf[2 * SHA512::DIGEST_SIZE + 1];
|
||||||
|
buf[2 * SHA512::DIGEST_SIZE] = 0;
|
||||||
|
for (int i = 0; i < SHA512::DIGEST_SIZE; i++)
|
||||||
|
sprintf(buf + i * 2, "%02x", digest[i]);
|
||||||
|
|
||||||
|
return std::string(buf);
|
||||||
|
}
|
||||||
68
dCommon/SHA512.h
Normal file
68
dCommon/SHA512.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// C++
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class SHA512 {
|
||||||
|
protected:
|
||||||
|
typedef unsigned char uint8;
|
||||||
|
typedef unsigned int uint32;
|
||||||
|
typedef unsigned long long uint64;
|
||||||
|
|
||||||
|
const static uint64 sha512_k[];
|
||||||
|
static const unsigned int SHA384_512_BLOCK_SIZE = (1024 / 8);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void init();
|
||||||
|
void update(const unsigned char* message, unsigned int len);
|
||||||
|
void final(unsigned char* digest);
|
||||||
|
static const unsigned int DIGEST_SIZE = (512 / 8);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void transform(const unsigned char* message, unsigned int block_nb);
|
||||||
|
unsigned int m_tot_len;
|
||||||
|
unsigned int m_len;
|
||||||
|
unsigned char m_block[2 * SHA384_512_BLOCK_SIZE];
|
||||||
|
uint64 m_h[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string sha512(std::string input);
|
||||||
|
|
||||||
|
#define SHA2_SHFR(x, n) (x >> n)
|
||||||
|
#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
|
||||||
|
#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
|
||||||
|
#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z))
|
||||||
|
#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
|
||||||
|
#define SHA512_F1(x) (SHA2_ROTR(x, 28) ^ SHA2_ROTR(x, 34) ^ SHA2_ROTR(x, 39))
|
||||||
|
#define SHA512_F2(x) (SHA2_ROTR(x, 14) ^ SHA2_ROTR(x, 18) ^ SHA2_ROTR(x, 41))
|
||||||
|
#define SHA512_F3(x) (SHA2_ROTR(x, 1) ^ SHA2_ROTR(x, 8) ^ SHA2_SHFR(x, 7))
|
||||||
|
#define SHA512_F4(x) (SHA2_ROTR(x, 19) ^ SHA2_ROTR(x, 61) ^ SHA2_SHFR(x, 6))
|
||||||
|
#define SHA2_UNPACK32(x, str) \
|
||||||
|
{ \
|
||||||
|
*((str) + 3) = (uint8) ((x) ); \
|
||||||
|
*((str) + 2) = (uint8) ((x) >> 8); \
|
||||||
|
*((str) + 1) = (uint8) ((x) >> 16); \
|
||||||
|
*((str) + 0) = (uint8) ((x) >> 24); \
|
||||||
|
}
|
||||||
|
#define SHA2_UNPACK64(x, str) \
|
||||||
|
{ \
|
||||||
|
*((str) + 7) = (uint8) ((x) ); \
|
||||||
|
*((str) + 6) = (uint8) ((x) >> 8); \
|
||||||
|
*((str) + 5) = (uint8) ((x) >> 16); \
|
||||||
|
*((str) + 4) = (uint8) ((x) >> 24); \
|
||||||
|
*((str) + 3) = (uint8) ((x) >> 32); \
|
||||||
|
*((str) + 2) = (uint8) ((x) >> 40); \
|
||||||
|
*((str) + 1) = (uint8) ((x) >> 48); \
|
||||||
|
*((str) + 0) = (uint8) ((x) >> 56); \
|
||||||
|
}
|
||||||
|
#define SHA2_PACK64(str, x) \
|
||||||
|
{ \
|
||||||
|
*(x) = ((uint64) *((str) + 7) ) \
|
||||||
|
| ((uint64) *((str) + 6) << 8) \
|
||||||
|
| ((uint64) *((str) + 5) << 16) \
|
||||||
|
| ((uint64) *((str) + 4) << 24) \
|
||||||
|
| ((uint64) *((str) + 3) << 32) \
|
||||||
|
| ((uint64) *((str) + 2) << 40) \
|
||||||
|
| ((uint64) *((str) + 1) << 48) \
|
||||||
|
| ((uint64) *((str) + 0) << 56); \
|
||||||
|
}
|
||||||
27
dCommon/Type.cpp
Normal file
27
dCommon/Type.cpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include "Type.h"
|
||||||
|
#ifdef __GNUG__
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <memory>
|
||||||
|
#include <cxxabi.h>
|
||||||
|
|
||||||
|
std::string demangle(const char* name) {
|
||||||
|
|
||||||
|
int status = -4; // some arbitrary value to eliminate the compiler warning
|
||||||
|
|
||||||
|
// enable c++11 by passing the flag -std=c++11 to g++
|
||||||
|
std::unique_ptr<char, void(*)(void*)> res{
|
||||||
|
abi::__cxa_demangle(name, NULL, NULL, &status),
|
||||||
|
std::free
|
||||||
|
};
|
||||||
|
|
||||||
|
return (status == 0) ? res.get() : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// does nothing if not g++
|
||||||
|
std::string demangle(const char* name) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
12
dCommon/Type.h
Normal file
12
dCommon/Type.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
std::string demangle(const char* name);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::string type(const T& t) {
|
||||||
|
|
||||||
|
return demangle(typeid(t).name());
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "ZCompression.h"
|
#include "ZCompression.h"
|
||||||
|
|
||||||
#include "zlib-ng.h"
|
#include <zlib.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) {
|
||||||
zng_stream zInfo = { 0 };
|
z_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 = zng_deflateInit(&zInfo, Z_DEFAULT_COMPRESSION); // zlib function
|
nErr = deflateInit(&zInfo, Z_DEFAULT_COMPRESSION); // zlib function
|
||||||
if (nErr == Z_OK) {
|
if (nErr == Z_OK) {
|
||||||
nErr = zng_deflate(&zInfo, Z_FINISH); // zlib function
|
nErr = deflate(&zInfo, Z_FINISH); // zlib function
|
||||||
if (nErr == Z_STREAM_END) {
|
if (nErr == Z_STREAM_END) {
|
||||||
nRet = zInfo.total_out;
|
nRet = zInfo.total_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zng_deflateEnd(&zInfo); // zlib function
|
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
|
||||||
zng_stream zInfo = { 0 };
|
z_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 = zng_inflateInit(&zInfo); // zlib function
|
nErr = inflateInit(&zInfo); // zlib function
|
||||||
if (nErr == Z_OK) {
|
if (nErr == Z_OK) {
|
||||||
nErr = zng_inflate(&zInfo, Z_FINISH); // zlib function
|
nErr = inflate(&zInfo, Z_FINISH); // zlib function
|
||||||
if (nErr == Z_STREAM_END) {
|
if (nErr == Z_STREAM_END) {
|
||||||
nRet = zInfo.total_out;
|
nRet = zInfo.total_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zng_inflateEnd(&zInfo); // zlib function
|
inflateEnd(&zInfo); // zlib function
|
||||||
return(nRet);
|
return(nRet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
#include "AssetManager.h"
|
#include "AssetManager.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.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)) {
|
||||||
@@ -45,10 +47,6 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
|||||||
this->LoadPackIndex();
|
this->LoadPackIndex();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eAssetBundleType::None:
|
|
||||||
case eAssetBundleType::Unpacked: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +77,8 @@ bool AssetManager::HasFile(const char* name) {
|
|||||||
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
||||||
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, (uint8_t*)fixedName.c_str(), fixedName.size());
|
||||||
crc = crc32b(crc, reinterpret_cast<uint8_t*>(const_cast<char*>("\0\0\0\0")), 4);
|
crc = crc32b(crc, (Bytef*)"\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) {
|
||||||
@@ -111,9 +109,9 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
|||||||
#endif
|
#endif
|
||||||
fseek(file, 0, SEEK_END);
|
fseek(file, 0, SEEK_END);
|
||||||
*len = ftell(file);
|
*len = ftell(file);
|
||||||
*data = static_cast<char*>(malloc(*len));
|
*data = (char*)malloc(*len);
|
||||||
fseek(file, 0, SEEK_SET);
|
fseek(file, 0, SEEK_SET);
|
||||||
int32_t readInData = fread(*data, sizeof(uint8_t), *len, file);
|
fread(*data, sizeof(uint8_t), *len, file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -127,8 +125,8 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
|||||||
fixedName = "client\\res\\" + fixedName;
|
fixedName = "client\\res\\" + fixedName;
|
||||||
}
|
}
|
||||||
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, (uint8_t*)fixedName.c_str(), fixedName.size());
|
||||||
crc = crc32b(crc, reinterpret_cast<uint8_t*>(const_cast<char*>("\0\0\0\0")), 4);
|
crc = crc32b(crc, (Bytef*)"\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) {
|
||||||
@@ -150,12 +148,13 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetStream AssetManager::GetFile(const char* name) {
|
AssetMemoryBuffer AssetManager::GetFileAsBuffer(const char* name) {
|
||||||
char* buf; uint32_t len;
|
char* buf;
|
||||||
|
uint32_t len;
|
||||||
|
|
||||||
bool success = this->GetFile(name, &buf, &len);
|
bool success = this->GetFile(name, &buf, &len);
|
||||||
|
|
||||||
return AssetStream(buf, len, success);
|
return AssetMemoryBuffer(buf, len, success);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
||||||
@@ -165,7 +164,7 @@ uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
|||||||
crc = base;
|
crc = base;
|
||||||
for (i = 0; i < l; i++) {
|
for (i = 0; i < l; i++) {
|
||||||
// xor next byte to upper bits of crc
|
// xor next byte to upper bits of crc
|
||||||
crc ^= (static_cast<unsigned int>(message[i]) << 24);
|
crc ^= (((unsigned int)message[i]) << 24);
|
||||||
for (j = 0; j < 8; j++) { // Do eight times.
|
for (j = 0; j < 8; j++) { // Do eight times.
|
||||||
msb = crc >> 31;
|
msb = crc >> 31;
|
||||||
crc <<= 1;
|
crc <<= 1;
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ struct AssetMemoryBuffer : std::streambuf {
|
|||||||
this->setg(base, base, base + n);
|
this->setg(base, base, base + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
~AssetMemoryBuffer() {
|
|
||||||
if (m_Success) free(m_Base);
|
|
||||||
}
|
|
||||||
|
|
||||||
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override {
|
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override {
|
||||||
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
|
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
|
||||||
}
|
}
|
||||||
@@ -44,17 +40,9 @@ struct AssetMemoryBuffer : std::streambuf {
|
|||||||
setg(eback(), eback() + off, egptr());
|
setg(eback(), eback() + off, egptr());
|
||||||
return gptr() - eback();
|
return gptr() - eback();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
struct AssetStream : std::istream {
|
void close() {
|
||||||
AssetStream(char* base, std::ptrdiff_t n, bool success) : std::istream(new AssetMemoryBuffer(base, n, success)) {}
|
delete m_Base;
|
||||||
|
|
||||||
~AssetStream() {
|
|
||||||
delete rdbuf();
|
|
||||||
}
|
|
||||||
|
|
||||||
operator bool() {
|
|
||||||
return reinterpret_cast<AssetMemoryBuffer*>(rdbuf())->m_Success;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,7 +56,7 @@ public:
|
|||||||
|
|
||||||
bool HasFile(const char* name);
|
bool HasFile(const char* name);
|
||||||
bool GetFile(const char* name, char** data, uint32_t* len);
|
bool GetFile(const char* name, char** data, uint32_t* len);
|
||||||
AssetStream GetFile(const char* name);
|
AssetMemoryBuffer GetFileAsBuffer(const char* name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadPackIndex();
|
void LoadPackIndex();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
set(DCOMMON_DCLIENT_SOURCES
|
set(DCOMMON_DCLIENT_SOURCES
|
||||||
"AssetManager.cpp"
|
|
||||||
"PackIndex.cpp"
|
"PackIndex.cpp"
|
||||||
"Pack.cpp"
|
"Pack.cpp"
|
||||||
|
"AssetManager.cpp"
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
#ifndef __CLIENTVERSION_H__
|
|
||||||
#define __CLIENTVERSION_H__
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace ClientVersion {
|
|
||||||
constexpr uint16_t major = 1;
|
|
||||||
constexpr uint16_t current = 10;
|
|
||||||
constexpr uint16_t minor = 64;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !__CLIENTVERSION_H__
|
|
||||||
@@ -76,8 +76,8 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
|||||||
fseek(file, pos, SEEK_SET);
|
fseek(file, pos, SEEK_SET);
|
||||||
|
|
||||||
if (!isCompressed) {
|
if (!isCompressed) {
|
||||||
char* tempData = static_cast<char*>(malloc(pkRecord.m_UncompressedSize));
|
char* tempData = (char*)malloc(pkRecord.m_UncompressedSize);
|
||||||
int32_t readInData = fread(tempData, sizeof(uint8_t), pkRecord.m_UncompressedSize, file);
|
fread(tempData, sizeof(uint8_t), pkRecord.m_UncompressedSize, file);
|
||||||
|
|
||||||
*data = tempData;
|
*data = tempData;
|
||||||
*len = pkRecord.m_UncompressedSize;
|
*len = pkRecord.m_UncompressedSize;
|
||||||
@@ -90,22 +90,22 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
|||||||
|
|
||||||
fseek(file, pos, SEEK_SET);
|
fseek(file, pos, SEEK_SET);
|
||||||
|
|
||||||
char* decompressedData = static_cast<char*>(malloc(pkRecord.m_UncompressedSize));
|
char* decompressedData = (char*)malloc(pkRecord.m_UncompressedSize);
|
||||||
uint32_t currentReadPos = 0;
|
uint32_t currentReadPos = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (currentReadPos >= pkRecord.m_UncompressedSize) break;
|
if (currentReadPos >= pkRecord.m_UncompressedSize) break;
|
||||||
|
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
int32_t readInData = fread(&size, sizeof(uint32_t), 1, file);
|
fread(&size, sizeof(uint32_t), 1, file);
|
||||||
pos += 4; // Move pointer position 4 to the right
|
pos += 4; // Move pointer position 4 to the right
|
||||||
|
|
||||||
char* chunk = static_cast<char*>(malloc(size));
|
char* chunk = (char*)malloc(size);
|
||||||
int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file);
|
fread(chunk, sizeof(int8_t), size, file);
|
||||||
pos += size; // Move pointer position the amount of bytes read to the right
|
pos += size; // Move pointer position the amount of bytes read to the right
|
||||||
|
|
||||||
int32_t err;
|
int32_t err;
|
||||||
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err);
|
currentReadPos += ZCompression::Decompress((uint8_t*)chunk, size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err);
|
||||||
|
|
||||||
free(chunk);
|
free(chunk);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct PackRecord {
|
struct PackRecord {
|
||||||
|
|||||||
@@ -1,17 +1,28 @@
|
|||||||
#include "PackIndex.h"
|
#include "PackIndex.h"
|
||||||
#include "BinaryIO.h"
|
#include "BinaryIO.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "dLogger.h"
|
||||||
|
|
||||||
PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
||||||
m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary);
|
m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary);
|
||||||
|
|
||||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
|
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
|
||||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
|
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
|
||||||
|
|
||||||
m_PackPaths.resize(m_PackPathCount);
|
for (int i = 0; i < m_PackPathCount; i++) {
|
||||||
for (auto& item : m_PackPaths) {
|
uint32_t stringLen = 0;
|
||||||
BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
|
BinaryIO::BinaryRead<uint32_t>(m_FileStream, stringLen);
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
for (int j = 0; j < stringLen; j++) {
|
||||||
|
char inChar;
|
||||||
|
BinaryIO::BinaryRead<char>(m_FileStream, inChar);
|
||||||
|
|
||||||
|
path += inChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PackPaths.push_back(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);
|
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);
|
||||||
@@ -23,7 +34,7 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
|||||||
m_PackFileIndices.push_back(packFileIndex);
|
m_PackFileIndices.push_back(packFileIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size());
|
Game::logger->Log("PackIndex", "Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size());
|
||||||
|
|
||||||
for (auto& item : m_PackPaths) {
|
for (auto& item : m_PackPaths) {
|
||||||
std::replace(item.begin(), item.end(), '\\', '/');
|
std::replace(item.begin(), item.end(), '\\', '/');
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "BinaryPathFinder.h"
|
#include "BinaryPathFinder.h"
|
||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
@@ -11,23 +10,8 @@ dConfig::dConfig(const std::string& filepath) {
|
|||||||
LoadConfig();
|
LoadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path GetConfigDir() {
|
|
||||||
std::filesystem::path config_dir = BinaryPathFinder::GetBinaryDir();
|
|
||||||
if (const char* env_p = std::getenv("DLU_CONFIG_DIR")) {
|
|
||||||
config_dir /= env_p;
|
|
||||||
}
|
|
||||||
return config_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool dConfig::Exists(const std::string& filepath) {
|
|
||||||
std::filesystem::path config_dir = GetConfigDir();
|
|
||||||
return std::filesystem::exists(config_dir / filepath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dConfig::LoadConfig() {
|
void dConfig::LoadConfig() {
|
||||||
std::filesystem::path config_dir = GetConfigDir();
|
std::ifstream in(BinaryPathFinder::GetBinaryDir() / m_ConfigFilePath);
|
||||||
|
|
||||||
std::ifstream in(config_dir / m_ConfigFilePath);
|
|
||||||
if (!in.good()) return;
|
if (!in.good()) return;
|
||||||
|
|
||||||
std::string line{};
|
std::string line{};
|
||||||
@@ -35,7 +19,7 @@ void dConfig::LoadConfig() {
|
|||||||
if (!line.empty() && line.front() != '#') ProcessLine(line);
|
if (!line.empty() && line.front() != '#') ProcessLine(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ifstream sharedConfig(config_dir / "sharedconfig.ini", std::ios::in);
|
std::ifstream sharedConfig(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini", std::ios::in);
|
||||||
if (!sharedConfig.good()) return;
|
if (!sharedConfig.good()) return;
|
||||||
|
|
||||||
line.clear();
|
line.clear();
|
||||||
@@ -50,20 +34,17 @@ void dConfig::ReloadConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string& dConfig::GetValue(std::string key) {
|
const std::string& dConfig::GetValue(std::string key) {
|
||||||
std::string upper_key(key);
|
|
||||||
std::transform(upper_key.begin(), upper_key.end(), upper_key.begin(), ::toupper);
|
|
||||||
if (const char* env_p = std::getenv(upper_key.c_str())) {
|
|
||||||
this->m_ConfigValues[key] = env_p;
|
|
||||||
}
|
|
||||||
return this->m_ConfigValues[key];
|
return this->m_ConfigValues[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
void dConfig::ProcessLine(const std::string& line) {
|
void dConfig::ProcessLine(const std::string& line) {
|
||||||
auto splitLoc = line.find('=');
|
auto splitLine = GeneralUtils::SplitString(line, '=');
|
||||||
auto key = line.substr(0, splitLoc);
|
|
||||||
auto value = line.substr(splitLoc + 1);
|
if (splitLine.size() != 2) return;
|
||||||
|
|
||||||
//Make sure that on Linux, we remove special characters:
|
//Make sure that on Linux, we remove special characters:
|
||||||
|
auto& key = splitLine.at(0);
|
||||||
|
auto& value = splitLine.at(1);
|
||||||
if (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1);
|
if (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1);
|
||||||
|
|
||||||
if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return;
|
if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return;
|
||||||
|
|||||||
@@ -7,11 +7,6 @@ class dConfig {
|
|||||||
public:
|
public:
|
||||||
dConfig(const std::string& filepath);
|
dConfig(const std::string& filepath);
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the specified filepath exists
|
|
||||||
*/
|
|
||||||
static const bool Exists(const std::string& filepath);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the specified key from the config. Returns an empty string if the value is not found.
|
* Gets the specified key from the config. Returns an empty string if the value is not found.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef __EADDFRIENDRESPONSECODE__H__
|
#ifndef __ADDFRIENDRESPONSECODE__H__
|
||||||
#define __EADDFRIENDRESPONSECODE__H__
|
#define __ADDFRIENDRESPONSECODE__H__
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
enum class eAddFriendResponseCode : uint8_t {
|
enum class AddFriendResponseCode : uint8_t {
|
||||||
ACCEPTED = 0,
|
ACCEPTED = 0,
|
||||||
REJECTED,
|
REJECTED,
|
||||||
BUSY,
|
BUSY,
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef __EADDFRIENDRESPONSETYPE__H__
|
#ifndef __ADDFRIENDRESPONSETYPE__H__
|
||||||
#define __EADDFRIENDRESPONSETYPE__H__
|
#define __ADDFRIENDRESPONSETYPE__H__
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
enum class eAddFriendResponseType : uint8_t {
|
enum class AddFriendResponseType : uint8_t {
|
||||||
ACCEPTED = 0,
|
ACCEPTED = 0,
|
||||||
ALREADYFRIEND,
|
ALREADYFRIEND,
|
||||||
INVALIDCHARACTER,
|
INVALIDCHARACTER,
|
||||||
@@ -21,4 +21,4 @@ enum class eAddFriendResponseType : uint8_t {
|
|||||||
FRIENDISFREETRIAL
|
FRIENDISFREETRIAL
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!__EADDFRIENDRESPONSETYPE__H__
|
#endif //!__ADDFRIENDRESPONSETYPE__H__
|
||||||
105
dCommon/dEnums/ItemSetPassiveAbilityID.h
Normal file
105
dCommon/dEnums/ItemSetPassiveAbilityID.h
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
2 Engineer (Rank 1) Item Set
|
||||||
|
3 Engineer (Rank 2) Item Set
|
||||||
|
4 Engineer (Rank 3) Item Set
|
||||||
|
7 Knight (Rank 1) Item Set
|
||||||
|
8 Knight (Rank 2) Item Set
|
||||||
|
9 Knight (Rank 3) Item Set
|
||||||
|
10 Space Ranger (Rank 1) Item Set
|
||||||
|
11 Space Ranger (Rank 2) Item Set
|
||||||
|
12 Space Ranger (Rank 3) Item Set
|
||||||
|
13 Samurai (Rank 1) Item Set
|
||||||
|
14 Samurai (Rank 2) Item Set
|
||||||
|
15 Samurai (Rank 3) Item Set
|
||||||
|
16 Sorcerer (Rank 1) Item Set
|
||||||
|
17 Sorcerer (Rank 2) Item Set
|
||||||
|
18 Sorcerer (Rank 3) Item Set
|
||||||
|
19 Space Marauder (Rank 1) Item Set
|
||||||
|
20 Space Marauder (Rank 2) Item Set
|
||||||
|
21 Space Marauder (Rank 3) Item Set
|
||||||
|
22 Shinobi (Rank 1) Item Set
|
||||||
|
23 Shinobi (Rank 2) Item Set
|
||||||
|
24 Shinobi (Rank 3) Item Set
|
||||||
|
25 Inventor (Rank 1) Item Set
|
||||||
|
26 Inventor (Rank 2) Item Set
|
||||||
|
27 Inventor (Rank 3) Item Set
|
||||||
|
28 Summoner (Rank 1) Item Set
|
||||||
|
29 Summoner (Rank 2) Item Set
|
||||||
|
30 Summoner (Rank 3) Item Set
|
||||||
|
31 Adventurer (Rank 1) Item Set
|
||||||
|
32 Adventurer (Rank 2) Item Set
|
||||||
|
33 Adventurer (Rank 3) Item Set
|
||||||
|
34 Daredevil (Rank 1) Item Set
|
||||||
|
35 Daredevil (Rank 2) Item Set
|
||||||
|
36 Daredevil (Rank 3) Item Set
|
||||||
|
37 Buccaneer (Rank 1) Item Set
|
||||||
|
38 Buccaneer (Rank 2) Item Set
|
||||||
|
39 Buccaneer (Rank 3) Item Set
|
||||||
|
40 Bone Suit Item Set
|
||||||
|
41 Imagination Spinjitzu Item Set
|
||||||
|
42 Bat Lord Item Set
|
||||||
|
43 Mosaic Jester Item Set
|
||||||
|
44 Explorien Bot Item Set
|
||||||
|
45 [Unnamed] Item Set
|
||||||
|
46 [Unnamed] Item Set
|
||||||
|
47 [Unnamed] Item Set
|
||||||
|
48 Earth Spinjitzu Item Set
|
||||||
|
49 [Unnamed] Item Set
|
||||||
|
50 Fire Spinjitzu Item Set
|
||||||
|
51 Ice Spinjitzu Item Set
|
||||||
|
52 Lightning Spinjitzu Item Set
|
||||||
|
*/
|
||||||
|
enum class ItemSetPassiveAbilityID
|
||||||
|
{
|
||||||
|
EngineerRank1 = 2,
|
||||||
|
EngineerRank2 = 3,
|
||||||
|
EngineerRank3 = 4,
|
||||||
|
KnightRank1 = 7,
|
||||||
|
KnightRank2 = 8,
|
||||||
|
KnightRank3 = 9,
|
||||||
|
SpaceRangerRank1 = 10,
|
||||||
|
SpaceRangerRank2 = 11,
|
||||||
|
SpaceRangerRank3 = 12,
|
||||||
|
SamuraiRank1 = 13,
|
||||||
|
SamuraiRank2 = 14,
|
||||||
|
SamuraiRank3 = 15,
|
||||||
|
SorcererRank1 = 16,
|
||||||
|
SorcererRank2 = 17,
|
||||||
|
SorcererRank3 = 18,
|
||||||
|
SpaceMarauderRank1 = 19,
|
||||||
|
SpaceMarauderRank2 = 20,
|
||||||
|
SpaceMarauderRank3 = 21,
|
||||||
|
ShinobiRank1 = 22,
|
||||||
|
ShinobiRank2 = 23,
|
||||||
|
ShinobiRank3 = 24,
|
||||||
|
InventorRank1 = 25,
|
||||||
|
InventorRank2 = 26,
|
||||||
|
InventorRank3 = 27,
|
||||||
|
SummonerRank1 = 28,
|
||||||
|
SummonerRank2 = 29,
|
||||||
|
SummonerRank3 = 30,
|
||||||
|
AdventurerRank1 = 31,
|
||||||
|
AdventurerRank2 = 32,
|
||||||
|
AdventurerRank3 = 33,
|
||||||
|
DaredevilRank1 = 34,
|
||||||
|
DaredevilRank2 = 35,
|
||||||
|
DaredevilRank3 = 36,
|
||||||
|
BuccaneerRank1 = 37,
|
||||||
|
BuccaneerRank2 = 38,
|
||||||
|
BuccaneerRank3 = 39,
|
||||||
|
BoneSuit = 40,
|
||||||
|
ImaginationSpinjitzu = 41,
|
||||||
|
BatLord = 42,
|
||||||
|
MosaicJester = 43,
|
||||||
|
ExplorienBot = 44,
|
||||||
|
Unnamed1 = 45,
|
||||||
|
Unnamed2 = 46,
|
||||||
|
Unnamed3 = 47,
|
||||||
|
EarthSpinjitzu = 48,
|
||||||
|
Unnamed4 = 49,
|
||||||
|
FireSpinjitzu = 50,
|
||||||
|
IceSpinjitzu = 51,
|
||||||
|
LightningSpinjitzu = 52
|
||||||
|
};
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace MessageType {
|
|
||||||
enum class Auth : uint32_t {
|
|
||||||
LOGIN_REQUEST = 0,
|
|
||||||
LOGOUT_REQUEST,
|
|
||||||
CREATE_NEW_ACCOUNT_REQUEST,
|
|
||||||
LEGOINTERFACE_AUTH_RESPONSE,
|
|
||||||
SESSIONKEY_RECEIVED_CONFIRM,
|
|
||||||
RUNTIME_CONFIG
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace MessageType {
|
|
||||||
//! The Internal Chat Packet Identifiers
|
|
||||||
enum class Chat : uint32_t {
|
|
||||||
LOGIN_SESSION_NOTIFY = 0,
|
|
||||||
GENERAL_CHAT_MESSAGE,
|
|
||||||
PRIVATE_CHAT_MESSAGE,
|
|
||||||
USER_CHANNEL_CHAT_MESSAGE,
|
|
||||||
WORLD_DISCONNECT_REQUEST,
|
|
||||||
WORLD_PROXIMITY_RESPONSE,
|
|
||||||
WORLD_PARCEL_RESPONSE,
|
|
||||||
ADD_FRIEND_REQUEST,
|
|
||||||
ADD_FRIEND_RESPONSE,
|
|
||||||
REMOVE_FRIEND,
|
|
||||||
GET_FRIENDS_LIST,
|
|
||||||
ADD_IGNORE,
|
|
||||||
REMOVE_IGNORE,
|
|
||||||
GET_IGNORE_LIST,
|
|
||||||
TEAM_MISSED_INVITE_CHECK,
|
|
||||||
TEAM_INVITE,
|
|
||||||
TEAM_INVITE_RESPONSE,
|
|
||||||
TEAM_KICK,
|
|
||||||
TEAM_LEAVE,
|
|
||||||
TEAM_SET_LOOT,
|
|
||||||
TEAM_SET_LEADER,
|
|
||||||
TEAM_GET_STATUS,
|
|
||||||
GUILD_CREATE,
|
|
||||||
GUILD_INVITE,
|
|
||||||
GUILD_INVITE_RESPONSE,
|
|
||||||
GUILD_LEAVE,
|
|
||||||
GUILD_KICK,
|
|
||||||
GUILD_GET_STATUS,
|
|
||||||
GUILD_GET_ALL,
|
|
||||||
SHOW_ALL,
|
|
||||||
BLUEPRINT_MODERATED,
|
|
||||||
BLUEPRINT_MODEL_READY,
|
|
||||||
PROPERTY_READY_FOR_APPROVAL,
|
|
||||||
PROPERTY_MODERATION_CHANGED,
|
|
||||||
PROPERTY_BUILDMODE_CHANGED,
|
|
||||||
PROPERTY_BUILDMODE_CHANGED_REPORT,
|
|
||||||
MAIL,
|
|
||||||
WORLD_INSTANCE_LOCATION_REQUEST,
|
|
||||||
REPUTATION_UPDATE,
|
|
||||||
SEND_CANNED_TEXT,
|
|
||||||
GMLEVEL_UPDATE,
|
|
||||||
CHARACTER_NAME_CHANGE_REQUEST,
|
|
||||||
CSR_REQUEST,
|
|
||||||
CSR_REPLY,
|
|
||||||
GM_KICK,
|
|
||||||
GM_ANNOUNCE,
|
|
||||||
GM_MUTE,
|
|
||||||
ACTIVITY_UPDATE,
|
|
||||||
WORLD_ROUTE_PACKET,
|
|
||||||
GET_ZONE_POPULATIONS,
|
|
||||||
REQUEST_MINIMUM_CHAT_MODE,
|
|
||||||
REQUEST_MINIMUM_CHAT_MODE_PRIVATE,
|
|
||||||
MATCH_REQUEST,
|
|
||||||
UGCMANIFEST_REPORT_MISSING_FILE,
|
|
||||||
UGCMANIFEST_REPORT_DONE_FILE,
|
|
||||||
UGCMANIFEST_REPORT_DONE_BLUEPRINT,
|
|
||||||
UGCC_REQUEST,
|
|
||||||
WHO,
|
|
||||||
WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE,
|
|
||||||
ACHIEVEMENT_NOTIFY,
|
|
||||||
GM_CLOSE_PRIVATE_CHAT_WINDOW,
|
|
||||||
UNEXPECTED_DISCONNECT,
|
|
||||||
PLAYER_READY,
|
|
||||||
GET_DONATION_TOTAL,
|
|
||||||
UPDATE_DONATION,
|
|
||||||
PRG_CSR_COMMAND,
|
|
||||||
HEARTBEAT_REQUEST_FROM_WORLD,
|
|
||||||
UPDATE_FREE_TRIAL_STATUS,
|
|
||||||
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
|
|
||||||
CREATE_TEAM,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace MessageType {
|
|
||||||
enum class Client : uint32_t {
|
|
||||||
LOGIN_RESPONSE = 0,
|
|
||||||
LOGOUT_RESPONSE,
|
|
||||||
LOAD_STATIC_ZONE,
|
|
||||||
CREATE_OBJECT,
|
|
||||||
CREATE_CHARACTER,
|
|
||||||
CREATE_CHARACTER_EXTENDED,
|
|
||||||
CHARACTER_LIST_RESPONSE,
|
|
||||||
CHARACTER_CREATE_RESPONSE,
|
|
||||||
CHARACTER_RENAME_RESPONSE,
|
|
||||||
CHAT_CONNECT_RESPONSE,
|
|
||||||
AUTH_ACCOUNT_CREATE_RESPONSE,
|
|
||||||
DELETE_CHARACTER_RESPONSE,
|
|
||||||
GAME_MSG,
|
|
||||||
CONNECT_CHAT,
|
|
||||||
TRANSFER_TO_WORLD,
|
|
||||||
IMPENDING_RELOAD_NOTIFY,
|
|
||||||
MAKE_GM_RESPONSE,
|
|
||||||
HTTP_MONITOR_INFO_RESPONSE,
|
|
||||||
SLASH_PUSH_MAP_RESPONSE,
|
|
||||||
SLASH_PULL_MAP_RESPONSE,
|
|
||||||
SLASH_LOCK_MAP_RESPONSE,
|
|
||||||
BLUEPRINT_SAVE_RESPONSE,
|
|
||||||
BLUEPRINT_LUP_SAVE_RESPONSE,
|
|
||||||
BLUEPRINT_LOAD_RESPONSE_ITEMID,
|
|
||||||
BLUEPRINT_GET_ALL_DATA_RESPONSE,
|
|
||||||
MODEL_INSTANTIATE_RESPONSE,
|
|
||||||
DEBUG_OUTPUT,
|
|
||||||
ADD_FRIEND_REQUEST,
|
|
||||||
ADD_FRIEND_RESPONSE,
|
|
||||||
REMOVE_FRIEND_RESPONSE,
|
|
||||||
GET_FRIENDS_LIST_RESPONSE,
|
|
||||||
UPDATE_FRIEND_NOTIFY,
|
|
||||||
ADD_IGNORE_RESPONSE,
|
|
||||||
REMOVE_IGNORE_RESPONSE,
|
|
||||||
GET_IGNORE_LIST_RESPONSE,
|
|
||||||
TEAM_INVITE,
|
|
||||||
TEAM_INVITE_INITIAL_RESPONSE,
|
|
||||||
GUILD_CREATE_RESPONSE,
|
|
||||||
GUILD_GET_STATUS_RESPONSE,
|
|
||||||
GUILD_INVITE,
|
|
||||||
GUILD_INVITE_INITIAL_RESPONSE,
|
|
||||||
GUILD_INVITE_FINAL_RESPONSE,
|
|
||||||
GUILD_INVITE_CONFIRM,
|
|
||||||
GUILD_ADD_PLAYER,
|
|
||||||
GUILD_REMOVE_PLAYER,
|
|
||||||
GUILD_LOGIN_LOGOUT,
|
|
||||||
GUILD_RANK_CHANGE,
|
|
||||||
GUILD_DATA,
|
|
||||||
GUILD_STATUS,
|
|
||||||
MAIL,
|
|
||||||
DB_PROXY_RESULT,
|
|
||||||
SHOW_ALL_RESPONSE,
|
|
||||||
WHO_RESPONSE,
|
|
||||||
SEND_CANNED_TEXT,
|
|
||||||
UPDATE_CHARACTER_NAME,
|
|
||||||
SET_NETWORK_SIMULATOR,
|
|
||||||
INVALID_CHAT_MESSAGE,
|
|
||||||
MINIMUM_CHAT_MODE_RESPONSE,
|
|
||||||
MINIMUM_CHAT_MODE_RESPONSE_PRIVATE,
|
|
||||||
CHAT_MODERATION_STRING,
|
|
||||||
UGC_MANIFEST_RESPONSE,
|
|
||||||
IN_LOGIN_QUEUE,
|
|
||||||
SERVER_STATES,
|
|
||||||
GM_CLOSE_TARGET_CHAT_WINDOW,
|
|
||||||
GENERAL_TEXT_FOR_LOCALIZATION,
|
|
||||||
UPDATE_FREE_TRIAL_STATUS,
|
|
||||||
UGC_DOWNLOAD_FAILED = 120
|
|
||||||
};
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace MessageType {
|
|
||||||
enum class Master : uint32_t {
|
|
||||||
REQUEST_PERSISTENT_ID = 1,
|
|
||||||
REQUEST_PERSISTENT_ID_RESPONSE,
|
|
||||||
REQUEST_ZONE_TRANSFER,
|
|
||||||
REQUEST_ZONE_TRANSFER_RESPONSE,
|
|
||||||
SERVER_INFO,
|
|
||||||
REQUEST_SESSION_KEY,
|
|
||||||
SET_SESSION_KEY,
|
|
||||||
SESSION_KEY_RESPONSE,
|
|
||||||
PLAYER_ADDED,
|
|
||||||
PLAYER_REMOVED,
|
|
||||||
|
|
||||||
CREATE_PRIVATE_ZONE,
|
|
||||||
REQUEST_PRIVATE_ZONE,
|
|
||||||
|
|
||||||
WORLD_READY,
|
|
||||||
PREP_ZONE,
|
|
||||||
|
|
||||||
SHUTDOWN,
|
|
||||||
SHUTDOWN_RESPONSE,
|
|
||||||
SHUTDOWN_IMMEDIATE,
|
|
||||||
|
|
||||||
SHUTDOWN_UNIVERSE,
|
|
||||||
|
|
||||||
AFFIRM_TRANSFER_REQUEST,
|
|
||||||
AFFIRM_TRANSFER_RESPONSE,
|
|
||||||
|
|
||||||
NEW_SESSION_ALERT
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace MessageType {
|
|
||||||
//! The Internal Server Packet Identifiers
|
|
||||||
enum class Server : uint32_t {
|
|
||||||
VERSION_CONFIRM = 0,
|
|
||||||
DISCONNECT_NOTIFY,
|
|
||||||
GENERAL_NOTIFY
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "magic_enum.hpp"
|
|
||||||
|
|
||||||
namespace MessageType {
|
|
||||||
enum class World : uint32_t {
|
|
||||||
VALIDATION = 1, // Session info
|
|
||||||
CHARACTER_LIST_REQUEST,
|
|
||||||
CHARACTER_CREATE_REQUEST,
|
|
||||||
LOGIN_REQUEST, // Character selected
|
|
||||||
GAME_MSG,
|
|
||||||
CHARACTER_DELETE_REQUEST,
|
|
||||||
CHARACTER_RENAME_REQUEST,
|
|
||||||
HAPPY_FLOWER_MODE_NOTIFY,
|
|
||||||
SLASH_RELOAD_MAP, // Reload map cmp
|
|
||||||
SLASH_PUSH_MAP_REQUEST, // Push map req cmd
|
|
||||||
SLASH_PUSH_MAP, // Push map cmd
|
|
||||||
SLASH_PULL_MAP, // Pull map cmd
|
|
||||||
LOCK_MAP_REQUEST,
|
|
||||||
GENERAL_CHAT_MESSAGE, // General chat message
|
|
||||||
HTTP_MONITOR_INFO_REQUEST,
|
|
||||||
SLASH_DEBUG_SCRIPTS, // Debug scripts cmd
|
|
||||||
MODELS_CLEAR,
|
|
||||||
EXHIBIT_INSERT_MODEL,
|
|
||||||
LEVEL_LOAD_COMPLETE, // Character data request
|
|
||||||
TMP_GUILD_CREATE,
|
|
||||||
ROUTE_PACKET, // Social?
|
|
||||||
POSITION_UPDATE,
|
|
||||||
MAIL,
|
|
||||||
WORD_CHECK, // AllowList word check
|
|
||||||
STRING_CHECK, // AllowList string check
|
|
||||||
GET_PLAYERS_IN_ZONE,
|
|
||||||
REQUEST_UGC_MANIFEST_INFO,
|
|
||||||
BLUEPRINT_GET_ALL_DATA_REQUEST,
|
|
||||||
CANCEL_MAP_QUEUE,
|
|
||||||
HANDLE_FUNNESS,
|
|
||||||
FAKE_PRG_CSR_MESSAGE,
|
|
||||||
REQUEST_FREE_TRIAL_REFRESH,
|
|
||||||
GM_SET_FREE_TRIAL_STATUS,
|
|
||||||
UI_HELP_TOP_5 = 91
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct magic_enum::customize::enum_range<MessageType::World> {
|
|
||||||
static constexpr int min = 0;
|
|
||||||
static constexpr int max = 91;
|
|
||||||
};
|
|
||||||
13
dCommon/dEnums/MissionLockState.h
Normal file
13
dCommon/dEnums/MissionLockState.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef MISSIONLOCKSTATE_H
|
||||||
|
#define MISSIONLOCKSTATE_H
|
||||||
|
|
||||||
|
enum class MissionLockState : int
|
||||||
|
{
|
||||||
|
MISSION_LOCK_LOCKED,
|
||||||
|
MISSION_LOCK_NEW,
|
||||||
|
MISSION_LOCK_UNLOCKED,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user