Compare commits

..

12 Commits

765 changed files with 13406 additions and 16098 deletions

View File

@@ -3,8 +3,8 @@ Dockerfile
*.md *.md
logo.png logo.png
versions.txt versions.txt
build.sh
docker-compose.yml docker-compose.yml
.env .env
docker/__pycache__ docker/__pycache__
.env.example .env.example
build

View File

@@ -1,7 +1,9 @@
# Full path to the LEGO Universe client # Full path to the LEGO Universe client
CLIENT_PATH=./client CLIENT_PATH=/Users/someuser/LEGO Universe
# Can improve build time
BUILD_THREADS=1
# Updates NET_VERSION in CMakeVariables.txt # Updates NET_VERSION in CMakeVariables.txt
NET_VERSION=171022 BUILD_VERSION=171022
# make sure this is a long random string # make sure this is a long random string
# grab a "SHA 256-bit Key" from here: https://keygen.io/ # grab a "SHA 256-bit Key" from here: https://keygen.io/
ACCOUNT_MANAGER_SECRET= ACCOUNT_MANAGER_SECRET=
@@ -10,5 +12,6 @@ EXTERNAL_IP=localhost
# Database values # Database values
# Be careful with special characters here. It is more safe to use normal characters and/or numbers. # Be careful with special characters here. It is more safe to use normal characters and/or numbers.
MARIADB_USER=darkflame MARIADB_USER=darkflame
MARIADB_PASSWORD= MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_DATABASE=darkflame MARIADB_DATABASE=darkflame

View File

@@ -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 }}

1
.gitignore vendored
View File

