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 docker-compose.override.yml
!*Test.bin !*Test.bin
!cmake/*

View File

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

View File

@@ -1,22 +1,32 @@
PROJECT_VERSION_MAJOR=1 PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=1 PROJECT_VERSION_MINOR=1
PROJECT_VERSION_PATCH=1 PROJECT_VERSION_PATCH=1
# LICENSE
LICENSE=AGPL-3.0
# Debugging # Debugging
# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs. # Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
__dynamic=1 DYNAMIC=1
# Set __ggdb to 1 to enable the -ggdb flag for the linker, including more debug info.
# __ggdb=1 # Set GGDB to 1 to enable the -ggdb flag for the linker, including more debug info.
# 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__=1 GGDB=0
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
# __compile_backtrace__=1 # 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. # 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. # When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
__enable_testing__=1 ENABLE_TESTING=1
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default. # The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/ OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
# 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 <iostream>
#include <string> #include <string>
#include <ctime> #include <ctime>
#include <csignal>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
@@ -28,7 +29,7 @@ namespace Game {
Logger* logger = nullptr; Logger* logger = nullptr;
dServer* server = nullptr; dServer* server = nullptr;
dConfig* config = nullptr; dConfig* config = nullptr;
bool shouldShutdown = false; Game::signal_t lastSignal = 0;
std::mt19937 randomEngine; std::mt19937 randomEngine;
} }
@@ -42,17 +43,20 @@ int main(int argc, char** argv) {
Diagnostics::SetProcessFileName(argv[0]); Diagnostics::SetProcessFileName(argv[0]);
Diagnostics::Initialize(); Diagnostics::Initialize();
std::signal(SIGINT, Game::OnSignal);
std::signal(SIGTERM, Game::OnSignal);
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Game::logger = SetupLogger(); Game::logger = SetupLogger();
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
//Read our config: //Read our config:
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string()); Game::config = new dConfig("authconfig.ini");
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
LOG("Starting Auth server..."); LOG("Starting Auth server...");
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); LOG("Version: %s", PROJECT_VERSION);
LOG("Compiled on: %s", __TIMESTAMP__); LOG("Compiled on: %s", __TIMESTAMP__);
try { try {
@@ -74,6 +78,7 @@ int main(int argc, char** argv) {
masterIP = masterInfo->ip; masterIP = masterInfo->ip;
masterPort = masterInfo->port; masterPort = masterInfo->port;
} }
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
Game::randomEngine = std::mt19937(time(0)); 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("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); 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: //Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now(); auto t = std::chrono::high_resolution_clock::now();
@@ -96,13 +101,16 @@ int main(int argc, char** argv) {
AuthPackets::LoadClaimCodes(); AuthPackets::LoadClaimCodes();
while (!Game::shouldShutdown) { Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) {
//Check if we're still connected to master: //Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) { if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++; framesSinceMasterDisconnect++;
if (framesSinceMasterDisconnect >= authFramerate) if (framesSinceMasterDisconnect >= authFramerate) {
LOG("No connection to master!");
break; //Exit our loop, shut down. break; //Exit our loop, shut down.
}
} else framesSinceMasterDisconnect = 0; } else framesSinceMasterDisconnect = 0;
//In world we'd update our other systems here. //In world we'd update our other systems here.
@@ -141,6 +149,7 @@ int main(int argc, char** argv) {
std::this_thread::sleep_until(t); std::this_thread::sleep_until(t);
} }
LOG("Exited Main Loop! (signal %d)", Game::lastSignal);
//Delete our objects here: //Delete our objects here:
Database::Destroy("AuthServer"); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;

View File

@@ -1,2 +1,3 @@
add_executable(AuthServer "AuthServer.cpp") add_executable(AuthServer "AuthServer.cpp")
target_link_libraries(AuthServer ${COMMON_LIBRARIES}) 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_executable(ChatServer "ChatServer.cpp")
add_library(dChatServer ${DCHATSERVER_SOURCES}) add_library(dChatServer ${DCHATSERVER_SOURCES})
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter) target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer) target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer)

View File

@@ -33,7 +33,7 @@ namespace Game {
dConfig* config = nullptr; dConfig* config = nullptr;
dChatFilter* chatFilter = nullptr; dChatFilter* chatFilter = nullptr;
AssetManager* assetManager = nullptr; AssetManager* assetManager = nullptr;
bool shouldShutdown = false; Game::signal_t lastSignal = 0;
std::mt19937 randomEngine; std::mt19937 randomEngine;
PlayerContainer playerContainer; PlayerContainer playerContainer;
} }
@@ -48,17 +48,20 @@ int main(int argc, char** argv) {
Diagnostics::SetProcessFileName(argv[0]); Diagnostics::SetProcessFileName(argv[0]);
Diagnostics::Initialize(); Diagnostics::Initialize();
std::signal(SIGINT, Game::OnSignal);
std::signal(SIGTERM, Game::OnSignal);
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Game::logger = SetupLogger(); Game::logger = SetupLogger();
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
//Read our config: //Read our config:
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string()); Game::config = new dConfig("chatconfig.ini");
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
LOG("Starting Chat server..."); LOG("Starting Chat server...");
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); LOG("Version: %s", PROJECT_VERSION);
LOG("Compiled on: %s", __TIMESTAMP__); LOG("Compiled on: %s", __TIMESTAMP__);
try { 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("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); 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")))); 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 framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 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: //Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) { if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++; framesSinceMasterDisconnect++;

View File

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

View File

@@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) {
#include <cstring> #include <cstring>
#include <exception> #include <exception>
#if defined(__include_backtrace__) #if defined(INCLUDE_BACKTRACE)
#include <backtrace.h> #include <backtrace.h>
#include <backtrace-supported.h> #include <backtrace-supported.h>
@@ -115,7 +115,14 @@ void GenerateDump() {
} }
void CatchUnhandled(int sig) { 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"; 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()); 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); backtrace_symbols_fd(array, size, STDOUT_FILENO);
# endif // defined(__GNUG__) # endif // defined(__GNUG__)
#else // __include_backtrace__ #else // INCLUDE_BACKTRACE
struct backtrace_state* state = backtrace_create_state( struct backtrace_state* state = backtrace_create_state(
Diagnostics::GetProcessFileName().c_str(), Diagnostics::GetProcessFileName().c_str(),
@@ -178,7 +185,7 @@ void CatchUnhandled(int sig) {
struct bt_ctx ctx = { state, 0 }; struct bt_ctx ctx = { state, 0 };
Bt(state); Bt(state);
#endif // __include_backtrace__ #endif // INCLUDE_BACKTRACE
exit(EXIT_FAILURE); 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 #pragma once
#include <string>
#include <random> #include <random>
#include <csignal>
class dServer; class dServer;
class Logger; class Logger;
@@ -15,6 +17,7 @@ class dZoneManager;
class PlayerContainer; class PlayerContainer;
namespace Game { namespace Game {
using signal_t = volatile std::sig_atomic_t;
extern Logger* logger; extern Logger* logger;
extern dServer* server; extern dServer* server;
extern InstanceManager* im; extern InstanceManager* im;
@@ -24,8 +27,14 @@ namespace Game {
extern RakPeerInterface* chatServer; extern RakPeerInterface* chatServer;
extern AssetManager* assetManager; extern AssetManager* assetManager;
extern SystemAddress chatSysAddr; extern SystemAddress chatSysAddr;
extern bool shouldShutdown; extern signal_t lastSignal;
extern EntityManager* entityManager; extern EntityManager* entityManager;
extern dZoneManager* zoneManager; extern dZoneManager* zoneManager;
extern PlayerContainer playerContainer; 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> #include <stdarg.h>
Writer::~Writer() { Writer::~Writer() {
// Flush before we close
Flush();
// Dont try to close stdcout... // Dont try to close stdcout...
if (!m_Outfile || m_IsConsoleWriter) return; if (!m_Outfile || m_IsConsoleWriter) return;
@@ -14,7 +16,7 @@ Writer::~Writer() {
} }
void Writer::Log(const char* time, const char* message) { void Writer::Log(const char* time, const char* message) {
if (!m_Outfile) return; if (!m_Outfile || !m_Enabled) return;
fputs(time, m_Outfile); fputs(time, m_Outfile);
fputs(message, m_Outfile); fputs(message, m_Outfile);

View File

@@ -10,8 +10,23 @@ dConfig::dConfig(const std::string& filepath) {
LoadConfig(); LoadConfig();
} }
std::filesystem::path GetConfigDir() {
std::filesystem::path config_dir = BinaryPathFinder::GetBinaryDir();
if (const char* env_p = std::getenv("DLU_CONFIG_DIR")) {
config_dir /= env_p;
}
return config_dir;
}
const bool dConfig::Exists(const std::string& filepath) {
std::filesystem::path config_dir = GetConfigDir();
return std::filesystem::exists(config_dir / filepath);
}
void dConfig::LoadConfig() { void dConfig::LoadConfig() {
std::ifstream in(BinaryPathFinder::GetBinaryDir() / m_ConfigFilePath); std::filesystem::path config_dir = GetConfigDir();
std::ifstream in(config_dir / m_ConfigFilePath);
if (!in.good()) return; if (!in.good()) return;
std::string line{}; std::string line{};
@@ -19,7 +34,7 @@ void dConfig::LoadConfig() {
if (!line.empty() && line.front() != '#') ProcessLine(line); 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; if (!sharedConfig.good()) return;
line.clear(); line.clear();
@@ -34,6 +49,11 @@ void dConfig::ReloadConfig() {
} }
const std::string& dConfig::GetValue(std::string key) { const std::string& dConfig::GetValue(std::string key) {
std::string upper_key(key);
std::transform(upper_key.begin(), upper_key.end(), upper_key.begin(), ::toupper);
if (const char* env_p = std::getenv(upper_key.c_str())) {
this->m_ConfigValues[key] = env_p;
}
return this->m_ConfigValues[key]; return this->m_ConfigValues[key];
} }

View File

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

View File

@@ -39,9 +39,11 @@
#include "CDRailActivatorComponent.h" #include "CDRailActivatorComponent.h"
#include "CDRewardCodesTable.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. // 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. // 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 #ifdef CDCLIENT_CACHE_ALL
#define CDCLIENT_DONT_CACHE_TABLE(x) x #define CDCLIENT_DONT_CACHE_TABLE(x) x

View File

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

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 SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
WHERE leaderboardsRanked.ranking WHERE leaderboardsRanked.ranking
BETWEEN 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 AND
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank) LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
ORDER BY ranking ASC; 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: //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)) { if (Database::Get()->GetCharacterInfo(objectID)) {
LOG("Character object id unavailable, check object_id_tracker!"); LOG("Character object id unavailable, check object_id_tracker!");
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE); 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; if (activityID > 0) m_ActivityID = activityID;
else m_ActivityID = parent->GetVar<int32_t>(u"activityID"); else m_ActivityID = parent->GetVar<int32_t>(u"activityID");
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>(); 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) { for (CDActivities activity : activities) {
m_ActivityInfo = activity; m_ActivityInfo = activity;
@@ -93,7 +93,7 @@ void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIniti
void ActivityComponent::ReloadConfig() { void ActivityComponent::ReloadConfig() {
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>(); 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) { for (auto activity : activities) {
auto mapID = m_ActivityInfo.instanceMapID; auto mapID = m_ActivityInfo.instanceMapID;
if (static_cast<Leaderboard::Type>(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") { 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) { 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 we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
if (HasLobby()) { if (HasLobby()) {
PlayerJoinLobby(player); PlayerJoinLobby(player);
@@ -531,7 +532,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
// First, get the activity data // First, get the activity data
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>(); 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()) { if (!activityRewards.empty()) {
uint32_t minCoins = 0; uint32_t minCoins = 0;

View File

@@ -95,10 +95,16 @@ void BuffComponent::Update(float deltaTime) {
if (buff.second.time <= 0.0f) { if (buff.second.time <= 0.0f) {
RemoveBuff(buff.first); 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) { 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); GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
m_Buffs.erase(iter); m_BuffsToRemove.push_back(id);
RemoveBuffEffect(id); RemoveBuffEffect(id);
} }

View File

@@ -140,6 +140,9 @@ private:
*/ */
std::map<int32_t, Buff> m_Buffs; 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 * 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... // 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; if (!vehicle || !this->m_Parent) return;
GameMessages::SendRacingResetPlayerToLastReset( GameMessages::SendRacingResetPlayerToLastReset(
m_Parent->GetObjectID(), racingPlayer.playerID, m_Parent->GetObjectID(), racingPlayer.playerID,

View File

@@ -249,7 +249,7 @@ bool Item::IsEquipped() const {
bool Item::Consume() { bool Item::Consume() {
auto* skillsTable = CDClientManager::Instance().GetTable<CDObjectSkillsTable>(); 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); return entry.objectTemplate == static_cast<uint32_t>(lot);
}); });

