Compare commits

...

16 Commits

Author SHA1 Message Date
David Markowitz
79bc6e73ff crash and log fix 2024-01-02 18:21:33 -08:00
David Markowitz
bb79528c0e fix: nullptr access for logger in master (#1380)
* fix nullptr access for logger

* fix nullptr access for logger

fix no save on crash

* Update MasterServer.cpp
2024-01-02 18:28:17 -06:00
David Markowitz
b1134b340f Add config update function (#1379)
Update CMakeLists.txt

FINALLY

dont ignore cmake module directory

move to separate file

very cool feature
tested that this still works

Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2024-01-02 18:25:57 -06:00
David Markowitz
1941679d27 feat: Bump standard to 20 (#1376)
* Bump to 20

* fix warnings and errors

* thanks RakNet
2024-01-02 07:53:00 +00:00
Daniel Seiler
85672e060a fix: signal handling (#1375)
* fix: signal handling

* fix: flush WorldServer logger before main loop

* fix: consolidate signal code
2024-01-01 21:50:00 -06:00
Daniel Seiler
18feea5fed fix: optional party phrases (#1377)
* fix: optional party phrases

Don't return early if there are no party phrases

* Update VanityUtilities.cpp
2024-01-01 17:08:38 -06:00
David Markowitz
e54faa3820 chore: organize build flags (#1371)
* chore: organize build flags

* Remove ambiguous include path

Don't be default incluyde bcrypt so you need to specify the folder.  Allows pre-processor to find the correct file.

* Revert settings

* working

f
2023-12-31 00:26:49 -06:00
David Markowitz
4ecb6ae30e fix: joining lobby twice (#1374) 2023-12-31 00:14:58 -06:00
Gie "Max" Vanommeslaeghe
c708246f73 Merge pull request #1247 from maxdelayer/main
Fix edge case where leaderboard viewing would cause unhandled exception
2023-12-31 00:43:37 +01:00
Gie "Max" Vanommeslaeghe
295ba628c2 Merge pull request #1372 from DarkflameUniverse/feature/update-connector
fix: bump connector version
2023-12-31 00:32:52 +01:00
Xiphoseer
d8f74f008f fix: bump connector version 2023-12-31 00:22:39 +01:00
Daniel Seiler
42a71bbeab feat: add DLU_CONFIG_DIR env var (#1370)
* feat: add DLU_CONFIG_DIR env var

* fix: PascalCase
2023-12-30 07:07:49 -06:00
Daniel Seiler
98d2f25af2 feat: get & print std::current_exception (#1366) 2023-12-30 08:04:26 +01:00
Daniel Seiler
dd9d94f75f feat: allow env var override for game config (#1367) 2023-12-30 08:04:09 +01:00
Daniel Seiler
f08df25085 feat: split out system() calls from the rest of MasterServer (#1368) 2023-12-30 08:00:43 +01:00
Max
a19bead268 Fix edge case where leaderboard viewing would cause unhandled exception 2023-10-29 22:40:06 -04:00
43 changed files with 439 additions and 268 deletions

1
.gitignore vendored
View File

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

View File

@@ -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,61 @@ 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)
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 +160,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 +184,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 +203,9 @@ set(INCLUDED_DIRECTORIES
"dCommon"
"dCommon/dClient"
"dCommon/dEnums"
"dChatFilter"
"dGame"
"dGame/dBehaviors"
"dGame/dComponents"
@@ -209,10 +216,14 @@ set(INCLUDED_DIRECTORIES
"dGame/dPropertyBehaviors"
"dGame/dPropertyBehaviors/ControlBehaviorMessages"
"dGame/dUtilities"
"dPhysics"
"dNavigation"
"dNavigation/dTerrain"
"dZoneManager"
"dDatabase"
"dDatabase/CDClientDatabase"
"dDatabase/CDClientDatabase/CDClientTables"
@@ -220,7 +231,9 @@ set(INCLUDED_DIRECTORIES
"dDatabase/GameDatabase/ITables"
"dDatabase/GameDatabase/MySQL"
"dDatabase/GameDatabase/MySQL/Tables"
"dNet"
"dScripts"
"dScripts/02_server"
"dScripts/ai"
@@ -304,28 +317,24 @@ set(INCLUDED_DIRECTORIES
"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})
@@ -378,10 +387,10 @@ add_subdirectory(dPhysics)
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()
@@ -419,6 +428,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()

View File

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

47
cmake/Utils.cmake Normal file
View File

@@ -0,0 +1,47 @@
# 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")
file(READ ${file_name} current_file_contents)
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
set(parsed_current_file_contents "")
# Remove comment lines so they do not interfere with the variable parsing
foreach(line ${current_file_contents})
string(FIND ${line} "#" is_comment)
if(NOT ${is_comment} EQUAL 0)
string(APPEND parsed_current_file_contents ${line})
endif()
endforeach()
set(found_new_option -1)
set(found_old_option -1)
set(current_value -1)
foreach(line ${current_file_contents})
string(FIND ${line} ${old_option_name} old_option_in_file)
if(${old_option_in_file} EQUAL 0)
set(found_old_option 1)
set(current_value ${line})
endif()
string(FIND ${line} ${new_option_name} found_new_option_in_file)
if(${found_new_option_in_file} EQUAL 0)
set(found_new_option 1)
endif()
endforeach(line ${current_file_contents})
if(${found_old_option} EQUAL 1 AND NOT ${found_new_option} EQUAL 1)
string(REPLACE ${old_option_name} ${new_option_name} current_value ${current_value})
file(APPEND ${file_name} "\n" ${current_value})
endif()
endfunction()

View File

@@ -1,6 +1,7 @@
#include <iostream>
#include <string>
#include <ctime>
#include <csignal>
#include <chrono>
#include <thread>
@@ -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,17 +43,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() / "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__);
try {
@@ -74,6 +78,7 @@ int main(int argc, char** argv) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
}
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
Game::randomEngine = std::mt19937(time(0));
@@ -83,7 +88,7 @@ int main(int argc, char** argv) {
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
Game::server = new dServer(Game::config->GetValue("external_ip"), 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();
@@ -96,13 +101,16 @@ int main(int argc, char** argv) {
AuthPackets::LoadClaimCodes();
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++;
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.
@@ -141,6 +149,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;

View File

@@ -1,2 +1,3 @@
add_executable(AuthServer "AuthServer.cpp")
target_link_libraries(AuthServer ${COMMON_LIBRARIES})
add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")

View File

@@ -6,6 +6,7 @@ set(DCHATSERVER_SOURCES
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)

View File

@@ -33,7 +33,7 @@ namespace Game {
dConfig* config = nullptr;
dChatFilter* chatFilter = nullptr;
AssetManager* assetManager = nullptr;
bool shouldShutdown = false;
Game::signal_t lastSignal = 0;
std::mt19937 randomEngine;
PlayerContainer playerContainer;
}
@@ -48,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 {
@@ -101,7 +104,7 @@ int main(int argc, char** argv) {
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
Game::server = new dServer(Game::config->GetValue("external_ip"), 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"))));
@@ -118,7 +121,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++;

View File

@@ -5,6 +5,7 @@ set(DCOMMON_SOURCES
"dConfig.cpp"
"Diagnostics.cpp"
"Logger.cpp"
"Game.cpp"
"GeneralUtils.cpp"
"LDFFormat.cpp"
"MD5.cpp"

View File

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

7
dCommon/Game.cpp Normal file
View File

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

View File

@@ -1,6 +1,8 @@
#pragma once
#include <string>
#include <random>
#include <csignal>
class dServer;
class Logger;
@@ -15,6 +17,7 @@ class dZoneManager;
class PlayerContainer;
namespace Game {
using signal_t = volatile std::sig_atomic_t;
extern Logger* logger;
extern dServer* server;
extern InstanceManager* im;
@@ -24,8 +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);
}

View File

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

View File

@@ -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,6 +49,11 @@ 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];
}

View File

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

View File

@@ -39,9 +39,11 @@
#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

View File

@@ -14,3 +14,7 @@ endforeach()
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)
if (${CDCLIENT_CACHE_ALL})
add_compile_definitions(dDatabase CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL})
endif()

View File

@@ -193,7 +193,7 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r
SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
WHERE leaderboardsRanked.ranking
BETWEEN
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9)
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), CAST(lowestRanking.lowestRank AS SIGNED) - 9)
AND
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
ORDER BY ranking ASC;

View File

@@ -263,7 +263,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
}
//Now that the name is ok, we can get an objectID from Master:
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) {
ObjectIDManager::Instance()->RequestPersistentID([=, this](uint32_t objectID) {
if (Database::Get()->GetCharacterInfo(objectID)) {
LOG("Character object id unavailable, check object_id_tracker!");
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);

View File

@@ -33,7 +33,7 @@ ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Compo
if (activityID > 0) m_ActivityID = activityID;
else m_ActivityID = parent->GetVar<int32_t>(u"activityID");
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
std::vector<CDActivities> activities = activitiesTable->Query([this](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (CDActivities activity : activities) {
m_ActivityInfo = activity;
@@ -93,7 +93,7 @@ void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIniti
void ActivityComponent::ReloadConfig() {
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
std::vector<CDActivities> activities = activitiesTable->Query([this](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (auto activity : activities) {
auto mapID = m_ActivityInfo.instanceMapID;
if (static_cast<Leaderboard::Type>(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") {
@@ -115,6 +115,7 @@ void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::stri
}
void ActivityComponent::PlayerJoin(Entity* player) {
if (PlayerIsInQueue(player)) return;
// If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
if (HasLobby()) {
PlayerJoinLobby(player);
@@ -531,7 +532,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
// First, get the activity data
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([this](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
if (!activityRewards.empty()) {
uint32_t minCoins = 0;

View File

@@ -95,10 +95,16 @@ void BuffComponent::Update(float deltaTime) {
if (buff.second.time <= 0.0f) {
RemoveBuff(buff.first);
break;
}
}
if (m_BuffsToRemove.empty()) return;
for (const auto& buff : m_BuffsToRemove) {
m_Buffs.erase(buff);
}
m_BuffsToRemove.clear();
}
const std::string& GetFxName(const std::string& buffname) {
@@ -216,7 +222,7 @@ void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
m_Buffs.erase(iter);
m_BuffsToRemove.push_back(id);
RemoveBuffEffect(id);
}

View File

@@ -140,6 +140,9 @@ private:
*/
std::map<int32_t, Buff> m_Buffs;
// Buffs to remove at the end of the update frame.
std::vector<int32_t> m_BuffsToRemove;
/**
* Parameters (=effects) for each buff
*/

View File

@@ -312,7 +312,7 @@ void RacingControlComponent::OnRequestDie(Entity* player) {
}
// Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else...
vehicle->AddCallbackTimer(2.0f, [=]() {
vehicle->AddCallbackTimer(2.0f, [=, this]() {
if (!vehicle || !this->m_Parent) return;
GameMessages::SendRacingResetPlayerToLastReset(
m_Parent->GetObjectID(), racingPlayer.playerID,

View File

@@ -249,7 +249,7 @@ bool Item::IsEquipped() const {
bool Item::Consume() {
auto* skillsTable = CDClientManager::Instance().GetTable<CDObjectSkillsTable>();
auto skills = skillsTable->Query([=](const CDObjectSkills entry) {
auto skills = skillsTable->Query([this](const CDObjectSkills entry) {
return entry.objectTemplate == static_cast<uint32_t>(lot);
});

View File

@@ -294,21 +294,20 @@ void VanityUtilities::ParseXML(const std::string& file) {
auto* partyPhrases = npcs->FirstChildElement("partyphrases");
if (partyPhrases == nullptr) {
LOG("Failed to parse party phrases");
return;
}
for (auto* phrase = partyPhrases->FirstChildElement("phrase"); phrase != nullptr;
phrase = phrase->NextSiblingElement("phrase")) {
// Get the phrase
auto* text = phrase->GetText();
if (text == nullptr) {
LOG("Failed to parse party phrase");
continue;
LOG("No party phrases found");
} else {
for (auto* phrase = partyPhrases->FirstChildElement("phrase"); phrase != nullptr;
phrase = phrase->NextSiblingElement("phrase")) {
// Get the phrase
auto* text = phrase->GetText();
if (text == nullptr) {
LOG("Failed to parse party phrase");
continue;
}
m_PartyPhrases.push_back(text);
}
m_PartyPhrases.push_back(text);
}
for (auto* npc = npcs->FirstChildElement("npc"); npc != nullptr; npc = npc->NextSiblingElement("npc")) {
@@ -525,12 +524,12 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) {
#endif
// Replace "__TIMESTAMP__" with the __TIMESTAMP__
GeneralUtils::ReplaceInString(line, "__TIMESTAMP__", __TIMESTAMP__);
// Replace "__VERSION__" wit'h the PROJECT_VERSION
GeneralUtils::ReplaceInString(line, "__VERSION__", STRINGIFY(PROJECT_VERSION));
// Replace "__VERSION__" with the PROJECT_VERSION
GeneralUtils::ReplaceInString(line, "__VERSION__", Game::projectVersion);
// Replace "__SOURCE__" with SOURCE
GeneralUtils::ReplaceInString(line, "__SOURCE__", Game::config->GetValue("source"));
// Replace "__LICENSE__" with LICENSE
GeneralUtils::ReplaceInString(line, "__LICENSE__", STRINGIFY(LICENSE));
GeneralUtils::ReplaceInString(line, "__LICENSE__", "AGPL-3.0");
if (line.find("##") != std::string::npos) {
// Add "&lt;font size=&apos;18&apos; color=&apos;#000000&apos;&gt;" before the header

View File

@@ -1,10 +1,12 @@
set(DMASTERSERVER_SOURCES
"InstanceManager.cpp"
"ObjectIDManager.cpp"
"Start.cpp"
)
add_library(dMasterServer ${DMASTERSERVER_SOURCES})
add_executable(MasterServer "MasterServer.cpp")
add_compile_definitions(MasterServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
target_link_libraries(dMasterServer ${COMMON_LIBRARIES})
target_link_libraries(MasterServer ${COMMON_LIBRARIES} dMasterServer)

View File

@@ -9,10 +9,11 @@
#include "CDZoneTableTable.h"
#include "MasterPackets.h"
#include "BitStreamUtils.h"
#include "BinaryPathFinder.h"
#include "eConnectionType.h"
#include "eMasterMessageType.h"
#include "Start.h"
InstanceManager::InstanceManager(Logger* logger, const std::string& externalIP) {
mLogger = logger;
mExternalIP = externalIP;
@@ -57,33 +58,7 @@ Instance* InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LW
instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, softCap, maxPlayers);
//Start the actual process:
#ifdef _WIN32
std::string cmd = "start " + (BinaryPathFinder::GetBinaryDir() / "WorldServer.exe").string() + " -zone ";
#else
std::string cmd;
if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) {
cmd = "sudo " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
} else {
cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
}
#endif
cmd.append(std::to_string(mapID));
cmd.append(" -port ");
cmd.append(std::to_string(port));
cmd.append(" -instance ");
cmd.append(std::to_string(m_LastInstanceID));
cmd.append(" -maxclients ");
cmd.append(std::to_string(maxPlayers));
cmd.append(" -clone ");
cmd.append(std::to_string(cloneID));
#ifndef _WIN32
cmd.append("&"); //Sends our next process to the background on Linux
#endif
auto ret = system(cmd.c_str());
StartWorldServer(mapID, port, m_LastInstanceID, maxPlayers, cloneID);
m_Instances.push_back(instance);
@@ -159,7 +134,7 @@ void InstanceManager::RemoveInstance(Instance* instance) {
if (m_Instances[i] == instance) {
instance->SetShutdownComplete(true);
if (!Game::shouldShutdown) RedirectPendingRequests(instance);
if (!Game::ShouldShutdown()) RedirectPendingRequests(instance);
delete m_Instances[i];
@@ -318,28 +293,7 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon
instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
//Start the actual process:
std::string cmd = "start " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
#ifndef _WIN32
cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
#endif
cmd.append(std::to_string(mapID));
cmd.append(" -port ");
cmd.append(std::to_string(port));
cmd.append(" -instance ");
cmd.append(std::to_string(m_LastInstanceID));
cmd.append(" -maxclients ");
cmd.append(std::to_string(maxPlayers));
cmd.append(" -clone ");
cmd.append(std::to_string(cloneID));
#ifndef WIN32
cmd.append("&"); //Sends our next process to the background on Linux
#endif
auto ret = system(cmd.c_str());
StartWorldServer(mapID, port, m_LastInstanceID, maxPlayers, cloneID);
m_Instances.push_back(instance);

View File

@@ -7,11 +7,7 @@
#include <thread>
#include <fstream>
#ifdef _WIN32
#include <bcrypt/BCrypt.hpp>
#else
#include <bcrypt.h>
#endif
#include <csignal>
@@ -43,6 +39,7 @@
#include "PacketUtils.h"
#include "FdbToSqlite.h"
#include "BitStreamUtils.h"
#include "Start.h"
namespace Game {
Logger* logger = nullptr;
@@ -50,16 +47,15 @@ namespace Game {
InstanceManager* im = nullptr;
dConfig* config = nullptr;
AssetManager* assetManager = nullptr;
bool shouldShutdown = false;
Game::signal_t lastSignal = 0;
bool universeShutdownRequested = false;
std::mt19937 randomEngine;
} //namespace Game
bool shutdownSequenceStarted = false;
void ShutdownSequence(int32_t signal = -1);
int ShutdownSequence(int32_t signal = -1);
int32_t FinalizeShutdown(int32_t signal = -1);
Logger* SetupLogger();
void StartAuthServer();
void StartChatServer();
void HandlePacket(Packet* packet);
std::map<uint32_t, std::string> activeSessions;
SystemAddress authServerMasterPeerSysAddr;
@@ -78,39 +74,39 @@ int main(int argc, char** argv) {
//Triggers the shutdown sequence at application exit
std::atexit([]() { ShutdownSequence(); });
signal(SIGINT, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); });
signal(SIGTERM, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); });
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;
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "authconfig.ini")) {
if (!dConfig::Exists("authconfig.ini")) {
LOG("Couldnt find authconfig.ini");
return EXIT_FAILURE;
}
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "chatconfig.ini")) {
if (!dConfig::Exists("chatconfig.ini")) {
LOG("Couldnt find chatconfig.ini");
return EXIT_FAILURE;
}
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "masterconfig.ini")) {
if (!dConfig::Exists("masterconfig.ini")) {
LOG("Couldnt find masterconfig.ini");
return EXIT_FAILURE;
}
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini")) {
if (!dConfig::Exists("sharedconfig.ini")) {
LOG("Couldnt find sharedconfig.ini");
return EXIT_FAILURE;
}
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "worldconfig.ini")) {
if (!dConfig::Exists("worldconfig.ini")) {
LOG("Couldnt find worldconfig.ini");
return EXIT_FAILURE;
}
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "masterconfig.ini").string());
Game::config = new dConfig("masterconfig.ini");
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
@@ -126,7 +122,7 @@ int main(int argc, char** argv) {
LOG("Using net version %s", Game::config->GetValue("client_net_version").c_str());
LOG("Starting Master 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
@@ -291,7 +287,7 @@ int main(int argc, char** argv) {
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::stoi(Game::config->GetValue("port"));
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::shouldShutdown);
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal);
//Query for the database for a server labeled "master"
@@ -326,7 +322,8 @@ int main(int argc, char** argv) {
uint32_t framesSinceLastSQLPing = 0;
uint32_t framesSinceKillUniverseCommand = 0;
while (true) {
Game::logger->Flush();
while (!Game::ShouldShutdown()) {
//In world we'd update our other systems here.
//Check for packets here:
@@ -360,10 +357,10 @@ int main(int argc, char** argv) {
framesSinceLastSQLPing++;
//10m shutdown for universe kill command
if (Game::shouldShutdown) {
if (Game::universeShutdownRequested) {
if (framesSinceKillUniverseCommand >= shutdownUniverseTime) {
//Break main loop and exit
break;
Game::lastSignal = -1;
} else
framesSinceKillUniverseCommand++;
}
@@ -407,7 +404,7 @@ int main(int argc, char** argv) {
t += std::chrono::milliseconds(masterFrameDelta);
std::this_thread::sleep_until(t);
}
return FinalizeShutdown(EXIT_SUCCESS);
return ShutdownSequence(EXIT_SUCCESS);
}
Logger* SetupLogger() {
@@ -804,7 +801,7 @@ void HandlePacket(Packet* packet) {
case eMasterMessageType::SHUTDOWN_UNIVERSE: {
LOG("Received shutdown universe command, shutting down in 10 minutes.");
Game::shouldShutdown = true;
Game::universeShutdownRequested = true;
break;
}
@@ -814,46 +811,12 @@ void HandlePacket(Packet* packet) {
}
}
void StartChatServer() {
if (Game::shouldShutdown) {
LOG("Currently shutting down. Chat will not be restarted.");
return;
}
#ifdef __APPLE__
//macOS doesn't need sudo to run on ports < 1024
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str());
#else
if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
}
#endif
}
void StartAuthServer() {
if (Game::shouldShutdown) {
LOG("Currently shutting down. Auth will not be restarted.");
return;
}
#ifdef __APPLE__
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str());
#else
if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
}
#endif
}
void ShutdownSequence(int32_t signal) {
int ShutdownSequence(int32_t signal) {
if (!Game::logger) return -1;
LOG("Recieved Signal %d", signal);
if (shutdownSequenceStarted) {
return;
LOG("Duplicate Shutdown Sequence");
return -1;
}
if (!Game::im) {
@@ -862,7 +825,7 @@ void ShutdownSequence(int32_t signal) {
Game::im->SetIsShuttingDown(true);
shutdownSequenceStarted = true;
Game::shouldShutdown = true;
Game::lastSignal = -1;
{
CBITSTREAM;
@@ -931,16 +894,20 @@ void ShutdownSequence(int32_t signal) {
}
}
FinalizeShutdown(signal);
return FinalizeShutdown(signal);
}
int32_t FinalizeShutdown(int32_t signal) {
//Delete our objects here:
Database::Destroy("MasterServer");
if (Game::config) delete Game::config;
Game::config = nullptr;
if (Game::im) delete Game::im;
Game::im = nullptr;
if (Game::server) delete Game::server;
Game::server = nullptr;
if (Game::logger) delete Game::logger;
Game::logger = nullptr;
if (signal != EXIT_SUCCESS) exit(signal);
return signal;

71
dMasterServer/Start.cpp Normal file
View File

@@ -0,0 +1,71 @@
#include "Start.h"
#include "Logger.h"
#include "dConfig.h"
#include "Game.h"
#include "BinaryPathFinder.h"
void StartChatServer() {
if (Game::ShouldShutdown()) {
LOG("Currently shutting down. Chat will not be restarted.");
return;
}
#ifdef __APPLE__
//macOS doesn't need sudo to run on ports < 1024
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str());
#else
if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
}
#endif
}
void StartAuthServer() {
if (Game::ShouldShutdown()) {
LOG("Currently shutting down. Auth will not be restarted.");
return;
}
#ifdef __APPLE__
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str());
#else
if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
}
#endif
}
void StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID) {
#ifdef _WIN32
std::string cmd = "start " + (BinaryPathFinder::GetBinaryDir() / "WorldServer.exe").string() + " -zone ";
#else
std::string cmd;
if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) {
cmd = "sudo " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
} else {
cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
}
#endif
cmd.append(std::to_string(mapID));
cmd.append(" -port ");
cmd.append(std::to_string(port));
cmd.append(" -instance ");
cmd.append(std::to_string(lastInstanceID));
cmd.append(" -maxclients ");
cmd.append(std::to_string(maxPlayers));
cmd.append(" -clone ");
cmd.append(std::to_string(cloneID));
#ifndef _WIN32
cmd.append("&"); //Sends our next process to the background on Linux
#endif
auto ret = system(cmd.c_str());
}

6
dMasterServer/Start.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include "dCommonVars.h"
void StartAuthServer();
void StartChatServer();
void StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID);

View File

@@ -11,11 +11,7 @@
#include "SHA512.h"
#include "GeneralUtils.h"
#ifdef _WIN32
#include <bcrypt/BCrypt.hpp>
#else
#include <bcrypt.h>
#endif
#include <BitStream.h>
#include <future>
@@ -59,7 +55,9 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::VERSION_CONFIRM);
uint32_t netVersion;
if (!GeneralUtils::TryParse(Game::config->GetValue("client_net_version"), netVersion)) {
const std::string& expectedVersion = Game::config->GetValue("client_net_version");
LOG("Expected Version: '%s'", expectedVersion.c_str());
if (!GeneralUtils::TryParse(expectedVersion, netVersion)) {
LOG("Failed to parse client_net_version. Cannot authenticate to %s:%i", nextServerIP.c_str(), nextServerPort);
return;
}

View File

@@ -39,7 +39,7 @@ public:
}
} ReceiveDownloadCompleteCB;
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, bool* shouldShutdown, unsigned int zoneID) {
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) {
mIP = ip;
mPort = port;
mZoneID = zoneID;
@@ -55,7 +55,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
mReplicaManager = nullptr;
mServerType = serverType;
mConfig = config;
mShouldShutdown = shouldShutdown;
mShouldShutdown = lastSignal;
//Attempt to start our server here:
mIsOkay = Startup();
@@ -75,7 +75,9 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
//Connect to master if we are not master:
if (serverType != ServerType::Master) {
SetupForMasterConnection();
ConnectToMaster();
if (!ConnectToMaster()) {
LOG("Failed ConnectToMaster!");
}
}
//Set up Replica if we're a world server:
@@ -129,7 +131,7 @@ Packet* dServer::ReceiveFromMaster() {
break;
}
case eMasterMessageType::SHUTDOWN:
*mShouldShutdown = true;
*mShouldShutdown = -2;
break;
//When we handle these packets in World instead dServer, we just return the packet's pointer.
@@ -236,10 +238,12 @@ void dServer::Shutdown() {
void dServer::SetupForMasterConnection() {
mMasterSocketDescriptor = SocketDescriptor(uint16_t(mPort + 1), 0);
mMasterPeer = RakNetworkFactory::GetRakPeerInterface();
mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1);
bool ret = mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1);
if (!ret) LOG("Failed MasterPeer Startup!");
}
bool dServer::ConnectToMaster() {
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15);
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include <string>
#include <csignal>
#include "RakPeerInterface.h"
#include "ReplicaManager.h"
#include "NetworkIDManager.h"
@@ -15,6 +16,10 @@ enum class ServerType : uint32_t {
World
};
namespace Game {
using signal_t = volatile std::sig_atomic_t;
}
class dServer {
public:
// Default constructor should only used for testing!
@@ -31,7 +36,7 @@ public:
int masterPort,
ServerType serverType,
dConfig* config,
bool* shouldShutdown,
Game::signal_t* shouldShutdown,
unsigned int zoneID = 0);
~dServer();
@@ -81,9 +86,9 @@ private:
NetworkIDManager* mNetIDManager = nullptr;
/**
* Whether or not to shut down the server. Pointer to Game::shouldShutdown.
* Whether or not to shut down the server. Pointer to Game::lastSignal.
*/
bool* mShouldShutdown = nullptr;
Game::signal_t* mShouldShutdown = nullptr;
SocketDescriptor mSocketDescriptor;
std::string mIP;
int mPort;

View File

@@ -5,6 +5,7 @@ set(DWORLDSERVER_SOURCES
add_library(dWorldServer ${DWORLDSERVER_SOURCES})
add_executable(WorldServer "WorldServer.cpp")
add_compile_definitions(WorldServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
target_link_libraries(dWorldServer ${COMMON_LIBRARIES})
target_link_libraries(WorldServer ${COMMON_LIBRARIES} dChatFilter dGame dZoneManager dPhysics Detour Recast tinyxml2 dWorldServer dNavigation)

View File

@@ -88,9 +88,10 @@ namespace Game {
RakPeerInterface* chatServer = nullptr;
std::mt19937 randomEngine;
SystemAddress chatSysAddr;
bool shouldShutdown = false;
Game::signal_t lastSignal = 0;
EntityManager* entityManager = nullptr;
dZoneManager* zoneManager = nullptr;
std::string projectVersion = PROJECT_VERSION;
} // namespace Game
bool chatDisabled = false;
@@ -123,8 +124,8 @@ int main(int argc, char** argv) {
// Triggers the shutdown sequence at application exit
std::atexit(WorldShutdownSequence);
signal(SIGINT, [](int) { WorldShutdownSequence(); });
signal(SIGTERM, [](int) { WorldShutdownSequence(); });
std::signal(SIGINT, Game::OnSignal);
std::signal(SIGTERM, Game::OnSignal);
uint32_t zoneID = 1000;
uint32_t cloneID = 0;
@@ -147,12 +148,12 @@ int main(int argc, char** argv) {
if (!Game::logger) return EXIT_FAILURE;
//Read our config:
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "worldconfig.ini").string());
Game::config = new dConfig("worldconfig.ini");
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
LOG("Starting World server...");
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
LOG("Version: %s", Game::projectVersion.c_str());
LOG("Compiled on: %s", __TIMESTAMP__);
if (Game::config->GetValue("disable_chat") == "1") chatDisabled = true;
@@ -211,7 +212,7 @@ int main(int argc, char** argv) {
UserManager::Instance()->Initialize();
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::shouldShutdown, zoneID);
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, zoneID);
//Connect to the chat server:
uint32_t chatPort = 1501;
@@ -312,6 +313,8 @@ int main(int argc, char** argv) {
uint32_t saveTime = 10 * 60 * currentFramerate; // 10 minutes in frames
uint32_t sqlPingTime = 10 * 60 * currentFramerate; // 10 minutes in frames
uint32_t emptyShutdownTime = (cloneID == 0 ? 30 : 5) * 60 * currentFramerate; // 30 minutes for main worlds, 5 for all others.
Game::logger->Flush(); // once immediately before the main loop
while (true) {
Metrics::StartMeasurement(MetricVariable::Frame);
Metrics::StartMeasurement(MetricVariable::GameLoop);
@@ -362,9 +365,9 @@ int main(int argc, char** argv) {
if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++;
if (framesSinceMasterDisconnect >= noMasterConnectionTimeout && !Game::shouldShutdown) {
if (framesSinceMasterDisconnect >= noMasterConnectionTimeout && !Game::ShouldShutdown()) {
LOG("Game loop running but no connection to master for %d frames, shutting down", noMasterConnectionTimeout);
Game::shouldShutdown = true;
Game::lastSignal = -1;
}
} else framesSinceMasterDisconnect = 0;
@@ -459,7 +462,7 @@ int main(int argc, char** argv) {
//If we haven't had any players for a while, time out and shut down:
if (framesSinceLastUser >= emptyShutdownTime) {
Game::shouldShutdown = true;
Game::lastSignal = -1;
}
} else {
framesSinceLastUser = 0;
@@ -512,7 +515,7 @@ int main(int argc, char** argv) {
}
}
if (Game::shouldShutdown && !worldShutdownSequenceComplete) {
if (Game::ShouldShutdown() && !worldShutdownSequenceComplete) {
WorldShutdownProcess(zoneID);
break;
}
@@ -821,7 +824,7 @@ void HandlePacket(Packet* packet) {
}
case eMasterMessageType::SHUTDOWN: {
Game::shouldShutdown = true;
Game::lastSignal = -1;
LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
break;
}
@@ -1288,14 +1291,17 @@ void WorldShutdownProcess(uint32_t zoneId) {
}
void WorldShutdownSequence() {
Game::shouldShutdown = true;
bool shouldShutdown = Game::ShouldShutdown() || worldShutdownSequenceComplete;
Game::lastSignal = -1;
#ifndef DARKFLAME_PLATFORM_WIN32
if (Game::shouldShutdown || worldShutdownSequenceComplete)
if (shouldShutdown)
#endif
{
return;
}
if (!Game::logger) return;
LOG("Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), instanceID);
WorldShutdownProcess(Game::server->GetZoneID());
FinalizeShutdown();

View File

@@ -51,20 +51,20 @@ Spawner::Spawner(const SpawnerInfo info) {
std::vector<Spawner*> spawnSmashSpawnersN = Game::zoneManager->GetSpawnersByName(m_Info.spawnOnSmashGroupName);
for (Entity* ssEntity : spawnSmashEntities) {
m_SpawnSmashFoundGroup = true;
ssEntity->AddDieCallback([=]() {
ssEntity->AddDieCallback([=, this]() {
Spawn();
});
}
for (Spawner* ssSpawner : spawnSmashSpawners) {
m_SpawnSmashFoundGroup = true;
ssSpawner->AddSpawnedEntityDieCallback([=]() {
ssSpawner->AddSpawnedEntityDieCallback([=, this]() {
Spawn();
});
}
for (Spawner* ssSpawner : spawnSmashSpawnersN) {
m_SpawnSmashFoundGroup = true;
m_SpawnOnSmash = ssSpawner;
ssSpawner->AddSpawnedEntityDieCallback([=]() {
ssSpawner->AddSpawnedEntityDieCallback([=, this]() {
Spawn();
});
}

View File

@@ -33,7 +33,7 @@ ARG BUILD_THREADS=1
ARG BUILD_VERSION=171022
RUN echo "Build server"
RUN sed -i -e "s/__maria_db_connector_compile_jobs__=.*/__maria_db_connector_compile_jobs__=${BUILD_THREADS}/g" CMakeVariables.txt
RUN sed -i -e "s/MARIADB_CONNECTOR_COMPILE_JOBS=.*/MARIADB_CONNECTOR_COMPILE_JOBS=${BUILD_THREADS}/g" CMakeVariables.txt
RUN mkdir -p cmake_build
RUN cd cmake_build && \
cmake .. -DCMAKE_BUILD_RPATH_USE_ORIGIN=TRUE && \

View File

@@ -26,7 +26,7 @@ TEST_F(EncodingTest, TestEncodingHello) {
};
TEST_F(EncodingTest, TestEncodingUmlaut) {
originalWord = u8"Frühling";
originalWord = reinterpret_cast<const char*>(u8"Frühling");
originalWordSv = originalWord;
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'F');

View File

@@ -11,4 +11,5 @@ namespace Game {
AssetManager* assetManager = nullptr;
SystemAddress chatSysAddr;
EntityManager* entityManager = nullptr;
std::string projectVersion;
}

View File

@@ -11,11 +11,17 @@ file(
GLOB SOURCES_LIBBCRYPT
LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/*.c
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/src/*.c
)
add_library(bcrypt ${SOURCES_LIBBCRYPT})
# Because we are not using the libbcrypt CMakeLists.txt, we need to include these headers for the library to use.
# fortunately they are only needed for building the libbcrypt directory and nothing else, so these are marked private.
target_include_directories(bcrypt PRIVATE "libbcrypt/include")
target_include_directories(bcrypt PRIVATE "libbcrypt/src")
# Source code for sqlite
add_subdirectory(SQLite)
@@ -31,7 +37,7 @@ add_subdirectory(raknet)
# Download Backtrace if configured
if(UNIX AND NOT APPLE)
include(FetchContent)
if (__include_backtrace__ AND __compile_backtrace__)
if (${INCLUDE_BACKTRACE} AND ${COMPILE_BACKTRACE})
FetchContent_Declare(
backtrace
GIT_REPOSITORY https://github.com/ianlancetaylor/libbacktrace.git

View File

@@ -93,7 +93,7 @@ else() # Build from source
-DINSTALL_PLUGINDIR=plugin
${MARIADB_EXTRA_CMAKE_ARGS}
PREFIX "${PROJECT_BINARY_DIR}/mariadbcpp"
BUILD_COMMAND cmake --build . --config RelWithDebInfo -j${__maria_db_connector_compile_jobs__}
BUILD_COMMAND cmake --build . --config RelWithDebInfo -j${MARIADB_CONNECTOR_COMPILE_JOBS}
INSTALL_COMMAND "")
ExternalProject_Get_Property(mariadb_connector_cpp BINARY_DIR)

View File

@@ -80,6 +80,8 @@ target_compile_options(raknet PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:
/w>)
set_property(TARGET raknet PROPERTY CXX_STANDARD 17)
if(WIN32)
# Link Win Sockets 2 to RakNet
target_link_libraries(raknet ws2_32)