@@ -122,4 +122,3 @@ docker/__pycache__
docker-compose.override.yml docker-compose.override.yml
!*Test.bin !*Test.bin
!cmake/*

6
.gitmodules vendored
View File

@@ -14,6 +14,6 @@
path = thirdparty/mariadb-connector-cpp path = thirdparty/mariadb-connector-cpp
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
ignore = dirty ignore = dirty
[submodule "thirdparty/magic_enum"] [submodule "thirdparty/AccountManager"]
path = thirdparty/magic_enum path = thirdparty/AccountManager
url = https://github.com/Neargye/magic_enum.git url = https://github.com/DarkflameUniverse/AccountManager

View File

@@ -2,8 +2,7 @@ cmake_minimum_required(VERSION 3.18)
project(Darkflame) project(Darkflame)
include(CTest) include(CTest)
set(CMAKE_CXX_STANDARD 20) set (CMAKE_CXX_STANDARD 17)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Read variables from file # Read variables from file
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables) FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
@@ -15,26 +14,30 @@ string(REPLACE "\n" ";" variables ${variables})
foreach(variable ${variables}) foreach(variable ${variables})
# If the string contains a #, skip it # If the string contains a #, skip it
if(NOT "${variable}" MATCHES "#") if(NOT "${variable}" MATCHES "#")
# Split the variable into name and value # Split the variable into name and value
string(REPLACE "=" ";" variable ${variable}) string(REPLACE "=" ";" variable ${variable})
# Check that the length of the variable is 2 (name and value) # Check that the length of the variable is 2 (name and value)
list(LENGTH variable length) list(LENGTH variable length)
if(${length} EQUAL 2) if(${length} EQUAL 2)
list(GET variable 0 variable_name) list(GET variable 0 variable_name)
list(GET variable 1 variable_value) list(GET variable 1 variable_value)
# Set the variable # Set the variable
set(${variable_name} ${variable_value}) set(${variable_name} ${variable_value})
# Add compiler definition
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}")
message(STATUS "Variable: ${variable_name} = ${variable_value}") message(STATUS "Variable: ${variable_name} = ${variable_value}")
endif() endif()
endif() endif()
endforeach() endforeach()
# Set the version # Set the version
set(PROJECT_VERSION "\"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"") set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
# Echo the version # Echo the version
message(STATUS "Version: ${PROJECT_VERSION}") message(STATUS "Version: ${PROJECT_VERSION}")
@@ -50,22 +53,19 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent. # Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
# Disabled no-register # Disabled no-register
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas. # Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
if(UNIX) if(UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC") if(APPLE)
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
else()
if(NOT 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 -static-libgcc -fPIC -lstdc++fs")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -lstdc++fs")
endif() endif()
if (__dynamic AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
endif() endif()
if (__ggdb)
if(${GGDB})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
endif() endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
elseif(MSVC) elseif(MSVC)
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now # Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
@@ -98,64 +98,47 @@ make_directory(${CMAKE_BINARY_DIR}/logs)
# Copy resource files on first build # Copy resource files on first build
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf") set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf")
message(STATUS "Checking resource file integrity") message(STATUS "Checking resource file integrity")
foreach (resource_file ${RESOURCE_FILES})
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) set(file_size 0)
if (EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size) file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
endif() endif()
if (${file_size} EQUAL 0)
if(${file_size} EQUAL 0)
configure_file( configure_file(
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file} ${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
COPYONLY COPYONLY
) )
message(STATUS "Moved " ${resource_file} " to project binary directory") message(STATUS "Moved " ${resource_file} " to project binary directory")
elseif(resource_file MATCHES ".ini") elseif (resource_file MATCHES ".ini")
message(STATUS "Checking " ${resource_file} " for missing config options") message(STATUS "Checking " ${resource_file} " for missing config options")
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents) 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})
string(REPLACE "\n" ";" current_file_contents ${current_file_contents}) string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
set(parsed_current_file_contents "") set(parsed_current_file_contents "")
# Remove comment lines so they do not interfere with the variable parsing # Remove comment lines so they do not interfere with the variable parsing
foreach(line ${current_file_contents}) foreach (line ${current_file_contents})
string(FIND ${line} "#" is_comment) string(FIND ${line} "#" is_comment)
if (NOT ${is_comment} EQUAL 0)
if(NOT ${is_comment} EQUAL 0)
string(APPEND parsed_current_file_contents ${line}) string(APPEND parsed_current_file_contents ${line})
endif() endif()
endforeach() endforeach()
file(READ ${CMAKE_SOURCE_DIR}/resources/${resource_file} depot_file_contents) 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})
string(REPLACE "\n" ";" depot_file_contents ${depot_file_contents}) string(REPLACE "\n" ";" depot_file_contents ${depot_file_contents})
set(line_to_add "") set(line_to_add "")
foreach (line ${depot_file_contents})
foreach(line ${depot_file_contents})
string(FIND ${line} "#" is_comment) string(FIND ${line} "#" is_comment)
if (NOT ${is_comment} EQUAL 0)
if(NOT ${is_comment} EQUAL 0)
string(REPLACE "=" ";" line_split ${line}) string(REPLACE "=" ";" line_split ${line})
list(GET line_split 0 variable_name) list(GET line_split 0 variable_name)
if (NOT ${parsed_current_file_contents} MATCHES ${variable_name})
if(NOT ${parsed_current_file_contents} MATCHES ${variable_name})
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file}) message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file})
set(line_to_add ${line_to_add} ${line}) set(line_to_add ${line_to_add} ${line})
foreach (line_to_append ${line_to_add})
foreach(line_to_append ${line_to_add})
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append}) file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
endforeach() endforeach()
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n") file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
endif() endif()
set(line_to_add "") set(line_to_add "")
else() else()
set(line_to_add ${line_to_add} ${line}) set(line_to_add ${line_to_add} ${line})
@@ -163,23 +146,21 @@ foreach(resource_file ${RESOURCE_FILES})
endforeach() endforeach()
endif() endif()
endforeach() endforeach()
message(STATUS "Resource file integrity check complete") 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 # 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) file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
endif()
# Copy vanity files on first build # Copy vanity files on first build
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml") set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
foreach(file ${VANITY_FILES}) foreach(file ${VANITY_FILES})
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY) configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
endforeach() endforeach()
@@ -187,18 +168,26 @@ endforeach()
# Move our migrations for MasterServer to run # Move our migrations for MasterServer to run
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql) file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
foreach(file ${SQL_FILES}) foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME) get_filename_component(file ${file} NAME)
configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file}) if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
configure_file(
${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file}
COPYONLY
)
endif()
endforeach() endforeach()
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql) file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)
foreach(file ${SQL_FILES}) foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME) get_filename_component(file ${file} NAME)
configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}) if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
configure_file(
${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}
COPYONLY
)
endif()
endforeach() endforeach()
# Create our list of include directories # Create our list of include directories
@@ -206,9 +195,7 @@ set(INCLUDED_DIRECTORIES
"dCommon" "dCommon"
"dCommon/dClient" "dCommon/dClient"
"dCommon/dEnums" "dCommon/dEnums"
"dChatFilter" "dChatFilter"
"dGame" "dGame"
"dGame/dBehaviors" "dGame/dBehaviors"
"dGame/dComponents" "dGame/dComponents"
@@ -219,53 +206,115 @@ set(INCLUDED_DIRECTORIES
"dGame/dPropertyBehaviors" "dGame/dPropertyBehaviors"
"dGame/dPropertyBehaviors/ControlBehaviorMessages" "dGame/dPropertyBehaviors/ControlBehaviorMessages"
"dGame/dUtilities" "dGame/dUtilities"
"dPhysics" "dPhysics"
"dNavigation" "dNavigation"
"dNavigation/dTerrain" "dNavigation/dTerrain"
"dZoneManager" "dZoneManager"
"dDatabase" "dDatabase"
"dDatabase/CDClientDatabase" "dDatabase/Tables"
"dDatabase/CDClientDatabase/CDClientTables"
"dDatabase/GameDatabase"
"dDatabase/GameDatabase/ITables"
"dDatabase/GameDatabase/MySQL"
"dDatabase/GameDatabase/MySQL/Tables"
"dNet" "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/raknet/Source"
"thirdparty/tinyxml2" "thirdparty/tinyxml2"
"thirdparty/recastnavigation" "thirdparty/recastnavigation"
"thirdparty/SQLite" "thirdparty/SQLite"
"thirdparty/cpplinq" "thirdparty/cpplinq"
"thirdparty/cpp-httplib"
"tests" "tests"
"tests/dCommonTests" "tests/dCommonTests"
"tests/dGameTests" "tests/dGameTests"
"tests/dGameTests/dComponentsTests" "tests/dGameTests/dComponentsTests"
) )
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux) # Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
if(APPLE) if (APPLE)
include_directories("/usr/local/include/") include_directories("/usr/local/include/")
endif() endif()
# Actually include the directories from our list if (WIN32)
foreach(dir ${INCLUDED_DIRECTORIES}) set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include")
include_directories(${PROJECT_SOURCE_DIR}/${dir}) elseif (UNIX)
endforeach() set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt")
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt")
if(NOT WIN32)
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt")
endif() endif()
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include") # 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()
# Add linking directories: # Add linking directories:
link_directories(${PROJECT_BINARY_DIR}) link_directories(${PROJECT_BINARY_DIR})
@@ -277,9 +326,8 @@ add_subdirectory(thirdparty)
file( file(
GLOB HEADERS_DDATABASE GLOB HEADERS_DDATABASE
LIST_DIRECTORIES false LIST_DIRECTORIES false
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/*.h ${PROJECT_SOURCE_DIR}/dDatabase/*.h
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables/*.h ${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables/*.h
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h ${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
) )
@@ -314,16 +362,15 @@ add_subdirectory(dGame)
add_subdirectory(dZoneManager) add_subdirectory(dZoneManager)
add_subdirectory(dNavigation) add_subdirectory(dNavigation)
add_subdirectory(dPhysics) add_subdirectory(dPhysics)
add_subdirectory(dServer)
# Create a list of common libraries shared between all binaries # Create a list of common libraries shared between all binaries
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp" "magic_enum") set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp")
# Add platform specific common libraries # Add platform specific common libraries
if(UNIX) if (UNIX)
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread") set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread")
if(NOT APPLE AND ${INCLUDE_BACKTRACE}) if (NOT APPLE AND __include_backtrace__)
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace") set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace")
endif() endif()
endif() endif()
@@ -334,6 +381,12 @@ add_subdirectory(dAuthServer)
add_subdirectory(dChatServer) add_subdirectory(dChatServer)
add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries
# Add our precompiled headers
target_precompile_headers(
dGame PRIVATE
${HEADERS_DGAME}
)
target_precompile_headers( target_precompile_headers(
dZoneManager PRIVATE dZoneManager PRIVATE
${HEADERS_DZONEMANAGER} ${HEADERS_DZONEMANAGER}
@@ -355,6 +408,6 @@ target_precompile_headers(
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>" "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
) )
if(${ENABLE_TESTING}) if (${__enable_testing__} MATCHES "1")
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()

View File

@@ -1,32 +1,20 @@
PROJECT_VERSION_MAJOR=1 PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=1 PROJECT_VERSION_MINOR=0
PROJECT_VERSION_PATCH=1 PROJECT_VERSION_PATCH=1
# LICENSE
LICENSE=AGPL-3.0
# Debugging # Debugging
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs. # Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
DYNAMIC=1 __dynamic=1
# Set __ggdb to 1 to enable the -ggdb flag for the linker, including more debug info.
# Set GGDB to 1 to enable the -ggdb flag for the linker, including more debug info. # __ggdb=1
# Do note, changing this will re-build the whole server # Set __include_backtrace__ to 1 to includes the backtrace library for better crashlogs.
GGDB=0 # __include_backtrace__=1
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
# Set INCLUDE_BACKTRACE to 1 to includes the backtrace library for better crashlogs. # __compile_backtrace__=1
# Do note, changing this will re-build the whole server
INCLUDE_BACKTRACE=0
# Set COMPILE_BACKTRACE to 1 to compile the backtrace library instead of using system libraries.
# Do note, changing this will re-build the whole server
COMPILE_BACKTRACE=0
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with. # Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with.
MARIADB_CONNECTOR_COMPILE_JOBS=1 __maria_db_connector_compile_jobs__=1
# When set to 1 and uncommented, compiling and linking testing folders and libraries will be done. # When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
ENABLE_TESTING=1 __enable_testing__=1
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default. # The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/ OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
# Whether or not to cache the entire CDClient Database into memory instead of lazy loading.
# 0 means to lazy load, all other values mean load the entire database.
CDCLIENT_CACHE_ALL=0

46
Docker.md Normal file
View 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
View 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.
![Docker Download Page](docker/images/Docker_Download_Page.png)
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.
![Docker Desktop Installer Configuration](docker/images/Docker_Desktop_Installer_Configuration.png)
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.
![WSL 2 download](docker/images/WSL_2_download.png)
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_
![Disable Dashboard Autostart](docker/images/DD_General_Settings.png)
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".
![Open PowerShell](docker/images/Open_Powershell.png)
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.
![setup done](docker/images/setup_finished.png)
19. Create an admin account by pasting `docker compose exec darkflame /app/MasterServer -a` and following the prompts.
![admin account creation](docker/images/Account_Creation.png)
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.
![server running](docker/images/Docker_Compose_Finished.png)
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.
![start stop buttons](docker/images/DD_Server_Startstop.png)

View File

@@ -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" ]

View File

@@ -23,9 +23,6 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s
### Supply of resource files ### Supply of resource files
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work. Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.
## Step by step walkthrough for a single-player server
If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.
## Steps to setup server ## Steps to setup server
* [Clone this repository](#clone-the-repository) * [Clone this repository](#clone-the-repository)
* [Install dependencies](#install-dependencies) * [Install dependencies](#install-dependencies)
@@ -37,7 +34,6 @@ If you would like a setup for a single player server only on a Windows machine,
* [Verify your setup](#verify-your-setup) * [Verify your setup](#verify-your-setup)
* [Running the server](#running-the-server) * [Running the server](#running-the-server)
* [User Guide](#user-guide) * [User Guide](#user-guide)
* [Docker](#docker)
## Clone the repository ## Clone the repository
If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win) If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win)
@@ -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. and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0.
### Linux Service
If you are running this on a linux based system, it will use your terminal to run the program interactively, preventing you using it for other tasks and requiring it to be open to run the server.
_Note: You could use screen or tmux instead for virtual terminals_
To run the server non-interactively, we can use a systemctl service by copying the following file:
```shell
cp ./systemd.example /etc/systemd/system/darkflame.service
```
Make sure to edit the file in `/etc/systemd/system/darkflame.service` and change the:
- `User` and `Group` to the user that runs the darkflame server.
- `ExecPath` to the full file path of the server executable.
To register, enable and start the service use the following commands:
- Reload the systemd manager configuration to make it aware of the new service file:
```shell
systemctl daemon-reload
```
- Start the service:
```shell
systemctl start darkflame.service
```
- Enable OR disable the service to start on boot using:
```shell
systemctl enable darkflame.service
systemctl disable darkflame.service
```
- Verify that the service is running without errors:
```shell
systemctl status darkflame.service
```
- You can also restart, stop, or check the logs of the service using journalctl
```shell
systemctl restart darkflame.service
systemctl stop darkflame.service
journalctl -xeu darkflame.service
```
### First admin user ### First admin user
Run `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users! Run `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users!
@@ -327,7 +285,6 @@ Below are known good SHA256 checksums of the client:
* `0d862f71eedcadc4494c4358261669721b40b2131101cbd6ef476c5a6ec6775b` (unpacked client, includes extra locales, rar compressed) * `0d862f71eedcadc4494c4358261669721b40b2131101cbd6ef476c5a6ec6775b` (unpacked client, includes extra locales, rar compressed)
If the returned hash matches one of the lines above then you can continue with setting up the server. If you are using a fully downloaded and complete client from live, then it will work, but the hash above may not match. Otherwise you must obtain a full install of LEGO® Universe 1.10.64. If the returned hash matches one of the lines above then you can continue with setting up the server. If you are using a fully downloaded and complete client from live, then it will work, but the hash above may not match. Otherwise you must obtain a full install of LEGO® Universe 1.10.64.
You must also make absolutely sure your LEGO Universe client is not in a Windows OneDrive. DLU is not and will not support a client being stored in a OneDrive, so ensure you have moved the client outside of that location.
### Darkflame Universe Client ### Darkflame Universe Client
Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there. Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there.
@@ -348,62 +305,6 @@ certutil -hashfile <file> SHA1
Known good *SHA1* checksum of the Darkflame Universe client: Known good *SHA1* checksum of the Darkflame Universe client:
- `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed) - `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed)
# Docker
The Darkflame Server is automatically built and published as a Docker Container / [OCI](https://opencontainers.org/) Image to the GitHub Container Registry at:
[`ghcr.io/darkflameuniverse/darkflameserver`](https://github.com/DarkflameUniverse/DarkflameServer/pkgs/container/darkflameserver).
## Compose
You can use the `docker-compose` tool to [setup a MariaDB database](#database-setup), run the Darkflame Server and manage it with [Nexus Dashboard](https://github.com/DarkflameUniverse/NexusDashboard) all
at once. For that:
- [Install Docker Desktop](https://docs.docker.com/get-docker/)
- Open the directory that contains your LU Client
- If the `legouniverse.exe` is in a subfolder called `client`, you're good to go. There may also be a folder `versions`.
- Otherwise, create a new `client` folder and move the exe and everything else (e.g. `res` and `locale`) in there. This is necessary to work around a bug in the client that will prevent that you to log back in after getting disconnected.
- Download the [docker-compose.yml](docker-compose.yml) file and place it next to `client`.
- Download the [.env.example](.env.example) file and place it next to `client` with the file name `.env`
- You may get warnings that this name starts with a dot, acknowledge those, this is intentional. Depending on your operating system, you may need to activate showing hidden files (e.g. Ctrl-H in Gnome on Linux) and/or file extensions ("File name extensions" in the "View" tab on Windows).
- Update the `ACCOUNT_MANAGER_SECRET` and `MARIADB_PASSWORD` with strong random passwords.
- Use a password generator like <https://keygen.io>
- Avoid `:` and `@` characters
- Once the database user is created, changing the password will not update it, so the server will just fail to connect.
- Set `EXTERNAL_IP` to your LAN IP or public IP if you want to host the game for friends & family
- Open a terminal in the folder with the `docker-compose.yml` and `client`
- Run `docker compose up -d`
- This might require `sudo` on Linux, and a recent version of [docker compose](https://docs.docker.com/compose/install/)
- Run `docker exec -it dlu-darkflameserver-1 /app/MasterServer -a` and follow the instructions to create the initial admin account
- Open <http://localhost:8000> to access Nexus Dashboard with the admin account to create normal users
- Set `AUTHSERVERIP=0:localhost` in `client/boot.cfg`
- Replace `localhost` with the value of `EXTERNAL_IP` if you changed that earlier.
- Also make sure `UGCUSE3DSERVICES=7:` is set to `0`
- Launch `legouniverse.exe`
## Standalone
This assumes that you have a database deployed to your host or in another docker container.
A basic deployment of this contianer would look like:
```sh
# example docker contianer deployment
docker run -it \
-v /path/to/configs/:/app/configs \
-v /path/to/logs/:/app/logs \
-v /path/to/dumps/:/app/dumps \
-v /path/to/res:/app/res:ro \
-v /path/to/resServer:/app/resServer \
-e DUMP_FOLDER=/app/dumps \
-p 1001:1001/udp \
-p 2005:2005/udp \
-p 3000-3300:3000-3300/udp \
ghcr.io/darkflameuniverse/darkflameserver:latest
```
You will need to replace the `/path/to/`'s to reflect the paths on your host.
Any config option in the `.ini`'s can be overridden with environmental variables: Ex: `log_to_console=1` from `shared_config.ini` would be overidden like `-e LOG_TO_CONSOLE=0`
# Development Documentation # Development Documentation
This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server
[Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html) [Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html)

View File

@@ -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()

View File

@@ -1,14 +1,13 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <ctime> #include <ctime>
#include <csignal>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
//DLU Includes: //DLU Includes:
#include "dCommonVars.h" #include "dCommonVars.h"
#include "dServer.h" #include "dServer.h"
#include "Logger.h" #include "dLogger.h"
#include "Database.h" #include "Database.h"
#include "dConfig.h" #include "dConfig.h"
#include "Diagnostics.h" #include "Diagnostics.h"
@@ -16,7 +15,7 @@
//RakNet includes: //RakNet includes:
#include "RakNetDefines.h" #include "RakNetDefines.h"
#include "MessageIdentifiers.h" #include <MessageIdentifiers.h>
//Auth includes: //Auth includes:
#include "AuthPackets.h" #include "AuthPackets.h"
@@ -25,17 +24,15 @@
#include "eAuthMessageType.h" #include "eAuthMessageType.h"
#include "Game.h" #include "Game.h"
#include "Server.h"
namespace Game { namespace Game {
Logger* logger = nullptr; dLogger* logger = nullptr;
dServer* server = nullptr; dServer* server = nullptr;
dConfig* config = nullptr; dConfig* config = nullptr;
Game::signal_t lastSignal = 0; bool shouldShutdown = false;
std::mt19937 randomEngine; std::mt19937 randomEngine;
} }
dLogger* SetupLogger();
void HandlePacket(Packet* packet); void HandlePacket(Packet* packet);
int main(int argc, char** argv) { int main(int argc, char** argv) {
@@ -45,23 +42,29 @@ int main(int argc, char** argv) {
Diagnostics::SetProcessFileName(argv[0]); Diagnostics::SetProcessFileName(argv[0]);
Diagnostics::Initialize(); Diagnostics::Initialize();
std::signal(SIGINT, Game::OnSignal);
std::signal(SIGTERM, Game::OnSignal);
Game::config = new dConfig("authconfig.ini");
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Server::SetupLogger("AuthServer"); Game::logger = SetupLogger();
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
LOG("Starting Auth server..."); //Read our config:
LOG("Version: %s", PROJECT_VERSION); Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string());
LOG("Compiled on: %s", __TIMESTAMP__); Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
Game::logger->Log("AuthServer", "Starting Auth server...");
Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__);
//Connect to the MySQL Database
std::string mysql_host = Game::config->GetValue("mysql_host");
std::string mysql_database = Game::config->GetValue("mysql_database");
std::string mysql_username = Game::config->GetValue("mysql_username");
std::string mysql_password = Game::config->GetValue("mysql_password");
try { try {
Database::Connect(); Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
} catch (sql::SQLException& ex) { } catch (sql::SQLException& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s", ex.what());
Database::Destroy("AuthServer"); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
@@ -71,26 +74,25 @@ int main(int argc, char** argv) {
//Find out the master's IP: //Find out the master's IP:
std::string masterIP; std::string masterIP;
uint32_t masterPort = 1500; uint32_t masterPort = 1500;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto masterInfo = Database::Get()->GetMasterInfo(); auto res = stmt->executeQuery();
if (masterInfo) { while (res->next()) {
masterIP = masterInfo->ip; masterIP = res->getString(1).c_str();
masterPort = masterInfo->port; masterPort = res->getInt(2);
} }
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
delete res;
delete stmt;
Game::randomEngine = std::mt19937(time(0)); Game::randomEngine = std::mt19937(time(0));
//It's safe to pass 'localhost' here, as the IP is only used as the external IP. //It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 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. uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default.
std::string ourIP = "localhost"; if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients); if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
GeneralUtils::TryParse(Game::config->GetValue("auth_server_port"), ourPort);
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: //Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now(); auto t = std::chrono::high_resolution_clock::now();
@@ -101,18 +103,13 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0; uint32_t framesSinceLastSQLPing = 0;
AuthPackets::LoadClaimCodes(); while (!Game::shouldShutdown) {
Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) {
//Check if we're still connected to master: //Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) { if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++; framesSinceMasterDisconnect++;
if (framesSinceMasterDisconnect >= authFramerate) { if (framesSinceMasterDisconnect >= authFramerate)
LOG("No connection to master!");
break; //Exit our loop, shut down. break; //Exit our loop, shut down.
}
} else framesSinceMasterDisconnect = 0; } else framesSinceMasterDisconnect = 0;
//In world we'd update our other systems here. //In world we'd update our other systems here.
@@ -137,12 +134,16 @@ int main(int argc, char** argv) {
//Find out the master's IP for absolutely no reason: //Find out the master's IP for absolutely no reason:
std::string masterIP; std::string masterIP;
uint32_t masterPort; uint32_t masterPort;
auto masterInfo = Database::Get()->GetMasterInfo(); sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
if (masterInfo) { auto res = stmt->executeQuery();
masterIP = masterInfo->ip; while (res->next()) {
masterPort = masterInfo->port; masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
} }
delete res;
delete stmt;
framesSinceLastSQLPing = 0; framesSinceLastSQLPing = 0;
} else framesSinceLastSQLPing++; } else framesSinceLastSQLPing++;
@@ -151,7 +152,6 @@ int main(int argc, char** argv) {
std::this_thread::sleep_until(t); std::this_thread::sleep_until(t);
} }
LOG("Exited Main Loop! (signal %d)", Game::lastSignal);
//Delete our objects here: //Delete our objects here:
Database::Destroy("AuthServer"); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;
@@ -161,6 +161,18 @@ int main(int argc, char** argv) {
return EXIT_SUCCESS; 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) { void HandlePacket(Packet* packet) {
if (packet->length < 4) return; if (packet->length < 4) return;

View File

@@ -1,7 +1,2 @@
add_executable(AuthServer "AuthServer.cpp") add_executable(AuthServer "AuthServer.cpp")
target_link_libraries(AuthServer ${COMMON_LIBRARIES})
target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer)
target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer)
add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")

View File

@@ -8,7 +8,7 @@
#include <regex> #include <regex>
#include "dCommonVars.h" #include "dCommonVars.h"
#include "Logger.h" #include "dLogger.h"
#include "dConfig.h" #include "dConfig.h"
#include "Database.h" #include "Database.h"
#include "Game.h" #include "Game.h"
@@ -32,11 +32,15 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
} }
//Read player names that are ok as well: //Read player names that are ok as well:
auto approvedNames = Database::Get()->GetApprovedCharacterNames(); auto stmt = Database::CreatePreppedStmt("select name from charinfo;");
for (auto& name : approvedNames) { auto res = stmt->executeQuery();
std::transform(name.begin(), name.end(), name.begin(), ::tolower); //Transform to lowercase while (res->next()) {
m_ApprovedWords.push_back(CalculateHash(name)); std::string line = res->getString(1).c_str();
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
m_ApprovedWords.push_back(CalculateHash(line));
} }
delete res;
delete stmt;
} }
dChatFilter::~dChatFilter() { dChatFilter::~dChatFilter() {
@@ -140,7 +144,7 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace_back(position, originalSegment.length());
} }
position += originalSegment.length() + 1; position += segment.length() + 1;
} }
return listOfBadSegments; return listOfBadSegments;

View File

@@ -1,14 +1,10 @@
set(DCHATSERVER_SOURCES set(DCHATSERVER_SOURCES
"ChatIgnoreList.cpp"
"ChatPacketHandler.cpp" "ChatPacketHandler.cpp"
"PlayerContainer.cpp" "PlayerContainer.cpp"
) )
add_executable(ChatServer "ChatServer.cpp") add_executable(ChatServer "ChatServer.cpp")
add_library(dChatServer ${DCHATSERVER_SOURCES}) 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(dChatServer ${COMMON_LIBRARIES} dChatFilter)
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer) target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer)

View File

@@ -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);
}

View File

@@ -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

View File

@@ -4,9 +4,44 @@
#include "BitStream.h" #include "BitStream.h"
struct PlayerData; struct PlayerData;
enum class eAddFriendResponseType : uint8_t; enum class eAddFriendResponseType : uint8_t;
enum class eChatChannel : uint8_t {
SystemNotify,
SystemWarning,
SystemError,
Broadcast,
Local,
LocalNoanim,
Emote,
Private,
Team,
TeamLocal,
Guild,
GuildNotify,
Property,
Admin,
CombatDamage,
CombatHealing,
CombatLoot,
CombatExp,
CombatDeath,
General,
Trade,
Lfg,
User
};
enum class eChatMessageResponseCode : uint8_t {
Sent,
NotOnline,
GeneralError,
ReceivedNewWhisper,
NotFriends,
SenderFreeTrial,
ReceiverFreeTrial,
};
namespace ChatPacketHandler { namespace ChatPacketHandler {
void HandleFriendlistRequest(Packet* packet); void HandleFriendlistRequest(Packet* packet);
void HandleFriendRequest(Packet* packet); void HandleFriendRequest(Packet* packet);
@@ -24,18 +59,18 @@ namespace ChatPacketHandler {
void HandleTeamLootOption(Packet* packet); void HandleTeamLootOption(Packet* packet);
void HandleTeamStatusRequest(Packet* packet); void HandleTeamStatusRequest(Packet* packet);
void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender); void SendTeamInvite(PlayerData* receiver, PlayerData* sender);
void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName); void SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName); void SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID); void SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID);
void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID); void SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName); void SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID); void SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state. //FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
void SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend); void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend);
void SendFriendRequest(const PlayerData& receiver, const PlayerData& sender); void SendFriendRequest(PlayerData* receiver, PlayerData* sender);
void SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U); void SendFriendResponse(PlayerData* receiver, PlayerData* sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful); void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful);
}; };

View File

@@ -6,7 +6,7 @@
//DLU Includes: //DLU Includes:
#include "dCommonVars.h" #include "dCommonVars.h"
#include "dServer.h" #include "dServer.h"
#include "Logger.h" #include "dLogger.h"
#include "Database.h" #include "Database.h"
#include "dConfig.h" #include "dConfig.h"
#include "dChatFilter.h" #include "dChatFilter.h"
@@ -19,28 +19,29 @@
#include "eChatMessageType.h" #include "eChatMessageType.h"
#include "eChatInternalMessageType.h" #include "eChatInternalMessageType.h"
#include "eWorldMessageType.h" #include "eWorldMessageType.h"
#include "ChatIgnoreList.h"
#include "Game.h" #include "Game.h"
#include "Server.h"
//RakNet includes: //RakNet includes:
#include "RakNetDefines.h" #include "RakNetDefines.h"
#include "MessageIdentifiers.h" #include <MessageIdentifiers.h>
namespace Game { namespace Game {
Logger* logger = nullptr; dLogger* logger = nullptr;
dServer* server = nullptr; dServer* server = nullptr;
dConfig* config = nullptr; dConfig* config = nullptr;
dChatFilter* chatFilter = nullptr; dChatFilter* chatFilter = nullptr;
AssetManager* assetManager = nullptr; AssetManager* assetManager = nullptr;
Game::signal_t lastSignal = 0; bool shouldShutdown = false;
std::mt19937 randomEngine; std::mt19937 randomEngine;
PlayerContainer playerContainer;
} }
dLogger* SetupLogger();
void HandlePacket(Packet* packet); void HandlePacket(Packet* packet);
PlayerContainer playerContainer;
int main(int argc, char** argv) { int main(int argc, char** argv) {
constexpr uint32_t chatFramerate = mediumFramerate; constexpr uint32_t chatFramerate = mediumFramerate;
constexpr uint32_t chatFrameDelta = mediumFrameDelta; constexpr uint32_t chatFrameDelta = mediumFrameDelta;
@@ -48,20 +49,18 @@ int main(int argc, char** argv) {
Diagnostics::SetProcessFileName(argv[0]); Diagnostics::SetProcessFileName(argv[0]);
Diagnostics::Initialize(); Diagnostics::Initialize();
std::signal(SIGINT, Game::OnSignal);
std::signal(SIGTERM, Game::OnSignal);
Game::config = new dConfig("chatconfig.ini");
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Server::SetupLogger("ChatServer"); Game::logger = SetupLogger();
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
//Read our config: //Read our config:
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string());
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
LOG("Starting Chat server..."); Game::logger->Log("ChatServer", "Starting Chat server...");
LOG("Version: %s", PROJECT_VERSION); Game::logger->Log("ChatServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
LOG("Compiled on: %s", __TIMESTAMP__); Game::logger->Log("ChatServer", "Compiled on: %s", __TIMESTAMP__);
try { try {
std::string clientPathStr = Game::config->GetValue("client_location"); std::string clientPathStr = Game::config->GetValue("client_location");
@@ -73,16 +72,21 @@ int main(int argc, char** argv) {
Game::assetManager = new AssetManager(clientPath); Game::assetManager = new AssetManager(clientPath);
} catch (std::runtime_error& ex) { } catch (std::runtime_error& ex) {
LOG("Got an error while setting up assets: %s", ex.what()); Game::logger->Log("ChatServer", "Got an error while setting up assets: %s", ex.what());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
//Connect to the MySQL Database //Connect to the MySQL Database
std::string mysql_host = Game::config->GetValue("mysql_host");
std::string mysql_database = Game::config->GetValue("mysql_database");
std::string mysql_username = Game::config->GetValue("mysql_username");
std::string mysql_password = Game::config->GetValue("mysql_password");
try { try {
Database::Connect(); Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
} catch (sql::SQLException& ex) { } catch (sql::SQLException& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s", ex.what());
Database::Destroy("ChatServer"); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
@@ -92,30 +96,28 @@ int main(int argc, char** argv) {
//Find out the master's IP: //Find out the master's IP:
std::string masterIP; std::string masterIP;
uint32_t masterPort = 1000; uint32_t masterPort = 1000;
auto masterInfo = Database::Get()->GetMasterInfo(); sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
if (masterInfo) { auto res = stmt->executeQuery();
masterIP = masterInfo->ip; while (res->next()) {
masterPort = masterInfo->port; masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
} }
delete res;
delete stmt;
//It's safe to pass 'localhost' here, as the IP is only used as the external IP. //It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 999; uint32_t maxClients = 50;
uint32_t ourPort = 1501; uint32_t ourPort = 1501;
std::string ourIP = "localhost"; if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients); if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
GeneralUtils::TryParse(Game::config->GetValue("chat_server_port"), ourPort);
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::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);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
bool dontGenerateDCF = false;
GeneralUtils::TryParse(Game::config->GetValue("dont_generate_dcf"), dontGenerateDCF);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::randomEngine = std::mt19937(time(0)); Game::randomEngine = std::mt19937(time(0));
Game::playerContainer.Initialize();
//Run it until server gets a kill message from Master: //Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now(); auto t = std::chrono::high_resolution_clock::now();
Packet* packet = nullptr; Packet* packet = nullptr;
@@ -125,8 +127,7 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0; uint32_t framesSinceLastSQLPing = 0;
Game::logger->Flush(); // once immediately before main loop while (!Game::shouldShutdown) {
while (!Game::ShouldShutdown()) {
//Check if we're still connected to master: //Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) { if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++; framesSinceMasterDisconnect++;
@@ -157,13 +158,16 @@ int main(int argc, char** argv) {
//Find out the master's IP for absolutely no reason: //Find out the master's IP for absolutely no reason:
std::string masterIP; std::string masterIP;
uint32_t masterPort; uint32_t masterPort;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto masterInfo = Database::Get()->GetMasterInfo(); auto res = stmt->executeQuery();
if (masterInfo) { while (res->next()) {
masterIP = masterInfo->ip; masterIP = res->getString(1).c_str();
masterPort = masterInfo->port; masterPort = res->getInt(2);
} }
delete res;
delete stmt;
framesSinceLastSQLPing = 0; framesSinceLastSQLPing = 0;
} else framesSinceLastSQLPing++; } else framesSinceLastSQLPing++;
@@ -181,13 +185,25 @@ int main(int argc, char** argv) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
dLogger* SetupLogger() {
std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/ChatServer_" + std::to_string(time(nullptr)) + ".log")).string();
bool logToConsole = false;
bool logDebugStatements = false;
#ifdef _DEBUG
logToConsole = true;
logDebugStatements = true;
#endif
return new dLogger(logPath, logToConsole, logDebugStatements);
}
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
LOG("A server has disconnected, erasing their connected players from the list."); Game::logger->Log("ChatServer", "A server has disconnected, erasing their connected players from the list.");
} }
if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) { 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. if (packet->length < 4) return; // Nothing left to process. Need 4 bytes to continue.
@@ -195,19 +211,19 @@ void HandlePacket(Packet* packet) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) { if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) {
switch (static_cast<eChatInternalMessageType>(packet->data[3])) { switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION: case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
Game::playerContainer.InsertPlayer(packet); playerContainer.InsertPlayer(packet);
break; break;
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION: case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
Game::playerContainer.RemovePlayer(packet); playerContainer.RemovePlayer(packet);
break; break;
case eChatInternalMessageType::MUTE_UPDATE: case eChatInternalMessageType::MUTE_UPDATE:
Game::playerContainer.MuteUpdate(packet); playerContainer.MuteUpdate(packet);
break; break;
case eChatInternalMessageType::CREATE_TEAM: case eChatInternalMessageType::CREATE_TEAM:
Game::playerContainer.CreateTeamServer(packet); playerContainer.CreateTeamServer(packet);
break; break;
case eChatInternalMessageType::ANNOUNCEMENT: { case eChatInternalMessageType::ANNOUNCEMENT: {
@@ -218,7 +234,7 @@ void HandlePacket(Packet* packet) {
} }
default: default:
LOG("Unknown CHAT_INTERNAL id: %i", int(packet->data[3])); Game::logger->Log("ChatServer", "Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
} }
} }
@@ -229,15 +245,7 @@ void HandlePacket(Packet* packet) {
break; break;
case eChatMessageType::GET_IGNORE_LIST: case eChatMessageType::GET_IGNORE_LIST:
ChatIgnoreList::GetIgnoreList(packet); Game::logger->Log("ChatServer", "Asked for ignore list, but is unimplemented right now.");
break;
case eChatMessageType::ADD_IGNORE:
ChatIgnoreList::AddIgnore(packet);
break;
case eChatMessageType::REMOVE_IGNORE:
ChatIgnoreList::RemoveIgnore(packet);
break; break;
case eChatMessageType::TEAM_GET_STATUS: case eChatMessageType::TEAM_GET_STATUS:
@@ -295,19 +303,19 @@ void HandlePacket(Packet* packet) {
break; break;
default: default:
LOG("Unknown CHAT id: %i", int(packet->data[3])); Game::logger->Log("ChatServer", "Unknown CHAT id: %i", int(packet->data[3]));
} }
} }
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::WORLD) { if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::WORLD) {
switch (static_cast<eWorldMessageType>(packet->data[3])) { switch (static_cast<eWorldMessageType>(packet->data[3])) {
case eWorldMessageType::ROUTE_PACKET: { case eWorldMessageType::ROUTE_PACKET: {
LOG("Routing packet from world"); Game::logger->Log("ChatServer", "Routing packet from world");
break; break;
} }
default: default:
LOG("Unknown World id: %i", int(packet->data[3])); Game::logger->Log("ChatServer", "Unknown World id: %i", int(packet->data[3]));
} }
} }
} }

View File

@@ -3,57 +3,51 @@
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
#include "ChatPacketHandler.h" #include "ChatPacketHandler.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "BitStreamUtils.h" #include "BitstreamUtils.h"
#include "Database.h" #include "Database.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h" #include "eChatInternalMessageType.h"
#include "ChatPackets.h"
#include "dConfig.h"
void PlayerContainer::Initialize() { PlayerContainer::PlayerContainer() {
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends"), m_MaxNumberOfBestFriends);
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends"), m_MaxNumberOfFriends);
} }
PlayerContainer::~PlayerContainer() { PlayerContainer::~PlayerContainer() {
m_Players.clear(); mPlayers.clear();
}
TeamData::TeamData() {
lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
} }
void PlayerContainer::InsertPlayer(Packet* packet) { void PlayerContainer::InsertPlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
LWOOBJID playerId; PlayerData* data = new PlayerData();
if (!inStream.Read(playerId)) { inStream.Read(data->playerID);
LOG("Failed to read player ID");
return;
}
auto& data = m_Players[playerId];
data.playerID = playerId;
uint32_t len; uint32_t len;
inStream.Read<uint32_t>(len); inStream.Read<uint32_t>(len);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
char character; inStream.Read<char>(character); char character; inStream.Read<char>(character);
data.playerName += character; data->playerName += character;
} }
inStream.Read(data.zoneID); inStream.Read(data->zoneID);
inStream.Read(data.muteExpire); inStream.Read(data->muteExpire);
data.sysAddr = packet->systemAddress; data->sysAddr = packet->systemAddress;
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) { void PlayerContainer::RemovePlayer(Packet* packet) {
@@ -62,36 +56,42 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
inStream.Read(playerID); inStream.Read(playerID);
//Before they get kicked, we need to also send a message to their friends saying that they disconnected. //Before they get kicked, we need to also send a message to their friends saying that they disconnected.
const auto& player = GetPlayerData(playerID); std::unique_ptr<PlayerData> player(this->GetPlayerData(playerID));
if (!player) { if (player == nullptr) {
LOG("Failed to find user: %llu", playerID);
return; return;
} }
for (const auto& fr : player.friends) { for (auto& fr : player->friends) {
const auto& fd = this->GetPlayerData(fr.friendID); auto fd = this->GetPlayerData(fr.friendID);
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend); if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend);
} }
auto* team = GetTeam(playerID); auto* team = GetTeam(playerID);
if (team != nullptr) { if (team != nullptr) {
const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName); const auto memberName = GeneralUtils::UTF8ToUTF16(std::string(player->playerName.c_str()));
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId); auto* otherMember = GetPlayerData(memberId);
if (!otherMember) continue; if (otherMember == nullptr) continue;
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 }); ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
} }
} }
LOG("Removed user: %llu", playerID); Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID);
m_Players.erase(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) { void PlayerContainer::MuteUpdate(Packet* packet) {
@@ -101,15 +101,15 @@ void PlayerContainer::MuteUpdate(Packet* packet) {
time_t expire = 0; time_t expire = 0;
inStream.Read(expire); inStream.Read(expire);
auto& player = this->GetPlayerDataMutable(playerID); auto* player = this->GetPlayerData(playerID);
if (!player) { if (player == nullptr) {
LOG("Failed to find user: %llu", playerID); Game::logger->Log("PlayerContainer", "Failed to find user: %llu", playerID);
return; return;
} }
player.muteExpire = expire; player->muteExpire = expire;
BroadcastMuteUpdate(playerID, expire); BroadcastMuteUpdate(playerID, expire);
} }
@@ -146,7 +146,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) { void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE); BitstreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
bitStream.Write(player); bitStream.Write(player);
bitStream.Write(time); bitStream.Write(time);
@@ -185,7 +185,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) { TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
auto* team = new TeamData(); auto* team = new TeamData();
team->teamID = ++m_TeamIDCounter; team->teamID = ++mTeamIDCounter;
team->leaderID = leader; team->leaderID = leader;
team->local = local; team->local = local;
@@ -207,32 +207,24 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
} }
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
if (team->memberIDs.size() >= 4) {
LOG("Tried to add player to team that already had 4 players");
const auto& player = GetPlayerData(playerID);
if (!player) return;
ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!");
return;
}
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID); const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
if (index != team->memberIDs.end()) return; if (index != team->memberIDs.end()) return;
team->memberIDs.push_back(playerID); team->memberIDs.push_back(playerID);
const auto& leader = GetPlayerData(team->leaderID); auto* leader = GetPlayerData(team->leaderID);
const auto& member = GetPlayerData(playerID); auto* member = GetPlayerData(playerID);
if (!leader || !member) return; if (leader == nullptr || member == nullptr) return;
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName); const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName);
const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName); const auto memberName = GeneralUtils::UTF8ToUTF16(member->playerName);
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader.playerID, leader.zoneID, team->lootFlag, 0, 0, leaderName); ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName);
if (!team->local) { if (!team->local) {
ChatPacketHandler::SendTeamSetLeader(member, leader.playerID); ChatPacketHandler::SendTeamSetLeader(member, leader->playerID);
} else { } else {
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
} }
@@ -240,16 +232,16 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
UpdateTeamsOnWorld(team, false); UpdateTeamsOnWorld(team, false);
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId); auto* otherMember = GetPlayerData(memberId);
if (otherMember == member) continue; if (otherMember == member) continue;
const auto otherMemberName = GetName(memberId); const auto otherMemberName = GetName(memberId);
ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0)); ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0));
if (otherMember) { if (otherMember != nullptr) {
ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID); ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member->playerID, memberName, member->zoneID);
} }
} }
} }
@@ -259,9 +251,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba
if (index == team->memberIDs.end()) return; if (index == team->memberIDs.end()) return;
const auto& member = GetPlayerData(playerID); auto* member = GetPlayerData(playerID);
if (member && !silent) { if (member != nullptr && !silent) {
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
} }
@@ -272,9 +264,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba
continue; continue;
} }
const auto& otherMember = GetPlayerData(memberId); auto* otherMember = GetPlayerData(memberId);
if (!otherMember) continue; if (otherMember == nullptr) continue;
ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName); ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName);
} }
@@ -296,9 +288,9 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
team->leaderID = newLeader; team->leaderID = newLeader;
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId); auto* otherMember = GetPlayerData(memberId);
if (!otherMember) continue; if (otherMember == nullptr) continue;
ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader); ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader);
} }
@@ -310,14 +302,14 @@ void PlayerContainer::DisbandTeam(TeamData* team) {
if (index == mTeams.end()) return; if (index == mTeams.end()) return;
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId); auto* otherMember = GetPlayerData(memberId);
if (!otherMember) continue; if (otherMember == nullptr) continue;
const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember.playerName); const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember->playerName);
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember.playerID, memberName); ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName);
} }
UpdateTeamsOnWorld(team, true); UpdateTeamsOnWorld(team, true);
@@ -332,19 +324,19 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
if (index == mTeams.end()) return; if (index == mTeams.end()) return;
const auto& leader = GetPlayerData(team->leaderID); auto* leader = GetPlayerData(team->leaderID);
if (!leader) return; if (leader == nullptr) return;
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName); const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName);
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId); auto* otherMember = GetPlayerData(memberId);
if (!otherMember) continue; if (otherMember == nullptr) continue;
if (!team->local) { if (!team->local) {
ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader.zoneID, team->lootFlag, 0, leaderName); ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName);
} }
} }
@@ -353,14 +345,14 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE); BitstreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
bitStream.Write(team->teamID); bitStream.Write(team->teamID);
bitStream.Write(deleteTeam); bitStream.Write(deleteTeam);
if (!deleteTeam) { if (!deleteTeam) {
bitStream.Write(team->lootFlag); bitStream.Write(team->lootFlag);
bitStream.Write<char>(team->memberIDs.size()); bitStream.Write(static_cast<char>(team->memberIDs.size()));
for (const auto memberID : team->memberIDs) { for (const auto memberID : team->memberIDs) {
bitStream.Write(memberID); bitStream.Write(memberID);
} }
@@ -370,42 +362,23 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
} }
std::u16string PlayerContainer::GetName(LWOOBJID playerID) { std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
const auto iter = m_Names.find(playerID); const auto& pair = mNames.find(playerID);
if (iter == m_Names.end()) return u""; if (pair == mNames.end()) return u"";
return iter->second; return pair->second;
} }
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) { LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
LWOOBJID toReturn = LWOOBJID_EMPTY; for (const auto& pair : mNames) {
if (pair.second == playerName) {
for (const auto& [id, name] : m_Names) { return pair.first;
if (name == playerName) {
toReturn = id;
break;
} }
} }
return toReturn; return LWOOBJID_EMPTY;
} }
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) { bool PlayerContainer::GetIsMuted(PlayerData* data) {
return m_Players[playerID]; return data->muteExpire == 1 || data->muteExpire > time(NULL);
}
PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) {
for (auto& [id, player] : m_Players) {
if (!player) continue;
if (player.playerName == playerName) return player;
}
return m_Players[LWOOBJID_EMPTY];
}
const PlayerData& PlayerContainer::GetPlayerData(const LWOOBJID& playerID) {
return GetPlayerDataMutable(playerID);
}
const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) {
return GetPlayerDataMutable(playerName);
} }

View File

@@ -7,45 +7,17 @@
#include "dServer.h" #include "dServer.h"
#include <unordered_map> #include <unordered_map>
struct IgnoreData {
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName(name), playerId(id) {}
inline bool operator==(const std::string& other) const noexcept {
return playerName == other;
}
inline bool operator==(const LWOOBJID& other) const noexcept {
return playerId == other;
}
LWOOBJID playerId = LWOOBJID_EMPTY;
std::string playerName;
};
struct PlayerData { struct PlayerData {
operator bool() const noexcept { LWOOBJID playerID;
return playerID != LWOOBJID_EMPTY;
}
bool operator==(const PlayerData& other) const noexcept {
return playerID == other.playerID;
}
bool GetIsMuted() const {
return muteExpire == 1 || muteExpire > time(NULL);
}
SystemAddress sysAddr{};
LWOZONEID zoneID{};
LWOOBJID playerID = LWOOBJID_EMPTY;
time_t muteExpire = 0;
uint8_t countOfBestFriends = 0;
std::string playerName; std::string playerName;
SystemAddress sysAddr;
LWOZONEID zoneID;
std::vector<FriendData> friends; std::vector<FriendData> friends;
std::vector<IgnoreData> ignoredPlayers; time_t muteExpire;
uint8_t countOfBestFriends = 0;
}; };
struct TeamData { struct TeamData {
TeamData();
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
LWOOBJID leaderID = LWOOBJID_EMPTY; LWOOBJID leaderID = LWOOBJID_EMPTY;
std::vector<LWOOBJID> memberIDs{}; std::vector<LWOOBJID> memberIDs{};
@@ -56,19 +28,31 @@ struct TeamData {
class PlayerContainer { class PlayerContainer {
public: public:
PlayerContainer();
~PlayerContainer(); ~PlayerContainer();
void Initialize();
void InsertPlayer(Packet* packet); void InsertPlayer(Packet* packet);
void RemovePlayer(Packet* packet); void RemovePlayer(Packet* packet);
void MuteUpdate(Packet* packet); void MuteUpdate(Packet* packet);
void CreateTeamServer(Packet* packet); void CreateTeamServer(Packet* packet);
void BroadcastMuteUpdate(LWOOBJID player, time_t time); void BroadcastMuteUpdate(LWOOBJID player, time_t time);
const PlayerData& GetPlayerData(const LWOOBJID& playerID); PlayerData* GetPlayerData(const LWOOBJID& playerID) {
const PlayerData& GetPlayerData(const std::string& playerName); auto it = mPlayers.find(playerID);
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID); if (it != mPlayers.end()) return it->second;
PlayerData& GetPlayerDataMutable(const std::string& playerName); return nullptr;
}
PlayerData* GetPlayerData(const std::string& playerName) {
for (auto player : mPlayers) {
if (player.second) {
std::string pn = player.second->playerName.c_str();
if (pn == playerName) return player.second;
}
}
return nullptr;
}
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members); TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
TeamData* CreateTeam(LWOOBJID leader, bool local = false); TeamData* CreateTeam(LWOOBJID leader, bool local = false);
@@ -81,15 +65,14 @@ public:
void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam); void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
std::u16string GetName(LWOOBJID playerID); std::u16string GetName(LWOOBJID playerID);
LWOOBJID GetId(const std::u16string& playerName); LWOOBJID GetId(const std::u16string& playerName);
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; } bool GetIsMuted(PlayerData* data);
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return mPlayers; }
private: private:
LWOOBJID m_TeamIDCounter = 0; LWOOBJID mTeamIDCounter = 0;
std::map<LWOOBJID, PlayerData> m_Players; std::map<LWOOBJID, PlayerData*> mPlayers;
std::vector<TeamData*> mTeams; std::vector<TeamData*> mTeams;
std::unordered_map<LWOOBJID, std::u16string> m_Names; std::unordered_map<LWOOBJID, std::u16string> mNames;
uint32_t m_MaxNumberOfBestFriends = 5;
uint32_t m_MaxNumberOfFriends = 50;
}; };

View File

@@ -2,7 +2,7 @@
#define __AMF3__H__ #define __AMF3__H__
#include "dCommonVars.h" #include "dCommonVars.h"
#include "Logger.h" #include "dLogger.h"
#include "Game.h" #include "Game.h"
#include <unordered_map> #include <unordered_map>

View File

@@ -1,7 +1,7 @@
#include "AmfSerialize.h" #include "AmfSerialize.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
// Writes an AMFValue pointer to a RakNet::BitStream // Writes an AMFValue pointer to a RakNet::BitStream
template<> template<>
@@ -29,7 +29,7 @@ void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
break; break;
} }
default: { default: {
LOG("Encountered unwritable AMFType %i!", type); Game::logger->Log("AmfSerialize", "Encountered unwritable AMFType %i!", type);
} }
case eAmf::Undefined: case eAmf::Undefined:
case eAmf::Null: 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. * RakNet writes in the correct byte order - do not reverse this.
*/ */
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) { 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) { if (v < 0x00200000) {
b4 = b4 & 0x7F; b4 = b4 & 0x7F;
if (v > 0x7F) { if (v > 0x7F) {
unsigned char b3; unsigned char b3;
v = v >> 7; v = v >> 7;
b3 = static_cast<unsigned char>(v) | 0x80; b3 = ((unsigned char)(v)) | 0x80;
if (v > 0x7F) { if (v > 0x7F) {
unsigned char b2; unsigned char b2;
v = v >> 7; v = v >> 7;
b2 = static_cast<unsigned char>(v) | 0x80; b2 = ((unsigned char)(v)) | 0x80;
bs->Write(b2); bs->Write(b2);
} }
@@ -76,11 +76,11 @@ void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
unsigned char b3; unsigned char b3;
v = v >> 8; v = v >> 8;
b3 = static_cast<unsigned char>(v) | 0x80; b3 = ((unsigned char)(v)) | 0x80;
v = v >> 7; v = v >> 7;
b2 = static_cast<unsigned char>(v) | 0x80; b2 = ((unsigned char)(v)) | 0x80;
v = v >> 7; v = v >> 7;
b1 = static_cast<unsigned char>(v) | 0x80; b1 = ((unsigned char)(v)) | 0x80;
bs->Write(b1); bs->Write(b1);
bs->Write(b2); 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. * RakNet writes in the correct byte order - do not reverse this.
*/ */
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) { void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
WriteFlagNumber(bs, static_cast<uint32_t>(str.size())); WriteFlagNumber(bs, (uint32_t)str.size());
bs->Write(str.c_str(), static_cast<uint32_t>(str.size())); bs->Write(str.c_str(), (uint32_t)str.size());
} }
/** /**

View File

@@ -4,7 +4,7 @@
#include "Amf3.h" #include "Amf3.h"
// RakNet // RakNet
#include "BitStream.h" #include <BitStream.h>
/*! /*!
\file AmfSerialize.h \file AmfSerialize.h

View File

@@ -1,6 +1,14 @@
#include "BinaryIO.h" #include "BinaryIO.h"
#include <string> #include <string>
void BinaryIO::WriteString(const std::string& stringToWrite, std::ofstream& outstream) {
//BinaryWrite(outstream, uint32_t(stringToWrite.length()));
for (size_t i = 0; i < size_t(stringToWrite.length()); ++i) {
BinaryIO::BinaryWrite(outstream, stringToWrite[i]);
}
}
//For reading null-terminated strings //For reading null-terminated strings
std::string BinaryIO::ReadString(std::istream& instream) { std::string BinaryIO::ReadString(std::istream& instream) {
std::string toReturn; std::string toReturn;
@@ -15,3 +23,36 @@ std::string BinaryIO::ReadString(std::istream& instream) {
return toReturn; return toReturn;
} }
//For reading strings of a specific size
std::string BinaryIO::ReadString(std::istream& instream, size_t size) {
std::string toReturn;
char buffer;
for (size_t i = 0; i < size; ++i) {
BinaryIO::BinaryRead(instream, buffer);
toReturn += buffer;
}
return toReturn;
}
std::string BinaryIO::ReadWString(std::istream& instream) {
size_t size;
BinaryRead(instream, size);
//toReturn.resize(size);
std::string test;
unsigned char buf;
for (size_t i = 0; i < size; ++i) {
//instream.ignore(1);
BinaryRead(instream, buf);
test += buf;
}
//printf("%s\n", test.c_str());
//instream.read((char*)&toReturn[0], size * 2);
//std::string str(toReturn.begin(), toReturn.end());
return test;
}

View File

@@ -1,17 +1,8 @@
#pragma once #pragma once
#ifndef __BINARYIO__H__
#define __BINARYIO__H__
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <string>
#include "Game.h"
#include "Logger.h"
namespace BinaryIO { namespace BinaryIO {
template<typename T> template<typename T>
std::ostream& BinaryWrite(std::ostream& stream, const T& value) { std::ostream& BinaryWrite(std::ostream& stream, const T& value) {
return stream.write(reinterpret_cast<const char*>(&value), sizeof(T)); return stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
@@ -24,51 +15,13 @@ namespace BinaryIO {
return stream.read(reinterpret_cast<char*>(&value), sizeof(T)); return stream.read(reinterpret_cast<char*>(&value), sizeof(T));
} }
enum class ReadType : int8_t { void WriteString(const std::string& stringToWrite, std::ofstream& outstream);
WideString = 0,
String = 1,
};
template<typename SizeType>
inline void ReadString(std::istream& stream, std::u16string& value) {
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
SizeType size;
BinaryRead(stream, size);
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
value.resize(size);
stream.read(reinterpret_cast<char*>(value.data()), size * sizeof(uint16_t));
}
template<typename SizeType>
inline void ReadString(std::istream& stream, std::string& value, ReadType readType) {
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
SizeType size;
BinaryRead(stream, size);
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
value.resize(size);
if (readType == ReadType::WideString) {
uint16_t wideChar;
// Faster to do this than to read a u16string and convert it to a string since we only go through allocator once
for (SizeType i = 0; i < size; ++i) {
BinaryRead(stream, wideChar);
value[i] = static_cast<char>(wideChar);
}
} else {
stream.read(value.data(), size);
}
}
std::string ReadString(std::istream& instream); std::string ReadString(std::istream& instream);
std::string ReadString(std::istream& instream, size_t size);
std::string ReadWString(std::istream& instream);
inline bool DoesFileExist(const std::string& name) { inline bool DoesFileExist(const std::string& name) {
std::ifstream f(name.c_str()); std::ifstream f(name.c_str());
return f.good(); return f.good();
} }
} }
#endif //!__BINARYIO__H__

View File

@@ -9,12 +9,13 @@
#include "Database.h" #include "Database.h"
#include "Game.h" #include "Game.h"
#include "ZCompression.h" #include "ZCompression.h"
#include "Logger.h" #include "dLogger.h"
//! Forward declarations //! Forward declarations
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
void WriteSd0Magic(char* input, uint32_t chunkSize); void WriteSd0Magic(char* input, uint32_t chunkSize);
bool CheckSd0Magic(std::istream& streamToCheck); bool CheckSd0Magic(sql::Blob* streamToCheck);
/** /**
* @brief Truncates all models with broken data from the database. * @brief Truncates all models with broken data from the database.
@@ -23,24 +24,28 @@ bool CheckSd0Magic(std::istream& streamToCheck);
*/ */
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
uint32_t modelsTruncated{}; uint32_t modelsTruncated{};
auto modelsToTruncate = Database::Get()->GetAllUgcModels(); auto modelsToTruncate = GetModelsFromDatabase();
bool previousCommitValue = Database::Get()->GetAutoCommit(); bool previousCommitValue = Database::GetAutoCommit();
Database::Get()->SetAutoCommit(false); Database::SetAutoCommit(false);
for (auto& model : modelsToTruncate) { while (modelsToTruncate->next()) {
std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;"));
std::unique_ptr<sql::PreparedStatement> pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;"));
std::string completeUncompressedModel{}; std::string completeUncompressedModel{};
uint32_t chunkCount{}; uint32_t chunkCount{};
uint64_t modelId = modelsToTruncate->getInt(1);
std::unique_ptr<sql::Blob> modelAsSd0(modelsToTruncate->getBlob(2));
// Check that header is sd0 by checking for the sd0 magic. // Check that header is sd0 by checking for the sd0 magic.
if (CheckSd0Magic(model.lxfmlData)) { if (CheckSd0Magic(modelAsSd0.get())) {
while (true) { while (true) {
uint32_t chunkSize{}; uint32_t chunkSize{};
model.lxfmlData.read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream modelAsSd0->read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream
// Check if good here since if at the end of an sd0 file, this will have eof flagged. // Check if good here since if at the end of an sd0 file, this will have eof flagged.
if (!model.lxfmlData.good()) break; if (!modelAsSd0->good()) break;
std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]); std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]);
for (uint32_t i = 0; i < chunkSize; i++) { for (uint32_t i = 0; i < chunkSize; i++) {
compressedChunk[i] = model.lxfmlData.get(); compressedChunk[i] = modelAsSd0->get();
} }
// Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size. // Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
@@ -51,17 +56,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
if (actualUncompressedSize != -1) { if (actualUncompressedSize != -1) {
uint32_t previousSize = completeUncompressedModel.size(); uint32_t previousSize = completeUncompressedModel.size();
completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get())); completeUncompressedModel.append((char*)uncompressedChunk.get());
completeUncompressedModel.resize(previousSize + actualUncompressedSize); completeUncompressedModel.resize(previousSize + actualUncompressedSize);
} else { } else {
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err); Game::logger->Log("BrickByBrickFix", "Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, modelId, err);
break; break;
} }
chunkCount++; chunkCount++;
} }
std::unique_ptr<tinyxml2::XMLDocument> document = std::make_unique<tinyxml2::XMLDocument>(); std::unique_ptr<tinyxml2::XMLDocument> document = std::make_unique<tinyxml2::XMLDocument>();
if (!document) { if (!document) {
LOG("Failed to initialize tinyxml document. Aborting."); Game::logger->Log("BrickByBrickFix", "Failed to initialize tinyxml document. Aborting.");
return 0; return 0;
} }
@@ -70,20 +75,28 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
"</LXFML>", "</LXFML>",
completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos
) { ) {
LOG("Brick-by-brick model %llu will be deleted!", model.id); Game::logger->Log("BrickByBrickFix",
Database::Get()->DeleteUgcModelData(model.id); "Brick-by-brick model %llu will be deleted!", modelId);
ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
ugcModelToDelete->execute();
pcModelToDelete->execute();
modelsTruncated++; modelsTruncated++;
} }
} }
} else { } else {
LOG("Brick-by-brick model %llu will be deleted!", model.id); Game::logger->Log("BrickByBrickFix",
Database::Get()->DeleteUgcModelData(model.id); "Brick-by-brick model %llu will be deleted!", modelId);
ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
ugcModelToDelete->execute();
pcModelToDelete->execute();
modelsTruncated++; modelsTruncated++;
} }
} }
Database::Get()->Commit(); Database::Commit();
Database::Get()->SetAutoCommit(previousCommitValue); Database::SetAutoCommit(previousCommitValue);
return modelsTruncated; return modelsTruncated;
} }
@@ -95,17 +108,21 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
*/ */
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
uint32_t updatedModels = 0; uint32_t updatedModels = 0;
auto modelsToUpdate = Database::Get()->GetAllUgcModels(); auto modelsToUpdate = GetModelsFromDatabase();
auto previousAutoCommitState = Database::Get()->GetAutoCommit(); auto previousAutoCommitState = Database::GetAutoCommit();
Database::Get()->SetAutoCommit(false); Database::SetAutoCommit(false);
for (auto& model : modelsToUpdate) { std::unique_ptr<sql::PreparedStatement> insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;"));
while (modelsToUpdate->next()) {
int64_t modelId = modelsToUpdate->getInt64(1);
std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2));
// Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib) // Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib)
// If it does, convert it to sd0. // If it does, convert it to sd0.
if (model.lxfmlData.get() == 0x78 && model.lxfmlData.get() == 0xDA) { if (oldLxfml->get() == 0x78 && oldLxfml->get() == 0xDA) {
// Get and save size of zlib compressed chunk. // Get and save size of zlib compressed chunk.
model.lxfmlData.seekg(0, std::ios::end); oldLxfml->seekg(0, std::ios::end);
uint32_t oldLxfmlSize = static_cast<uint32_t>(model.lxfmlData.tellg()); uint32_t oldLxfmlSize = static_cast<uint32_t>(oldLxfml->tellg());
model.lxfmlData.seekg(0); oldLxfml->seekg(0);
// Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size. // Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size.
uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9; uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9;
@@ -113,27 +130,36 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize); WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize);
for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) { for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) {
sd0ConvertedModel.get()[i] = model.lxfmlData.get(); sd0ConvertedModel.get()[i] = oldLxfml->get();
} }
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader); std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
std::istringstream outputStringStream(outputString); std::istringstream outputStringStream(outputString);
insertionStatement->setBlob(1, static_cast<std::istream*>(&outputStringStream));
insertionStatement->setInt64(2, modelId);
try { try {
Database::Get()->UpdateUgcModelData(model.id, outputStringStream); insertionStatement->executeUpdate();
LOG("Updated model %i to sd0", model.id); Game::logger->Log("BrickByBrickFix", "Updated model %i to sd0", modelId);
updatedModels++; updatedModels++;
} catch (sql::SQLException exception) { } catch (sql::SQLException exception) {
LOG("Failed to update model %i. This model should be inspected manually to see why." Game::logger->Log(
"The database error is %s", model.id, exception.what()); "BrickByBrickFix",
"Failed to update model %i. This model should be inspected manually to see why."
"The database error is %s", modelId, exception.what());
} }
} }
} }
Database::Get()->Commit(); Database::Commit();
Database::Get()->SetAutoCommit(previousAutoCommitState); Database::SetAutoCommit(previousAutoCommitState);
return updatedModels; return updatedModels;
} }
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase() {
std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;"));
return std::unique_ptr<sql::ResultSet>(modelsRawDataQuery->executeQuery());
}
/** /**
* @brief Writes sd0 magic at the front of a char* * @brief Writes sd0 magic at the front of a char*
* *
@@ -149,6 +175,6 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) {
*reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array *reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array
} }
bool CheckSd0Magic(std::istream& streamToCheck) { bool CheckSd0Magic(sql::Blob* streamToCheck) {
return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF; return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF;
} }

View File

@@ -4,8 +4,7 @@ set(DCOMMON_SOURCES
"BinaryIO.cpp" "BinaryIO.cpp"
"dConfig.cpp" "dConfig.cpp"
"Diagnostics.cpp" "Diagnostics.cpp"
"Logger.cpp" "dLogger.cpp"
"Game.cpp"
"GeneralUtils.cpp" "GeneralUtils.cpp"
"LDFFormat.cpp" "LDFFormat.cpp"
"MD5.cpp" "MD5.cpp"
@@ -13,7 +12,7 @@ set(DCOMMON_SOURCES
"NiPoint3.cpp" "NiPoint3.cpp"
"NiQuaternion.cpp" "NiQuaternion.cpp"
"SHA512.cpp" "SHA512.cpp"
"Demangler.cpp" "Type.cpp"
"ZCompression.cpp" "ZCompression.cpp"
"BrickByBrickFix.cpp" "BrickByBrickFix.cpp"
"BinaryPathFinder.cpp" "BinaryPathFinder.cpp"

View File

@@ -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__

View File

@@ -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);
}

View File

@@ -1,6 +1,6 @@
#include "Diagnostics.h" #include "Diagnostics.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
// If we're on Win32, we'll include our minidump writer // If we're on Win32, we'll include our minidump writer
#ifdef _WIN32 #ifdef _WIN32
@@ -9,7 +9,7 @@
#include <Dbghelp.h> #include <Dbghelp.h>
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
void make_minidump(EXCEPTION_POINTERS* e) { void make_minidump(EXCEPTION_POINTERS* e) {
auto hDbgHelp = LoadLibraryA("dbghelp"); auto hDbgHelp = LoadLibraryA("dbghelp");
@@ -28,7 +28,7 @@ void make_minidump(EXCEPTION_POINTERS* e) {
"_%4d%02d%02d_%02d%02d%02d.dmp", "_%4d%02d%02d_%02d%02d%02d.dmp",
t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
} }
LOG("Creating crash dump %s", name); Game::logger->Log("Diagnostics", "Creating crash dump %s", name);
auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE) if (hFile == INVALID_HANDLE_VALUE)
return; return;
@@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) {
#include <cstring> #include <cstring>
#include <exception> #include <exception>
#if defined(INCLUDE_BACKTRACE) #if defined(__include_backtrace__)
#include <backtrace.h> #include <backtrace.h>
#include <backtrace-supported.h> #include <backtrace-supported.h>
@@ -83,7 +83,7 @@ struct bt_ctx {
static inline void Bt(struct backtrace_state* state) { static inline void Bt(struct backtrace_state* state) {
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log"; std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
LOG("backtrace is enabled, crash dump located at %s", fileName.c_str()); Game::logger->Log("Diagnostics", "backtrace is enabled, crash dump located at %s", fileName.c_str());
FILE* file = fopen(fileName.c_str(), "w+"); FILE* file = fopen(fileName.c_str(), "w+");
if (file != nullptr) { if (file != nullptr) {
backtrace_print(state, 2, file); backtrace_print(state, 2, file);
@@ -107,7 +107,7 @@ static void ErrorCallback(void* data, const char* msg, int errnum) {
} }
#endif #endif
#include "Demangler.h" #include "Type.h"
void GenerateDump() { void GenerateDump() {
std::string cmd = "sudo gcore " + std::to_string(getpid()); std::string cmd = "sudo gcore " + std::to_string(getpid());
@@ -115,66 +115,58 @@ void GenerateDump() {
} }
void CatchUnhandled(int sig) { void CatchUnhandled(int sig) {
std::exception_ptr eptr = std::current_exception(); #ifndef __include_backtrace__
try {
if (eptr) std::rethrow_exception(eptr);
} catch(const std::exception& e) {
LOG("Caught exception: '%s'", e.what());
}
#ifndef INCLUDE_BACKTRACE
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log"; std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
LOG("Encountered signal %i, creating crash dump %s", sig, fileName.c_str()); Game::logger->Log("Diagnostics", "Encountered signal %i, creating crash dump %s", sig, fileName.c_str());
if (Diagnostics::GetProduceMemoryDump()) { if (Diagnostics::GetProduceMemoryDump()) {
GenerateDump(); GenerateDump();
} }
constexpr uint8_t MaxStackTrace = 32;
void* array[MaxStackTrace]; void* array[10];
size_t size; size_t size;
// get void*'s for all entries on the stack // get void*'s for all entries on the stack
size = backtrace(array, MaxStackTrace); size = backtrace(array, 10);
# if defined(__GNUG__) #if defined(__GNUG__) and defined(__dynamic)
// Loop through the returned addresses, and get the symbols to be demangled // Loop through the returned addresses, and get the symbols to be demangled
char** strings = backtrace_symbols(array, size); char** strings = backtrace_symbols(array, size);
FILE* file = fopen(fileName.c_str(), "w+");
if (file != NULL) {
fprintf(file, "Error: signal %d:\n", sig);
}
// Print the stack trace // Print the stack trace
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' // Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' and extract the function name
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
std::string functionName = strings[i]; std::string functionName = strings[i];
std::string::size_type start = functionName.find('('); std::string::size_type start = functionName.find('(');
std::string::size_type end = functionName.find('+'); std::string::size_type end = functionName.find('+');
if (start != std::string::npos && end != std::string::npos) { if (start != std::string::npos && end != std::string::npos) {
std::string demangled = functionName.substr(start + 1, end - start - 1); std::string demangled = functionName.substr(start + 1, end - start - 1);
demangled = Demangler::Demangle(demangled.c_str()); demangled = demangle(functionName.c_str());
// If the demangled string is not empty, then we can replace the mangled string with the demangled one if (demangled.empty()) {
if (!demangled.empty()) { Game::logger->Log("Diagnostics", "[%02zu] %s", i, demangled.c_str());
demangled.push_back('('); } else {
demangled += functionName.substr(end); Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
functionName = demangled;
} }
} } else {
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
LOG("[%02zu] %s", i, functionName.c_str());
if (file != NULL) {
fprintf(file, "[%02zu] %s\n", i, functionName.c_str());
} }
} }
# else // defined(__GNUG__) #else
backtrace_symbols_fd(array, size, STDOUT_FILENO); backtrace_symbols_fd(array, size, STDOUT_FILENO);
# endif // defined(__GNUG__) #endif
#else // INCLUDE_BACKTRACE FILE* file = fopen(fileName.c_str(), "w+");
if (file != NULL) {
// print out all the frames to stderr
fprintf(file, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, fileno(file));
fclose(file);
}
#else
struct backtrace_state* state = backtrace_create_state( struct backtrace_state* state = backtrace_create_state(
Diagnostics::GetProcessFileName().c_str(), Diagnostics::GetProcessFileName().c_str(),
@@ -185,7 +177,7 @@ void CatchUnhandled(int sig) {
struct bt_ctx ctx = { state, 0 }; struct bt_ctx ctx = { state, 0 };
Bt(state); Bt(state);
#endif // INCLUDE_BACKTRACE #endif
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -204,10 +196,10 @@ void MakeBacktrace() {
sigact.sa_sigaction = CritErrHdlr; sigact.sa_sigaction = CritErrHdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO; sigact.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGSEGV, &sigact, nullptr) != 0 || if (sigaction(SIGSEGV, &sigact, (struct sigaction*)nullptr) != 0 ||
sigaction(SIGFPE, &sigact, nullptr) != 0 || sigaction(SIGFPE, &sigact, (struct sigaction*)nullptr) != 0 ||
sigaction(SIGABRT, &sigact, nullptr) != 0 || sigaction(SIGABRT, &sigact, (struct sigaction*)nullptr) != 0 ||
sigaction(SIGILL, &sigact, nullptr) != 0) { sigaction(SIGILL, &sigact, (struct sigaction*)nullptr) != 0) {
fprintf(stderr, "error setting signal handler for %d (%s)\n", fprintf(stderr, "error setting signal handler for %d (%s)\n",
SIGSEGV, SIGSEGV,
strsignal(SIGSEGV)); strsignal(SIGSEGV));

View File

@@ -9,7 +9,7 @@
#include "CDClientDatabase.h" #include "CDClientDatabase.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
#include "AssetManager.h" #include "AssetManager.h"
#include "eSqliteDataType.h" #include "eSqliteDataType.h"
@@ -28,21 +28,23 @@ FdbToSqlite::Convert::Convert(std::string binaryOutPath) {
this->m_BinaryOutPath = binaryOutPath; this->m_BinaryOutPath = binaryOutPath;
} }
bool FdbToSqlite::Convert::ConvertDatabase(AssetStream& buffer) { bool FdbToSqlite::Convert::ConvertDatabase(AssetMemoryBuffer& buffer) {
if (m_ConversionStarted) return false; if (m_ConversionStarted) return false;
std::istream cdClientBuffer(&buffer);
this->m_ConversionStarted = true; this->m_ConversionStarted = true;
try { try {
CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite"); CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite");
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;"); CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
int32_t numberOfTables = ReadInt32(buffer); int32_t numberOfTables = ReadInt32(cdClientBuffer);
ReadTables(numberOfTables, buffer); ReadTables(numberOfTables, cdClientBuffer);
CDClientDatabase::ExecuteQuery("COMMIT;"); CDClientDatabase::ExecuteQuery("COMMIT;");
} catch (CppSQLite3Exception& e) { } catch (CppSQLite3Exception& e) {
LOG("Encountered error %s converting FDB to SQLite", e.errorMessage()); Game::logger->Log("FdbToSqlite", "Encountered error %s converting FDB to SQLite", e.errorMessage());
return false; return false;
} }

View File

@@ -7,7 +7,7 @@
#include <iosfwd> #include <iosfwd>
#include <map> #include <map>
#include "AssetManager.h" class AssetMemoryBuffer;
enum class eSqliteDataType : int32_t; enum class eSqliteDataType : int32_t;
@@ -27,7 +27,7 @@ namespace FdbToSqlite {
* *
* @return true if the database was converted properly, false otherwise. * @return true if the database was converted properly, false otherwise.
*/ */
bool ConvertDatabase(AssetStream& buffer); bool ConvertDatabase(AssetMemoryBuffer& buffer);
/** /**
* @brief Reads a 32 bit int from the fdb file. * @brief Reads a 32 bit int from the fdb file.

View File

@@ -1,7 +0,0 @@
#include "Game.h"
namespace Game {
void OnSignal(int signal) {
lastSignal = signal;
}
}

View File

@@ -1,11 +1,9 @@
#pragma once #pragma once
#include <string>
#include <random> #include <random>
#include <csignal>
class dServer; class dServer;
class Logger; class dLogger;
class InstanceManager; class InstanceManager;
class dChatFilter; class dChatFilter;
class dConfig; class dConfig;
@@ -14,11 +12,9 @@ class AssetManager;
struct SystemAddress; struct SystemAddress;
class EntityManager; class EntityManager;
class dZoneManager; class dZoneManager;
class PlayerContainer;
namespace Game { namespace Game {
using signal_t = volatile std::sig_atomic_t; extern dLogger* logger;
extern Logger* logger;
extern dServer* server; extern dServer* server;
extern InstanceManager* im; extern InstanceManager* im;
extern dChatFilter* chatFilter; extern dChatFilter* chatFilter;
@@ -27,14 +23,7 @@ namespace Game {
extern RakPeerInterface* chatServer; extern RakPeerInterface* chatServer;
extern AssetManager* assetManager; extern AssetManager* assetManager;
extern SystemAddress chatSysAddr; extern SystemAddress chatSysAddr;
extern signal_t lastSignal; extern bool shouldShutdown;
extern EntityManager* entityManager; extern EntityManager* entityManager;
extern dZoneManager* zoneManager; extern dZoneManager* zoneManager;
extern PlayerContainer playerContainer;
extern std::string projectVersion;
inline bool ShouldShutdown() {
return lastSignal != 0;
}
void OnSignal(int signal);
} }

View File

@@ -53,7 +53,7 @@ bool _IsSuffixChar(uint8_t c) {
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) { bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
size_t rem = slice.length(); size_t rem = slice.length();
if (slice.empty()) return false; if (slice.empty()) return false;
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front()); const uint8_t* bytes = (const uint8_t*)&slice.front();
if (rem > 0) { if (rem > 0) {
uint8_t first = bytes[0]; uint8_t first = bytes[0];
if (first < 0x80) { // 1 byte character if (first < 0x80) { // 1 byte character

View File

@@ -9,11 +9,11 @@
#include <functional> #include <functional>
#include <type_traits> #include <type_traits>
#include <stdexcept> #include <stdexcept>
#include "BitStream.h" #include <BitStream.h>
#include "NiPoint3.h" #include "NiPoint3.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
enum eInventoryType : uint32_t; enum eInventoryType : uint32_t;
enum class eObjectBits : size_t; enum class eObjectBits : size_t;
@@ -126,11 +126,6 @@ namespace GeneralUtils {
template <typename T> template <typename T>
T Parse(const char* value); T Parse(const char* value);
template <>
inline bool Parse(const char* value) {
return std::stoi(value);
}
template <> template <>
inline int32_t Parse(const char* value) { inline int32_t Parse(const char* value) {
return std::stoi(value); return std::stoi(value);
@@ -151,11 +146,6 @@ namespace GeneralUtils {
return std::stod(value); return std::stod(value);
} }
template <>
inline uint16_t Parse(const char* value) {
return std::stoul(value);
}
template <> template <>
inline uint32_t Parse(const char* value) { inline uint32_t Parse(const char* value) {
return std::stoul(value); return std::stoul(value);
@@ -234,30 +224,18 @@ namespace GeneralUtils {
return T(); return T();
} }
/** // on Windows we need to undef these or else they conflict with our numeric limits calls
* Casts the value of an enum entry to its underlying type // DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
* @param entry Enum entry to cast #ifdef _WIN32
* @returns The enum entry's value in its underlying type #undef min
*/ #undef max
template <typename eType> #endif
inline constexpr typename std::underlying_type_t<eType> CastUnderlyingType(const eType entry) {
static_assert(std::is_enum_v<eType>, "Not an enum");
return static_cast<typename std::underlying_type_t<eType>>(entry);
}
// 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
template <typename T> template <typename T>
inline T GenerateRandomNumber() { inline T GenerateRandomNumber() {
// Make sure it is a numeric type // Make sure it is a numeric type
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type"); static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max()); return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
} }
} }