View File

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

View File

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

View File

@@ -9,10 +9,11 @@
#include "CDZoneTableTable.h" #include "CDZoneTableTable.h"
#include "MasterPackets.h" #include "MasterPackets.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "BinaryPathFinder.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eMasterMessageType.h" #include "eMasterMessageType.h"
#include "Start.h"
InstanceManager::InstanceManager(Logger* logger, const std::string& externalIP) { InstanceManager::InstanceManager(Logger* logger, const std::string& externalIP) {
mLogger = logger; mLogger = logger;
mExternalIP = externalIP; 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); instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, softCap, maxPlayers);
//Start the actual process: //Start the actual process:
#ifdef _WIN32 StartWorldServer(mapID, port, m_LastInstanceID, maxPlayers, cloneID);
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());
m_Instances.push_back(instance); m_Instances.push_back(instance);
@@ -159,7 +134,7 @@ void InstanceManager::RemoveInstance(Instance* instance) {
if (m_Instances[i] == instance) { if (m_Instances[i] == instance) {
instance->SetShutdownComplete(true); instance->SetShutdownComplete(true);
if (!Game::shouldShutdown) RedirectPendingRequests(instance); if (!Game::ShouldShutdown()) RedirectPendingRequests(instance);
delete m_Instances[i]; 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); instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
//Start the actual process: //Start the actual process:
std::string cmd = "start " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone "; StartWorldServer(mapID, port, m_LastInstanceID, maxPlayers, cloneID);
#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());
m_Instances.push_back(instance); m_Instances.push_back(instance);

View File

@@ -7,11 +7,7 @@
#include <thread> #include <thread>
#include <fstream> #include <fstream>
#ifdef _WIN32
#include <bcrypt/BCrypt.hpp> #include <bcrypt/BCrypt.hpp>
#else
#include <bcrypt.h>
#endif
#include <csignal> #include <csignal>
@@ -43,6 +39,7 @@
#include "PacketUtils.h" #include "PacketUtils.h"
#include "FdbToSqlite.h" #include "FdbToSqlite.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "Start.h"
namespace Game { namespace Game {
Logger* logger = nullptr; Logger* logger = nullptr;
@@ -50,16 +47,15 @@ namespace Game {
InstanceManager* im = nullptr; InstanceManager* im = nullptr;
dConfig* config = nullptr; dConfig* config = nullptr;
AssetManager* assetManager = nullptr; AssetManager* assetManager = nullptr;
bool shouldShutdown = false; Game::signal_t lastSignal = 0;
bool universeShutdownRequested = false;
std::mt19937 randomEngine; std::mt19937 randomEngine;
} //namespace Game } //namespace Game
bool shutdownSequenceStarted = false; bool shutdownSequenceStarted = false;
void ShutdownSequence(int32_t signal = -1); int ShutdownSequence(int32_t signal = -1);
int32_t FinalizeShutdown(int32_t signal = -1); int32_t FinalizeShutdown(int32_t signal = -1);
Logger* SetupLogger(); Logger* SetupLogger();
void StartAuthServer();
void StartChatServer();
void HandlePacket(Packet* packet); void HandlePacket(Packet* packet);
std::map<uint32_t, std::string> activeSessions; std::map<uint32_t, std::string> activeSessions;
SystemAddress authServerMasterPeerSysAddr; SystemAddress authServerMasterPeerSysAddr;
@@ -78,39 +74,39 @@ int main(int argc, char** argv) {
//Triggers the shutdown sequence at application exit //Triggers the shutdown sequence at application exit
std::atexit([]() { ShutdownSequence(); }); std::atexit([]() { ShutdownSequence(); });
signal(SIGINT, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); }); std::signal(SIGINT, Game::OnSignal);
signal(SIGTERM, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); }); std::signal(SIGTERM, Game::OnSignal);
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Game::logger = SetupLogger(); Game::logger = SetupLogger();
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "authconfig.ini")) { if (!dConfig::Exists("authconfig.ini")) {
LOG("Couldnt find authconfig.ini"); LOG("Couldnt find authconfig.ini");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "chatconfig.ini")) { if (!dConfig::Exists("chatconfig.ini")) {
LOG("Couldnt find chatconfig.ini"); LOG("Couldnt find chatconfig.ini");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "masterconfig.ini")) { if (!dConfig::Exists("masterconfig.ini")) {
LOG("Couldnt find masterconfig.ini"); LOG("Couldnt find masterconfig.ini");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini")) { if (!dConfig::Exists("sharedconfig.ini")) {
LOG("Couldnt find sharedconfig.ini"); LOG("Couldnt find sharedconfig.ini");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "worldconfig.ini")) { if (!dConfig::Exists("worldconfig.ini")) {
LOG("Couldnt find worldconfig.ini"); LOG("Couldnt find worldconfig.ini");
return EXIT_FAILURE; 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->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); 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("Using net version %s", Game::config->GetValue("client_net_version").c_str());
LOG("Starting Master server..."); LOG("Starting Master server...");
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); LOG("Version: %s", PROJECT_VERSION);
LOG("Compiled on: %s", __TIMESTAMP__); LOG("Compiled on: %s", __TIMESTAMP__);
//Connect to the MySQL Database //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("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::stoi(Game::config->GetValue("port")); 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" //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 framesSinceLastSQLPing = 0;
uint32_t framesSinceKillUniverseCommand = 0; uint32_t framesSinceKillUniverseCommand = 0;
while (true) { Game::logger->Flush();
while (!Game::ShouldShutdown()) {
//In world we'd update our other systems here. //In world we'd update our other systems here.
//Check for packets here: //Check for packets here:
@@ -360,10 +357,10 @@ int main(int argc, char** argv) {
framesSinceLastSQLPing++; framesSinceLastSQLPing++;
//10m shutdown for universe kill command //10m shutdown for universe kill command
if (Game::shouldShutdown) { if (Game::universeShutdownRequested) {
if (framesSinceKillUniverseCommand >= shutdownUniverseTime) { if (framesSinceKillUniverseCommand >= shutdownUniverseTime) {
//Break main loop and exit //Break main loop and exit
break; Game::lastSignal = -1;
} else } else
framesSinceKillUniverseCommand++; framesSinceKillUniverseCommand++;
} }
@@ -407,7 +404,7 @@ int main(int argc, char** argv) {
t += std::chrono::milliseconds(masterFrameDelta); t += std::chrono::milliseconds(masterFrameDelta);
std::this_thread::sleep_until(t); std::this_thread::sleep_until(t);
} }
return FinalizeShutdown(EXIT_SUCCESS); return ShutdownSequence(EXIT_SUCCESS);
} }
Logger* SetupLogger() { Logger* SetupLogger() {
@@ -804,7 +801,7 @@ void HandlePacket(Packet* packet) {
case eMasterMessageType::SHUTDOWN_UNIVERSE: { case eMasterMessageType::SHUTDOWN_UNIVERSE: {
LOG("Received shutdown universe command, shutting down in 10 minutes."); LOG("Received shutdown universe command, shutting down in 10 minutes.");
Game::shouldShutdown = true; Game::universeShutdownRequested = true;
break; break;
} }
@@ -814,46 +811,12 @@ void HandlePacket(Packet* packet) {
} }
} }
void StartChatServer() { int ShutdownSequence(int32_t signal) {
if (Game::shouldShutdown) { if (!Game::logger) return -1;
LOG("Currently shutting down. Chat will not be restarted."); LOG("Recieved Signal %d", signal);
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) {
if (shutdownSequenceStarted) { if (shutdownSequenceStarted) {
return; LOG("Duplicate Shutdown Sequence");
return -1;
} }
if (!Game::im) { if (!Game::im) {
@@ -862,7 +825,7 @@ void ShutdownSequence(int32_t signal) {
Game::im->SetIsShuttingDown(true); Game::im->SetIsShuttingDown(true);
shutdownSequenceStarted = true; shutdownSequenceStarted = true;
Game::shouldShutdown = true; Game::lastSignal = -1;
{ {
CBITSTREAM; CBITSTREAM;
@@ -931,16 +894,20 @@ void ShutdownSequence(int32_t signal) {
} }
} }
FinalizeShutdown(signal); return FinalizeShutdown(signal);
} }
int32_t FinalizeShutdown(int32_t signal) { int32_t FinalizeShutdown(int32_t signal) {
//Delete our objects here: //Delete our objects here:
Database::Destroy("MasterServer"); Database::Destroy("MasterServer");
if (Game::config) delete Game::config; if (Game::config) delete Game::config;
Game::config = nullptr;
if (Game::im) delete Game::im; if (Game::im) delete Game::im;
Game::im = nullptr;
if (Game::server) delete Game::server; if (Game::server) delete Game::server;
Game::server = nullptr;
if (Game::logger) delete Game::logger; if (Game::logger) delete Game::logger;
Game::logger = nullptr;
if (signal != EXIT_SUCCESS) exit(signal); if (signal != EXIT_SUCCESS) exit(signal);
return 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 "SHA512.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#ifdef _WIN32
#include <bcrypt/BCrypt.hpp> #include <bcrypt/BCrypt.hpp>
#else
#include <bcrypt.h>
#endif
#include <BitStream.h> #include <BitStream.h>
#include <future> #include <future>
@@ -59,7 +55,9 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c
RakNet::BitStream bitStream; RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::VERSION_CONFIRM); BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::VERSION_CONFIRM);
uint32_t netVersion; 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); LOG("Failed to parse client_net_version. Cannot authenticate to %s:%i", nextServerIP.c_str(), nextServerPort);
return; return;
} }

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ set(DWORLDSERVER_SOURCES
add_library(dWorldServer ${DWORLDSERVER_SOURCES}) add_library(dWorldServer ${DWORLDSERVER_SOURCES})
add_executable(WorldServer "WorldServer.cpp") add_executable(WorldServer "WorldServer.cpp")
add_compile_definitions(WorldServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
target_link_libraries(dWorldServer ${COMMON_LIBRARIES}) target_link_libraries(dWorldServer ${COMMON_LIBRARIES})
target_link_libraries(WorldServer ${COMMON_LIBRARIES} dChatFilter dGame dZoneManager dPhysics Detour Recast tinyxml2 dWorldServer dNavigation) 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; RakPeerInterface* chatServer = nullptr;
std::mt19937 randomEngine; std::mt19937 randomEngine;
SystemAddress chatSysAddr; SystemAddress chatSysAddr;
bool shouldShutdown = false; Game::signal_t lastSignal = 0;
EntityManager* entityManager = nullptr; EntityManager* entityManager = nullptr;
dZoneManager* zoneManager = nullptr; dZoneManager* zoneManager = nullptr;
std::string projectVersion = PROJECT_VERSION;
} // namespace Game } // namespace Game
bool chatDisabled = false; bool chatDisabled = false;
@@ -123,8 +124,8 @@ int main(int argc, char** argv) {
// Triggers the shutdown sequence at application exit // Triggers the shutdown sequence at application exit
std::atexit(WorldShutdownSequence); std::atexit(WorldShutdownSequence);
signal(SIGINT, [](int) { WorldShutdownSequence(); }); std::signal(SIGINT, Game::OnSignal);
signal(SIGTERM, [](int) { WorldShutdownSequence(); }); std::signal(SIGTERM, Game::OnSignal);
uint32_t zoneID = 1000; uint32_t zoneID = 1000;
uint32_t cloneID = 0; uint32_t cloneID = 0;
@@ -147,12 +148,12 @@ int main(int argc, char** argv) {
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
//Read our config: //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->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
LOG("Starting World server..."); 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__); LOG("Compiled on: %s", __TIMESTAMP__);
if (Game::config->GetValue("disable_chat") == "1") chatDisabled = true; if (Game::config->GetValue("disable_chat") == "1") chatDisabled = true;
@@ -211,7 +212,7 @@ int main(int argc, char** argv) {
UserManager::Instance()->Initialize(); UserManager::Instance()->Initialize();
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", 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: //Connect to the chat server:
uint32_t chatPort = 1501; 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 saveTime = 10 * 60 * currentFramerate; // 10 minutes in frames
uint32_t sqlPingTime = 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. 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) { while (true) {
Metrics::StartMeasurement(MetricVariable::Frame); Metrics::StartMeasurement(MetricVariable::Frame);
Metrics::StartMeasurement(MetricVariable::GameLoop); Metrics::StartMeasurement(MetricVariable::GameLoop);
@@ -362,9 +365,9 @@ int main(int argc, char** argv) {
if (!Game::server->GetIsConnectedToMaster()) { if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++; 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); LOG("Game loop running but no connection to master for %d frames, shutting down", noMasterConnectionTimeout);
Game::shouldShutdown = true; Game::lastSignal = -1;
} }
} else framesSinceMasterDisconnect = 0; } 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 we haven't had any players for a while, time out and shut down:
if (framesSinceLastUser >= emptyShutdownTime) { if (framesSinceLastUser >= emptyShutdownTime) {
Game::shouldShutdown = true; Game::lastSignal = -1;
} }
} else { } else {
framesSinceLastUser = 0; framesSinceLastUser = 0;
@@ -512,7 +515,7 @@ int main(int argc, char** argv) {
} }
} }
if (Game::shouldShutdown && !worldShutdownSequenceComplete) { if (Game::ShouldShutdown() && !worldShutdownSequenceComplete) {
WorldShutdownProcess(zoneID); WorldShutdownProcess(zoneID);
break; break;
} }
@@ -821,7 +824,7 @@ void HandlePacket(Packet* packet) {
} }
case eMasterMessageType::SHUTDOWN: { 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()); LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
break; break;
} }
@@ -1288,14 +1291,17 @@ void WorldShutdownProcess(uint32_t zoneId) {
} }
void WorldShutdownSequence() { void WorldShutdownSequence() {
Game::shouldShutdown = true; bool shouldShutdown = Game::ShouldShutdown() || worldShutdownSequenceComplete;
Game::lastSignal = -1;
#ifndef DARKFLAME_PLATFORM_WIN32 #ifndef DARKFLAME_PLATFORM_WIN32
if (Game::shouldShutdown || worldShutdownSequenceComplete) if (shouldShutdown)
#endif #endif
{ {
return; return;
} }
if (!Game::logger) return;
LOG("Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), instanceID); LOG("Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), instanceID);
WorldShutdownProcess(Game::server->GetZoneID()); WorldShutdownProcess(Game::server->GetZoneID());
FinalizeShutdown(); FinalizeShutdown();

View File

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

View File

@@ -33,7 +33,7 @@ ARG BUILD_THREADS=1
ARG BUILD_VERSION=171022 ARG BUILD_VERSION=171022
RUN echo "Build server" 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 mkdir -p cmake_build
RUN cd cmake_build && \ RUN cd cmake_build && \
cmake .. -DCMAKE_BUILD_RPATH_USE_ORIGIN=TRUE && \ cmake .. -DCMAKE_BUILD_RPATH_USE_ORIGIN=TRUE && \

View File

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

View File

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

View File

@@ -11,11 +11,17 @@ file(
GLOB SOURCES_LIBBCRYPT GLOB SOURCES_LIBBCRYPT
LIST_DIRECTORIES false LIST_DIRECTORIES false
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/*.c
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/src/*.c ${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/src/*.c
) )
add_library(bcrypt ${SOURCES_LIBBCRYPT}) 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 # Source code for sqlite
add_subdirectory(SQLite) add_subdirectory(SQLite)
@@ -31,7 +37,7 @@ add_subdirectory(raknet)
# Download Backtrace if configured # Download Backtrace if configured
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
include(FetchContent) include(FetchContent)
if (__include_backtrace__ AND __compile_backtrace__) if (${INCLUDE_BACKTRACE} AND ${COMPILE_BACKTRACE})
FetchContent_Declare( FetchContent_Declare(
backtrace backtrace
GIT_REPOSITORY https://github.com/ianlancetaylor/libbacktrace.git GIT_REPOSITORY https://github.com/ianlancetaylor/libbacktrace.git

View File

@@ -93,7 +93,7 @@ else() # Build from source
-DINSTALL_PLUGINDIR=plugin -DINSTALL_PLUGINDIR=plugin
${MARIADB_EXTRA_CMAKE_ARGS} ${MARIADB_EXTRA_CMAKE_ARGS}
PREFIX "${PROJECT_BINARY_DIR}/mariadbcpp" 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 "") INSTALL_COMMAND "")
ExternalProject_Get_Property(mariadb_connector_cpp BINARY_DIR) ExternalProject_Get_Property(mariadb_connector_cpp BINARY_DIR)

View File

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