diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d7ffbeb3a89..f3611e58b51 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -16,8 +16,6 @@ set(COMMON_SOURCES $<$:${CMAKE_CURRENT_SOURCE_DIR}/debug_windows.cpp> ${CMAKE_CURRENT_SOURCE_DIR}/filewatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/filewatcher.h - ${CMAKE_CURRENT_SOURCE_DIR}/kernel.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/kernel.h ${CMAKE_CURRENT_SOURCE_DIR}/logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/logging.h ${CMAKE_CURRENT_SOURCE_DIR}/lua.cpp @@ -27,8 +25,6 @@ set(COMMON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/mmo.h ${CMAKE_CURRENT_SOURCE_DIR}/settings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/settings.h - ${CMAKE_CURRENT_SOURCE_DIR}/socket.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/socket.h ${CMAKE_CURRENT_SOURCE_DIR}/sql.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sql.h ${CMAKE_CURRENT_SOURCE_DIR}/string.h diff --git a/src/common/application.cpp b/src/common/application.cpp index 80f27ea2141..699f0e8d61f 100644 --- a/src/common/application.cpp +++ b/src/common/application.cpp @@ -20,43 +20,50 @@ */ #include "application.h" + #include "debug.h" #include "logging.h" #include "lua.h" #include "settings.h" #include "taskmgr.h" +#include "xirand.h" #ifdef _WIN32 #include #endif Application::Application(std::string const& serverName, int argc, char** argv) -: m_ServerName(serverName) -, m_RequestExit(false) -, gArgParser(std::make_unique(argv[0])) +: serverName_(serverName) +, requestExit_(false) +, ioContext_() +, argParser_(std::make_unique(argv[0])) +, lua_(lua_init()) { #ifdef _WIN32 - SetConsoleTitleA(fmt::format("{}-server", serverName).c_str()); + SetConsoleTitleA(fmt::format("{}-server", serverName_).c_str()); #endif - gArgParser->add_argument("--log") - .default_value(fmt::format("log/{}-server.log", serverName)); + argParser_->add_argument("--log") + .default_value(fmt::format("log/{}-server.log", serverName_)); try { - gArgParser->parse_args(argc, argv); + argParser_->parse_args(argc, argv); } catch (const std::runtime_error& err) { std::cerr << err.what() << "\n"; - std::cerr << *gArgParser << "\n"; + std::cerr << *argParser_ << "\n"; std::exit(1); } - auto logName = gArgParser->get("--log"); - logging::InitializeLog(serverName, logName, false); - lua_init(); - settings::init(); + auto logName = argParser_->get("--log"); + logging::InitializeLog(serverName_, logName, false); + + settings::init(lua_); + + xirand::seed(); + ShowInfo("Begin %s-server Init...", serverName); #ifdef ENV64BIT @@ -69,31 +76,46 @@ Application::Application(std::string const& serverName, int argc, char** argv) ShowInfo("The %s-server is ready to work...", serverName); ShowInfo("======================================================================="); +} - // clang-format off - gConsoleService = std::make_unique(); - - gConsoleService->RegisterCommand("exit", "Terminate the program.", - [&](std::vector& inputs) +void Application::run() +{ + ShowInfo("starting io_context"); + + // This busy loop looks nasty, however -- + // https://think-async.com/Asio/asio-1.24.0/doc/asio/reference/io_service.html + // + // If an exception is thrown from a handler, the exception is allowed to propagate through the throwing thread's invocation of + // run(), run_one(), run_for(), run_until(), poll() or poll_one(). No other threads that are calling any of these functions are affected. + // It is then the responsibility of the application to catch the exception. + while (Application::isRunning()) { - fmt::print("> Goodbye!\n"); - m_RequestExit = true; - }); - // clang-format on + try + { + // NOTE: io_context.run() takes over and blocks this thread. Anything after this point will only fire + // if io_context finishes! + ioContext_.run(); + break; + } + catch (std::exception& e) + { + // TODO: make a list of "allowed exceptions", the rest can/should cause shutdown. + ShowError(fmt::format("Inner fatal: {}", e.what())); + } + } } -bool Application::IsRunning() +bool Application::isRunning() { - return !m_RequestExit; + return !requestExit_; } -void Application::Tick() +auto Application::lua() -> sol::state_view { - // Main runtime cycle - duration next; - while (!m_RequestExit) - { - next = CTaskMgr::getInstance()->DoTimer(server_clock::now()); - std::this_thread::sleep_for(next); - } + return lua_; +} + +auto Application::ioContext() -> asio::io_context& +{ + return ioContext_; } diff --git a/src/common/application.h b/src/common/application.h index fbcb07ec10c..2e17433d1e7 100644 --- a/src/common/application.h +++ b/src/common/application.h @@ -21,12 +21,15 @@ #pragma once -#include -#include +#include "console_service.h" +#include "lua.h" #include -#include "console_service.h" +#include + +#include +#include class Application { @@ -39,13 +42,20 @@ class Application Application& operator=(const Application&) = delete; Application& operator=(Application&&) = delete; - virtual bool IsRunning(); - virtual void Tick(); + void run(); + bool isRunning(); + + auto lua() -> sol::state_view; + auto ioContext() -> asio::io_context&; protected: - std::string m_ServerName; - std::atomic m_RequestExit; + std::string serverName_; + std::atomic requestExit_; + + sol::state lua_; + + asio::io_context ioContext_; - std::unique_ptr gArgParser; - std::unique_ptr gConsoleService; + std::unique_ptr argParser_; + std::unique_ptr consoleService_; }; diff --git a/src/common/cbasetypes.h b/src/common/cbasetypes.h index f92451ff7af..27cbfa9ee67 100644 --- a/src/common/cbasetypes.h +++ b/src/common/cbasetypes.h @@ -49,6 +49,12 @@ inline void destroy_arr(T*& ptr) ptr = nullptr; } +template +T& ref(U* buf, std::size_t index) +{ + return *reinterpret_cast(reinterpret_cast(buf) + index); +} + #include using namespace std::literals::chrono_literals; diff --git a/src/common/console_service.cpp b/src/common/console_service.cpp index 860f4478b69..f37a5a9c68e 100644 --- a/src/common/console_service.cpp +++ b/src/common/console_service.cpp @@ -22,7 +22,6 @@ #include "console_service.h" #include "database.h" -#include "lua.h" #include @@ -131,18 +130,17 @@ ConsoleService::ConsoleService() } }); - RegisterCommand("lua", "Provides a Lua REPL", - [](std::vector& inputs) - { - if (inputs.size() >= 2) - { - // Remove "lua" from the front of the inputs - inputs = std::vector(inputs.begin() + 1, inputs.end()); - - auto input = fmt::format("local var = {}; if type(var) ~= \"nil\" then print(var) end", fmt::join(inputs, " ")); - lua.safe_script(input); - } - }); + // RegisterCommand("lua", "Provides a Lua REPL", + // [](std::vector& inputs) + // { + // if (inputs.size() >= 2) + // { + // // Remove "lua" from the front of the inputs + // inputs = std::vector(inputs.begin() + 1, inputs.end()); + // auto input = fmt::format("local var = {}; if type(var) ~= \"nil\" then print(var) end", fmt::join(inputs, " ")); + // lua.safe_script(input); + // } + // }); RegisterCommand("crash", "Crash the process", [](std::vector& inputs) diff --git a/src/common/kernel.cpp b/src/common/kernel.cpp deleted file mode 100644 index aa82bef37af..00000000000 --- a/src/common/kernel.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/* -=========================================================================== - - Copyright (c) 2010-2015 Darkstar Dev Teams - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see http://www.gnu.org/licenses/ - -=========================================================================== -*/ - -#include "common/kernel.h" - -#include "common/debug.h" -#include "common/logging.h" -#include "common/lua.h" -#include "common/settings.h" -#include "common/socket.h" -#include "common/taskmgr.h" -#include "common/timer.h" -#include "common/version.h" -#include "common/watchdog.h" - -#include -#if defined(__linux__) || defined(__APPLE__) -#define BACKWARD_HAS_BFD 1 -#include "ext/backward/backward.hpp" -#endif - -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#endif - -#ifdef __linux__ -#include -#include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) -#include -#define HAS_YAMA_PRCTL -#endif -#endif - -#ifdef TRACY_ENABLE -void* operator new(std::size_t count) -{ - void* ptr = malloc(count); - TracyAlloc(ptr, count); - return ptr; -} - -void operator delete(void* ptr) noexcept -{ - TracyFree(ptr); - free(ptr); -} - -void operator delete(void* ptr, std::size_t count) noexcept -{ - TracyFree(ptr); - free(ptr); -} - -void operator delete[](void* ptr) noexcept -{ - TracyFree(ptr); - free(ptr); -} - -void operator delete[](void* ptr, std::size_t count) noexcept -{ - TracyFree(ptr); - free(ptr); -} -#endif // TRACY_ENABLE - -std::atomic gRunFlag = true; - -std::array, MAX_FD> sessions; - -// This must be manually created -std::unique_ptr gConsoleService; - -// Copyright (c) Athena Dev Teams -// Added by Gabuzomeu -// -// This is an implementation of signal() using sigaction() for portability. -// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced -// Programming in the UNIX Environment_. - -#ifdef WIN32 // windows don't have SIGPIPE -#define SIGPIPE SIGINT -#endif - -#ifndef POSIX -#define compat_signal(signo, func) signal(signo, func) -#else -sigfunc* compat_signal(int signo, sigfunc* func) -{ - sigaction sact; - sigaction oact; - - sact.sa_handler = func; - sigemptyset(&sact.sa_mask); - sact.sa_flags = 0; -#ifdef SA_INTERRUPT - sact.sa_flags |= SA_INTERRUPT; /* SunOS */ -#endif - - if (sigaction(signo, &sact, &oact) < 0) - return (SIG_ERR); - - return (oact.sa_handler); -} -#endif - -/************************************************************************ - * * - * CORE : Magical backtrace dump procedure (Linux + gdb) * - * * - ************************************************************************/ - -static void dump_backtrace() // handled in debug_osx.cpp and debug_linux.cpp -{ -} - -/************************************************************************ - * * - * CORE : Signal Sub Function * - * * - ************************************************************************/ - -static void sig_proc(int sn) -{ - switch (sn) - { - case SIGINT: - case SIGTERM: - gRunFlag = false; - gConsoleService->stop(); - break; - case SIGABRT: - case SIGSEGV: - case SIGFPE: - gConsoleService->stop(); - dump_backtrace(); - do_abort(); -#ifdef _WIN32 -#ifdef _DEBUG - // Pass the signal to the system's default handler - compat_signal(sn, SIG_DFL); - raise(sn); -#endif // _DEBUG -#endif // _WIN32 - - break; -#ifndef _WIN32 - case SIGXFSZ: - // ignore and allow it to set errno to EFBIG - ShowWarning("Max file size reached!"); - // run_flag = 0; // should we quit? - break; - case SIGPIPE: - // ShowInfo ("Broken pipe found... closing socket"); // set to eof in socket.c - break; // does nothing here -#endif - } -} - -void signals_init() -{ - compat_signal(SIGTERM, sig_proc); - compat_signal(SIGINT, sig_proc); -#if !defined(_DEBUG) && defined(_WIN32) // need unhandled exceptions to debug on Windows - compat_signal(SIGABRT, sig_proc); - compat_signal(SIGSEGV, sig_proc); - compat_signal(SIGFPE, sig_proc); -#endif -} - -/************************************************************************ - * * - * Warning if logged in as superuser (root) * - * * - ************************************************************************/ - -void usercheck() -{ - // We _need_ root/admin for Tracy to be able to collect the full suite - // of information, so we disable this warning if Tracy is enabled. -#ifndef TRACY_ENABLE - if (debug::isUserRoot()) - { - ShowWarning("You are running as the root superuser or admin."); - ShowWarning("It is unnecessary and unsafe to run with root privileges."); - std::this_thread::sleep_for(std::chrono::seconds(5)); - } -#endif // TRACY_ENABLE -} - -/************************************************************************ - * * - * CORE : MAINROUTINE * - * * - ************************************************************************/ -#ifndef DEFINE_OWN_MAIN -int main(int argc, char** argv) -{ - debug::init(); - -#ifdef _WIN32 - // Disable Quick Edit Mode (Mark) in Windows Console to prevent users from accidentially - // causing the server to freeze. - HANDLE hInput; - DWORD prev_mode; - hInput = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hInput, &prev_mode); - SetConsoleMode(hInput, ENABLE_EXTENDED_FLAGS | (prev_mode & ~ENABLE_QUICK_EDIT_MODE)); -#endif // _WIN32 - - log_init(argc, argv); - set_socket_type(); - signals_init(); - timer_init(); - - lua_init(); - settings::init(); - ShowInfo(fmt::format("Last Branch: {}", version::GetGitBranch())); - ShowInfo(fmt::format("SHA: {} ({})", version::GetGitSha(), version::GetGitDate())); - - usercheck(); - - socket_init(); - - do_init(argc, argv); - - fd_set rfd = {}; - { // Main runtime cycle - duration next = std::chrono::milliseconds(200); - - // clang-format off - auto period = settings::get("main.INACTIVITY_WATCHDOG_PERIOD"); - auto periodMs = (period > 0) ? std::chrono::milliseconds(period) : 2000ms; - auto watchdog = Watchdog(periodMs, [&]() - { - ShowCritical(fmt::format("Process main tick has taken {}ms or more.", period).c_str()); - if (debug::isRunningUnderDebugger()) - { - ShowCritical("Detaching watchdog thread, it will not fire again until restart."); - } - else if (!settings::get("main.DISABLE_INACTIVITY_WATCHDOG")) - { -#ifndef SIGKILL -#define SIGKILL 9 -#endif // SIGKILL - ShowCritical("Watchdog thread time exceeded. Killing process."); - std::raise(SIGKILL); - } - }); - // clang-format on - - while (gRunFlag) - { - next = CTaskMgr::getInstance()->DoTimer(server_clock::now()); - do_sockets(&rfd, next); - watchdog.update(); - } - } - -#ifdef _WIN32 - // Re-enable Quick Edit Mode upon Exiting if it is still disabled - SetConsoleMode(hInput, prev_mode); -#endif // _WIN32 - gConsoleService->stop(); - - do_final(EXIT_SUCCESS); -} -#endif // DEFINE_OWN_MAIN diff --git a/src/common/lua.cpp b/src/common/lua.cpp index 645a22ac3c4..273e1fb9ed2 100644 --- a/src/common/lua.cpp +++ b/src/common/lua.cpp @@ -27,15 +27,15 @@ #include #include -sol::state lua; - /** * @brief Load the bare minimum required to use Lua. */ -void lua_init() +auto lua_init() -> sol::state { TracyZoneScoped; + sol::state lua; + lua.open_libraries(); // Globally require bit library @@ -66,12 +66,14 @@ void lua_init() result.get()["start"]; ShowInfo("Started script debugger"); } + + return lua; } /** * @brief */ -std::string lua_to_string_depth(const sol::object& obj, std::size_t depth) +std::string lua_to_string_depth(sol::state_view lua, const sol::object& obj, std::size_t depth) { switch (obj.get_type()) { @@ -139,11 +141,11 @@ std::string lua_to_string_depth(const sol::object& obj, std::size_t depth) { if (keyObj.get_type() == sol::type::string) { - stringVec.emplace_back(fmt::format("{}{}: {}", indent, lua_to_string_depth(keyObj, 0), lua_to_string_depth(valObj, depth + 1))); + stringVec.emplace_back(fmt::format("{}{}: {}", indent, lua_to_string_depth(lua, keyObj, 0), lua_to_string_depth(lua, valObj, depth + 1))); } else { - stringVec.emplace_back(fmt::format("{}{}", indent, lua_to_string_depth(valObj, depth + 1))); + stringVec.emplace_back(fmt::format("{}{}", indent, lua_to_string_depth(lua, valObj, depth + 1))); } } @@ -169,7 +171,7 @@ std::string lua_to_string_depth(const sol::object& obj, std::size_t depth) /** * @brief */ -std::string lua_to_string(sol::variadic_args va) +std::string lua_to_string(sol::state_view lua, sol::variadic_args va) { TracyZoneScoped; @@ -186,7 +188,7 @@ std::string lua_to_string(sol::variadic_args va) } else { - vec.emplace_back(lua_to_string_depth(va[i], 0)); + vec.emplace_back(lua_to_string_depth(lua, va[i], 0)); } } @@ -196,14 +198,14 @@ std::string lua_to_string(sol::variadic_args va) /** * @brief */ -void lua_print(sol::variadic_args va) +void lua_print(sol::state_view lua, sol::variadic_args va) { TracyZoneScoped; - ShowLua(lua_to_string(va).c_str()); + ShowLua(lua_to_string(lua, va).c_str()); } -std::string lua_fmt(const std::string& fmtStr, sol::variadic_args va) +std::string lua_fmt(sol::state_view lua, const std::string& fmtStr, sol::variadic_args va) { fmt::dynamic_format_arg_store store; for (auto const& arg : va) @@ -234,7 +236,7 @@ std::string lua_fmt(const std::string& fmtStr, sol::variadic_args va) } default: { - store.push_back(lua_to_string_depth(arg, 0)); + store.push_back(lua_to_string_depth(lua, arg, 0)); break; } } diff --git a/src/common/lua.h b/src/common/lua.h index 7467b1aa345..d3cab2c60aa 100644 --- a/src/common/lua.h +++ b/src/common/lua.h @@ -25,12 +25,10 @@ #include "lua.hpp" #include -extern sol::state lua; - -void lua_init(); -auto lua_to_string_depth(const sol::object& obj, std::size_t depth) -> std::string; -auto lua_to_string(sol::variadic_args va) -> std::string; -void lua_print(sol::variadic_args va); -auto lua_fmt(const std::string& fmtStr, sol::variadic_args va) -> std::string; +auto lua_init() -> sol::state; +auto lua_to_string_depth(sol::state_view lua, const sol::object& obj, std::size_t depth) -> std::string; +auto lua_to_string(sol::state_view lua, sol::variadic_args va) -> std::string; +void lua_print(sol::state_view lua, sol::variadic_args va); +auto lua_fmt(sol::state_view lua, const std::string& fmtStr, sol::variadic_args va) -> std::string; #endif // _LUA_H diff --git a/src/common/scheduler.h b/src/common/scheduler.h new file mode 100644 index 00000000000..d9babf87271 --- /dev/null +++ b/src/common/scheduler.h @@ -0,0 +1,192 @@ +/* +=========================================================================== + + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using duration = std::chrono::steady_clock::duration; +using time_point = std::chrono::steady_clock::time_point; + +namespace +{ + constexpr std::chrono::milliseconds kMinWaitMs{ 50 }; + constexpr std::chrono::milliseconds kMaxWaitMs{ 1000 }; +} // namespace + +using TaskFunc = std::function; + +enum class TaskKind +{ + TASK_ONCE, + TASK_INTERVAL +}; + +struct Task +{ + uint64_t id; + time_point tick; // Next scheduled execution time. + duration interval; // Recurrence interval. + TaskKind kind; // One-time or recurring. + TaskFunc handler; // Task callback. +}; + +struct TaskComparator +{ + bool operator()(const std::unique_ptr& a, const std::unique_ptr& b) const + { + return a->tick > b->tick; + } +}; + +class Scheduler +{ +public: + Scheduler(asio::io_context& ioContext) + : m_ioContext(ioContext) + , nextTaskId(0) + { + } + + ~Scheduler() + { + tasks.clear(); + } + + uint64_t addTask(duration delay, bool recurring, std::function handler) + { + uint64_t id = nextTaskId++; + + auto now = std::chrono::steady_clock::now(); + + auto t = std::make_unique(); + t->id = id; + t->tick = now + delay; + t->interval = delay; + t->kind = recurring ? TaskKind::TASK_INTERVAL : TaskKind::TASK_ONCE; + t->handler = handler; + + tasks.push_back(std::move(t)); + + // Rebuild the heap. + std::push_heap(tasks.begin(), tasks.end(), TaskComparator()); + + return id; + } + + // Actively remove tasks with the given ID. + void removeTask(uint64_t taskId) + { + const auto pred = [taskId](const auto& t) + { + return t->id == taskId; + }; + + auto newEnd = std::remove_if(tasks.begin(), tasks.end(), pred); + if (newEnd != tasks.end()) + { + tasks.erase(newEnd, tasks.end()); + std::make_heap(tasks.begin(), tasks.end(), TaskComparator()); + } + } + + // Process all tasks that are due at or before 'now'. + // For recurring tasks, reschedule them using: + // new_tick = (if >1s late: now, else previous tick) + interval. + // Returns the time until the next scheduled task, clamped between kMinWaitMs and kMaxWaitMs. + duration run(time_point now) + { + duration diff = std::chrono::seconds(1); + while (!tasks_.empty()) + { + // tasks.front() is the task with the earliest tick. + diff = tasks.front()->tick - now; + if (diff > duration::zero()) + { + break; // No tasks are overdue. + } + + // Pop the top task. + std::pop_heap(tasks.begin(), tasks.end(), TaskComparator()); + auto task = std::move(tasks.back()); + tasks.pop_back(); + + // If the task is very late (>1s late), call its handler with 'now'. + if (diff < -std::chrono::seconds(1)) + { + task->handler(now); + } + else + { + task->handler(task->tick); + } + + if (task->kind == TaskKind::TASK_INTERVAL) + { + // Reschedule recurring task. + if (now - task->tick > std::chrono::seconds(1)) + { + task->tick = now + task->interval; + } + else + { + t->tick += t->interval; + } + + tasks.push_back(std::move(task)); + + // Rebuild the heap. + std::push_heap(tasks.begin(), tasks.end(), TaskComparator()); + } + + // One-time tasks are destroyed when they fall out of scope. + } + + // We clamp here because: + // A minimum duration prevents the network run duration from being too short (which might starve network processing). + // A maximum duration prevents a long wait (if no tasks are due) that might delay periodic network processing. + diff = std::clamp(diff, kMinWaitMs, kMaxWaitMs); + + return diff; + } + +private: + using TaskHeap = std::vector>; // Maintained as a heap. + + // NOTE: We don't use ioContext_ yet, we're mimicking the synchronous TaskMgr. + asio::io_context& ioContext_; + + uint64_t nextTaskId_; + TaskHeap tasks_; +}; diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 2ed105b085e..3de89f05b0e 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -33,7 +33,15 @@ namespace settings { - std::unordered_map settingsMap; + namespace detail + { + std::unordered_map settingsMap; + + auto getSettingsMap() -> std::unordered_map& + { + return settingsMap; + } + } // We need this to figure out which environment variables are numbers // so we can pass them to the lua settings properly typed. @@ -55,7 +63,7 @@ namespace settings * Load the settings Lua files found in /settings/. Default settings are loaded first from /settings/default/, * and are then replaced by the settings found in /settings/, if any. */ - void init() + void init(sol::state_view lua) { // Load defaults for (auto const& entry : sorted_directory_iterator("./settings/default/")) @@ -107,15 +115,15 @@ namespace settings if (innerValObj.is()) { - settingsMap[key] = innerValObj.as(); + detail::settingsMap[key] = innerValObj.as(); } else if (innerValObj.is()) { - settingsMap[key] = innerValObj.as(); + detail::settingsMap[key] = innerValObj.as(); } else if (innerValObj.is()) { - settingsMap[key] = innerValObj.as(); + detail::settingsMap[key] = innerValObj.as(); } } } @@ -172,15 +180,15 @@ namespace settings if (innerValObj.is()) { - settingsMap[key] = innerValObj.as(); + detail::settingsMap[key] = innerValObj.as(); } else if (innerValObj.is()) { - settingsMap[key] = innerValObj.as(); + detail::settingsMap[key] = innerValObj.as(); } else if (innerValObj.is()) { - settingsMap[key] = innerValObj.as(); + detail::settingsMap[key] = innerValObj.as(); } // Apply any environment variables over the default/user settings. @@ -199,18 +207,18 @@ namespace settings // Therefor we need to check if the value is a number. if (isNumber(value)) { - settingsMap[key] = std::stod(value); + detail::settingsMap[key] = std::stod(value); } else { - settingsMap[key] = value; + detail::settingsMap[key] = value; } } } } // Push the consolidated defaults + user settings back up into xi.settings - for (const auto& [key, value] : settingsMap) + for (const auto& [key, value] : detail::settingsMap) { auto parts = split(key, "."); auto outer = to_lower(parts[0]); @@ -228,7 +236,7 @@ namespace settings void visit(const std::function& visitor) { - for (auto& [key, value] : settingsMap) + for (auto& [key, value] : detail::settingsMap) { visitor(key, value); } diff --git a/src/common/settings.h b/src/common/settings.h index 89197e2ca7b..ca99009cba3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -23,6 +23,7 @@ #define _SETTINGS_H #include "logging.h" +#include "lua.h" #include "utils.h" #include @@ -32,22 +33,26 @@ namespace settings { - // https://en.cppreference.com/w/cpp/utility/variant/visit - // helper type for the visitor - template - struct overloaded : Ts... + using SettingsVariant_t = std::variant; + + namespace detail { - // cppcheck-suppress syntaxError - using Ts::operator()...; - }; - // explicit deduction guide (not needed as of C++20) - template - overloaded(Ts...) -> overloaded; + // https://en.cppreference.com/w/cpp/utility/variant/visit + // helper type for the visitor + template + struct overloaded : Ts... + { + // cppcheck-suppress syntaxError + using Ts::operator()...; + }; + // explicit deduction guide (not needed as of C++20) + template + overloaded(Ts...) -> overloaded; - using SettingsVariant_t = std::variant; - extern std::unordered_map settingsMap; + auto getSettingsMap() -> std::unordered_map&; + } // detail - void init(); + void init(sol::state_view lua); /** * @brief @@ -65,6 +70,8 @@ namespace settings template T get(std::string name) { + auto& settingsMap = detail::getSettingsMap(); + // out = type being requested T out{}; @@ -76,7 +83,7 @@ namespace settings // arg = type held inside the variant std::visit( - overloaded{ + detail::overloaded{ [&](bool const& arg) { if constexpr (std::is_same_v) @@ -165,6 +172,8 @@ namespace settings // TODO: Publish back up into Lua void set(const auto& name, const auto& value) { + auto& settingsMap = detail::getSettingsMap(); + const auto key = to_upper(name); settingsMap[key] = SettingsVariant_t(value); } diff --git a/src/common/socket.cpp b/src/common/socket.cpp deleted file mode 100644 index 71474f317f8..00000000000 --- a/src/common/socket.cpp +++ /dev/null @@ -1,1318 +0,0 @@ -/* -=========================================================================== - - Copyright (c) 2010-2015 Darkstar Dev Teams - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see http://www.gnu.org/licenses/ - -=========================================================================== -*/ - -#include "common/cbasetypes.h" -#include "common/kernel.h" -#include "common/logging.h" -#include "common/mmo.h" -#include "common/taskmgr.h" -#include "common/timer.h" -#include "common/utils.h" - -#include "settings.h" -#include "socket.h" - -#include - -#include -#include -#include -#include - -#ifdef WIN32 -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef SIOCGIFCONF -#include // SIOCGIFCONF on Solaris, maybe others? [Shinomori] -#endif - -#ifdef HAVE_SETRLIMIT -#include -#endif -#endif - -///////////////////////////////////////////////////////////////////// -#if defined(WIN32) -///////////////////////////////////////////////////////////////////// -// windows portability layer - -typedef int socklen_t; - -#define sErrno WSAGetLastError() -#define S_ENOTSOCK WSAENOTSOCK -#define S_EWOULDBLOCK WSAEWOULDBLOCK -#define S_EINTR WSAEINTR -#define S_ECONNABORTED WSAECONNABORTED - -SOCKET sock_arr[MAX_FD]; -int sock_arr_len = 0; - -/// Returns the first fd associated with the socket. -/// Returns -1 if the socket is not found. -/// -/// @param s Socket -/// @return Fd or -1 -int sock2fd(SOCKET s) -{ - TracyZoneScoped; - int fd; - - // search for the socket - for (fd = 1; fd < sock_arr_len; ++fd) - if (sock_arr[fd] == s) - break; // found the socket - if (fd == sock_arr_len) - return -1; // not found - return fd; -} - -/// Inserts the socket into the global array of sockets. -/// Returns a new fd associated with the socket. -/// If there are too many sockets it closes the socket, sets an error and -// returns -1 instead. -/// Since fd 0 is reserved, it returns values in the range [1,MAX_FD[. -/// -/// @param s Socket -/// @return New fd or -1 -int sock2newfd(SOCKET s) -{ - TracyZoneScoped; - int fd; - - // find an empty position - for (fd = 1; fd < sock_arr_len; ++fd) - if (sock_arr[fd] == INVALID_SOCKET) - break; // empty position - if (fd == (sizeof(sock_arr) / sizeof(sock_arr[0]))) - { - // too many sockets - closesocket(s); - WSASetLastError(WSAEMFILE); - return -1; - } - sock_arr[fd] = s; - if (sock_arr_len <= fd) - sock_arr_len = fd + 1; - return fd; -} - -int sAccept(int fd, struct sockaddr* addr, int* addrlen) -{ - TracyZoneScoped; - SOCKET s; - - // accept connection - s = accept(fd2sock(fd), addr, addrlen); - if (s == INVALID_SOCKET) - return -1; // error - return sock2newfd(s); -} - -int sClose(int fd) -{ - TracyZoneScoped; - int ret = closesocket(fd2sock(fd)); - fd2sock(fd) = INVALID_SOCKET; - return ret; -} - -int sSocket(int af, int type, int protocol) -{ - TracyZoneScoped; - SOCKET s; - - // create socket - s = socket(af, type, protocol); - if (s == INVALID_SOCKET) - return -1; // error - return sock2newfd(s); -} -/////////////////////////////////////////////////////////////////////// -#else // *nix sys -/////////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////////////////////////////// -#endif -///////////////////////////////////////////////////////////////////// - -/* - * - * COMMON LEVEL - * - */ - -socket_type SOCKET_TYPE; - -fd_set readfds; -int32 fd_max; -time_t last_tick; -time_t tick_time; -time_t stall_time = 60; - -int32 makeConnection(uint32 ip, uint16 port, int32 type) -{ - TracyZoneScoped; - - sockaddr_in remote_address{}; - - int32 fd = 0; - int32 result = 0; - - fd = sSocket(AF_INET, type, 0); - - if (fd == -1) - { - ShowError("make_connection: socket creation failed (port %d, code %d)!", port, sErrno); - return -1; - } - if (fd == 0) - { // reserved - ShowError("make_connection: Socket #0 is reserved - Please report this!!!"); - sClose(fd); - return -1; - } - if (fd >= MAX_FD) - { // socket number too big - ShowError("make_connection: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", - fd, MAX_FD); - sClose(fd); - return -1; - } - - linger opt{}; - opt.l_onoff = 0; // SO_DONTLINGER - opt.l_linger = 0; // Do not care - if (sSetsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&opt, sizeof(opt))) - { - ShowWarning("setsocketopts: Unable to set SO_LINGER mode for connection #%d!", fd); - } - - remote_address.sin_family = AF_INET; - remote_address.sin_addr.s_addr = htonl(ip); - remote_address.sin_port = htons(port); - - ShowInfo(fmt::format("Connecting to {}:{}", ip2str(ip), port)); - - result = sConnect(fd, (struct sockaddr*)(&remote_address), sizeof(struct sockaddr_in)); - if (result == SOCKET_ERROR) - { - ShowError("make_connection: connect failed (socket #%d, port %d, code %d)!", fd, port, sErrno); - do_close(fd); - return -1; - } - // Now the socket can be made non-blocking. [Skotlex] - // set_nonblocking(fd, 1); - u_long yes = 1; - if (sIoctl(fd, FIONBIO, &yes) != 0) - { - ShowError("set_nonblocking: Failed to set socket #%d to non-blocking mode (code %d) - Please report this!!!", fd, sErrno); - } - - if (fd_max <= fd) - { - fd_max = fd + 1; - } - sFD_SET(fd, &readfds); - - return fd; -} - -void do_close(int32 fd) -{ - TracyZoneScoped; -#ifdef __APPLE__ - sFD_CLR(fd, &readfds); // this needs to be done before closing the socket -#endif - sShutdown(fd, SHUT_RDWR); // Disallow further reads/writes - sClose(fd); // We don't really care if these closing functions return an error, we are just shutting down and not reusing this socket. -} - -bool _vsocket_init() -{ - TracyZoneScoped; -#ifdef WIN32 - { // Start up windows networking - WSADATA wsaData; - WORD wVersionRequested = MAKEWORD(2, 0); - if (WSAStartup(wVersionRequested, &wsaData) != 0) - { - ShowError("socket_init: WinSock not available!"); - return false; - } - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) - { - ShowError("socket_init: WinSock version mismatch (2.0 or compatible required)!"); - return false; - } - } -#elif defined(HAVE_SETRLIMIT) && !defined(CYGWIN) - // NOTE: getrlimit and setrlimit have bogus behavior in cygwin. - // "Number of fds is virtually unlimited in cygwin" (sys/param.h) - { // set socket limit to MAX_FD - rlimit rlp; - if (0 == getrlimit(RLIMIT_NOFILE, &rlp)) - { - rlp.rlim_cur = MAX_FD; - if (0 != setrlimit(RLIMIT_NOFILE, &rlp)) - { // failed, try setting the maximum too (permission to change system limits is required) - rlp.rlim_max = MAX_FD; - if (0 != setrlimit(RLIMIT_NOFILE, &rlp)) - { // failed - // set to maximum allowed - getrlimit(RLIMIT_NOFILE, &rlp); - rlp.rlim_cur = rlp.rlim_max; - setrlimit(RLIMIT_NOFILE, &rlp); - // report limit - getrlimit(RLIMIT_NOFILE, &rlp); - ShowWarning("socket_init: failed to set socket limit to %d (current limit %d).", MAX_FD, (int)rlp.rlim_cur); - } - } - } - } -#endif - - sFD_ZERO(&readfds); - - // initialize last send-receive tick - last_tick = time(nullptr); - return true; -} - -bool _vsocket_final() -{ - return true; -} - -// hostname/ip conversion functions -std::string ip2str(uint32 ip) -{ - uint32 reversed_ip = htonl(ip); - char address[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &reversed_ip, address, INET_ADDRSTRLEN); - - // This is internal, so we can trust it. - return fmt::format("{}", asStringFromUntrustedSource(address)); -} - -uint32 str2ip(const char* ip_str) -{ - uint32 ip = 0; - inet_pton(AF_INET, ip_str, &ip); - - return ntohl(ip); -} - -/*****************************************************************************/ -/* - * - * TCP LEVEL - * - */ - -int ip_rules = 1; -static int connect_check(uint32 ip); - -////////////////////////////// -// IP rules and connection limits - -typedef struct _connect_history -{ - struct _connect_history* next; - uint32 ip; - time_point tick; - int count; - unsigned ddos : 1; - _connect_history() - { - next = nullptr; - ip = 0; - count = 0; - ddos = 0; - } -} ConnectHistory; - -using AccessControl = struct _access_control -{ - uint32 ip; - uint32 mask; -}; - -enum _aco : uint8 -{ - ACO_DENY_ALLOW, - ACO_ALLOW_DENY, - ACO_MUTUAL_FAILURE -}; - -static std::vector access_allow; -static std::vector access_deny; -static int access_order = ACO_DENY_ALLOW; -static int access_debug = 0; -static bool udp_debug = false; -static bool tcp_debug = false; -//-- -static int connect_count = 10; -static duration connect_interval = 3s; -static duration connect_lockout = 10min; - -/// Connection history, an array of linked lists. -/// The array's index for any ip is ip&0xFFFF -static ConnectHistory* connect_history[0x10000]; - -static int connect_check_(uint32 ip); - -/// Verifies if the IP can connect. (with debug info) -/// @see connect_check_() -static int connect_check(uint32 ip) -{ - TracyZoneScoped; - int result = connect_check_(ip); - if (access_debug) - { - ShowInfo(fmt::format("connect_check: Connection from {} {}", ip2str(ip), result ? "allowed." : "denied!")); - } - return result; -} - -/// Verifies if the IP can connect. -/// 0 : Connection Rejected -/// 1 or 2 : Connection Accepted -static int connect_check_(uint32 ip) -{ - TracyZoneScoped; - ConnectHistory* hist = connect_history[ip & 0xFFFF]; - int is_allowip = 0; - int is_denyip = 0; - int connect_ok = 0; - - // Search the allow list - for (auto const& entry : access_allow) - { - if ((ip & entry.mask) == (entry.ip & entry.mask)) - { - if (access_debug) - { - ShowInfo( - fmt::format("connect_check: Found match from allow list:{} IP:{} Mask:{}", - ip2str(ip), - ip2str(entry.ip), - ip2str(entry.mask))); - } - is_allowip = 1; - break; - } - } - // Search the deny list - for (auto const& entry : access_deny) - { - if ((ip & entry.mask) == (entry.ip & entry.mask)) - { - if (access_debug) - { - ShowInfo( - fmt::format("connect_check: Found match from deny list:{} IP:{} Mask:{}", - ip2str(ip), - ip2str(entry.ip), - ip2str(entry.mask))); - } - is_denyip = 1; - break; - } - } - // Decide connection status - // 0 : Reject - // 1 : Accept - // 2 : Unconditional Accept (accepts even if flagged as possible DDoS) - switch (access_order) - { - case ACO_DENY_ALLOW: - default: - if (is_denyip) - { - connect_ok = 0; // Reject - } - else if (is_allowip) - { - connect_ok = 2; // Unconditional Accept - } - else - { - connect_ok = 1; // Accept - } - break; - case ACO_ALLOW_DENY: - if (is_allowip) - { - connect_ok = 2; // Unconditional Accept - } - else if (is_denyip) - { - connect_ok = 0; // Reject - } - else - { - connect_ok = 1; // Accept - } - break; - case ACO_MUTUAL_FAILURE: - if (is_allowip && !is_denyip) - { - connect_ok = 2; // Unconditional Accept - } - else - { - connect_ok = 0; // Reject - } - break; - } - - // Inspect connection history - while (hist) - { - if (ip == hist->ip) - { // IP found - if (hist->ddos) - { // flagged as possible DDoS - return (connect_ok == 2 ? 1 : 0); - } - if ((server_clock::now() - hist->tick) < connect_interval) - { // connection within connect_interval limit - hist->tick = server_clock::now(); - if (hist->count++ >= connect_count) - { // to many attempts detected - hist->ddos = 1; - ShowWarning(fmt::format("connect_check: too many connection attempts detected from {}!", ip2str(ip))); - return (connect_ok == 2 ? 1 : 0); - } - return connect_ok; - } - - // not within connect_interval, clear data - hist->tick = server_clock::now(); - hist->count = 0; - return connect_ok; - } - hist = hist->next; - } - // IP not found, add to history - hist = new ConnectHistory{}; - hist->ip = ip; - hist->tick = server_clock::now(); - hist->next = connect_history[ip & 0xFFFF]; - connect_history[ip & 0xFFFF] = hist; - return connect_ok; -} - -/// Timer function. -/// Deletes old connection history records. -static int connect_check_clear(time_point tick, CTaskMgr::CTask* PTask) -{ - TracyZoneScoped; - int clear = 0; - int list = 0; - ConnectHistory root{}; - ConnectHistory* prev_hist = nullptr; - ConnectHistory* hist = nullptr; - - for (int i = 0; i < 0x10000; ++i) - { - prev_hist = &root; - root.next = hist = connect_history[i]; - while (hist) - { - if ((!hist->ddos && (tick - hist->tick) > connect_interval * 3) || (hist->ddos && (tick - hist->tick) > connect_lockout)) - { // Remove connection history - prev_hist->next = hist->next; - destroy(hist); - hist = prev_hist->next; - clear++; - } - else - { - prev_hist = hist; - hist = hist->next; - } - list++; - } - connect_history[i] = root.next; - } - if (access_debug) - { - ShowInfo("connect_check_clear: Cleared %d of %d from IP list.", clear, list); - } - return list; -} - -/// Parses the ip address and mask and puts it into acc. -/// Returns 1 is successful, 0 otherwise. -int access_ipmask(const char* str, AccessControl* acc) -{ - TracyZoneScoped; - uint32 ip = 0; - uint32 mask = 0; - unsigned int a[4]{}; - unsigned int m[4]{}; - int n = 0; - - if (strcmp(str, "all") == 0) - { - ip = 0; - mask = 0; - } - else - { - if (((n = sscanf(str, "%u.%u.%u.%u/%u.%u.%u.%u", a, a + 1, a + 2, a + 3, m, m + 1, m + 2, m + 3)) != 8 && // not an ip + standard mask - (n = sscanf(str, "%u.%u.%u.%u/%u", a, a + 1, a + 2, a + 3, m)) != 5 && // not an ip + bit mask - (n = sscanf(str, "%u.%u.%u.%u", a, a + 1, a + 2, a + 3)) != 4) || // not an ip - a[0] > 255 || - a[1] > 255 || a[2] > 255 || a[3] > 255 || // invalid ip - (n == 8 && (m[0] > 255 || m[1] > 255 || m[2] > 255 || m[3] > 255)) || // invalid standard mask - (n == 5 && m[0] > 32)) - { // invalid bit mask - return 0; - } - ip = (a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24)); - if (n == 8) - { // standard mask - mask = (a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24)); - } - else if (n == 5) - { // bit mask - mask = 0; - while (m[0]) - { - mask = (mask >> 1) | 0x80000000; - --m[0]; - } - } - else - { // just this ip - mask = 0xFFFFFFFF; - } - } - - ip = ntohl(ip); - - if (access_debug) - { - ShowInfo(fmt::format("access_ipmask: Loaded IP:{} mask:{}", ip2str(ip), ip2str(mask))); - } - acc->ip = ip; - acc->mask = mask; - return 1; -} - -////////////////////////////// -int recv_to_fifo(int fd) -{ - TracyZoneScoped; - int len = 0; - - if (!session_isActive(fd)) - { - return -1; - } - - auto prev_length = sessions[fd]->rdata.size(); - sessions[fd]->rdata.resize(prev_length + 0x7FF); - len = sRecv(fd, sessions[fd]->rdata.data() + prev_length, (int)(sessions[fd]->rdata.capacity() - prev_length), 0); - - if (len == SOCKET_ERROR) - { // An exception has occured - if (sErrno != S_EWOULDBLOCK) - { - set_eof(fd); - } - return 0; - } - - if (len == 0) - { // Normal connection end. - set_eof(fd); - return 0; - } - - sessions[fd]->rdata.resize(prev_length + len); - sessions[fd]->rdata_tick = last_tick; - return 0; -} - -int send_from_fifo(int fd) -{ - TracyZoneScoped; - int len = 0; - - if (!session_isValid(fd)) - { - return -1; - } - - if (sessions[fd]->wdata.empty()) - { - return 0; // nothing to send - } - - len = sSend(fd, sessions[fd]->wdata.data(), (int)sessions[fd]->wdata.size(), 0); - - if (len == SOCKET_ERROR) - { // An exception has occured - if (sErrno != S_EWOULDBLOCK) - { - // ShowDebug("send_from_fifo: error %d, ending connection #%d", sErrno, fd); - sessions[fd]->wdata.clear(); // Clear the send queue as we can't send anymore. [Skotlex] - set_eof(fd); - } - return 0; - } - - if (len > 0) - { - // some data could not be transferred? - // shift unsent data to the beginning of the queue - if ((size_t)len < sessions[fd]->wdata.size()) - { - sessions[fd]->wdata.erase(0, len); - } - else - { - sessions[fd]->wdata.clear(); - } - } - - return 0; -} - -/*====================================== - * CORE : Default processing functions - *--------------------------------------*/ -int null_recv(int fd) -{ - return 0; -} -int null_send(int fd) -{ - return 0; -} -int null_parse(int fd) -{ - return 0; -} - -ParseFunc default_func_parse = null_parse; - -bool session_isValid(int fd) -{ - TracyZoneScoped; - return (fd > 0 && fd < MAX_FD && sessions[fd] != nullptr); -} -bool session_isActive(int fd) -{ - TracyZoneScoped; - return (session_isValid(fd) && !sessions[fd]->flag.eof); -} - -int32 makeConnection_tcp(uint32 ip, uint16 port) -{ - TracyZoneScoped; - int fd = makeConnection(ip, port, SOCK_STREAM); - if (fd > 0) - { - create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse); - sessions[fd]->client_addr = ip; - } - return fd; -} -/*====================================== - * CORE : Connection functions - *--------------------------------------*/ -int connect_client(int listen_fd, sockaddr_in& client_address) -{ - TracyZoneScoped; - - int fd = 0; - socklen_t len{}; - - len = sizeof(client_address); - - fd = sAccept(listen_fd, (struct sockaddr*)&client_address, &len); - if (fd == -1) - { - ShowError("connect_client: accept failed (code %d, listen_fd %d)!", sErrno, listen_fd); - return -1; - } - if (fd == 0) - { // reserved - ShowError("connect_client: Socket #0 is reserved - Please report this!!!"); - sClose(fd); - return -1; - } - if (fd >= MAX_FD) - { // socket number too big - ShowError("connect_client: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", - fd, MAX_FD); - sClose(fd); - return -1; - } - - if (ip_rules && !connect_check(ntohl(client_address.sin_addr.s_addr))) - { - do_close(fd); - return -1; - } - - if (fd_max <= fd) - { - fd_max = fd + 1; - } -#ifdef __APPLE__ - sFD_SET(fd, &readfds); -#endif - return fd; -} - -int32 makeListenBind_tcp(const char* ip, uint16 port, RecvFunc connect_client) -{ - TracyZoneScoped; - sockaddr_in server_address{}; - int fd = 0; - int result = 0; - - fd = sSocket(AF_INET, SOCK_STREAM, 0); - - if (fd == -1) - { - ShowError("make_listen_bind: socket creation failed (port %d, code %d)!", port, sErrno); - ShowError("Is another process using this port?"); - do_final(EXIT_FAILURE); - } - - if (fd == 0) - { // reserved - ShowError("make_listen_bind: Socket #0 is reserved - Please report this!!!"); - sClose(fd); - return -1; - } - - if (fd >= MAX_FD) - { // socket number too big - ShowError("make_listen_bind: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", - fd, MAX_FD); - sClose(fd); - return -1; - } - - server_address.sin_family = AF_INET; - inet_pton(AF_INET, ip, &server_address.sin_addr.s_addr); - server_address.sin_port = htons(port); - - // https://stackoverflow.com/questions/3229860/what-is-the-meaning-of-so-reuseaddr-setsockopt-option-linux - // Avoid hangs in TIME_WAIT state of TCP -#ifdef WIN32 - // Windows doesn't seem to have this problem, but apparently this would be the right way to explicitly mimic SO_REUSEADDR unix's behavior. - setsockopt(sock_arr[fd], SOL_SOCKET, SO_DONTLINGER, "\x00\x00\x00\x00", 4); -#else - int enable = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - { - ShowError("setsockopt SO_REUSEADDR failed!"); - } -#endif - - result = sBind(fd, (struct sockaddr*)&server_address, sizeof(server_address)); - if (result == SOCKET_ERROR) - { - ShowError("make_listen_bind: bind failed (socket #%d, port %d, code %d)!", fd, port, sErrno); - do_final(EXIT_FAILURE); - } - result = sListen(fd, 5); - if (result == SOCKET_ERROR) - { - ShowError("make_listen_bind: listen failed (socket #%d, port %d, code %d)!", fd, port, sErrno); - do_final(EXIT_FAILURE); - } - - if (fd_max <= fd) - { - fd_max = fd + 1; - } - sFD_SET(fd, &readfds); - - create_session(fd, connect_client, null_send, null_parse); - sessions[fd]->client_addr = 0; // just listens - sessions[fd]->rdata_tick = 0; // disable timeouts on this socket - - return fd; -} - -int32 RFIFOSKIP(int32 fd, size_t len) -{ - TracyZoneScoped; - struct socket_data* s = nullptr; - - if (!session_isActive(fd)) - { - return 0; - } - - s = sessions[fd].get(); - - if (s->rdata.size() < s->rdata_pos + len) - { - ShowError("RFIFOSKIP: skipped past end of read buffer! Adjusting from %d to %d (session #%d)", len, RFIFOREST(fd), fd); - len = RFIFOREST(fd); - } - - s->rdata_pos = s->rdata_pos + len; - return 0; -} - -void do_close_tcp(int32 fd) -{ - TracyZoneScoped; - flush_fifo(fd); - do_close(fd); - if (sessions[fd]) - { - delete_session(fd); - } -} - -/// -/// -/// Get the access list object collection from the provided string. The string -/// provided is in the form of "127.0.0.1,192.168.0.0/16" where each entry is -/// separated by a comma. This will break apart all individual entries and then -/// for each entry that is validated will be pushed into our result collection, -/// otherwise an error will be displayed. -/// -/// -/// The access list that we are parsing for individual entries. -/// std::vector collection that contains all AccessControl entries. -/// -std::vector get_access_list(std::string const& access_list) -{ - // with the provided comma delimited access list, we will convert into a - // vector of string entries - std::vector result{}; - - std::stringstream ss(access_list); - while (ss.good()) - { - std::string entry; - // get string delimited by comma character - getline(ss, entry, ','); - - if (entry == "") - { - // skip - continue; - } - - // validate our entry before pushing it into our results list - AccessControl acc{}; - if (access_ipmask(entry.c_str(), &acc)) - { - result.emplace_back(acc); - } - else - { - ShowError("socket_config_read: Invalid ip or ip range '%s'!", entry); - } - } - - return result; -} - -/// -/// -/// Setting up the UDP settings, currently just has a debug flag. -/// -/// @NOTE I've added a UDP debug flag, the access debug flag is shared between -/// both UDP and TCP. Can be confusing if you believe you are setting the -/// flag and then it gets overwritten by the other setting. The new flags -/// are just not being leveraged just yet (not sure where they would go). -/// -/// -void socket_udp_setup() -{ - // debug setting - udp_debug = settings::get("network.UDP_DEBUG"); - access_debug = settings::get("network.UDP_DEBUG"); -} - -/// -/// -/// Handling the TCP setup properties for the socket. All leveraging the new -/// settings handling of LUA files. The only issue I had was related to the -/// allow and deny lists. There would need to be an extension to the get() -/// method in order to allow LUA lists { "a", "b" }. To allieviate this I've -/// implemented a get_access_list() method in order to parse a string that -/// splits entries with commas. All other settings are straight forward using -/// the settings get() method. Added all "packet_tcp.conf" settings to the -/// new "settings/default/network.lua" file. -/// -/// -void socket_tcp_setup() -{ - // debug setting (shared?) - access_debug = settings::get("network.TCP_DEBUG"); - - // sockets configuration - tcp_debug = settings::get("network.TCP_DEBUG"); - stall_time = settings::get("network.TCP_STALL_TIME"); - - // IP rules settings - ip_rules = settings::get("network.TCP_ENABLE_IP_RULES"); - - // ordering of the checks - auto ordering = settings::get("network.TCP_ORDER"); - if (ordering == "deny,allow") - { - access_order = ACO_DENY_ALLOW; - } - else if (ordering == "allow,deny") - { - access_order = ACO_ALLOW_DENY; - } - else if (ordering == "mutual-failure") - { - access_order = ACO_MUTUAL_FAILURE; - } - - // get the allow and deny list - if (access_debug) - { - ShowInfo("Loading allow access list..."); - } - auto allow_list_str = settings::get("network.TCP_ALLOW"); - access_allow = get_access_list(allow_list_str); - if (access_debug) - { - ShowInfo("Size of allow access list: %d", access_allow.size()); - } - - if (access_debug) - { - ShowInfo("Loading deny access list..."); - } - auto deny_list_str = settings::get("network.TCP_DENY"); - access_deny = get_access_list(deny_list_str); - if (access_debug) - { - ShowInfo("Size of deny access list: %d", access_deny.size()); - } - - // connection limit settings - connect_interval = std::chrono::milliseconds( - settings::get("network.TCP_CONNECT_INTERVAL")); - connect_count = settings::get("network.TCP_CONNECT_COUNT"); - connect_lockout = std::chrono::milliseconds( - settings::get("network.TCP_CONNECT_LOCKOUT")); -} - -void socket_init_tcp() -{ - TracyZoneScoped; - if (!_vsocket_init()) - { - return; - } - - // setup our socket - socket_tcp_setup(); - - // sessions[0] is now currently used for disconnected sessions of the map - // server, and as such, should hold enough buffer (it is a vacuum so to - // speak) as it is never flushed. [Skotlex] - create_session(0, null_recv, null_send, null_parse); - - // Delete old connection history every 5 minutes - memset(connect_history, 0, sizeof(connect_history)); - CTaskMgr::getInstance()->AddTask( - "connect_check_clear", - server_clock::now() + 1s, - nullptr, - CTaskMgr::TASK_INTERVAL, - 5min, - connect_check_clear); -} - -void socket_final_tcp() -{ - TracyZoneScoped; - if (!_vsocket_final()) - { - return; - } - - ConnectHistory* hist = nullptr; - ConnectHistory* next_hist = nullptr; - - for (int i = 0; i < 0x10000; ++i) - { - hist = connect_history[i]; - while (hist) - { - next_hist = hist->next; - destroy(hist); - hist = next_hist; - } - } - - for (int i = 1; i < fd_max; i++) - { - if (sessions[i]) - { - do_close_tcp(i); - } - } -} - -void flush_fifo(int32 fd) -{ - TracyZoneScoped; - if (sessions[fd] != nullptr) - { - sessions[fd]->func_send(fd); - } -} - -void flush_fifos() -{ - TracyZoneScoped; - for (int i = 1; i < fd_max; i++) - { - flush_fifo(i); - } -} - -void set_defaultparse(ParseFunc defaultparse) -{ - TracyZoneScoped; - default_func_parse = defaultparse; -} - -void set_eof(int32 fd) -{ - TracyZoneScoped; - if (session_isActive(fd)) - { - sessions[fd]->flag.eof = 1; - } -} - -int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseFunc func_parse) -{ - TracyZoneScoped; - - DebugSockets(fmt::format("create_session fd: {}", fd).c_str()); - - sessions[fd] = std::make_unique(func_recv, func_send, func_parse); - - sessions[fd]->rdata.reserve(RFIFO_SIZE); - sessions[fd]->wdata.reserve(WFIFO_SIZE); - - sessions[fd]->rdata_tick = last_tick; - - return 0; -} - -int delete_session(int fd) -{ - TracyZoneScoped; - - DebugSockets(fmt::format("delete_session fd: {}", fd).c_str()); - - if (fd <= 0 || fd >= MAX_FD) - { - return -1; - } - - sessions[fd] = nullptr; - - // In order to resize fd_max to the minimum possible size, we have to find - // the fd in use with the highest value. We will iterate through the session - // list backwards until we find the first non-nullptr entry. - // clang-format off - auto result = std::find_if(sessions.rbegin(), sessions.rend(), - [](std::unique_ptr& entry) - { - return entry != nullptr; - }); - // clang-format on - - auto old_fd_max = fd_max; - - fd_max = std::distance(result, sessions.rend()); - - DebugSockets(fmt::format("Resizing fd_max from {} to {}.", old_fd_max, fd_max).c_str()); - - return 0; -} - -/*====================================== - * CORE : Socket options - *--------------------------------------*/ -void set_nonblocking(int fd, unsigned long yes) -{ - TracyZoneScoped; - // FIONBIO Use with a nonzero argp parameter to enable the nonblocking mode of socket s. - // The argp parameter is zero if nonblocking is to be disabled. - if (sIoctl(fd, FIONBIO, &yes) != 0) - { - ShowError("set_nonblocking: Failed to set socket #%d to non-blocking mode (code %d) - Please report this!!!", fd, sErrno); - } -} - -/* - * - * UDP LEVEL - * - */ -int32 makeBind_udp(uint32 ip, uint16 port) -{ - TracyZoneScoped; - sockaddr_in server_address{}; - int fd = 0; - int result = 0; - - fd = sSocket(AF_INET, SOCK_DGRAM, 0); - - if (fd == -1) - { - ShowError("make_listen_bind: socket creation failed (port %d, code %d)!", port, sErrno); - do_final(EXIT_FAILURE); - } - if (fd == 0) - { // reserved - ShowError("make_listen_bind: Socket #0 is reserved - Please report this!!!"); - sClose(fd); - return -1; - } - if (fd >= MAX_FD) - { // socket number too big - ShowError("make_listen_bind: New socket #%d is greater than can we handle! Increase the value of MAX_FD (currently %d) for your OS to fix this!", - fd, MAX_FD); - sClose(fd); - return -1; - } - - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = htonl(ip); - server_address.sin_port = htons(port); - - result = sBind(fd, (struct sockaddr*)&server_address, sizeof(server_address)); - if (result == SOCKET_ERROR) - { - ShowError("make_listen_bind: bind failed (socket #%d, port %d, code %d)!", fd, port, sErrno); - do_final(EXIT_FAILURE); - } - - if (fd_max <= fd) - { - fd_max = fd + 1; - } - sFD_SET(fd, &readfds); - return fd; -} - -void socket_init_udp() -{ - TracyZoneScoped; - if (!_vsocket_init()) - { - return; - } - - // setup our socket - socket_udp_setup(); -} - -void do_close_udp(int32 fd) -{ - TracyZoneScoped; - do_close(fd); -} - -void socket_final_udp() -{ - TracyZoneScoped; - if (!_vsocket_final()) - { - return; - } -} - -int32 recvudp(int32 fd, void* buff, size_t nbytes, int32 flags, struct sockaddr* from, socklen_t* addrlen) -{ - TracyZoneScoped; - return sRecvfrom(fd, (char*)buff, (int)nbytes, flags, from, addrlen); -} - -int32 sendudp(int32 fd, void* buff, size_t nbytes, int32 flags, const struct sockaddr* from, socklen_t addrlen) -{ - TracyZoneScoped; - return sSendto(fd, (const char*)buff, (int)nbytes, flags, from, addrlen); -} - -void socket_init() -{ - TracyZoneScoped; - switch (SOCKET_TYPE) - { - case socket_type::TCP: - socket_init_tcp(); - break; - case socket_type::UDP: - socket_init_udp(); - break; - default: - break; - } -} - -void socket_final() -{ - TracyZoneScoped; - switch (SOCKET_TYPE) - { - case socket_type::TCP: - socket_final_tcp(); - break; - case socket_type::UDP: - socket_final_udp(); - break; - default: - break; - } -} diff --git a/src/common/socket.h b/src/common/socket.h deleted file mode 100644 index 1ba51162b2c..00000000000 --- a/src/common/socket.h +++ /dev/null @@ -1,340 +0,0 @@ -/* -=========================================================================== - - Copyright (c) 2010-2015 Darkstar Dev Teams - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see http://www.gnu.org/licenses/ - -=========================================================================== -*/ - -#ifndef _SOCKET_H_ -#define _SOCKET_H_ - -#ifndef _CBASETYPES_H_ -#include "common/cbasetypes.h" -#endif - -#ifdef __APPLE__ -#include -#endif - -#ifdef __APPLE__ -#define MAX_FD FD_SETSIZE -#else -#define MAX_FD 10240 -#endif - -#ifdef WIN32 -#include -#include -typedef long in_addr_t; -#else -#include -#include -#include -#include -#include -#endif - -#include -#include -#include -#include - -/* - * - * COMMON LEVEL - * - */ -///////////////////////////////////////////////////////////////////// -#if defined(WIN32) -///////////////////////////////////////////////////////////////////// -// windows portability layer -typedef int socklen_t; - -#define sErrno WSAGetLastError() -#define S_ENOTSOCK WSAENOTSOCK -#define S_EWOULDBLOCK WSAEWOULDBLOCK -#define S_EINTR WSAEINTR -#define S_ECONNABORTED WSAECONNABORTED - -#define SHUT_RD SD_RECEIVE -#define SHUT_WR SD_SEND -#define SHUT_RDWR SD_BOTH - -// global array of sockets (emulating linux) -// fd is the position in the array -extern SOCKET sock_arr[MAX_FD]; -extern int sock_arr_len; - -/// Returns the socket associated with the target fd. -/// -/// @param fd Target fd. -/// @return Socket -#define fd2sock(fd) sock_arr[fd] - -/// Returns the first fd associated with the socket. -/// Returns -1 if the socket is not found. -/// -/// @param s Socket -/// @return Fd or -1 -int sock2fd(SOCKET s); - -/// Inserts the socket into the global array of sockets. -/// Returns a new fd associated with the socket. -/// If there are too many sockets it closes the socket, sets an error and -// returns -1 instead. -/// Since fd 0 is reserved, it returns values in the range [1,MAX_FD[. -/// -/// @param s Socket -/// @return New fd or -1 -int sock2newfd(SOCKET s); - -int sAccept(int fd, struct sockaddr* addr, int* addrlen); - -int sClose(int fd); -int sSocket(int af, int type, int protocol); - -#define sBind(fd, name, namelen) bind(fd2sock(fd), name, namelen) -#define sListen(fd, backlog) listen(fd2sock(fd), backlog) -#define sIoctl(fd, cmd, argp) ioctlsocket(fd2sock(fd), cmd, argp) -#define sConnect(fd, name, namelen) connect(fd2sock(fd), name, namelen) -#define sRecv(fd, buf, len, flags) recv(fd2sock(fd), buf, len, flags) -#define sRecvfrom(fd, buf, len, flags, from, addrlen) recvfrom(fd2sock(fd), buf, len, flags, from, addrlen) -#define sSelect select -#define sSend(fd, buf, len, flags) send(fd2sock(fd), buf, len, flags) -#define sSendto(fd, buf, len, flags, to, addrlen) sendto(fd2sock(fd), buf, len, flags, to, addrlen) -#define sSetsockopt(fd, level, optname, optval, optlen) setsockopt(fd2sock(fd), level, optname, optval, optlen) -#define sShutdown(fd, how) shutdown(fd2sock(fd), how) -#define sFD_SET(fd, set) FD_SET(fd2sock(fd), set) -#define sFD_CLR(fd, set) FD_CLR(fd2sock(fd), set) -#define sFD_ISSET(fd, set) FD_ISSET(fd2sock(fd), set) -#define sFD_ZERO FD_ZERO -#else -// nix portability layer - -#define SOCKET_ERROR (-1) - -#define sErrno errno -#define S_ENOTSOCK EBADF -#define S_EWOULDBLOCK EAGAIN -#define S_EINTR EINTR -#define S_ECONNABORTED ECONNABORTED - -#define sAccept accept -#define sClose close -#define sSocket socket - -#define sBind bind -#define sConnect connect -#define sIoctl ioctl -#define sListen listen -#define sRecv recv -#define sRecvfrom recvfrom -#define sSelect select -#define sSend send -#define sSendto sendto -#define sSetsockopt setsockopt -#define sShutdown shutdown -#define sFD_SET FD_SET -#define sFD_CLR FD_CLR -#define sFD_ISSET FD_ISSET -#define sFD_ZERO FD_ZERO - -#endif - -#define TOB(n) ((uint8)((n) & std::numeric_limits::max())) -#define TOW(n) ((uint16)((n) & std::numeric_limits::max())) -#define TOL(n) ((uint32)((n) & std::numeric_limits::max())) - -enum class socket_type -{ - TCP, - UDP -}; - -extern socket_type SOCKET_TYPE; - -extern fd_set readfds; -extern int fd_max; -extern time_t last_tick; -extern time_t stall_time; - -int32 makeConnection(uint32 ip, uint16 port, int32 type); - -int32 do_sockets(fd_set* rfd, duration next); - -void do_close(int32 fd); - -void socket_init(); - -void socket_final(); - -// hostname/ip conversion functions -std::string ip2str(uint32 ip); - -uint32 str2ip(const char* ip_str); - -/************************************************/ -/* - * - * TCP LEVEL - * - */ - -// initial recv buffer size (this will also be the max. size) -// biggest known packet: S 0153 .w .?B -> 24x24 256 color .bmp (0153 + len.w + 1618/1654/1756 bytes) -#define RFIFO_SIZE (2 * 1024) -// initial send buffer size (will be resized as needed) -#define WFIFO_SIZE (16 * 1024) - -// Maximum size of pending data in the write fifo. (for non-server connections) -// The connection is closed if it goes over the limit. -#define WFIFO_MAX (1 * 1024 * 1024) - -// Struct declaration -typedef int (*RecvFunc)(int fd); -typedef int (*SendFunc)(int fd); -typedef int (*ParseFunc)(int fd); - -// socket I/O macros -#define RFIFOHEAD(fd) -#define WFIFOHEAD(fd, size) \ - do \ - { \ - if ((fd) && sessions[fd]->wdata_size + (size) > sessions[fd]->max_wdata) \ - realloc_writefifo(fd, size); \ - } while (0) -//------------------- -#define RFIFOP(fd, pos) (sessions[fd]->rdata + sessions[fd]->rdata_pos + (pos)) -#define WFIFOP(fd, pos) (sessions[fd]->wdata + sessions[fd]->wdata_size + (pos)) - -#define RFIFOB(fd, pos) (*(uint8*)RFIFOP(fd, pos)) -#define WFIFOB(fd, pos) (*(uint8*)WFIFOP(fd, pos)) -#define RFIFOW(fd, pos) (*(uint16*)RFIFOP(fd, pos)) -#define WFIFOW(fd, pos) (*(uint16*)WFIFOP(fd, pos)) -#define RFIFOL(fd, pos) (*(uint32*)RFIFOP(fd, pos)) -#define WFIFOL(fd, pos) (*(uint32*)WFIFOP(fd, pos)) - -#define RFIFOREST(fd) (sessions[fd]->flag.eof ? 0 : sessions[fd]->rdata.size() - sessions[fd]->rdata_pos) -#define RFIFOFLUSH(fd) \ - do \ - { \ - if (sessions[fd]->rdata.size() == sessions[fd]->rdata_pos) \ - { \ - sessions[fd]->rdata_pos = 0; \ - sessions[fd]->rdata.clear(); \ - } \ - else \ - { \ - sessions[fd]->rdata.erase(0, sessions[fd]->rdata_pos); \ - sessions[fd]->rdata_pos = 0; \ - } \ - } while (0) - -struct socket_data -{ - struct - { - unsigned char eof : 1; - unsigned char server : 1; - } flag; - - uint32 client_addr; // remote client address - - std::string rdata, wdata; - size_t rdata_pos; - time_t rdata_tick; // time of last recv (for detecting timeouts); zero when timeout is disabled - - RecvFunc func_recv; - SendFunc func_send; - ParseFunc func_parse; - - bool ver_mismatch; - void* session_data; // stores application-specific data related to the session - - socket_data(RecvFunc _func_recv, SendFunc _func_send, ParseFunc _func_parse) - : rdata_tick(time(0)) - , func_recv(_func_recv) - , func_send(_func_send) - , func_parse(_func_parse) - { - client_addr = 0; - flag.eof = '\0'; - flag.server = '\0'; - rdata_pos = 0; - ver_mismatch = 0; - session_data = nullptr; - } -}; - -// Data prototype declaration -extern std::array, MAX_FD> sessions; - -////////////////////////////////// -// some checking on sockets -bool session_isValid(int fd); -bool session_isActive(int fd); - -int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseFunc func_parse); -int delete_session(int fd); -////////////////////////////////// -int32 recv_to_fifo(int32 fd); - -int32 send_from_fifo(int32 fd); - -int32 connect_client(int32 listen_fd, sockaddr_in& client_address); - -int32 makeConnection_tcp(uint32 ip, uint16 port); - -int32 makeListenBind_tcp(const char* ip, uint16 port, RecvFunc connect_client); - -int32 RFIFOSKIP(int32 fd, size_t len); - -void socket_init_tcp(void); -void socket_final_tcp(void); - -void do_close_tcp(int32 fd); - -void flush_fifo(int32 fd); -void flush_fifos(void); - -void set_defaultparse(ParseFunc defaultparse); - -void set_eof(int32 fd); - -void set_nonblocking(int fd, unsigned long yes); - -/* - * - * UDP LEVEL - * - */ -int32 makeBind_udp(uint32 ip, uint16 port); - -void socket_init_udp(void); -void do_close_udp(int32 fd); -void socket_final_udp(void); - -int32 recvudp(int32 fd, void* buff, size_t nbytes, int32 flags, struct sockaddr* from, socklen_t* addrlen); -int32 sendudp(int32 fd, void* buff, size_t nbytes, int32 flags, const struct sockaddr* from, socklen_t addrlen); - -template -T& ref(U* buf, std::size_t index) -{ - return *reinterpret_cast(reinterpret_cast(buf) + index); -} - -#endif // _SOCKET_H // diff --git a/src/common/sql.cpp b/src/common/sql.cpp index 054297c8d2c..56ec7eff4ea 100644 --- a/src/common/sql.cpp +++ b/src/common/sql.cpp @@ -34,12 +34,6 @@ #include #include -// TODO: Since kernel.cpp isn't used by the processes which now use Application, we can't -// : store this global flag there. So we're storing it here until all processes are -// : refactored to use Application. Once that's done this should be moved out of static -// : storage in this unit to a member of Application. -std::atomic gProcessLoaded = false; - SqlConnection::SqlConnection() : SqlConnection(settings::get("network.SQL_LOGIN").c_str(), settings::get("network.SQL_PASSWORD").c_str(), @@ -366,7 +360,7 @@ int32 SqlConnection::QueryStr(const char* query) auto endTime = hires_clock::now(); auto dTime = std::chrono::duration_cast(endTime - startTime); - if (gProcessLoaded && settings::get("logging.SQL_SLOW_QUERY_LOG_ENABLE")) + if (settings::get("logging.SQL_SLOW_QUERY_LOG_ENABLE")) { if (dTime > std::chrono::milliseconds(settings::get("logging.SQL_SLOW_QUERY_ERROR_TIME"))) { diff --git a/src/login/CMakeLists.txt b/src/login/CMakeLists.txt index 9ecdd4a6dce..54a7c2d07ae 100644 --- a/src/login/CMakeLists.txt +++ b/src/login/CMakeLists.txt @@ -19,14 +19,6 @@ set(SOURCES view_session.cpp ) -# Temporarily remove clashing old code -# TODO: Remove this block -list(REMOVE_ITEM SOURCES - ${CMAKE_SOURCE_DIR}/src/common/kernel.cpp - ${CMAKE_SOURCE_DIR}/src/common/kernel.h - ${CMAKE_SOURCE_DIR}/src/common/socket.cpp - ${CMAKE_SOURCE_DIR}/src/common/socket.h) - if(UNIX) set(resource "") else() diff --git a/src/login/auth_session.cpp b/src/login/auth_session.cpp index 0b78e233767..92c8589ee73 100644 --- a/src/login/auth_session.cpp +++ b/src/login/auth_session.cpp @@ -21,7 +21,6 @@ #include "auth_session.h" -#include "common/socket.h" // for ref #include "common/utils.h" #include @@ -70,7 +69,7 @@ void auth_session::start() void auth_session::do_read() { // clang-format off - socket_.async_read_some(asio::buffer(data_, max_length), + socket_.async_read_some(asio::buffer(buffer_.data(), buffer_.size()), [this, self = shared_from_this()](std::error_code ec, std::size_t length) { if (!ec) @@ -88,11 +87,11 @@ void auth_session::do_read() void auth_session::read_func() { - const auto newModeFlag = ref(data_, 0) == 0xFF; + const auto newModeFlag = ref(buffer_.data(), 0) == 0xFF; if (!newModeFlag) { ShowDebug("Old xiloader connected. Not supported."); - ref(data_, 0) = LOGIN_ERROR; + ref(buffer_.data(), 0) = LOGIN_ERROR; do_write(1); return; } @@ -102,10 +101,11 @@ void auth_session::read_func() char usernameBuffer[17] = {}; char passwordBuffer[33] = {}; - std::memcpy(usernameBuffer, data_ + 0x09, 16); - std::memcpy(passwordBuffer, data_ + 0x19, 32); + std::memcpy(usernameBuffer, buffer_.data() + 0x09, 16); + std::memcpy(passwordBuffer, buffer_.data() + 0x19, 32); + // 1 byte of command at 0x39 - const std::string version(data_ + 0x61, 5); + const std::string version(reinterpret_cast(buffer_.data() + 0x61), 5); std::string username{ usernameBuffer }; std::string password{ passwordBuffer }; @@ -116,13 +116,13 @@ void auth_session::read_func() // Major and minor version changes should be breaking, patch should not. if (strncmp(version.c_str(), SUPPORTED_XILOADER_VERSION, 3) != 0) { - ref(data_, 0) = LOGIN_ERROR_VERSION_UNSUPPORTED; + ref(buffer_.data(), 0) = LOGIN_ERROR_VERSION_UNSUPPORTED; do_write(1); return; } - const int8 code = ref(data_, 0x39); + const int8 code = ref(buffer_.data(), 0x39); DebugSockets(fmt::format("auth code: {} from {}", code, ipAddress)); @@ -170,7 +170,7 @@ void auth_session::read_func() // It's a BCrypt hash, so we can validate it. if (!BCrypt::validatePassword(password, passHash)) { - ref(data_, 0) = LOGIN_ERROR; + ref(buffer_.data(), 0) = LOGIN_ERROR; do_write(1); return; } @@ -185,7 +185,7 @@ void auth_session::read_func() { if (rset->get(passColumn) != passHash) { - ref(data_, 0) = LOGIN_ERROR; + ref(buffer_.data(), 0) = LOGIN_ERROR; do_write(1); return; } @@ -195,7 +195,7 @@ void auth_session::read_func() db::preparedStmt("UPDATE accounts SET accounts.password = ? WHERE accounts.login = ?", passHash, username); if (!BCrypt::validatePassword(password, passHash)) { - ref(data_, 0) = LOGIN_ERROR; + ref(buffer_.data(), 0) = LOGIN_ERROR; do_write(1); return; } @@ -271,14 +271,14 @@ void auth_session::read_func() }*/ // Success - std::memset(data_, 0, 49); - ref(data_, 0) = LOGIN_SUCCESS; - ref(data_, 1) = accountID; + std::memset(buffer_.data(), 0, 49); + ref(buffer_.data(), 0) = LOGIN_SUCCESS; + ref(buffer_.data(), 1) = accountID; unsigned char hash[16]; uint32 hashData = std::time(nullptr) ^ getpid(); md5(reinterpret_cast(&hashData), hash, sizeof(hashData)); - std::memcpy(data_ + 5, hash, 16); + std::memcpy(buffer_.data() + 5, hash, 16); do_write(21); @@ -288,13 +288,13 @@ void auth_session::read_func() } else if (status & ACCOUNT_STATUS_CODE::BANNED) { - ref(data_, 0) = LOGIN_FAIL; + ref(buffer_.data(), 0) = LOGIN_FAIL; do_write(33); } } else // No account match { - ref(data_, 0) = LOGIN_ERROR; + ref(buffer_.data(), 0) = LOGIN_ERROR; do_write(1); } } @@ -308,7 +308,7 @@ void auth_session::read_func() { ShowWarningFmt("login_parse: New account attempt <{}> but is disabled in settings.", username); - ref(data_, 0) = LOGIN_ERROR_CREATE_DISABLED; + ref(buffer_.data(), 0) = LOGIN_ERROR_CREATE_DISABLED; do_write(1); return; } @@ -317,7 +317,7 @@ void auth_session::read_func() const auto rset = db::preparedStmt("SELECT accounts.id FROM accounts WHERE accounts.login = ?", username); if (!rset) { - ref(data_, 0) = LOGIN_ERROR_CREATE; + ref(buffer_.data(), 0) = LOGIN_ERROR_CREATE; do_write(1); return; } @@ -334,7 +334,7 @@ void auth_session::read_func() } else { - ref(data_, 0) = LOGIN_ERROR_CREATE; + ref(buffer_.data(), 0) = LOGIN_ERROR_CREATE; do_write(1); return; } @@ -356,18 +356,18 @@ void auth_session::read_func() accid, username, BCrypt::generateHash(password), strtimecreate, static_cast(ACCOUNT_STATUS_CODE::NORMAL), static_cast(ACCOUNT_PRIVILEGE_CODE::USER)); if (!rset2) { - ref(data_, 0) = LOGIN_ERROR_CREATE; + ref(buffer_.data(), 0) = LOGIN_ERROR_CREATE; do_write(1); return; } - ref(data_, 0) = LOGIN_SUCCESS_CREATE; + ref(buffer_.data(), 0) = LOGIN_SUCCESS_CREATE; do_write(1); return; } else { - ref(data_, 0) = LOGIN_ERROR_CREATE_TAKEN; + ref(buffer_.data(), 0) = LOGIN_ERROR_CREATE_TAKEN; do_write(1); return; } @@ -393,7 +393,7 @@ void auth_session::read_func() // It's a BCrypt hash, so we can validate it. if (!BCrypt::validatePassword(password, passHash)) { - ref(data_, 0) = LOGIN_ERROR_CHANGE_PASSWORD; + ref(buffer_.data(), 0) = LOGIN_ERROR_CHANGE_PASSWORD; do_write(1); return; } @@ -407,7 +407,7 @@ void auth_session::read_func() { if (rset->get(0) != passHash) { - ref(data_, 0) = LOGIN_ERROR_CHANGE_PASSWORD; + ref(buffer_.data(), 0) = LOGIN_ERROR_CHANGE_PASSWORD; do_write(1); return; } @@ -417,7 +417,7 @@ void auth_session::read_func() db::preparedStmt("UPDATE accounts SET accounts.password = ? WHERE accounts.login = ?", passHash.c_str(), username); if (!BCrypt::validatePassword(password, passHash)) { - ref(data_, 0) = LOGIN_ERROR_CHANGE_PASSWORD; + ref(buffer_.data(), 0) = LOGIN_ERROR_CHANGE_PASSWORD; do_write(1); return; } @@ -432,7 +432,7 @@ void auth_session::read_func() if (rset == nullptr || rset->rowsCount() == 0) { ShowWarningFmt("login_parse: user <{}> could not be found using the provided information. Aborting.", username); - ref(data_, 0) = LOGIN_ERROR; + ref(buffer_.data(), 0) = LOGIN_ERROR; do_write(1); return; } @@ -445,19 +445,19 @@ void auth_session::read_func() if (status & ACCOUNT_STATUS_CODE::BANNED) { ShowInfoFmt("login_parse: banned user <{}> detected. Aborting.", username); - ref(data_, 0) = LOGIN_ERROR_CHANGE_PASSWORD; + ref(buffer_.data(), 0) = LOGIN_ERROR_CHANGE_PASSWORD; do_write(1); } if (status & ACCOUNT_STATUS_CODE::NORMAL) { // Account info verified, grab password - std::string updated_password(data_ + 0x40, 32); + std::string updated_password(reinterpret_cast(buffer_.data() + 0x40), 32); if (updated_password == "") { ShowWarningFmt("login_parse: Empty password: Could not update password for user <{}>.", username); - ref(data_, 0) = LOGIN_ERROR_CHANGE_PASSWORD; + ref(buffer_.data(), 0) = LOGIN_ERROR_CHANGE_PASSWORD; do_write(1); return; } @@ -471,13 +471,13 @@ void auth_session::read_func() if (!rset2) { ShowWarningFmt("login_parse: Error trying to update password in database for user <{}>.", username); - ref(data_, 0) = LOGIN_ERROR_CHANGE_PASSWORD; + ref(buffer_.data(), 0) = LOGIN_ERROR_CHANGE_PASSWORD; do_write(1); return; } - memset(data_, 0, 33); - ref(data_, 0) = LOGIN_SUCCESS_CHANGE_PASSWORD; + std::memset(buffer_.data(), 0, 33); + ref(buffer_.data(), 0) = LOGIN_SUCCESS_CHANGE_PASSWORD; do_write(33); ShowInfoFmt("login_parse: password updated for account {} successfully.", accid); @@ -496,7 +496,7 @@ void auth_session::read_func() void auth_session::do_write(std::size_t length) { // clang-format off - asio::async_write(socket_, asio::buffer(data_, length), + asio::async_write(socket_, asio::buffer(buffer_.data(), length), [this, self = shared_from_this()](std::error_code ec, std::size_t /*length*/) { if (!ec) diff --git a/src/login/auth_session.h b/src/login/auth_session.h index e6c55f1460d..aedc7abb0fa 100644 --- a/src/login/auth_session.h +++ b/src/login/auth_session.h @@ -94,8 +94,8 @@ DECLARE_FORMAT_AS_UNDERLYING(ACCOUNT_PRIVILEGE_CODE); class auth_session : public handler_session { public: - auth_session(asio::ssl::stream socket) - : handler_session(std::move(socket)) + auth_session(Application& application, asio::ssl::stream socket) + : handler_session(application, std::move(socket)) { DebugSockets(fmt::format("auth_session from {}", ipAddress)); } diff --git a/src/login/connect_server.cpp b/src/login/connect_server.cpp index 4274b3e0042..1fa153e72a4 100644 --- a/src/login/connect_server.cpp +++ b/src/login/connect_server.cpp @@ -24,31 +24,6 @@ ConnectServer::ConnectServer(int argc, char** argv) : Application("connect", argc, argv) { - asio::io_context io_context; - - // clang-format off - gConsoleService->RegisterCommand("stats", "Print server runtime statistics", - [](std::vector& inputs) - { - size_t uniqueIPs = loginHelpers::getAuthenticatedSessions().size(); - size_t uniqueAccounts = 0; - - for (auto& ipAddrMap: loginHelpers::getAuthenticatedSessions()) - { - uniqueAccounts += loginHelpers::getAuthenticatedSessions()[ipAddrMap.first].size(); - } - ShowInfo("Serving %u IP addresses with %u accounts\n", uniqueIPs, uniqueAccounts); - }); - - gConsoleService->RegisterCommand("exit", "Safely close the login server", - [&](std::vector& inputs) - { - m_RequestExit = true; - io_context.stop(); - gConsoleService->stop(); - }); - // clang-format on - #ifndef _WIN32 rlimit limits{}; @@ -65,52 +40,19 @@ ConnectServer::ConnectServer(int argc, char** argv) } } #endif - xirand::seed(); - - try - { - // Generate a self signed cert if one doesn't exist. - certificateHelpers::generateSelfSignedCert(); - - ShowInfo("creating ports"); - // Handler creates session of type T for specific port on connection. - handler auth(io_context, settings::get("network.LOGIN_AUTH_PORT")); - handler view(io_context, settings::get("network.LOGIN_VIEW_PORT")); - handler data(io_context, settings::get("network.LOGIN_DATA_PORT")); - asio::steady_timer cleanup_callback(io_context, std::chrono::minutes(15)); + // Generate a self signed cert if one doesn't exist. + certificateHelpers::generateSelfSignedCert(); - cleanup_callback.async_wait(std::bind(&ConnectServer::periodicCleanup, this, std::placeholders::_1, &cleanup_callback)); + ShowInfo("creating ports"); - // NOTE: io_context.run() takes over and blocks this thread. Anything after this point will only fire - // if io_context finishes! - ShowInfo("starting io_context"); + // Handler creates session of type T for specific port on connection. + handler auth(*this, ioContext_, settings::get("network.LOGIN_AUTH_PORT")); + handler view(*this, ioContext_, settings::get("network.LOGIN_VIEW_PORT")); + handler data(*this, ioContext_, settings::get("network.LOGIN_DATA_PORT")); - // This busy loop looks nasty, however -- - // https://think-async.com/Asio/asio-1.24.0/doc/asio/reference/io_service.html - /* If an exception is thrown from a handler, the exception is allowed to propagate through the throwing thread's invocation of - run(), run_one(), run_for(), run_until(), poll() or poll_one(). No other threads that are calling any of these functions are affected. - It is then the responsibility of the application to catch the exception. - */ - - while (Application::IsRunning()) - { - try - { - io_context.run(); - break; - } - catch (std::exception& e) - { - // TODO: make a list of "allowed exceptions", the rest can/should cause shutdown. - ShowError(fmt::format("Inner fatal: {}", e.what())); - } - } - } - catch (std::exception& e) - { - ShowError(fmt::format("Outer fatal: {}", e.what())); - } + // asio::steady_timer cleanup_callback(ioContext_, std::chrono::minutes(15)); + // cleanup_callback.async_wait(std::bind(&ConnectServer::periodicCleanup, this, std::placeholders::_1, &cleanup_callback)); } void ConnectServer::periodicCleanup(const asio::error_code& error, asio::steady_timer* timer) @@ -150,7 +92,7 @@ void ConnectServer::periodicCleanup(const asio::error_code& error, asio::steady_ } } - if (Application::IsRunning()) + if (Application::isRunning()) { // reset timer timer->expires_at(timer->expiry() + std::chrono::minutes(15)); diff --git a/src/login/connect_server.h b/src/login/connect_server.h index 3939726f019..87deef2bac3 100644 --- a/src/login/connect_server.h +++ b/src/login/connect_server.h @@ -68,19 +68,6 @@ class ConnectServer final : public Application public: ConnectServer(int argc, char** argv); - ~ConnectServer() override - { - // Everything should be handled with RAII - } - - // TODO: Currently never called. Need io_context asio::steady_timer callback with taskmgr to control timing? - void Tick() override - { - Application::Tick(); - - // Connect Server specific things - } - // This cleanup function is to periodically poll for auth sessions that were successful but xiloader failed to actually launch FFXI // When this happens, the data/view socket are never opened and will never be cleaned up normally. // Auth is closed before any other sessions are open, so the data/view cleanups aren't sufficient diff --git a/src/login/data_session.cpp b/src/login/data_session.cpp index a8b4c1b67ab..d412d3512ae 100644 --- a/src/login/data_session.cpp +++ b/src/login/data_session.cpp @@ -25,7 +25,7 @@ void data_session::read_func() { - std::string sessionHash = loginHelpers::getHashFromPacket(ipAddress, data_); + std::string sessionHash = loginHelpers::getHashFromPacket(ipAddress, buffer_.data()); if (sessionHash == "") { @@ -41,11 +41,11 @@ void data_session::read_func() session_t& session = loginHelpers::get_authenticated_session(ipAddress, sessionHash); if (!session.data_session) { - session.data_session = std::make_shared(std::forward>(socket_)); + session.data_session = std::make_shared(application_, std::forward>(socket_)); session.data_session->sessionHash = sessionHash; } - uint8 code = ref(data_, 0); + uint8 code = ref(buffer_.data(), 0); DebugSockets(fmt::format("data code: {} from {}", code, ipAddress)); switch (code) @@ -53,11 +53,11 @@ void data_session::read_func() case 0xA1: // 161 { auto maintMode = settings::get("login.MAINT_MODE"); - uint32 recievedAcccountID = ref(data_, 1); + uint32 recievedAcccountID = ref(buffer_.data(), 1); if (session.accountID == recievedAcccountID) { - session.serverIP = ref(data_, 5); + session.serverIP = ref(buffer_.data(), 5); uint32 numContentIds = 0; @@ -195,27 +195,27 @@ void data_session::read_func() // from logging in or creating new characters if (maintMode > 0 && i == 0) { - if (auto data = session.view_session.get()) + if (auto viewSession = session.view_session.get()) { - loginHelpers::generateErrorMessage(data->data_, loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); - data->do_write(0x24); + loginHelpers::generateErrorMessage(viewSession->buffer_.data(), loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); + viewSession->do_write(0x24); } ShowWarning(fmt::format("char:({}) attmpted login during maintenance mode (0xA2). Sending error to client.", session.accountID)); return; } - if (auto data = session.data_session.get()) + if (auto dataSession = session.data_session.get()) { uList[0] = 0x03; // Send character list command in xiloader uList[1] = characterInfoResponse.characters; // xiloader interprets this as the number of characters in the list - std::memset(data->data_, 0, sizeof(data_)); - std::memcpy(data->data_, uList, 0x148); + std::memset(dataSession->buffer_.data(), 0, sizeof(buffer_.data())); + std::memcpy(dataSession->buffer_.data(), uList, 0x148); - data->do_write(0x148); + dataSession->do_write(0x148); } - if (auto data = session.view_session.get()) + if (auto viewSession = session.view_session.get()) { // size of packet + 1 uint32 + the actually set number of characters uint32_t size = sizeof(packet_t) + sizeof(uint32_t) + sizeof(lpkt_chr_info_sub2) * characterInfoResponse.characters; @@ -226,9 +226,9 @@ void data_session::read_func() loginPackets::copyHashIntoPacket(characterInfoResponse, hash); - std::memset(data->data_, 0, sizeof(data_)); - std::memcpy(data->data_, &characterInfoResponse, size); - data->do_write(size); + std::memset(viewSession->buffer_.data(), 0, sizeof(buffer_.data())); + std::memcpy(viewSession->buffer_.data(), &characterInfoResponse, size); + viewSession->do_write(size); } } } @@ -237,7 +237,7 @@ void data_session::read_func() { // Some kind of magic regarding the blowfish keys uint8 key3[20] = {}; - std::memcpy(key3, data_ + 1, sizeof(key3)); + std::memcpy(key3, buffer_.data() + 1, sizeof(key3)); // https://github.com/atom0s/XiPackets/blob/main/lobby/S2C_0x000B_ResponseNextLogin.md lpkt_next_login characterSelectionResponse = {}; @@ -251,7 +251,7 @@ void data_session::read_func() { ShowWarning(fmt::format("data_session: login data corrupt (0xA2). Disconnecting client {}", ipAddress)); - loginHelpers::generateErrorMessage(data_, loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); + loginHelpers::generateErrorMessage(buffer_.data(), loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); do_write(0x24); socket_.lowest_layer().close(); return; @@ -362,11 +362,11 @@ void data_session::read_func() if (hasActiveSession) { ShowWarning(fmt::format("data_session: account {} is already logged in.", session.accountID)); - if (auto data = session.view_session.get()) + if (auto viewSession = session.view_session.get()) { // Send error message to the client. - loginHelpers::generateErrorMessage(data->data_, loginErrors::errorCode::UNABLE_TO_CONNECT_TO_WORLD_SERVER); // "Unable to connect to world server. Specified operation failed" - data->do_write(0x24); + loginHelpers::generateErrorMessage(viewSession->buffer_.data(), loginErrors::errorCode::UNABLE_TO_CONNECT_TO_WORLD_SERVER); // "Unable to connect to world server. Specified operation failed" + viewSession->do_write(0x24); return; } @@ -394,11 +394,11 @@ void data_session::read_func() session.accountID, charid, session_key, ZoneIP, ZonePort, accountIP, session.versionMismatch ? 1 : 0)) { - if (auto data = session.view_session.get()) + if (auto viewSession = session.view_session.get()) { // Send error message to the client. - loginHelpers::generateErrorMessage(data->data_, loginErrors::errorCode::UNABLE_TO_CONNECT_TO_WORLD_SERVER); // "Unable to connect to world server. Specified operation failed" - data->do_write(0x24); + loginHelpers::generateErrorMessage(viewSession->buffer_.data(), loginErrors::errorCode::UNABLE_TO_CONNECT_TO_WORLD_SERVER); // "Unable to connect to world server. Specified operation failed" + viewSession->do_write(0x24); return; } } @@ -408,22 +408,22 @@ void data_session::read_func() } else { - if (auto data = session.view_session.get()) + if (auto viewSession = session.view_session.get()) { // Send error message to the client. - loginHelpers::generateErrorMessage(data->data_, loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); - data->do_write(0x24); + loginHelpers::generateErrorMessage(viewSession->buffer_.data(), loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); + viewSession->do_write(0x24); return; } } } else { - if (auto data = session.view_session.get()) + if (auto viewSession = session.view_session.get()) { // Send error message to the client. - loginHelpers::generateErrorMessage(data->data_, loginErrors::errorCode::UNABLE_TO_CONNECT_TO_WORLD_SERVER); // "Unable to connect to world server. Specified operation failed" - data->do_write(0x24); + loginHelpers::generateErrorMessage(viewSession->buffer_.data(), loginErrors::errorCode::UNABLE_TO_CONNECT_TO_WORLD_SERVER); // "Unable to connect to world server. Specified operation failed" + viewSession->do_write(0x24); return; } } @@ -433,13 +433,13 @@ void data_session::read_func() loginPackets::copyHashIntoPacket(characterSelectionResponse, Hash); - if (auto data = session.view_session.get()) + if (auto viewSession = session.view_session.get()) { - std::memcpy(data->data_, &characterSelectionResponse, sizeof(characterSelectionResponse)); - data->do_write(sizeof(characterSelectionResponse)); + std::memcpy(viewSession->buffer_.data(), &characterSelectionResponse, sizeof(characterSelectionResponse)); + viewSession->do_write(sizeof(characterSelectionResponse)); - data->socket_.lowest_layer().shutdown(asio::socket_base::shutdown_both); // Client waits for us to close the socket - data->socket_.lowest_layer().close(); + viewSession->socket_.lowest_layer().shutdown(asio::socket_base::shutdown_both); // Client waits for us to close the socket + viewSession->socket_.lowest_layer().close(); session.view_session = nullptr; } @@ -469,9 +469,9 @@ void data_session::read_func() case 0xFE: // 254 { // Reply with nothing to keep xiloader spinning, may not be needed. - if (auto data = session.data_session.get()) + if (auto dataSession = session.data_session.get()) { - data->do_write(0); + dataSession->do_write(0); } } break; diff --git a/src/login/data_session.h b/src/login/data_session.h index bdbf34937c0..265f76d56e7 100644 --- a/src/login/data_session.h +++ b/src/login/data_session.h @@ -33,8 +33,8 @@ class data_session : public handler_session { public: - data_session(asio::ssl::stream socket) - : handler_session(std::move(socket)) + data_session(Application& application, asio::ssl::stream socket) + : handler_session(application, std::move(socket)) { DebugSockets("data_session from IP %s", ipAddress); } diff --git a/src/login/handler.h b/src/login/handler.h index 0dfe529f531..b663ede4d68 100644 --- a/src/login/handler.h +++ b/src/login/handler.h @@ -33,8 +33,9 @@ template class handler { public: - handler(asio::io_context& io_context, unsigned int port) - : acceptor_(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)) + handler(Application& application, asio::io_context& io_context, unsigned int port) + : application_(application) + , acceptor_(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)) , sslContext_(asio::ssl::context::tls_server) { acceptor_.set_option(asio::socket_base::reuse_address(true)); @@ -52,23 +53,23 @@ class handler { // clang-format off acceptor_.async_accept( - [this](std::error_code ec, asio::ip::tcp::socket socket) + [this, &application = application_](std::error_code ec, asio::ip::tcp::socket socket) { if (!ec) { if constexpr (std::is_same_v) { - auto auth_handler = std::make_shared(asio::ssl::stream(std::move(socket), sslContext_)); + auto auth_handler = std::make_shared(application, asio::ssl::stream(std::move(socket), sslContext_)); auth_handler->start(); } else if constexpr (std::is_same_v) { - auto view_handler = std::make_shared(asio::ssl::stream(std::move(socket), sslContext_)); + auto view_handler = std::make_shared(application, asio::ssl::stream(std::move(socket), sslContext_)); view_handler->start(); } else if constexpr (std::is_same_v) { - auto data_handler = std::make_shared(asio::ssl::stream(std::move(socket), sslContext_)); + auto data_handler = std::make_shared(application, asio::ssl::stream(std::move(socket), sslContext_)); data_handler->start(); } } @@ -82,6 +83,7 @@ class handler // clang-format on } + Application& application_; asio::ip::tcp::acceptor acceptor_; asio::ssl::context sslContext_; }; diff --git a/src/login/handler_session.cpp b/src/login/handler_session.cpp index 294c7b8c763..7114ffebfc5 100644 --- a/src/login/handler_session.cpp +++ b/src/login/handler_session.cpp @@ -21,8 +21,9 @@ #include "handler_session.h" -handler_session::handler_session(asio::ssl::stream socket) +handler_session::handler_session(Application& application, asio::ssl::stream socket) : socket_(std::move(socket)) +, application_(application) { asio::error_code ec = {}; socket_.lowest_layer().set_option(asio::socket_base::reuse_address(true)); @@ -58,7 +59,7 @@ void handler_session::start() void handler_session::do_read() { // clang-format off - socket_.next_layer().async_read_some(asio::buffer(data_, max_length), + socket_.next_layer().async_read_some(asio::buffer(buffer_.data(), buffer_.size()), [this, self = shared_from_this()](std::error_code ec, std::size_t length) { if (!ec) @@ -77,7 +78,7 @@ void handler_session::do_read() void handler_session::do_write(std::size_t length) { // clang-format off - asio::async_write(socket_.next_layer(), asio::buffer(data_, length), + asio::async_write(socket_.next_layer(), asio::buffer(buffer_.data(), buffer_.size()), [this, self = shared_from_this()](std::error_code ec, std::size_t /*length*/) { if (!ec) diff --git a/src/login/handler_session.h b/src/login/handler_session.h index 5778ff95608..081f2c7d962 100644 --- a/src/login/handler_session.h +++ b/src/login/handler_session.h @@ -21,27 +21,30 @@ #pragma once +#include "common/application.h" + #include #include #include #include +#include +#include + class handler_session : public std::enable_shared_from_this { public: - handler_session(asio::ssl::stream socket); - + handler_session(Application& application, asio::ssl::stream socket); virtual ~handler_session() = default; void start(); void do_read(); + void do_write(std::size_t length); virtual void handle_error(std::error_code ec, std::shared_ptr self) = 0; - void do_write(std::size_t length); - virtual void read_func() = 0; virtual void write_func() = 0; @@ -50,10 +53,7 @@ class handler_session asio::ssl::stream socket_; - // TODO: Use std::array - enum - { - max_length = 4096 - }; - char data_[max_length] = {}; + std::array buffer_; + + Application& application_; }; diff --git a/src/login/login_helpers.cpp b/src/login/login_helpers.cpp index 51e18680dd6..7ee1e926286 100644 --- a/src/login/login_helpers.cpp +++ b/src/login/login_helpers.cpp @@ -65,7 +65,7 @@ namespace loginHelpers } // https://github.com/atom0s/XiPackets/blob/main/lobby/S2C_0x0004_ResponseError.md - void generateErrorMessage(char* packet, uint16 errorCode) + void generateErrorMessage(uint8* packet, uint16 errorCode) { std::memset(packet, 0, 0x24); @@ -220,7 +220,7 @@ namespace loginHelpers return 0; } - int32 createCharacter(session_t& session, char* buf) + int32 createCharacter(session_t& session, uint8* buf) { char_mini createchar; @@ -290,9 +290,9 @@ namespace loginHelpers return 0; } - std::string getHashFromPacket(std::string const& ip_str, char* data) + std::string getHashFromPacket(std::string const& ip_str, uint8* data) { - std::string hash = std::string(data + 12, 16); + std::string hash = std::string((const char*)data + 12, 16); if (authenticatedSessions_[ip_str].find(hash) == authenticatedSessions_[ip_str].end()) { diff --git a/src/login/login_helpers.h b/src/login/login_helpers.h index 9b5ed035d71..c259cc7dcd9 100644 --- a/src/login/login_helpers.h +++ b/src/login/login_helpers.h @@ -25,7 +25,6 @@ #include #include -#include // for ref #include #include @@ -88,7 +87,7 @@ namespace loginHelpers uint32 str2ip(const char* ip_str); // https://github.com/atom0s/XiPackets/blob/main/lobby/S2C_0x0004_ResponseError.md - void generateErrorMessage(char* packet, uint16 errorCode); + void generateErrorMessage(uint8* packet, uint16 errorCode); uint16 generateExpansionBitmask(); @@ -96,9 +95,9 @@ namespace loginHelpers int32 saveCharacter(uint32 accid, uint32 charid, char_mini* createchar); - int32 createCharacter(session_t& session, char* buf); + int32 createCharacter(session_t& session, uint8* buf); - void PrintPacket(const char* data, uint32 size); + void PrintPacket(uint8* data, uint32 size); - std::string getHashFromPacket(std::string const& ip_str, char* data); + std::string getHashFromPacket(std::string const& ip_str, uint8* data); } // namespace loginHelpers diff --git a/src/login/main.cpp b/src/login/main.cpp index 4025a7b4768..56c8373ca6d 100644 --- a/src/login/main.cpp +++ b/src/login/main.cpp @@ -18,6 +18,7 @@ =========================================================================== */ + #include "connect_server.h" // openssl applink.c prevents issues with debug vs release vs threaded/single threaded .dlls at runtime @@ -26,18 +27,9 @@ #include #endif -// TODO: Standardize our running arguments for shutdown and thread signals -std::atomic gRunFlag = true; - int main(int argc, char** argv) { auto pConnectServer = std::make_unique(argc, argv); - - // TODO: We don't need this - while (pConnectServer->IsRunning()) - { - pConnectServer->Tick(); - } - + pConnectServer->run(); return 0; } diff --git a/src/login/view_session.cpp b/src/login/view_session.cpp index b7262799ff9..2d0b23deb65 100644 --- a/src/login/view_session.cpp +++ b/src/login/view_session.cpp @@ -29,9 +29,11 @@ void view_session::read_func() { - uint8 code = ref(data_, 8); + auto lua = application_.lua(); - std::string sessionHash = loginHelpers::getHashFromPacket(ipAddress, data_); + uint8 code = ref(buffer_.data(), 8); + + std::string sessionHash = loginHelpers::getHashFromPacket(ipAddress, buffer_.data()); if (sessionHash == "") { @@ -41,7 +43,7 @@ void view_session::read_func() session_t& session = loginHelpers::get_authenticated_session(ipAddress, sessionHash); if (!session.view_session) { - session.view_session = std::make_shared(std::forward>(socket_)); + session.view_session = std::make_shared(application_, std::forward>(socket_)); } session.view_session->sessionHash = sessionHash; @@ -51,9 +53,9 @@ void view_session::read_func() { case 0x07: // 07: "Notifying lobby server of current selections." { - auto requestedCharacterID = ref(data_, 28); + auto requestedCharacterID = ref(buffer_.data(), 28); char requestedCharacter[PacketNameLength] = {}; - std::memcpy(&requestedCharacter, data_ + 36, PacketNameLength - 1); + std::memcpy(&requestedCharacter, buffer_.data() + 36, PacketNameLength - 1); uint32 accountID = 0; @@ -80,8 +82,8 @@ void view_session::read_func() if (auto data = session.data_session) { - std::memset(data->data_, 0, 0x05); - data->data_[0] = 0x02; + std::memset(data->buffer_.data(), 0, 0x05); + data->buffer_.data()[0] = 0x02; data->do_write(0x05); } } @@ -90,29 +92,29 @@ void view_session::read_func() { if (!settings::get("login.CHARACTER_DELETION")) { - loginHelpers::generateErrorMessage(data_, loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); + loginHelpers::generateErrorMessage(buffer_.data(), loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); do_write(0x24); return; } - memset(data_, 0, 0x20); - data_[0] = 0x20; // size + std::memset(buffer_.data(), 0, 0x20); + buffer_.data()[0] = 0x20; // size - data_[4] = 0x49; // I - data_[5] = 0x58; // X - data_[6] = 0x46; // F - data_[7] = 0x46; // F + buffer_.data()[4] = 0x49; // I + buffer_.data()[5] = 0x58; // X + buffer_.data()[6] = 0x46; // F + buffer_.data()[7] = 0x46; // F - data_[8] = 0x03; // result + buffer_.data()[8] = 0x03; // result unsigned char hash[16]; - md5(reinterpret_cast(data_), hash, 0x20); - std::memcpy(data_ + 12, hash, 16); + md5(reinterpret_cast(buffer_.data()), hash, 0x20); + std::memcpy(buffer_.data() + 12, hash, 16); do_write(0x20); - uint32 charID = ref(data_, 0x20); + uint32 charID = ref(buffer_.data(), 0x20); ShowInfo(fmt::format("attempt to delete char:<{}> from ip:<{}>", charID, ipAddress)); @@ -143,7 +145,7 @@ void view_session::read_func() case 0x21: // 33: Registering character name onto the lobby server { // creating new char - if (loginHelpers::createCharacter(session, data_) == -1) + if (loginHelpers::createCharacter(session, buffer_.data()) == -1) { socket_.lowest_layer().close(); return; @@ -152,21 +154,21 @@ void view_session::read_func() session.justCreatedNewChar = true; ShowInfo(fmt::format("char <{}> was successfully created on account {}", session.requestedNewCharacterName, session.accountID)); - memset(data_, 0, 0x20); + std::memset(buffer_.data(), 0, 0x20); - data_[0] = 0x20; // size + buffer_.data()[0] = 0x20; // size - data_[4] = 0x49; // I - data_[5] = 0x58; // X - data_[6] = 0x46; // F - data_[7] = 0x46; // F + buffer_.data()[4] = 0x49; // I + buffer_.data()[5] = 0x58; // X + buffer_.data()[6] = 0x46; // F + buffer_.data()[7] = 0x46; // F - data_[8] = 0x03; // result + buffer_.data()[8] = 0x03; // result unsigned char hash[16]; - md5(reinterpret_cast(data_), hash, 0x20); - std::memcpy(data_ + 12, hash, 16); + md5(buffer_.data(), hash, 0x20); + std::memcpy(buffer_.data() + 12, hash, 16); do_write(0x20); } @@ -174,11 +176,11 @@ void view_session::read_func() case 0x22: // 34: Checking name and Gold World Pass { // block creation of character if in maintenance mode or generally disabled - auto maintMode = settings::get("login.MAINT_MODE"); - auto enableCharacterCreation = settings::get("login.CHARACTER_CREATION"); + const auto maintMode = settings::get("login.MAINT_MODE"); + const auto enableCharacterCreation = settings::get("login.CHARACTER_CREATION"); if (maintMode > 0 || !enableCharacterCreation) { - loginHelpers::generateErrorMessage(data_, loginErrors::errorCode::FAILED_TO_REGISTER_WITH_THE_NAME_SERVER); + loginHelpers::generateErrorMessage(buffer_.data(), loginErrors::errorCode::FAILED_TO_REGISTER_WITH_THE_NAME_SERVER); do_write(0x24); return; } @@ -186,7 +188,7 @@ void view_session::read_func() { // creating new char char CharName[PacketNameLength] = {}; - std::memcpy(CharName, data_ + 32, PacketNameLength - 1); + std::memcpy(CharName, buffer_.data() + 32, PacketNameLength - 1); std::optional invalidNameReason = std::nullopt; @@ -245,6 +247,7 @@ void view_session::read_func() // TODO: Don't raw-access Lua like this outside of Lua helper code. // (optional) Check if the name contains any words on the bad word list + auto loginSettingsTable = lua["xi"]["settings"]["login"].get(); if (auto badWordsList = loginSettingsTable.get_or("BANNED_WORDS_LIST", sol::lua_nil); badWordsList.valid()) { @@ -266,7 +269,7 @@ void view_session::read_func() // Send error code: // The character name you entered is unavailable. Please choose another name. // TODO: This message is displayed in Japanese, needs fixing. - loginHelpers::generateErrorMessage(data_, loginErrors::errorCode::CHARACTER_NAME_UNAVAILABLE); + loginHelpers::generateErrorMessage(buffer_.data(), loginErrors::errorCode::CHARACTER_NAME_UNAVAILABLE); do_write(0x24); return; } @@ -275,20 +278,20 @@ void view_session::read_func() // copy charname session.requestedNewCharacterName = CharName; - memset(data_, 0, 0x20); - data_[0] = 0x20; // size + std::memset(buffer_.data(), 0, 0x20); + buffer_.data()[0] = 0x20; // size - data_[4] = 0x49; // I - data_[5] = 0x58; // X - data_[6] = 0x46; // F - data_[7] = 0x46; // F + buffer_.data()[4] = 0x49; // I + buffer_.data()[5] = 0x58; // X + buffer_.data()[6] = 0x46; // F + buffer_.data()[7] = 0x46; // F - data_[8] = 0x03; // result + buffer_.data()[8] = 0x03; // result unsigned char hash[16]; - md5(reinterpret_cast(data_), hash, 0x20); - std::memcpy(data_ + 12, hash, 16); + md5(buffer_.data(), hash, 0x20); + std::memcpy(buffer_.data() + 12, hash, 16); do_write(0x20); } @@ -297,7 +300,7 @@ void view_session::read_func() break; case 0x26: // 38: Version + Expansions, "Setting up connection." { - std::string client_ver_data((data_ + 0x74), 6); // Full length is 10 but we drop last 4. This contains "E" in the english client. Perhaps this can be used as a hint for language? + std::string client_ver_data(reinterpret_cast(buffer_.data() + 0x74), 6); // Full length is 10 but we drop last 4. This contains "E" in the english client. Perhaps this can be used as a hint for language? client_ver_data = client_ver_data + "xx_x"; // And then we replace those last 4 DebugSockets(fmt::format("Version: {} from {}", client_ver_data, ipAddress)); @@ -340,10 +343,10 @@ void view_session::read_func() if (fatalMismatch) { - if (auto data = session.view_session.get()) + if (auto viewSession = session.view_session.get()) { - loginHelpers::generateErrorMessage(data->data_, loginErrors::errorCode::GAMES_DATA_HAS_BEEN_UPDATED); // "The games data has been updated" - data->do_write(0x24); + loginHelpers::generateErrorMessage(viewSession->buffer_.data(), loginErrors::errorCode::GAMES_DATA_HAS_BEEN_UPDATED); // "The games data has been updated" + viewSession->do_write(0x24); return; } } @@ -368,34 +371,34 @@ void view_session::read_func() ref(packet.data(), 32) = loginHelpers::generateExpansionBitmask(); ref(packet.data(), 36) = loginHelpers::generateFeatureBitmask(); - std::memset(data_, 0, 0x28); - std::memcpy(data_, packet.data(), 0x28); + std::memset(buffer_.data(), 0, 0x28); + std::memcpy(buffer_.data(), packet.data(), 0x28); // Hash the packet data and then write the value of the hash into the packet. unsigned char hash[16]; - md5(reinterpret_cast(data_), hash, 0x28); - std::memcpy(data_ + 12, hash, 16); + md5(buffer_.data(), hash, 0x28); + std::memcpy(buffer_.data() + 12, hash, 16); DebugSockets("view_session: Sending version and expansions info to account %d", session.accountID); - if (auto data = session.view_session.get()) + if (auto dataSession = session.view_session.get()) { - std::memcpy(data->data_, data_, 0x28); - data->do_write(0x28); + std::memcpy(dataSession->buffer_.data(), buffer_.data(), 0x28); + dataSession->do_write(0x28); } } break; case 0x1F: // 31: "Acquiring Player Data" { - if (auto data = session.data_session.get()) + if (auto dataSession = session.data_session.get()) { - std::memset(data->data_, 0, 5); - data->data_[0] = 0x01; - data->do_write(0x05); + std::memset(dataSession->buffer_.data(), 0, 5); + dataSession->buffer_.data()[0] = 0x01; + dataSession->do_write(0x05); } else { - loginHelpers::generateErrorMessage(data_, loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); // "Could not connect to lobby server.\nPlease check this title's news for announcements." + loginHelpers::generateErrorMessage(buffer_.data(), loginErrors::errorCode::COULD_NOT_CONNECT_TO_LOBBY_SERVER); // "Could not connect to lobby server.\nPlease check this title's news for announcements." do_write(0x24); // This used to error, but this case is probably not valid after sessionHash. // TODO: is this this else block still needed? return; } @@ -403,7 +406,7 @@ void view_session::read_func() break; case 0x24: // 36: "Acquiring FINAL FANTASY XI server data" { - std::memset(data_, 0, 0x40); + std::memset(buffer_.data(), 0, 0x40); auto serverName = settings::get("main.SERVER_NAME"); lpkt_world_list worldList = {}; @@ -419,7 +422,7 @@ void view_session::read_func() worldList.world_name[0].no = 0x20; std::memcpy(worldList.world_name[0].name, serverName.c_str(), std::clamp(serverName.length(), 0, 15)); - if (auto data = session.view_session.get()) + if (auto dataSession = session.view_session.get()) { worldList.packet_size = sizeof(packet_t) + sizeof(uint32_t) + sizeof(lpkt_world_name) * worldList.sumofworld; @@ -427,8 +430,8 @@ void view_session::read_func() md5(reinterpret_cast(&worldList), Hash, worldList.packet_size); loginPackets::copyHashIntoPacket(worldList, Hash); - std::memcpy(data->data_, &worldList, worldList.packet_size); - data->do_write(worldList.packet_size); + std::memcpy(dataSession->buffer_.data(), &worldList, worldList.packet_size); + dataSession->do_write(worldList.packet_size); } } break; diff --git a/src/login/view_session.h b/src/login/view_session.h index a71247e7ffb..dfc425a9d97 100644 --- a/src/login/view_session.h +++ b/src/login/view_session.h @@ -38,8 +38,8 @@ class view_session : public handler_session { public: - view_session(asio::ssl::stream socket) - : handler_session(std::move(socket)) + view_session(Application& application, asio::ssl::stream socket) + : handler_session(application, std::move(socket)) { DebugSockets("view_session from IP %s", ipAddress); } diff --git a/src/map/lua/luautils.cpp b/src/map/lua/luautils.cpp index 300c28f5c69..b59f5e58955 100644 --- a/src/map/lua/luautils.cpp +++ b/src/map/lua/luautils.cpp @@ -638,7 +638,7 @@ namespace luautils ShowInfo("[FileWatcher] RELOADING ALL LUA SETTINGS FILES"); - settings::init(); + settings::init(lua_); return; } diff --git a/src/common/kernel.h b/src/map/main.cpp similarity index 64% rename from src/common/kernel.h rename to src/map/main.cpp index 4c7967476f7..bd52a744855 100644 --- a/src/common/kernel.h +++ b/src/map/main.cpp @@ -1,7 +1,7 @@ /* =========================================================================== - Copyright (c) 2010-2015 Darkstar Dev Teams + Copyright (c) 2025 LandSandBoat Dev Teams This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,21 +19,11 @@ =========================================================================== */ -#ifndef _KERNEL_H_ -#define _KERNEL_H_ +#include "map_server.h" -#include "cbasetypes.h" -#include "console_service.h" -#include "settings.h" - -extern std::atomic gRunFlag; - -extern void log_init(int, char**); -extern int32 do_init(int32, char**); -extern void set_socket_type(void); -extern void do_abort(void); -extern void do_final(int); - -extern std::unique_ptr gConsoleService; - -#endif // _KERNEL_H_ +int main(int argc, char** argv) +{ + auto pMapServer = std::make_unique(argc, argv); + pMapServer->run(); + return 0; +} diff --git a/src/map/map_constants.h b/src/map/map_constants.h new file mode 100644 index 00000000000..b17b314253e --- /dev/null +++ b/src/map/map_constants.h @@ -0,0 +1,24 @@ +/* +=========================================================================== + + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +static constexpr std::size_t MAX_BUFFER_SIZE = 1024; diff --git a/src/map/map_network_handler.cpp b/src/map/map_network_handler.cpp new file mode 100644 index 00000000000..b666a00784d --- /dev/null +++ b/src/map/map_network_handler.cpp @@ -0,0 +1,22 @@ +/* +=========================================================================== + + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once diff --git a/src/map/map_network_handler.h b/src/map/map_network_handler.h new file mode 100644 index 00000000000..c804ed71c8a --- /dev/null +++ b/src/map/map_network_handler.h @@ -0,0 +1,125 @@ +/* +=========================================================================== + + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "asio.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Session +{ + uint64_t ipp; +}; + +static constexpr std::size_t MAX_BUFFER_SIZE = 1024; + +using asio::ip::udp; + +class MapNetworkHandler : public std::enable_shared_from_this +{ +public: + NetworkHandler(asio::io_context& ioContext, unsigned short port) + : socket(ioContext, udp::endpoint(udp::v4(), port)) + { + } + + void queue_async_recv() + { + socket.async_receive_from( + asio::buffer(buffer), // Reused for each receive. + remoteEndpoint, // Reused for each receive. + [self = shared_from_this()](const asio::error_code& ec, std::size_t bytesRecvd) + { + self->handleReceive(ec, bytesRecvd); + }); + } + + // Process network events for the given duration. + void run_for(duration d) + { + socket.get_executor().context().run_for(d); + } + +private: + // TODO: This should exist outside? + std::shared_ptr lookupSession(uint64_t ipp) + { + auto it = sessions.find(ipp); + if (it != sessions.end()) + { + return it->second; + } + auto session = std::make_shared(); + session->ipp = ipp; + sessions[ipp] = session; + return session; + } + + void handleReceive(const asio::error_code& ec, std::size_t bytesRecvd) + { + if (!ec && bytesRecvd > 0) + { + std::cout << "Received " << bytesRecvd << " bytes from " + << remoteEndpoint.address().to_string() << ":" + << remoteEndpoint.port() << std::endl; + + auto ip = remoteEndpoint.address().to_v4().to_ulong(); + uint16_t port = remoteEndpoint.port(); + uint64_t ipp = ip; + ipp |= (static_cast(port) << 32); + + auto session = lookupSession(ipp); + if (session) + { + std::cout << "Session found/created for ipp: " << ipp << std::endl; + std::string data(buffer.data(), bytesRecvd); + std::cout << "Data: " << data << std::endl; + } + } + else if (ec) + { + std::cerr << "Receive error: " << ec.message() << std::endl; + } + + // Queue the next asynchronous receive. + queue_async_recv(); + } + + using SessionMap = std::unordered_map>; + using Buffer = std::array; + + udp::socket socket; + udp::endpoint remoteEndpoint; // Reused for each receive. + Buffer buffer; // Reused for each receive. + + SessionMap sessions; +}; diff --git a/src/map/map.h b/src/map/map_server.cpp similarity index 59% rename from src/map/map.h rename to src/map/map_server.cpp index 7c764bf032c..ebaa98a4378 100644 --- a/src/map/map.h +++ b/src/map/map_server.cpp @@ -1,7 +1,7 @@ /* =========================================================================== - Copyright (c) 2010-2015 Darkstar Dev Teams + Copyright (c) 2025 LandSandBoat Dev Teams This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,31 +19,7 @@ =========================================================================== */ -#ifndef _MAP_H -#define _MAP_H - -#include "common/cbasetypes.h" - -#include "common/blowfish.h" -#include "common/kernel.h" -#include "common/md52.h" -#include "common/mmo.h" -#include "common/socket.h" -#include "common/sql.h" -#include "common/taskmgr.h" -#include "common/xirand.h" - -#include -#include - -#include "command_handler.h" -#include "zone.h" - -/************************************************************************ - * * - * Map's working session * - * * - ************************************************************************/ +#pragma once struct map_session_data_t { @@ -92,42 +68,10 @@ struct map_session_data_t } }; -extern uint32 map_amntplayers; -extern int32 map_fd; - -// 2.5 updates per second -static constexpr float server_tick_rate = 2.5f; - -// Update every 400ms -static constexpr float server_tick_interval = 1000.0f / server_tick_rate; - -// Check Trigger Areas every 200ms -static constexpr float server_trigger_area_interval = server_tick_interval / 2.0f; - typedef std::map map_session_list_t; extern map_session_list_t map_session_list; -extern in_addr map_ip; -extern uint16 map_port; - extern inline map_session_data_t* mapsession_getbyipp(uint64 ipp); extern inline map_session_data_t* mapsession_createsession(uint32 ip, uint16 port); -extern std::unique_ptr _sql; - -extern bool gLoadAllLua; - -//======================================================================= - -int32 recv_parse(int8* buff, size_t* buffsize, sockaddr_in* from, map_session_data_t*); // main function to parse recv packets -int32 parse(int8* buff, size_t* buffsize, sockaddr_in* from, map_session_data_t*); // main function parsing the packets -int32 send_parse(int8* buff, size_t* buffsize, sockaddr_in* from, map_session_data_t*, bool usePreviousKey); // main function is building big packet - -void map_helpscreen(int32 flag); - -int32 map_cleanup(time_point tick, CTaskMgr::CTask* PTask); // Clean up timed out players int32 map_close_session(time_point tick, map_session_data_t* map_session_data); - -int32 map_garbage_collect(time_point tick, CTaskMgr::CTask* PTask); - -#endif //_MAP_H diff --git a/src/map/map_server.h b/src/map/map_server.h new file mode 100644 index 00000000000..be16b7a04a7 --- /dev/null +++ b/src/map/map_server.h @@ -0,0 +1,70 @@ +/* +=========================================================================== + + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "common/application.h" + +class MapServer final : public Application +{ +public: + MapServer(int argc, char** argv) + : Application("map", argc, argv) + , scheduler(ioContext_) + , networkHandler(ioContext_, Application::getPort()) // Or something? + { + } + + void run() + { + // Queue the first async network receive. + networkHandler_->queue_async_recv(); + + // TODO: Make atomic, hide in Application:: functions. + bool running = true; + + while (running) + { + const auto now = std::chrono::steady_clock::now(); + + // Process scheduler tasks; run() returns a timeout duration. + const auto next = scheduler.run(now); + + // Process network events for that duration. + networkHandler->run_for(next); + + // If input is available (e.g., Enter pressed), exit. + if (std::cin.rdbuf()->in_avail() > 0) + { + running = false; + } + } + } + +private: + // TODO: This should be part of Application at this point. + asio::io_context ioContext_; + + Scheduler scheduler_; + NetworkHandler networkHandler_; + MapState mapState_; + MapSessions mapSessions_; // Should this be in networkHandler or mapState instead? +}; diff --git a/src/map/map_session.cpp b/src/map/map_session.cpp new file mode 100644 index 00000000000..dab359c5c56 --- /dev/null +++ b/src/map/map_session.cpp @@ -0,0 +1,74 @@ +/* +=========================================================================== + + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "map_session.h" + +map_session_data_t* mapsession_getbyipp(uint64 ipp) +{ + TracyZoneScoped; + map_session_list_t::iterator i = map_session_list.begin(); + while (i != map_session_list.end()) + { + if ((*i).first == ipp) + { + return (*i).second; + } + ++i; + } + return nullptr; +} + +map_session_data_t* mapsession_createsession(uint32 ip, uint16 port) +{ + TracyZoneScoped; + + const auto ipstr = ip2str(ip); + + const auto rset = db::preparedStmt("SELECT charid FROM accounts_sessions WHERE inet_ntoa(client_addr) = ? LIMIT 1", ipstr); + + if (rset == nullptr) + { + ShowError("SQL query failed in mapsession_createsession!"); + return nullptr; + } + + if (rset->rowsCount() == 0) + { + // This is noisy and not really necessary + DebugSockets(fmt::format("recv_parse: Invalid login attempt from {}", ipstr)); + return nullptr; + } + + map_session_data_t* map_session_data = new map_session_data_t(); + + map_session_data->server_packet_data = new int8[MAX_BUFFER_SIZE + 20]; + + map_session_data->last_update = time(nullptr); + map_session_data->client_addr = ip; + map_session_data->client_port = port; + + uint64 port64 = port; + uint64 ipp = ip; + ipp |= port64 << 32; + map_session_list[ipp] = map_session_data; + + return map_session_data; +} diff --git a/src/map/map_session.h b/src/map/map_session.h new file mode 100644 index 00000000000..ebaa98a4378 --- /dev/null +++ b/src/map/map_session.h @@ -0,0 +1,77 @@ +/* +=========================================================================== + + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +struct map_session_data_t +{ + uint32 client_addr = 0; + uint16 client_port = 0; + uint16 client_packet_id = 0; // id of the last packet that came from the client + uint16 server_packet_id = 0; // id of the last packet sent by the server + int8* server_packet_data = nullptr; // a pointer to the packet, which was previously sent to the client + size_t server_packet_size = 0; // the size of the packet that was previously sent to the client + time_t last_update = 0; // time of last packet recv + blowfish_t blowfish = {}; // unique decypher keys, these are the currently expected keys + CCharEntity* PChar = nullptr; // game char + uint8 shuttingDown = 0; // prevents double session closing + uint32 charID = 0; + + // Store old blowfish data, when a player recieves 0x00B their key should increment + // If it doesn't, and we can still successfully decrypt here, that means we need to resend 0x00B. + blowfish_t prev_blowfish = {}; + + // Used to resend 0x00B zoneout packet in case the client needs it + uint8 zone_type = 0; + uint32 zone_ipp = 0; + + void incrementBlowfish() + { + prev_blowfish = blowfish; + + blowfish.key[4] += 2; + + initBlowfish(); + } + + void initBlowfish() + { + md5((uint8*)(blowfish.key), blowfish.hash, 20); + + for (uint32 i = 0; i < 16; ++i) + { + if (blowfish.hash[i] == 0) + { + std::memset(blowfish.hash + i, 0, 16 - i); + break; + } + } + blowfish_init((int8*)blowfish.hash, 16, blowfish.P, blowfish.S[0]); + } +}; + +typedef std::map map_session_list_t; +extern map_session_list_t map_session_list; + +extern inline map_session_data_t* mapsession_getbyipp(uint64 ipp); +extern inline map_session_data_t* mapsession_createsession(uint32 ip, uint16 port); + +int32 map_close_session(time_point tick, map_session_data_t* map_session_data); diff --git a/src/map/map.cpp b/src/map/map_state.cpp similarity index 88% rename from src/map/map.cpp rename to src/map/map_state.cpp index 08c16bf83b1..2b3eea59c95 100644 --- a/src/map/map.cpp +++ b/src/map/map_state.cpp @@ -2,6 +2,7 @@ =========================================================================== Copyright (c) 2010-2015 Darkstar Dev Teams + Copyright (c) 2025 LandSandBoat Dev Teams This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,99 +20,7 @@ =========================================================================== */ -#include "map.h" - -#include "common/async.h" -#include "common/blowfish.h" -#include "common/console_service.h" -#include "common/database.h" -#include "common/debug.h" -#include "common/logging.h" -#include "common/timer.h" -#include "common/utils.h" -#include "common/vana_time.h" -#include "common/version.h" -#include "common/zlib.h" - -#include "ability.h" -#include "daily_system.h" -#include "job_points.h" -#include "latent_effect_container.h" -#include "linkshell.h" -#include "message.h" -#include "mob_spell_list.h" -#include "monstrosity.h" -#include "packet_guard.h" -#include "packet_system.h" -#include "roe.h" -#include "spell.h" -#include "status_effect_container.h" -#include "time_server.h" -#include "transport.h" -#include "zone.h" -#include "zone_entities.h" - -#include "ai/controllers/automaton_controller.h" - -#include "items/item_equipment.h" - -#include "packets/basic.h" -#include "packets/chat_message.h" -#include "packets/server_ip.h" - -#include "utils/battleutils.h" -#include "utils/charutils.h" -#include "utils/fishingutils.h" -#include "utils/gardenutils.h" -#include "utils/guildutils.h" -#include "utils/instanceutils.h" -#include "utils/itemutils.h" -#include "utils/mobutils.h" -#include "utils/moduleutils.h" -#include "utils/petutils.h" -#include "utils/serverutils.h" -#include "utils/synergyutils.h" -#include "utils/synthutils.h" -#include "utils/trustutils.h" -#include "utils/zoneutils.h" - -#include -#include -#include -#include -#include - -#include - -#ifdef WIN32 -#include -#endif - -const char* MAP_CONF_FILENAME = nullptr; - -int8* g_PBuff = nullptr; // Global packet clipboard -int8* g_PBuffCopy = nullptr; // Copy of above, used to decrypt a second time if necessary. -int8* PTempBuff = nullptr; // Temporary packet clipboard - -int32 map_fd = 0; // main socket -uint32 map_amntplayers = 0; // map amnt unique players - -in_addr map_ip = {}; -uint16 map_port = 0; - -map_session_list_t map_session_list = {}; - -nonstd::jthread messageThread; - -std::unique_ptr _sql; - -extern std::map g_PZoneList; // Global array of pointers for zones - -bool gLoadAllLua = false; - -std::unordered_map>>> PacketMods; - -extern std::atomic gProcessLoaded; +#include "map_state.h" namespace { @@ -124,76 +33,6 @@ namespace uint32 TotalPacketsDelayedPerTick = 0U; } // namespace -/************************************************************************ - * * - * mapsession_getbyipp * - * * - ************************************************************************/ - -map_session_data_t* mapsession_getbyipp(uint64 ipp) -{ - TracyZoneScoped; - map_session_list_t::iterator i = map_session_list.begin(); - while (i != map_session_list.end()) - { - if ((*i).first == ipp) - { - return (*i).second; - } - ++i; - } - return nullptr; -} - -/************************************************************************ - * * - * mapsession_createsession * - * * - ************************************************************************/ - -map_session_data_t* mapsession_createsession(uint32 ip, uint16 port) -{ - TracyZoneScoped; - - const auto ipstr = ip2str(ip); - - const auto rset = db::preparedStmt("SELECT charid FROM accounts_sessions WHERE inet_ntoa(client_addr) = ? LIMIT 1", ipstr); - - if (rset == nullptr) - { - ShowError("SQL query failed in mapsession_createsession!"); - return nullptr; - } - - if (rset->rowsCount() == 0) - { - // This is noisy and not really necessary - DebugSockets(fmt::format("recv_parse: Invalid login attempt from {}", ipstr)); - return nullptr; - } - - map_session_data_t* map_session_data = new map_session_data_t(); - - map_session_data->server_packet_data = new int8[MAX_BUFFER_SIZE + 20]; - - map_session_data->last_update = time(nullptr); - map_session_data->client_addr = ip; - map_session_data->client_port = port; - - uint64 port64 = port; - uint64 ipp = ip; - ipp |= port64 << 32; - map_session_list[ipp] = map_session_data; - - return map_session_data; -} - -/************************************************************************ - * * - * do_init * - * * - ************************************************************************/ - int32 do_init(int32 argc, char** argv) { TracyZoneScoped; @@ -402,7 +241,7 @@ int32 do_init(int32 argc, char** argv) [&](std::vector& inputs) { fmt::print("Reloading settings files\n"); - settings::init(); + settings::init(lua_); }); gConsoleService->RegisterCommand("reload_recipes", "Reload crafting recipes.", @@ -417,7 +256,6 @@ int32 do_init(int32 argc, char** argv) { fmt::print("> Goodbye!\n"); gConsoleService->stop(); - gRunFlag = false; }); // clang-format on @@ -430,11 +268,6 @@ int32 do_init(int32 argc, char** argv) return 0; } -/************************************************************************ - * * - * do_final * - * * - ************************************************************************/ void do_final(int code) { TracyZoneScoped; @@ -489,23 +322,11 @@ void do_final(int code) } } -/************************************************************************ - * * - * do_abort * - * * - ************************************************************************/ - void do_abort() { do_final(EXIT_FAILURE); } -/************************************************************************ - * * - * set_socket_type * - * * - ************************************************************************/ - void set_socket_type() { SOCKET_TYPE = socket_type::UDP; diff --git a/src/map/map_state.h b/src/map/map_state.h new file mode 100644 index 00000000000..645a15ffe34 --- /dev/null +++ b/src/map/map_state.h @@ -0,0 +1,65 @@ +/* +=========================================================================== + + Copyright (c) 2010-2015 Darkstar Dev Teams + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#ifndef _MAP_H +#define _MAP_H + +#include "common/cbasetypes.h" + +#include "common/blowfish.h" +#include "common/kernel.h" +#include "common/md52.h" +#include "common/mmo.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/taskmgr.h" +#include "common/xirand.h" + +#include +#include + +#include "command_handler.h" +#include "zone.h" + +class MapState final +{ +public: + +private: + // Legacy SQL connection + // TODO: Remove me and use db::preparedStmt everywhere + std::unique_ptr sql_; + + // std::map> mapSessions_; + + // ip/port information + extern in_addr map_ip; + extern uint16 map_port; + + // Garbage collection & other misc tasks + int32 map_garbage_collect(time_point tick, CTaskMgr::CTask* PTask); + int32 map_cleanup(time_point tick, CTaskMgr::CTask* PTask); // Clean up timed out players + + // Debug things like "gLoadAllLua" + + // Platform platform() +}; diff --git a/src/map/packets/basic.h b/src/map/packets/basic.h index 603db2c149e..11de2d31e1e 100644 --- a/src/map/packets/basic.h +++ b/src/map/packets/basic.h @@ -23,7 +23,6 @@ #define _BASICPACKET_H #include "common/cbasetypes.h" -#include "common/socket.h" #include "common/tracy.h" #include diff --git a/src/search/CMakeLists.txt b/src/search/CMakeLists.txt index 1914ddf28c3..c85e07d3933 100644 --- a/src/search/CMakeLists.txt +++ b/src/search/CMakeLists.txt @@ -13,14 +13,6 @@ set(SOURCES search_server.cpp ) -# Temporarily remove clashing old code -# TODO: Remove this block -list(REMOVE_ITEM SOURCES - ${CMAKE_SOURCE_DIR}/src/common/kernel.cpp - ${CMAKE_SOURCE_DIR}/src/common/kernel.h - ${CMAKE_SOURCE_DIR}/src/common/socket.cpp - ${CMAKE_SOURCE_DIR}/src/common/socket.h) - if(UNIX) set(resource "") else() diff --git a/src/search/main.cpp b/src/search/main.cpp index e79699abb0f..248dc85be9e 100644 --- a/src/search/main.cpp +++ b/src/search/main.cpp @@ -18,13 +18,12 @@ =========================================================================== */ -#include "search_server.h" -// TODO: Standardize our running arguments for shutdown and thread signals -std::atomic gRunFlag = true; +#include "search_server.h" int main(int argc, char** argv) { auto pSearchServer = std::make_unique(argc, argv); + pSearchServer->run(); return 0; } diff --git a/src/search/packets/auction_history.cpp b/src/search/packets/auction_history.cpp index 5ba2560a957..d8f5a0eb97a 100644 --- a/src/search/packets/auction_history.cpp +++ b/src/search/packets/auction_history.cpp @@ -21,7 +21,6 @@ #include #include "common/logging.h" -#include "common/socket.h" #include "data_loader.h" diff --git a/src/search/packets/auction_list.cpp b/src/search/packets/auction_list.cpp index 12095ea5316..3df935545e9 100644 --- a/src/search/packets/auction_list.cpp +++ b/src/search/packets/auction_list.cpp @@ -21,7 +21,6 @@ #include #include "common/logging.h" -#include "common/socket.h" #include "data_loader.h" diff --git a/src/search/packets/linkshell_list.cpp b/src/search/packets/linkshell_list.cpp index dc301f16091..af25fd8f15c 100644 --- a/src/search/packets/linkshell_list.cpp +++ b/src/search/packets/linkshell_list.cpp @@ -20,7 +20,6 @@ */ #include "common/logging.h" -#include "common/socket.h" #include "common/utils.h" #include "data_loader.h" diff --git a/src/search/packets/party_list.cpp b/src/search/packets/party_list.cpp index 0899a9e77eb..6009cd9dea1 100644 --- a/src/search/packets/party_list.cpp +++ b/src/search/packets/party_list.cpp @@ -20,7 +20,6 @@ */ #include "common/logging.h" -#include "common/socket.h" #include "common/utils.h" #include "data_loader.h" diff --git a/src/search/packets/search_comment.cpp b/src/search/packets/search_comment.cpp index ccf4a54f5d7..fdece1a2cb3 100644 --- a/src/search/packets/search_comment.cpp +++ b/src/search/packets/search_comment.cpp @@ -2,37 +2,26 @@ #include "search_comment.h" #include "common/logging.h" -#include "common/socket.h" #include "common/utils.h" SearchCommentPacket::SearchCommentPacket(uint32 playerId, std::string const& comment) { - ref(data, 0x08) = 154; // Search comment packet size - ref(data, 0x0A) = 0x80; // Search server packet - ref(data, 0x0B) = 0x88; // Packet type + ref(buffer_.data(), 0x08) = 154; // Search comment packet size + ref(buffer_.data(), 0x0A) = 0x80; // Search server packet + ref(buffer_.data(), 0x0B) = 0x88; // Packet type - ref(data, 0x0E) = 0x01; + ref(buffer_.data(), 0x0E) = 0x01; - ref(data, 0x18) = playerId; + ref(buffer_.data(), 0x18) = playerId; - ref(data, 0x1C) = 124; // Comment length + ref(buffer_.data(), 0x1C) = 124; // Comment length // Add comment bytes - memcpy(&data[0x1E], comment.c_str(), comment.length()); + std::memcpy(&buffer_.data()[0x1E], comment.c_str(), comment.length()); // Fill rest with whitespace - memset(&data[0x1E + comment.length()], ' ', 123 - comment.length()); + std::memset(&buffer_.data()[0x1E + comment.length()], ' ', 123 - comment.length()); // End comment with 0 byte - data[0x9A] = 0; -} - -uint8* SearchCommentPacket::GetData() -{ - return data; -} - -uint16 SearchCommentPacket::GetSize() -{ - return 204; + buffer_.data()[0x9A] = 0; } diff --git a/src/search/packets/search_comment.h b/src/search/packets/search_comment.h index 7f8dffae613..cb33296f854 100644 --- a/src/search/packets/search_comment.h +++ b/src/search/packets/search_comment.h @@ -3,6 +3,8 @@ #define _SEARCH_COMMENT_PACKET_H_ #include "common/cbasetypes.h" + +#include #include class SearchCommentPacket @@ -10,11 +12,8 @@ class SearchCommentPacket public: SearchCommentPacket(uint32 playerId, std::string const& comment); - uint8* GetData(); - uint16 GetSize(); - private: - uint8 data[204]{}; + std::array buffer_{}; }; #endif diff --git a/src/search/packets/search_list.cpp b/src/search/packets/search_list.cpp index a00bb38791c..f344cb38fc2 100644 --- a/src/search/packets/search_list.cpp +++ b/src/search/packets/search_list.cpp @@ -20,7 +20,6 @@ */ #include "common/logging.h" -#include "common/socket.h" #include "common/utils.h" #include "data_loader.h" diff --git a/src/search/search_handler.cpp b/src/search/search_handler.cpp index d93f6892383..37527d2f5ed 100644 --- a/src/search/search_handler.cpp +++ b/src/search/search_handler.cpp @@ -21,8 +21,7 @@ #include "search_handler.h" #include "common/md52.h" -#include "common/socket.h" // for ref -#include "common/utils.h" // for unpack/pack bits +#include "common/utils.h" // for unpack/pack bits #include "data_loader.h" #include #include diff --git a/src/search/search_handler.h b/src/search/search_handler.h index cc8ea6058fc..76a77fef229 100644 --- a/src/search/search_handler.h +++ b/src/search/search_handler.h @@ -48,28 +48,22 @@ class search_handler { public: search_handler(asio::ip::tcp::socket socket, asio::io_context& io_context, shared_guarded>& IPAddressesInUseList, shared_guarded>& IPAddressWhitelist); - ~search_handler(); void start(); void do_read(); + void do_write(); void handle_error(std::error_code ec, std::shared_ptr self); - void do_write(); - void read_func(uint16_t length); +protected: std::string ipAddress; // Store IP address in class -- once the file handle is invalid this can no longer be obtained from socket_ asio::ip::tcp::socket socket_; - // TODO: Use std::array - enum - { - max_length = 4096 - }; - uint8_t data_[max_length] = {}; + std::array buffer_; // Blowfish key // clang-format off diff --git a/src/search/search_server.cpp b/src/search/search_server.cpp index 8dc86bde22e..f050e95ed34 100644 --- a/src/search/search_server.cpp +++ b/src/search/search_server.cpp @@ -25,8 +25,6 @@ SearchServer::SearchServer(int argc, char** argv) : Application("search", argc, argv) { - asio::io_context io_context; - // clang-format off gConsoleService->RegisterCommand( "ah_cleanup", fmt::format("AH task to return items older than {} days.", settings::get("search.EXPIRE_DAYS")), @@ -47,9 +45,9 @@ SearchServer::SearchServer(int argc, char** argv) [&](std::vector& inputs) { fmt::print("> Goodbye!"); - m_RequestExit = true; - io_context.stop(); - gConsoleService->stop(); + requestExit_ = true; + ioContext_.stop(); + consoleService_->stop(); }); // clang-format on @@ -69,71 +67,38 @@ SearchServer::SearchServer(int argc, char** argv) } } #endif - xirand::seed(); - - try - { - ShowInfo("creating ports"); - - // clang-format off - const auto search_handler_handler = handler(io_context, settings::get("network.SEARCH_PORT"), [&](asio::ip::tcp::socket socket) { - const auto handler = std::make_shared(std::move(socket), io_context, IPAddressesInUse_, IPAddressWhitelist_); - handler->start(); - }); - // clang-format on - // AH cleanup callback. May not be used if settings doesn't enable it. - asio::steady_timer cleanup_callback(io_context, std::chrono::seconds(settings::get("search.EXPIRE_INTERVAL"))); + ShowInfo("creating ports"); - if (settings::get("search.EXPIRE_AUCTIONS")) - { - ShowInfo("AH task to return items older than %u days is running", settings::get("search.EXPIRE_DAYS")); - - ahCleanup(); - - cleanup_callback.async_wait(std::bind(&SearchServer::periodicCleanup, this, std::placeholders::_1, &cleanup_callback)); - } + // clang-format off + const auto search_handler_handler = handler(io_context, settings::get("network.SEARCH_PORT"), [&](asio::ip::tcp::socket socket) { + const auto handler = std::make_shared(std::move(socket), io_context, IPAddressesInUse_, IPAddressWhitelist_); + handler->start(); + }); + // clang-format on - sol::table accessWhitelist = lua["xi"]["settings"]["search"]["ACCESS_WHITELIST"].get_or_create(); - for (auto const& [_, value] : accessWhitelist) - { - // clang-format off - auto str = value.as(); - IPAddressWhitelist_.write([str](auto& ipWhitelist) - { - ipWhitelist.insert(str); - }); - // clang-format on - } + // AH cleanup callback. May not be used if settings doesn't enable it. + asio::steady_timer cleanup_callback(io_context, std::chrono::seconds(settings::get("search.EXPIRE_INTERVAL"))); - // NOTE: io_context.run() takes over and blocks this thread. Anything after this point will only fire - // if io_context finishes! - ShowInfo("starting io_context"); + if (settings::get("search.EXPIRE_AUCTIONS")) + { + ShowInfo("AH task to return items older than %u days is running", settings::get("search.EXPIRE_DAYS")); - // This busy loop looks nasty, however -- - // https://think-async.com/Asio/asio-1.24.0/doc/asio/reference/io_service.html - /* If an exception is thrown from a handler, the exception is allowed to propagate through the throwing thread's invocation of - run(), run_one(), run_for(), run_until(), poll() or poll_one(). No other threads that are calling any of these functions are affected. - It is then the responsibility of the application to catch the exception. - */ + ahCleanup(); - while (Application::IsRunning()) - { - try - { - io_context.run(); - break; - } - catch (std::exception& e) - { - // TODO: make a list of "allowed exceptions", the rest can/should cause shutdown. - ShowError(fmt::format("Inner fatal: {}", e.what())); - } - } + cleanup_callback.async_wait(std::bind(&SearchServer::periodicCleanup, this, std::placeholders::_1, &cleanup_callback)); } - catch (std::exception& e) + + sol::table accessWhitelist = lua["xi"]["settings"]["search"]["ACCESS_WHITELIST"].get_or_create(); + for (auto const& [_, value] : accessWhitelist) { - ShowError(fmt::format("Outer fatal: {}", e.what())); + // clang-format off + auto str = value.as(); + IPAddressWhitelist_.write([str](auto& ipWhitelist) + { + ipWhitelist.insert(str); + }); + // clang-format on } } @@ -149,7 +114,7 @@ void SearchServer::periodicCleanup(const asio::error_code& error, asio::steady_t { ahCleanup(); - if (Application::IsRunning()) + if (Application::isRunning()) { // reset timer timer->expires_at(timer->expiry() + std::chrono::seconds(settings::get("search.EXPIRE_INTERVAL"))); diff --git a/src/search/search_server.h b/src/search/search_server.h index e567eda1f14..4a6871512b2 100644 --- a/src/search/search_server.h +++ b/src/search/search_server.h @@ -46,22 +46,9 @@ class SearchServer final : public Application public: SearchServer(int argc, char** argv); - ~SearchServer() override - { - // Everything should be handled with RAII - } - void periodicCleanup(const asio::error_code& error, asio::steady_timer* timer); - void ahCleanup(); - // TODO: Currently never called. Need io_context asio::steady_timer callback with taskmgr to control timing? - void Tick() override - { - Application::Tick(); - - // Search Server specific things - } private: // A single IP should only have one request in flight at a time, so we are going to // be tracking the IP addresses of incoming requests and if we haven't cleared the diff --git a/src/world/CMakeLists.txt b/src/world/CMakeLists.txt index 81fa819669d..5d95f2c96a4 100644 --- a/src/world/CMakeLists.txt +++ b/src/world/CMakeLists.txt @@ -22,14 +22,6 @@ set(SOURCES world_server.h ) -# Temporarily remove clashing old code -# TODO: Remove this block -list(REMOVE_ITEM SOURCES - ${CMAKE_SOURCE_DIR}/src/common/kernel.cpp - ${CMAKE_SOURCE_DIR}/src/common/kernel.h - ${CMAKE_SOURCE_DIR}/src/common/socket.cpp - ${CMAKE_SOURCE_DIR}/src/common/socket.h) - if(UNIX) set(resource "") else() diff --git a/src/world/main.cpp b/src/world/main.cpp index 2e115fe72ba..32564810c1e 100644 --- a/src/world/main.cpp +++ b/src/world/main.cpp @@ -21,17 +21,9 @@ #include "world_server.h" -// TODO: Standardize our running arguments for shutdown and thread signals -std::atomic gRunFlag = true; - int main(int argc, char** argv) { auto pWorldServer = std::make_unique(argc, argv); - - while (pWorldServer->IsRunning()) - { - pWorldServer->Tick(); - } - + pWorldServer->run(); return 0; }