View File

@@ -4,7 +4,7 @@
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
// C++ // C++
#include <string_view> #include <string_view>
@@ -48,7 +48,7 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
try { try {
type = static_cast<eLDFType>(strtol(ldfTypeAndValue.first.data(), &storage, 10)); type = static_cast<eLDFType>(strtol(ldfTypeAndValue.first.data(), &storage, 10));
} catch (std::exception) { } 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; return nullptr;
} }
@@ -61,33 +61,35 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} }
case LDF_TYPE_S32: { case LDF_TYPE_S32: {
int32_t data; try {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) { int32_t data = static_cast<int32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10));
LOG("Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); 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; return nullptr;
} }
returnValue = new LDFData<int32_t>(key, data);
break; break;
} }
case LDF_TYPE_FLOAT: { case LDF_TYPE_FLOAT: {
float data; try {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) { float data = strtof(ldfTypeAndValue.second.data(), &storage);
LOG("Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); 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; return nullptr;
} }
returnValue = new LDFData<float>(key, data);
break; break;
} }
case LDF_TYPE_DOUBLE: { case LDF_TYPE_DOUBLE: {
double data; try {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) { double data = strtod(ldfTypeAndValue.second.data(), &storage);
LOG("Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); 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; return nullptr;
} }
returnValue = new LDFData<double>(key, data);
break; break;
} }
@@ -100,8 +102,10 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") { } else if (ldfTypeAndValue.second == "false") {
data = 0; data = 0;
} else { } else {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) { try {
LOG("Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); 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; return nullptr;
} }
} }
@@ -118,8 +122,10 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") { } else if (ldfTypeAndValue.second == "false") {
data = false; data = false;
} else { } else {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) { try {
LOG("Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); 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; return nullptr;
} }
} }
@@ -129,22 +135,24 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} }
case LDF_TYPE_U64: { case LDF_TYPE_U64: {
uint64_t data; try {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) { uint64_t data = static_cast<uint64_t>(strtoull(ldfTypeAndValue.second.data(), &storage, 10));
LOG("Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); 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; return nullptr;
} }
returnValue = new LDFData<uint64_t>(key, data);
break; break;
} }
case LDF_TYPE_OBJID: { case LDF_TYPE_OBJID: {
LWOOBJID data; try {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) { LWOOBJID data = static_cast<LWOOBJID>(strtoll(ldfTypeAndValue.second.data(), &storage, 10));
LOG("Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); 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; return nullptr;
} }
returnValue = new LDFData<LWOOBJID>(key, data);
break; break;
} }
@@ -155,12 +163,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} }
case LDF_TYPE_UNKNOWN: { 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; break;
} }
default: { 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; break;
} }
} }

