From 6e8d232e78ce1ebfb8cc70fabf1d69a93d086d27 Mon Sep 17 00:00:00 2001 From: bodnjenie14 <126781031+bodnjenie14@users.noreply.github.com> Date: Thu, 23 May 2024 07:48:21 +0100 Subject: [PATCH 1/3] External console * Added Experimental External Console Co-authored-by: bodnjenie14 <134313862+bodnjenie23@users.noreply.github.com> --- source/proxy-dll/component/console.cpp | 405 +++++++++++++++++++++++++ source/proxy-dll/component/console.hpp | 44 +++ source/proxy-dll/component/logger.cpp | 1 + 3 files changed, 450 insertions(+) create mode 100644 source/proxy-dll/component/console.cpp create mode 100644 source/proxy-dll/component/console.hpp diff --git a/source/proxy-dll/component/console.cpp b/source/proxy-dll/component/console.cpp new file mode 100644 index 0000000..be0b587 --- /dev/null +++ b/source/proxy-dll/component/console.cpp @@ -0,0 +1,405 @@ +//TODO: Fix console branding + +#include +#include "loader/component_loader.hpp" +#include +#include "definitions/game.hpp" +#include "console.hpp" +#include +#include + +#define OUTPUT_HANDLE GetStdHandle(STD_OUTPUT_HANDLE) + +namespace console +{ + namespace + { + const char* branding_str = "Project-Bo4 >"; + + size_t branding_lenght = std::strlen(branding_str); + + utilities::hook::detour printf_hook; + std::recursive_mutex print_mutex; + + struct + { + bool kill; + std::thread thread; + HANDLE kill_event; + char buffer[512]{}; + int cursor; + std::int32_t history_index = -1; + std::deque history{}; + } con{}; + + void set_cursor_pos(int x) + { + CONSOLE_SCREEN_BUFFER_INFO info{}; + GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info); + info.dwCursorPosition.X = static_cast(x); + SetConsoleCursorPosition(OUTPUT_HANDLE, info.dwCursorPosition); + } + + void show_cursor(const bool show) + { + CONSOLE_CURSOR_INFO info{}; + GetConsoleCursorInfo(OUTPUT_HANDLE, &info); + info.bVisible = show; + SetConsoleCursorInfo(OUTPUT_HANDLE, &info); + } + + template + int invoke_printf(const char* fmt, Args&&... args) + { + return printf_hook.invoke(fmt, std::forward(args)...); + } + + std::string format(va_list* ap, const char* message) + { + static thread_local char buffer[0x1000]; + + const auto count = vsnprintf_s(buffer, _TRUNCATE, message, *ap); + if (count < 0) + { + return {}; + } + + return {buffer, static_cast(count)}; + } + + void update() + { + std::lock_guard _0(print_mutex); + + show_cursor(false); + set_cursor_pos(0); + invoke_printf("%s", con.buffer); + //invoke_printf("%s %s", branding_str, con.buffer); + + set_cursor_pos(con.cursor); + //set_cursor_pos(branding_lenght + con.cursor); + show_cursor(true); + } + + void clear_output() + { + std::lock_guard _0(print_mutex); + + show_cursor(false); + set_cursor_pos(0); + + for (auto i = 0; i < std::strlen(con.buffer); i++) + { + invoke_printf(" "); + } + + set_cursor_pos(con.cursor); + show_cursor(true); + } + + int dispatch_message(const int type, const std::string& message) + { + + std::lock_guard _0(print_mutex); + + clear_output(); + set_cursor_pos(0); + + const auto res = invoke_printf("%s", message.data()); + + if (message.size() <= 0 || message[message.size() - 1] != '\n') + { + invoke_printf("\n"); + } + + update(); + return res; + } + + void clear() + { + std::lock_guard _0(print_mutex); + + clear_output(); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + + con.cursor = 0; + set_cursor_pos(0); + } + + size_t get_max_input_length() + { + CONSOLE_SCREEN_BUFFER_INFO info{}; + GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info); + const auto columns = static_cast(info.srWindow.Right - info.srWindow.Left - 1); + return std::max(size_t(0), std::min(columns, sizeof(con.buffer))); + } + + void handle_resize() + { + clear(); + update(); + } + + void handle_input(const INPUT_RECORD record) + { + if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) + { + handle_resize(); + return; + } + + if (record.EventType != KEY_EVENT || !record.Event.KeyEvent.bKeyDown) + { + return; + } + + std::lock_guard _0(print_mutex); + + const auto key = record.Event.KeyEvent.wVirtualKeyCode; + switch (key) + { + case VK_UP: + { + if (++con.history_index >= con.history.size()) + { + con.history_index = static_cast(con.history.size()) - 1; + } + + clear(); + + if (con.history_index != -1) + { + strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); + con.cursor = static_cast(strlen(con.buffer)); + } + + update(); + break; + } + case VK_DOWN: + { + if (--con.history_index < -1) + { + con.history_index = -1; + } + + clear(); + + if (con.history_index != -1) + { + strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); + con.cursor = static_cast(strlen(con.buffer)); + } + + update(); + break; + } + case VK_LEFT: + { + if (con.cursor > 0) + { + con.cursor--; + set_cursor_pos(con.cursor); + //set_cursor_pos(branding_lenght + con.cursor); + + } + + break; + } + case VK_RIGHT: + { + if (con.cursor < std::strlen(con.buffer)) + { + con.cursor++; + set_cursor_pos(con.cursor); + //set_cursor_pos(branding_lenght + con.cursor); + } + + break; + } + case VK_RETURN: + { + if (con.history_index != -1) + { + const auto itr = con.history.begin() + con.history_index; + + if (*itr == con.buffer) + { + con.history.erase(con.history.begin() + con.history_index); + } + } + + if (con.buffer[0]) + { + con.history.push_front(con.buffer); + } + + if (con.history.size() > 10) + { + con.history.erase(con.history.begin() + 10); + } + + con.history_index = -1; + game::Cbuf_AddText(0, utilities::string::va("%s \n", con.buffer)); + + con.cursor = 0; + + clear_output(); + + invoke_printf("]%s\r\n", con.buffer); + + strncpy_s(con.buffer, "", sizeof(con.buffer)); + break; + } + case VK_BACK: + { + if (con.cursor <= 0) + { + break; + } + + clear_output(); + + std::memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor, strlen(con.buffer) + 1 - con.cursor); + con.buffer - 1; + con.cursor--; + + update(); + break; + } + case VK_ESCAPE: + { + con.cursor = 0; + clear_output(); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + break; + } + default: + { + const auto c = record.Event.KeyEvent.uChar.AsciiChar; + if (!c) + { + break; + } + + if (std::strlen(con.buffer) + 1 >= get_max_input_length()) + { + break; + } + + std::memmove(con.buffer + con.cursor + 1, + con.buffer + con.cursor, std::strlen(con.buffer) + 1 - con.cursor); + con.buffer[con.cursor] = c; + con.cursor++; + + update(); + break; + } + } + } + + int __cdecl printf_stub(const char* fmt, ...) + { + va_list ap; + va_start(ap, fmt); + const auto result = format(&ap, fmt); + va_end(ap); + + return dispatch_message(con_type_info, result); + } + } + + void print(const int type, const char* fmt, ...) + { + va_list ap; + va_start(ap, fmt); + const auto result = format(&ap, fmt); + va_end(ap); + + dispatch_message(type, result); + } + + class component final : public component_interface + { + public: + component() + { + ShowWindow(GetConsoleWindow(), SW_HIDE); + } + + void pre_start() override + { + FILE* empty; + AllocConsole(); + AttachConsole(GetCurrentProcessId()); + freopen_s(&empty, "CONOUT$", "r", stdin); + freopen_s(&empty, "CONOUT$", "w", stdout); + freopen_s(&empty, "CONOUT$", "w", stderr); + SetConsoleTitle("Project-Bo4:"); + + printf_hook.create(printf, printf_stub); + + ShowWindow(GetConsoleWindow(), SW_SHOW); + + con.kill_event = CreateEvent(NULL, TRUE, FALSE, NULL); + + con.thread = utilities::thread::create_named_thread("Console", []() + { + const auto handle = GetStdHandle(STD_INPUT_HANDLE); + HANDLE handles[2] = {handle, con.kill_event}; + MSG msg{}; + + INPUT_RECORD record{}; + DWORD num_events{}; + + while (!con.kill) + { + const auto result = MsgWaitForMultipleObjects(2, handles, FALSE, INFINITE, QS_ALLINPUT); + if (con.kill) + { + return; + } + + switch (result) + { + case WAIT_OBJECT_0: + { + if (!ReadConsoleInput(handle, &record, 1, &num_events) || num_events == 0) + { + break; + } + + handle_input(record); + break; + } + case WAIT_OBJECT_0 + 1: + { + if (!PeekMessageA(&msg, GetConsoleWindow(), NULL, NULL, PM_REMOVE)) + { + break; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + } + } + } + }); + } + + void pre_destroy() override + { + con.kill = true; + SetEvent(con.kill_event); + + if (con.thread.joinable()) + { + con.thread.join(); + } + } + }; +} + +REGISTER_COMPONENT(console::component) diff --git a/source/proxy-dll/component/console.hpp b/source/proxy-dll/component/console.hpp new file mode 100644 index 0000000..4101a3a --- /dev/null +++ b/source/proxy-dll/component/console.hpp @@ -0,0 +1,44 @@ +#pragma once + +namespace console +{ + HWND get_window(); + void set_title(std::string title); + void set_size(int width, int height); + + enum console_type + { + con_type_error = 1, + con_type_debug = 2, + con_type_warning = 3, + con_type_info = 7 + }; + + void print(int type, const char* fmt, ...); + + template + void error(const char* fmt, Args&&... args) + { + print(con_type_error, fmt, std::forward(args)...); + } + + template + void debug(const char* fmt, Args&&... args) + { +#ifdef DEBUG + print(con_type_debug, fmt, std::forward(args)...); +#endif + } + + template + void warn(const char* fmt, Args&&... args) + { + print(con_type_warning, fmt, std::forward(args)...); + } + + template + void info(const char* fmt, Args&&... args) + { + print(con_type_info, fmt, std::forward(args)...); + } +} \ No newline at end of file diff --git a/source/proxy-dll/component/logger.cpp b/source/proxy-dll/component/logger.cpp index 67822bf..bf23c61 100644 --- a/source/proxy-dll/component/logger.cpp +++ b/source/proxy-dll/component/logger.cpp @@ -37,6 +37,7 @@ namespace logger OutputDebugStringA(text.c_str()); #endif // OUTPUT_DEBUG_API + printf(text.c_str()); //print debug messages to new console std::ofstream fs; fs.open("project-bo4.log", std::ios_base::app); From d00351181081118fc7d36755d9ee975b7db9f846 Mon Sep 17 00:00:00 2001 From: Ren_ <58993316+renzopereyra115@users.noreply.github.com> Date: Wed, 22 May 2024 23:53:38 -0700 Subject: [PATCH 2/3] Update README.md --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f0f7408..f2f7a9e 100644 --- a/README.md +++ b/README.md @@ -2,52 +2,52 @@ ![code](https://raw.githubusercontent.com/project-bo4/shield-development/master/assets/readme_header.jpg) ## SHIELD -A very experimental modification platform for Call of Duty®: Black Ops 4 run by community, aiming at improving both functionality and performance of original game. +A very experimental modification platform for Call of Duty®: Black Ops 4 run by the community, aiming at improving both functionality and performance of the original game. ## DEMONWARE -As of May11, 2023 we merged 'demonware' branch into 'master'. it includes a built-in demonware server emulator which allows player to start game without connection to official online servers. +As of May 11, 2023, we have merged the 'demonware' branch into 'master'. It includes a built-in demonware server emulator which allows the player to start a game without an active connection to the official online servers. ## INSTRUCTIONS -> You should have publisher files required for playing online under LPC folder of your game directory. -> If its not the case then start original game through battlenet launcher once to get those downloaded. +> You should already have the publisher files required for playing online (found under the LPC folder of your game directory). +> If this is not the case, then run Black Ops 4 through Battle.NET Launcher once to get those files downloaded. -1- Clone repository with its sub-modules and use generate.bat to make visual studio solution then compile project and copy ``d3d11.dll`` into your bo4 folder. +1- Clone repository with its sub-modules and use ```generate.bat``` to make a Visual Studio solution, then compile project and copy ``d3d11.dll`` into your BO4 folder. 2- Start BlackOps4.exe -*In case you wanted to revert back to original battlenet servers just delete ``d3d11.dll``. you can put it back later whenever you want to re-install client.* +*In case you wanted to revert back to using the original Battle.NET servers, just delete the generated ``d3d11.dll`` file from your game folder. You can re-generate and copy ```d3d11.dll``` back later whenever you want to re-install the client.* ## SHIELD DOCUMENTATION Documentation on shield can be found [here](https://shield-bo4.gitbook.io/). ## SUBMITTING ISSUES -Github issues section is only for reporting programmatically errors of client. please dont use it for requesting features or seeking help with personal issues such as faulty game data or similar problems. use battlenet's scan and repair feature to fix those problems. +The Github Issues section is only for reporting programmatical errors related to the client. Please don't use it for requesting features or seeking help with personal issues such as experiencing faulty game data or similar problems. Use Battle.NET's 'Scan And Repair' feature to fix those problems. ## NOTES -- Base SDK(well kinda...) used by this project is developed by [Maurice Heumann](https://github.com/momo5502); Thanks to the guy. +- Base SDK (well kinda...) used by this project is developed by [Maurice Heumann](https://github.com/momo5502); Thanks to the guy. -- There are some 3rd-party project/tools that have influenced and helped the project in particular ways; If you belive there is something originated from you and want to be credited please contact any of our social media accounts. +- There are some 3rd-party project/tools that have influenced and helped the project in particular ways; If you belive there is something originated from you and want to be credited, please contact any of our social media accounts. -- This Project is created purely for educational purposes. its free and open-sourced under gnu license. developers are not responsible or liable for misuse of this product. +- This Project is created purely for educational purposes. It's free and open-sourced under the GNU License. Developers are not responsible or liable for misuse of this product. ## Roadmap ### CLIENT-SIDE: -- [x] Online Battlenet Connection Requirement Removal +- [x] Online Battle.NET Connection Requirement Removal - [x] Built-in Demonware Server Emulation - [x] BlackBox Crash Reporting Component - [x] In-game Interactable Console -- [x] Gsc & Lua Modding Support +- [x] GSC & Lua Modding Support - [ ] Enable Aim-Assist for Game Controllers -- [ ] Miscellaneous Features such as 1st person view fov -- [ ] Fix *uncommon* runtime black screen issue along other instabilities +- [ ] Miscellaneous Features such as 1st-Person View FOV +- [ ] Fix *uncommon* Runtime "Black Screen" issue along other instabilities ### SERVER-SIDE: - [x] Fully Functional Online Server Emulator -- [ ] Implement Basic Matchmaking with QOS Logics +- [ ] Implement Basic Matchmaking with QOS Logic - [ ] Try to Create Dedicated Server off Public Ship Builds From 324fcc242546ca6e627c5fee0c2caa9012c86848 Mon Sep 17 00:00:00 2001 From: bodnjenie14 <126781031+bodnjenie14@users.noreply.github.com> Date: Mon, 27 May 2024 17:04:52 +0100 Subject: [PATCH 3/3] Updated Console Added command line tag for external console --- source/proxy-dll/component/console.cpp | 322 ++++++++++++------------- 1 file changed, 153 insertions(+), 169 deletions(-) diff --git a/source/proxy-dll/component/console.cpp b/source/proxy-dll/component/console.cpp index be0b587..0bcc814 100644 --- a/source/proxy-dll/component/console.cpp +++ b/source/proxy-dll/component/console.cpp @@ -1,5 +1,3 @@ -//TODO: Fix console branding - #include #include "loader/component_loader.hpp" #include @@ -14,9 +12,8 @@ namespace console { namespace { - const char* branding_str = "Project-Bo4 >"; - - size_t branding_lenght = std::strlen(branding_str); + const char* branding_str = "shield> "; + size_t branding_length = std::strlen(branding_str); utilities::hook::detour printf_hook; std::recursive_mutex print_mutex; @@ -64,7 +61,7 @@ namespace console return {}; } - return {buffer, static_cast(count)}; + return { buffer, static_cast(count) }; } void update() @@ -73,11 +70,8 @@ namespace console show_cursor(false); set_cursor_pos(0); - invoke_printf("%s", con.buffer); - //invoke_printf("%s %s", branding_str, con.buffer); - - set_cursor_pos(con.cursor); - //set_cursor_pos(branding_lenght + con.cursor); + invoke_printf("%s%s", branding_str, con.buffer); + set_cursor_pos(branding_length + con.cursor); show_cursor(true); } @@ -88,18 +82,17 @@ namespace console show_cursor(false); set_cursor_pos(0); - for (auto i = 0; i < std::strlen(con.buffer); i++) + for (auto i = 0; i < branding_length + std::strlen(con.buffer); i++) { invoke_printf(" "); } - set_cursor_pos(con.cursor); + set_cursor_pos(0); show_cursor(true); } int dispatch_message(const int type, const std::string& message) { - std::lock_guard _0(print_mutex); clear_output(); @@ -132,7 +125,7 @@ namespace console CONSOLE_SCREEN_BUFFER_INFO info{}; GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info); const auto columns = static_cast(info.srWindow.Right - info.srWindow.Left - 1); - return std::max(size_t(0), std::min(columns, sizeof(con.buffer))); + return std::max(size_t(0), std::min(columns, sizeof(con.buffer) - branding_length)); } void handle_resize() @@ -159,143 +152,135 @@ namespace console const auto key = record.Event.KeyEvent.wVirtualKeyCode; switch (key) { - case VK_UP: + case VK_UP: + { + if (++con.history_index >= con.history.size()) { - if (++con.history_index >= con.history.size()) - { - con.history_index = static_cast(con.history.size()) - 1; - } - - clear(); - - if (con.history_index != -1) - { - strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); - con.cursor = static_cast(strlen(con.buffer)); - } - - update(); - break; + con.history_index = static_cast(con.history.size()) - 1; } - case VK_DOWN: + + clear(); + + if (con.history_index != -1) { - if (--con.history_index < -1) - { - con.history_index = -1; - } - - clear(); - - if (con.history_index != -1) - { - strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); - con.cursor = static_cast(strlen(con.buffer)); - } - - update(); - break; + strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); + con.cursor = static_cast(strlen(con.buffer)); } - case VK_LEFT: + + update(); + break; + } + case VK_DOWN: + { + if (--con.history_index < -1) { - if (con.cursor > 0) - { - con.cursor--; - set_cursor_pos(con.cursor); - //set_cursor_pos(branding_lenght + con.cursor); - - } - - break; - } - case VK_RIGHT: - { - if (con.cursor < std::strlen(con.buffer)) - { - con.cursor++; - set_cursor_pos(con.cursor); - //set_cursor_pos(branding_lenght + con.cursor); - } - - break; - } - case VK_RETURN: - { - if (con.history_index != -1) - { - const auto itr = con.history.begin() + con.history_index; - - if (*itr == con.buffer) - { - con.history.erase(con.history.begin() + con.history_index); - } - } - - if (con.buffer[0]) - { - con.history.push_front(con.buffer); - } - - if (con.history.size() > 10) - { - con.history.erase(con.history.begin() + 10); - } - con.history_index = -1; - game::Cbuf_AddText(0, utilities::string::va("%s \n", con.buffer)); - - con.cursor = 0; - - clear_output(); - - invoke_printf("]%s\r\n", con.buffer); - - strncpy_s(con.buffer, "", sizeof(con.buffer)); - break; } - case VK_BACK: + + clear(); + + if (con.history_index != -1) { - if (con.cursor <= 0) - { - break; - } + strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); + con.cursor = static_cast(strlen(con.buffer)); + } - clear_output(); - - std::memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor, strlen(con.buffer) + 1 - con.cursor); - con.buffer - 1; + update(); + break; + } + case VK_LEFT: + { + if (con.cursor > 0) + { con.cursor--; - - update(); - break; + set_cursor_pos(branding_length + con.cursor); } - case VK_ESCAPE: - { - con.cursor = 0; - clear_output(); - strncpy_s(con.buffer, "", sizeof(con.buffer)); - break; - } - default: - { - const auto c = record.Event.KeyEvent.uChar.AsciiChar; - if (!c) - { - break; - } - if (std::strlen(con.buffer) + 1 >= get_max_input_length()) - { - break; - } - - std::memmove(con.buffer + con.cursor + 1, - con.buffer + con.cursor, std::strlen(con.buffer) + 1 - con.cursor); - con.buffer[con.cursor] = c; + break; + } + case VK_RIGHT: + { + if (con.cursor < std::strlen(con.buffer)) + { con.cursor++; + set_cursor_pos(branding_length + con.cursor); + } - update(); + break; + } + case VK_RETURN: + { + if (con.history_index != -1) + { + const auto itr = con.history.begin() + con.history_index; + + if (*itr == con.buffer) + { + con.history.erase(con.history.begin() + con.history_index); + } + } + + if (con.buffer[0]) + { + con.history.push_front(con.buffer); + } + + if (con.history.size() > 10) + { + con.history.erase(con.history.begin() + 10); + } + + con.history_index = -1; + game::Cbuf_AddText(0, utilities::string::va("%s \n", con.buffer)); + + con.cursor = 0; + + clear_output(); + invoke_printf("%s%s\r\n", branding_str, con.buffer); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + break; + } + case VK_BACK: + { + if (con.cursor <= 0) + { break; } + + clear_output(); + std::memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor, strlen(con.buffer) + 1 - con.cursor); + con.cursor--; + + update(); + break; + } + case VK_ESCAPE: + { + con.cursor = 0; + clear_output(); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + break; + } + default: + { + const auto c = record.Event.KeyEvent.uChar.AsciiChar; + if (!c) + { + break; + } + + if (std::strlen(con.buffer) + 1 >= get_max_input_length()) + { + break; + } + + std::memmove(con.buffer + con.cursor + 1, con.buffer + con.cursor, std::strlen(con.buffer) + 1 - con.cursor); + con.buffer[con.cursor] = c; + con.cursor++; + + update(); + break; + } } } @@ -309,7 +294,7 @@ namespace console return dispatch_message(con_type_info, result); } } - + void print(const int type, const char* fmt, ...) { va_list ap; @@ -345,48 +330,47 @@ namespace console con.kill_event = CreateEvent(NULL, TRUE, FALSE, NULL); con.thread = utilities::thread::create_named_thread("Console", []() - { - const auto handle = GetStdHandle(STD_INPUT_HANDLE); - HANDLE handles[2] = {handle, con.kill_event}; - MSG msg{}; - - INPUT_RECORD record{}; - DWORD num_events{}; - - while (!con.kill) { - const auto result = MsgWaitForMultipleObjects(2, handles, FALSE, INFINITE, QS_ALLINPUT); - if (con.kill) - { - return; - } + const auto handle = GetStdHandle(STD_INPUT_HANDLE); + HANDLE handles[2] = { handle, con.kill_event }; + MSG msg{}; - switch (result) + INPUT_RECORD record{}; + DWORD num_events{}; + + while (!con.kill) { - case WAIT_OBJECT_0: - { - if (!ReadConsoleInput(handle, &record, 1, &num_events) || num_events == 0) + const auto result = MsgWaitForMultipleObjects(2, handles, FALSE, INFINITE, QS_ALLINPUT); + if (con.kill) { - break; + return; } - handle_input(record); - break; - } - case WAIT_OBJECT_0 + 1: - { - if (!PeekMessageA(&msg, GetConsoleWindow(), NULL, NULL, PM_REMOVE)) + switch (result) { + case WAIT_OBJECT_0: + { + if (!ReadConsoleInput(handle, &record, 1, &num_events) || num_events == 0) + { + break; + } + + handle_input(record); break; } + case WAIT_OBJECT_0 + 1: + { + if (!PeekMessageA(&msg, GetConsoleWindow(), NULL, NULL, PM_REMOVE)) + { + break; + } - TranslateMessage(&msg); - DispatchMessage(&msg); - break; - } - } - } - }); + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + } + } + } }); } void pre_destroy() override