diff --git a/src/client/component/console.cpp b/src/client/component/console.cpp index 557f47b5..0d887199 100644 --- a/src/client/component/console.cpp +++ b/src/client/component/console.cpp @@ -21,6 +21,12 @@ namespace console using message_queue = std::queue; utils::concurrency::container messages; + bool native_console() + { + static const auto flag = utils::flags::has_flag("nativeconsole"); + return flag; + } + void hide_console() { auto* const con_window = GetConsoleWindow(); @@ -28,11 +34,9 @@ namespace console DWORD process; GetWindowThreadProcessId(con_window, &process); - if (process == GetCurrentProcessId() || IsDebuggerPresent()) + if (!native_console() && (process == GetCurrentProcessId() || IsDebuggerPresent())) { -#ifndef NATIVE_CONSOLE ShowWindow(con_window, SW_HIDE); -#endif } } @@ -48,6 +52,12 @@ namespace console void dispatch_message(const int type, const std::string& message) { + if (native_console()) + { + printf("%s\n", message.data()); + return; + } + game_console::print(type, message); messages.access([&message](message_queue& msgs) { @@ -68,14 +78,17 @@ namespace console { hide_console(); -#ifdef NATIVE_CONSOLE - setvbuf(stdout, nullptr, _IONBF, 0); - setvbuf(stderr, nullptr, _IONBF, 0); -#else - (void)_pipe(this->handles_, 1024, _O_TEXT); - (void)_dup2(this->handles_[1], 1); - (void)_dup2(this->handles_[1], 2); -#endif + if (native_console()) + { + setvbuf(stdout, nullptr, _IONBF, 0); + setvbuf(stderr, nullptr, _IONBF, 0); + } + else + { + (void)_pipe(this->handles_, 1024, _O_TEXT); + (void)_dup2(this->handles_[1], 1); + (void)_dup2(this->handles_[1], 2); + } } void post_start() override @@ -84,7 +97,14 @@ namespace console this->console_runner_ = utils::thread::create_named_thread("Console IO", [this] { - this->runner(); + if (native_console()) + { + this->native_input(); + } + else + { + this->runner(); + } }); } @@ -137,11 +157,9 @@ namespace console { this->console_thread_ = utils::thread::create_named_thread("Console", [this]() { - if (game::environment::is_dedi() || !utils::flags::has_flag("noconsole")) + if (!native_console() && (game::environment::is_dedi() || !utils::flags::has_flag("noconsole"))) { -#ifndef NATIVE_CONSOLE game::Sys_ShowConsole(); -#endif } if (!game::environment::is_dedi()) @@ -231,6 +249,19 @@ namespace console std::this_thread::yield(); } + + void native_input() + { + std::string cmd; + + while (true) + { + std::getline(std::cin, cmd); + command::execute(cmd); + } + + std::this_thread::yield(); + } }; HWND get_window() diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp new file mode 100644 index 00000000..6e63f082 --- /dev/null +++ b/src/client/component/dedicated.cpp @@ -0,0 +1,336 @@ +#include +#include "loader/component_loader.hpp" +#include "scheduler.hpp" +#include "server_list.hpp" +#include "network.hpp" +#include "command.hpp" +#include "game/game.hpp" +#include "game/dvars.hpp" +#include "dvars.hpp" +#include "console.hpp" + +#include +#include + +namespace dedicated +{ + namespace + { + utils::hook::detour gscr_set_dynamic_dvar_hook; + utils::hook::detour com_quit_f_hook; + + void init_dedicated_server() + { + static bool initialized = false; + if (initialized) return; + initialized = true; + + // R_LoadGraphicsAssets + reinterpret_cast(0x1405DF4B0)(); + } + + void send_heartbeat() + { + auto* const dvar = game::Dvar_FindVar("sv_lanOnly"); + if (dvar && dvar->current.enabled) + { + return; + } + + game::netadr_s target{}; + if (server_list::get_master_server(target)) + { + network::send(target, "heartbeat", "H1"); + } + } + + std::vector& get_startup_command_queue() + { + static std::vector startup_command_queue; + return startup_command_queue; + } + + void execute_startup_command(int client, int /*controllerIndex*/, const char* command) + { + if (game::Live_SyncOnlineDataFlags(0) == 0) + { + game::Cbuf_ExecuteBufferInternal(0, 0, command, game::Cmd_ExecuteSingleCommand); + } + else + { + get_startup_command_queue().emplace_back(command); + } + } + + void execute_startup_command_queue() + { + const auto queue = get_startup_command_queue(); + get_startup_command_queue().clear(); + + for (const auto& command : queue) + { + game::Cbuf_ExecuteBufferInternal(0, 0, command.data(), game::Cmd_ExecuteSingleCommand); + } + } + + std::vector& get_console_command_queue() + { + static std::vector console_command_queue; + return console_command_queue; + } + + void execute_console_command(const int client, const char* command) + { + if (game::Live_SyncOnlineDataFlags(0) == 0) + { + game::Cbuf_AddText(client, command); + game::Cbuf_AddText(client, "\n"); + } + else + { + get_console_command_queue().emplace_back(command); + } + } + + void execute_console_command_queue() + { + const auto queue = get_console_command_queue(); + get_console_command_queue().clear(); + + for (const auto& command : queue) + { + game::Cbuf_AddText(0, command.data()); + game::Cbuf_AddText(0, "\n"); + } + } + + void sync_gpu_stub() + { + std::this_thread::sleep_for(1ms); + } + + game::dvar_t* gscr_set_dynamic_dvar() + { + /* + auto s = game::Scr_GetString(0); + auto* dvar = game::Dvar_FindVar(s); + + if (dvar && !strncmp("scr_", dvar->name, 4)) + { + return dvar; + } + */ + + return gscr_set_dynamic_dvar_hook.invoke(); + } + + void kill_server() + { + for (auto i = 0; i < *game::mp::svs_numclients; ++i) + { + if (game::mp::svs_clients[i].header.state >= 3) + { + game::SV_GameSendServerCommand(i, game::SV_CMD_CAN_IGNORE, + utils::string::va("r \"%s\"", "EXE_ENDOFGAME")); + } + } + + com_quit_f_hook.invoke(); + } + + void sys_error_stub(const char* msg, ...) + { + char buffer[2048]; + + va_list ap; + va_start(ap, msg); + + vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap); + + va_end(ap); + + scheduler::once([]() + { + command::execute("map_rotate"); + }, scheduler::main, 3s); + + game::Com_Error(game::ERR_DROP, "%s", buffer); + } + } + + void initialize() + { + command::execute("exec default_xboxlive.cfg", true); + command::execute("onlinegame 1", true); + command::execute("xblive_privatematch 1", true); + } + + class component final : public component_interface + { + public: + void* load_import(const std::string& library, const std::string& function) override + { + return nullptr; + } + + void post_unpack() override + { + if (!game::environment::is_dedi()) + { + return; + } + +#ifdef DEBUG + printf("Starting dedicated server\n"); +#endif + + // Register dedicated dvar + dvars::register_bool("dedicated", true, game::DVAR_FLAG_READ); + + // Add lanonly mode + dvars::register_bool("sv_lanOnly", false, game::DVAR_FLAG_NONE); + + // Disable VirtualLobby + dvars::override::register_bool("virtualLobbyEnabled", false, game::DVAR_FLAG_READ); + + // Disable r_preloadShaders + dvars::override::register_bool("r_preloadShaders", false, game::DVAR_FLAG_READ); + + // Don't allow sv_hostname to be changed by the game + dvars::disable::set_string("sv_hostname"); + + // Stop crashing from sys_errors + utils::hook::jump(0x140511520, sys_error_stub); + + // Hook R_SyncGpu + utils::hook::jump(0x1405E12F0, sync_gpu_stub); + + utils::hook::jump(0x140254800, init_dedicated_server); + + // delay startup commands until the initialization is done + utils::hook::call(0x1400D72D6, execute_startup_command); + + // delay console commands until the initialization is done + utils::hook::call(0x1400D808C, execute_console_command); + utils::hook::nop(0x1400D80A4, 5); + + // patch GScr_SetDynamicDvar to behave better + gscr_set_dynamic_dvar_hook.create(0x14036B600, &gscr_set_dynamic_dvar); + + utils::hook::nop(0x1404ED90E, 5); // don't load config file + utils::hook::nop(0x140403D92, 5); // ^ + utils::hook::set(0x1400DC1D0, 0xC3); // don't save config file + utils::hook::set(0x140274710, 0xC3); // disable self-registration + utils::hook::set(0x140515890, 0xC3); // init sound system (1) + utils::hook::set(0x1406574F0, 0xC3); // init sound system (2) + utils::hook::set(0x140620D10, 0xC3); // render thread + utils::hook::set(0x14025B850, 0xC3); // called from Com_Frame, seems to do renderer stuff + utils::hook::set(0x1402507B0, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly + utils::hook::set(0x1405D5178, 0x00); // r_loadForRenderer default to 0 + utils::hook::set(0x14050C2D0, 0xC3); // recommended settings check - TODO: Check hook + utils::hook::set(0x140514C00, 0xC3); // some mixer-related function called on shutdown + utils::hook::set(0x140409830, 0xC3); // dont load ui gametype stuff + + utils::hook::nop(0x140481B06, 6); // unknown check in SV_ExecuteClientMessage + utils::hook::nop(0x140480FAC, 4); // allow first slot to be occupied + utils::hook::nop(0x14025619B, 2); // properly shut down dedicated servers + utils::hook::nop(0x14025615E, 2); // ^ + utils::hook::nop(0x1402561C0, 5); // don't shutdown renderer + + utils::hook::set(0x140091840, 0xC3); // something to do with blendShapeVertsView + utils::hook::nop(0x140659A0D, 8); // sound thing + + // (COULD NOT FIND IN H1) + // utils::hook::set(0x1404D6960, 0xC3); // cpu detection stuff? + utils::hook::set(0x1405E97F0, 0xC3); // gfx stuff during fastfile loading + utils::hook::set(0x1405E9700, 0xC3); // ^ + utils::hook::set(0x1405E9790, 0xC3); // ^ + utils::hook::set(0x1402C1180, 0xC3); // ^ + utils::hook::set(0x1405E9750, 0xC3); // ^ + utils::hook::set(0x1405AD5B0, 0xC3); // directx stuff + utils::hook::set(0x1405DB150, 0xC3); // ^ + utils::hook::set(0x140625220, 0xC3); // ^ - mutex + utils::hook::set(0x1405DB650, 0xC3); // ^ + + utils::hook::set(0x14008B5F0, 0xC3); // rendering stuff + utils::hook::set(0x1405DB8B0, 0xC3); // ^ + utils::hook::set(0x1405DB9C0, 0xC3); // ^ + utils::hook::set(0x1405DC050, 0xC3); // ^ + utils::hook::set(0x1405DCBA0, 0xC3); // ^ + utils::hook::set(0x1405DD240, 0xC3); // ^ + + // shaders + utils::hook::set(0x1400916A0, 0xC3); // ^ + utils::hook::set(0x140091610, 0xC3); // ^ + utils::hook::set(0x14061ACC0, 0xC3); // ^ - mutex + + utils::hook::set(0x140516080, 0xC3); // idk + utils::hook::set(0x1405AE5F0, 0xC3); // ^ + + utils::hook::set(0x1405E0B30, 0xC3); // R_Shutdown + utils::hook::set(0x1405AE400, 0xC3); // shutdown stuff + utils::hook::set(0x1405E0C00, 0xC3); // ^ + utils::hook::set(0x1405DFE50, 0xC3); // ^ + + // utils::hook::set(0x1404B67E0, 0xC3); // sound crashes (H1 - questionable, function looks way different) + + utils::hook::set(0x14048B660, 0xC3); // disable host migration + + utils::hook::set(0x14042B2E0, 0xC3); // render synchronization lock + utils::hook::set(0x14042B210, 0xC3); // render synchronization unlock + + utils::hook::set(0x140176D2D, 0xEB); // LUI: Unable to start the LUI system due to errors in main.lua + + utils::hook::nop(0x140506ECE, 5); // Disable sound pak file loading + utils::hook::nop(0x140506ED6, 2); // ^ + utils::hook::set(0x1402C5910, 0xC3); // Disable image pak file loading + + // Reduce min required memory + utils::hook::set(0x14050C717, 0x80000000); + + utils::hook::set(0x1402BF7F0, 0xC3); // some loop + utils::hook::set(0x14007E150, 0xC3); // related to shader caching / techsets / fastfiles + + // initialize the game after onlinedataflags is 32 (workaround) + scheduler::schedule([=]() + { + if (game::Live_SyncOnlineDataFlags(0) == 32 && game::Sys_IsDatabaseReady2()) + { + scheduler::once([]() + { + command::execute("xstartprivateparty", true); + command::execute("disconnect", true); // 32 -> 0 + }, scheduler::pipeline::main, 1s); + return scheduler::cond_end; + } + + return scheduler::cond_continue; + }, scheduler::pipeline::main, 1s); + + scheduler::on_game_initialized([]() + { + initialize(); + + console::info("==================================\n"); + console::info("Server started!\n"); + console::info("==================================\n"); + + // remove disconnect command + game::Cmd_RemoveCommand("disconnect"); + + execute_startup_command_queue(); + execute_console_command_queue(); + + // Send heartbeat to dpmaster + scheduler::once(send_heartbeat, scheduler::pipeline::server); + scheduler::loop(send_heartbeat, scheduler::pipeline::server, 10min); + command::add("heartbeat", send_heartbeat); + }, scheduler::pipeline::main, 1s); + + command::add("killserver", kill_server); + com_quit_f_hook.create(0x1400DA640, &kill_server); + } + }; +} + +REGISTER_COMPONENT(dedicated::component) \ No newline at end of file diff --git a/src/client/component/dvars.cpp b/src/client/component/dvars.cpp index 1f46d4db..4a086a27 100644 --- a/src/client/component/dvars.cpp +++ b/src/client/component/dvars.cpp @@ -229,7 +229,7 @@ namespace dvars { set_string_overrides[name] = value; } - + void set_from_string(const std::string& name, const std::string& value) { set_from_string_overrides[name] = value; @@ -413,7 +413,6 @@ namespace dvars auto* var = find_dvar(override::set_from_string_overrides, dvar->hash); if (var) { - printf("fucker\n"); string = var->data(); } diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index fe12fd6b..42295455 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -36,19 +36,6 @@ namespace patches return sv_kick_client_num_hook.invoke(client_num, reason); } - utils::hook::detour gscr_set_save_dvar_hook; - - void gscr_set_save_dvar_stub() - { - const auto string = utils::string::to_lower(utils::hook::invoke(SELECT_VALUE(0x140375210, 0x140443150), 0)); - if (string == "cg_fov" || string == "cg_fovscale") - { - return; - } - - gscr_set_save_dvar_hook.invoke(); - } - std::string get_login_username() { char username[UNLEN + 1]; @@ -167,15 +154,15 @@ namespace patches // Unlock fps in main menu utils::hook::set(SELECT_VALUE(0x14018D47B, 0x14025B86B), 0xEB); // H1(1.4) - // Fix mouse lag - utils::hook::nop(SELECT_VALUE(0x1403E3C05, 0x1404DB1AF), 6); - scheduler::loop([]() + if (!game::environment::is_dedi()) { - SetThreadExecutionState(ES_DISPLAY_REQUIRED); - }, scheduler::pipeline::main); - - // Prevent game from overriding cg_fov and cg_fovscale values - gscr_set_save_dvar_hook.create(SELECT_VALUE(0x1402AE020, 0x14036B600), &gscr_set_save_dvar_stub); + // Fix mouse lag + utils::hook::nop(SELECT_VALUE(0x1403E3C05, 0x1404DB1AF), 6); + scheduler::loop([]() + { + SetThreadExecutionState(ES_DISPLAY_REQUIRED); + }, scheduler::pipeline::main); + } // Make cg_fov and cg_fovscale saved dvars dvars::override::register_float("cg_fov", 65.f, 40.f, 200.f, game::DvarFlags::DVAR_FLAG_SAVED); diff --git a/src/client/component/server_list.cpp b/src/client/component/server_list.cpp index f34f6690..c93317bd 100644 --- a/src/client/component/server_list.cpp +++ b/src/client/component/server_list.cpp @@ -74,7 +74,7 @@ namespace server_list { master_state.requesting = true; - network::send(master_state.address, "getservers", utils::string::va("S1 %i full empty", PROTOCOL)); + network::send(master_state.address, "getservers", utils::string::va("H1 %i full empty", PROTOCOL)); } } @@ -303,7 +303,8 @@ namespace server_list bool get_master_server(game::netadr_s& address) { - return game::NET_StringToAdr("master.xlabs.dev:20810", &address); + return game::NET_StringToAdr("135.148.53.121:20810", &address); + // return game::NET_StringToAdr("master.xlabs.dev:20810", &address); } void handle_info_response(const game::netadr_s& address, const utils::info_string& info) diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index df5ac33e..8d0c361f 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -1,8 +1,11 @@ #include +#include "loader/component_loader.hpp" + #include #include "game.hpp" #include +#include namespace dvars { @@ -2759,4 +2762,4 @@ namespace dvars return game::Dvar_RegisterVec4(hash, "", x, y, z, w, min, max, flags); } -} \ No newline at end of file +} diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 2d7ea23f..d82ee023 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -9,9 +9,12 @@ namespace game **************************************************************/ WEAK symbol Cbuf_AddText{0x140342EB0, 0x1404033B0}; + WEAK symbol Cbuf_ExecuteBufferInternal{0, 0x1404034C0}; WEAK symbol Conbuf_AppendText{0x1403E3300, 0x140513FF0}; WEAK symbol Cmd_ExecuteSingleCommand{0x140343980, 0x140403F60}; WEAK symbol Cmd_AddCommandInternal{0x1403433E0, 0x140403950}; + WEAK symbol Cmd_RemoveCommand{0x140343FF0, 0x1404045D0}; WEAK symbol Cmd_TokenizeString{0x140344110, 0x1404046F0}; WEAK symbol Cmd_EndTokenizeString{0x140343630, 0x140403C20}; @@ -86,6 +89,7 @@ namespace game H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S, CP, CC, game::R_DrawSomething(S)) WEAK symbol Scr_GetFloat{0x140374D20, 0x140442D10}; + WEAK symbol Scr_GetString{0, 0x14032F0A0}; WEAK symbol Scr_GetNumParam{0x140374F30, 0x140442E70}; WEAK symbol ScrPlace_GetViewPlacement{0x1401981F0, 0x140288550};