View File

@@ -63,15 +63,15 @@ private:
//! Writes the key to the packet //! Writes the key to the packet
void WriteKey(RakNet::BitStream* 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) { for (uint32_t i = 0; i < this->key.length(); ++i) {
packet->Write<uint16_t>(this->key[i]); packet->Write(static_cast<uint16_t>(this->key[i]));
} }
} }
//! Writes the value to the packet //! Writes the value to the packet
void WriteValue(RakNet::BitStream* packet) { void WriteValue(RakNet::BitStream* packet) {
packet->Write<uint8_t>(this->GetValueType()); packet->Write(static_cast<uint8_t>(this->GetValueType()));
packet->Write(this->value); packet->Write(this->value);
} }
@@ -179,30 +179,30 @@ template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF
// The specialized version for std::u16string (UTF-16) // The specialized version for std::u16string (UTF-16)
template<> template<>
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream* packet) { inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream* packet) {
packet->Write<uint8_t>(this->GetValueType()); packet->Write(static_cast<uint8_t>(this->GetValueType()));
packet->Write<uint32_t>(this->value.length()); packet->Write(static_cast<uint32_t>(this->value.length()));
for (uint32_t i = 0; i < this->value.length(); ++i) { for (uint32_t i = 0; i < this->value.length(); ++i) {
packet->Write<uint16_t>(this->value[i]); packet->Write(static_cast<uint16_t>(this->value[i]));
} }
} }
// The specialized version for bool // The specialized version for bool
template<> template<>
inline void LDFData<bool>::WriteValue(RakNet::BitStream* packet) { inline void LDFData<bool>::WriteValue(RakNet::BitStream* packet) {
packet->Write<uint8_t>(this->GetValueType()); packet->Write(static_cast<uint8_t>(this->GetValueType()));
packet->Write<uint8_t>(this->value); packet->Write(static_cast<uint8_t>(this->value));
} }
// The specialized version for std::string (UTF-8) // The specialized version for std::string (UTF-8)
template<> template<>
inline void LDFData<std::string>::WriteValue(RakNet::BitStream* packet) { inline void LDFData<std::string>::WriteValue(RakNet::BitStream* packet) {
packet->Write<uint8_t>(this->GetValueType()); packet->Write(static_cast<uint8_t>(this->GetValueType()));
packet->Write<uint32_t>(this->value.length()); packet->Write(static_cast<uint32_t>(this->value.length()));
for (uint32_t i = 0; i < this->value.length(); ++i) { for (uint32_t i = 0; i < this->value.length(); ++i) {
packet->Write<uint8_t>(this->value[i]); packet->Write(static_cast<uint8_t>(this->value[i]));
} }
} }

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -107,7 +107,7 @@ void Metrics::EndMeasurement(MetricVariable variable) {
} }
float Metrics::ToMiliseconds(int64_t nanoseconds) { float Metrics::ToMiliseconds(int64_t nanoseconds) {
return static_cast<float>(nanoseconds) / 1e6; return (float)nanoseconds / 1e6;
} }
std::string Metrics::MetricVariableToString(MetricVariable variable) { std::string Metrics::MetricVariableToString(MetricVariable variable) {
@@ -193,34 +193,34 @@ size_t Metrics::GetPeakRSS() {
/* Windows -------------------------------------------------- */ /* Windows -------------------------------------------------- */
PROCESS_MEMORY_COUNTERS info; PROCESS_MEMORY_COUNTERS info;
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
return static_cast<size_t>(info.PeakWorkingSetSize); return (size_t)info.PeakWorkingSetSize;
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) #elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
/* AIX and Solaris ------------------------------------------ */ /* AIX and Solaris ------------------------------------------ */
struct psinfo psinfo; struct psinfo psinfo;
int fd = -1; int fd = -1;
if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1) if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
return static_cast<size_t>(0L); /* Can't open? */ return (size_t)0L; /* Can't open? */
if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
close(fd); close(fd);
return static_cast<size_t>(0L); /* Can't read? */ return (size_t)0L; /* Can't read? */
} }
close(fd); close(fd);
return static_cast<size_t>(psinfo.pr_rssize * 1024L); return (size_t)(psinfo.pr_rssize * 1024L);
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
/* BSD, Linux, and OSX -------------------------------------- */ /* BSD, Linux, and OSX -------------------------------------- */
struct rusage rusage; struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage); getrusage(RUSAGE_SELF, &rusage);
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
return static_cast<size_t>(rusage.ru_maxrss); return (size_t)rusage.ru_maxrss;
#else #else
return static_cast<size_t>(rusage.ru_maxrss * 1024L); return (size_t)(rusage.ru_maxrss * 1024L);
#endif #endif
#else #else
/* Unknown OS ----------------------------------------------- */ /* Unknown OS ----------------------------------------------- */
return static_cast<size_t>(0L); /* Unsupported. */ return (size_t)0L; /* Unsupported. */
#endif #endif
} }
@@ -234,33 +234,33 @@ size_t Metrics::GetCurrentRSS() {
/* Windows -------------------------------------------------- */ /* Windows -------------------------------------------------- */
PROCESS_MEMORY_COUNTERS info; PROCESS_MEMORY_COUNTERS info;
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
return static_cast<size_t>(info.WorkingSetSize); return (size_t)info.WorkingSetSize;
#elif defined(__APPLE__) && defined(__MACH__) #elif defined(__APPLE__) && defined(__MACH__)
/* OSX ------------------------------------------------------ */ /* OSX ------------------------------------------------------ */
struct mach_task_basic_info info; struct mach_task_basic_info info;
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
reinterpret_cast<task_info_t>(&info), &infoCount) != KERN_SUCCESS) (task_info_t)&info, &infoCount) != KERN_SUCCESS)
return static_cast<size_t>(0L); /* Can't access? */ return (size_t)0L; /* Can't access? */
return static_cast<size_t>(info.resident_size); return (size_t)info.resident_size;
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
/* Linux ---------------------------------------------------- */ /* Linux ---------------------------------------------------- */
long rss = 0L; long rss = 0L;
FILE* fp = NULL; FILE* fp = NULL;
if ((fp = fopen("/proc/self/statm", "r")) == NULL) if ((fp = fopen("/proc/self/statm", "r")) == NULL)
return static_cast<size_t>(0L); /* Can't open? */ return (size_t)0L; /* Can't open? */
if (fscanf(fp, "%*s%ld", &rss) != 1) { if (fscanf(fp, "%*s%ld", &rss) != 1) {
fclose(fp); fclose(fp);
return static_cast<size_t>(0L); /* Can't read? */ return (size_t)0L; /* Can't read? */
} }
fclose(fp); fclose(fp);
return static_cast<size_t>(rss) * static_cast<size_t>(sysconf(_SC_PAGESIZE)); return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE);
#else #else
/* AIX, BSD, Solaris, and Unknown OS ------------------------ */ /* AIX, BSD, Solaris, and Unknown OS ------------------------ */
return static_cast<size_t>(0L); /* Unsupported. */ return (size_t)0L; /* Unsupported. */
#endif #endif
} }

