mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-16 20:24:39 -06:00
Compare commits
106 Commits
persistent
...
fix-crashf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcd7e8d299 | ||
|
|
2804dc3ec2 | ||
|
|
870b56fe02 | ||
|
|
66ac5a1b7a | ||
| 321d354e96 | |||
| 72b69c7899 | |||
|
|
d283bbd1c4 | ||
|
|
10baa98f00 | ||
|
|
c7c84c21ef | ||
|
|
09fb1dfff9 | ||
| 9116317834 | |||
|
|
a84ca1f00d | ||
|
|
bb79528c0e | ||
|
|
b1134b340f | ||
|
|
1941679d27 | ||
|
|
85672e060a | ||
|
|
18feea5fed | ||
|
|
e54faa3820 | ||
|
|
4ecb6ae30e | ||
|
|
c708246f73 | ||
|
|
295ba628c2 | ||
|
|
d8f74f008f | ||
|
|
42a71bbeab | ||
|
|
98d2f25af2 | ||
|
|
dd9d94f75f | ||
|
|
f08df25085 | ||
| 1bdce8384f | |||
| 15954413ae | |||
| fddf99946f | |||
|
|
ef6f2f133e | ||
|
|
46ac039a3b | ||
|
|
ffa2f9986c | ||
|
|
d2d67384a7 | ||
|
|
dbf37c2d2b | ||
|
|
81dc4e2216 | ||
|
|
6de224a2fa | ||
|
|
5e9355b1ff | ||
|
|
6b9798595e | ||
|
|
e58218cfbc | ||
|
|
fcf4d6c6fa | ||
|
|
c1e8546d48 | ||
|
|
c07c909a57 | ||
| 42ffd00478 | |||
|
|
799269c79e | ||
|
|
e2391665b9 | ||
|
|
24c2361248 | ||
|
|
511672c5cb | ||
|
|
c0b969e3f0 | ||
| 1f399a7277 | |||
| 3d85f6639e | |||
|
|
731b828c12 | ||
|
|
eca87c7257 | ||
|
|
0b9dbaedbf | ||
| 7de07a7722 | |||
|
|
62a1e135c3 | ||
|
|
de0b560a8c | ||
| e8a0f50ec9 | |||
| b3564cb9ea | |||
|
|
3df3552467 | ||
|
|
a1f8ab763d | ||
|
|
0c32be01ba | ||
|
|
8a15906885 | ||
|
|
af70f871cb | ||
|
|
fd20baaf09 | ||
|
|
0217f88c44 | ||
|
|
198b3371c5 | ||
|
|
4e5facd0c5 | ||
|
|
c931f5d456 | ||
| df83f0d847 | |||
|
|
9c5388c70e | ||
|
|
1a199151da | ||
|
|
98dc291b57 | ||
|
|
1001e41528 | ||
|
|
59bf91b14f | ||
|
|
50ad2a29ec | ||
|
|
81b1b73f04 | ||
|
|
12ea2dfb2e | ||
|
|
d193fe61be | ||
|
|
511407c8ea | ||
|
|
df3515f474 | ||
| 8e84cafdfa | |||
|
|
57e3a4f4ef | ||
|
|
98822d400f | ||
|
|
7f623d358c | ||
| b68823b4cb | |||
|
|
7bbe5ffc39 | ||
|
|
0ddd20e2b5 | ||
|
|
f59ca8b1da | ||
|
|
a44f998bd1 | ||
| 78d8c57752 | |||
|
|
8b270ca97a | ||
|
|
59303a232e | ||
| 8cd5bf7b8d | |||
| 8a9883c224 | |||
|
|
79ff6e7ee4 | ||
|
|
4167d98667 | ||
|
|
23c5d13151 | ||
|
|
68e5737b74 | ||
|
|
411dce7457 | ||
| 175b354e68 | |||
| 4c3949e5d8 | |||
| f727e3951c | |||
| 4867136133 | |||
| 6e07798023 | |||
| 3dc7b6ef7f | |||
|
|
a19bead268 |
@@ -3,8 +3,8 @@ Dockerfile
|
||||
*.md
|
||||
logo.png
|
||||
versions.txt
|
||||
build.sh
|
||||
docker-compose.yml
|
||||
.env
|
||||
docker/__pycache__
|
||||
.env.example
|
||||
.env.example
|
||||
build
|
||||
|
||||
56
.github/workflows/build-and-push-docker.yml
vendored
Normal file
56
.github/workflows/build-and-push-docker.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
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
1
.gitignore
vendored
@@ -122,3 +122,4 @@ docker/__pycache__
|
||||
docker-compose.override.yml
|
||||
|
||||
!*Test.bin
|
||||
!cmake/*
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -14,6 +14,6 @@
|
||||
path = thirdparty/mariadb-connector-cpp
|
||||
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
|
||||
ignore = dirty
|
||||
[submodule "thirdparty/AccountManager"]
|
||||
path = thirdparty/AccountManager
|
||||
url = https://github.com/DarkflameUniverse/AccountManager
|
||||
[submodule "thirdparty/magic_enum"]
|
||||
path = thirdparty/magic_enum
|
||||
url = https://github.com/Neargye/magic_enum.git
|
||||
|
||||
223
CMakeLists.txt
223
CMakeLists.txt
@@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.18)
|
||||
project(Darkflame)
|
||||
include(CTest)
|
||||
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
# Read variables from file
|
||||
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
|
||||
@@ -14,30 +15,26 @@ string(REPLACE "\n" ";" variables ${variables})
|
||||
foreach(variable ${variables})
|
||||
# If the string contains a #, skip it
|
||||
if(NOT "${variable}" MATCHES "#")
|
||||
|
||||
# Split the variable into name and value
|
||||
string(REPLACE "=" ";" variable ${variable})
|
||||
|
||||
# Check that the length of the variable is 2 (name and value)
|
||||
list(LENGTH variable length)
|
||||
if(${length} EQUAL 2)
|
||||
|
||||
if(${length} EQUAL 2)
|
||||
list(GET variable 0 variable_name)
|
||||
list(GET variable 1 variable_value)
|
||||
|
||||
# Set the variable
|
||||
set(${variable_name} ${variable_value})
|
||||
|
||||
# Add compiler definition
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}")
|
||||
|
||||
message(STATUS "Variable: ${variable_name} = ${variable_value}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Set the version
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
set(PROJECT_VERSION "\"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"")
|
||||
|
||||
# Echo the version
|
||||
message(STATUS "Version: ${PROJECT_VERSION}")
|
||||
@@ -53,19 +50,22 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
|
||||
# Disabled no-register
|
||||
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC -lstdc++fs")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -fPIC")
|
||||
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)
|
||||
|
||||
if(NOT APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -lstdc++fs")
|
||||
endif()
|
||||
if (__dynamic AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
|
||||
if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
|
||||
endif()
|
||||
if (__ggdb)
|
||||
|
||||
if(${GGDB})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
|
||||
elseif(MSVC)
|
||||
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
|
||||
@@ -98,47 +98,64 @@ make_directory(${CMAKE_BINARY_DIR}/logs)
|
||||
# Copy resource files on first build
|
||||
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf")
|
||||
message(STATUS "Checking resource file integrity")
|
||||
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)
|
||||
if (EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||
|
||||
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
|
||||
endif()
|
||||
if (${file_size} EQUAL 0)
|
||||
|
||||
if(${file_size} EQUAL 0)
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
|
||||
COPYONLY
|
||||
)
|
||||
message(STATUS "Moved " ${resource_file} " to project binary directory")
|
||||
elseif (resource_file MATCHES ".ini")
|
||||
elseif(resource_file MATCHES ".ini")
|
||||
message(STATUS "Checking " ${resource_file} " for missing config options")
|
||||
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents)
|
||||
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
|
||||
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
|
||||
set(parsed_current_file_contents "")
|
||||
|
||||
# Remove comment lines so they do not interfere with the variable parsing
|
||||
foreach (line ${current_file_contents})
|
||||
foreach(line ${current_file_contents})
|
||||
string(FIND ${line} "#" is_comment)
|
||||
if (NOT ${is_comment} EQUAL 0)
|
||||
|
||||
if(NOT ${is_comment} EQUAL 0)
|
||||
string(APPEND parsed_current_file_contents ${line})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
file(READ ${CMAKE_SOURCE_DIR}/resources/${resource_file} depot_file_contents)
|
||||
string(REPLACE "\\\n" "" depot_file_contents ${depot_file_contents})
|
||||
string(REPLACE "\n" ";" depot_file_contents ${depot_file_contents})
|
||||
set(line_to_add "")
|
||||
foreach (line ${depot_file_contents})
|
||||
|
||||
foreach(line ${depot_file_contents})
|
||||
string(FIND ${line} "#" is_comment)
|
||||
if (NOT ${is_comment} EQUAL 0)
|
||||
|
||||
if(NOT ${is_comment} EQUAL 0)
|
||||
string(REPLACE "=" ";" line_split ${line})
|
||||
list(GET line_split 0 variable_name)
|
||||
if (NOT ${parsed_current_file_contents} MATCHES ${variable_name})
|
||||
|
||||
if(NOT ${parsed_current_file_contents} MATCHES ${variable_name})
|
||||
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file})
|
||||
set(line_to_add ${line_to_add} ${line})
|
||||
foreach (line_to_append ${line_to_add})
|
||||
|
||||
foreach(line_to_append ${line_to_add})
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
|
||||
endif()
|
||||
|
||||
set(line_to_add "")
|
||||
else()
|
||||
set(line_to_add ${line_to_add} ${line})
|
||||
@@ -146,24 +163,23 @@ foreach (resource_file ${RESOURCE_FILES})
|
||||
endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Resource file integrity check complete")
|
||||
|
||||
# if navmeshes directory does not exist, create it
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes)
|
||||
if(NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes)
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/navmeshes)
|
||||
endif()
|
||||
|
||||
# Copy navmesh data on first build and extract it
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip
|
||||
COPYONLY
|
||||
)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip COPYONLY)
|
||||
|
||||
file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip DESTINATION ${PROJECT_BINARY_DIR}/navmeshes)
|
||||
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
|
||||
|
||||
# Copy vanity files on first build
|
||||
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
|
||||
|
||||
foreach(file ${VANITY_FILES})
|
||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
|
||||
endforeach()
|
||||
@@ -171,26 +187,18 @@ endforeach()
|
||||
# Move our migrations for MasterServer to run
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/)
|
||||
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
|
||||
|
||||
foreach(file ${SQL_FILES})
|
||||
get_filename_component(file ${file} NAME)
|
||||
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()
|
||||
configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
|
||||
endforeach()
|
||||
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
|
||||
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)
|
||||
|
||||
foreach(file ${SQL_FILES})
|
||||
get_filename_component(file ${file} NAME)
|
||||
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()
|
||||
configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
|
||||
endforeach()
|
||||
|
||||
# Create our list of include directories
|
||||
@@ -198,7 +206,9 @@ set(INCLUDED_DIRECTORIES
|
||||
"dCommon"
|
||||
"dCommon/dClient"
|
||||
"dCommon/dEnums"
|
||||
|
||||
"dChatFilter"
|
||||
|
||||
"dGame"
|
||||
"dGame/dBehaviors"
|
||||
"dGame/dComponents"
|
||||
@@ -209,116 +219,54 @@ set(INCLUDED_DIRECTORIES
|
||||
"dGame/dPropertyBehaviors"
|
||||
"dGame/dPropertyBehaviors/ControlBehaviorMessages"
|
||||
"dGame/dUtilities"
|
||||
|
||||
"dPhysics"
|
||||
|
||||
"dNavigation"
|
||||
"dNavigation/dTerrain"
|
||||
"dZoneManager"
|
||||
"dDatabase"
|
||||
"dDatabase/Tables"
|
||||
"dNet"
|
||||
"dScripts"
|
||||
"dScripts/02_server"
|
||||
"dScripts/ai"
|
||||
"dScripts/client"
|
||||
"dScripts/EquipmentScripts"
|
||||
"dScripts/EquipmentTriggers"
|
||||
"dScripts/zone"
|
||||
"dScripts/02_server/DLU"
|
||||
"dScripts/02_server/Enemy"
|
||||
"dScripts/02_server/Equipment"
|
||||
"dScripts/02_server/Map"
|
||||
"dScripts/02_server/Minigame"
|
||||
"dScripts/02_server/Objects"
|
||||
"dScripts/02_server/Pets"
|
||||
"dScripts/02_server/Enemy/AG"
|
||||
"dScripts/02_server/Enemy/AM"
|
||||
"dScripts/02_server/Enemy/FV"
|
||||
"dScripts/02_server/Enemy/General"
|
||||
"dScripts/02_server/Enemy/Survival"
|
||||
"dScripts/02_server/Enemy/VE"
|
||||
"dScripts/02_server/Enemy/Waves"
|
||||
"dScripts/02_server/Map/AG"
|
||||
"dScripts/02_server/Map/AG_Spider_Queen"
|
||||
"dScripts/02_server/Map/AM"
|
||||
"dScripts/02_server/Map/FV"
|
||||
"dScripts/02_server/Map/General"
|
||||
"dScripts/02_server/Map/GF"
|
||||
"dScripts/02_server/Map/njhub"
|
||||
"dScripts/02_server/Map/NS"
|
||||
"dScripts/02_server/Map/NT"
|
||||
"dScripts/02_server/Map/PR"
|
||||
"dScripts/02_server/Map/Property"
|
||||
"dScripts/02_server/Map/SS"
|
||||
"dScripts/02_server/Map/VE"
|
||||
"dScripts/02_server/Map/FV/Racing"
|
||||
"dScripts/02_server/Map/General/Ninjago"
|
||||
"dScripts/02_server/Map/njhub/boss_instance"
|
||||
"dScripts/02_server/Map/NS/Waves"
|
||||
"dScripts/02_server/Map/Property/AG_Med"
|
||||
"dScripts/02_server/Map/Property/AG_Small"
|
||||
"dScripts/02_server/Map/Property/NS_Med"
|
||||
"dScripts/02_server/Minigame/General"
|
||||
"dScripts/ai/ACT"
|
||||
"dScripts/ai/AG"
|
||||
"dScripts/ai/FV"
|
||||
"dScripts/ai/GENERAL"
|
||||
"dScripts/ai/GF"
|
||||
"dScripts/ai/MINIGAME"
|
||||
"dScripts/ai/NP"
|
||||
"dScripts/ai/NS"
|
||||
"dScripts/ai/PETS"
|
||||
"dScripts/ai/PROPERTY"
|
||||
"dScripts/ai/RACING"
|
||||
"dScripts/ai/SPEC"
|
||||
"dScripts/ai/WILD"
|
||||
"dScripts/ai/ACT/FootRace"
|
||||
"dScripts/ai/MINIGAME/SG_GF"
|
||||
"dScripts/ai/MINIGAME/SG_GF/SERVER"
|
||||
"dScripts/ai/NS/NS_PP_01"
|
||||
"dScripts/ai/NS/WH"
|
||||
"dScripts/ai/PROPERTY/AG"
|
||||
"dScripts/ai/RACING/OBJECTS"
|
||||
"dScripts/client/ai"
|
||||
"dScripts/client/ai/PR"
|
||||
"dScripts/zone/AG"
|
||||
"dScripts/zone/LUPs"
|
||||
"dScripts/zone/PROPERTY"
|
||||
"dScripts/zone/PROPERTY/FV"
|
||||
"dScripts/zone/PROPERTY/GF"
|
||||
"dScripts/zone/PROPERTY/NS"
|
||||
|
||||
"dZoneManager"
|
||||
|
||||
"dDatabase"
|
||||
"dDatabase/CDClientDatabase"
|
||||
"dDatabase/CDClientDatabase/CDClientTables"
|
||||
"dDatabase/GameDatabase"
|
||||
"dDatabase/GameDatabase/ITables"
|
||||
"dDatabase/GameDatabase/MySQL"
|
||||
"dDatabase/GameDatabase/MySQL/Tables"
|
||||
|
||||
"dNet"
|
||||
|
||||
"thirdparty/magic_enum/include/magic_enum"
|
||||
"thirdparty/raknet/Source"
|
||||
"thirdparty/tinyxml2"
|
||||
"thirdparty/recastnavigation"
|
||||
"thirdparty/SQLite"
|
||||
"thirdparty/cpplinq"
|
||||
"thirdparty/cpp-httplib"
|
||||
|
||||
"tests"
|
||||
"tests/dCommonTests"
|
||||
"tests/dGameTests"
|
||||
"tests/dGameTests/dComponentsTests"
|
||||
)
|
||||
)
|
||||
|
||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
||||
if (APPLE)
|
||||
if(APPLE)
|
||||
include_directories("/usr/local/include/")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include")
|
||||
elseif (UNIX)
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt")
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt")
|
||||
endif()
|
||||
|
||||
# Add binary directory as an include directory
|
||||
include_directories(${PROJECT_BINARY_DIR})
|
||||
|
||||
# Actually include the directories from our list
|
||||
foreach (dir ${INCLUDED_DIRECTORIES})
|
||||
foreach(dir ${INCLUDED_DIRECTORIES})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/${dir})
|
||||
endforeach()
|
||||
|
||||
if(NOT WIN32)
|
||||
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt")
|
||||
endif()
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include")
|
||||
|
||||
# Add linking directories:
|
||||
link_directories(${PROJECT_BINARY_DIR})
|
||||
|
||||
@@ -329,8 +277,9 @@ add_subdirectory(thirdparty)
|
||||
file(
|
||||
GLOB HEADERS_DDATABASE
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables/*.h
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
|
||||
)
|
||||
|
||||
@@ -367,13 +316,13 @@ add_subdirectory(dNavigation)
|
||||
add_subdirectory(dPhysics)
|
||||
|
||||
# Create a list of common libraries shared between all binaries
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp")
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp" "magic_enum")
|
||||
|
||||
# Add platform specific common libraries
|
||||
if (UNIX)
|
||||
if(UNIX)
|
||||
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread")
|
||||
|
||||
if (NOT APPLE AND __include_backtrace__)
|
||||
if(NOT APPLE AND ${INCLUDE_BACKTRACE})
|
||||
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace")
|
||||
endif()
|
||||
endif()
|
||||
@@ -384,12 +333,6 @@ add_subdirectory(dAuthServer)
|
||||
add_subdirectory(dChatServer)
|
||||
add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries
|
||||
|
||||
# Add our precompiled headers
|
||||
target_precompile_headers(
|
||||
dGame PRIVATE
|
||||
${HEADERS_DGAME}
|
||||
)
|
||||
|
||||
target_precompile_headers(
|
||||
dZoneManager PRIVATE
|
||||
${HEADERS_DZONEMANAGER}
|
||||
@@ -411,6 +354,6 @@ target_precompile_headers(
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
|
||||
)
|
||||
|
||||
if (${__enable_testing__} MATCHES "1")
|
||||
if(${ENABLE_TESTING})
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
PROJECT_VERSION_MAJOR=1
|
||||
PROJECT_VERSION_MINOR=1
|
||||
PROJECT_VERSION_PATCH=1
|
||||
# LICENSE
|
||||
LICENSE=AGPL-3.0
|
||||
|
||||
# Debugging
|
||||
# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
||||
__dynamic=1
|
||||
# Set __ggdb to 1 to enable the -ggdb flag for the linker, including more debug info.
|
||||
# __ggdb=1
|
||||
# Set __include_backtrace__ to 1 to includes the backtrace library for better crashlogs.
|
||||
# __include_backtrace__=1
|
||||
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
|
||||
# __compile_backtrace__=1
|
||||
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
||||
DYNAMIC=1
|
||||
|
||||
# Set GGDB to 1 to enable the -ggdb flag for the linker, including more debug info.
|
||||
# Do note, changing this will re-build the whole server
|
||||
GGDB=0
|
||||
|
||||
# Set INCLUDE_BACKTRACE to 1 to includes the backtrace library for better crashlogs.
|
||||
# Do note, changing this will re-build the whole server
|
||||
INCLUDE_BACKTRACE=0
|
||||
|
||||
# Set COMPILE_BACKTRACE to 1 to compile the backtrace library instead of using system libraries.
|
||||
# Do note, changing this will re-build the whole server
|
||||
COMPILE_BACKTRACE=0
|
||||
|
||||
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with.
|
||||
__maria_db_connector_compile_jobs__=1
|
||||
MARIADB_CONNECTOR_COMPILE_JOBS=1
|
||||
|
||||
# When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
|
||||
__enable_testing__=1
|
||||
ENABLE_TESTING=1
|
||||
|
||||
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
|
||||
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
|
||||
# Uncomment the below line to cache the entire CDClient into memory
|
||||
# CDCLIENT_CACHE_ALL=1
|
||||
|
||||
# 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
46
Docker.md
@@ -1,46 +0,0 @@
|
||||
# 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`
|
||||
@@ -1,58 +0,0 @@
|
||||
# Installation under Windows
|
||||
## First Run
|
||||
1. Navigate to the [Docker download page](https://www.docker.com/products/docker-desktop) and download docker.
|
||||
|
||||

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

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

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

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

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

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

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

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

|
||||
51
Dockerfile
Normal file
51
Dockerfile
Normal file
@@ -0,0 +1,51 @@
|
||||
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" ]
|
||||
40
README.md
40
README.md
@@ -23,6 +23,9 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s
|
||||
### Supply of resource files
|
||||
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.
|
||||
|
||||
## Step by step walkthrough for a single-player server
|
||||
If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.
|
||||
|
||||
## Steps to setup server
|
||||
* [Clone this repository](#clone-the-repository)
|
||||
* [Install dependencies](#install-dependencies)
|
||||
@@ -34,6 +37,7 @@ Darkflame Universe is a server emulator and does not distribute any LEGO® Unive
|
||||
* [Verify your setup](#verify-your-setup)
|
||||
* [Running the server](#running-the-server)
|
||||
* [User Guide](#user-guide)
|
||||
* [Docker](#docker)
|
||||
|
||||
## Clone the repository
|
||||
If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win)
|
||||
@@ -344,6 +348,42 @@ certutil -hashfile <file> SHA1
|
||||
Known good *SHA1* checksum of the Darkflame Universe client:
|
||||
- `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed)
|
||||
|
||||
# Docker
|
||||
|
||||
## Standalone
|
||||
|
||||
For standalone deployment, you can use the image provided via Github's Container Repository:
|
||||
`ghcr.io/darkflameuniverse/darkflameserver`
|
||||
|
||||
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`
|
||||
|
||||
## Compose
|
||||
|
||||
See the [compose](docker-compose.yml) file in the root of the repo.
|
||||
|
||||
This compose file is for a full deployment: MariaDB, DarkflameServer, and Nexus Dashboard.
|
||||
|
||||
All of the environmental options are listed in the compose file so the can be pass through, or you can edit the compose file to suit your specific needs
|
||||
|
||||
# Development Documentation
|
||||
This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server
|
||||
[Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html)
|
||||
|
||||
51
cmake/Utils.cmake
Normal file
51
cmake/Utils.cmake
Normal file
@@ -0,0 +1,51 @@
|
||||
# Parses a config file for a specific option and appends the new option if it does not exist
|
||||
# If the new option does exist, this function will do nothing.
|
||||
# file_name: The name of the file to parse
|
||||
# old_option_name: The name of the option to find
|
||||
# new_option_name: The name of the option to add
|
||||
function(UpdateConfigOption file_name old_option_name new_option_name)
|
||||
string(APPEND old_option_name "=")
|
||||
string(APPEND new_option_name "=")
|
||||
message(STATUS "Checking " ${file_name} " for " ${old_option_name} " and adding " ${new_option_name} " if it does not exist")
|
||||
if(NOT EXISTS ${file_name})
|
||||
message(STATUS ${file_name} " does not exist. Doing nothing")
|
||||
return()
|
||||
endif()
|
||||
file(READ ${file_name} current_file_contents)
|
||||
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
|
||||
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
|
||||
set(parsed_current_file_contents "")
|
||||
|
||||
# Remove comment lines so they do not interfere with the variable parsing
|
||||
foreach(line ${current_file_contents})
|
||||
string(FIND ${line} "#" is_comment)
|
||||
|
||||
if(NOT ${is_comment} EQUAL 0)
|
||||
string(APPEND parsed_current_file_contents ${line})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(found_new_option -1)
|
||||
set(found_old_option -1)
|
||||
set(current_value -1)
|
||||
|
||||
foreach(line ${current_file_contents})
|
||||
string(FIND ${line} ${old_option_name} old_option_in_file)
|
||||
|
||||
if(${old_option_in_file} EQUAL 0)
|
||||
set(found_old_option 1)
|
||||
set(current_value ${line})
|
||||
endif()
|
||||
|
||||
string(FIND ${line} ${new_option_name} found_new_option_in_file)
|
||||
|
||||
if(${found_new_option_in_file} EQUAL 0)
|
||||
set(found_new_option 1)
|
||||
endif()
|
||||
endforeach(line ${current_file_contents})
|
||||
|
||||
if(${found_old_option} EQUAL 1 AND NOT ${found_new_option} EQUAL 1)
|
||||
string(REPLACE ${old_option_name} ${new_option_name} current_value ${current_value})
|
||||
file(APPEND ${file_name} "\n" ${current_value})
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include <csignal>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
@@ -15,7 +16,7 @@
|
||||
|
||||
//RakNet includes:
|
||||
#include "RakNetDefines.h"
|
||||
#include <MessageIdentifiers.h>
|
||||
#include "MessageIdentifiers.h"
|
||||
|
||||
//Auth includes:
|
||||
#include "AuthPackets.h"
|
||||
@@ -28,7 +29,7 @@ namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
dServer* server = nullptr;
|
||||
dConfig* config = nullptr;
|
||||
bool shouldShutdown = false;
|
||||
Game::signal_t lastSignal = 0;
|
||||
std::mt19937 randomEngine;
|
||||
}
|
||||
|
||||
@@ -42,27 +43,24 @@ int main(int argc, char** argv) {
|
||||
Diagnostics::SetProcessFileName(argv[0]);
|
||||
Diagnostics::Initialize();
|
||||
|
||||
std::signal(SIGINT, Game::OnSignal);
|
||||
std::signal(SIGTERM, Game::OnSignal);
|
||||
|
||||
//Create all the objects we need to run our service:
|
||||
Game::logger = SetupLogger();
|
||||
if (!Game::logger) return EXIT_FAILURE;
|
||||
|
||||
//Read our config:
|
||||
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string());
|
||||
Game::config = new dConfig("authconfig.ini");
|
||||
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
|
||||
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
|
||||
|
||||
LOG("Starting Auth server...");
|
||||
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||
LOG("Version: %s", PROJECT_VERSION);
|
||||
LOG("Compiled on: %s", __TIMESTAMP__);
|
||||
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||
|
||||
try {
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
Database::Connect();
|
||||
} catch (sql::SQLException& ex) {
|
||||
LOG("Got an error while connecting to the database: %s", ex.what());
|
||||
Database::Destroy("AuthServer");
|
||||
@@ -74,25 +72,26 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1500;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
}
|
||||
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||
uint32_t maxClients = 50;
|
||||
uint32_t maxClients = 999;
|
||||
uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default.
|
||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||
std::string ourIP = "localhost";
|
||||
GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients);
|
||||
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(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
|
||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
@@ -103,13 +102,18 @@ int main(int argc, char** argv) {
|
||||
uint32_t framesSinceMasterDisconnect = 0;
|
||||
uint32_t framesSinceLastSQLPing = 0;
|
||||
|
||||
while (!Game::shouldShutdown) {
|
||||
AuthPackets::LoadClaimCodes();
|
||||
|
||||
Game::logger->Flush(); // once immediately before main loop
|
||||
while (!Game::ShouldShutdown()) {
|
||||
//Check if we're still connected to master:
|
||||
if (!Game::server->GetIsConnectedToMaster()) {
|
||||
framesSinceMasterDisconnect++;
|
||||
|
||||
if (framesSinceMasterDisconnect >= authFramerate)
|
||||
if (framesSinceMasterDisconnect >= authFramerate) {
|
||||
LOG("No connection to master!");
|
||||
break; //Exit our loop, shut down.
|
||||
}
|
||||
} else framesSinceMasterDisconnect = 0;
|
||||
|
||||
//In world we'd update our other systems here.
|
||||
@@ -134,16 +138,12 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
|
||||
@@ -152,6 +152,7 @@ int main(int argc, char** argv) {
|
||||
std::this_thread::sleep_until(t);
|
||||
}
|
||||
|
||||
LOG("Exited Main Loop! (signal %d)", Game::lastSignal);
|
||||
//Delete our objects here:
|
||||
Database::Destroy("AuthServer");
|
||||
delete Game::server;
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
add_executable(AuthServer "AuthServer.cpp")
|
||||
target_link_libraries(AuthServer ${COMMON_LIBRARIES})
|
||||
add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
||||
|
||||
@@ -32,15 +32,11 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
|
||||
}
|
||||
|
||||
//Read player names that are ok as well:
|
||||
auto stmt = Database::CreatePreppedStmt("select name from charinfo;");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
std::string line = res->getString(1).c_str();
|
||||
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
|
||||
m_ApprovedWords.push_back(CalculateHash(line));
|
||||
auto approvedNames = Database::Get()->GetApprovedCharacterNames();
|
||||
for (auto& name : approvedNames) {
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower); //Transform to lowercase
|
||||
m_ApprovedWords.push_back(CalculateHash(name));
|
||||
}
|
||||
delete res;
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
dChatFilter::~dChatFilter() {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
set(DCHATSERVER_SOURCES
|
||||
"ChatIgnoreList.cpp"
|
||||
"ChatPacketHandler.cpp"
|
||||
"PlayerContainer.cpp"
|
||||
)
|
||||
|
||||
add_executable(ChatServer "ChatServer.cpp")
|
||||
add_library(dChatServer ${DCHATSERVER_SOURCES})
|
||||
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
||||
|
||||
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
|
||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer)
|
||||
|
||||
173
dChatServer/ChatIgnoreList.cpp
Normal file
173
dChatServer/ChatIgnoreList.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "PlayerContainer.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "PacketUtils.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.GetPlayerData(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", playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
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.push_back(IgnoreData{ ignoredPlayer.id, ignoredPlayer.name });
|
||||
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.GetPlayerData(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(33);
|
||||
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
|
||||
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.push_back(IgnoreData{ ignoredPlayerId, toIgnoreStr });
|
||||
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.GetPlayerData(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(33);
|
||||
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);
|
||||
}
|
||||
27
dChatServer/ChatIgnoreList.h
Normal file
27
dChatServer/ChatIgnoreList.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#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__
|
||||
@@ -19,46 +19,29 @@
|
||||
#include "eClientMessageType.h"
|
||||
#include "eGameMessageType.h"
|
||||
|
||||
extern PlayerContainer playerContainer;
|
||||
|
||||
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
//Get from the packet which player we want to do something with:
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = 0;
|
||||
inStream.Read(playerID);
|
||||
|
||||
auto player = playerContainer.GetPlayerData(playerID);
|
||||
auto player = Game::playerContainer.GetPlayerData(playerID);
|
||||
if (!player) return;
|
||||
|
||||
//Get our friends list from the Db. Using a derived table since the friend of a player can be in either column.
|
||||
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt(
|
||||
"SELECT fr.requested_player, best_friend, ci.name FROM "
|
||||
"(SELECT CASE "
|
||||
"WHEN player_id = ? THEN friend_id "
|
||||
"WHEN friend_id = ? THEN player_id "
|
||||
"END AS requested_player, best_friend FROM friends) AS fr "
|
||||
"JOIN charinfo AS ci ON ci.id = fr.requested_player "
|
||||
"WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?;"));
|
||||
stmt->setUInt(1, static_cast<uint32_t>(playerID));
|
||||
stmt->setUInt(2, static_cast<uint32_t>(playerID));
|
||||
stmt->setUInt(3, static_cast<uint32_t>(playerID));
|
||||
|
||||
std::vector<FriendData> friends;
|
||||
|
||||
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
|
||||
while (res->next()) {
|
||||
auto friendsList = Database::Get()->GetFriendsList(playerID);
|
||||
for (const auto& friendData : friendsList) {
|
||||
FriendData fd;
|
||||
fd.isFTP = false; // not a thing in DLU
|
||||
fd.friendID = res->getUInt(1);
|
||||
fd.friendID = friendData.friendID;
|
||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
|
||||
|
||||
fd.isBestFriend = res->getInt(2) == 3; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
||||
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
||||
if (fd.isBestFriend) player->countOfBestFriends += 1;
|
||||
fd.friendName = res->getString(3);
|
||||
fd.friendName = friendData.friendName;
|
||||
|
||||
//Now check if they're online:
|
||||
auto fr = playerContainer.GetPlayerData(fd.friendID);
|
||||
auto fr = Game::playerContainer.GetPlayerData(fd.friendID);
|
||||
|
||||
if (fr) {
|
||||
fd.isOnline = true;
|
||||
@@ -71,7 +54,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
fd.zoneID = LWOZONEID();
|
||||
}
|
||||
|
||||
friends.push_back(fd);
|
||||
player->friends.push_back(fd);
|
||||
}
|
||||
|
||||
//Now, we need to send the friendlist to the server they came from:
|
||||
@@ -83,22 +66,17 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
|
||||
bitStream.Write<uint8_t>(0);
|
||||
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
|
||||
bitStream.Write((uint16_t)friends.size());
|
||||
bitStream.Write<uint16_t>(player->friends.size());
|
||||
|
||||
for (auto& data : friends) {
|
||||
for (auto& data : player->friends) {
|
||||
data.Serialize(bitStream);
|
||||
}
|
||||
|
||||
player->friends = friends;
|
||||
|
||||
SystemAddress sysAddr = player->sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
auto maxNumberOfBestFriendsAsString = Game::config->GetValue("max_number_of_best_friends");
|
||||
// If this config option doesn't exist, default to 5 which is what live used.
|
||||
auto maxNumberOfBestFriends = maxNumberOfBestFriendsAsString != "" ? std::stoi(maxNumberOfBestFriendsAsString) : 5U;
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID requestorPlayerID;
|
||||
inStream.Read(requestorPlayerID);
|
||||
@@ -117,7 +95,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
char isBestFriendRequest{};
|
||||
inStream.Read(isBestFriendRequest);
|
||||
|
||||
auto requestor = playerContainer.GetPlayerData(requestorPlayerID);
|
||||
auto requestor = Game::playerContainer.GetPlayerData(requestorPlayerID);
|
||||
if (!requestor) {
|
||||
LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str());
|
||||
return;
|
||||
@@ -127,7 +105,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN);
|
||||
return;
|
||||
};
|
||||
std::unique_ptr<PlayerData> requestee(playerContainer.GetPlayerData(playerName));
|
||||
std::unique_ptr<PlayerData> requestee(Game::playerContainer.GetPlayerData(playerName));
|
||||
|
||||
// Check if player is online first
|
||||
if (isBestFriendRequest && !requestee) {
|
||||
@@ -155,35 +133,26 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
// If at this point we dont have a target, then they arent online and we cant send the request.
|
||||
// Send the response code that corresponds to what the error is.
|
||||
if (!requestee) {
|
||||
std::unique_ptr<sql::PreparedStatement> nameQuery(Database::CreatePreppedStmt("SELECT name from charinfo where name = ?;"));
|
||||
nameQuery->setString(1, playerName);
|
||||
std::unique_ptr<sql::ResultSet> result(nameQuery->executeQuery());
|
||||
|
||||
requestee.reset(new PlayerData());
|
||||
requestee->playerName = playerName;
|
||||
auto responseType = Database::Get()->GetCharacterInfo(playerName)
|
||||
? eAddFriendResponseType::NOTONLINE
|
||||
: eAddFriendResponseType::INVALIDCHARACTER;
|
||||
|
||||
SendFriendResponse(requestor, requestee.get(), result->next() ? eAddFriendResponseType::NOTONLINE : eAddFriendResponseType::INVALIDCHARACTER);
|
||||
SendFriendResponse(requestor, requestee.get(), responseType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBestFriendRequest) {
|
||||
std::unique_ptr<sql::PreparedStatement> friendUpdate(Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
|
||||
friendUpdate->setUInt(1, static_cast<uint32_t>(requestorPlayerID));
|
||||
friendUpdate->setUInt(2, static_cast<uint32_t>(requestee->playerID));
|
||||
friendUpdate->setUInt(3, static_cast<uint32_t>(requestee->playerID));
|
||||
friendUpdate->setUInt(4, static_cast<uint32_t>(requestorPlayerID));
|
||||
std::unique_ptr<sql::ResultSet> result(friendUpdate->executeQuery());
|
||||
|
||||
LWOOBJID queryPlayerID = LWOOBJID_EMPTY;
|
||||
LWOOBJID queryFriendID = LWOOBJID_EMPTY;
|
||||
uint8_t oldBestFriendStatus{};
|
||||
uint8_t bestFriendStatus{};
|
||||
|
||||
if (result->next()) {
|
||||
auto bestFriendInfo = Database::Get()->GetBestFriendStatus(requestorPlayerID, requestee->playerID);
|
||||
if (bestFriendInfo) {
|
||||
// Get the IDs
|
||||
queryPlayerID = result->getInt(1);
|
||||
queryFriendID = result->getInt(2);
|
||||
oldBestFriendStatus = result->getInt(3);
|
||||
LWOOBJID queryPlayerID = bestFriendInfo->playerCharacterId;
|
||||
LWOOBJID queryFriendID = bestFriendInfo->friendCharacterId;
|
||||
oldBestFriendStatus = bestFriendInfo->bestFriendStatus;
|
||||
bestFriendStatus = oldBestFriendStatus;
|
||||
|
||||
// Set the bits
|
||||
@@ -204,22 +173,17 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
|
||||
// Only do updates if there was a change in the bff status.
|
||||
if (oldBestFriendStatus != bestFriendStatus) {
|
||||
if (requestee->countOfBestFriends >= maxNumberOfBestFriends || requestor->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
if (requestee->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
auto maxBestFriends = Game::playerContainer.GetMaxNumberOfBestFriends();
|
||||
if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) {
|
||||
if (requestee->countOfBestFriends >= maxBestFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
|
||||
}
|
||||
if (requestor->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
if (requestor->countOfBestFriends >= maxBestFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false);
|
||||
}
|
||||
} else {
|
||||
// Then update the database with this new info.
|
||||
std::unique_ptr<sql::PreparedStatement> updateQuery(Database::CreatePreppedStmt("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
|
||||
updateQuery->setUInt(1, bestFriendStatus);
|
||||
updateQuery->setUInt(2, static_cast<uint32_t>(requestorPlayerID));
|
||||
updateQuery->setUInt(3, static_cast<uint32_t>(requestee->playerID));
|
||||
updateQuery->setUInt(4, static_cast<uint32_t>(requestee->playerID));
|
||||
updateQuery->setUInt(5, static_cast<uint32_t>(requestorPlayerID));
|
||||
updateQuery->executeUpdate();
|
||||
Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee->playerID, bestFriendStatus);
|
||||
// Sent the best friend update here if the value is 3
|
||||
if (bestFriendStatus == 3U) {
|
||||
requestee->countOfBestFriends += 1;
|
||||
@@ -242,8 +206,15 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true);
|
||||
}
|
||||
} else {
|
||||
// Do not send this if we are requesting to be a best friend.
|
||||
SendFriendRequest(requestee.get(), requestor);
|
||||
auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
|
||||
if (requestee->friends.size() >= maxFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
|
||||
} else if (requestor->friends.size() >= maxFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false);
|
||||
} else {
|
||||
// Do not send this if we are requesting to be a best friend.
|
||||
SendFriendRequest(requestee.get(), requestor);
|
||||
}
|
||||
}
|
||||
|
||||
// If the player is actually a player and not a ghost one defined above, release it from being deleted.
|
||||
@@ -259,8 +230,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
|
||||
std::string friendName = PacketUtils::ReadString(0x15, packet, true);
|
||||
|
||||
//Now to try and find both of these:
|
||||
auto requestor = playerContainer.GetPlayerData(playerID);
|
||||
auto requestee = playerContainer.GetPlayerData(friendName);
|
||||
auto requestor = Game::playerContainer.GetPlayerData(playerID);
|
||||
auto requestee = Game::playerContainer.GetPlayerData(friendName);
|
||||
if (!requestor || !requestee) return;
|
||||
|
||||
eAddFriendResponseType serverResponseCode{};
|
||||
@@ -314,11 +285,7 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
|
||||
requesteeData.isOnline = true;
|
||||
requestor->friends.push_back(requesteeData);
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> statement(Database::CreatePreppedStmt("INSERT IGNORE INTO `friends` (`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?);"));
|
||||
statement->setUInt(1, static_cast<uint32_t>(requestor->playerID));
|
||||
statement->setUInt(2, static_cast<uint32_t>(requestee->playerID));
|
||||
statement->setInt(3, 0);
|
||||
statement->execute();
|
||||
Database::Get()->AddFriend(requestor->playerID, requestee->playerID);
|
||||
}
|
||||
|
||||
if (serverResponseCode != eAddFriendResponseType::DECLINED) SendFriendResponse(requestor, requestee, serverResponseCode, isAlreadyBestFriends);
|
||||
@@ -333,28 +300,20 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
|
||||
|
||||
//we'll have to query the db here to find the user, since you can delete them while they're offline.
|
||||
//First, we need to find their ID:
|
||||
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? LIMIT 1;"));
|
||||
stmt->setString(1, friendName.c_str());
|
||||
|
||||
LWOOBJID friendID = 0;
|
||||
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
|
||||
while (res->next()) {
|
||||
friendID = res->getUInt(1);
|
||||
auto friendIdResult = Database::Get()->GetCharacterInfo(friendName);
|
||||
if (friendIdResult) {
|
||||
friendID = friendIdResult->id;
|
||||
}
|
||||
|
||||
// Convert friendID to LWOOBJID
|
||||
GeneralUtils::SetBit(friendID, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(friendID, eObjectBits::CHARACTER);
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> deletestmt(Database::CreatePreppedStmt("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
|
||||
deletestmt->setUInt(1, static_cast<uint32_t>(playerID));
|
||||
deletestmt->setUInt(2, static_cast<uint32_t>(friendID));
|
||||
deletestmt->setUInt(3, static_cast<uint32_t>(friendID));
|
||||
deletestmt->setUInt(4, static_cast<uint32_t>(playerID));
|
||||
deletestmt->execute();
|
||||
Database::Get()->RemoveFriend(playerID, friendID);
|
||||
|
||||
//Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended:
|
||||
auto goonA = playerContainer.GetPlayerData(playerID);
|
||||
auto goonA = Game::playerContainer.GetPlayerData(playerID);
|
||||
if (goonA) {
|
||||
// Remove the friend from our list of friends
|
||||
for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) {
|
||||
@@ -367,7 +326,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
|
||||
SendRemoveFriend(goonA, friendName, true);
|
||||
}
|
||||
|
||||
auto goonB = playerContainer.GetPlayerData(friendID);
|
||||
auto goonB = Game::playerContainer.GetPlayerData(friendID);
|
||||
if (!goonB) return;
|
||||
// Do it again for other person
|
||||
for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) {
|
||||
@@ -378,7 +337,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID));
|
||||
std::string goonAName = GeneralUtils::UTF16ToWTF8(Game::playerContainer.GetName(playerID));
|
||||
SendRemoveFriend(goonB, goonAName, true);
|
||||
}
|
||||
|
||||
@@ -387,11 +346,11 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
|
||||
auto* sender = playerContainer.GetPlayerData(playerID);
|
||||
auto* sender = Game::playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (sender == nullptr) return;
|
||||
|
||||
if (playerContainer.GetIsMuted(sender)) return;
|
||||
if (Game::playerContainer.GetIsMuted(sender)) return;
|
||||
|
||||
const auto senderName = std::string(sender->playerName.c_str());
|
||||
|
||||
@@ -406,12 +365,12 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
|
||||
if (channel != 8) return;
|
||||
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team == nullptr) return;
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
auto* otherMember = playerContainer.GetPlayerData(memberId);
|
||||
auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (otherMember == nullptr) return;
|
||||
|
||||
@@ -445,11 +404,11 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
||||
std::string message = PacketUtils::ReadString(0xAA, packet, true, 512);
|
||||
|
||||
//Get the bois:
|
||||
auto goonA = playerContainer.GetPlayerData(senderID);
|
||||
auto goonB = playerContainer.GetPlayerData(receiverName);
|
||||
auto goonA = Game::playerContainer.GetPlayerData(senderID);
|
||||
auto goonB = Game::playerContainer.GetPlayerData(receiverName);
|
||||
if (!goonA || !goonB) return;
|
||||
|
||||
if (playerContainer.GetIsMuted(goonA)) return;
|
||||
if (Game::playerContainer.GetIsMuted(goonA)) return;
|
||||
|
||||
std::string goonAName = goonA->playerName.c_str();
|
||||
std::string goonBName = goonB->playerName.c_str();
|
||||
@@ -507,25 +466,25 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) {
|
||||
inStream.Read(playerID);
|
||||
std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true);
|
||||
|
||||
auto* player = playerContainer.GetPlayerData(playerID);
|
||||
auto* player = Game::playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team == nullptr) {
|
||||
team = playerContainer.CreateTeam(playerID);
|
||||
team = Game::playerContainer.CreateTeam(playerID);
|
||||
}
|
||||
|
||||
auto* other = playerContainer.GetPlayerData(invitedPlayer);
|
||||
auto* other = Game::playerContainer.GetPlayerData(invitedPlayer);
|
||||
|
||||
if (other == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerContainer.GetTeam(other->playerID) != nullptr) {
|
||||
if (Game::playerContainer.GetTeam(other->playerID) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -558,12 +517,12 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* team = playerContainer.GetTeam(leaderID);
|
||||
auto* team = Game::playerContainer.GetTeam(leaderID);
|
||||
|
||||
if (team == nullptr) {
|
||||
LOG("Failed to find team for leader (%llu)", leaderID);
|
||||
|
||||
team = playerContainer.GetTeam(playerID);
|
||||
team = Game::playerContainer.GetTeam(playerID);
|
||||
}
|
||||
|
||||
if (team == nullptr) {
|
||||
@@ -571,7 +530,7 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
playerContainer.AddMember(team, playerID);
|
||||
Game::playerContainer.AddMember(team, playerID);
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
|
||||
@@ -581,12 +540,12 @@ void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
|
||||
uint32_t size = 0;
|
||||
inStream.Read(size);
|
||||
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
LOG("(%llu) leaving team", playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
playerContainer.RemoveMember(team, playerID, false, false, true);
|
||||
Game::playerContainer.RemoveMember(team, playerID, false, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,24 +558,24 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet) {
|
||||
|
||||
LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str());
|
||||
|
||||
auto* kicked = playerContainer.GetPlayerData(kickedPlayer);
|
||||
auto* kicked = Game::playerContainer.GetPlayerData(kickedPlayer);
|
||||
|
||||
LWOOBJID kickedId = LWOOBJID_EMPTY;
|
||||
|
||||
if (kicked != nullptr) {
|
||||
kickedId = kicked->playerID;
|
||||
} else {
|
||||
kickedId = playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
|
||||
kickedId = Game::playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
|
||||
}
|
||||
|
||||
if (kickedId == LWOOBJID_EMPTY) return;
|
||||
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID || team->leaderID == kickedId) return;
|
||||
|
||||
playerContainer.RemoveMember(team, kickedId, false, true, false);
|
||||
Game::playerContainer.RemoveMember(team, kickedId, false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,16 +588,16 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet) {
|
||||
|
||||
LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str());
|
||||
|
||||
auto* promoted = playerContainer.GetPlayerData(promotedPlayer);
|
||||
auto* promoted = Game::playerContainer.GetPlayerData(promotedPlayer);
|
||||
|
||||
if (promoted == nullptr) return;
|
||||
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID) return;
|
||||
|
||||
playerContainer.PromoteMember(team, promoted->playerID);
|
||||
Game::playerContainer.PromoteMember(team, promoted->playerID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,16 +611,16 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet) {
|
||||
char option;
|
||||
inStream.Read(option);
|
||||
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID) return;
|
||||
|
||||
team->lootFlag = option;
|
||||
|
||||
playerContainer.TeamStatusUpdate(team);
|
||||
Game::playerContainer.TeamStatusUpdate(team);
|
||||
|
||||
playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
Game::playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,18 +629,18 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
auto* data = playerContainer.GetPlayerData(playerID);
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
auto* data = Game::playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (team != nullptr && data != nullptr) {
|
||||
if (team->local && data->zoneID.GetMapID() != team->zoneId.GetMapID() && data->zoneID.GetCloneID() != team->zoneId.GetCloneID()) {
|
||||
playerContainer.RemoveMember(team, playerID, false, false, true, true);
|
||||
Game::playerContainer.RemoveMember(team, playerID, false, false, true, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (team->memberIDs.size() <= 1 && !team->local) {
|
||||
playerContainer.DisbandTeam(team);
|
||||
Game::playerContainer.DisbandTeam(team);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -692,16 +651,16 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY);
|
||||
}
|
||||
|
||||
playerContainer.TeamStatusUpdate(team);
|
||||
Game::playerContainer.TeamStatusUpdate(team);
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
auto* otherMember = playerContainer.GetPlayerData(memberId);
|
||||
auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (memberId == playerID) continue;
|
||||
|
||||
const auto memberName = playerContainer.GetName(memberId);
|
||||
const auto memberName = Game::playerContainer.GetName(memberId);
|
||||
|
||||
if (otherMember != nullptr) {
|
||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID);
|
||||
@@ -709,7 +668,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0));
|
||||
}
|
||||
|
||||
playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
Game::playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,7 +705,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeader
|
||||
bitStream.Write(ucLootFlag);
|
||||
bitStream.Write(ucNumOfOtherPlayers);
|
||||
bitStream.Write(ucResponseCode);
|
||||
bitStream.Write(static_cast<uint32_t>(wsLeaderName.size()));
|
||||
bitStream.Write<uint32_t>(wsLeaderName.size());
|
||||
for (const auto character : wsLeaderName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
@@ -771,7 +730,7 @@ void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderI
|
||||
bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
|
||||
bitStream.Write(ucLootFlag);
|
||||
bitStream.Write(ucNumOfOtherPlayers);
|
||||
bitStream.Write(static_cast<uint32_t>(wsLeaderName.size()));
|
||||
bitStream.Write<uint32_t>(wsLeaderName.size());
|
||||
for (const auto character : wsLeaderName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
@@ -812,7 +771,7 @@ void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTria
|
||||
bitStream.Write(bLocal);
|
||||
bitStream.Write(bNoLootOnDeath);
|
||||
bitStream.Write(i64PlayerID);
|
||||
bitStream.Write(static_cast<uint32_t>(wsPlayerName.size()));
|
||||
bitStream.Write<uint32_t>(wsPlayerName.size());
|
||||
for (const auto character : wsPlayerName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
@@ -843,7 +802,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband
|
||||
bitStream.Write(bLocal);
|
||||
bitStream.Write(i64LeaderID);
|
||||
bitStream.Write(i64PlayerID);
|
||||
bitStream.Write(static_cast<uint32_t>(wsPlayerName.size()));
|
||||
bitStream.Write<uint32_t>(wsPlayerName.size());
|
||||
for (const auto character : wsPlayerName) {
|
||||
bitStream.Write(character);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
#include "eChatMessageType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "eWorldMessageType.h"
|
||||
#include "ChatIgnoreList.h"
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
//RakNet includes:
|
||||
#include "RakNetDefines.h"
|
||||
#include <MessageIdentifiers.h>
|
||||
#include "MessageIdentifiers.h"
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
@@ -32,16 +33,14 @@ namespace Game {
|
||||
dConfig* config = nullptr;
|
||||
dChatFilter* chatFilter = nullptr;
|
||||
AssetManager* assetManager = nullptr;
|
||||
bool shouldShutdown = false;
|
||||
Game::signal_t lastSignal = 0;
|
||||
std::mt19937 randomEngine;
|
||||
PlayerContainer playerContainer;
|
||||
}
|
||||
|
||||
|
||||
Logger* SetupLogger();
|
||||
void HandlePacket(Packet* packet);
|
||||
|
||||
PlayerContainer playerContainer;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
constexpr uint32_t chatFramerate = mediumFramerate;
|
||||
constexpr uint32_t chatFrameDelta = mediumFrameDelta;
|
||||
@@ -49,17 +48,20 @@ int main(int argc, char** argv) {
|
||||
Diagnostics::SetProcessFileName(argv[0]);
|
||||
Diagnostics::Initialize();
|
||||
|
||||
std::signal(SIGINT, Game::OnSignal);
|
||||
std::signal(SIGTERM, Game::OnSignal);
|
||||
|
||||
//Create all the objects we need to run our service:
|
||||
Game::logger = SetupLogger();
|
||||
if (!Game::logger) return EXIT_FAILURE;
|
||||
|
||||
//Read our config:
|
||||
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string());
|
||||
Game::config = new dConfig("chatconfig.ini");
|
||||
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
|
||||
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
|
||||
|
||||
LOG("Starting Chat server...");
|
||||
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||
LOG("Version: %s", PROJECT_VERSION);
|
||||
LOG("Compiled on: %s", __TIMESTAMP__);
|
||||
|
||||
try {
|
||||
@@ -78,13 +80,8 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||
|
||||
try {
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
Database::Connect();
|
||||
} catch (sql::SQLException& ex) {
|
||||
LOG("Got an error while connecting to the database: %s", ex.what());
|
||||
Database::Destroy("ChatServer");
|
||||
@@ -96,28 +93,30 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1000;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||
uint32_t maxClients = 50;
|
||||
uint32_t maxClients = 999;
|
||||
uint32_t ourPort = 1501;
|
||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||
std::string ourIP = "localhost";
|
||||
GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients);
|
||||
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(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
|
||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
|
||||
|
||||
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::playerContainer.Initialize();
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
Packet* packet = nullptr;
|
||||
@@ -127,7 +126,8 @@ int main(int argc, char** argv) {
|
||||
uint32_t framesSinceMasterDisconnect = 0;
|
||||
uint32_t framesSinceLastSQLPing = 0;
|
||||
|
||||
while (!Game::shouldShutdown) {
|
||||
Game::logger->Flush(); // once immediately before main loop
|
||||
while (!Game::ShouldShutdown()) {
|
||||
//Check if we're still connected to master:
|
||||
if (!Game::server->GetIsConnectedToMaster()) {
|
||||
framesSinceMasterDisconnect++;
|
||||
@@ -158,15 +158,12 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
}
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
@@ -211,19 +208,19 @@ void HandlePacket(Packet* packet) {
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) {
|
||||
switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
|
||||
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
|
||||
playerContainer.InsertPlayer(packet);
|
||||
Game::playerContainer.InsertPlayer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
|
||||
playerContainer.RemovePlayer(packet);
|
||||
Game::playerContainer.RemovePlayer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::MUTE_UPDATE:
|
||||
playerContainer.MuteUpdate(packet);
|
||||
Game::playerContainer.MuteUpdate(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::CREATE_TEAM:
|
||||
playerContainer.CreateTeamServer(packet);
|
||||
Game::playerContainer.CreateTeamServer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::ANNOUNCEMENT: {
|
||||
@@ -245,7 +242,15 @@ void HandlePacket(Packet* packet) {
|
||||
break;
|
||||
|
||||
case eChatMessageType::GET_IGNORE_LIST:
|
||||
LOG("Asked for ignore list, but is unimplemented right now.");
|
||||
ChatIgnoreList::GetIgnoreList(packet);
|
||||
break;
|
||||
|
||||
case eChatMessageType::ADD_IGNORE:
|
||||
ChatIgnoreList::AddIgnore(packet);
|
||||
break;
|
||||
|
||||
case eChatMessageType::REMOVE_IGNORE:
|
||||
ChatIgnoreList::RemoveIgnore(packet);
|
||||
break;
|
||||
|
||||
case eChatMessageType::TEAM_GET_STATUS:
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
#include "ChatPackets.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
PlayerContainer::PlayerContainer() {
|
||||
void PlayerContainer::Initialize() {
|
||||
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() {
|
||||
mPlayers.clear();
|
||||
m_Players.clear();
|
||||
}
|
||||
|
||||
TeamData::TeamData() {
|
||||
@@ -41,19 +43,12 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
inStream.Read(data->muteExpire);
|
||||
data->sysAddr = packet->systemAddress;
|
||||
|
||||
mNames[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
|
||||
m_Names[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
|
||||
|
||||
mPlayers.insert(std::make_pair(data->playerID, data));
|
||||
m_Players.insert(std::make_pair(data->playerID, data));
|
||||
LOG("Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
|
||||
|
||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||
|
||||
insertLog->setInt(1, data->playerID);
|
||||
insertLog->setInt(2, 0);
|
||||
insertLog->setUInt64(3, time(nullptr));
|
||||
insertLog->setInt(4, data->zoneID.GetMapID());
|
||||
|
||||
insertLog->executeUpdate();
|
||||
Database::Get()->UpdateActivityLog(data->playerID, eActivityType::PlayerLoggedIn, data->zoneID.GetMapID());
|
||||
}
|
||||
|
||||
void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
@@ -88,16 +83,9 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
}
|
||||
|
||||
LOG("Removed user: %llu", playerID);
|
||||
mPlayers.erase(playerID);
|
||||
m_Players.erase(playerID);
|
||||
|
||||
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();
|
||||
Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player->zoneID.GetMapID());
|
||||
}
|
||||
|
||||
void PlayerContainer::MuteUpdate(Packet* packet) {
|
||||
@@ -191,7 +179,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
||||
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
|
||||
auto* team = new TeamData();
|
||||
|
||||
team->teamID = ++mTeamIDCounter;
|
||||
team->teamID = ++m_TeamIDCounter;
|
||||
team->leaderID = leader;
|
||||
team->local = local;
|
||||
|
||||
@@ -366,7 +354,7 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
|
||||
if (!deleteTeam) {
|
||||
bitStream.Write(team->lootFlag);
|
||||
bitStream.Write(static_cast<char>(team->memberIDs.size()));
|
||||
bitStream.Write<char>(team->memberIDs.size());
|
||||
for (const auto memberID : team->memberIDs) {
|
||||
bitStream.Write(memberID);
|
||||
}
|
||||
@@ -376,15 +364,15 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
}
|
||||
|
||||
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
|
||||
const auto& pair = mNames.find(playerID);
|
||||
const auto& pair = m_Names.find(playerID);
|
||||
|
||||
if (pair == mNames.end()) return u"";
|
||||
if (pair == m_Names.end()) return u"";
|
||||
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
|
||||
for (const auto& pair : mNames) {
|
||||
for (const auto& pair : m_Names) {
|
||||
if (pair.second == playerName) {
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,26 @@
|
||||
#include "dServer.h"
|
||||
#include <unordered_map>
|
||||
|
||||
struct IgnoreData {
|
||||
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;
|
||||
std::string playerName;
|
||||
};
|
||||
|
||||
struct PlayerData {
|
||||
LWOOBJID playerID;
|
||||
std::string playerName;
|
||||
SystemAddress sysAddr;
|
||||
LWOZONEID zoneID;
|
||||
std::vector<FriendData> friends;
|
||||
std::vector<IgnoreData> ignoredPlayers;
|
||||
time_t muteExpire;
|
||||
uint8_t countOfBestFriends = 0;
|
||||
};
|
||||
@@ -29,9 +43,9 @@ struct TeamData {
|
||||
|
||||
class PlayerContainer {
|
||||
public:
|
||||
PlayerContainer();
|
||||
~PlayerContainer();
|
||||
|
||||
void Initialize();
|
||||
void InsertPlayer(Packet* packet);
|
||||
void RemovePlayer(Packet* packet);
|
||||
void MuteUpdate(Packet* packet);
|
||||
@@ -39,13 +53,13 @@ public:
|
||||
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
||||
|
||||
PlayerData* GetPlayerData(const LWOOBJID& playerID) {
|
||||
auto it = mPlayers.find(playerID);
|
||||
if (it != mPlayers.end()) return it->second;
|
||||
auto it = m_Players.find(playerID);
|
||||
if (it != m_Players.end()) return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PlayerData* GetPlayerData(const std::string& playerName) {
|
||||
for (auto player : mPlayers) {
|
||||
for (auto player : m_Players) {
|
||||
if (player.second) {
|
||||
std::string pn = player.second->playerName.c_str();
|
||||
if (pn == playerName) return player.second;
|
||||
@@ -67,13 +81,17 @@ public:
|
||||
std::u16string GetName(LWOOBJID playerID);
|
||||
LWOOBJID GetId(const std::u16string& playerName);
|
||||
bool GetIsMuted(PlayerData* data);
|
||||
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
||||
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
||||
|
||||
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return mPlayers; }
|
||||
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return m_Players; }
|
||||
|
||||
private:
|
||||
LWOOBJID mTeamIDCounter = 0;
|
||||
std::map<LWOOBJID, PlayerData*> mPlayers;
|
||||
LWOOBJID m_TeamIDCounter = 0;
|
||||
std::map<LWOOBJID, PlayerData*> m_Players;
|
||||
std::vector<TeamData*> mTeams;
|
||||
std::unordered_map<LWOOBJID, std::u16string> mNames;
|
||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
||||
uint32_t m_MaxNumberOfFriends = 50;
|
||||
};
|
||||
|
||||
|
||||
@@ -54,17 +54,17 @@ void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||
unsigned char b4 = (unsigned char)v;
|
||||
unsigned char b4 = static_cast<unsigned char>(v);
|
||||
if (v < 0x00200000) {
|
||||
b4 = b4 & 0x7F;
|
||||
if (v > 0x7F) {
|
||||
unsigned char b3;
|
||||
v = v >> 7;
|
||||
b3 = ((unsigned char)(v)) | 0x80;
|
||||
b3 = static_cast<unsigned char>(v) | 0x80;
|
||||
if (v > 0x7F) {
|
||||
unsigned char b2;
|
||||
v = v >> 7;
|
||||
b2 = ((unsigned char)(v)) | 0x80;
|
||||
b2 = static_cast<unsigned char>(v) | 0x80;
|
||||
bs->Write(b2);
|
||||
}
|
||||
|
||||
@@ -76,11 +76,11 @@ void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||
unsigned char b3;
|
||||
|
||||
v = v >> 8;
|
||||
b3 = ((unsigned char)(v)) | 0x80;
|
||||
b3 = static_cast<unsigned char>(v) | 0x80;
|
||||
v = v >> 7;
|
||||
b2 = ((unsigned char)(v)) | 0x80;
|
||||
b2 = static_cast<unsigned char>(v) | 0x80;
|
||||
v = v >> 7;
|
||||
b1 = ((unsigned char)(v)) | 0x80;
|
||||
b1 = static_cast<unsigned char>(v) | 0x80;
|
||||
|
||||
bs->Write(b1);
|
||||
bs->Write(b2);
|
||||
@@ -105,8 +105,8 @@ void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
|
||||
WriteFlagNumber(bs, (uint32_t)str.size());
|
||||
bs->Write(str.c_str(), (uint32_t)str.size());
|
||||
WriteFlagNumber(bs, static_cast<uint32_t>(str.size()));
|
||||
bs->Write(str.c_str(), static_cast<uint32_t>(str.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Amf3.h"
|
||||
|
||||
// RakNet
|
||||
#include <BitStream.h>
|
||||
#include "BitStream.h"
|
||||
|
||||
/*!
|
||||
\file AmfSerialize.h
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
#include "BinaryIO.h"
|
||||
#include <string>
|
||||
|
||||
void BinaryIO::WriteString(const std::string& stringToWrite, std::ofstream& outstream) {
|
||||
//BinaryWrite(outstream, uint32_t(stringToWrite.length()));
|
||||
|
||||
for (size_t i = 0; i < size_t(stringToWrite.length()); ++i) {
|
||||
BinaryIO::BinaryWrite(outstream, stringToWrite[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//For reading null-terminated strings
|
||||
std::string BinaryIO::ReadString(std::istream& instream) {
|
||||
std::string toReturn;
|
||||
@@ -23,36 +15,3 @@ std::string BinaryIO::ReadString(std::istream& instream) {
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
//For reading strings of a specific size
|
||||
std::string BinaryIO::ReadString(std::istream& instream, size_t size) {
|
||||
std::string toReturn;
|
||||
char buffer;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
BinaryIO::BinaryRead(instream, buffer);
|
||||
toReturn += buffer;
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::string BinaryIO::ReadWString(std::istream& instream) {
|
||||
size_t size;
|
||||
BinaryRead(instream, size);
|
||||
//toReturn.resize(size);
|
||||
std::string test;
|
||||
unsigned char buf;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
//instream.ignore(1);
|
||||
BinaryRead(instream, buf);
|
||||
test += buf;
|
||||
}
|
||||
|
||||
//printf("%s\n", test.c_str());
|
||||
|
||||
//instream.read((char*)&toReturn[0], size * 2);
|
||||
//std::string str(toReturn.begin(), toReturn.end());
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __BINARYIO__H__
|
||||
#define __BINARYIO__H__
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace BinaryIO {
|
||||
|
||||
template<typename T>
|
||||
std::ostream& BinaryWrite(std::ostream& stream, const T& value) {
|
||||
return stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
|
||||
@@ -15,13 +24,51 @@ namespace BinaryIO {
|
||||
return stream.read(reinterpret_cast<char*>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
void WriteString(const std::string& stringToWrite, std::ofstream& outstream);
|
||||
enum class ReadType : int8_t {
|
||||
WideString = 0,
|
||||
String = 1,
|
||||
};
|
||||
|
||||
template<typename SizeType>
|
||||
inline void ReadString(std::istream& stream, std::u16string& value) {
|
||||
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
|
||||
|
||||
SizeType size;
|
||||
BinaryRead(stream, size);
|
||||
|
||||
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
|
||||
value.resize(size);
|
||||
stream.read(reinterpret_cast<char*>(value.data()), size * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
template<typename SizeType>
|
||||
inline void ReadString(std::istream& stream, std::string& value, ReadType readType) {
|
||||
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
|
||||
|
||||
SizeType size;
|
||||
BinaryRead(stream, size);
|
||||
|
||||
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
|
||||
value.resize(size);
|
||||
if (readType == ReadType::WideString) {
|
||||
uint16_t wideChar;
|
||||
|
||||
// Faster to do this than to read a u16string and convert it to a string since we only go through allocator once
|
||||
for (SizeType i = 0; i < size; ++i) {
|
||||
BinaryRead(stream, wideChar);
|
||||
value[i] = static_cast<char>(wideChar);
|
||||
}
|
||||
} else {
|
||||
stream.read(value.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ReadString(std::istream& instream);
|
||||
std::string ReadString(std::istream& instream, size_t size);
|
||||
std::string ReadWString(std::istream& instream);
|
||||
|
||||
inline bool DoesFileExist(const std::string& name) {
|
||||
std::ifstream f(name.c_str());
|
||||
return f.good();
|
||||
}
|
||||
}
|
||||
|
||||
#endif //!__BINARYIO__H__
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
|
||||
//! Forward declarations
|
||||
|
||||
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
|
||||
void WriteSd0Magic(char* input, uint32_t chunkSize);
|
||||
bool CheckSd0Magic(sql::Blob* streamToCheck);
|
||||
bool CheckSd0Magic(std::istream& streamToCheck);
|
||||
|
||||
/**
|
||||
* @brief Truncates all models with broken data from the database.
|
||||
@@ -24,28 +23,24 @@ bool CheckSd0Magic(sql::Blob* streamToCheck);
|
||||
*/
|
||||
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
uint32_t modelsTruncated{};
|
||||
auto modelsToTruncate = GetModelsFromDatabase();
|
||||
bool previousCommitValue = Database::GetAutoCommit();
|
||||
Database::SetAutoCommit(false);
|
||||
while (modelsToTruncate->next()) {
|
||||
std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;"));
|
||||
std::unique_ptr<sql::PreparedStatement> pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;"));
|
||||
auto modelsToTruncate = Database::Get()->GetAllUgcModels();
|
||||
bool previousCommitValue = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
for (auto& model : modelsToTruncate) {
|
||||
std::string completeUncompressedModel{};
|
||||
uint32_t chunkCount{};
|
||||
uint64_t modelId = modelsToTruncate->getInt(1);
|
||||
std::unique_ptr<sql::Blob> modelAsSd0(modelsToTruncate->getBlob(2));
|
||||
// Check that header is sd0 by checking for the sd0 magic.
|
||||
if (CheckSd0Magic(modelAsSd0.get())) {
|
||||
if (CheckSd0Magic(model.lxfmlData)) {
|
||||
while (true) {
|
||||
uint32_t chunkSize{};
|
||||
modelAsSd0->read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream
|
||||
model.lxfmlData.read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream
|
||||
|
||||
// Check if good here since if at the end of an sd0 file, this will have eof flagged.
|
||||
if (!modelAsSd0->good()) break;
|
||||
if (!model.lxfmlData.good()) break;
|
||||
|
||||
std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]);
|
||||
for (uint32_t i = 0; i < chunkSize; i++) {
|
||||
compressedChunk[i] = modelAsSd0->get();
|
||||
compressedChunk[i] = model.lxfmlData.get();
|
||||
}
|
||||
|
||||
// Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
|
||||
@@ -56,10 +51,10 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
|
||||
if (actualUncompressedSize != -1) {
|
||||
uint32_t previousSize = completeUncompressedModel.size();
|
||||
completeUncompressedModel.append((char*)uncompressedChunk.get());
|
||||
completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get()));
|
||||
completeUncompressedModel.resize(previousSize + actualUncompressedSize);
|
||||
} else {
|
||||
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, modelId, err);
|
||||
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err);
|
||||
break;
|
||||
}
|
||||
chunkCount++;
|
||||
@@ -75,26 +70,20 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
"</LXFML>",
|
||||
completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos
|
||||
) {
|
||||
LOG("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();
|
||||
LOG("Brick-by-brick model %llu will be deleted!", model.id);
|
||||
Database::Get()->DeleteUgcModelData(model.id);
|
||||
modelsTruncated++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG("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();
|
||||
LOG("Brick-by-brick model %llu will be deleted!", model.id);
|
||||
Database::Get()->DeleteUgcModelData(model.id);
|
||||
modelsTruncated++;
|
||||
}
|
||||
}
|
||||
|
||||
Database::Commit();
|
||||
Database::SetAutoCommit(previousCommitValue);
|
||||
Database::Get()->Commit();
|
||||
Database::Get()->SetAutoCommit(previousCommitValue);
|
||||
return modelsTruncated;
|
||||
}
|
||||
|
||||
@@ -106,21 +95,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
*/
|
||||
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
uint32_t updatedModels = 0;
|
||||
auto modelsToUpdate = GetModelsFromDatabase();
|
||||
auto previousAutoCommitState = Database::GetAutoCommit();
|
||||
Database::SetAutoCommit(false);
|
||||
std::unique_ptr<sql::PreparedStatement> insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;"));
|
||||
while (modelsToUpdate->next()) {
|
||||
int64_t modelId = modelsToUpdate->getInt64(1);
|
||||
std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2));
|
||||
auto modelsToUpdate = Database::Get()->GetAllUgcModels();
|
||||
auto previousAutoCommitState = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
for (auto& model : modelsToUpdate) {
|
||||
// Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib)
|
||||
// If it does, convert it to sd0.
|
||||
if (oldLxfml->get() == 0x78 && oldLxfml->get() == 0xDA) {
|
||||
|
||||
if (model.lxfmlData.get() == 0x78 && model.lxfmlData.get() == 0xDA) {
|
||||
// Get and save size of zlib compressed chunk.
|
||||
oldLxfml->seekg(0, std::ios::end);
|
||||
uint32_t oldLxfmlSize = static_cast<uint32_t>(oldLxfml->tellg());
|
||||
oldLxfml->seekg(0);
|
||||
model.lxfmlData.seekg(0, std::ios::end);
|
||||
uint32_t oldLxfmlSize = static_cast<uint32_t>(model.lxfmlData.tellg());
|
||||
model.lxfmlData.seekg(0);
|
||||
|
||||
// Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size.
|
||||
uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9;
|
||||
@@ -128,34 +113,27 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
|
||||
WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize);
|
||||
for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) {
|
||||
sd0ConvertedModel.get()[i] = oldLxfml->get();
|
||||
sd0ConvertedModel.get()[i] = model.lxfmlData.get();
|
||||
}
|
||||
|
||||
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
|
||||
std::istringstream outputStringStream(outputString);
|
||||
|
||||
insertionStatement->setBlob(1, static_cast<std::istream*>(&outputStringStream));
|
||||
insertionStatement->setInt64(2, modelId);
|
||||
try {
|
||||
insertionStatement->executeUpdate();
|
||||
LOG("Updated model %i to sd0", modelId);
|
||||
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
|
||||
LOG("Updated model %i to sd0", model.id);
|
||||
updatedModels++;
|
||||
} catch (sql::SQLException exception) {
|
||||
LOG("Failed to update model %i. This model should be inspected manually to see why."
|
||||
"The database error is %s", modelId, exception.what());
|
||||
"The database error is %s", model.id, exception.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
Database::Commit();
|
||||
Database::SetAutoCommit(previousAutoCommitState);
|
||||
Database::Get()->Commit();
|
||||
Database::Get()->SetAutoCommit(previousAutoCommitState);
|
||||
return updatedModels;
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase() {
|
||||
std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;"));
|
||||
return std::unique_ptr<sql::ResultSet>(modelsRawDataQuery->executeQuery());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes sd0 magic at the front of a char*
|
||||
*
|
||||
@@ -171,6 +149,6 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) {
|
||||
*reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array
|
||||
}
|
||||
|
||||
bool CheckSd0Magic(sql::Blob* streamToCheck) {
|
||||
return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF;
|
||||
bool CheckSd0Magic(std::istream& streamToCheck) {
|
||||
return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ set(DCOMMON_SOURCES
|
||||
"dConfig.cpp"
|
||||
"Diagnostics.cpp"
|
||||
"Logger.cpp"
|
||||
"Game.cpp"
|
||||
"GeneralUtils.cpp"
|
||||
"LDFFormat.cpp"
|
||||
"MD5.cpp"
|
||||
|
||||
@@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) {
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
|
||||
#if defined(__include_backtrace__)
|
||||
#if defined(INCLUDE_BACKTRACE)
|
||||
#include <backtrace.h>
|
||||
|
||||
#include <backtrace-supported.h>
|
||||
@@ -115,7 +115,14 @@ void GenerateDump() {
|
||||
}
|
||||
|
||||
void CatchUnhandled(int sig) {
|
||||
#ifndef __include_backtrace__
|
||||
std::exception_ptr eptr = std::current_exception();
|
||||
try {
|
||||
if (eptr) std::rethrow_exception(eptr);
|
||||
} catch(const std::exception& e) {
|
||||
LOG("Caught exception: '%s'", e.what());
|
||||
}
|
||||
|
||||
#ifndef INCLUDE_BACKTRACE
|
||||
|
||||
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());
|
||||
@@ -167,7 +174,7 @@ void CatchUnhandled(int sig) {
|
||||
backtrace_symbols_fd(array, size, STDOUT_FILENO);
|
||||
# endif // defined(__GNUG__)
|
||||
|
||||
#else // __include_backtrace__
|
||||
#else // INCLUDE_BACKTRACE
|
||||
|
||||
struct backtrace_state* state = backtrace_create_state(
|
||||
Diagnostics::GetProcessFileName().c_str(),
|
||||
@@ -178,7 +185,7 @@ void CatchUnhandled(int sig) {
|
||||
struct bt_ctx ctx = { state, 0 };
|
||||
Bt(state);
|
||||
|
||||
#endif // __include_backtrace__
|
||||
#endif // INCLUDE_BACKTRACE
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -197,10 +204,10 @@ void MakeBacktrace() {
|
||||
sigact.sa_sigaction = CritErrHdlr;
|
||||
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
if (sigaction(SIGSEGV, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||
sigaction(SIGFPE, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||
sigaction(SIGABRT, &sigact, (struct sigaction*)nullptr) != 0 ||
|
||||
sigaction(SIGILL, &sigact, (struct sigaction*)nullptr) != 0) {
|
||||
if (sigaction(SIGSEGV, &sigact, nullptr) != 0 ||
|
||||
sigaction(SIGFPE, &sigact, nullptr) != 0 ||
|
||||
sigaction(SIGABRT, &sigact, nullptr) != 0 ||
|
||||
sigaction(SIGILL, &sigact, nullptr) != 0) {
|
||||
fprintf(stderr, "error setting signal handler for %d (%s)\n",
|
||||
SIGSEGV,
|
||||
strsignal(SIGSEGV));
|
||||
|
||||
@@ -28,19 +28,17 @@ FdbToSqlite::Convert::Convert(std::string binaryOutPath) {
|
||||
this->m_BinaryOutPath = binaryOutPath;
|
||||
}
|
||||
|
||||
bool FdbToSqlite::Convert::ConvertDatabase(AssetMemoryBuffer& buffer) {
|
||||
bool FdbToSqlite::Convert::ConvertDatabase(AssetStream& buffer) {
|
||||
if (m_ConversionStarted) return false;
|
||||
|
||||
std::istream cdClientBuffer(&buffer);
|
||||
|
||||
this->m_ConversionStarted = true;
|
||||
try {
|
||||
CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite");
|
||||
|
||||
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
|
||||
|
||||
int32_t numberOfTables = ReadInt32(cdClientBuffer);
|
||||
ReadTables(numberOfTables, cdClientBuffer);
|
||||
int32_t numberOfTables = ReadInt32(buffer);
|
||||
ReadTables(numberOfTables, buffer);
|
||||
|
||||
CDClientDatabase::ExecuteQuery("COMMIT;");
|
||||
} catch (CppSQLite3Exception& e) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
|
||||
class AssetMemoryBuffer;
|
||||
#include "AssetManager.h"
|
||||
|
||||
enum class eSqliteDataType : int32_t;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace FdbToSqlite {
|
||||
*
|
||||
* @return true if the database was converted properly, false otherwise.
|
||||
*/
|
||||
bool ConvertDatabase(AssetMemoryBuffer& buffer);
|
||||
bool ConvertDatabase(AssetStream& buffer);
|
||||
|
||||
/**
|
||||
* @brief Reads a 32 bit int from the fdb file.
|
||||
|
||||
7
dCommon/Game.cpp
Normal file
7
dCommon/Game.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "Game.h"
|
||||
|
||||
namespace Game {
|
||||
void OnSignal(int signal) {
|
||||
lastSignal = signal;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <csignal>
|
||||
|
||||
class dServer;
|
||||
class Logger;
|
||||
@@ -12,8 +14,10 @@ class AssetManager;
|
||||
struct SystemAddress;
|
||||
class EntityManager;
|
||||
class dZoneManager;
|
||||
class PlayerContainer;
|
||||
|
||||
namespace Game {
|
||||
using signal_t = volatile std::sig_atomic_t;
|
||||
extern Logger* logger;
|
||||
extern dServer* server;
|
||||
extern InstanceManager* im;
|
||||
@@ -23,7 +27,14 @@ namespace Game {
|
||||
extern RakPeerInterface* chatServer;
|
||||
extern AssetManager* assetManager;
|
||||
extern SystemAddress chatSysAddr;
|
||||
extern bool shouldShutdown;
|
||||
extern signal_t lastSignal;
|
||||
extern EntityManager* entityManager;
|
||||
extern dZoneManager* zoneManager;
|
||||
extern PlayerContainer playerContainer;
|
||||
extern std::string projectVersion;
|
||||
|
||||
inline bool ShouldShutdown() {
|
||||
return lastSignal != 0;
|
||||
}
|
||||
void OnSignal(int signal);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ bool _IsSuffixChar(uint8_t c) {
|
||||
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||
size_t rem = slice.length();
|
||||
if (slice.empty()) return false;
|
||||
const uint8_t* bytes = (const uint8_t*)&slice.front();
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
|
||||
if (rem > 0) {
|
||||
uint8_t first = bytes[0];
|
||||
if (first < 0x80) { // 1 byte character
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
#include <BitStream.h>
|
||||
#include "BitStream.h"
|
||||
#include "NiPoint3.h"
|
||||
|
||||
#include "Game.h"
|
||||
@@ -151,6 +151,11 @@ namespace GeneralUtils {
|
||||
return std::stod(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint16_t Parse(const char* value) {
|
||||
return std::stoul(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t Parse(const char* value) {
|
||||
return std::stoul(value);
|
||||
@@ -229,18 +234,30 @@ namespace GeneralUtils {
|
||||
return T();
|
||||
}
|
||||
|
||||
// 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
|
||||
/**
|
||||
* Casts the value of an enum entry to its underlying type
|
||||
* @param entry Enum entry to cast
|
||||
* @returns The enum entry's value in its underlying type
|
||||
*/
|
||||
template <typename eType>
|
||||
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>
|
||||
inline T GenerateRandomNumber() {
|
||||
// Make sure it is a numeric type
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
|
||||
|
||||
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,15 +63,15 @@ private:
|
||||
|
||||
//! Writes the key to the packet
|
||||
void WriteKey(RakNet::BitStream* packet) {
|
||||
packet->Write(static_cast<uint8_t>(this->key.length() * sizeof(uint16_t)));
|
||||
packet->Write<uint8_t>(this->key.length() * sizeof(uint16_t));
|
||||
for (uint32_t i = 0; i < this->key.length(); ++i) {
|
||||
packet->Write(static_cast<uint16_t>(this->key[i]));
|
||||
packet->Write<uint16_t>(this->key[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//! Writes the value to the packet
|
||||
void WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
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)
|
||||
template<>
|
||||
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
|
||||
packet->Write(static_cast<uint32_t>(this->value.length()));
|
||||
packet->Write<uint32_t>(this->value.length());
|
||||
for (uint32_t i = 0; i < this->value.length(); ++i) {
|
||||
packet->Write(static_cast<uint16_t>(this->value[i]));
|
||||
packet->Write<uint16_t>(this->value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// The specialized version for bool
|
||||
template<>
|
||||
inline void LDFData<bool>::WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
|
||||
packet->Write(static_cast<uint8_t>(this->value));
|
||||
packet->Write<uint8_t>(this->value);
|
||||
}
|
||||
|
||||
// The specialized version for std::string (UTF-8)
|
||||
template<>
|
||||
inline void LDFData<std::string>::WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write(static_cast<uint8_t>(this->GetValueType()));
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
|
||||
packet->Write(static_cast<uint32_t>(this->value.length()));
|
||||
packet->Write<uint32_t>(this->value.length());
|
||||
for (uint32_t i = 0; i < this->value.length(); ++i) {
|
||||
packet->Write(static_cast<uint8_t>(this->value[i]));
|
||||
packet->Write<uint8_t>(this->value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
Writer::~Writer() {
|
||||
// Flush before we close
|
||||
Flush();
|
||||
// Dont try to close stdcout...
|
||||
if (!m_Outfile || m_IsConsoleWriter) return;
|
||||
|
||||
@@ -14,7 +16,7 @@ Writer::~Writer() {
|
||||
}
|
||||
|
||||
void Writer::Log(const char* time, const char* message) {
|
||||
if (!m_Outfile) return;
|
||||
if (!m_Outfile || !m_Enabled) return;
|
||||
|
||||
fputs(time, m_Outfile);
|
||||
fputs(message, m_Outfile);
|
||||
|
||||
@@ -107,7 +107,7 @@ void Metrics::EndMeasurement(MetricVariable variable) {
|
||||
}
|
||||
|
||||
float Metrics::ToMiliseconds(int64_t nanoseconds) {
|
||||
return (float)nanoseconds / 1e6;
|
||||
return static_cast<float>(nanoseconds) / 1e6;
|
||||
}
|
||||
|
||||
std::string Metrics::MetricVariableToString(MetricVariable variable) {
|
||||
@@ -193,34 +193,34 @@ size_t Metrics::GetPeakRSS() {
|
||||
/* Windows -------------------------------------------------- */
|
||||
PROCESS_MEMORY_COUNTERS info;
|
||||
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
||||
return (size_t)info.PeakWorkingSetSize;
|
||||
return static_cast<size_t>(info.PeakWorkingSetSize);
|
||||
|
||||
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
|
||||
/* AIX and Solaris ------------------------------------------ */
|
||||
struct psinfo psinfo;
|
||||
int fd = -1;
|
||||
if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
|
||||
return (size_t)0L; /* Can't open? */
|
||||
return static_cast<size_t>(0L); /* Can't open? */
|
||||
if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
|
||||
close(fd);
|
||||
return (size_t)0L; /* Can't read? */
|
||||
return static_cast<size_t>(0L); /* Can't read? */
|
||||
}
|
||||
close(fd);
|
||||
return (size_t)(psinfo.pr_rssize * 1024L);
|
||||
return static_cast<size_t>(psinfo.pr_rssize * 1024L);
|
||||
|
||||
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
|
||||
/* BSD, Linux, and OSX -------------------------------------- */
|
||||
struct rusage rusage;
|
||||
getrusage(RUSAGE_SELF, &rusage);
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
return (size_t)rusage.ru_maxrss;
|
||||
return static_cast<size_t>(rusage.ru_maxrss);
|
||||
#else
|
||||
return (size_t)(rusage.ru_maxrss * 1024L);
|
||||
return static_cast<size_t>(rusage.ru_maxrss * 1024L);
|
||||
#endif
|
||||
|
||||
#else
|
||||
/* Unknown OS ----------------------------------------------- */
|
||||
return (size_t)0L; /* Unsupported. */
|
||||
return static_cast<size_t>(0L); /* Unsupported. */
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -234,33 +234,33 @@ size_t Metrics::GetCurrentRSS() {
|
||||
/* Windows -------------------------------------------------- */
|
||||
PROCESS_MEMORY_COUNTERS info;
|
||||
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
||||
return (size_t)info.WorkingSetSize;
|
||||
return static_cast<size_t>(info.WorkingSetSize);
|
||||
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
/* OSX ------------------------------------------------------ */
|
||||
struct mach_task_basic_info info;
|
||||
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
|
||||
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
|
||||
(task_info_t)&info, &infoCount) != KERN_SUCCESS)
|
||||
return (size_t)0L; /* Can't access? */
|
||||
return (size_t)info.resident_size;
|
||||
reinterpret_cast<task_info_t>(&info), &infoCount) != KERN_SUCCESS)
|
||||
return static_cast<size_t>(0L); /* Can't access? */
|
||||
return static_cast<size_t>(info.resident_size);
|
||||
|
||||
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
|
||||
/* Linux ---------------------------------------------------- */
|
||||
long rss = 0L;
|
||||
FILE* fp = NULL;
|
||||
if ((fp = fopen("/proc/self/statm", "r")) == NULL)
|
||||
return (size_t)0L; /* Can't open? */
|
||||
return static_cast<size_t>(0L); /* Can't open? */
|
||||
if (fscanf(fp, "%*s%ld", &rss) != 1) {
|
||||
fclose(fp);
|
||||
return (size_t)0L; /* Can't read? */
|
||||
return static_cast<size_t>(0L); /* Can't read? */
|
||||
}
|
||||
fclose(fp);
|
||||
return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE);
|
||||
return static_cast<size_t>(rss) * static_cast<size_t>(sysconf(_SC_PAGESIZE));
|
||||
|
||||
#else
|
||||
/* AIX, BSD, Solaris, and Unknown OS ------------------------ */
|
||||
return (size_t)0L; /* Unsupported. */
|
||||
return static_cast<size_t>(0L); /* Unsupported. */
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -114,13 +114,13 @@ bool NiPoint3::operator!=(const NiPoint3& point) const {
|
||||
//! Operator for subscripting
|
||||
float& NiPoint3::operator[](int i) {
|
||||
float* base = &x;
|
||||
return (float&)base[i];
|
||||
return base[i];
|
||||
}
|
||||
|
||||
//! Operator for subscripting
|
||||
const float& NiPoint3::operator[](int i) const {
|
||||
const float* base = &x;
|
||||
return (float&)base[i];
|
||||
return base[i];
|
||||
}
|
||||
|
||||
//! Operator for addition of vectors
|
||||
@@ -181,7 +181,7 @@ bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3&
|
||||
if (this->y < minPoint.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
|
||||
@@ -232,10 +232,21 @@ NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target,
|
||||
float dx = target.x - current.x;
|
||||
float dy = target.y - current.y;
|
||||
float dz = target.z - current.z;
|
||||
float lengthSquared = (float)((double)dx * (double)dx + (double)dy * (double)dy + (double)dz * (double)dz);
|
||||
if ((double)lengthSquared == 0.0 || (double)maxDistanceDelta >= 0.0 && (double)lengthSquared <= (double)maxDistanceDelta * (double)maxDistanceDelta)
|
||||
|
||||
float lengthSquared = static_cast<float>(
|
||||
static_cast<double>(dx) * static_cast<double>(dx) +
|
||||
static_cast<double>(dy) * static_cast<double>(dy) +
|
||||
static_cast<double>(dz) * static_cast<double>(dz)
|
||||
);
|
||||
|
||||
if (static_cast<double>(lengthSquared) == 0.0
|
||||
|| static_cast<double>(maxDistanceDelta) >= 0.0
|
||||
&& static_cast<double>(lengthSquared)
|
||||
<= static_cast<double>(maxDistanceDelta) * static_cast<double>(maxDistanceDelta)) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Source: http://www.zedwood.com/article/cpp-sha512-function
|
||||
|
||||
#include "SHA512.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ZCompression.h"
|
||||
|
||||
#include <zlib.h>
|
||||
#include "zlib.h"
|
||||
|
||||
namespace ZCompression {
|
||||
int32_t GetMaxCompressedLength(int32_t nLenSrc) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include <zlib.h>
|
||||
#include "zlib.h"
|
||||
|
||||
AssetManager::AssetManager(const std::filesystem::path& path) {
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
@@ -81,8 +81,8 @@ bool AssetManager::HasFile(const char* name) {
|
||||
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
||||
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
|
||||
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size());
|
||||
crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4);
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
||||
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
|
||||
|
||||
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
||||
if (item.m_Crc == crc) {
|
||||
@@ -113,7 +113,7 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
||||
#endif
|
||||
fseek(file, 0, SEEK_END);
|
||||
*len = ftell(file);
|
||||
*data = (char*)malloc(*len);
|
||||
*data = static_cast<char*>(malloc(*len));
|
||||
fseek(file, 0, SEEK_SET);
|
||||
int32_t readInData = fread(*data, sizeof(uint8_t), *len, file);
|
||||
fclose(file);
|
||||
@@ -129,8 +129,8 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
||||
fixedName = "client\\res\\" + fixedName;
|
||||
}
|
||||
int32_t packIndex = -1;
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size());
|
||||
crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4);
|
||||
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
||||
crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
|
||||
|
||||
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
||||
if (item.m_Crc == crc) {
|
||||
@@ -152,13 +152,12 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
||||
return success;
|
||||
}
|
||||
|
||||
AssetMemoryBuffer AssetManager::GetFileAsBuffer(const char* name) {
|
||||
char* buf;
|
||||
uint32_t len;
|
||||
AssetStream AssetManager::GetFile(const char* name) {
|
||||
char* buf; uint32_t len;
|
||||
|
||||
bool success = this->GetFile(name, &buf, &len);
|
||||
|
||||
return AssetMemoryBuffer(buf, len, success);
|
||||
return AssetStream(buf, len, success);
|
||||
}
|
||||
|
||||
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
||||
@@ -168,7 +167,7 @@ uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
||||
crc = base;
|
||||
for (i = 0; i < l; i++) {
|
||||
// xor next byte to upper bits of crc
|
||||
crc ^= (((unsigned int)message[i]) << 24);
|
||||
crc ^= (static_cast<unsigned int>(message[i]) << 24);
|
||||
for (j = 0; j < 8; j++) { // Do eight times.
|
||||
msb = crc >> 31;
|
||||
crc <<= 1;
|
||||
|
||||
@@ -25,6 +25,10 @@ struct AssetMemoryBuffer : std::streambuf {
|
||||
this->setg(base, base, base + n);
|
||||
}
|
||||
|
||||
~AssetMemoryBuffer() {
|
||||
if (m_Success) free(m_Base);
|
||||
}
|
||||
|
||||
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override {
|
||||
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
|
||||
}
|
||||
@@ -40,9 +44,17 @@ struct AssetMemoryBuffer : std::streambuf {
|
||||
setg(eback(), eback() + off, egptr());
|
||||
return gptr() - eback();
|
||||
}
|
||||
};
|
||||
|
||||
void close() {
|
||||
free(m_Base);
|
||||
struct AssetStream : std::istream {
|
||||
AssetStream(char* base, std::ptrdiff_t n, bool success) : std::istream(new AssetMemoryBuffer(base, n, success)) {}
|
||||
|
||||
~AssetStream() {
|
||||
delete rdbuf();
|
||||
}
|
||||
|
||||
operator bool() {
|
||||
return reinterpret_cast<AssetMemoryBuffer*>(rdbuf())->m_Success;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -56,7 +68,7 @@ public:
|
||||
|
||||
bool HasFile(const char* name);
|
||||
bool GetFile(const char* name, char** data, uint32_t* len);
|
||||
AssetMemoryBuffer GetFileAsBuffer(const char* name);
|
||||
AssetStream GetFile(const char* name);
|
||||
|
||||
private:
|
||||
void LoadPackIndex();
|
||||
|
||||
@@ -76,7 +76,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
||||
fseek(file, pos, SEEK_SET);
|
||||
|
||||
if (!isCompressed) {
|
||||
char* tempData = (char*)malloc(pkRecord.m_UncompressedSize);
|
||||
char* tempData = static_cast<char*>(malloc(pkRecord.m_UncompressedSize));
|
||||
int32_t readInData = fread(tempData, sizeof(uint8_t), pkRecord.m_UncompressedSize, file);
|
||||
|
||||
*data = tempData;
|
||||
@@ -90,7 +90,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
||||
|
||||
fseek(file, pos, SEEK_SET);
|
||||
|
||||
char* decompressedData = (char*)malloc(pkRecord.m_UncompressedSize);
|
||||
char* decompressedData = static_cast<char*>(malloc(pkRecord.m_UncompressedSize));
|
||||
uint32_t currentReadPos = 0;
|
||||
|
||||
while (true) {
|
||||
@@ -100,12 +100,12 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
|
||||
int32_t readInData = fread(&size, sizeof(uint32_t), 1, file);
|
||||
pos += 4; // Move pointer position 4 to the right
|
||||
|
||||
char* chunk = (char*)malloc(size);
|
||||
char* chunk = static_cast<char*>(malloc(size));
|
||||
int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file);
|
||||
pos += size; // Move pointer position the amount of bytes read to the right
|
||||
|
||||
int32_t err;
|
||||
currentReadPos += ZCompression::Decompress((uint8_t*)chunk, size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err);
|
||||
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err);
|
||||
|
||||
free(chunk);
|
||||
}
|
||||
|
||||
@@ -8,21 +8,10 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) {
|
||||
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
|
||||
|
||||
for (int i = 0; i < m_PackPathCount; i++) {
|
||||
uint32_t stringLen = 0;
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, stringLen);
|
||||
|
||||
std::string path;
|
||||
|
||||
for (int j = 0; j < stringLen; j++) {
|
||||
char inChar;
|
||||
BinaryIO::BinaryRead<char>(m_FileStream, inChar);
|
||||
|
||||
path += inChar;
|
||||
}
|
||||
|
||||
m_PackPaths.push_back(path);
|
||||
|
||||
m_PackPaths.resize(m_PackPathCount);
|
||||
for (auto& item : m_PackPaths) {
|
||||
BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
|
||||
}
|
||||
|
||||
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);
|
||||
|
||||
@@ -10,8 +10,23 @@ dConfig::dConfig(const std::string& filepath) {
|
||||
LoadConfig();
|
||||
}
|
||||
|
||||
std::filesystem::path GetConfigDir() {
|
||||
std::filesystem::path config_dir = BinaryPathFinder::GetBinaryDir();
|
||||
if (const char* env_p = std::getenv("DLU_CONFIG_DIR")) {
|
||||
config_dir /= env_p;
|
||||
}
|
||||
return config_dir;
|
||||
}
|
||||
|
||||
const bool dConfig::Exists(const std::string& filepath) {
|
||||
std::filesystem::path config_dir = GetConfigDir();
|
||||
return std::filesystem::exists(config_dir / filepath);
|
||||
}
|
||||
|
||||
void dConfig::LoadConfig() {
|
||||
std::ifstream in(BinaryPathFinder::GetBinaryDir() / m_ConfigFilePath);
|
||||
std::filesystem::path config_dir = GetConfigDir();
|
||||
|
||||
std::ifstream in(config_dir / m_ConfigFilePath);
|
||||
if (!in.good()) return;
|
||||
|
||||
std::string line{};
|
||||
@@ -19,7 +34,7 @@ void dConfig::LoadConfig() {
|
||||
if (!line.empty() && line.front() != '#') ProcessLine(line);
|
||||
}
|
||||
|
||||
std::ifstream sharedConfig(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini", std::ios::in);
|
||||
std::ifstream sharedConfig(config_dir / "sharedconfig.ini", std::ios::in);
|
||||
if (!sharedConfig.good()) return;
|
||||
|
||||
line.clear();
|
||||
@@ -34,17 +49,20 @@ void dConfig::ReloadConfig() {
|
||||
}
|
||||
|
||||
const std::string& dConfig::GetValue(std::string key) {
|
||||
std::string upper_key(key);
|
||||
std::transform(upper_key.begin(), upper_key.end(), upper_key.begin(), ::toupper);
|
||||
if (const char* env_p = std::getenv(upper_key.c_str())) {
|
||||
this->m_ConfigValues[key] = env_p;
|
||||
}
|
||||
return this->m_ConfigValues[key];
|
||||
}
|
||||
|
||||
void dConfig::ProcessLine(const std::string& line) {
|
||||
auto splitLine = GeneralUtils::SplitString(line, '=');
|
||||
|
||||
if (splitLine.size() != 2) return;
|
||||
auto splitLoc = line.find('=');
|
||||
auto key = line.substr(0, splitLoc);
|
||||
auto value = line.substr(splitLoc + 1);
|
||||
|
||||
//Make sure that on Linux, we remove special characters:
|
||||
auto& key = splitLine.at(0);
|
||||
auto& value = splitLine.at(1);
|
||||
if (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1);
|
||||
|
||||
if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return;
|
||||
|
||||
@@ -7,6 +7,11 @@ class dConfig {
|
||||
public:
|
||||
dConfig(const std::string& filepath);
|
||||
|
||||
/**
|
||||
* Checks whether the specified filepath exists
|
||||
*/
|
||||
static const bool Exists(const std::string& filepath);
|
||||
|
||||
/**
|
||||
* Gets the specified key from the config. Returns an empty string if the value is not found.
|
||||
*
|
||||
|
||||
29
dCommon/dEnums/StringifiedEnum.h
Normal file
29
dCommon/dEnums/StringifiedEnum.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#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__
|
||||
@@ -148,11 +148,11 @@ public:
|
||||
if (size > maxSize) size = maxSize;
|
||||
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
bitStream.Write(static_cast<uint16_t>(friendName[i]));
|
||||
bitStream.Write<uint16_t>(friendName[i]);
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < remSize; ++j) {
|
||||
bitStream.Write(static_cast<uint16_t>(0));
|
||||
bitStream.Write<uint16_t>(0);
|
||||
}
|
||||
|
||||
bitStream.Write<uint32_t>(0); //???
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -166,7 +166,8 @@ enum ePlayerFlag : int32_t {
|
||||
NJ_LIGHTNING_SPINJITZU = 2031,
|
||||
NJ_ICE_SPINJITZU = 2032,
|
||||
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__
|
||||
|
||||
15
dCommon/dEnums/eQuickBuildState.h
Normal file
15
dCommon/dEnums/eQuickBuildState.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef __EQUICKBUILDSTATE__H__
|
||||
#define __EQUICKBUILDSTATE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eQuickBuildState : uint32_t {
|
||||
OPEN,
|
||||
COMPLETED = 2,
|
||||
RESETTING = 4,
|
||||
BUILDING,
|
||||
INCOMPLETE
|
||||
};
|
||||
|
||||
|
||||
#endif //!__EQUICKBUILDSTATE__H__
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef __EREBUILDSTATE__H__
|
||||
#define __EREBUILDSTATE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eRebuildState : uint32_t {
|
||||
OPEN,
|
||||
COMPLETED = 2,
|
||||
RESETTING = 4,
|
||||
BUILDING,
|
||||
INCOMPLETE
|
||||
};
|
||||
|
||||
|
||||
#endif //!__EREBUILDSTATE__H__
|
||||
@@ -34,7 +34,7 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PLATFORM_BOUNDARY,
|
||||
MODULE,
|
||||
ARCADE,
|
||||
VEHICLE_PHYSICS, // Havok demo based
|
||||
HAVOK_VEHICLE_PHYSICS,
|
||||
MOVEMENT_AI,
|
||||
EXHIBIT,
|
||||
OVERHEAD_ICON,
|
||||
@@ -50,11 +50,11 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PROPERTY_ENTRANCE,
|
||||
FX,
|
||||
PROPERTY_MANAGEMENT,
|
||||
VEHICLE_PHYSICS_NEW, // internal physics based on havok
|
||||
VEHICLE_PHYSICS,
|
||||
PHYSICS_SYSTEM,
|
||||
QUICK_BUILD,
|
||||
SWITCH,
|
||||
ZONE_CONTROL, // Minigame
|
||||
MINI_GAME_CONTROL,
|
||||
CHANGLING,
|
||||
CHOICE_BUILD,
|
||||
PACKAGE,
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
enum class eWorldMessageType : uint32_t {
|
||||
VALIDATION = 1, // Session info
|
||||
CHARACTER_LIST_REQUEST,
|
||||
@@ -36,7 +38,14 @@ enum class eWorldMessageType : uint32_t {
|
||||
HANDLE_FUNNESS,
|
||||
FAKE_PRG_CSR_MESSAGE,
|
||||
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__
|
||||
|
||||
@@ -37,10 +37,13 @@
|
||||
#include "CDPropertyTemplateTable.h"
|
||||
#include "CDFeatureGatingTable.h"
|
||||
#include "CDRailActivatorComponent.h"
|
||||
#include "CDRewardCodesTable.h"
|
||||
|
||||
#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
|
||||
// # define CDCLIENT_CACHE_ALL
|
||||
#endif // CDCLIENT_CACHE_ALL
|
||||
|
||||
#ifdef CDCLIENT_CACHE_ALL
|
||||
#define CDCLIENT_DONT_CACHE_TABLE(x) x
|
||||
@@ -82,6 +85,7 @@ CDClientManager::CDClientManager() {
|
||||
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDRarityTableTable::Instance().LoadValuesFromDatabase();
|
||||
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDRewardCodesTable::Instance().LoadValuesFromDatabase();
|
||||
CDRewardsTable::Instance().LoadValuesFromDatabase();
|
||||
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
||||
@@ -10,7 +10,7 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() {
|
||||
entry.component_type = static_cast<eReplicaComponentType>(tableData.getIntField("component_type", 0));
|
||||
entry.component_id = tableData.getIntField("component_id", -1);
|
||||
|
||||
this->mappedEntries.insert_or_assign(((uint64_t)entry.component_type) << 32 | ((uint64_t)entry.id), entry.component_id);
|
||||
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();
|
||||
@@ -22,7 +22,7 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() {
|
||||
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(((uint64_t)componentType) << 32 | ((uint64_t)id));
|
||||
auto iter = mappedEntries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
|
||||
return iter == mappedEntries.end() ? defaultValue : iter->second;
|
||||
}
|
||||
|
||||
@@ -38,15 +38,14 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
|
||||
entry.component_type = static_cast<eReplicaComponentType>(tableData.getIntField("component_type", 0));
|
||||
entry.component_id = tableData.getIntField("component_id", -1);
|
||||
|
||||
this->mappedEntries.insert_or_assign(((uint64_t)entry.component_type) << 32 | ((uint64_t)entry.id), entry.component_id);
|
||||
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(((uint64_t)componentType) << 32 | ((uint64_t)id));
|
||||
auto iter = this->mappedEntries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
|
||||
|
||||
return iter == this->mappedEntries.end() ? defaultValue : iter->second;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
struct CDComponentsRegistry {
|
||||
unsigned int id; //!< The LOT is used as the ID
|
||||
@@ -42,9 +42,9 @@ std::vector<CDFeatureGating> CDFeatureGatingTable::Query(std::function<bool(CDFe
|
||||
return data;
|
||||
}
|
||||
|
||||
bool CDFeatureGatingTable::FeatureUnlocked(const std::string& feature) const {
|
||||
bool CDFeatureGatingTable::FeatureUnlocked(const CDFeatureGating& feature) const {
|
||||
for (const auto& entry : entries) {
|
||||
if (entry.featureName == feature) {
|
||||
if (entry.featureName == feature.featureName && feature >= entry) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,12 @@ struct CDFeatureGating {
|
||||
int32_t current;
|
||||
int32_t minor;
|
||||
std::string description;
|
||||
|
||||
bool operator>=(const CDFeatureGating& b) const {
|
||||
return (this->major > b.major) ||
|
||||
(this->major == b.major && this->current > b.current) ||
|
||||
(this->major == b.major && this->current == b.current && this->minor >= b.minor);
|
||||
}
|
||||
};
|
||||
|
||||
class CDFeatureGatingTable : public CDTable<CDFeatureGatingTable> {
|
||||
@@ -21,7 +27,7 @@ public:
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDFeatureGating> Query(std::function<bool(CDFeatureGating)> predicate);
|
||||
|
||||
bool FeatureUnlocked(const std::string& feature) const;
|
||||
bool FeatureUnlocked(const CDFeatureGating& feature) const;
|
||||
|
||||
const std::vector<CDFeatureGating>& GetEntries(void) const;
|
||||
};
|
||||
@@ -25,7 +25,7 @@ void CDMissionEmailTable::LoadValuesFromDatabase() {
|
||||
entry.notificationGroup = tableData.getIntField("notificationGroup", -1);
|
||||
entry.missionID = tableData.getIntField("missionID", -1);
|
||||
entry.attachmentLOT = tableData.getIntField("attachmentLOT", 0);
|
||||
entry.localize = (bool)tableData.getIntField("localize", -1);
|
||||
entry.localize = static_cast<bool>(tableData.getIntField("localize", 1));
|
||||
entry.locStatus = tableData.getIntField("locStatus", -1);
|
||||
entry.gate_version = tableData.getStringField("gate_version", "");
|
||||
|
||||
@@ -50,4 +50,3 @@ std::vector<CDMissionEmail> CDMissionEmailTable::Query(std::function<bool(CDMiss
|
||||
const std::vector<CDMissionEmail>& CDMissionEmailTable::GetEntries() const {
|
||||
return this->entries;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user