mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-16 20:24:39 -06:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79094b6664 |
@@ -3,8 +3,8 @@ Dockerfile
|
||||
*.md
|
||||
logo.png
|
||||
versions.txt
|
||||
build.sh
|
||||
docker-compose.yml
|
||||
.env
|
||||
docker/__pycache__
|
||||
.env.example
|
||||
build
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# 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
|
||||
NET_VERSION=171022
|
||||
BUILD_VERSION=171022
|
||||
# make sure this is a long random string
|
||||
# grab a "SHA 256-bit Key" from here: https://keygen.io/
|
||||
ACCOUNT_MANAGER_SECRET=
|
||||
@@ -10,5 +12,6 @@ EXTERNAL_IP=localhost
|
||||
# Database values
|
||||
# Be careful with special characters here. It is more safe to use normal characters and/or numbers.
|
||||
MARIADB_USER=darkflame
|
||||
MARIADB_PASSWORD=
|
||||
MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME
|
||||
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME
|
||||
MARIADB_DATABASE=darkflame
|
||||
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 }}
|
||||
10
.github/workflows/build-and-test.yml
vendored
10
.github/workflows/build-and-test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-2022, ubuntu-22.04, macos-13 ]
|
||||
os: [ windows-2022, ubuntu-20.04, macos-11 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -25,11 +25,9 @@ jobs:
|
||||
with:
|
||||
vs-version: '[17,18)'
|
||||
msbuild-architecture: x64
|
||||
- name: Install libssl and switch to XCode 15.2 (Mac Only)
|
||||
if: ${{ matrix.os == 'macos-13' }}
|
||||
run: |
|
||||
brew install openssl@3
|
||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||
- name: Install libssl (Mac Only)
|
||||
if: ${{ matrix.os == 'macos-11' }}
|
||||
run: brew install openssl@3
|
||||
- name: cmake
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -122,4 +122,3 @@ docker/__pycache__
|
||||
docker-compose.override.yml
|
||||
|
||||
!*Test.bin
|
||||
!cmake/*
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -14,6 +14,6 @@
|
||||
path = thirdparty/mariadb-connector-cpp
|
||||
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
|
||||
ignore = dirty
|
||||
[submodule "thirdparty/magic_enum"]
|
||||
path = thirdparty/magic_enum
|
||||
url = https://github.com/Neargye/magic_enum.git
|
||||
[submodule "thirdparty/AccountManager"]
|
||||
path = thirdparty/AccountManager
|
||||
url = https://github.com/DarkflameUniverse/AccountManager
|
||||
|
||||
248
CMakeLists.txt
248
CMakeLists.txt
@@ -2,9 +2,7 @@ cmake_minimum_required(VERSION 3.18)
|
||||
project(Darkflame)
|
||||
include(CTest)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Read variables from file
|
||||
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
|
||||
@@ -16,26 +14,30 @@ string(REPLACE "\n" ";" variables ${variables})
|
||||
foreach(variable ${variables})
|
||||
# If the string contains a #, skip it
|
||||
if(NOT "${variable}" MATCHES "#")
|
||||
|
||||
# Split the variable into name and value
|
||||
string(REPLACE "=" ";" variable ${variable})
|
||||
|
||||
# Check that the length of the variable is 2 (name and value)
|
||||
list(LENGTH variable length)
|
||||
|
||||
if(${length} EQUAL 2)
|
||||
|
||||
list(GET variable 0 variable_name)
|
||||
list(GET variable 1 variable_value)
|
||||
|
||||
# Set the variable
|
||||
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}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# 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
|
||||
message(STATUS "Version: ${PROJECT_VERSION}")
|
||||
@@ -51,22 +53,19 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
|
||||
# Disabled no-register
|
||||
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
|
||||
if(UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC")
|
||||
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)
|
||||
|
||||
if(NOT APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -lstdc++fs")
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC -lstdc++fs")
|
||||
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")
|
||||
endif()
|
||||
|
||||
if(${GGDB})
|
||||
if (__ggdb)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
|
||||
elseif(MSVC)
|
||||
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
|
||||
@@ -98,89 +97,29 @@ make_directory(${CMAKE_BINARY_DIR}/logs)
|
||||
|
||||
# Copy resource files on first build
|
||||
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf")
|
||||
message(STATUS "Checking resource file integrity")
|
||||
|
||||
include(Utils)
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/authconfig.ini "port" "auth_server_port")
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/chatconfig.ini "port" "chat_server_port")
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/masterconfig.ini "port" "master_server_port")
|
||||
|
||||
foreach(resource_file ${RESOURCE_FILES})
|
||||
set(file_size 0)
|
||||
|
||||
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
|
||||
endif()
|
||||
|
||||
if(${file_size} EQUAL 0)
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
|
||||
COPYONLY
|
||||
)
|
||||
message(STATUS "Moved " ${resource_file} " to project binary directory")
|
||||
elseif(resource_file MATCHES ".ini")
|
||||
message(STATUS "Checking " ${resource_file} " for missing config options")
|
||||
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents)
|
||||
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})
|
||||
message("Moved ${resource_file} to project binary directory")
|
||||
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 ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
|
||||
endif()
|
||||
|
||||
set(line_to_add "")
|
||||
else()
|
||||
set(line_to_add ${line_to_add} ${line})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Resource file integrity check complete")
|
||||
|
||||
# if navmeshes directory does not exist, create it
|
||||
if(NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes)
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/navmeshes)
|
||||
endif()
|
||||
|
||||
# Copy navmesh data on first build and extract it
|
||||
configure_file(${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip COPYONLY)
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes/)
|
||||
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(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip)
|
||||
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
|
||||
endif()
|
||||
|
||||
# Copy vanity files on first build
|
||||
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
|
||||
|
||||
foreach(file ${VANITY_FILES})
|
||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
|
||||
endforeach()
|
||||
@@ -188,18 +127,26 @@ endforeach()
|
||||
# Move our migrations for MasterServer to run
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/)
|
||||
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
|
||||
|
||||
foreach(file ${SQL_FILES})
|
||||
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()
|
||||
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
|
||||
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)
|
||||
|
||||
foreach(file ${SQL_FILES})
|
||||
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()
|
||||
|
||||
# Create our list of include directories
|
||||
@@ -207,9 +154,7 @@ set(INCLUDED_DIRECTORIES
|
||||
"dCommon"
|
||||
"dCommon/dClient"
|
||||
"dCommon/dEnums"
|
||||
|
||||
"dChatFilter"
|
||||
|
||||
"dGame"
|
||||
"dGame/dBehaviors"
|
||||
"dGame/dComponents"
|
||||
@@ -220,32 +165,89 @@ set(INCLUDED_DIRECTORIES
|
||||
"dGame/dPropertyBehaviors"
|
||||
"dGame/dPropertyBehaviors/ControlBehaviorMessages"
|
||||
"dGame/dUtilities"
|
||||
|
||||
"dPhysics"
|
||||
|
||||
"dNavigation"
|
||||
"dNavigation/dTerrain"
|
||||
|
||||
"dZoneManager"
|
||||
|
||||
"dDatabase"
|
||||
"dDatabase/CDClientDatabase"
|
||||
"dDatabase/CDClientDatabase/CDClientTables"
|
||||
"dDatabase/GameDatabase"
|
||||
"dDatabase/GameDatabase/ITables"
|
||||
"dDatabase/GameDatabase/MySQL"
|
||||
"dDatabase/GameDatabase/MySQL/Tables"
|
||||
|
||||
"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/magic_enum/include/magic_enum"
|
||||
"thirdparty/raknet/Source"
|
||||
"thirdparty/tinyxml2"
|
||||
"thirdparty/recastnavigation"
|
||||
"thirdparty/SQLite"
|
||||
"thirdparty/cpplinq"
|
||||
"thirdparty/cpp-httplib"
|
||||
"thirdparty/MD5"
|
||||
|
||||
"tests"
|
||||
"tests/dCommonTests"
|
||||
@@ -258,32 +260,33 @@ if(APPLE)
|
||||
include_directories("/usr/local/include/")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include")
|
||||
elseif (UNIX)
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt")
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt")
|
||||
endif()
|
||||
|
||||
# Add binary directory as an include directory
|
||||
include_directories(${PROJECT_BINARY_DIR})
|
||||
|
||||
# Actually include the directories from our list
|
||||
foreach (dir ${INCLUDED_DIRECTORIES})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/${dir})
|
||||
endforeach()
|
||||
|
||||
if(NOT WIN32)
|
||||
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt")
|
||||
endif()
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include")
|
||||
|
||||
# Add linking directories:
|
||||
link_directories(${PROJECT_BINARY_DIR})
|
||||
|
||||
# Load all of our third party directories
|
||||
add_subdirectory(thirdparty)
|
||||
if (UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
endif()
|
||||
|
||||
# Glob together all headers that need to be precompiled
|
||||
file(
|
||||
GLOB HEADERS_DDATABASE
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
|
||||
)
|
||||
|
||||
@@ -318,16 +321,15 @@ add_subdirectory(dGame)
|
||||
add_subdirectory(dZoneManager)
|
||||
add_subdirectory(dNavigation)
|
||||
add_subdirectory(dPhysics)
|
||||
add_subdirectory(dServer)
|
||||
|
||||
# Create a list of common libraries shared between all binaries
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp" "magic_enum" "MD5")
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp")
|
||||
|
||||
# Add platform specific common libraries
|
||||
if (UNIX)
|
||||
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")
|
||||
endif()
|
||||
endif()
|
||||
@@ -338,6 +340,12 @@ add_subdirectory(dAuthServer)
|
||||
add_subdirectory(dChatServer)
|
||||
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(
|
||||
dZoneManager PRIVATE
|
||||
${HEADERS_DZONEMANAGER}
|
||||
@@ -359,6 +367,6 @@ target_precompile_headers(
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
|
||||
)
|
||||
|
||||
if(${ENABLE_TESTING})
|
||||
if (${__enable_testing__} MATCHES "1")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
"generator": "Unix Makefiles"
|
||||
},
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"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-13",
|
||||
"name": "ci-macos-11",
|
||||
"displayName": "CI configure step for MacOS",
|
||||
"description": "Same as default, Used in GitHub actions workflow",
|
||||
"inherits": "default"
|
||||
@@ -67,15 +67,15 @@
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"configurePreset": "ci-ubuntu-22.04",
|
||||
"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-13",
|
||||
"configurePreset": "ci-macos-13",
|
||||
"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
|
||||
@@ -83,8 +83,8 @@
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"configurePreset": "ci-ubuntu-22.04",
|
||||
"name": "ci-ubuntu-20.04",
|
||||
"configurePreset": "ci-ubuntu-20.04",
|
||||
"displayName": "CI Tests on Linux",
|
||||
"description": "Runs all tests on a linux configuration",
|
||||
"execution": {
|
||||
@@ -95,8 +95,8 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"configurePreset": "ci-macos-13",
|
||||
"name": "ci-macos-11",
|
||||
"configurePreset": "ci-macos-11",
|
||||
"displayName": "CI Tests on MacOS",
|
||||
"description": "Runs all tests on a Mac configuration",
|
||||
"execution": {
|
||||
|
||||
@@ -1,32 +1,24 @@
|
||||
PROJECT_VERSION_MAJOR=1
|
||||
PROJECT_VERSION_MINOR=1
|
||||
PROJECT_VERSION_PATCH=1
|
||||
|
||||
PROJECT_VERSION_MINOR=0
|
||||
PROJECT_VERSION_PATCH=4
|
||||
# LICENSE
|
||||
LICENSE=AGPL-3.0
|
||||
# The network version.
|
||||
# 171023 - Darkflame Universe client
|
||||
# 171022 - Unmodded client
|
||||
NET_VERSION=171022
|
||||
# Debugging
|
||||
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
||||
DYNAMIC=1
|
||||
|
||||
# Set GGDB to 1 to enable the -ggdb flag for the linker, including more debug info.
|
||||
# Do note, changing this will re-build the whole server
|
||||
GGDB=0
|
||||
|
||||
# Set INCLUDE_BACKTRACE to 1 to includes the backtrace library for better crashlogs.
|
||||
# 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 __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
||||
__dynamic=1
|
||||
# Set __ggdb to 1 to enable the -ggdb flag for the linker, including more debug info.
|
||||
# __ggdb=1
|
||||
# Set __include_backtrace__ to 1 to includes the backtrace library for better crashlogs.
|
||||
# __include_backtrace__=1
|
||||
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
|
||||
# __compile_backtrace__=1
|
||||
# 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.
|
||||
ENABLE_TESTING=1
|
||||
|
||||
__enable_testing__=1
|
||||
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
|
||||
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.
|
||||
|
||||

|
||||
51
Dockerfile
51
Dockerfile
@@ -1,51 +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/src/mariadb_connector_cpp-build/libmariadbcpp.so /usr/local/lib/
|
||||
COPY --from=build /app/build/mariadbcpp/src/mariadb_connector_cpp-build/libmariadb/libmariadb/libmariadb.so.3 /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" ]
|
||||
105
README.md
105
README.md
@@ -23,9 +23,6 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s
|
||||
### 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.
|
||||
|
||||
## 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
|
||||
* [Clone this repository](#clone-the-repository)
|
||||
* [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)
|
||||
* [Running the server](#running-the-server)
|
||||
* [User Guide](#user-guide)
|
||||
* [Docker](#docker)
|
||||
|
||||
## Clone the repository
|
||||
If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win)
|
||||
@@ -183,7 +179,7 @@ If you would like to build the server faster, append `-j<number>` where number i
|
||||
### Notes
|
||||
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 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
|
||||
This server has a few steps that need to be taken to configure the server for your use case.
|
||||
@@ -228,44 +224,6 @@ sudo setcap 'cap_net_bind_service=+ep' AuthServer
|
||||
```
|
||||
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
|
||||
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!
|
||||
|
||||
@@ -327,7 +285,6 @@ Below are known good SHA256 checksums of the client:
|
||||
* `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.
|
||||
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 clients identify themselves using a higher version number than the regular live clients out there.
|
||||
@@ -348,66 +305,6 @@ certutil -hashfile <file> SHA1
|
||||
Known good *SHA1* checksum of the Darkflame Universe client:
|
||||
- `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
|
||||
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)
|
||||
|
||||
@@ -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 +1,13 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include <csignal>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
//DLU Includes:
|
||||
#include "dCommonVars.h"
|
||||
#include "dServer.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
#include "Database.h"
|
||||
#include "dConfig.h"
|
||||
#include "Diagnostics.h"
|
||||
@@ -16,7 +15,7 @@
|
||||
|
||||
//RakNet includes:
|
||||
#include "RakNetDefines.h"
|
||||
#include "MessageIdentifiers.h"
|
||||
#include <MessageIdentifiers.h>
|
||||
|
||||
//Auth includes:
|
||||
#include "AuthPackets.h"
|
||||
@@ -25,17 +24,14 @@
|
||||
#include "eAuthMessageType.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Server.h"
|
||||
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
dLogger* logger = nullptr;
|
||||
dServer* server = nullptr;
|
||||
dConfig* config = nullptr;
|
||||
Game::signal_t lastSignal = 0;
|
||||
std::mt19937 randomEngine;
|
||||
bool shouldShutdown = false;
|
||||
}
|
||||
|
||||
dLogger* SetupLogger();
|
||||
void HandlePacket(Packet* packet);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -45,23 +41,29 @@ int main(int argc, char** argv) {
|
||||
Diagnostics::SetProcessFileName(argv[0]);
|
||||
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:
|
||||
Server::SetupLogger("AuthServer");
|
||||
Game::logger = SetupLogger();
|
||||
if (!Game::logger) return EXIT_FAILURE;
|
||||
|
||||
LOG("Starting Auth server...");
|
||||
LOG("Version: %s", PROJECT_VERSION);
|
||||
LOG("Compiled on: %s", __TIMESTAMP__);
|
||||
//Read our config:
|
||||
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string());
|
||||
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 {
|
||||
Database::Connect();
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} 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");
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
@@ -71,26 +73,23 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1500;
|
||||
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
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.
|
||||
std::string ourIP = "localhost";
|
||||
const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
|
||||
uint32_t maxClients = 50;
|
||||
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.
|
||||
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);
|
||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
@@ -101,18 +100,13 @@ int main(int argc, char** argv) {
|
||||
uint32_t framesSinceMasterDisconnect = 0;
|
||||
uint32_t framesSinceLastSQLPing = 0;
|
||||
|
||||
AuthPackets::LoadClaimCodes();
|
||||
|
||||
Game::logger->Flush(); // once immediately before main loop
|
||||
while (!Game::ShouldShutdown()) {
|
||||
while (!Game::shouldShutdown) {
|
||||
//Check if we're still connected to master:
|
||||
if (!Game::server->GetIsConnectedToMaster()) {
|
||||
framesSinceMasterDisconnect++;
|
||||
|
||||
if (framesSinceMasterDisconnect >= authFramerate) {
|
||||
LOG("No connection to master!");
|
||||
if (framesSinceMasterDisconnect >= authFramerate)
|
||||
break; //Exit our loop, shut down.
|
||||
}
|
||||
} else framesSinceMasterDisconnect = 0;
|
||||
|
||||
//In world we'd update our other systems here.
|
||||
@@ -137,12 +131,16 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
|
||||
@@ -151,7 +149,6 @@ int main(int argc, char** argv) {
|
||||
std::this_thread::sleep_until(t);
|
||||
}
|
||||
|
||||
LOG("Exited Main Loop! (signal %d)", Game::lastSignal);
|
||||
//Delete our objects here:
|
||||
Database::Destroy("AuthServer");
|
||||
delete Game::server;
|
||||
@@ -161,6 +158,18 @@ int main(int argc, char** argv) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
dLogger* SetupLogger() {
|
||||
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->length < 4) return;
|
||||
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
add_executable(AuthServer "AuthServer.cpp")
|
||||
|
||||
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}\"")
|
||||
target_link_libraries(AuthServer ${COMMON_LIBRARIES})
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <regex>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
#include "dConfig.h"
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
@@ -32,11 +32,15 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
|
||||
}
|
||||
|
||||
//Read player names that are ok as well:
|
||||
auto approvedNames = Database::Get()->GetApprovedCharacterNames();
|
||||
for (auto& name : approvedNames) {
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower); //Transform to lowercase
|
||||
m_ApprovedWords.push_back(CalculateHash(name));
|
||||
auto stmt = Database::CreatePreppedStmt("select name from charinfo;");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
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() {
|
||||
@@ -140,7 +144,7 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
|
||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||
}
|
||||
|
||||
position += originalSegment.length() + 1;
|
||||
position += segment.length() + 1;
|
||||
}
|
||||
|
||||
return listOfBadSegments;
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
set(DCHATSERVER_SOURCES
|
||||
"ChatIgnoreList.cpp"
|
||||
"ChatPacketHandler.cpp"
|
||||
"PlayerContainer.cpp"
|
||||
)
|
||||
|
||||
add_executable(ChatServer "ChatServer.cpp")
|
||||
add_library(dChatServer ${DCHATSERVER_SOURCES})
|
||||
target_include_directories(dChatServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer)
|
||||
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
||||
|
||||
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 "eChatInternalMessageType.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_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
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,56 +4,16 @@
|
||||
#include "BitStream.h"
|
||||
|
||||
struct PlayerData;
|
||||
|
||||
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 {
|
||||
void HandleFriendlistRequest(Packet* packet);
|
||||
void HandleFriendRequest(Packet* packet);
|
||||
void HandleFriendResponse(Packet* packet);
|
||||
void HandleRemoveFriend(Packet* packet);
|
||||
void HandleGMLevelUpdate(Packet* packet);
|
||||
|
||||
void HandleChatMessage(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 HandleTeamInviteResponse(Packet* packet);
|
||||
@@ -63,18 +23,18 @@ namespace ChatPacketHandler {
|
||||
void HandleTeamLootOption(Packet* packet);
|
||||
void HandleTeamStatusRequest(Packet* packet);
|
||||
|
||||
void SendTeamInvite(const PlayerData& receiver, const 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 SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
|
||||
void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID);
|
||||
void SendTeamAddPlayer(const 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 SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
|
||||
void SendTeamInvite(PlayerData* receiver, PlayerData* sender);
|
||||
void SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
|
||||
void SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
|
||||
void SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID);
|
||||
void SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
|
||||
void SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
|
||||
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.
|
||||
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 SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
|
||||
void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful);
|
||||
void SendFriendRequest(PlayerData* receiver, PlayerData* sender);
|
||||
void SendFriendResponse(PlayerData* receiver, PlayerData* sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
|
||||
void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//DLU Includes:
|
||||
#include "dCommonVars.h"
|
||||
#include "dServer.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
#include "Database.h"
|
||||
#include "dConfig.h"
|
||||
#include "dChatFilter.h"
|
||||
@@ -19,29 +19,28 @@
|
||||
#include "eChatMessageType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "eWorldMessageType.h"
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "StringifiedEnum.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Server.h"
|
||||
|
||||
//RakNet includes:
|
||||
#include "RakNetDefines.h"
|
||||
#include "MessageIdentifiers.h"
|
||||
#include <MessageIdentifiers.h>
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
dLogger* logger = nullptr;
|
||||
dServer* server = nullptr;
|
||||
dConfig* config = nullptr;
|
||||
dChatFilter* chatFilter = nullptr;
|
||||
AssetManager* assetManager = nullptr;
|
||||
Game::signal_t lastSignal = 0;
|
||||
std::mt19937 randomEngine;
|
||||
PlayerContainer playerContainer;
|
||||
bool shouldShutdown = false;
|
||||
}
|
||||
|
||||
|
||||
dLogger* SetupLogger();
|
||||
void HandlePacket(Packet* packet);
|
||||
|
||||
PlayerContainer playerContainer;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
constexpr uint32_t chatFramerate = mediumFramerate;
|
||||
constexpr uint32_t chatFrameDelta = mediumFrameDelta;
|
||||
@@ -49,20 +48,18 @@ int main(int argc, char** argv) {
|
||||
Diagnostics::SetProcessFileName(argv[0]);
|
||||
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:
|
||||
Server::SetupLogger("ChatServer");
|
||||
Game::logger = SetupLogger();
|
||||
if (!Game::logger) return EXIT_FAILURE;
|
||||
|
||||
//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...");
|
||||
LOG("Version: %s", PROJECT_VERSION);
|
||||
LOG("Compiled on: %s", __TIMESTAMP__);
|
||||
Game::logger->Log("ChatServer", "Starting Chat server...");
|
||||
Game::logger->Log("ChatServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||
Game::logger->Log("ChatServer", "Compiled on: %s", __TIMESTAMP__);
|
||||
|
||||
try {
|
||||
std::string clientPathStr = Game::config->GetValue("client_location");
|
||||
@@ -74,16 +71,21 @@ int main(int argc, char** argv) {
|
||||
|
||||
Game::assetManager = new AssetManager(clientPath);
|
||||
} 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;
|
||||
}
|
||||
|
||||
//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 {
|
||||
Database::Connect();
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} 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");
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
@@ -93,26 +95,25 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1000;
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
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.
|
||||
std::string ourIP = "localhost";
|
||||
const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
|
||||
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("chat_server_port")).value_or(1501);
|
||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
||||
uint32_t maxClients = 50;
|
||||
uint32_t ourPort = 1501;
|
||||
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());
|
||||
|
||||
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", dontGenerateDCF);
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
Game::playerContainer.Initialize();
|
||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
@@ -123,8 +124,7 @@ int main(int argc, char** argv) {
|
||||
uint32_t framesSinceMasterDisconnect = 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:
|
||||
if (!Game::server->GetIsConnectedToMaster()) {
|
||||
framesSinceMasterDisconnect++;
|
||||
@@ -155,13 +155,16 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
|
||||
@@ -179,13 +182,25 @@ int main(int argc, char** argv) {
|
||||
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) {
|
||||
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.");
|
||||
}
|
||||
|
||||
if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
||||
LOG("A server is connecting, awaiting user list.");
|
||||
Game::logger->Log("ChatServer", "A server is connecting, awaiting user list.");
|
||||
}
|
||||
|
||||
if (packet->length < 4) return; // Nothing left to process. Need 4 bytes to continue.
|
||||
@@ -193,19 +208,19 @@ void HandlePacket(Packet* packet) {
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) {
|
||||
switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
|
||||
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
|
||||
Game::playerContainer.InsertPlayer(packet);
|
||||
playerContainer.InsertPlayer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
|
||||
Game::playerContainer.RemovePlayer(packet);
|
||||
playerContainer.RemovePlayer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::MUTE_UPDATE:
|
||||
Game::playerContainer.MuteUpdate(packet);
|
||||
playerContainer.MuteUpdate(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::CREATE_TEAM:
|
||||
Game::playerContainer.CreateTeamServer(packet);
|
||||
playerContainer.CreateTeamServer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::ANNOUNCEMENT: {
|
||||
@@ -216,27 +231,18 @@ void HandlePacket(Packet* packet) {
|
||||
}
|
||||
|
||||
default:
|
||||
LOG("Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
|
||||
Game::logger->Log("ChatServer", "Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
|
||||
eChatMessageType chat_message_type = static_cast<eChatMessageType>(packet->data[3]);
|
||||
switch (chat_message_type) {
|
||||
switch (static_cast<eChatMessageType>(packet->data[3])) {
|
||||
case eChatMessageType::GET_FRIENDS_LIST:
|
||||
ChatPacketHandler::HandleFriendlistRequest(packet);
|
||||
break;
|
||||
|
||||
case eChatMessageType::GET_IGNORE_LIST:
|
||||
ChatIgnoreList::GetIgnoreList(packet);
|
||||
break;
|
||||
|
||||
case eChatMessageType::ADD_IGNORE:
|
||||
ChatIgnoreList::AddIgnore(packet);
|
||||
break;
|
||||
|
||||
case eChatMessageType::REMOVE_IGNORE:
|
||||
ChatIgnoreList::RemoveIgnore(packet);
|
||||
Game::logger->Log("ChatServer", "Asked for ignore list, but is unimplemented right now.");
|
||||
break;
|
||||
|
||||
case eChatMessageType::TEAM_GET_STATUS:
|
||||
@@ -292,73 +298,21 @@ void HandlePacket(Packet* packet) {
|
||||
case eChatMessageType::TEAM_SET_LOOT:
|
||||
ChatPacketHandler::HandleTeamLootOption(packet);
|
||||
break;
|
||||
case eChatMessageType::GMLEVEL_UPDATE:
|
||||
ChatPacketHandler::HandleGMLevelUpdate(packet);
|
||||
break;
|
||||
case eChatMessageType::LOGIN_SESSION_NOTIFY:
|
||||
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
|
||||
case eChatMessageType::WORLD_DISCONNECT_REQUEST:
|
||||
case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
|
||||
case eChatMessageType::WORLD_PARCEL_RESPONSE:
|
||||
case eChatMessageType::TEAM_MISSED_INVITE_CHECK:
|
||||
case eChatMessageType::GUILD_CREATE:
|
||||
case eChatMessageType::GUILD_INVITE:
|
||||
case eChatMessageType::GUILD_INVITE_RESPONSE:
|
||||
case eChatMessageType::GUILD_LEAVE:
|
||||
case eChatMessageType::GUILD_KICK:
|
||||
case eChatMessageType::GUILD_GET_STATUS:
|
||||
case eChatMessageType::GUILD_GET_ALL:
|
||||
case eChatMessageType::SHOW_ALL:
|
||||
case eChatMessageType::BLUEPRINT_MODERATED:
|
||||
case eChatMessageType::BLUEPRINT_MODEL_READY:
|
||||
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL:
|
||||
case eChatMessageType::PROPERTY_MODERATION_CHANGED:
|
||||
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED:
|
||||
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED_REPORT:
|
||||
case eChatMessageType::MAIL:
|
||||
case eChatMessageType::WORLD_INSTANCE_LOCATION_REQUEST:
|
||||
case eChatMessageType::REPUTATION_UPDATE:
|
||||
case eChatMessageType::SEND_CANNED_TEXT:
|
||||
case eChatMessageType::CHARACTER_NAME_CHANGE_REQUEST:
|
||||
case eChatMessageType::CSR_REQUEST:
|
||||
case eChatMessageType::CSR_REPLY:
|
||||
case eChatMessageType::GM_KICK:
|
||||
case eChatMessageType::GM_ANNOUNCE:
|
||||
case eChatMessageType::WORLD_ROUTE_PACKET:
|
||||
case eChatMessageType::GET_ZONE_POPULATIONS:
|
||||
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE:
|
||||
case eChatMessageType::MATCH_REQUEST:
|
||||
case eChatMessageType::UGCMANIFEST_REPORT_MISSING_FILE:
|
||||
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE:
|
||||
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
|
||||
case eChatMessageType::UGCC_REQUEST:
|
||||
case eChatMessageType::WHO:
|
||||
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
|
||||
case eChatMessageType::ACHIEVEMENT_NOTIFY:
|
||||
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
||||
case eChatMessageType::UNEXPECTED_DISCONNECT:
|
||||
case eChatMessageType::PLAYER_READY:
|
||||
case eChatMessageType::GET_DONATION_TOTAL:
|
||||
case eChatMessageType::UPDATE_DONATION:
|
||||
case eChatMessageType::PRG_CSR_COMMAND:
|
||||
case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD:
|
||||
case eChatMessageType::UPDATE_FREE_TRIAL_STATUS:
|
||||
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chat_message_type).data(), chat_message_type);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG("Unknown CHAT Message id: %i", chat_message_type);
|
||||
Game::logger->Log("ChatServer", "Unknown CHAT id: %i", int(packet->data[3]));
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::WORLD) {
|
||||
switch (static_cast<eWorldMessageType>(packet->data[3])) {
|
||||
case eWorldMessageType::ROUTE_PACKET: {
|
||||
LOG("Routing packet from world");
|
||||
Game::logger->Log("ChatServer", "Routing packet from world");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG("Unknown World id: %i", int(packet->data[3]));
|
||||
Game::logger->Log("ChatServer", "Unknown World id: %i", int(packet->data[3]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,56 +3,51 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
#include "ChatPacketHandler.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "Database.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
void PlayerContainer::Initialize() {
|
||||
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);
|
||||
PlayerContainer::PlayerContainer() {
|
||||
}
|
||||
|
||||
TeamData::TeamData() {
|
||||
lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
|
||||
PlayerContainer::~PlayerContainer() {
|
||||
mPlayers.clear();
|
||||
}
|
||||
|
||||
void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerId;
|
||||
if (!inStream.Read(playerId)) {
|
||||
LOG("Failed to read player ID");
|
||||
return;
|
||||
}
|
||||
|
||||
auto& data = m_Players[playerId];
|
||||
data.playerID = playerId;
|
||||
PlayerData* data = new PlayerData();
|
||||
inStream.Read(data->playerID);
|
||||
|
||||
uint32_t len;
|
||||
inStream.Read<uint32_t>(len);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
char character; inStream.Read<char>(character);
|
||||
data.playerName += character;
|
||||
data->playerName += character;
|
||||
}
|
||||
|
||||
inStream.Read(data.zoneID);
|
||||
inStream.Read(data.muteExpire);
|
||||
inStream.Read(data.gmLevel);
|
||||
data.sysAddr = packet->systemAddress;
|
||||
inStream.Read(data->zoneID);
|
||||
inStream.Read(data->muteExpire);
|
||||
data->sysAddr = packet->systemAddress;
|
||||
|
||||
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||
mNames[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
|
||||
|
||||
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
|
||||
mPlayers.insert(std::make_pair(data->playerID, data));
|
||||
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
|
||||
|
||||
Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID());
|
||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||
|
||||
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) {
|
||||
@@ -61,36 +56,42 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
inStream.Read(playerID);
|
||||
|
||||
//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) {
|
||||
LOG("Failed to find user: %llu", playerID);
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& fr : player.friends) {
|
||||
const auto& fd = this->GetPlayerData(fr.friendID);
|
||||
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend);
|
||||
for (auto& fr : player->friends) {
|
||||
auto fd = this->GetPlayerData(fr.friendID);
|
||||
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend);
|
||||
}
|
||||
|
||||
auto* team = GetTeam(playerID);
|
||||
|
||||
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) {
|
||||
const auto& otherMember = GetPlayerData(memberId);
|
||||
auto* otherMember = GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
if (otherMember == nullptr) continue;
|
||||
|
||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Removed user: %llu", playerID);
|
||||
m_Players.erase(playerID);
|
||||
Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID);
|
||||
mPlayers.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) {
|
||||
@@ -100,15 +101,15 @@ void PlayerContainer::MuteUpdate(Packet* packet) {
|
||||
time_t expire = 0;
|
||||
inStream.Read(expire);
|
||||
|
||||
auto& player = this->GetPlayerDataMutable(playerID);
|
||||
auto* player = this->GetPlayerData(playerID);
|
||||
|
||||
if (!player) {
|
||||
LOG("Failed to find user: %llu", playerID);
|
||||
if (player == nullptr) {
|
||||
Game::logger->Log("PlayerContainer", "Failed to find user: %llu", playerID);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
player.muteExpire = expire;
|
||||
player->muteExpire = expire;
|
||||
|
||||
BroadcastMuteUpdate(playerID, expire);
|
||||
}
|
||||
@@ -145,7 +146,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
|
||||
|
||||
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
|
||||
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
|
||||
|
||||
bitStream.Write(player);
|
||||
bitStream.Write(time);
|
||||
@@ -184,7 +185,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
||||
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
|
||||
auto* team = new TeamData();
|
||||
|
||||
team->teamID = ++m_TeamIDCounter;
|
||||
team->teamID = ++mTeamIDCounter;
|
||||
team->leaderID = leader;
|
||||
team->local = local;
|
||||
|
||||
@@ -206,32 +207,24 @@ TeamData* PlayerContainer::GetTeam(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);
|
||||
|
||||
if (index != team->memberIDs.end()) return;
|
||||
|
||||
team->memberIDs.push_back(playerID);
|
||||
|
||||
const auto& leader = GetPlayerData(team->leaderID);
|
||||
const auto& member = GetPlayerData(playerID);
|
||||
auto* leader = GetPlayerData(team->leaderID);
|
||||
auto* member = GetPlayerData(playerID);
|
||||
|
||||
if (!leader || !member) return;
|
||||
if (leader == nullptr || member == nullptr) return;
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
|
||||
const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName);
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->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) {
|
||||
ChatPacketHandler::SendTeamSetLeader(member, leader.playerID);
|
||||
ChatPacketHandler::SendTeamSetLeader(member, leader->playerID);
|
||||
} else {
|
||||
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
|
||||
}
|
||||
@@ -239,16 +232,16 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
const auto& otherMember = GetPlayerData(memberId);
|
||||
auto* otherMember = GetPlayerData(memberId);
|
||||
|
||||
if (otherMember == member) continue;
|
||||
|
||||
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) {
|
||||
ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID);
|
||||
if (otherMember != nullptr) {
|
||||
ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member->playerID, memberName, member->zoneID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,9 +251,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -271,9 +264,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba
|
||||
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);
|
||||
}
|
||||
@@ -295,9 +288,9 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
|
||||
team->leaderID = newLeader;
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -309,14 +302,14 @@ void PlayerContainer::DisbandTeam(TeamData* team) {
|
||||
if (index == mTeams.end()) return;
|
||||
|
||||
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::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);
|
||||
@@ -331,19 +324,19 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
||||
|
||||
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) {
|
||||
const auto& otherMember = GetPlayerData(memberId);
|
||||
auto* otherMember = GetPlayerData(memberId);
|
||||
|
||||
if (!otherMember) continue;
|
||||
if (otherMember == nullptr) continue;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,14 +345,14 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
||||
|
||||
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
|
||||
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
|
||||
|
||||
bitStream.Write(team->teamID);
|
||||
bitStream.Write(deleteTeam);
|
||||
|
||||
if (!deleteTeam) {
|
||||
bitStream.Write(team->lootFlag);
|
||||
bitStream.Write<char>(team->memberIDs.size());
|
||||
bitStream.Write(static_cast<char>(team->memberIDs.size()));
|
||||
for (const auto memberID : team->memberIDs) {
|
||||
bitStream.Write(memberID);
|
||||
}
|
||||
@@ -369,42 +362,23 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
}
|
||||
|
||||
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 toReturn = LWOOBJID_EMPTY;
|
||||
|
||||
for (const auto& [id, name] : m_Names) {
|
||||
if (name == playerName) {
|
||||
toReturn = id;
|
||||
break;
|
||||
for (const auto& pair : mNames) {
|
||||
if (pair.second == playerName) {
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
return LWOOBJID_EMPTY;
|
||||
}
|
||||
|
||||
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
|
||||
return m_Players[playerID];
|
||||
}
|
||||
|
||||
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);
|
||||
bool PlayerContainer::GetIsMuted(PlayerData* data) {
|
||||
return data->muteExpire == 1 || data->muteExpire > time(NULL);
|
||||
}
|
||||
|
||||
@@ -7,49 +7,17 @@
|
||||
#include "dServer.h"
|
||||
#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 {
|
||||
operator bool() const noexcept {
|
||||
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;
|
||||
LWOOBJID playerID;
|
||||
std::string playerName;
|
||||
SystemAddress sysAddr;
|
||||
LWOZONEID zoneID;
|
||||
std::vector<FriendData> friends;
|
||||
std::vector<IgnoreData> ignoredPlayers;
|
||||
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
|
||||
bool isFTP = false;
|
||||
time_t muteExpire;
|
||||
uint8_t countOfBestFriends = 0;
|
||||
};
|
||||
|
||||
struct TeamData {
|
||||
TeamData();
|
||||
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
|
||||
LWOOBJID leaderID = LWOOBJID_EMPTY;
|
||||
std::vector<LWOOBJID> memberIDs{};
|
||||
@@ -60,17 +28,31 @@ struct TeamData {
|
||||
|
||||
class PlayerContainer {
|
||||
public:
|
||||
void Initialize();
|
||||
PlayerContainer();
|
||||
~PlayerContainer();
|
||||
|
||||
void InsertPlayer(Packet* packet);
|
||||
void RemovePlayer(Packet* packet);
|
||||
void MuteUpdate(Packet* packet);
|
||||
void CreateTeamServer(Packet* packet);
|
||||
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
||||
|
||||
const PlayerData& GetPlayerData(const LWOOBJID& playerID);
|
||||
const PlayerData& GetPlayerData(const std::string& playerName);
|
||||
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
||||
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
||||
PlayerData* GetPlayerData(const LWOOBJID& playerID) {
|
||||
auto it = mPlayers.find(playerID);
|
||||
if (it != mPlayers.end()) return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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* CreateTeam(LWOOBJID leader, bool local = false);
|
||||
@@ -83,15 +65,14 @@ public:
|
||||
void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
|
||||
std::u16string GetName(LWOOBJID playerID);
|
||||
LWOOBJID GetId(const std::u16string& playerName);
|
||||
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
||||
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
||||
bool GetIsMuted(PlayerData* data);
|
||||
|
||||
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return mPlayers; }
|
||||
|
||||
private:
|
||||
LWOOBJID m_TeamIDCounter = 0;
|
||||
std::map<LWOOBJID, PlayerData> m_Players;
|
||||
LWOOBJID mTeamIDCounter = 0;
|
||||
std::map<LWOOBJID, PlayerData*> mPlayers;
|
||||
std::vector<TeamData*> mTeams;
|
||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
||||
uint32_t m_MaxNumberOfFriends = 50;
|
||||
std::unordered_map<LWOOBJID, std::u16string> mNames;
|
||||
};
|
||||
|
||||
|
||||
246
dCommon/Amf3.h
246
dCommon/Amf3.h
@@ -2,7 +2,7 @@
|
||||
#define __AMF3__H__
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
#include "Game.h"
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -31,69 +31,54 @@ enum class eAmf : uint8_t {
|
||||
|
||||
class AMFBaseValue {
|
||||
public:
|
||||
[[nodiscard]] constexpr virtual eAmf GetValueType() const noexcept { return eAmf::Undefined; }
|
||||
constexpr AMFBaseValue() noexcept = default;
|
||||
constexpr virtual ~AMFBaseValue() noexcept = default;
|
||||
virtual eAmf GetValueType() { return eAmf::Undefined; };
|
||||
AMFBaseValue() {};
|
||||
virtual ~AMFBaseValue() {};
|
||||
};
|
||||
|
||||
// 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;
|
||||
AMFValue() {};
|
||||
AMFValue(ValueType value) { SetValue(value); };
|
||||
virtual ~AMFValue() override {};
|
||||
|
||||
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override;
|
||||
|
||||
[[nodiscard]] const ValueType& GetValue() const { return m_Data; }
|
||||
|
||||
void SetValue(const ValueType value) { m_Data = value; }
|
||||
eAmf GetValueType() override { return eAmf::Undefined; };
|
||||
|
||||
const ValueType& GetValue() { return data; };
|
||||
void SetValue(ValueType value) { data = value; };
|
||||
protected:
|
||||
ValueType m_Data;
|
||||
ValueType 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;
|
||||
AMFValue() {};
|
||||
AMFValue(const char* value) { SetValue(std::string(value)); };
|
||||
virtual ~AMFValue() override {};
|
||||
|
||||
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::String; }
|
||||
eAmf GetValueType() override { return eAmf::String; };
|
||||
|
||||
[[nodiscard]] const std::string& GetValue() const { return m_Data; }
|
||||
void SetValue(const std::string& value) { m_Data = value; }
|
||||
const std::string& GetValue() { return data; };
|
||||
void SetValue(std::string value) { data = value; };
|
||||
protected:
|
||||
std::string m_Data;
|
||||
std::string 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>;
|
||||
typedef AMFValue<std::nullptr_t> AMFNullValue;
|
||||
typedef AMFValue<bool> AMFBoolValue;
|
||||
typedef AMFValue<int32_t> AMFIntValue;
|
||||
typedef AMFValue<std::string> AMFStringValue;
|
||||
typedef AMFValue<double> AMFDoubleValue;
|
||||
|
||||
template<> inline eAmf AMFValue<std::nullptr_t>::GetValueType() { return eAmf::Null; };
|
||||
template<> inline eAmf AMFValue<bool>::GetValueType() { return this->data ? eAmf::True : eAmf::False; };
|
||||
template<> inline eAmf AMFValue<int32_t>::GetValueType() { return eAmf::Integer; };
|
||||
template<> inline eAmf AMFValue<uint32_t>::GetValueType() { return eAmf::Integer; };
|
||||
template<> inline eAmf AMFValue<std::string>::GetValueType() { return eAmf::String; };
|
||||
template<> inline eAmf AMFValue<double>::GetValueType() { return eAmf::Double; };
|
||||
|
||||
/**
|
||||
* The AMFArrayValue object holds 2 types of lists:
|
||||
@@ -104,14 +89,15 @@ using AMFDoubleValue = AMFValue<double>;
|
||||
* and are not to be deleted by a caller.
|
||||
*/
|
||||
class AMFArrayValue : public AMFBaseValue {
|
||||
using AMFAssociative = std::unordered_map<std::string, AMFBaseValue*>;
|
||||
using AMFDense = std::vector<AMFBaseValue*>;
|
||||
|
||||
typedef std::unordered_map<std::string, AMFBaseValue*> AMFAssociative;
|
||||
typedef std::vector<AMFBaseValue*> AMFDense;
|
||||
|
||||
public:
|
||||
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; }
|
||||
eAmf GetValueType() override { return eAmf::Array; };
|
||||
|
||||
~AMFArrayValue() override {
|
||||
for (const auto* valueToDelete : GetDense()) {
|
||||
for (auto valueToDelete : GetDense()) {
|
||||
if (valueToDelete) {
|
||||
delete valueToDelete;
|
||||
valueToDelete = nullptr;
|
||||
@@ -123,17 +109,17 @@ public:
|
||||
valueToDelete.second = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the Associative portion of the object
|
||||
*/
|
||||
[[nodiscard]] inline const AMFAssociative& GetAssociative() const noexcept { return m_Associative; }
|
||||
inline AMFAssociative& GetAssociative() { return this->associative; };
|
||||
|
||||
/**
|
||||
* Returns the dense portion of the object
|
||||
*/
|
||||
[[nodiscard]] inline const AMFDense& GetDense() const noexcept { return m_Dense; }
|
||||
inline AMFDense& GetDense() { return this->dense; };
|
||||
|
||||
/**
|
||||
* Inserts an AMFValue into the associative portion with the given key.
|
||||
@@ -150,47 +136,47 @@ public:
|
||||
* 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& key, const ValueType value) {
|
||||
const auto element = m_Associative.find(key);
|
||||
std::pair<AMFValue<ValueType>*, bool> Insert(const std::string& key, ValueType value) {
|
||||
auto element = associative.find(key);
|
||||
AMFValue<ValueType>* val = nullptr;
|
||||
bool found = true;
|
||||
if (element == m_Associative.cend()) {
|
||||
if (element == associative.end()) {
|
||||
val = new AMFValue<ValueType>(value);
|
||||
m_Associative.emplace(key, val);
|
||||
associative.insert(std::make_pair(key, val));
|
||||
} else {
|
||||
val = dynamic_cast<AMFValue<ValueType>*>(element->second);
|
||||
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& key) {
|
||||
const auto element = m_Associative.find(key);
|
||||
std::pair<AMFBaseValue*, bool> Insert(const std::string& key) {
|
||||
auto element = associative.find(key);
|
||||
AMFArrayValue* val = nullptr;
|
||||
bool found = true;
|
||||
if (element == m_Associative.cend()) {
|
||||
if (element == associative.end()) {
|
||||
val = new AMFArrayValue();
|
||||
m_Associative.emplace(key, val);
|
||||
associative.insert(std::make_pair(key, val));
|
||||
} else {
|
||||
val = dynamic_cast<AMFArrayValue*>(element->second);
|
||||
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) {
|
||||
std::pair<AMFBaseValue*, bool> Insert(const uint32_t& index) {
|
||||
AMFArrayValue* val = nullptr;
|
||||
bool inserted = false;
|
||||
if (index >= m_Dense.size()) {
|
||||
m_Dense.resize(index + 1);
|
||||
if (index >= dense.size()) {
|
||||
dense.resize(index + 1);
|
||||
val = new AMFArrayValue();
|
||||
m_Dense.at(index) = val;
|
||||
dense.at(index) = val;
|
||||
inserted = true;
|
||||
}
|
||||
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index)), inserted);
|
||||
}
|
||||
return std::make_pair(dynamic_cast<AMFArrayValue*>(dense.at(index)), inserted);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Inserts an AMFValue into the AMFArray key'd by index.
|
||||
@@ -203,17 +189,17 @@ public:
|
||||
* what was at the index.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) {
|
||||
std::pair<AMFValue<ValueType>*, bool> Insert(const uint32_t& index, ValueType value) {
|
||||
AMFValue<ValueType>* val = nullptr;
|
||||
bool inserted = false;
|
||||
if (index >= m_Dense.size()) {
|
||||
m_Dense.resize(index + 1);
|
||||
if (index >= this->dense.size()) {
|
||||
this->dense.resize(index + 1);
|
||||
val = new AMFValue<ValueType>(value);
|
||||
m_Dense.at(index) = val;
|
||||
this->dense.at(index) = val;
|
||||
inserted = true;
|
||||
}
|
||||
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index)), inserted);
|
||||
}
|
||||
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(this->dense.at(index)), inserted);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts an AMFValue into the associative portion with the given key.
|
||||
@@ -224,15 +210,15 @@ public:
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*/
|
||||
void Insert(const std::string& key, AMFBaseValue* const value) {
|
||||
const auto element = m_Associative.find(key);
|
||||
if (element != m_Associative.cend() && element->second) {
|
||||
void Insert(const std::string& key, AMFBaseValue* value) {
|
||||
auto element = associative.find(key);
|
||||
if (element != associative.end() && element->second) {
|
||||
delete element->second;
|
||||
element->second = value;
|
||||
} else {
|
||||
m_Associative.emplace(key, value);
|
||||
}
|
||||
associative.insert(std::make_pair(key, value));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts an AMFValue into the associative portion with the given index.
|
||||
@@ -243,15 +229,15 @@ public:
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*/
|
||||
void Insert(const size_t index, AMFBaseValue* const value) {
|
||||
if (index < m_Dense.size()) {
|
||||
const AMFDense::const_iterator itr = m_Dense.cbegin() + index;
|
||||
if (*itr) delete m_Dense.at(index);
|
||||
void Insert(const uint32_t index, AMFBaseValue* value) {
|
||||
if (index < dense.size()) {
|
||||
AMFDense::iterator itr = dense.begin() + index;
|
||||
if (*itr) delete dense.at(index);
|
||||
} else {
|
||||
m_Dense.resize(index + 1);
|
||||
}
|
||||
m_Dense.at(index) = value;
|
||||
dense.resize(index + 1);
|
||||
}
|
||||
dense.at(index) = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pushes an AMFValue into the back of the dense portion.
|
||||
@@ -264,9 +250,9 @@ public:
|
||||
* @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;
|
||||
}
|
||||
inline AMFValue<ValueType>* Push(ValueType value) {
|
||||
return Insert(this->dense.size(), value).first;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the key from the associative portion
|
||||
@@ -275,49 +261,52 @@ public:
|
||||
*
|
||||
* @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()) {
|
||||
void Remove(const std::string& key, bool deleteValue = true) {
|
||||
AMFAssociative::iterator it = this->associative.find(key);
|
||||
if (it != this->associative.end()) {
|
||||
if (deleteValue) delete it->second;
|
||||
m_Associative.erase(it);
|
||||
this->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;
|
||||
void Remove(const uint32_t index) {
|
||||
if (!this->dense.empty() && index < this->dense.size()) {
|
||||
auto itr = this->dense.begin() + index;
|
||||
if (*itr) delete (*itr);
|
||||
m_Dense.erase(itr);
|
||||
this->dense.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
void Pop() {
|
||||
if (!m_Dense.empty()) Remove(m_Dense.size() - 1);
|
||||
if (!this->dense.empty()) Remove(this->dense.size() - 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] AMFArrayValue* GetArray(const std::string& key) const {
|
||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second) : nullptr;
|
||||
AMFArrayValue* GetArray(const std::string& key) {
|
||||
AMFAssociative::const_iterator it = this->associative.find(key);
|
||||
if (it != this->associative.end()) {
|
||||
return dynamic_cast<AMFArrayValue*>(it->second);
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
[[nodiscard]] AMFArrayValue* GetArray(const size_t index) const {
|
||||
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index)) : nullptr;
|
||||
}
|
||||
AMFArrayValue* GetArray(const uint32_t index) {
|
||||
return index >= this->dense.size() ? nullptr : dynamic_cast<AMFArrayValue*>(this->dense.at(index));
|
||||
};
|
||||
|
||||
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string& key) {
|
||||
inline AMFArrayValue* InsertArray(const std::string& key) {
|
||||
return static_cast<AMFArrayValue*>(Insert(key).first);
|
||||
}
|
||||
};
|
||||
|
||||
[[maybe_unused]] inline AMFArrayValue* InsertArray(const size_t index) {
|
||||
inline AMFArrayValue* InsertArray(const uint32_t index) {
|
||||
return static_cast<AMFArrayValue*>(Insert(index).first);
|
||||
}
|
||||
};
|
||||
|
||||
[[maybe_unused]] inline AMFArrayValue* PushArray() {
|
||||
return static_cast<AMFArrayValue*>(Insert(m_Dense.size()).first);
|
||||
}
|
||||
inline AMFArrayValue* PushArray() {
|
||||
return static_cast<AMFArrayValue*>(Insert(this->dense.size()).first);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an AMFValue by the key from the associative portion and converts it
|
||||
@@ -329,18 +318,18 @@ public:
|
||||
* @return The AMFValue
|
||||
*/
|
||||
template <typename AmfType>
|
||||
[[nodiscard]] AMFValue<AmfType>* Get(const std::string& key) const {
|
||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||
return it != m_Associative.cend() ?
|
||||
AMFValue<AmfType>* Get(const std::string& key) const {
|
||||
AMFAssociative::const_iterator it = this->associative.find(key);
|
||||
return it != this->associative.end() ?
|
||||
dynamic_cast<AMFValue<AmfType>*>(it->second) :
|
||||
nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Get from the array but dont cast it
|
||||
[[nodiscard]] AMFBaseValue* Get(const std::string& key) const {
|
||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||
return it != m_Associative.cend() ? it->second : nullptr;
|
||||
}
|
||||
AMFBaseValue* Get(const std::string& key) const {
|
||||
AMFAssociative::const_iterator it = this->associative.find(key);
|
||||
return it != this->associative.end() ? it->second : nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get an AMFValue object at a position in the dense portion.
|
||||
@@ -352,28 +341,27 @@ public:
|
||||
* @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)) :
|
||||
AMFValue<AmfType>* Get(uint32_t index) const {
|
||||
return index < this->dense.size() ?
|
||||
dynamic_cast<AMFValue<AmfType>*>(this->dense.at(index)) :
|
||||
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) : nullptr;
|
||||
}
|
||||
|
||||
AMFBaseValue* Get(const uint32_t index) const {
|
||||
return index < this->dense.size() ? this->dense.at(index) : nullptr;
|
||||
};
|
||||
private:
|
||||
/**
|
||||
* The associative portion. These values are key'd with strings to an AMFValue.
|
||||
*/
|
||||
AMFAssociative m_Associative;
|
||||
AMFAssociative associative;
|
||||
|
||||
/**
|
||||
* The dense portion. These AMFValue's are stored one after
|
||||
* another with the most recent addition being at the back.
|
||||
*/
|
||||
AMFDense m_Dense;
|
||||
AMFDense dense;
|
||||
};
|
||||
|
||||
#endif //!__AMF3__H__
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "AmfSerialize.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
// Writes an AMFValue pointer to a RakNet::BitStream
|
||||
template<>
|
||||
@@ -29,7 +29,7 @@ void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG("Encountered unwritable AMFType %i!", type);
|
||||
Game::logger->Log("AmfSerialize", "Encountered unwritable AMFType %i!", type);
|
||||
}
|
||||
case eAmf::Undefined:
|
||||
case eAmf::Null:
|
||||
@@ -54,17 +54,17 @@ void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
|
||||
* 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);
|
||||
unsigned char b4 = (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;
|
||||
b3 = ((unsigned char)(v)) | 0x80;
|
||||
if (v > 0x7F) {
|
||||
unsigned char b2;
|
||||
v = v >> 7;
|
||||
b2 = static_cast<unsigned char>(v) | 0x80;
|
||||
b2 = ((unsigned char)(v)) | 0x80;
|
||||
bs->Write(b2);
|
||||
}
|
||||
|
||||
@@ -76,11 +76,11 @@ void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||
unsigned char b3;
|
||||
|
||||
v = v >> 8;
|
||||
b3 = static_cast<unsigned char>(v) | 0x80;
|
||||
b3 = ((unsigned char)(v)) | 0x80;
|
||||
v = v >> 7;
|
||||
b2 = static_cast<unsigned char>(v) | 0x80;
|
||||
b2 = ((unsigned char)(v)) | 0x80;
|
||||
v = v >> 7;
|
||||
b1 = static_cast<unsigned char>(v) | 0x80;
|
||||
b1 = ((unsigned char)(v)) | 0x80;
|
||||
|
||||
bs->Write(b1);
|
||||
bs->Write(b2);
|
||||
@@ -105,8 +105,8 @@ void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
|
||||
* 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()));
|
||||
WriteFlagNumber(bs, (uint32_t)str.size());
|
||||
bs->Write(str.c_str(), (uint32_t)str.size());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Amf3.h"
|
||||
|
||||
// RakNet
|
||||
#include "BitStream.h"
|
||||
#include <BitStream.h>
|
||||
|
||||
/*!
|
||||
\file AmfSerialize.h
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
#include "BinaryIO.h"
|
||||
#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
|
||||
std::string BinaryIO::ReadString(std::istream& instream) {
|
||||
std::string toReturn;
|
||||
@@ -15,3 +23,36 @@ std::string BinaryIO::ReadString(std::istream& instream) {
|
||||
|
||||
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
|
||||
|
||||
#ifndef __BINARYIO__H__
|
||||
#define __BINARYIO__H__
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace BinaryIO {
|
||||
|
||||
template<typename T>
|
||||
std::ostream& BinaryWrite(std::ostream& stream, const T& value) {
|
||||
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));
|
||||
}
|
||||
|
||||
enum class ReadType : int8_t {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteString(const std::string& stringToWrite, std::ofstream& outstream);
|
||||
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) {
|
||||
std::ifstream f(name.c_str());
|
||||
return f.good();
|
||||
}
|
||||
}
|
||||
|
||||
#endif //!__BINARYIO__H__
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
#include "ZCompression.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
//! Forward declarations
|
||||
|
||||
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
|
||||
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.
|
||||
@@ -23,24 +24,28 @@ bool CheckSd0Magic(std::istream& streamToCheck);
|
||||
*/
|
||||
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
uint32_t modelsTruncated{};
|
||||
auto modelsToTruncate = Database::Get()->GetAllUgcModels();
|
||||
bool previousCommitValue = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
for (auto& model : modelsToTruncate) {
|
||||
auto modelsToTruncate = GetModelsFromDatabase();
|
||||
bool previousCommitValue = Database::GetAutoCommit();
|
||||
Database::SetAutoCommit(false);
|
||||
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{};
|
||||
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.
|
||||
if (CheckSd0Magic(model.lxfmlData)) {
|
||||
if (CheckSd0Magic(modelAsSd0.get())) {
|
||||
while (true) {
|
||||
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.
|
||||
if (!model.lxfmlData.good()) break;
|
||||
if (!modelAsSd0->good()) break;
|
||||
|
||||
std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]);
|
||||
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.
|
||||
@@ -51,17 +56,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
|
||||
if (actualUncompressedSize != -1) {
|
||||
uint32_t previousSize = completeUncompressedModel.size();
|
||||
completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get()));
|
||||
completeUncompressedModel.append((char*)uncompressedChunk.get());
|
||||
completeUncompressedModel.resize(previousSize + actualUncompressedSize);
|
||||
} 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;
|
||||
}
|
||||
chunkCount++;
|
||||
}
|
||||
std::unique_ptr<tinyxml2::XMLDocument> document = std::make_unique<tinyxml2::XMLDocument>();
|
||||
if (!document) {
|
||||
LOG("Failed to initialize tinyxml document. Aborting.");
|
||||
Game::logger->Log("BrickByBrickFix", "Failed to initialize tinyxml document. Aborting.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -70,20 +75,28 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
"</LXFML>",
|
||||
completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos
|
||||
) {
|
||||
LOG("Brick-by-brick model %llu will be deleted!", model.id);
|
||||
Database::Get()->DeleteUgcModelData(model.id);
|
||||
Game::logger->Log("BrickByBrickFix",
|
||||
"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++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG("Brick-by-brick model %llu will be deleted!", model.id);
|
||||
Database::Get()->DeleteUgcModelData(model.id);
|
||||
Game::logger->Log("BrickByBrickFix",
|
||||
"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++;
|
||||
}
|
||||
}
|
||||
|
||||
Database::Get()->Commit();
|
||||
Database::Get()->SetAutoCommit(previousCommitValue);
|
||||
Database::Commit();
|
||||
Database::SetAutoCommit(previousCommitValue);
|
||||
return modelsTruncated;
|
||||
}
|
||||
|
||||
@@ -95,17 +108,21 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
*/
|
||||
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
uint32_t updatedModels = 0;
|
||||
auto modelsToUpdate = Database::Get()->GetAllUgcModels();
|
||||
auto previousAutoCommitState = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
for (auto& model : modelsToUpdate) {
|
||||
auto modelsToUpdate = GetModelsFromDatabase();
|
||||
auto previousAutoCommitState = Database::GetAutoCommit();
|
||||
Database::SetAutoCommit(false);
|
||||
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)
|
||||
// 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.
|
||||
model.lxfmlData.seekg(0, std::ios::end);
|
||||
uint32_t oldLxfmlSize = static_cast<uint32_t>(model.lxfmlData.tellg());
|
||||
model.lxfmlData.seekg(0);
|
||||
oldLxfml->seekg(0, std::ios::end);
|
||||
uint32_t oldLxfmlSize = static_cast<uint32_t>(oldLxfml->tellg());
|
||||
oldLxfml->seekg(0);
|
||||
|
||||
// Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size.
|
||||
uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9;
|
||||
@@ -113,27 +130,36 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
|
||||
WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize);
|
||||
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::istringstream outputStringStream(outputString);
|
||||
|
||||
insertionStatement->setBlob(1, static_cast<std::istream*>(&outputStringStream));
|
||||
insertionStatement->setInt64(2, modelId);
|
||||
try {
|
||||
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
|
||||
LOG("Updated model %i to sd0", model.id);
|
||||
insertionStatement->executeUpdate();
|
||||
Game::logger->Log("BrickByBrickFix", "Updated model %i to sd0", modelId);
|
||||
updatedModels++;
|
||||
} catch (sql::SQLException exception) {
|
||||
LOG("Failed to update model %i. This model should be inspected manually to see why."
|
||||
"The database error is %s", model.id, exception.what());
|
||||
Game::logger->Log(
|
||||
"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::Get()->SetAutoCommit(previousAutoCommitState);
|
||||
Database::Commit();
|
||||
Database::SetAutoCommit(previousAutoCommitState);
|
||||
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*
|
||||
*
|
||||
@@ -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
|
||||
}
|
||||
|
||||
bool CheckSd0Magic(std::istream& streamToCheck) {
|
||||
return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF;
|
||||
bool CheckSd0Magic(sql::Blob* streamToCheck) {
|
||||
return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF;
|
||||
}
|
||||
|
||||
@@ -4,26 +4,21 @@ set(DCOMMON_SOURCES
|
||||
"BinaryIO.cpp"
|
||||
"dConfig.cpp"
|
||||
"Diagnostics.cpp"
|
||||
"Logger.cpp"
|
||||
"Game.cpp"
|
||||
"dLogger.cpp"
|
||||
"GeneralUtils.cpp"
|
||||
"LDFFormat.cpp"
|
||||
"MD5.cpp"
|
||||
"Metrics.cpp"
|
||||
"NiPoint3.cpp"
|
||||
"NiQuaternion.cpp"
|
||||
"Demangler.cpp"
|
||||
"SHA512.cpp"
|
||||
"Type.cpp"
|
||||
"ZCompression.cpp"
|
||||
"BrickByBrickFix.cpp"
|
||||
"BinaryPathFinder.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)
|
||||
|
||||
foreach(file ${DCOMMON_DCLIENT_SOURCES})
|
||||
|
||||
@@ -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 "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
// If we're on Win32, we'll include our minidump writer
|
||||
#ifdef _WIN32
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <Dbghelp.h>
|
||||
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
void make_minidump(EXCEPTION_POINTERS* e) {
|
||||
auto hDbgHelp = LoadLibraryA("dbghelp");
|
||||
@@ -28,7 +28,7 @@ void make_minidump(EXCEPTION_POINTERS* e) {
|
||||
"_%4d%02d%02d_%02d%02d%02d.dmp",
|
||||
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);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
@@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) {
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
|
||||
#if defined(INCLUDE_BACKTRACE)
|
||||
#if defined(__include_backtrace__)
|
||||
#include <backtrace.h>
|
||||
|
||||
#include <backtrace-supported.h>
|
||||
@@ -83,7 +83,7 @@ struct bt_ctx {
|
||||
|
||||
static inline void Bt(struct backtrace_state* state) {
|
||||
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+");
|
||||
if (file != nullptr) {
|
||||
backtrace_print(state, 2, file);
|
||||
@@ -107,7 +107,7 @@ static void ErrorCallback(void* data, const char* msg, int errnum) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "Demangler.h"
|
||||
#include "Type.h"
|
||||
|
||||
void GenerateDump() {
|
||||
std::string cmd = "sudo gcore " + std::to_string(getpid());
|
||||
@@ -115,66 +115,58 @@ void GenerateDump() {
|
||||
}
|
||||
|
||||
void CatchUnhandled(int sig) {
|
||||
std::exception_ptr eptr = std::current_exception();
|
||||
try {
|
||||
if (eptr) std::rethrow_exception(eptr);
|
||||
} catch(const std::exception& e) {
|
||||
LOG("Caught exception: '%s'", e.what());
|
||||
}
|
||||
|
||||
#ifndef INCLUDE_BACKTRACE
|
||||
#ifndef __include_backtrace__
|
||||
|
||||
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()) {
|
||||
GenerateDump();
|
||||
}
|
||||
constexpr uint8_t MaxStackTrace = 32;
|
||||
void* array[MaxStackTrace];
|
||||
|
||||
void* array[10];
|
||||
size_t size;
|
||||
|
||||
// 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
|
||||
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
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
|
||||
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
|
||||
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' and extract the function name
|
||||
std::string functionName = strings[i];
|
||||
std::string::size_type start = functionName.find('(');
|
||||
std::string::size_type end = functionName.find('+');
|
||||
if (start != std::string::npos && end != std::string::npos) {
|
||||
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()) {
|
||||
demangled.push_back('(');
|
||||
demangled += functionName.substr(end);
|
||||
functionName = demangled;
|
||||
if (demangled.empty()) {
|
||||
Game::logger->Log("Diagnostics", "[%02zu] %s", i, demangled.c_str());
|
||||
} else {
|
||||
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
|
||||
}
|
||||
} 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);
|
||||
# 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(
|
||||
Diagnostics::GetProcessFileName().c_str(),
|
||||
@@ -185,7 +177,7 @@ void CatchUnhandled(int sig) {
|
||||
struct bt_ctx ctx = { state, 0 };
|
||||
Bt(state);
|
||||
|
||||
#endif // INCLUDE_BACKTRACE
|
||||
#endif
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -204,10 +196,10 @@ void MakeBacktrace() {
|
||||
sigact.sa_sigaction = CritErrHdlr;
|
||||
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
if (sigaction(SIGSEGV, &sigact, nullptr) != 0 ||
|
||||
sigaction(SIGFPE, &sigact, nullptr) != 0 ||
|
||||
sigaction(SIGABRT, &sigact, nullptr) != 0 ||
|
||||
sigaction(SIGILL, &sigact, nullptr) != 0) {
|
||||
if (sigaction(SIGSEGV, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||
sigaction(SIGFPE, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||
sigaction(SIGABRT, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||
sigaction(SIGILL, &sigact, (struct sigaction*)nullptr) != 0) {
|
||||
fprintf(stderr, "error setting signal handler for %d (%s)\n",
|
||||
SIGSEGV,
|
||||
strsignal(SIGSEGV));
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "CDClientDatabase.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
#include "AssetManager.h"
|
||||
|
||||
#include "eSqliteDataType.h"
|
||||
@@ -28,21 +28,23 @@ FdbToSqlite::Convert::Convert(std::string binaryOutPath) {
|
||||
this->m_BinaryOutPath = binaryOutPath;
|
||||
}
|
||||
|
||||
bool FdbToSqlite::Convert::ConvertDatabase(AssetStream& buffer) {
|
||||
bool FdbToSqlite::Convert::ConvertDatabase(AssetMemoryBuffer& buffer) {
|
||||
if (m_ConversionStarted) return false;
|
||||
|
||||
std::istream cdClientBuffer(&buffer);
|
||||
|
||||
this->m_ConversionStarted = true;
|
||||
try {
|
||||
CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite");
|
||||
|
||||
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
|
||||
|
||||
int32_t numberOfTables = ReadInt32(buffer);
|
||||
ReadTables(numberOfTables, buffer);
|
||||
int32_t numberOfTables = ReadInt32(cdClientBuffer);
|
||||
ReadTables(numberOfTables, cdClientBuffer);
|
||||
|
||||
CDClientDatabase::ExecuteQuery("COMMIT;");
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
|
||||
#include "AssetManager.h"
|
||||
class AssetMemoryBuffer;
|
||||
|
||||
enum class eSqliteDataType : int32_t;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace FdbToSqlite {
|
||||
*
|
||||
* @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.
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#include "Game.h"
|
||||
|
||||
namespace Game {
|
||||
void OnSignal(int signal) {
|
||||
lastSignal = signal;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <csignal>
|
||||
|
||||
class dServer;
|
||||
class Logger;
|
||||
class dLogger;
|
||||
class InstanceManager;
|
||||
class dChatFilter;
|
||||
class dConfig;
|
||||
@@ -13,12 +11,9 @@ class RakPeerInterface;
|
||||
class AssetManager;
|
||||
struct SystemAddress;
|
||||
class EntityManager;
|
||||
class dZoneManager;
|
||||
class PlayerContainer;
|
||||
|
||||
namespace Game {
|
||||
using signal_t = volatile std::sig_atomic_t;
|
||||
extern Logger* logger;
|
||||
extern dLogger* logger;
|
||||
extern dServer* server;
|
||||
extern InstanceManager* im;
|
||||
extern dChatFilter* chatFilter;
|
||||
@@ -27,14 +22,6 @@ namespace Game {
|
||||
extern RakPeerInterface* chatServer;
|
||||
extern AssetManager* assetManager;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ bool _IsSuffixChar(uint8_t c) {
|
||||
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||
size_t rem = slice.length();
|
||||
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) {
|
||||
uint8_t first = bytes[0];
|
||||
if (first < 0x80) { // 1 byte character
|
||||
@@ -320,24 +320,6 @@ std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::stri
|
||||
return sortedFiles;
|
||||
}
|
||||
|
||||
#ifdef DARKFLAME_PLATFORM_MACOS
|
||||
|
||||
// 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);
|
||||
bool GeneralUtils::TryParse(const std::string& x, const std::string& y, const std::string& z, NiPoint3& dst) {
|
||||
return TryParse<float>(x.c_str(), dst.x) && TryParse<float>(y.c_str(), dst.y) && TryParse<float>(z.c_str(), dst.z);
|
||||
}
|
||||
|
||||
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,22 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
// C++
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <stdint.h>
|
||||
#include <random>
|
||||
#include <ctime>
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#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 "Logger.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
enum eInventoryType : uint32_t;
|
||||
enum class eObjectBits : size_t;
|
||||
@@ -114,127 +111,6 @@ namespace GeneralUtils {
|
||||
*/
|
||||
bool CheckBit(int64_t value, uint32_t index);
|
||||
|
||||
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
std::u16string ReadWString(RakNet::BitStream* inStream);
|
||||
|
||||
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter);
|
||||
|
||||
std::vector<std::u16string> SplitString(const std::u16string& str, char16_t delimiter);
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
||||
|
||||
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder);
|
||||
|
||||
// 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 = uint32_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(const std::string_view str) {
|
||||
numeric_parse_t<T> result;
|
||||
|
||||
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>{};
|
||||
}
|
||||
|
||||
#ifdef DARKFLAME_PLATFORM_MACOS
|
||||
|
||||
// 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(const std::string_view str) noexcept
|
||||
try {
|
||||
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& strX, const std::string& strY, const std::string& 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 passingn a reference to a vector of three strings
|
||||
* @param str The string vector representing the X, Y, and Xcoordinates
|
||||
* @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::vector<std::string>& str) {
|
||||
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::u16string to_u16string(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
|
||||
|
||||
//! Generates a random number
|
||||
@@ -258,28 +134,93 @@ namespace GeneralUtils {
|
||||
return T();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 typename std::underlying_type_t<eType> CastUnderlyingType(const eType entry) noexcept {
|
||||
return static_cast<typename std::underlying_type_t<eType>>(entry);
|
||||
}
|
||||
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
// on Windows we need to undef these or else they conflict with our numeric limits calls
|
||||
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
std::u16string ReadWString(RakNet::BitStream* inStream);
|
||||
|
||||
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter);
|
||||
|
||||
std::vector<std::u16string> SplitString(const std::u16string& str, char16_t delimiter);
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
||||
|
||||
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder);
|
||||
|
||||
template <typename T>
|
||||
inline T GenerateRandomNumber() {
|
||||
// Make sure it is a numeric type
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
T Parse(const char* value);
|
||||
|
||||
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 <>
|
||||
inline eReplicaComponentType Parse(const char* value) {
|
||||
return static_cast<eReplicaComponentType>(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);
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& x, const std::string& y, const std::string& z, NiPoint3& 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
// C++
|
||||
#include <string_view>
|
||||
@@ -48,7 +48,7 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
|
||||
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());
|
||||
Game::logger->Log("LDFFormat", "Attempted to process invalid ldf type (%s) from string (%s)", ldfTypeAndValue.first.data(), format.data());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -61,33 +61,35 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
|
||||
}
|
||||
|
||||
case LDF_TYPE_S32: {
|
||||
const auto data = GeneralUtils::TryParse<int32_t>(ldfTypeAndValue.second);
|
||||
if (!data) {
|
||||
LOG("Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
||||
try {
|
||||
int32_t data = static_cast<int32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10));
|
||||
returnValue = new LDFData<int32_t>(key, data);
|
||||
} catch (std::exception) {
|
||||
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
||||
return nullptr;
|
||||
}
|
||||
returnValue = new LDFData<int32_t>(key, data.value());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LDF_TYPE_FLOAT: {
|
||||
const auto data = GeneralUtils::TryParse<float>(ldfTypeAndValue.second);
|
||||
if (!data) {
|
||||
LOG("Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
||||
try {
|
||||
float data = strtof(ldfTypeAndValue.second.data(), &storage);
|
||||
returnValue = new LDFData<float>(key, data);
|
||||
} catch (std::exception) {
|
||||
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
||||
return nullptr;
|
||||
}
|
||||
returnValue = new LDFData<float>(key, data.value());
|
||||
break;
|
||||
}
|
||||
|
||||
case LDF_TYPE_DOUBLE: {
|
||||
const auto data = GeneralUtils::TryParse<double>(ldfTypeAndValue.second);
|
||||
if (!data) {
|
||||
LOG("Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
||||
try {
|
||||
double data = strtod(ldfTypeAndValue.second.data(), &storage);
|
||||
returnValue = new LDFData<double>(key, data);
|
||||
} catch (std::exception) {
|
||||
Game::logger->Log("LDFFormat", "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;
|
||||
}
|
||||
|
||||
@@ -100,12 +102,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
|
||||
} else if (ldfTypeAndValue.second == "false") {
|
||||
data = 0;
|
||||
} else {
|
||||
const auto dataOptional = GeneralUtils::TryParse<uint32_t>(ldfTypeAndValue.second);
|
||||
if (!dataOptional) {
|
||||
LOG("Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
||||
try {
|
||||
data = static_cast<uint32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10));
|
||||
} catch (std::exception) {
|
||||
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
||||
return nullptr;
|
||||
}
|
||||
data = dataOptional.value();
|
||||
}
|
||||
|
||||
returnValue = new LDFData<uint32_t>(key, data);
|
||||
@@ -120,12 +122,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
|
||||
} 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());
|
||||
try {
|
||||
data = static_cast<bool>(strtol(ldfTypeAndValue.second.data(), &storage, 10));
|
||||
} catch (std::exception) {
|
||||
Game::logger->Log("LDFFormat", "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);
|
||||
@@ -133,22 +135,24 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
|
||||
}
|
||||
|
||||
case LDF_TYPE_U64: {
|
||||
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());
|
||||
try {
|
||||
uint64_t data = static_cast<uint64_t>(strtoull(ldfTypeAndValue.second.data(), &storage, 10));
|
||||
returnValue = new LDFData<uint64_t>(key, data);
|
||||
} catch (std::exception) {
|
||||
Game::logger->Log("LDFFormat", "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());
|
||||
try {
|
||||
LWOOBJID data = static_cast<LWOOBJID>(strtoll(ldfTypeAndValue.second.data(), &storage, 10));
|
||||
returnValue = new LDFData<LWOOBJID>(key, data);
|
||||
} catch (std::exception) {
|
||||
Game::logger->Log("LDFFormat", "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;
|
||||
}
|
||||
|
||||
@@ -159,12 +163,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
|
||||
}
|
||||
|
||||
case LDF_TYPE_UNKNOWN: {
|
||||
LOG("Warning: Attempted to process invalid unknown value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
|
||||
Game::logger->Log("LDFFormat", "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());
|
||||
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LDF type (%d) from string (%s)", type, format.data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,15 +63,15 @@ private:
|
||||
|
||||
//! Writes the key to the packet
|
||||
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) {
|
||||
packet->Write<uint16_t>(this->key[i]);
|
||||
packet->Write(static_cast<uint16_t>(this->key[i]));
|
||||
}
|
||||
}
|
||||
|
||||
//! Writes the value to the packet
|
||||
void WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||
packet->Write(this->value);
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
return new LDFData<T>(key, value);
|
||||
}
|
||||
|
||||
inline static const T Default = {};
|
||||
inline static T Default = {};
|
||||
};
|
||||
|
||||
// LDF Types
|
||||
@@ -179,30 +179,30 @@ template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF
|
||||
// The specialized version for std::u16string (UTF-16)
|
||||
template<>
|
||||
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) {
|
||||
packet->Write<uint16_t>(this->value[i]);
|
||||
packet->Write(static_cast<uint16_t>(this->value[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// The specialized version for bool
|
||||
template<>
|
||||
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)
|
||||
template<>
|
||||
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) {
|
||||
packet->Write<uint8_t>(this->value[i]);
|
||||
packet->Write(static_cast<uint8_t>(this->value[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
return static_cast<float>(nanoseconds) / 1e6;
|
||||
return (float)nanoseconds / 1e6;
|
||||
}
|
||||
|
||||
std::string Metrics::MetricVariableToString(MetricVariable variable) {
|
||||
@@ -193,34 +193,34 @@ size_t Metrics::GetPeakRSS() {
|
||||
/* Windows -------------------------------------------------- */
|
||||
PROCESS_MEMORY_COUNTERS 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__)))
|
||||
/* AIX and Solaris ------------------------------------------ */
|
||||
struct psinfo psinfo;
|
||||
int fd = -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)) {
|
||||
close(fd);
|
||||
return static_cast<size_t>(0L); /* Can't read? */
|
||||
return (size_t)0L; /* Can't read? */
|
||||
}
|
||||
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__))
|
||||
/* BSD, Linux, and OSX -------------------------------------- */
|
||||
struct rusage rusage;
|
||||
getrusage(RUSAGE_SELF, &rusage);
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
return static_cast<size_t>(rusage.ru_maxrss);
|
||||
return (size_t)rusage.ru_maxrss;
|
||||
#else
|
||||
return static_cast<size_t>(rusage.ru_maxrss * 1024L);
|
||||
return (size_t)(rusage.ru_maxrss * 1024L);
|
||||
#endif
|
||||
|
||||
#else
|
||||
/* Unknown OS ----------------------------------------------- */
|
||||
return static_cast<size_t>(0L); /* Unsupported. */
|
||||
return (size_t)0L; /* Unsupported. */
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -234,33 +234,33 @@ size_t Metrics::GetCurrentRSS() {
|
||||
/* Windows -------------------------------------------------- */
|
||||
PROCESS_MEMORY_COUNTERS info;
|
||||
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
||||
return static_cast<size_t>(info.WorkingSetSize);
|
||||
return (size_t)info.WorkingSetSize;
|
||||
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
/* OSX ------------------------------------------------------ */
|
||||
struct mach_task_basic_info info;
|
||||
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
|
||||
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
|
||||
reinterpret_cast<task_info_t>(&info), &infoCount) != KERN_SUCCESS)
|
||||
return static_cast<size_t>(0L); /* Can't access? */
|
||||
return static_cast<size_t>(info.resident_size);
|
||||
(task_info_t)&info, &infoCount) != KERN_SUCCESS)
|
||||
return (size_t)0L; /* Can't access? */
|
||||
return (size_t)info.resident_size;
|
||||
|
||||
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
|
||||
/* Linux ---------------------------------------------------- */
|
||||
long rss = 0L;
|
||||
FILE* fp = 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) {
|
||||
fclose(fp);
|
||||
return static_cast<size_t>(0L); /* Can't read? */
|
||||
return (size_t)0L; /* Can't read? */
|
||||
}
|
||||
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
|
||||
/* AIX, BSD, Solaris, and Unknown OS ------------------------ */
|
||||
return static_cast<size_t>(0L); /* Unsupported. */
|
||||
return (size_t)0L; /* Unsupported. */
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,201 @@
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
|
||||
// C++
|
||||
#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
|
||||
float NiPoint3::Length() const {
|
||||
return std::sqrt(x * x + y * y + z * z);
|
||||
float NiPoint3::Length(void) const {
|
||||
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
|
||||
NiPoint3 NiPoint3::Unitize() const {
|
||||
NiPoint3 NiPoint3::Unitize(void) const {
|
||||
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 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
|
||||
|
||||
//! 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) {
|
||||
const auto dot = a.DotProduct(b);
|
||||
const auto lenA = a.SquaredLength();
|
||||
@@ -34,24 +211,47 @@ float NiPoint3::Distance(const NiPoint3& a, const NiPoint3& b) {
|
||||
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 dy = target.y - current.y;
|
||||
float dz = target.z - current.z;
|
||||
|
||||
float lengthSquared = static_cast<float>(
|
||||
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)) {
|
||||
float lengthSquared = (float)((double)dx * (double)dx + (double)dy * (double)dy + (double)dz * (double)dz);
|
||||
if ((double)lengthSquared == 0.0 || (double)maxDistanceDelta >= 0.0 && (double)lengthSquared <= (double)maxDistanceDelta * (double)maxDistanceDelta)
|
||||
return target;
|
||||
}
|
||||
|
||||
float length = std::sqrt(lengthSquared);
|
||||
float length = (float)std::sqrt((double)lengthSquared);
|
||||
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__
|
||||
#define __NIPOINT3_H__
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
\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
|
||||
class NiPoint3 {
|
||||
public:
|
||||
float x{ 0 }; //!< The x position
|
||||
float y{ 0 }; //!< The y position
|
||||
float z{ 0 }; //!< The z position
|
||||
float x; //!< The x position
|
||||
float y; //!< The y position
|
||||
float z; //!< The z position
|
||||
|
||||
|
||||
//! Initializer
|
||||
constexpr NiPoint3() = default;
|
||||
NiPoint3(void);
|
||||
|
||||
//! Initializer
|
||||
/*!
|
||||
@@ -27,21 +26,23 @@ public:
|
||||
\param y The y coordinate
|
||||
\param z The z coordinate
|
||||
*/
|
||||
constexpr NiPoint3(const float x, const float y, const float z) noexcept
|
||||
: x{ x }
|
||||
, y{ y }
|
||||
, z{ z } {
|
||||
}
|
||||
NiPoint3(float x, float y, float z);
|
||||
|
||||
//! Copy Constructor
|
||||
/*!
|
||||
\param point The point to copy
|
||||
*/
|
||||
constexpr NiPoint3(const NiPoint3& point) noexcept
|
||||
: x{ point.x }
|
||||
, y{ point.y }
|
||||
, z{ point.z } {
|
||||
}
|
||||
NiPoint3(const NiPoint3& point);
|
||||
|
||||
//! Destructor
|
||||
~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
|
||||
|
||||
@@ -49,37 +50,38 @@ public:
|
||||
/*!
|
||||
\return The x coordinate
|
||||
*/
|
||||
[[nodiscard]] constexpr float GetX() const noexcept;
|
||||
float GetX(void) const;
|
||||
|
||||
//! Sets the X coordinate
|
||||
/*!
|
||||
\param x The x coordinate
|
||||
*/
|
||||
constexpr void SetX(const float x) noexcept;
|
||||
void SetX(float x);
|
||||
|
||||
//! Gets the Y coordinate
|
||||
/*!
|
||||
\return The y coordinate
|
||||
*/
|
||||
[[nodiscard]] constexpr float GetY() const noexcept;
|
||||
float GetY(void) const;
|
||||
|
||||
//! Sets the Y coordinate
|
||||
/*!
|
||||
\param y The y coordinate
|
||||
*/
|
||||
constexpr void SetY(const float y) noexcept;
|
||||
void SetY(float y);
|
||||
|
||||
//! Gets the Z coordinate
|
||||
/*!
|
||||
\return The z coordinate
|
||||
*/
|
||||
[[nodiscard]] constexpr float GetZ() const noexcept;
|
||||
float GetZ(void) const;
|
||||
|
||||
//! Sets the Z coordinate
|
||||
/*!
|
||||
\param z The z coordinate
|
||||
*/
|
||||
constexpr void SetZ(const float z) noexcept;
|
||||
void SetZ(float z);
|
||||
|
||||
|
||||
// MARK: Member Functions
|
||||
|
||||
@@ -87,70 +89,70 @@ public:
|
||||
/*!
|
||||
\return The scalar length of the vector
|
||||
*/
|
||||
[[nodiscard]] float Length() const;
|
||||
float Length(void) const;
|
||||
|
||||
//! Gets 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
|
||||
/*!
|
||||
\param vec The second vector
|
||||
\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
|
||||
/*!
|
||||
\param vec The second vector
|
||||
\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
|
||||
/*!
|
||||
\returns The current vector
|
||||
*/
|
||||
[[nodiscard]] NiPoint3 Unitize() const;
|
||||
NiPoint3 Unitize(void) const;
|
||||
|
||||
|
||||
// MARK: Operators
|
||||
|
||||
//! Operator to check for equality
|
||||
constexpr bool operator==(const NiPoint3& point) const noexcept;
|
||||
bool operator==(const NiPoint3& point) const;
|
||||
|
||||
//! Operator to check for inequality
|
||||
constexpr bool operator!=(const NiPoint3& point) const noexcept;
|
||||
bool operator!=(const NiPoint3& point) const;
|
||||
|
||||
//! Operator for subscripting
|
||||
constexpr float& operator[](const int i) noexcept;
|
||||
float& operator[](int i);
|
||||
|
||||
//! Operator for subscripting
|
||||
constexpr const float& operator[](const int i) const noexcept;
|
||||
const float& operator[](int i) const;
|
||||
|
||||
//! 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;
|
||||
NiPoint3 operator+=(const NiPoint3& point) const;
|
||||
|
||||
//! 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
|
||||
constexpr NiPoint3 operator+(const float fScalar) const noexcept;
|
||||
NiPoint3 operator+(float fScalar) const;
|
||||
|
||||
//! 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
|
||||
constexpr NiPoint3 operator*(const float fScalar) const noexcept;
|
||||
NiPoint3 operator*(float fScalar) const;
|
||||
|
||||
//! Operator for scalar division of a vector
|
||||
constexpr NiPoint3 operator/(const float fScalar) const noexcept;
|
||||
NiPoint3 operator/(float fScalar) const;
|
||||
|
||||
|
||||
// MARK: Helper Functions
|
||||
|
||||
@@ -160,14 +162,14 @@ public:
|
||||
\param maxPoint The maximum point of the bounding 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
|
||||
/*!
|
||||
\param sphereCenter The sphere center
|
||||
\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
|
||||
@@ -175,30 +177,15 @@ public:
|
||||
\param p Refrence point
|
||||
\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.
|
||||
[[nodiscard]] constexpr NiPoint3 RotateByQuaternion(const NiQuaternion& rotation) noexcept;
|
||||
NiPoint3 RotateByQuaternion(const NiQuaternion& rotation);
|
||||
};
|
||||
|
||||
// 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++
|
||||
#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
|
||||
|
||||
//! 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 angles;
|
||||
|
||||
@@ -30,9 +111,22 @@ Vector3 NiQuaternion::GetEulerAngles() const {
|
||||
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
|
||||
|
||||
//! 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) {
|
||||
//To make sure we don't orient around the X/Z axis:
|
||||
NiPoint3 source = sourcePoint;
|
||||
@@ -42,7 +136,7 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d
|
||||
|
||||
NiPoint3 forwardVector = NiPoint3(dest - source).Unitize();
|
||||
|
||||
NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
|
||||
NiPoint3 posZ = NiPoint3::UNIT_Z;
|
||||
NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize();
|
||||
|
||||
float dot = posZ.DotProduct(forwardVector);
|
||||
@@ -54,11 +148,10 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d
|
||||
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) {
|
||||
NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize();
|
||||
|
||||
NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
|
||||
NiPoint3 posZ = NiPoint3::UNIT_Z;
|
||||
NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize();
|
||||
|
||||
float dot = posZ.DotProduct(forwardVector);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef __NIQUATERNION_H__
|
||||
#define __NIQUATERNION_H__
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#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
|
||||
class NiQuaternion {
|
||||
public:
|
||||
float w{ 1 }; //!< The w coordinate
|
||||
float x{ 0 }; //!< The x coordinate
|
||||
float y{ 0 }; //!< The y coordinate
|
||||
float z{ 0 }; //!< The z coordinate
|
||||
float w; //!< The w coordinate
|
||||
float x; //!< The x coordinate
|
||||
float y; //!< The y coordinate
|
||||
float z; //!< The z coordinate
|
||||
|
||||
|
||||
//! The initializer
|
||||
constexpr NiQuaternion() = default;
|
||||
NiQuaternion(void);
|
||||
|
||||
//! The initializer
|
||||
/*!
|
||||
@@ -31,12 +30,13 @@ public:
|
||||
\param y The y coordinate
|
||||
\param z The z coordinate
|
||||
*/
|
||||
constexpr NiQuaternion(const float w, const float x, const float y, const float z) noexcept
|
||||
: w{ w }
|
||||
, x{ x }
|
||||
, y{ y }
|
||||
, z{ z } {
|
||||
}
|
||||
NiQuaternion(float w, float x, float y, float z);
|
||||
|
||||
//! Destructor
|
||||
~NiQuaternion(void);
|
||||
|
||||
// MARK: Constants
|
||||
static const NiQuaternion IDENTITY; //!< Quaternion(1, 0, 0, 0)
|
||||
|
||||
// MARK: Setters / Getters
|
||||
|
||||
@@ -44,49 +44,50 @@ public:
|
||||
/*!
|
||||
\return The w coordinate
|
||||
*/
|
||||
[[nodiscard]] constexpr float GetW() const noexcept;
|
||||
float GetW(void) const;
|
||||
|
||||
//! Sets the W coordinate
|
||||
/*!
|
||||
\param w The w coordinate
|
||||
*/
|
||||
constexpr void SetW(const float w) noexcept;
|
||||
void SetW(float w);
|
||||
|
||||
//! Gets the X coordinate
|
||||
/*!
|
||||
\return The x coordinate
|
||||
*/
|
||||
[[nodiscard]] constexpr float GetX() const noexcept;
|
||||
float GetX(void) const;
|
||||
|
||||
//! Sets the X coordinate
|
||||
/*!
|
||||
\param x The x coordinate
|
||||
*/
|
||||
constexpr void SetX(const float x) noexcept;
|
||||
void SetX(float x);
|
||||
|
||||
//! Gets the Y coordinate
|
||||
/*!
|
||||
\return The y coordinate
|
||||
*/
|
||||
[[nodiscard]] constexpr float GetY() const noexcept;
|
||||
float GetY(void) const;
|
||||
|
||||
//! Sets the Y coordinate
|
||||
/*!
|
||||
\param y The y coordinate
|
||||
*/
|
||||
constexpr void SetY(const float y) noexcept;
|
||||
void SetY(float y);
|
||||
|
||||
//! Gets the Z coordinate
|
||||
/*!
|
||||
\return The z coordinate
|
||||
*/
|
||||
[[nodiscard]] constexpr float GetZ() const noexcept;
|
||||
float GetZ(void) const;
|
||||
|
||||
//! Sets the Z coordinate
|
||||
/*!
|
||||
\param z The z coordinate
|
||||
*/
|
||||
constexpr void SetZ(const float z) noexcept;
|
||||
void SetZ(float z);
|
||||
|
||||
|
||||
// MARK: Member Functions
|
||||
|
||||
@@ -94,29 +95,31 @@ public:
|
||||
/*!
|
||||
\return The forward vector of the quaternion
|
||||
*/
|
||||
[[nodiscard]] constexpr Vector3 GetForwardVector() const noexcept;
|
||||
Vector3 GetForwardVector(void) const;
|
||||
|
||||
//! Returns the up vector from 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
|
||||
/*!
|
||||
\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
|
||||
|
||||
//! Operator to check for equality
|
||||
constexpr bool operator==(const NiQuaternion& rot) const noexcept;
|
||||
bool operator==(const NiQuaternion& rot) const;
|
||||
|
||||
//! Operator to check for inequality
|
||||
constexpr bool operator!=(const NiQuaternion& rot) const noexcept;
|
||||
bool operator!=(const NiQuaternion& rot) const;
|
||||
|
||||
|
||||
// MARK: Helper Functions
|
||||
|
||||
@@ -126,7 +129,7 @@ public:
|
||||
\param destPoint The destination location
|
||||
\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
|
||||
/*!
|
||||
@@ -134,7 +137,7 @@ public:
|
||||
\param destPoint The destination location
|
||||
\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
|
||||
/*!
|
||||
@@ -142,17 +145,7 @@ public:
|
||||
\param angle The angle relative to this axis
|
||||
\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,50 +0,0 @@
|
||||
#ifndef __POSITIONUPDATE__H__
|
||||
#define __POSITIONUPDATE__H__
|
||||
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
|
||||
|
||||
struct RemoteInputInfo {
|
||||
RemoteInputInfo() {
|
||||
m_RemoteInputX = 0;
|
||||
m_RemoteInputY = 0;
|
||||
m_IsPowersliding = false;
|
||||
m_IsModified = false;
|
||||
}
|
||||
|
||||
void operator=(const RemoteInputInfo& other) {
|
||||
m_RemoteInputX = other.m_RemoteInputX;
|
||||
m_RemoteInputY = other.m_RemoteInputY;
|
||||
m_IsPowersliding = other.m_IsPowersliding;
|
||||
m_IsModified = other.m_IsModified;
|
||||
}
|
||||
|
||||
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;
|
||||
float m_RemoteInputY;
|
||||
bool m_IsPowersliding;
|
||||
bool m_IsModified;
|
||||
};
|
||||
|
||||
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 "zlib.h"
|
||||
#include <zlib.h>
|
||||
|
||||
namespace ZCompression {
|
||||
int32_t GetMaxCompressedLength(int32_t nLenSrc) {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
#include "AssetManager.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
#include "zlib.h"
|
||||
#include <zlib.h>
|
||||
|
||||
AssetManager::AssetManager(const std::filesystem::path& path) {
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
@@ -81,8 +81,8 @@ bool AssetManager::HasFile(const char* name) {
|
||||
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
||||
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());
|
||||
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size());
|
||||
crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4);
|
||||
|
||||
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
||||
if (item.m_Crc == crc) {
|
||||
@@ -113,7 +113,7 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
||||
#endif
|
||||
fseek(file, 0, SEEK_END);
|
||||
*len = ftell(file);
|
||||
*data = static_cast<char*>(malloc(*len));
|
||||
*data = (char*)malloc(*len);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
int32_t readInData = fread(*data, sizeof(uint8_t), *len, file);
|
||||
fclose(file);
|
||||
@@ -129,8 +129,8 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
||||
fixedName = "client\\res\\" + fixedName;
|
||||
}
|
||||
int32_t packIndex = -1;
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
||||
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size());
|
||||
crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4);
|
||||
|
||||
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
||||
if (item.m_Crc == crc) {
|
||||
@@ -152,12 +152,13 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
||||
return success;
|
||||
}
|
||||
|
||||
AssetStream AssetManager::GetFile(const char* name) {
|
||||
char* buf; uint32_t len;
|
||||
AssetMemoryBuffer AssetManager::GetFileAsBuffer(const char* name) {
|
||||
char* buf;
|
||||
uint32_t 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) {
|
||||
@@ -167,7 +168,7 @@ uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
||||
crc = base;
|
||||
for (i = 0; i < l; i++) {
|
||||
// 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.
|
||||
msb = crc >> 31;
|
||||
crc <<= 1;
|
||||
|
||||
@@ -25,10 +25,6 @@ struct AssetMemoryBuffer : std::streambuf {
|
||||
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 {
|
||||
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());
|
||||
return gptr() - eback();
|
||||
}
|
||||
};
|
||||
|
||||
struct AssetStream : std::istream {
|
||||
AssetStream(char* base, std::ptrdiff_t n, bool success) : std::istream(new AssetMemoryBuffer(base, n, success)) {}
|
||||
|
||||
~AssetStream() {
|
||||
delete rdbuf();
|
||||
}
|
||||
|
||||
operator bool() {
|
||||
return reinterpret_cast<AssetMemoryBuffer*>(rdbuf())->m_Success;
|
||||
void close() {
|
||||
delete m_Base;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,7 +56,7 @@ public:
|
||||
|
||||
bool HasFile(const char* name);
|
||||
bool GetFile(const char* name, char** data, uint32_t* len);
|
||||
AssetStream GetFile(const char* name);
|
||||
AssetMemoryBuffer GetFileAsBuffer(const char* name);
|
||||
|
||||
private:
|
||||
void LoadPackIndex();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set(DCOMMON_DCLIENT_SOURCES
|
||||
"AssetManager.cpp"
|
||||
"PackIndex.cpp"
|
||||
"Pack.cpp"
|
||||
"AssetManager.cpp"
|
||||
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,7 +76,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
||||
fseek(file, pos, SEEK_SET);
|
||||
|
||||
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);
|
||||
|
||||
*data = tempData;
|
||||
@@ -90,7 +90,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
||||
|
||||
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;
|
||||
|
||||
while (true) {
|
||||
@@ -100,12 +100,12 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
||||
int32_t readInData = fread(&size, sizeof(uint32_t), 1, file);
|
||||
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);
|
||||
pos += size; // Move pointer position the amount of bytes read to the right
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PackRecord {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "PackIndex.h"
|
||||
#include "BinaryIO.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
||||
m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary);
|
||||
@@ -9,9 +9,20 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
|
||||
|
||||
m_PackPaths.resize(m_PackPathCount);
|
||||
for (auto& item : m_PackPaths) {
|
||||
BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
|
||||
for (int i = 0; i < m_PackPathCount; i++) {
|
||||
uint32_t stringLen = 0;
|
||||
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);
|
||||
@@ -23,7 +34,7 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
||||
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) {
|
||||
std::replace(item.begin(), item.end(), '\\', '/');
|
||||
|
||||
@@ -10,23 +10,8 @@ dConfig::dConfig(const std::string& filepath) {
|
||||
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() {
|
||||
std::filesystem::path config_dir = GetConfigDir();
|
||||
|
||||
std::ifstream in(config_dir / m_ConfigFilePath);
|
||||
std::ifstream in(BinaryPathFinder::GetBinaryDir() / m_ConfigFilePath);
|
||||
if (!in.good()) return;
|
||||
|
||||
std::string line{};
|
||||
@@ -34,7 +19,7 @@ void dConfig::LoadConfig() {
|
||||
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;
|
||||
|
||||
line.clear();
|
||||
@@ -49,20 +34,17 @@ void dConfig::ReloadConfig() {
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
void dConfig::ProcessLine(const std::string& line) {
|
||||
auto splitLoc = line.find('=');
|
||||
auto key = line.substr(0, splitLoc);
|
||||
auto value = line.substr(splitLoc + 1);
|
||||
auto splitLine = GeneralUtils::SplitString(line, '=');
|
||||
|
||||
if (splitLine.size() != 2) return;
|
||||
|
||||
//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 (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return;
|
||||
|
||||
@@ -7,11 +7,6 @@ class dConfig {
|
||||
public:
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef __STRINGIFIEDENUM_H__
|
||||
#define __STRINGIFIEDENUM_H__
|
||||
|
||||
#include <string>
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace StringifiedEnum {
|
||||
template<typename T>
|
||||
const std::string_view ToString(const T e) {
|
||||
static_assert(std::is_enum_v<T>, "Not an enum"); // Check type
|
||||
|
||||
constexpr auto& sv = magic_enum::enum_entries<T>();
|
||||
|
||||
const auto it = std::lower_bound(
|
||||
sv.begin(), sv.end(), e,
|
||||
[&](const std::pair<T, std::string_view>& lhs, const T rhs) { return lhs.first < rhs; }
|
||||
);
|
||||
|
||||
std::string_view output;
|
||||
if (it != sv.end() && it->first == e) {
|
||||
output = it->second;
|
||||
} else {
|
||||
output = "UNKNOWN";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !__STRINGIFIEDENUM_H__
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "BitStream.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eClientMessageType.h"
|
||||
#include "BitStreamUtils.h"
|
||||
|
||||
#pragma warning (disable:4251) //Disables SQL warnings
|
||||
|
||||
@@ -33,90 +32,92 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
|
||||
#define CBITSTREAM RakNet::BitStream bitStream;
|
||||
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits());
|
||||
#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
|
||||
#define CMSGHEADER PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
|
||||
#define SEND_PACKET Game::server->Send(&bitStream, sysAddr, false);
|
||||
#define SEND_PACKET_BROADCAST Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
|
||||
//=========== TYPEDEFS ==========
|
||||
|
||||
using LOT = int32_t; //!< A LOT
|
||||
using LWOOBJID = int64_t; //!< An object ID (should be unsigned actually but ok)
|
||||
using TSkillID = int32_t; //!< A skill ID
|
||||
using LWOCLONEID = uint32_t; //!< Used for Clone IDs
|
||||
using LWOMAPID = uint16_t; //!< Used for Map IDs
|
||||
using LWOINSTANCEID = uint16_t; //!< Used for Instance IDs
|
||||
using PROPERTYCLONELIST = uint32_t; //!< Used for Property Clone IDs
|
||||
using StripId = uint32_t;
|
||||
typedef int32_t LOT; //!< A LOT
|
||||
typedef int64_t LWOOBJID; //!< An object ID (should be unsigned actually but ok)
|
||||
typedef int32_t TSkillID; //!< A skill ID
|
||||
typedef uint32_t LWOCLONEID; //!< Used for Clone IDs
|
||||
typedef uint16_t LWOMAPID; //!< Used for Map IDs
|
||||
typedef uint16_t LWOINSTANCEID; //!< Used for Instance IDs
|
||||
typedef uint32_t PROPERTYCLONELIST; //!< Used for Property Clone IDs
|
||||
typedef uint32_t StripId;
|
||||
|
||||
constexpr LWOOBJID LWOOBJID_EMPTY = 0; //!< An empty object ID
|
||||
constexpr LOT LOT_NULL = -1; //!< A null LOT
|
||||
constexpr int32_t LOOTTYPE_NONE = 0; //!< No loot type available
|
||||
constexpr float SECONDARY_PRIORITY = 1.0f; //!< Secondary Priority
|
||||
constexpr uint32_t INVENTORY_MAX = 9999999; //!< The Maximum Inventory Size
|
||||
constexpr LWOCLONEID LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID
|
||||
constexpr LWOINSTANCEID LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
|
||||
constexpr LWOMAPID LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
|
||||
constexpr uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
|
||||
const LWOOBJID LWOOBJID_EMPTY = 0; //!< An empty object ID
|
||||
const LOT LOT_NULL = -1; //!< A null LOT
|
||||
const int32_t LOOTTYPE_NONE = 0; //!< No loot type available
|
||||
const float SECONDARY_PRIORITY = 1.0f; //!< Secondary Priority
|
||||
const uint32_t INVENTORY_MAX = 9999999; //!< The Maximum Inventory Size
|
||||
const uint32_t LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID
|
||||
const uint16_t LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
|
||||
const uint16_t LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
|
||||
const uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
|
||||
|
||||
constexpr float PI = 3.14159f;
|
||||
const float PI = 3.14159f;
|
||||
|
||||
//============ STRUCTS ==============
|
||||
|
||||
struct LWOSCENEID {
|
||||
public:
|
||||
constexpr LWOSCENEID() noexcept { m_sceneID = -1; m_layerID = 0; }
|
||||
constexpr LWOSCENEID(int32_t sceneID) noexcept { m_sceneID = sceneID; m_layerID = 0; }
|
||||
constexpr LWOSCENEID(int32_t sceneID, uint32_t layerID) noexcept { m_sceneID = sceneID; m_layerID = layerID; }
|
||||
LWOSCENEID() { m_sceneID = -1; m_layerID = 0; }
|
||||
LWOSCENEID(int sceneID) { m_sceneID = sceneID; m_layerID = 0; }
|
||||
LWOSCENEID(int sceneID, unsigned int layerID) { m_sceneID = sceneID; m_layerID = layerID; }
|
||||
|
||||
constexpr LWOSCENEID& operator=(const LWOSCENEID& rhs) noexcept { m_sceneID = rhs.m_sceneID; m_layerID = rhs.m_layerID; return *this; }
|
||||
constexpr LWOSCENEID& operator=(const int32_t rhs) noexcept { m_sceneID = rhs; m_layerID = 0; return *this; }
|
||||
LWOSCENEID& operator=(const LWOSCENEID& rhs) { m_sceneID = rhs.m_sceneID; m_layerID = rhs.m_layerID; return *this; }
|
||||
LWOSCENEID& operator=(const int rhs) { m_sceneID = rhs; m_layerID = 0; return *this; }
|
||||
|
||||
constexpr bool operator<(const LWOSCENEID& rhs) const noexcept { return (m_sceneID < rhs.m_sceneID || (m_sceneID == rhs.m_sceneID && m_layerID < rhs.m_layerID)); }
|
||||
constexpr bool operator<(const int32_t rhs) const noexcept { return m_sceneID < rhs; }
|
||||
bool operator<(const LWOSCENEID& rhs) const { return (m_sceneID < rhs.m_sceneID || (m_sceneID == rhs.m_sceneID && m_layerID < rhs.m_layerID)); }
|
||||
bool operator<(const int rhs) const { return m_sceneID < rhs; }
|
||||
|
||||
constexpr bool operator==(const LWOSCENEID& rhs) const noexcept { return (m_sceneID == rhs.m_sceneID && m_layerID == rhs.m_layerID); }
|
||||
constexpr bool operator==(const int32_t rhs) const noexcept { return m_sceneID == rhs; }
|
||||
bool operator==(const LWOSCENEID& rhs) const { return (m_sceneID == rhs.m_sceneID && m_layerID == rhs.m_layerID); }
|
||||
bool operator==(const int rhs) const { return m_sceneID == rhs; }
|
||||
|
||||
constexpr int32_t GetSceneID() const noexcept { return m_sceneID; }
|
||||
constexpr uint32_t GetLayerID() const noexcept { return m_layerID; }
|
||||
const int GetSceneID() const { return m_sceneID; }
|
||||
const unsigned int GetLayerID() const { return m_layerID; }
|
||||
|
||||
constexpr void SetSceneID(const int32_t sceneID) noexcept { m_sceneID = sceneID; }
|
||||
constexpr void SetLayerID(const uint32_t layerID) noexcept { m_layerID = layerID; }
|
||||
void SetSceneID(const int sceneID) { m_sceneID = sceneID; }
|
||||
void SetLayerID(const unsigned int layerID) { m_layerID = layerID; }
|
||||
|
||||
private:
|
||||
int32_t m_sceneID;
|
||||
uint32_t m_layerID;
|
||||
int m_sceneID;
|
||||
unsigned int m_layerID;
|
||||
};
|
||||
|
||||
struct LWOZONEID {
|
||||
public:
|
||||
constexpr const LWOMAPID& GetMapID() const noexcept { return m_MapID; }
|
||||
constexpr const LWOINSTANCEID& GetInstanceID() const noexcept { return m_InstanceID; }
|
||||
constexpr const LWOCLONEID& GetCloneID() const noexcept { return m_CloneID; }
|
||||
const LWOMAPID& GetMapID() const { return m_MapID; }
|
||||
const LWOINSTANCEID& GetInstanceID() const { return m_InstanceID; }
|
||||
const LWOCLONEID& GetCloneID() const { return m_CloneID; }
|
||||
|
||||
//In order: def constr, constr, assign op
|
||||
constexpr LWOZONEID() noexcept = default;
|
||||
constexpr LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) noexcept { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; }
|
||||
constexpr LWOZONEID(const LWOZONEID& replacement) noexcept { *this = replacement; }
|
||||
LWOZONEID() { m_MapID = LWOMAPID_INVALID; m_InstanceID = LWOINSTANCEID_INVALID; m_CloneID = LWOCLONEID_INVALID; }
|
||||
LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; }
|
||||
LWOZONEID(const LWOZONEID& replacement) { *this = replacement; }
|
||||
|
||||
private:
|
||||
LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc...
|
||||
LWOINSTANCEID m_InstanceID = LWOINSTANCEID_INVALID; //Instances host the same world, but on a different dWorld process.
|
||||
LWOCLONEID m_CloneID = LWOCLONEID_INVALID; //To differentiate between "your property" and "my property". Always 0 for non-prop worlds.
|
||||
LWOMAPID m_MapID; //1000 for VE, 1100 for AG, etc...
|
||||
LWOINSTANCEID m_InstanceID; //Instances host the same world, but on a different dWorld process.
|
||||
LWOCLONEID m_CloneID; //To differentiate between "your property" and "my property". Always 0 for non-prop worlds.
|
||||
};
|
||||
|
||||
constexpr LWOSCENEID LWOSCENEID_INVALID = -1;
|
||||
const LWOSCENEID LWOSCENEID_INVALID = -1;
|
||||
|
||||
struct LWONameValue {
|
||||
uint32_t length = 0; //!< The length of the name
|
||||
std::u16string name; //!< The name
|
||||
|
||||
LWONameValue() = default;
|
||||
LWONameValue(void) {}
|
||||
|
||||
LWONameValue(const std::u16string& name) {
|
||||
this->name = name;
|
||||
this->length = static_cast<uint32_t>(name.length());
|
||||
}
|
||||
|
||||
~LWONameValue(void) {}
|
||||
};
|
||||
|
||||
struct FriendData {
|
||||
@@ -128,7 +129,7 @@ public:
|
||||
LWOOBJID friendID;
|
||||
std::string friendName;
|
||||
|
||||
void Serialize(RakNet::BitStream& bitStream) const {
|
||||
void Serialize(RakNet::BitStream& bitStream) {
|
||||
bitStream.Write<uint8_t>(isOnline);
|
||||
bitStream.Write<uint8_t>(isBestFriend);
|
||||
bitStream.Write<uint8_t>(isFTP);
|
||||
@@ -146,11 +147,11 @@ public:
|
||||
if (size > maxSize) size = maxSize;
|
||||
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
bitStream.Write<uint16_t>(friendName[i]);
|
||||
bitStream.Write(static_cast<uint16_t>(friendName[i]));
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < remSize; ++j) {
|
||||
bitStream.Write<uint16_t>(0);
|
||||
bitStream.Write(static_cast<uint16_t>(0));
|
||||
}
|
||||
|
||||
bitStream.Write<uint32_t>(0); //???
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,20 @@ enum class ePermissionMap : uint64_t {
|
||||
* The character has restricted chat access, bit 6.
|
||||
*/
|
||||
RestrictedChatAccess = 0x1 << 6,
|
||||
|
||||
//
|
||||
// Combined permissions
|
||||
//
|
||||
|
||||
/**
|
||||
* The character is marked as 'old', restricted from trade and mail.
|
||||
*/
|
||||
Old = RestrictedTradeAccess | RestrictedMailAccess,
|
||||
|
||||
/**
|
||||
* The character is soft banned, restricted from trade, mail, and chat.
|
||||
*/
|
||||
SoftBanned = RestrictedTradeAccess | RestrictedMailAccess | RestrictedChatAccess,
|
||||
};
|
||||
|
||||
#endif //!__EPERMISSIONMAP__H__
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#ifndef __EPETABILITYTYPE__H__
|
||||
#define __EPETABILITYTYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class ePetAbilityType : uint32_t {
|
||||
Invalid,
|
||||
GoToObject,
|
||||
JumpOnObject,
|
||||
DigAtPosition
|
||||
};
|
||||
|
||||
#endif //!__EPETABILITYTYPE__H__
|
||||
@@ -166,8 +166,7 @@ enum ePlayerFlag : int32_t {
|
||||
NJ_LIGHTNING_SPINJITZU = 2031,
|
||||
NJ_ICE_SPINJITZU = 2032,
|
||||
NJ_FIRE_SPINJITZU = 2033,
|
||||
NJ_WU_SHOW_DAILY_CHEST = 2099,
|
||||
DLU_SKIP_CINEMATICS = 1'000'000,
|
||||
NJ_WU_SHOW_DAILY_CHEST = 2099
|
||||
};
|
||||
|
||||
#endif //!__EPLAYERFLAG__H__
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef __EQUICKBUILDSTATE__H__
|
||||
#define __EQUICKBUILDSTATE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eQuickBuildState : uint32_t {
|
||||
OPEN,
|
||||
COMPLETED = 2,
|
||||
RESETTING = 4,
|
||||
BUILDING,
|
||||
INCOMPLETE
|
||||
};
|
||||
|
||||
|
||||
#endif //!__EQUICKBUILDSTATE__H__
|
||||
15
dCommon/dEnums/eRebuildState.h
Normal file
15
dCommon/dEnums/eRebuildState.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef __EREBUILDSTATE__H__
|
||||
#define __EREBUILDSTATE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eRebuildState : uint32_t {
|
||||
OPEN,
|
||||
COMPLETED = 2,
|
||||
RESETTING = 4,
|
||||
BUILDING,
|
||||
INCOMPLETE
|
||||
};
|
||||
|
||||
|
||||
#endif //!__EREBUILDSTATE__H__
|
||||
@@ -34,7 +34,7 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PLATFORM_BOUNDARY,
|
||||
MODULE,
|
||||
ARCADE,
|
||||
HAVOK_VEHICLE_PHYSICS,
|
||||
VEHICLE_PHYSICS, // Havok demo based
|
||||
MOVEMENT_AI,
|
||||
EXHIBIT,
|
||||
OVERHEAD_ICON,
|
||||
@@ -50,11 +50,11 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PROPERTY_ENTRANCE,
|
||||
FX,
|
||||
PROPERTY_MANAGEMENT,
|
||||
VEHICLE_PHYSICS,
|
||||
VEHICLE_PHYSICS_NEW, // internal physics based on havok
|
||||
PHYSICS_SYSTEM,
|
||||
QUICK_BUILD,
|
||||
SWITCH,
|
||||
MINI_GAME_CONTROL,
|
||||
ZONE_CONTROL, // Minigame
|
||||
CHANGLING,
|
||||
CHOICE_BUILD,
|
||||
PACKAGE,
|
||||
@@ -101,12 +101,12 @@ enum class eReplicaComponentType : uint32_t {
|
||||
TRADE,
|
||||
USER_CONTROL,
|
||||
IGNORE_LIST,
|
||||
MULTI_ZONE_ENTRANCE,
|
||||
ROCKET_LAUNCH_LUP,
|
||||
BUFF_REAL, // the real buff component, should just be name BUFF
|
||||
INTERACTION_MANAGER,
|
||||
DONATION_VENDOR,
|
||||
COMBAT_MEDIATOR,
|
||||
ACHIEVEMENT_VENDOR,
|
||||
COMMENDATION_VENDOR,
|
||||
GATE_RUSH_CONTROL,
|
||||
RAIL_ACTIVATOR,
|
||||
ROLLER,
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef __EVENDORTRANSACTIONRESULT__
|
||||
#define __EVENDORTRANSACTIONRESULT__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eVendorTransactionResult : uint32_t {
|
||||
SELL_SUCCESS = 0,
|
||||
SELL_FAIL,
|
||||
PURCHASE_SUCCESS,
|
||||
PURCHASE_FAIL,
|
||||
DONATION_FAIL,
|
||||
DONATION_FULL
|
||||
};
|
||||
|
||||
#endif // !__EVENDORTRANSACTIONRESULT__
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
enum class eWorldMessageType : uint32_t {
|
||||
VALIDATION = 1, // Session info
|
||||
CHARACTER_LIST_REQUEST,
|
||||
@@ -38,14 +36,7 @@ enum class eWorldMessageType : uint32_t {
|
||||
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<eWorldMessageType> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 91;
|
||||
GM_SET_FREE_TRIAL_STATUS
|
||||
};
|
||||
|
||||
#endif //!__EWORLDMESSAGETYPE__H__
|
||||
|
||||
110
dCommon/dLogger.cpp
Normal file
110
dCommon/dLogger.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "dLogger.h"
|
||||
|
||||
dLogger::dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements) {
|
||||
m_logToConsole = logToConsole;
|
||||
m_logDebugStatements = logDebugStatements;
|
||||
m_outpath = outpath;
|
||||
|
||||
#ifdef _WIN32
|
||||
mFile = std::ofstream(m_outpath);
|
||||
if (!mFile) { printf("Couldn't open %s for writing!\n", outpath.c_str()); }
|
||||
#else
|
||||
fp = fopen(outpath.c_str(), "wt");
|
||||
if (fp == NULL) { printf("Couldn't open %s for writing!\n", outpath.c_str()); }
|
||||
#endif
|
||||
}
|
||||
|
||||
dLogger::~dLogger() {
|
||||
#ifdef _WIN32
|
||||
mFile.close();
|
||||
#else
|
||||
if (fp != nullptr) {
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void dLogger::vLog(const char* format, va_list args) {
|
||||
#ifdef _WIN32
|
||||
time_t t = time(NULL);
|
||||
struct tm time;
|
||||
localtime_s(&time, &t);
|
||||
char timeStr[70];
|
||||
strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", &time);
|
||||
char message[2048];
|
||||
vsnprintf(message, 2048, format, args);
|
||||
|
||||
if (m_logToConsole) std::cout << "[" << timeStr << "] " << message;
|
||||
mFile << "[" << timeStr << "] " << message;
|
||||
#else
|
||||
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);
|
||||
|
||||
if (m_logToConsole) {
|
||||
fputs("[", stdout);
|
||||
fputs(timeStr, stdout);
|
||||
fputs("] ", stdout);
|
||||
fputs(message, stdout);
|
||||
}
|
||||
|
||||
if (fp != nullptr) {
|
||||
fputs("[", fp);
|
||||
fputs(timeStr, fp);
|
||||
fputs("] ", fp);
|
||||
fputs(message, fp);
|
||||
} else {
|
||||
printf("Logger not initialized!\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void dLogger::LogBasic(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vLog(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void dLogger::LogBasic(const std::string& message) {
|
||||
LogBasic(message.c_str());
|
||||
}
|
||||
|
||||
void dLogger::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 dLogger::Log(const std::string& className, const std::string& message) {
|
||||
Log(className.c_str(), message.c_str());
|
||||
}
|
||||
|
||||
void dLogger::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 dLogger::LogDebug(const std::string& className, const std::string& message) {
|
||||
LogDebug(className.c_str(), message.c_str());
|
||||
}
|
||||
|
||||
void dLogger::Flush() {
|
||||
#ifdef _WIN32
|
||||
mFile.flush();
|
||||
#else
|
||||
if (fp != nullptr) {
|
||||
std::fflush(fp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
38
dCommon/dLogger.h
Normal file
38
dCommon/dLogger.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <ctime>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
class dLogger {
|
||||
public:
|
||||
dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements);
|
||||
~dLogger();
|
||||
|
||||
void SetLogToConsole(bool logToConsole) { m_logToConsole = logToConsole; }
|
||||
void SetLogDebugStatements(bool logDebugStatements) { m_logDebugStatements = logDebugStatements; }
|
||||
void vLog(const char* format, va_list args);
|
||||
|
||||
void LogBasic(const std::string& message);
|
||||
void LogBasic(const char* format, ...);
|
||||
void Log(const char* className, const char* format, ...);
|
||||
void Log(const std::string& className, const std::string& message);
|
||||
void LogDebug(const std::string& className, const std::string& message);
|
||||
void LogDebug(const char* className, const char* format, ...);
|
||||
|
||||
void Flush();
|
||||
|
||||
const bool GetIsLoggingToConsole() const { return m_logToConsole; }
|
||||
|
||||
private:
|
||||
bool m_logDebugStatements;
|
||||
bool m_logToConsole;
|
||||
std::string m_outpath;
|
||||
std::ofstream mFile;
|
||||
|
||||
#ifndef _WIN32
|
||||
//Glorious linux can run with SPEED:
|
||||
FILE* fp = nullptr;
|
||||
#endif
|
||||
};
|
||||
@@ -4,13 +4,9 @@
|
||||
// Static Variables
|
||||
static CppSQLite3DB* conn = new CppSQLite3DB();
|
||||
|
||||
// Status Variables
|
||||
bool CDClientDatabase::isConnected = false;
|
||||
|
||||
//! Opens a connection with the CDClient
|
||||
void CDClientDatabase::Connect(const std::string& filename) {
|
||||
conn->open(filename.c_str());
|
||||
isConnected = true;
|
||||
}
|
||||
|
||||
//! Queries the CDClient
|
||||
@@ -13,12 +13,16 @@
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
// Enable this to cache all entries in each table for fast access, comes with more memory cost
|
||||
//#define CDCLIENT_CACHE_ALL
|
||||
|
||||
/*!
|
||||
\file CDClientDatabase.hpp
|
||||
\brief An interface between the CDClient.sqlite file and the server
|
||||
*/
|
||||
|
||||
//! The CDClient Database namespace
|
||||
namespace CDClientDatabase {
|
||||
/**
|
||||
* Boolean defining the connection status of CDClient
|
||||
*/
|
||||
extern bool isConnected;
|
||||
|
||||
//! Opens a connection with the CDClient
|
||||
/*!
|
||||
@@ -1,163 +0,0 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "CDActivityRewardsTable.h"
|
||||
#include "CDAnimationsTable.h"
|
||||
#include "CDBehaviorParameterTable.h"
|
||||
#include "CDBehaviorTemplateTable.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDCurrencyTableTable.h"
|
||||
#include "CDDestructibleComponentTable.h"
|
||||
#include "CDEmoteTable.h"
|
||||
#include "CDInventoryComponentTable.h"
|
||||
#include "CDItemComponentTable.h"
|
||||
#include "CDItemSetsTable.h"
|
||||
#include "CDItemSetSkillsTable.h"
|
||||
#include "CDLevelProgressionLookupTable.h"
|
||||
#include "CDLootMatrixTable.h"
|
||||
#include "CDLootTableTable.h"
|
||||
#include "CDMissionNPCComponentTable.h"
|
||||
#include "CDMissionTasksTable.h"
|
||||
#include "CDMissionsTable.h"
|
||||
#include "CDObjectSkillsTable.h"
|
||||
#include "CDObjectsTable.h"
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
#include "CDRebuildComponentTable.h"
|
||||
#include "CDScriptComponentTable.h"
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
#include "CDVendorComponentTable.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "CDPackageComponentTable.h"
|
||||
#include "CDProximityMonitorComponentTable.h"
|
||||
#include "CDMovementAIComponentTable.h"
|
||||
#include "CDBrickIDTableTable.h"
|
||||
#include "CDRarityTableTable.h"
|
||||
#include "CDMissionEmailTable.h"
|
||||
#include "CDRewardsTable.h"
|
||||
#include "CDPropertyEntranceComponentTable.h"
|
||||
#include "CDPropertyTemplateTable.h"
|
||||
#include "CDFeatureGatingTable.h"
|
||||
#include "CDRailActivatorComponent.h"
|
||||
#include "CDRewardCodesTable.h"
|
||||
#include "CDPetComponentTable.h"
|
||||
|
||||
#include <exception>
|
||||
|
||||
#ifndef CDCLIENT_CACHE_ALL
|
||||
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
|
||||
// A vanilla CDClient takes about 46MB of memory + the regular world data.
|
||||
// # define CDCLIENT_CACHE_ALL
|
||||
#endif // CDCLIENT_CACHE_ALL
|
||||
|
||||
#ifdef CDCLIENT_CACHE_ALL
|
||||
#define CDCLIENT_DONT_CACHE_TABLE(x) x
|
||||
#else
|
||||
#define CDCLIENT_DONT_CACHE_TABLE(x)
|
||||
#endif
|
||||
|
||||
class CDClientConnectionException : public std::exception {
|
||||
public:
|
||||
virtual const char* what() const throw() {
|
||||
return "CDClientDatabase is not connected!";
|
||||
}
|
||||
};
|
||||
|
||||
// Using a macro to reduce repetitive code and issues from copy and paste.
|
||||
// As a note, ## in a macro is used to concatenate two tokens together.
|
||||
|
||||
#define SPECIALIZE_TABLE_STORAGE(table) \
|
||||
template<> typename table::StorageType& CDClientManager::GetEntriesMutable<table>() { return table##Entries; };
|
||||
|
||||
#define DEFINE_TABLE_STORAGE(table) namespace { table::StorageType table##Entries; }; SPECIALIZE_TABLE_STORAGE(table)
|
||||
|
||||
DEFINE_TABLE_STORAGE(CDActivityRewardsTable);
|
||||
DEFINE_TABLE_STORAGE(CDActivitiesTable);
|
||||
DEFINE_TABLE_STORAGE(CDAnimationsTable);
|
||||
DEFINE_TABLE_STORAGE(CDBehaviorParameterTable);
|
||||
DEFINE_TABLE_STORAGE(CDBehaviorTemplateTable);
|
||||
DEFINE_TABLE_STORAGE(CDBrickIDTableTable);
|
||||
DEFINE_TABLE_STORAGE(CDComponentsRegistryTable);
|
||||
DEFINE_TABLE_STORAGE(CDCurrencyTableTable);
|
||||
DEFINE_TABLE_STORAGE(CDDestructibleComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDEmoteTableTable);
|
||||
DEFINE_TABLE_STORAGE(CDFeatureGatingTable);
|
||||
DEFINE_TABLE_STORAGE(CDInventoryComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDItemComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDItemSetSkillsTable);
|
||||
DEFINE_TABLE_STORAGE(CDItemSetsTable);
|
||||
DEFINE_TABLE_STORAGE(CDLevelProgressionLookupTable);
|
||||
DEFINE_TABLE_STORAGE(CDLootMatrixTable);
|
||||
DEFINE_TABLE_STORAGE(CDLootTableTable);
|
||||
DEFINE_TABLE_STORAGE(CDMissionEmailTable);
|
||||
DEFINE_TABLE_STORAGE(CDMissionNPCComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDMissionTasksTable);
|
||||
DEFINE_TABLE_STORAGE(CDMissionsTable);
|
||||
DEFINE_TABLE_STORAGE(CDMovementAIComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDObjectSkillsTable);
|
||||
DEFINE_TABLE_STORAGE(CDObjectsTable);
|
||||
DEFINE_TABLE_STORAGE(CDPhysicsComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDPackageComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDPetComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDProximityMonitorComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDPropertyEntranceComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDPropertyTemplateTable);
|
||||
DEFINE_TABLE_STORAGE(CDRailActivatorComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDRarityTableTable);
|
||||
DEFINE_TABLE_STORAGE(CDRebuildComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDRewardCodesTable);
|
||||
DEFINE_TABLE_STORAGE(CDRewardsTable);
|
||||
DEFINE_TABLE_STORAGE(CDScriptComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
||||
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDZoneTableTable);
|
||||
|
||||
void CDClientManager::LoadValuesFromDatabase() {
|
||||
if (!CDClientDatabase::isConnected) throw CDClientConnectionException();
|
||||
|
||||
CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
|
||||
CDActivitiesTable::Instance().LoadValuesFromDatabase();
|
||||
CDCLIENT_DONT_CACHE_TABLE(CDAnimationsTable::Instance().LoadValuesFromDatabase());
|
||||
CDBehaviorParameterTable::Instance().LoadValuesFromDatabase();
|
||||
CDBehaviorTemplateTable::Instance().LoadValuesFromDatabase();
|
||||
CDBrickIDTableTable::Instance().LoadValuesFromDatabase();
|
||||
CDCLIENT_DONT_CACHE_TABLE(CDComponentsRegistryTable::Instance().LoadValuesFromDatabase());
|
||||
CDCurrencyTableTable::Instance().LoadValuesFromDatabase();
|
||||
CDDestructibleComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDEmoteTableTable::Instance().LoadValuesFromDatabase();
|
||||
CDFeatureGatingTable::Instance().LoadValuesFromDatabase();
|
||||
CDInventoryComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDCLIENT_DONT_CACHE_TABLE(CDItemComponentTable::Instance().LoadValuesFromDatabase());
|
||||
CDItemSetSkillsTable::Instance().LoadValuesFromDatabase();
|
||||
CDItemSetsTable::Instance().LoadValuesFromDatabase();
|
||||
CDLevelProgressionLookupTable::Instance().LoadValuesFromDatabase();
|
||||
CDCLIENT_DONT_CACHE_TABLE(CDLootMatrixTable::Instance().LoadValuesFromDatabase());
|
||||
CDCLIENT_DONT_CACHE_TABLE(CDLootTableTable::Instance().LoadValuesFromDatabase());
|
||||
CDMissionEmailTable::Instance().LoadValuesFromDatabase();
|
||||
CDMissionNPCComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDMissionTasksTable::Instance().LoadValuesFromDatabase();
|
||||
CDMissionsTable::Instance().LoadValuesFromDatabase();
|
||||
CDMovementAIComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDObjectSkillsTable::Instance().LoadValuesFromDatabase();
|
||||
CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase());
|
||||
CDPhysicsComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDPackageComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDPetComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDPropertyTemplateTable::Instance().LoadValuesFromDatabase();
|
||||
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDRarityTableTable::Instance().LoadValuesFromDatabase();
|
||||
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDRewardCodesTable::Instance().LoadValuesFromDatabase();
|
||||
CDRewardsTable::Instance().LoadValuesFromDatabase();
|
||||
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
||||
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDZoneTableTable::Instance().LoadValuesFromDatabase();
|
||||
}
|
||||
|
||||
void CDClientManager::LoadValuesFromDefaults() {
|
||||
LOG("Loading default CDClient tables!");
|
||||
|
||||
CDPetComponentTable::Instance().LoadValuesFromDefaults();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef __CDCLIENTMANAGER__H__
|
||||
#define __CDCLIENTMANAGER__H__
|
||||
|
||||
#define UNUSED_TABLE(v)
|
||||
|
||||
/**
|
||||
* Initialize the CDClient tables so they are all loaded into memory.
|
||||
*/
|
||||
namespace CDClientManager {
|
||||
void LoadValuesFromDatabase();
|
||||
void LoadValuesFromDefaults();
|
||||
|
||||
/**
|
||||
* Fetch a table from CDClient
|
||||
*
|
||||
* @tparam Table type to fetch
|
||||
* @return A pointer to the requested table.
|
||||
*/
|
||||
template<typename T>
|
||||
T* GetTable();
|
||||
|
||||
/**
|
||||
* Fetch a table from CDClient
|
||||
* Note: Calling this function without a template specialization in CDClientManager.cpp will cause a linker error.
|
||||
*
|
||||
* @tparam Table type to fetch
|
||||
* @return A pointer to the requested table.
|
||||
*/
|
||||
template<typename T>
|
||||
typename T::StorageType& GetEntriesMutable();
|
||||
};
|
||||
|
||||
|
||||
// These are included after the CDClientManager namespace declaration as CDTable as of Jan 29 2024 relies on CDClientManager in Templated code.
|
||||
#include "CDTable.h"
|
||||
|
||||
#include "Singleton.h"
|
||||
|
||||
template<typename T>
|
||||
T* CDClientManager::GetTable() {
|
||||
return &T::Instance();
|
||||
};
|
||||
|
||||
#endif //!__CDCLIENTMANAGER__H__
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
struct CDActivities {
|
||||
uint32_t ActivityID;
|
||||
uint32_t locStatus;
|
||||
uint32_t instanceMapID;
|
||||
uint32_t minTeams;
|
||||
uint32_t maxTeams;
|
||||
uint32_t minTeamSize;
|
||||
uint32_t maxTeamSize;
|
||||
uint32_t waitTime;
|
||||
uint32_t startDelay;
|
||||
bool requiresUniqueData;
|
||||
uint32_t leaderboardType;
|
||||
bool localize;
|
||||
int32_t optionalCostLOT;
|
||||
int32_t optionalCostCount;
|
||||
bool showUIRewards;
|
||||
uint32_t CommunityActivityFlagID;
|
||||
std::string gate_version;
|
||||
bool noTeamLootOnDeath;
|
||||
float optionalPercentage;
|
||||
};
|
||||
|
||||
class CDActivitiesTable : public CDTable<CDActivitiesTable, std::vector<CDActivities>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDActivities> Query(std::function<bool(CDActivities)> predicate);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
struct CDActivityRewards {
|
||||
uint32_t objectTemplate; //!< The object template (?)
|
||||
uint32_t ActivityRewardIndex; //!< The activity reward index
|
||||
int32_t activityRating; //!< The activity rating
|
||||
uint32_t LootMatrixIndex; //!< The loot matrix index
|
||||
uint32_t CurrencyIndex; //!< The currency index
|
||||
uint32_t ChallengeRating; //!< The challenge rating
|
||||
std::string description; //!< The description
|
||||
};
|
||||
|
||||
class CDActivityRewardsTable : public CDTable<CDActivityRewardsTable, std::vector<CDActivityRewards>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDActivityRewards> Query(std::function<bool(CDActivityRewards)> predicate);
|
||||
};
|
||||
@@ -1,117 +0,0 @@
|
||||
#include "CDAnimationsTable.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "Game.h"
|
||||
|
||||
|
||||
void CDAnimationsTable::LoadValuesFromDatabase() {
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Animations");
|
||||
auto& animations = GetEntriesMutable();
|
||||
while (!tableData.eof()) {
|
||||
std::string animation_type = tableData.getStringField("animation_type", "");
|
||||
DluAssert(!animation_type.empty());
|
||||
AnimationGroupID animationGroupID = tableData.getIntField("animationGroupID", -1);
|
||||
DluAssert(animationGroupID != -1);
|
||||
|
||||
CDAnimation entry;
|
||||
entry.animation_name = tableData.getStringField("animation_name", "");
|
||||
entry.chance_to_play = tableData.getFloatField("chance_to_play", 1.0f);
|
||||
UNUSED_COLUMN(entry.min_loops = tableData.getIntField("min_loops", 0);)
|
||||
UNUSED_COLUMN(entry.max_loops = tableData.getIntField("max_loops", 0);)
|
||||
entry.animation_length = tableData.getFloatField("animation_length", 0.0f);
|
||||
UNUSED_COLUMN(entry.hideEquip = tableData.getIntField("hideEquip", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.ignoreUpperBody = tableData.getIntField("ignoreUpperBody", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.restartable = tableData.getIntField("restartable", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.face_animation_name = tableData.getStringField("face_animation_name", "");)
|
||||
UNUSED_COLUMN(entry.priority = tableData.getFloatField("priority", 0.0f);)
|
||||
UNUSED_COLUMN(entry.blendTime = tableData.getFloatField("blendTime", 0.0f);)
|
||||
|
||||
animations[CDAnimationKey(animation_type, animationGroupID)].push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
bool CDAnimationsTable::CacheData(CppSQLite3Statement& queryToCache) {
|
||||
auto tableData = queryToCache.execQuery();
|
||||
// If we received a bad lookup, cache it anyways so we do not run the query again.
|
||||
if (tableData.eof()) return false;
|
||||
auto& animations = GetEntriesMutable();
|
||||
|
||||
do {
|
||||
std::string animation_type = tableData.getStringField("animation_type", "");
|
||||
DluAssert(!animation_type.empty());
|
||||
AnimationGroupID animationGroupID = tableData.getIntField("animationGroupID", -1);
|
||||
DluAssert(animationGroupID != -1);
|
||||
|
||||
CDAnimation entry;
|
||||
entry.animation_name = tableData.getStringField("animation_name", "");
|
||||
entry.chance_to_play = tableData.getFloatField("chance_to_play", 1.0f);
|
||||
UNUSED_COLUMN(entry.min_loops = tableData.getIntField("min_loops", 0);)
|
||||
UNUSED_COLUMN(entry.max_loops = tableData.getIntField("max_loops", 0);)
|
||||
entry.animation_length = tableData.getFloatField("animation_length", 0.0f);
|
||||
UNUSED_COLUMN(entry.hideEquip = tableData.getIntField("hideEquip", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.ignoreUpperBody = tableData.getIntField("ignoreUpperBody", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.restartable = tableData.getIntField("restartable", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.face_animation_name = tableData.getStringField("face_animation_name", "");)
|
||||
UNUSED_COLUMN(entry.priority = tableData.getFloatField("priority", 0.0f);)
|
||||
UNUSED_COLUMN(entry.blendTime = tableData.getFloatField("blendTime", 0.0f);)
|
||||
|
||||
animations[CDAnimationKey(animation_type, animationGroupID)].push_back(entry);
|
||||
tableData.nextRow();
|
||||
} while (!tableData.eof());
|
||||
|
||||
tableData.finalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDAnimationsTable::CacheAnimations(const CDAnimationKey animationKey) {
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Animations WHERE animationGroupID = ? and animation_type = ?");
|
||||
query.bind(1, static_cast<int32_t>(animationKey.second));
|
||||
query.bind(2, animationKey.first.c_str());
|
||||
auto& animations = GetEntriesMutable();
|
||||
// If we received a bad lookup, cache it anyways so we do not run the query again.
|
||||
if (!CacheData(query)) {
|
||||
animations[animationKey];
|
||||
}
|
||||
}
|
||||
|
||||
void CDAnimationsTable::CacheAnimationGroup(AnimationGroupID animationGroupID) {
|
||||
auto& animations = GetEntriesMutable();
|
||||
auto animationEntryCached = animations.find(CDAnimationKey("", animationGroupID));
|
||||
if (animationEntryCached != animations.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Animations WHERE animationGroupID = ?");
|
||||
query.bind(1, static_cast<int32_t>(animationGroupID));
|
||||
|
||||
// Cache the query so we don't run the query again.
|
||||
CacheData(query);
|
||||
animations[CDAnimationKey("", animationGroupID)];
|
||||
}
|
||||
|
||||
std::optional<CDAnimation> CDAnimationsTable::GetAnimation(const AnimationID& animationType, const std::string& previousAnimationName, const AnimationGroupID animationGroupID) {
|
||||
auto& animations = GetEntriesMutable();
|
||||
CDAnimationKey animationKey(animationType, animationGroupID);
|
||||
auto animationEntryCached = animations.find(animationKey);
|
||||
if (animationEntryCached == animations.end()) {
|
||||
this->CacheAnimations(animationKey);
|
||||
}
|
||||
|
||||
auto animationEntry = animations.find(animationKey);
|
||||
// If we have only one animation, return it regardless of the chance to play.
|
||||
if (animationEntry->second.size() == 1) {
|
||||
return animationEntry->second.front();
|
||||
}
|
||||
auto randomAnimation = GeneralUtils::GenerateRandomNumber<float>(0, 1);
|
||||
|
||||
for (auto& animationEntry : animationEntry->second) {
|
||||
randomAnimation -= animationEntry.chance_to_play;
|
||||
// This is how the client gets the random animation.
|
||||
if (animationEntry.animation_name != previousAnimationName && randomAnimation <= 0.0f) return animationEntry;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
#include "CDBehaviorParameterTable.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
namespace {
|
||||
std::unordered_map<std::string, uint32_t> m_ParametersList;
|
||||
};
|
||||
|
||||
uint64_t GetKey(const uint32_t behaviorID, const uint32_t parameterID) {
|
||||
uint64_t key = behaviorID;
|
||||
key <<= 31U;
|
||||
key |= parameterID;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void CDBehaviorParameterTable::LoadValuesFromDatabase() {
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter");
|
||||
auto& entries = GetEntriesMutable();
|
||||
while (!tableData.eof()) {
|
||||
uint32_t behaviorID = tableData.getIntField("behaviorID", -1);
|
||||
auto candidateStringToAdd = std::string(tableData.getStringField("parameterID", ""));
|
||||
auto parameter = m_ParametersList.find(candidateStringToAdd);
|
||||
uint32_t parameterId;
|
||||
if (parameter != m_ParametersList.end()) {
|
||||
parameterId = parameter->second;
|
||||
} else {
|
||||
parameterId = m_ParametersList.insert(std::make_pair(candidateStringToAdd, m_ParametersList.size())).first->second;
|
||||
}
|
||||
uint64_t hash = GetKey(behaviorID, parameterId);
|
||||
float value = tableData.getFloatField("value", -1.0f);
|
||||
|
||||
entries.insert(std::make_pair(hash, value));
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
float CDBehaviorParameterTable::GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue) {
|
||||
auto parameterID = m_ParametersList.find(name);
|
||||
if (parameterID == m_ParametersList.end()) return defaultValue;
|
||||
auto hash = GetKey(behaviorID, parameterID->second);
|
||||
|
||||
// Search for specific parameter
|
||||
auto& entries = GetEntriesMutable();
|
||||
auto it = entries.find(hash);
|
||||
return it != entries.end() ? it->second : defaultValue;
|
||||
}
|
||||
|
||||
std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
uint64_t hashBase = behaviorID;
|
||||
std::map<std::string, float> returnInfo;
|
||||
for (auto& [parameterString, parameterId] : m_ParametersList) {
|
||||
uint64_t hash = GetKey(hashBase, parameterId);
|
||||
auto infoCandidate = entries.find(hash);
|
||||
if (infoCandidate != entries.end()) {
|
||||
returnInfo.insert(std::make_pair(parameterString, infoCandidate->second));
|
||||
}
|
||||
}
|
||||
return returnInfo;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
typedef uint64_t BehaviorParameterHash;
|
||||
typedef float BehaviorParameterValue;
|
||||
|
||||
class CDBehaviorParameterTable : public CDTable<CDBehaviorParameterTable, std::unordered_map<BehaviorParameterHash, BehaviorParameterValue>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
float GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue = 0);
|
||||
|
||||
std::map<std::string, float> GetParametersByBehaviorID(uint32_t behaviorID);
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
struct CDBehaviorTemplate {
|
||||
uint32_t behaviorID; //!< The Behavior ID
|
||||
uint32_t templateID; //!< The Template ID (LOT)
|
||||
uint32_t effectID; //!< The Effect ID attached
|
||||
std::unordered_set<std::string>::iterator effectHandle; //!< The effect handle
|
||||
};
|
||||
|
||||
class CDBehaviorTemplateTable : public CDTable<CDBehaviorTemplateTable, std::unordered_map<uint32_t, CDBehaviorTemplate>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
const CDBehaviorTemplate GetByBehaviorID(uint32_t behaviorID);
|
||||
};
|
||||
@@ -1,53 +0,0 @@
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
void CDComponentsRegistryTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ComponentsRegistry");
|
||||
auto& entries = GetEntriesMutable();
|
||||
while (!tableData.eof()) {
|
||||
CDComponentsRegistry entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.component_type = static_cast<eReplicaComponentType>(tableData.getIntField("component_type", 0));
|
||||
entry.component_id = tableData.getIntField("component_id", -1);
|
||||
|
||||
entries.insert_or_assign(static_cast<uint64_t>(entry.component_type) << 32 | static_cast<uint64_t>(entry.id), entry.component_id);
|
||||
entries.insert_or_assign(entry.id, 0);
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
auto exists = entries.find(id);
|
||||
if (exists != entries.end()) {
|
||||
auto iter = entries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
|
||||
return iter == entries.end() ? defaultValue : iter->second;
|
||||
}
|
||||
|
||||
// Now get the data. Get all components of this entity so we dont do a query for each component
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ComponentsRegistry WHERE id = ?;");
|
||||
query.bind(1, static_cast<int32_t>(id));
|
||||
|
||||
auto tableData = query.execQuery();
|
||||
|
||||
while (!tableData.eof()) {
|
||||
CDComponentsRegistry entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.component_type = static_cast<eReplicaComponentType>(tableData.getIntField("component_type", 0));
|
||||
entry.component_id = tableData.getIntField("component_id", -1);
|
||||
|
||||
entries.insert_or_assign(static_cast<uint64_t>(entry.component_type) << 32 | static_cast<uint64_t>(entry.id), entry.component_id);
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
entries.insert_or_assign(id, 0);
|
||||
|
||||
auto iter = entries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
|
||||
|
||||
return iter == entries.end() ? defaultValue : iter->second;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
struct CDComponentsRegistry {
|
||||
uint32_t id; //!< The LOT is used as the ID
|
||||
eReplicaComponentType component_type; //!< See ComponentTypes enum for values
|
||||
uint32_t component_id; //!< The ID used within the component's table (0 may either mean it's non-networked, or that the ID is actually 0
|
||||
};
|
||||
|
||||
|
||||
class CDComponentsRegistryTable : public CDTable<CDComponentsRegistryTable, std::unordered_map<uint64_t, uint32_t>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0);
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
/*!
|
||||
\file CDCurrencyTableTable.hpp
|
||||
\brief Contains data for the CurrencyTable table
|
||||
*/
|
||||
|
||||
//! CurrencyTable Struct
|
||||
struct CDCurrencyTable {
|
||||
uint32_t currencyIndex; //!< The Currency Index
|
||||
uint32_t npcminlevel; //!< The minimum level of the npc
|
||||
uint32_t minvalue; //!< The minimum currency
|
||||
uint32_t maxvalue; //!< The maximum currency
|
||||
uint32_t id; //!< The ID of the currency index
|
||||
};
|
||||
|
||||
//! CurrencyTable table
|
||||
class CDCurrencyTableTable : public CDTable<CDCurrencyTableTable, std::vector<CDCurrencyTable>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDCurrencyTable> Query(std::function<bool(CDCurrencyTable)> predicate);
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
struct CDDestructibleComponent {
|
||||
uint32_t id; //!< The component ID from the ComponentsRegistry Table
|
||||
int32_t faction; //!< The Faction ID of the object
|
||||
std::string factionList; //!< A list of the faction IDs
|
||||
int32_t life; //!< The amount of life of the object
|
||||
uint32_t imagination; //!< The amount of imagination of the object
|
||||
int32_t LootMatrixIndex; //!< The Loot Matrix Index
|
||||
int32_t CurrencyIndex; //!< The Currency Index
|
||||
uint32_t level; //!< ???
|
||||
float armor; //!< The amount of armor of the object
|
||||
uint32_t death_behavior; //!< The behavior ID of the death behavior
|
||||
bool isnpc; //!< Whether or not the object is an NPC
|
||||
uint32_t attack_priority; //!< ???
|
||||
bool isSmashable; //!< Whether or not the object is smashable
|
||||
int32_t difficultyLevel; //!< ???
|
||||
};
|
||||
|
||||
class CDDestructibleComponentTable : public CDTable<CDDestructibleComponentTable, std::vector<CDDestructibleComponent>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDDestructibleComponent> Query(std::function<bool(CDDestructibleComponent)> predicate);
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
struct CDFeatureGating {
|
||||
std::string featureName;
|
||||
int32_t major;
|
||||
int32_t current;
|
||||
int32_t minor;
|
||||
std::string description;
|
||||
|
||||
bool operator>=(const CDFeatureGating& b) const {
|
||||
return (this->major > b.major) ||
|
||||
(this->major == b.major && this->current > b.current) ||
|
||||
(this->major == b.major && this->current == b.current && this->minor >= b.minor);
|
||||
}
|
||||
};
|
||||
|
||||
class CDFeatureGatingTable : public CDTable<CDFeatureGatingTable, std::vector<CDFeatureGating>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDFeatureGating> Query(std::function<bool(CDFeatureGating)> predicate);
|
||||
|
||||
bool FeatureUnlocked(const CDFeatureGating& feature) const;
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
struct CDInventoryComponent {
|
||||
uint32_t id; //!< The component ID for this object
|
||||
uint32_t itemid; //!< The LOT of the object
|
||||
uint32_t count; //!< The count of the items the object has
|
||||
bool equip; //!< Whether or not to equip the item
|
||||
};
|
||||
|
||||
class CDInventoryComponentTable : public CDTable<CDInventoryComponentTable, std::vector<CDInventoryComponent>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDInventoryComponent> Query(std::function<bool(CDInventoryComponent)> predicate);
|
||||
};
|
||||
@@ -1,61 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
struct CDItemComponent {
|
||||
uint32_t id; //!< The Component ID
|
||||
std::string equipLocation; //!< The equip location
|
||||
uint32_t baseValue; //!< The monetary base value of the item
|
||||
bool isKitPiece; //!< Whether or not the item belongs to a kit
|
||||
uint32_t rarity; //!< The rarity of the item
|
||||
uint32_t itemType; //!< The item type
|
||||
int64_t itemInfo; //!< The item info
|
||||
bool inLootTable; //!< Whether or not the item is in a loot table
|
||||
bool inVendor; //!< Whether or not the item is in a vendor inventory
|
||||
bool isUnique; //!< ???
|
||||
bool isBOP; //!< ???
|
||||
bool isBOE; //!< ???
|
||||
uint32_t reqFlagID; //!< User must have completed this flag to get the item
|
||||
uint32_t reqSpecialtyID; //!< ???
|
||||
uint32_t reqSpecRank; //!< ???
|
||||
uint32_t reqAchievementID; //!< The required achievement must be completed
|
||||
uint32_t stackSize; //!< The stack size of the item
|
||||
uint32_t color1; //!< Something to do with item color...
|
||||
uint32_t decal; //!< The decal of the item
|
||||
uint32_t offsetGroupID; //!< Something to do with group IDs
|
||||
uint32_t buildTypes; //!< Something to do with building
|
||||
std::string reqPrecondition; //!< The required precondition
|
||||
uint32_t animationFlag; //!< The Animation Flag
|
||||
uint32_t equipEffects; //!< The effect played when the item is equipped
|
||||
bool readyForQA; //!< ???
|
||||
uint32_t itemRating; //!< ???
|
||||
bool isTwoHanded; //!< Whether or not the item is double handed
|
||||
uint32_t minNumRequired; //!< Maybe the minimum number required for a mission, or to own this object?
|
||||
uint32_t delResIndex; //!< ???
|
||||
uint32_t currencyLOT; //!< ???
|
||||
uint32_t altCurrencyCost; //!< ???
|
||||
std::string subItems; //!< A comma seperate string of sub items (maybe for multi-itemed things like faction test gear set)
|
||||
UNUSED(std::string audioEventUse); //!< ???
|
||||
bool noEquipAnimation; //!< Whether or not there is an equip animation
|
||||
uint32_t commendationLOT; //!< The commendation LOT
|
||||
uint32_t commendationCost; //!< The commendation cost
|
||||
UNUSED(std::string audioEquipMetaEventSet); //!< ???
|
||||
std::string currencyCosts; //!< Used for crafting
|
||||
UNUSED(std::string ingredientInfo); //!< Unused
|
||||
uint32_t locStatus; //!< ???
|
||||
uint32_t forgeType; //!< Forge Type
|
||||
float SellMultiplier; //!< Something to do with early vendors perhaps (but replaced)
|
||||
};
|
||||
|
||||
class CDItemComponentTable : public CDTable<CDItemComponentTable, std::map<uint32_t, CDItemComponent>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
static std::map<LOT, uint32_t> ParseCraftingCurrencies(const CDItemComponent& itemComponent);
|
||||
|
||||
// Gets an entry by ID
|
||||
const CDItemComponent& GetItemComponentByID(uint32_t skillID);
|
||||
|
||||
static CDItemComponent Default;
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
struct CDItemSetSkills {
|
||||
uint32_t SkillSetID; //!< The skill set ID
|
||||
uint32_t SkillID; //!< The skill ID
|
||||
uint32_t SkillCastType; //!< The skill cast type
|
||||
};
|
||||
|
||||
class CDItemSetSkillsTable : public CDTable<CDItemSetSkillsTable, std::vector<CDItemSetSkills>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDItemSetSkills> Query(std::function<bool(CDItemSetSkills)> predicate);
|
||||
|
||||
std::vector<CDItemSetSkills> GetBySkillID(uint32_t SkillSetID);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user