View File

@@ -114,13 +114,13 @@ bool NiPoint3::operator!=(const NiPoint3& point) const {
//! Operator for subscripting //! Operator for subscripting
float& NiPoint3::operator[](int i) { float& NiPoint3::operator[](int i) {
float* base = &x; float* base = &x;
return base[i]; return (float&)base[i];
} }
//! Operator for subscripting //! Operator for subscripting
const float& NiPoint3::operator[](int i) const { const float& NiPoint3::operator[](int i) const {
const float* base = &x; const float* base = &x;
return base[i]; return (float&)base[i];
} }
//! Operator for addition of vectors //! Operator for addition of vectors
@@ -136,13 +136,6 @@ NiPoint3& NiPoint3::operator+=(const NiPoint3& point) {
return *this; return *this;
} }
NiPoint3& NiPoint3::operator*=(const float scalar) {
this->x *= scalar;
this->y *= scalar;
this->z *= scalar;
return *this;
}
//! Operator for subtraction of vectors //! Operator for subtraction of vectors
NiPoint3 NiPoint3::operator-(const NiPoint3& point) const { NiPoint3 NiPoint3::operator-(const NiPoint3& point) const {
return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z); return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z);
@@ -181,7 +174,7 @@ bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3&
if (this->y < minPoint.y) return false; if (this->y < minPoint.y) return false;
if (this->y > maxPoint.y) return false; if (this->y > maxPoint.y) return false;
return (this->z < maxPoint.z && this->z > minPoint.z); return (this->z < maxPoint.z&& this->z > minPoint.z);
} }
//! Checks to see if the point (or vector) is within a sphere //! Checks to see if the point (or vector) is within a sphere
@@ -232,21 +225,10 @@ NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target,
float dx = target.x - current.x; float dx = target.x - current.x;
float dy = target.y - current.y; float dy = target.y - current.y;
float dz = target.z - current.z; float dz = target.z - current.z;
float lengthSquared = (float)((double)dx * (double)dx + (double)dy * (double)dy + (double)dz * (double)dz);
float lengthSquared = static_cast<float>( if ((double)lengthSquared == 0.0 || (double)maxDistanceDelta >= 0.0 && (double)lengthSquared <= (double)maxDistanceDelta * (double)maxDistanceDelta)
static_cast<double>(dx) * static_cast<double>(dx) +
static_cast<double>(dy) * static_cast<double>(dy) +
static_cast<double>(dz) * static_cast<double>(dz)
);
if (static_cast<double>(lengthSquared) == 0.0
|| static_cast<double>(maxDistanceDelta) >= 0.0
&& static_cast<double>(lengthSquared)
<= static_cast<double>(maxDistanceDelta) * static_cast<double>(maxDistanceDelta)) {
return target; return target;
} float length = (float)std::sqrt((double)lengthSquared);
float length = std::sqrt(lengthSquared);
return NiPoint3(current.x + dx / length * maxDistanceDelta, current.y + dy / length * maxDistanceDelta, current.z + dz / length * maxDistanceDelta); return NiPoint3(current.x + dx / length * maxDistanceDelta, current.y + dy / length * maxDistanceDelta, current.z + dz / length * maxDistanceDelta);
} }

View File

@@ -138,8 +138,6 @@ public:
//! Operator for addition of vectors //! Operator for addition of vectors
NiPoint3& operator+=(const NiPoint3& point); NiPoint3& operator+=(const NiPoint3& point);
NiPoint3& operator*=(const float scalar);
//! Operator for subtraction of vectors //! Operator for subtraction of vectors
NiPoint3 operator-(const NiPoint3& point) const; NiPoint3 operator-(const NiPoint3& point) const;

View File

@@ -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 = NiPoint3::ZERO;
NiPoint3 linearVelocity = NiPoint3::ZERO;
};
struct PositionUpdate {
NiPoint3 position = NiPoint3::ZERO;
NiQuaternion rotation = NiQuaternion::IDENTITY;
bool onGround = false;
bool onRail = false;
NiPoint3 velocity = NiPoint3::ZERO;
NiPoint3 angularVelocity = NiPoint3::ZERO;
LocalSpaceInfo localSpaceInfo;
RemoteInputInfo remoteInputInfo;
};
#endif //!__POSITIONUPDATE__H__

View File

@@ -1,5 +1,3 @@
// Source: http://www.zedwood.com/article/cpp-sha512-function
#include "SHA512.h" #include "SHA512.h"
#include <cstring> #include <cstring>

27
dCommon/Type.cpp Normal file
View 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
View 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());
}

View File

@@ -1,6 +1,6 @@
#include "ZCompression.h" #include "ZCompression.h"
#include "zlib.h" #include <zlib.h>
namespace ZCompression { namespace ZCompression {
int32_t GetMaxCompressedLength(int32_t nLenSrc) { int32_t GetMaxCompressedLength(int32_t nLenSrc) {

View File

@@ -2,9 +2,9 @@
#include "AssetManager.h" #include "AssetManager.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
#include "zlib.h" #include <zlib.h>
AssetManager::AssetManager(const std::filesystem::path& path) { AssetManager::AssetManager(const std::filesystem::path& path) {
if (!std::filesystem::is_directory(path)) { if (!std::filesystem::is_directory(path)) {
@@ -81,8 +81,8 @@ bool AssetManager::HasFile(const char* name) {
std::replace(fixedName.begin(), fixedName.end(), '/', '\\'); std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName; if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size()); uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size());
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4); crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4);
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) { for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
if (item.m_Crc == crc) { if (item.m_Crc == crc) {
@@ -113,7 +113,7 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
#endif #endif
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
*len = ftell(file); *len = ftell(file);
*data = static_cast<char*>(malloc(*len)); *data = (char*)malloc(*len);
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
int32_t readInData = fread(*data, sizeof(uint8_t), *len, file); int32_t readInData = fread(*data, sizeof(uint8_t), *len, file);
fclose(file); fclose(file);
@@ -129,8 +129,8 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
fixedName = "client\\res\\" + fixedName; fixedName = "client\\res\\" + fixedName;
} }
int32_t packIndex = -1; int32_t packIndex = -1;
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size()); uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size());
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4); crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4);
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) { for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
if (item.m_Crc == crc) { if (item.m_Crc == crc) {
@@ -152,12 +152,13 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
return success; return success;
} }
AssetStream AssetManager::GetFile(const char* name) { AssetMemoryBuffer AssetManager::GetFileAsBuffer(const char* name) {
char* buf; uint32_t len; char* buf;
uint32_t len;
bool success = this->GetFile(name, &buf, &len); bool success = this->GetFile(name, &buf, &len);
return AssetStream(buf, len, success); return AssetMemoryBuffer(buf, len, success);
} }
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) { uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
@@ -167,7 +168,7 @@ uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
crc = base; crc = base;
for (i = 0; i < l; i++) { for (i = 0; i < l; i++) {
// xor next byte to upper bits of crc // xor next byte to upper bits of crc
crc ^= (static_cast<unsigned int>(message[i]) << 24); crc ^= (((unsigned int)message[i]) << 24);
for (j = 0; j < 8; j++) { // Do eight times. for (j = 0; j < 8; j++) { // Do eight times.
msb = crc >> 31; msb = crc >> 31;
crc <<= 1; crc <<= 1;

View File

@@ -25,10 +25,6 @@ struct AssetMemoryBuffer : std::streambuf {
this->setg(base, base, base + n); this->setg(base, base, base + n);
} }
~AssetMemoryBuffer() {
if (m_Success) free(m_Base);
}
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override { pos_type seekpos(pos_type sp, std::ios_base::openmode which) override {
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which); return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
} }
@@ -44,17 +40,9 @@ struct AssetMemoryBuffer : std::streambuf {
setg(eback(), eback() + off, egptr()); setg(eback(), eback() + off, egptr());
return gptr() - eback(); return gptr() - eback();
} }
};
struct AssetStream : std::istream { void close() {
AssetStream(char* base, std::ptrdiff_t n, bool success) : std::istream(new AssetMemoryBuffer(base, n, success)) {} delete m_Base;
~AssetStream() {
delete rdbuf();
}
operator bool() {
return reinterpret_cast<AssetMemoryBuffer*>(rdbuf())->m_Success;
} }
}; };
@@ -68,7 +56,7 @@ public:
bool HasFile(const char* name); bool HasFile(const char* name);
bool GetFile(const char* name, char** data, uint32_t* len); bool GetFile(const char* name, char** data, uint32_t* len);
AssetStream GetFile(const char* name); AssetMemoryBuffer GetFileAsBuffer(const char* name);
private: private:
void LoadPackIndex(); void LoadPackIndex();

View File

@@ -76,7 +76,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
fseek(file, pos, SEEK_SET); fseek(file, pos, SEEK_SET);
if (!isCompressed) { if (!isCompressed) {
char* tempData = static_cast<char*>(malloc(pkRecord.m_UncompressedSize)); char* tempData = (char*)malloc(pkRecord.m_UncompressedSize);
int32_t readInData = fread(tempData, sizeof(uint8_t), pkRecord.m_UncompressedSize, file); int32_t readInData = fread(tempData, sizeof(uint8_t), pkRecord.m_UncompressedSize, file);
*data = tempData; *data = tempData;
@@ -90,7 +90,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
fseek(file, pos, SEEK_SET); fseek(file, pos, SEEK_SET);
char* decompressedData = static_cast<char*>(malloc(pkRecord.m_UncompressedSize)); char* decompressedData = (char*)malloc(pkRecord.m_UncompressedSize);
uint32_t currentReadPos = 0; uint32_t currentReadPos = 0;
while (true) { while (true) {
@@ -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); int32_t readInData = fread(&size, sizeof(uint32_t), 1, file);
pos += 4; // Move pointer position 4 to the right pos += 4; // Move pointer position 4 to the right
char* chunk = static_cast<char*>(malloc(size)); char* chunk = (char*)malloc(size);
int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file); int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file);
pos += size; // Move pointer position the amount of bytes read to the right pos += size; // Move pointer position the amount of bytes read to the right
int32_t err; int32_t err;
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err); currentReadPos += ZCompression::Decompress((uint8_t*)chunk, size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err);
free(chunk); free(chunk);
} }

View File

@@ -3,7 +3,6 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <filesystem> #include <filesystem>
#include <fstream>
#pragma pack(push, 1) #pragma pack(push, 1)
struct PackRecord { struct PackRecord {

View File

@@ -1,17 +1,28 @@
#include "PackIndex.h" #include "PackIndex.h"
#include "BinaryIO.h" #include "BinaryIO.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "dLogger.h"
PackIndex::PackIndex(const std::filesystem::path& filePath) { PackIndex::PackIndex(const std::filesystem::path& filePath) {
m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary); m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary);
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
m_PackPaths.resize(m_PackPathCount); for (int i = 0; i < m_PackPathCount; i++) {
for (auto& item : m_PackPaths) { uint32_t stringLen = 0;
BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String); BinaryIO::BinaryRead<uint32_t>(m_FileStream, stringLen);
std::string path;
for (int j = 0; j < stringLen; j++) {
char inChar;
BinaryIO::BinaryRead<char>(m_FileStream, inChar);
path += inChar;
}
m_PackPaths.push_back(path);
} }
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);
@@ -23,7 +34,7 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) {
m_PackFileIndices.push_back(packFileIndex); m_PackFileIndices.push_back(packFileIndex);
} }
LOG("Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size()); Game::logger->Log("PackIndex", "Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size());
for (auto& item : m_PackPaths) { for (auto& item : m_PackPaths) {
std::replace(item.begin(), item.end(), '\\', '/'); std::replace(item.begin(), item.end(), '\\', '/');

View File

@@ -10,23 +10,8 @@ dConfig::dConfig(const std::string& filepath) {
LoadConfig(); LoadConfig();
} }
std::filesystem::path GetConfigDir() {
std::filesystem::path config_dir = BinaryPathFinder::GetBinaryDir();
if (const char* env_p = std::getenv("DLU_CONFIG_DIR")) {
config_dir /= env_p;
}
return config_dir;
}
const bool dConfig::Exists(const std::string& filepath) {
std::filesystem::path config_dir = GetConfigDir();
return std::filesystem::exists(config_dir / filepath);
}
void dConfig::LoadConfig() { void dConfig::LoadConfig() {
std::filesystem::path config_dir = GetConfigDir(); std::ifstream in(BinaryPathFinder::GetBinaryDir() / m_ConfigFilePath);
std::ifstream in(config_dir / m_ConfigFilePath);
if (!in.good()) return; if (!in.good()) return;
std::string line{}; std::string line{};
@@ -34,7 +19,7 @@ void dConfig::LoadConfig() {
if (!line.empty() && line.front() != '#') ProcessLine(line); if (!line.empty() && line.front() != '#') ProcessLine(line);
} }
std::ifstream sharedConfig(config_dir / "sharedconfig.ini", std::ios::in); std::ifstream sharedConfig(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini", std::ios::in);
if (!sharedConfig.good()) return; if (!sharedConfig.good()) return;
line.clear(); line.clear();
@@ -49,20 +34,17 @@ void dConfig::ReloadConfig() {
} }
const std::string& dConfig::GetValue(std::string key) { const std::string& dConfig::GetValue(std::string key) {
std::string upper_key(key);
std::transform(upper_key.begin(), upper_key.end(), upper_key.begin(), ::toupper);
if (const char* env_p = std::getenv(upper_key.c_str())) {
this->m_ConfigValues[key] = env_p;
}
return this->m_ConfigValues[key]; return this->m_ConfigValues[key];
} }
void dConfig::ProcessLine(const std::string& line) { void dConfig::ProcessLine(const std::string& line) {
auto splitLoc = line.find('='); auto splitLine = GeneralUtils::SplitString(line, '=');
auto key = line.substr(0, splitLoc);
auto value = line.substr(splitLoc + 1); if (splitLine.size() != 2) return;
//Make sure that on Linux, we remove special characters: //Make sure that on Linux, we remove special characters:
auto& key = splitLine.at(0);
auto& value = splitLine.at(1);
if (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1); if (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1);
if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return; if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return;

View File

@@ -7,11 +7,6 @@ class dConfig {
public: public:
dConfig(const std::string& filepath); dConfig(const std::string& filepath);
/**
* Checks whether the specified filepath exists
*/
static const bool Exists(const std::string& filepath);
/** /**
* Gets the specified key from the config. Returns an empty string if the value is not found. * Gets the specified key from the config. Returns an empty string if the value is not found.
* *

View File

@@ -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__

View File

@@ -9,7 +9,7 @@
#include "BitStream.h" #include "BitStream.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eClientMessageType.h" #include "eClientMessageType.h"
#include "BitStreamUtils.h" #include "BitstreamUtils.h"
#pragma warning (disable:4251) //Disables SQL warnings #pragma warning (disable:4251) //Disables SQL warnings
@@ -33,7 +33,7 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
#define CBITSTREAM RakNet::BitStream bitStream; #define CBITSTREAM RakNet::BitStream bitStream;
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false); #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 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 BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
#define SEND_PACKET Game::server->Send(&bitStream, sysAddr, false); #define SEND_PACKET Game::server->Send(&bitStream, sysAddr, false);
#define SEND_PACKET_BROADCAST Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); #define SEND_PACKET_BROADCAST Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
@@ -130,7 +130,7 @@ public:
LWOOBJID friendID; LWOOBJID friendID;
std::string friendName; std::string friendName;
void Serialize(RakNet::BitStream& bitStream) const { void Serialize(RakNet::BitStream& bitStream) {
bitStream.Write<uint8_t>(isOnline); bitStream.Write<uint8_t>(isOnline);
bitStream.Write<uint8_t>(isBestFriend); bitStream.Write<uint8_t>(isBestFriend);
bitStream.Write<uint8_t>(isFTP); bitStream.Write<uint8_t>(isFTP);
@@ -148,11 +148,11 @@ public:
if (size > maxSize) size = maxSize; if (size > maxSize) size = maxSize;
for (uint32_t i = 0; i < size; ++i) { 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) { 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); //??? bitStream.Write<uint32_t>(0); //???

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,20 @@ enum class ePermissionMap : uint64_t {
* The character has restricted chat access, bit 6. * The character has restricted chat access, bit 6.
*/ */
RestrictedChatAccess = 0x1 << 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__ #endif //!__EPERMISSIONMAP__H__

View File

@@ -1,13 +0,0 @@
#ifndef __EPETABILITYTYPE__H__
#define __EPETABILITYTYPE__H__
#include <cstdint>
enum class ePetAbilityType : uint32_t {
Invalid,
GoToObject,
JumpOnObject,
DigAtPosition
};
#endif //!__EPETABILITYTYPE__H__

View File

@@ -166,8 +166,7 @@ enum ePlayerFlag : int32_t {
NJ_LIGHTNING_SPINJITZU = 2031, NJ_LIGHTNING_SPINJITZU = 2031,
NJ_ICE_SPINJITZU = 2032, NJ_ICE_SPINJITZU = 2032,
NJ_FIRE_SPINJITZU = 2033, NJ_FIRE_SPINJITZU = 2033,
NJ_WU_SHOW_DAILY_CHEST = 2099, NJ_WU_SHOW_DAILY_CHEST = 2099
DLU_SKIP_CINEMATICS = 1'000'000,
}; };
#endif //!__EPLAYERFLAG__H__ #endif //!__EPLAYERFLAG__H__

View File

@@ -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__

View 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__

View File

@@ -34,7 +34,7 @@ enum class eReplicaComponentType : uint32_t {
PLATFORM_BOUNDARY, PLATFORM_BOUNDARY,
MODULE, MODULE,
ARCADE, ARCADE,
HAVOK_VEHICLE_PHYSICS, VEHICLE_PHYSICS, // Havok demo based
MOVEMENT_AI, MOVEMENT_AI,
EXHIBIT, EXHIBIT,
OVERHEAD_ICON, OVERHEAD_ICON,
@@ -50,11 +50,11 @@ enum class eReplicaComponentType : uint32_t {
PROPERTY_ENTRANCE, PROPERTY_ENTRANCE,
FX, FX,
PROPERTY_MANAGEMENT, PROPERTY_MANAGEMENT,
VEHICLE_PHYSICS, VEHICLE_PHYSICS_NEW, // internal physics based on havok
PHYSICS_SYSTEM, PHYSICS_SYSTEM,
QUICK_BUILD, QUICK_BUILD,
SWITCH, SWITCH,
MINI_GAME_CONTROL, ZONE_CONTROL, // Minigame
CHANGLING, CHANGLING,
CHOICE_BUILD, CHOICE_BUILD,
PACKAGE, PACKAGE,
@@ -101,7 +101,7 @@ enum class eReplicaComponentType : uint32_t {
TRADE, TRADE,
USER_CONTROL, USER_CONTROL,
IGNORE_LIST, IGNORE_LIST,
MULTI_ZONE_ENTRANCE, ROCKET_LAUNCH_LUP,
BUFF_REAL, // the real buff component, should just be name BUFF BUFF_REAL, // the real buff component, should just be name BUFF
INTERACTION_MANAGER, INTERACTION_MANAGER,
DONATION_VENDOR, DONATION_VENDOR,

48
dCommon/dEnums/eStamps.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef __ESTAMPS__H__
#define __ESTAMPS__H__
#include <cstdint>
enum class eStamps : uint32_t {
PASSPORT_AUTH_START,
PASSPORT_AUTH_BYPASS,
PASSPORT_AUTH_ERROR,
PASSPORT_AUTH_DB_SELECT_START,
PASSPORT_AUTH_DB_SELECT_FINISH,
PASSPORT_AUTH_DB_INSERT_START,
PASSPORT_AUTH_DB_INSERT_FINISH,
PASSPORT_AUTH_LEGOINT_COMMUNICATION_START,
PASSPORT_AUTH_LEGOINT_RECEIVED,
PASSPORT_AUTH_LEGOINT_THREAD_SPAWN,
PASSPORT_AUTH_LEGOINT_WEBSERVICE_START,
PASSPORT_AUTH_LEGOINT_WEBSERVICE_FINISH,
PASSPORT_AUTH_LEGOINT_LEGOCLUB_START,
PASSPORT_AUTH_LEGOINT_LEGOCLUB_FINISH,
PASSPORT_AUTH_LEGOINT_THREAD_FINISH,
PASSPORT_AUTH_LEGOINT_REPLY,
PASSPORT_AUTH_LEGOINT_ERROR,
PASSPORT_AUTH_LEGOINT_COMMUNICATION_END,
PASSPORT_AUTH_LEGOINT_DISCONNECT,
PASSPORT_AUTH_WORLD_COMMUNICATION_START,
PASSPORT_AUTH_CLIENT_OS,
PASSPORT_AUTH_WORLD_PACKET_RECEIVED,
PASSPORT_AUTH_IM_COMMUNICATION_START,
PASSPORT_AUTH_IM_LOGIN_START,
PASSPORT_AUTH_IM_LOGIN_ALREADY_LOGGED_IN,
PASSPORT_AUTH_IM_OTHER_LOGIN_REMOVED,
PASSPORT_AUTH_IM_LOGIN_QUEUED,
PASSPORT_AUTH_IM_LOGIN_RESPONSE,
PASSPORT_AUTH_IM_COMMUNICATION_END,
PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH,
PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH,
PASSPORT_AUTH_WORLD_DISCONNECT,
NO_LEGO_INTERFACE,
DB_ERROR,
GM_REQUIRED,
NO_LEGO_WEBSERVICE_XML,
LEGO_WEBSERVICE_TIMEOUT,
LEGO_WEBSERVICE_ERROR,
NO_WORLD_SERVER
};
#endif //!__ESTAMPS__H__

View File

@@ -3,8 +3,6 @@
#include <cstdint> #include <cstdint>
#include "magic_enum.hpp"
enum class eWorldMessageType : uint32_t { enum class eWorldMessageType : uint32_t {
VALIDATION = 1, // Session info VALIDATION = 1, // Session info
CHARACTER_LIST_REQUEST, CHARACTER_LIST_REQUEST,
@@ -38,14 +36,7 @@ enum class eWorldMessageType : uint32_t {
HANDLE_FUNNESS, HANDLE_FUNNESS,
FAKE_PRG_CSR_MESSAGE, FAKE_PRG_CSR_MESSAGE,
REQUEST_FREE_TRIAL_REFRESH, REQUEST_FREE_TRIAL_REFRESH,
GM_SET_FREE_TRIAL_STATUS, 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;
}; };
#endif //!__EWORLDMESSAGETYPE__H__ #endif //!__EWORLDMESSAGETYPE__H__

110
dCommon/dLogger.cpp Normal file
View 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
View 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
};

View File

@@ -4,13 +4,9 @@
// Static Variables // Static Variables
static CppSQLite3DB* conn = new CppSQLite3DB(); static CppSQLite3DB* conn = new CppSQLite3DB();
// Status Variables
bool CDClientDatabase::isConnected = false;
//! Opens a connection with the CDClient //! Opens a connection with the CDClient
void CDClientDatabase::Connect(const std::string& filename) { void CDClientDatabase::Connect(const std::string& filename) {
conn->open(filename.c_str()); conn->open(filename.c_str());
isConnected = true;
} }
//! Queries the CDClient //! Queries the CDClient

View File

@@ -13,12 +13,16 @@
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
//! The CDClient Database namespace // 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 { namespace CDClientDatabase {
/**
* Boolean defining the connection status of CDClient
*/
extern bool isConnected;
//! Opens a connection with the CDClient //! Opens a connection with the CDClient
/*! /*!

View File

@@ -1,113 +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 <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!";
}
};
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();
}

View File

@@ -1,27 +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> {
private:
std::vector<CDActivityRewards> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
std::vector<CDActivityRewards> Query(std::function<bool(CDActivityRewards)> predicate);
std::vector<CDActivityRewards> GetEntries() const;
};

View File

@@ -1,51 +0,0 @@
#include "CDComponentsRegistryTable.h"
#include "eReplicaComponentType.h"
void CDComponentsRegistryTable::LoadValuesFromDatabase() {
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ComponentsRegistry");
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);
this->mappedEntries.insert_or_assign(static_cast<uint64_t>(entry.component_type) << 32 | static_cast<uint64_t>(entry.id), entry.component_id);
this->mappedEntries.insert_or_assign(entry.id, 0);
tableData.nextRow();
}
tableData.finalize();
}
int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) {
auto exists = mappedEntries.find(id);
if (exists != mappedEntries.end()) {
auto iter = mappedEntries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
return iter == mappedEntries.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);
this->mappedEntries.insert_or_assign(static_cast<uint64_t>(entry.component_type) << 32 | static_cast<uint64_t>(entry.id), entry.component_id);
tableData.nextRow();
}
mappedEntries.insert_or_assign(id, 0);
auto iter = this->mappedEntries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
return iter == this->mappedEntries.end() ? defaultValue : iter->second;
}

View File

@@ -1,33 +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> {
private:
std::vector<CDDestructibleComponent> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
std::vector<CDDestructibleComponent> Query(std::function<bool(CDDestructibleComponent)> predicate);
const std::vector<CDDestructibleComponent>& GetEntries(void) const;
};

View File

@@ -1,26 +0,0 @@
#include "CDEmoteTable.h"
void CDEmoteTableTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Emotes");
while (!tableData.eof()) {
CDEmoteTable entry;
entry.ID = tableData.getIntField("id", -1);
entry.animationName = tableData.getStringField("animationName", "");
entry.iconFilename = tableData.getStringField("iconFilename", "");
entry.channel = tableData.getIntField("channel", -1);
entry.locked = tableData.getIntField("locked", -1) != 0;
entry.localize = tableData.getIntField("localize", -1) != 0;
entry.locState = tableData.getIntField("locStatus", -1);
entry.gateVersion = tableData.getStringField("gate_version", "");
entries.insert(std::make_pair(entry.ID, entry));
tableData.nextRow();
}
tableData.finalize();
}
CDEmoteTable* CDEmoteTableTable::GetEmote(int32_t id) {
auto itr = entries.find(id);
return itr != entries.end() ? &itr->second : nullptr;
}

View File

@@ -1,64 +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> {
private:
std::map<uint32_t, CDItemComponent> entries;
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;
};

View File

@@ -1,35 +0,0 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDItemSets {
uint32_t setID; //!< The item set ID
uint32_t locStatus; //!< The loc status
std::string itemIDs; //!< THe item IDs
uint32_t kitType; //!< The item kit type
uint32_t kitRank; //!< The item kit rank
uint32_t kitImage; //!< The item kit image
uint32_t skillSetWith2; //!< The skill set with 2
uint32_t skillSetWith3; //!< The skill set with 3
uint32_t skillSetWith4; //!< The skill set with 4
uint32_t skillSetWith5; //!< The skill set with 5
uint32_t skillSetWith6; //!< The skill set with 6
bool localize; //!< Whether or localize
std::string gate_version; //!< The gate version
uint32_t kitID; //!< The kit ID
float priority; //!< The priority
};
class CDItemSetsTable : public CDTable<CDItemSetsTable> {
private:
std::vector<CDItemSets> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
std::vector<CDItemSets> Query(std::function<bool(CDItemSets)> predicate);
const std::vector<CDItemSets>& GetEntries(void) const;
};

View File

@@ -1,58 +0,0 @@
#include "CDLootMatrixTable.h"
CDLootMatrix CDLootMatrixTable::ReadRow(CppSQLite3Query& tableData) const {
CDLootMatrix entry{};
if (tableData.eof()) return entry;
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
entry.percent = tableData.getFloatField("percent", -1.0f);
entry.minToDrop = tableData.getIntField("minToDrop", -1);
entry.maxToDrop = tableData.getIntField("maxToDrop", -1);
entry.flagID = tableData.getIntField("flagID", -1);
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
return entry;
}
void CDLootMatrixTable::LoadValuesFromDatabase() {
// First, get the size of the table
uint32_t size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootMatrix");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootMatrix");
while (!tableData.eof()) {
CDLootMatrix entry;
uint32_t lootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
this->entries[lootMatrixIndex].push_back(ReadRow(tableData));
tableData.nextRow();
}
}
const LootMatrixEntries& CDLootMatrixTable::GetMatrix(uint32_t matrixId) {
auto itr = this->entries.find(matrixId);
if (itr != this->entries.end()) {
return itr->second;
}
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootMatrix where LootMatrixIndex = ?;");
query.bind(1, static_cast<int32_t>(matrixId));
auto tableData = query.execQuery();
while (!tableData.eof()) {
this->entries[matrixId].push_back(ReadRow(tableData));
tableData.nextRow();
}
return this->entries[matrixId];
}

View File

@@ -1,29 +0,0 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDLootMatrix {
uint32_t LootTableIndex; //!< The Loot Table Index
uint32_t RarityTableIndex; //!< The Rarity Table Index
float percent; //!< The percent that this matrix is used?
uint32_t minToDrop; //!< The minimum amount of loot from this matrix to drop
uint32_t maxToDrop; //!< The maximum amount of loot from this matrix to drop
uint32_t flagID; //!< ???
UNUSED(std::string gate_version); //!< The Gate Version
};
typedef uint32_t LootMatrixIndex;
typedef std::vector<CDLootMatrix> LootMatrixEntries;
class CDLootMatrixTable : public CDTable<CDLootMatrixTable> {
public:
void LoadValuesFromDatabase();
// Gets a matrix by ID or inserts a blank one if none existed.
const LootMatrixEntries& GetMatrix(uint32_t matrixId);
private:
CDLootMatrix ReadRow(CppSQLite3Query& tableData) const;
std::unordered_map<LootMatrixIndex, LootMatrixEntries> entries;
};

View File

@@ -1,86 +0,0 @@
#include "CDLootTableTable.h"
#include "CDClientManager.h"
#include "CDComponentsRegistryTable.h"
#include "CDItemComponentTable.h"
#include "eReplicaComponentType.h"
// Sort the tables by their rarity so the highest rarity items are first.
void SortTable(LootTableEntries& table) {
auto* componentsRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto* itemComponentTable = CDClientManager::Instance().GetTable<CDItemComponentTable>();
// We modify the table in place so the outer loop keeps track of what is sorted
// and the inner loop finds the highest rarity item and swaps it with the current position
// of the outer loop.
for (auto oldItrOuter = table.begin(); oldItrOuter != table.end(); oldItrOuter++) {
auto lootToInsert = oldItrOuter;
// Its fine if this starts at 0, even if this doesnt match lootToInsert as the actual highest will
// either be found and overwrite these values, or the original is somehow zero and is still the highest rarity.
uint32_t highestLootRarity = 0;
for (auto oldItrInner = oldItrOuter; oldItrInner != table.end(); oldItrInner++) {
uint32_t itemComponentId = componentsRegistryTable->GetByIDAndType(oldItrInner->itemid, eReplicaComponentType::ITEM);
uint32_t rarity = itemComponentTable->GetItemComponentByID(itemComponentId).rarity;
if (rarity > highestLootRarity) {
highestLootRarity = rarity;
lootToInsert = oldItrInner;
}
}
std::swap(*oldItrOuter, *lootToInsert);
}
}
CDLootTable CDLootTableTable::ReadRow(CppSQLite3Query& tableData) const {
CDLootTable entry{};
if (tableData.eof()) return entry;
entry.itemid = tableData.getIntField("itemid", -1);
entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false;
entry.sortPriority = tableData.getIntField("sortPriority", -1);
return entry;
}
void CDLootTableTable::LoadValuesFromDatabase() {
// First, get the size of the table
uint32_t size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootTable");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootTable");
while (!tableData.eof()) {
CDLootTable entry;
uint32_t lootTableIndex = tableData.getIntField("LootTableIndex", -1);
this->entries[lootTableIndex].push_back(ReadRow(tableData));
tableData.nextRow();
}
for (auto& [id, table] : this->entries) {
SortTable(table);
}
}
const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
auto itr = this->entries.find(tableId);
if (itr != this->entries.end()) {
return itr->second;
}
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootTable WHERE LootTableIndex = ?;");
query.bind(1, static_cast<int32_t>(tableId));
auto tableData = query.execQuery();
while (!tableData.eof()) {
CDLootTable entry;
this->entries[tableId].push_back(ReadRow(tableData));
tableData.nextRow();
}
SortTable(this->entries[tableId]);
return this->entries[tableId];
}

View File

@@ -1,26 +0,0 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDLootTable {
uint32_t itemid; //!< The LOT of the item
uint32_t LootTableIndex; //!< The Loot Table Index
bool MissionDrop; //!< Whether or not this loot table is a mission drop
uint32_t sortPriority; //!< The sorting priority
};
typedef uint32_t LootTableIndex;
typedef std::vector<CDLootTable> LootTableEntries;
class CDLootTableTable : public CDTable<CDLootTableTable> {
private:
CDLootTable ReadRow(CppSQLite3Query& tableData) const;
std::unordered_map<LootTableIndex, LootTableEntries> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
const LootTableEntries& GetTable(uint32_t tableId);
};

View File

@@ -1,81 +0,0 @@
#pragma once
// Custom Classes
#include "CDTable.h"
#include <map>
#include <cstdint>
struct CDMissions {
int32_t id; //!< The Mission ID
std::string defined_type; //!< The type of mission
std::string defined_subtype; //!< The subtype of the mission
int32_t UISortOrder; //!< The UI Sort Order for the mission
int32_t offer_objectID; //!< The LOT of the mission giver
int32_t target_objectID; //!< The LOT of the mission's target
int64_t reward_currency; //!< The amount of currency to reward the player
int32_t LegoScore; //!< The amount of LEGO Score to reward the player
int64_t reward_reputation; //!< The reputation to award the player
bool isChoiceReward; //!< Whether or not the user has the option to choose their loot
int32_t reward_item1; //!< The first rewarded item
int32_t reward_item1_count; //!< The count of the first item to be rewarded
int32_t reward_item2; //!< The second rewarded item
int32_t reward_item2_count; //!< The count of the second item to be rewarded
int32_t reward_item3; //!< The third rewarded item
int32_t reward_item3_count; //!< The count of the third item to be rewarded
int32_t reward_item4; //!< The fourth rewarded item
int32_t reward_item4_count; //!< The count of the fourth item to be rewarded
int32_t reward_emote; //!< The first emote to be rewarded
int32_t reward_emote2; //!< The second emote to be rewarded
int32_t reward_emote3; //!< The third emote to be rewarded
int32_t reward_emote4; //!< The fourth emote to be rewarded
int32_t reward_maximagination; //!< The amount of max imagination to reward
int32_t reward_maxhealth; //!< The amount of max health to reward
int32_t reward_maxinventory; //!< The amount of max inventory to reward
int32_t reward_maxmodel; //!< ???
int32_t reward_maxwidget; //!< ???
int32_t reward_maxwallet; //!< ???
bool repeatable; //!< Whether or not this mission can be repeated (for instance, is it a daily mission)
int64_t reward_currency_repeatable; //!< The repeatable reward
int32_t reward_item1_repeatable; //!< The first rewarded item
int32_t reward_item1_repeat_count; //!< The count of the first item to be rewarded
int32_t reward_item2_repeatable; //!< The second rewarded item
int32_t reward_item2_repeat_count; //!< The count of the second item to be rewarded
int32_t reward_item3_repeatable; //!< The third rewarded item
int32_t reward_item3_repeat_count; //!< The count of the third item to be rewarded
int32_t reward_item4_repeatable; //!< The fourth rewarded item
int32_t reward_item4_repeat_count; //!< The count of the fourth item to be rewarded
int32_t time_limit; //!< The time limit of the mission
bool isMission; //!< Maybe to differentiate between missions and achievements?
int32_t missionIconID; //!< The mission icon ID
std::string prereqMissionID; //!< A '|' seperated list of prerequisite missions
bool localize; //!< Whether or not to localize the mission
bool inMOTD; //!< In Match of the Day(?)
int64_t cooldownTime; //!< The mission cooldown time
bool isRandom; //!< ???
std::string randomPool; //!< ???
int32_t UIPrereqID; //!< ???
UNUSED(std::string gate_version); //!< The gate version
UNUSED(std::string HUDStates); //!< ???
UNUSED(int32_t locStatus); //!< ???
int32_t reward_bankinventory; //!< The amount of bank space this mission rewards
};
class CDMissionsTable : public CDTable<CDMissionsTable> {
private:
std::vector<CDMissions> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
std::vector<CDMissions> Query(std::function<bool(CDMissions)> predicate);
// Gets all the entries in the table
const std::vector<CDMissions>& GetEntries() const;
const CDMissions* GetPtrByMissionID(uint32_t missionID) const;
const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const;
static CDMissions Default;
};

View File

@@ -1,61 +0,0 @@
#include "CDPetComponentTable.h"
namespace {
// Default entries for fallback
CDPetComponent defaultEntry{
.id = 0,
UNUSED_ENTRY(.minTameUpdateTime = 60.0f,)
UNUSED_ENTRY(.maxTameUpdateTime = 300.0f,)
UNUSED_ENTRY(.percentTameChance = 101.0f,)
UNUSED_ENTRY(.tameability = 100.0f,)
UNUSED_ENTRY(.elementType = 1,)
.walkSpeed = 2.5f,
.runSpeed = 5.0f,
.sprintSpeed = 10.0f,
UNUSED_ENTRY(.idleTimeMin = 60.0f,)
UNUSED_ENTRY(.idleTimeMax = 300.0f,)
UNUSED_ENTRY(.petForm = 0,)
.imaginationDrainRate = 60.0f,
UNUSED_ENTRY(.AudioMetaEventSet = "",)
UNUSED_ENTRY(.buffIDs = "",)
};
}
void CDPetComponentTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PetComponent");
while (!tableData.eof()) {
const uint32_t componentID = tableData.getIntField("id", defaultEntry.id);
auto& entry = m_Entries[componentID];
entry.id = componentID;
UNUSED_COLUMN(entry.minTameUpdateTime = tableData.getFloatField("minTameUpdateTime", defaultEntry.minTameUpdateTime));
UNUSED_COLUMN(entry.maxTameUpdateTime = tableData.getFloatField("maxTameUpdateTime", defaultEntry.maxTameUpdateTime));
UNUSED_COLUMN(entry.percentTameChance = tableData.getFloatField("percentTameChance", defaultEntry.percentTameChance));
UNUSED_COLUMN(entry.tameability = tableData.getFloatField("tamability", defaultEntry.tameability)); // Mispelled as "tamability" in CDClient
UNUSED_COLUMN(entry.elementType = tableData.getIntField("elementType", defaultEntry.elementType));
entry.walkSpeed = static_cast<float>(tableData.getFloatField("walkSpeed", defaultEntry.walkSpeed));
entry.runSpeed = static_cast<float>(tableData.getFloatField("runSpeed", defaultEntry.runSpeed));
entry.sprintSpeed = static_cast<float>(tableData.getFloatField("sprintSpeed", defaultEntry.sprintSpeed));
UNUSED_COLUMN(entry.idleTimeMin = tableData.getFloatField("idleTimeMin", defaultEntry.idleTimeMin));
UNUSED_COLUMN(entry.idleTimeMax = tableData.getFloatField("idleTimeMax", defaultEntry.idleTimeMax));
UNUSED_COLUMN(entry.petForm = tableData.getIntField("petForm", defaultEntry.petForm));
entry.imaginationDrainRate = static_cast<float>(tableData.getFloatField("imaginationDrainRate", defaultEntry.imaginationDrainRate));
UNUSED_COLUMN(entry.AudioMetaEventSet = tableData.getStringField("AudioMetaEventSet", defaultEntry.AudioMetaEventSet));
UNUSED_COLUMN(entry.buffIDs = tableData.getStringField("buffIDs", defaultEntry.buffIDs));
tableData.nextRow();
}
}
void CDPetComponentTable::LoadValuesFromDefaults() {
m_Entries.insert(std::make_pair(defaultEntry.id, defaultEntry));
}
CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) {
auto itr = m_Entries.find(componentID);
if (itr == m_Entries.end()) {
LOG("Unable to load pet component (ID %i) values from database! Using default values instead.", componentID);
return defaultEntry;
}
return itr->second;
}

View File

@@ -1,45 +0,0 @@
#pragma once
#include "CDTable.h"
#include <cstdint>
#include <string>
struct CDPetComponent {
uint32_t id;
UNUSED_COLUMN(float minTameUpdateTime;)
UNUSED_COLUMN(float maxTameUpdateTime;)
UNUSED_COLUMN(float percentTameChance;)
UNUSED_COLUMN(float tameability;) // Mispelled as "tamability" in CDClient
UNUSED_COLUMN(uint32_t elementType;)
float walkSpeed;
float runSpeed;
float sprintSpeed;
UNUSED_COLUMN(float idleTimeMin;)
UNUSED_COLUMN(float idleTimeMax;)
UNUSED_COLUMN(uint32_t petForm;)
float imaginationDrainRate;
UNUSED_COLUMN(std::string AudioMetaEventSet;)
UNUSED_COLUMN(std::string buffIDs;)
};
class CDPetComponentTable : public CDTable<CDPetComponentTable> {
public:
/**
* Load values from the CD client database
*/
void LoadValuesFromDatabase();
/**
* Load the default values into memory instead of attempting to connect to the CD client database
*/
void LoadValuesFromDefaults();
/**
* Gets the pet component table corresponding to the pet component ID
* @returns A reference to the corresponding table, or the default if one could not be found
*/
CDPetComponent& GetByID(const uint32_t componentID);
private:
std::map<uint32_t, CDPetComponent> m_Entries;
};

View File

@@ -1,35 +0,0 @@
#include "CDPhysicsComponentTable.h"
void CDPhysicsComponentTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PhysicsComponent");
while (!tableData.eof()) {
CDPhysicsComponent entry;
entry.id = tableData.getIntField("id", -1);
entry.bStatic = tableData.getIntField("static", -1) != 0;
entry.physicsAsset = tableData.getStringField("physics_asset", "");
UNUSED(entry->jump = tableData.getIntField("jump", -1) != 0);
UNUSED(entry->doublejump = tableData.getIntField("doublejump", -1) != 0);
entry.speed = tableData.getFloatField("speed", -1);
UNUSED(entry->rotSpeed = tableData.getFloatField("rotSpeed", -1));
entry.playerHeight = tableData.getFloatField("playerHeight");
entry.playerRadius = tableData.getFloatField("playerRadius");
entry.pcShapeType = tableData.getIntField("pcShapeType");
entry.collisionGroup = tableData.getIntField("collisionGroup");
UNUSED(entry->airSpeed = tableData.getFloatField("airSpeed"));
UNUSED(entry->boundaryAsset = tableData.getStringField("boundaryAsset"));
UNUSED(entry->jumpAirSpeed = tableData.getFloatField("jumpAirSpeed"));
UNUSED(entry->friction = tableData.getFloatField("friction"));
UNUSED(entry->gravityVolumeAsset = tableData.getStringField("gravityVolumeAsset"));
m_entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow();
}
tableData.finalize();
}
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(uint32_t componentID) {
auto itr = m_entries.find(componentID);
return itr != m_entries.end() ? &itr->second : nullptr;
}

View File

@@ -1,34 +0,0 @@
#include "CDRarityTableTable.h"
void CDRarityTableTable::LoadValuesFromDatabase() {
// First, get the size of the table
uint32_t size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RarityTable");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RarityTable order by randmax desc;");
while (!tableData.eof()) {
uint32_t rarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
CDRarityTable entry;
entry.randmax = tableData.getFloatField("randmax", -1);
entry.rarity = tableData.getIntField("rarity", -1);
entries[rarityTableIndex].push_back(entry);
tableData.nextRow();
}
}
const std::vector<CDRarityTable>& CDRarityTableTable::GetRarityTable(uint32_t id) {
return entries[id];
}

View File

@@ -1,23 +0,0 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDRarityTable {
float randmax;
uint32_t rarity;
};
typedef std::vector<CDRarityTable> RarityTable;
class CDRarityTableTable : public CDTable<CDRarityTableTable> {
private:
typedef uint32_t RarityTableIndex;
std::unordered_map<RarityTableIndex, std::vector<CDRarityTable>> entries;
public:
void LoadValuesFromDatabase();
const std::vector<CDRarityTable>& GetRarityTable(uint32_t predicate);
};

View File

@@ -1,47 +0,0 @@
#include "CDRewardCodesTable.h"
void CDRewardCodesTable::LoadValuesFromDatabase() {
// First, get the size of the table
uint32_t size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RewardCodes");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RewardCodes");
while (!tableData.eof()) {
CDRewardCode entry;
entry.id = tableData.getIntField("id", -1);
entry.code = tableData.getStringField("code", "");
entry.attachmentLOT = tableData.getIntField("attachmentLOT", -1);
UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1));
UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", ""));
this->entries.push_back(entry);
tableData.nextRow();
}
}
LOT CDRewardCodesTable::GetAttachmentLOT(uint32_t rewardCodeId) const {
for (auto const &entry : this->entries){
if (rewardCodeId == entry.id) return entry.attachmentLOT;
}
return LOT_NULL;
}
uint32_t CDRewardCodesTable::GetCodeID(std::string code) const {
for (auto const &entry : this->entries){
if (code == entry.code) return entry.id;
}
return -1;
}

View File

@@ -1,25 +0,0 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDRewardCode {
uint32_t id;
std::string code;
LOT attachmentLOT;
UNUSED(uint32_t locStatus);
UNUSED(std::string gate_version);
};
class CDRewardCodesTable : public CDTable<CDRewardCodesTable> {
private:
std::vector<CDRewardCode> entries;
public:
void LoadValuesFromDatabase();
const std::vector<CDRewardCode>& GetEntries() const;
LOT GetAttachmentLOT(uint32_t rewardCodeId) const;
uint32_t GetCodeID(std::string code) const;
};

View File

@@ -1,29 +0,0 @@
#include "CDRewardsTable.h"
void CDRewardsTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Rewards");
while (!tableData.eof()) {
CDRewards entry;
entry.id = tableData.getIntField("id", -1);
entry.levelID = tableData.getIntField("LevelID", -1);
entry.missionID = tableData.getIntField("MissionID", -1);
entry.rewardType = tableData.getIntField("RewardType", -1);
entry.value = tableData.getIntField("value", -1);
entry.count = tableData.getIntField("count", -1);
m_entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow();
}
tableData.finalize();
}
std::vector<CDRewards> CDRewardsTable::GetByLevelID(uint32_t levelID) {
std::vector<CDRewards> result{};
for (const auto& e : m_entries) {
if (e.second.levelID == levelID) result.push_back(e.second);
}
return result;
}

View File

@@ -1,39 +0,0 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDSkillBehavior {
uint32_t skillID; //!< The Skill ID of the skill
UNUSED(uint32_t locStatus); //!< ??
uint32_t behaviorID; //!< The Behavior ID of the skill
uint32_t imaginationcost; //!< The imagination cost of the skill
uint32_t cooldowngroup; //!< The cooldown group ID of the skill
float cooldown; //!< The cooldown time of the skill
UNUSED(bool isNpcEditor); //!< ???
UNUSED(uint32_t skillIcon); //!< The Skill Icon ID
UNUSED(std::string oomSkillID); //!< ???
UNUSED(uint32_t oomBehaviorEffectID); //!< ???
UNUSED(uint32_t castTypeDesc); //!< The cast type description(?)
UNUSED(uint32_t imBonusUI); //!< The imagination bonus of the skill
UNUSED(nint32_t lifeBonusUI); //!< The life bonus of the skill
UNUSED(uint32_t armorBonusUI); //!< The armor bonus of the skill
UNUSED(uint32_t damageUI); //!< ???
UNUSED(bool hideIcon); //!< Whether or not to show the icon
UNUSED(bool localize); //!< ???
UNUSED(std::string gate_version); //!< ???
UNUSED(uint32_t cancelType); //!< The cancel type (?)
};
class CDSkillBehaviorTable : public CDTable<CDSkillBehaviorTable> {
private:
std::map<uint32_t, CDSkillBehavior> entries;
CDSkillBehavior m_empty;
public:
void LoadValuesFromDatabase();
// Gets an entry by skillID
const CDSkillBehavior& GetSkillByID(uint32_t skillID);
};

View File

@@ -1,12 +0,0 @@
set(DDATABASE_CDCLIENTDATABASE_SOURCES
"CDClientDatabase.cpp"
"CDClientManager.cpp"
)
add_subdirectory(CDClientTables)
foreach(file ${DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES})
set(DDATABASE_CDCLIENTDATABASE_SOURCES ${DDATABASE_CDCLIENTDATABASE_SOURCES} "CDClientTables/${file}")
endforeach()
set(DDATABASE_CDCLIENTDATABASE_SOURCES ${DDATABASE_CDCLIENTDATABASE_SOURCES} PARENT_SCOPE)

View File

@@ -0,0 +1,80 @@
#include "CDClientManager.h"
#include "CDActivityRewardsTable.h"
#include "CDAnimationsTable.h"
#include "CDBehaviorParameterTable.h"
#include "CDBehaviorTemplateTable.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"
CDClientManager::CDClientManager() {
CDActivityRewardsTable::Instance();
CDAnimationsTable::Instance();
CDBehaviorParameterTable::Instance();
CDBehaviorTemplateTable::Instance();
CDComponentsRegistryTable::Instance();
CDCurrencyTableTable::Instance();
CDDestructibleComponentTable::Instance();
CDEmoteTableTable::Instance();
CDInventoryComponentTable::Instance();
CDItemComponentTable::Instance();
CDItemSetsTable::Instance();
CDItemSetSkillsTable::Instance();
CDLevelProgressionLookupTable::Instance();
CDLootMatrixTable::Instance();
CDLootTableTable::Instance();
CDMissionNPCComponentTable::Instance();
CDMissionTasksTable::Instance();
CDMissionsTable::Instance();
CDObjectSkillsTable::Instance();
CDObjectsTable::Instance();
CDPhysicsComponentTable::Instance();
CDRebuildComponentTable::Instance();
CDScriptComponentTable::Instance();
CDSkillBehaviorTable::Instance();
CDZoneTableTable::Instance();
CDVendorComponentTable::Instance();
CDActivitiesTable::Instance();
CDPackageComponentTable::Instance();
CDProximityMonitorComponentTable::Instance();
CDMovementAIComponentTable::Instance();
CDBrickIDTableTable::Instance();
CDRarityTableTable::Instance();
CDMissionEmailTable::Instance();
CDRewardsTable::Instance();
CDPropertyEntranceComponentTable::Instance();
CDPropertyTemplateTable::Instance();
CDFeatureGatingTable::Instance();
CDRailActivatorComponentTable::Instance();
}

View File

@@ -11,10 +11,7 @@
*/ */
class CDClientManager : public Singleton<CDClientManager> { class CDClientManager : public Singleton<CDClientManager> {
public: public:
CDClientManager() = default; CDClientManager();
void LoadValuesFromDatabase();
void LoadValuesFromDefaults();
/** /**
* Fetch a table from CDClient * Fetch a table from CDClient

View File

@@ -1,20 +1,13 @@
set(DDATABASE_SOURCES) set(DDATABASE_SOURCES "CDClientDatabase.cpp"
"CDClientManager.cpp"
"Database.cpp"
"MigrationRunner.cpp")
add_subdirectory(CDClientDatabase) add_subdirectory(Tables)
foreach(file ${DDATABASE_CDCLIENTDATABASE_SOURCES}) foreach(file ${DDATABASE_TABLES_SOURCES})
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "CDClientDatabase/${file}") set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}")
endforeach()
add_subdirectory(GameDatabase)
foreach(file ${DDATABASE_GAMEDATABASE_SOURCES})
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "GameDatabase/${file}")
endforeach() endforeach()
add_library(dDatabase STATIC ${DDATABASE_SOURCES}) add_library(dDatabase STATIC ${DDATABASE_SOURCES})
target_link_libraries(dDatabase sqlite3 mariadbConnCpp) target_link_libraries(dDatabase sqlite3 mariadbConnCpp)
if (${CDCLIENT_CACHE_ALL})
add_compile_definitions(dDatabase CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL})
endif()

118
dDatabase/Database.cpp Normal file
View File

@@ -0,0 +1,118 @@
#include "Database.h"
#include "Game.h"
#include "dConfig.h"
#include "dLogger.h"
using namespace std;
#pragma warning (disable:4251) //Disables SQL warnings
sql::Driver* Database::driver;
sql::Connection* Database::con;
sql::Properties Database::props;
std::string Database::database;
void Database::Connect(const string& host, const string& database, const string& username, const string& password) {
//To bypass debug issues:
const char* szDatabase = database.c_str();
const char* szUsername = username.c_str();
const char* szPassword = password.c_str();
driver = sql::mariadb::get_driver_instance();
sql::Properties properties;
// The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where
// 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the
// presence of a /
// 2) even avoiding that, the connector still assumes you're connecting with a tcp socket
// So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost,
// which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate
// property manually (which the URL parsing fails to do)
const std::string UNIX_PROTO = "unix://";
const std::string PIPE_PROTO = "pipe://";
if (host.find(UNIX_PROTO) == 0) {
properties["hostName"] = "unix://localhost";
properties["localSocket"] = host.substr(UNIX_PROTO.length()).c_str();
} else if (host.find(PIPE_PROTO) == 0) {
properties["hostName"] = "pipe://localhost";
properties["pipe"] = host.substr(PIPE_PROTO.length()).c_str();
} else {
properties["hostName"] = host.c_str();
}
properties["user"] = szUsername;
properties["password"] = szPassword;
properties["autoReconnect"] = "true";
Database::props = properties;
Database::database = database;
Database::Connect();
}
void Database::Connect() {
// `connect(const Properties& props)` segfaults in windows debug, but
// `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly
if (Database::props.find("localSocket") != Database::props.end() || Database::props.find("pipe") != Database::props.end()) {
con = driver->connect(Database::props);
} else {
con = driver->connect(Database::props["hostName"].c_str(), Database::props["user"].c_str(), Database::props["password"].c_str());
}
con->setSchema(Database::database.c_str());
}
void Database::Destroy(std::string source, bool log) {
if (!con) return;
if (log) {
if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!", source.c_str());
else Game::logger->Log("Database", "Destroying MySQL connection!");
}
con->close();
delete con;
} //Destroy
sql::Statement* Database::CreateStmt() {
sql::Statement* toReturn = con->createStatement();
return toReturn;
} //CreateStmt
sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) {
const char* test = query.c_str();
size_t size = query.length();
sql::SQLString str(test, size);
if (!con) {
Connect();
Game::logger->Log("Database", "Trying to reconnect to MySQL");
}
if (!con->isValid() || con->isClosed()) {
delete con;
con = nullptr;
Connect();
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection");
}
auto* stmt = con->prepareStatement(str);
return stmt;
} //CreatePreppedStmt
void Database::Commit() {
Database::con->commit();
}
bool Database::GetAutoCommit() {
// TODO This should not just access a pointer. A future PR should update this
// to check for null and throw an error if the connection is not valid.
return con->getAutoCommit();
}
void Database::SetAutoCommit(bool value) {
// TODO This should not just access a pointer. A future PR should update this
// to check for null and throw an error if the connection is not valid.
Database::con->setAutoCommit(value);
}

31
dDatabase/Database.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <conncpp.hpp>
class MySqlException : public std::runtime_error {
public:
MySqlException() : std::runtime_error("MySQL error!") {}
MySqlException(const std::string& msg) : std::runtime_error(msg.c_str()) {}
};
class Database {
private:
static sql::Driver* driver;
static sql::Connection* con;
static sql::Properties props;
static std::string database;
public:
static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
static void Connect();
static void Destroy(std::string source = "", bool log = true);
static sql::Statement* CreateStmt();
static sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
static void Commit();
static bool GetAutoCommit();
static void SetAutoCommit(bool value);
static std::string GetDatabase() { return database; }
static sql::Properties GetProperties() { return props; }
};

Some files were not shown because too many files have changed in this diff Show More