diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index 04c5b418..cbb1c1ad 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -1,6 +1,8 @@ #include #include "Console.hpp" +#include "Terminus_4.49.1.ttf.hpp" + #include #ifdef MOUSE_MOVED @@ -32,14 +34,14 @@ namespace Components bool Console::SkipShutdown = false; COLORREF Console::TextColor = -#if DEBUG +#if _DEBUG RGB(255, 200, 117); #else RGB(120, 237, 122); #endif COLORREF Console::BackgroundColor = -#if DEBUG +#if _DEBUG RGB(35, 21, 0); #else RGB(25, 32, 25); @@ -64,16 +66,16 @@ namespace Components const std::string mapname = (*Game::sv_mapname)->current.string; const auto hostname = TextRenderer::StripColors((*Game::sv_hostname)->current.string); - if (Console::HasConsole) + if (HasConsole) { SetConsoleTitleA(hostname.data()); auto clientCount = 0; - auto maxclientCount = *Game::svs_clientCount; + auto maxClientCount = *Game::svs_clientCount; - if (maxclientCount) + if (maxClientCount) { - for (int i = 0; i < maxclientCount; ++i) + for (auto i = 0; i < maxClientCount; ++i) { if (Game::svs_clients[i].header.state >= Game::CS_CONNECTED) { @@ -83,18 +85,17 @@ namespace Components } else { - maxclientCount = Dvar::Var("party_maxplayers").get(); - //maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); - clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); + maxClientCount = *Game::party_maxplayers ? (*Game::party_maxplayers)->current.integer : 18; + clientCount = Game::PartyHost_CountMembers(Game::g_lobbyData); } wclear(InfoWindow); - wprintw(InfoWindow, "%s : %d/%d players : map %s", hostname.data(), clientCount, maxclientCount, (!mapname.empty()) ? mapname.data() : "none"); + wprintw(InfoWindow, "%s : %d/%d players : map %s", hostname.data(), clientCount, maxClientCount, (!mapname.empty()) ? mapname.data() : "none"); wnoutrefresh(InfoWindow); } - else if (IsWindow(Console::GetWindow()) != FALSE) + else if (IsWindow(GetWindow()) != FALSE) { - SetWindowTextA(Console::GetWindow(), Utils::String::VA("IW4x(" VERSION ") : %s", hostname.data())); + SetWindowTextA(GetWindow(), Utils::String::VA("IW4x(" VERSION ") : %s", hostname.data())); } } @@ -106,35 +107,35 @@ namespace Components void Console::RefreshOutput() { - prefresh(OutputWindow, ((Console::OutputTop > 0) ? (Console::OutputTop - 1) : 0), 0, 1, 0, Console::Height - 2, Console::Width - 1); + prefresh(OutputWindow, ((OutputTop > 0) ? (OutputTop - 1) : 0), 0, 1, 0, Height - 2, Width - 1); } void Console::ScrollOutput(int amount) { - Console::OutputTop += amount; + OutputTop += amount; - if (Console::OutputTop > OUTPUT_MAX_TOP) + if (OutputTop > OUTPUT_MAX_TOP) { - Console::OutputTop = OUTPUT_MAX_TOP; + OutputTop = OUTPUT_MAX_TOP; } - else if (Console::OutputTop < 0) + else if (OutputTop < 0) { - Console::OutputTop = 0; + OutputTop = 0; } // make it only scroll the top if there's more than HEIGHT lines - if (Console::OutBuffer >= 0) + if (OutBuffer >= 0) { - Console::OutBuffer += amount; + OutBuffer += amount; - if (Console::OutBuffer >= Console::Height) + if (OutBuffer >= Height) { - Console::OutBuffer = -1; + OutBuffer = -1; } - if (Console::OutputTop < Console::Height) + if (OutputTop < Height) { - Console::OutputTop = 0; + OutputTop = 0; } } } @@ -149,17 +150,13 @@ namespace Components if (getDpiForWindow) { - dpi = getDpiForWindow(hWnd); + dpi = static_cast(getDpiForWindow(hWnd)); } else if (getDpiForMonitor) { HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); UINT xdpi, ydpi; - LRESULT success = getDpiForMonitor(hMonitor, 0, &xdpi, &ydpi); - if (success == S_OK) - { - dpi = static_cast(ydpi); - } + getDpiForMonitor(hMonitor, 0, &xdpi, &ydpi); dpi = 96; } @@ -167,33 +164,33 @@ namespace Components { HDC hDC = GetDC(hWnd); INT ydpi = GetDeviceCaps(hDC, LOGPIXELSY); - ReleaseDC(NULL, hDC); + ReleaseDC(nullptr, hDC); dpi = ydpi; } constexpr auto unawareDpi = 96.0f; - return dpi / unawareDpi; + return static_cast(dpi) / unawareDpi; } const char* Console::Input() { - if (!Console::HasConsole) + if (!HasConsole) { - Console::ShowPrompt(); + ShowPrompt(); wrefresh(InputWindow); - Console::HasConsole = true; + HasConsole = true; } - int currentTime = static_cast(GetTickCount64()); // Make our compiler happy - if ((currentTime - Console::LastRefresh) > 250) + auto currentTime = static_cast(GetTickCount64()); // Make our compiler happy + if ((currentTime - LastRefresh) > 250) { - Console::RefreshOutput(); - Console::LastRefresh = currentTime; + RefreshOutput(); + LastRefresh = currentTime; } - int c = wgetch(InputWindow); + auto c = wgetch(InputWindow); if (c == ERR) { @@ -208,28 +205,28 @@ namespace Components wattron(OutputWindow, COLOR_PAIR(10) | A_BOLD); wprintw(OutputWindow, "%s", "]"); - if (Console::LineBufferIndex) + if (LineBufferIndex) { - wprintw(OutputWindow, "%s", Console::LineBuffer); + wprintw(OutputWindow, "%s", LineBuffer); } wprintw(OutputWindow, "%s", "\n"); wattroff(OutputWindow, A_BOLD); wclear(InputWindow); - Console::ShowPrompt(); + ShowPrompt(); wrefresh(InputWindow); - Console::ScrollOutput(1); - Console::RefreshOutput(); + ScrollOutput(1); + RefreshOutput(); - if (Console::LineBufferIndex) + if (LineBufferIndex) { - strcpy_s(Console::LineBuffer2, Console::LineBuffer); - strcat_s(Console::LineBuffer, "\n"); - Console::LineBufferIndex = 0; - return Console::LineBuffer; + strcpy_s(LineBuffer2, LineBuffer); + strcat_s(LineBuffer, "\n"); + LineBufferIndex = 0; + return LineBuffer; } break; @@ -237,22 +234,22 @@ namespace Components case 'c' - 'a' + 1: // ctrl-c case 27: { - Console::LineBuffer[0] = '\0'; - Console::LineBufferIndex = 0; + LineBuffer[0] = '\0'; + LineBufferIndex = 0; wclear(InputWindow); - Console::ShowPrompt(); + ShowPrompt(); wrefresh(InputWindow); break; } case 8: // backspace { - if (Console::LineBufferIndex > 0) + if (LineBufferIndex > 0) { - Console::LineBufferIndex--; - Console::LineBuffer[Console::LineBufferIndex] = '\0'; + LineBufferIndex--; + LineBuffer[LineBufferIndex] = '\0'; wprintw(InputWindow, "%c %c", static_cast(c), static_cast(c)); wrefresh(InputWindow); @@ -261,35 +258,35 @@ namespace Components } case KEY_PPAGE: { - Console::ScrollOutput(-1); - Console::RefreshOutput(); + ScrollOutput(-1); + RefreshOutput(); break; } case KEY_NPAGE: { - Console::ScrollOutput(1); - Console::RefreshOutput(); + ScrollOutput(1); + RefreshOutput(); break; } case KEY_UP: { wclear(InputWindow); - Console::ShowPrompt(); - wprintw(InputWindow, "%s", Console::LineBuffer2); + ShowPrompt(); + wprintw(InputWindow, "%s", LineBuffer2); wrefresh(InputWindow); - strcpy_s(Console::LineBuffer, Console::LineBuffer2); - Console::LineBufferIndex = strlen(Console::LineBuffer); + strcpy_s(LineBuffer, LineBuffer2); + LineBufferIndex = static_cast(std::strlen(LineBuffer)); break; } default: - if (c <= 127 && Console::LineBufferIndex < 1022) + if (c <= 127 && LineBufferIndex < 1022) { - // temporary workaround , find out what overwrites our index later on + // temporary workaround, find out what overwrites our index later on //consoleLineBufferIndex = strlen(consoleLineBuffer); - Console::LineBuffer[Console::LineBufferIndex++] = static_cast(c); - Console::LineBuffer[Console::LineBufferIndex] = '\0'; + LineBuffer[LineBufferIndex++] = static_cast(c); + LineBuffer[LineBufferIndex] = '\0'; wprintw(InputWindow, "%c", static_cast(c)); wrefresh(InputWindow); } @@ -318,31 +315,31 @@ namespace Components void Console::Create() { - Console::OutputTop = 0; - Console::OutBuffer = 0; - Console::LastRefresh = 0; - Console::LineBufferIndex = 0; - Console::HasConsole = false; + OutputTop = 0; + OutBuffer = 0; + LastRefresh = 0; + LineBufferIndex = 0; + HasConsole = false; CONSOLE_SCREEN_BUFFER_INFO info; if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info)) { - Console::Width = info.dwSize.X; - Console::Height = info.srWindow.Bottom - info.srWindow.Top + 1; + Width = info.dwSize.X; + Height = info.srWindow.Bottom - info.srWindow.Top + 1; } else { - Console::Height = 25; - Console::Width = 80; + Height = 25; + Width = 80; } initscr(); raw(); noecho(); - OutputWindow = newpad(Console::Height - 1, Console::Width); - InputWindow = newwin(1, Console::Width, Console::Height - 1, 0); - InfoWindow = newwin(1, Console::Width, 0, 0); + OutputWindow = newpad(Height - 1, Width); + InputWindow = newwin(1, Width, Height - 1, 0); + InfoWindow = newwin(1, Width, 0, 0); scrollok(OutputWindow, true); idlok(OutputWindow, true); @@ -370,7 +367,7 @@ namespace Components wrefresh(InfoWindow); wrefresh(InputWindow); - Console::RefreshOutput(); + RefreshOutput(); } void Console::Error(const char* fmt, ...) @@ -384,7 +381,7 @@ namespace Components Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}\n", buf); - Console::RefreshOutput(); + RefreshOutput(); if (IsDebuggerPresent()) { @@ -394,7 +391,7 @@ namespace Components } } - TerminateProcess(GetCurrentProcess(), 0xDEADDEAD); + TerminateProcess(GetCurrentProcess(), EXIT_FAILURE); } void Console::Print(const char* message) @@ -406,11 +403,9 @@ namespace Components { if (*p == '^') { - char color; ++p; - color = (*p - '0'); - + const char color = (*p - '0'); if (color < 9 && color > 0) { wattron(OutputWindow, COLOR_PAIR(color + 2)); @@ -426,26 +421,26 @@ namespace Components wattron(OutputWindow, COLOR_PAIR(9)); - Console::RefreshOutput(); + RefreshOutput(); } - HFONT __stdcall Console::ReplaceFont( - [[maybe_unused]] int cHeight, - int cWidth, - int cEscapement, - int cOrientation, - [[maybe_unused]] int cWeight, - DWORD bItalic, - DWORD bUnderline, - DWORD bStrikeOut, - DWORD iCharSet, - [[maybe_unused]] DWORD iOutPrecision, - DWORD iClipPrecision, - [[maybe_unused]] DWORD iQuality, - [[maybe_unused]] DWORD iPitchAndFamily, + HFONT CALLBACK Console::ReplaceFont( + [[maybe_unused]] int cHeight, + int cWidth, + int cEscapement, + int cOrientation, + [[maybe_unused]] int cWeight, + DWORD bItalic, + DWORD bUnderline, + DWORD bStrikeOut, + DWORD iCharSet, + [[maybe_unused]] DWORD iOutPrecision, + DWORD iClipPrecision, + [[maybe_unused]] DWORD iQuality, + [[maybe_unused]] DWORD iPitchAndFamily, [[maybe_unused]] LPCSTR pszFaceName) { - auto font = CreateFontA( + HFONT font = CreateFontA( 12, cWidth, cEscapement, @@ -459,7 +454,8 @@ namespace Components iClipPrecision, NONANTIALIASED_QUALITY, 0x31, - "Terminus (TTF)"); // Terminus (TTF) + "Terminus (TTF)" + ); // Terminus (TTF) return font; } @@ -467,7 +463,7 @@ namespace Components void Console::GetWindowPos(HWND hWnd, int* x, int* y) { HWND hWndParent = GetParent(hWnd); - POINT p = { 0 }; + POINT p{}; MapWindowPoints(hWnd, hWndParent, &p, 1); @@ -478,8 +474,8 @@ namespace Components BOOL CALLBACK Console::ResizeChildWindow(HWND hwndChild, LPARAM lParam) { auto id = GetWindowLong(hwndChild, GWL_ID); - bool isInputBox = id == INPUT_BOX; - bool isOutputBox = id == OUTPUT_BOX; + auto isInputBox = id == INPUT_BOX; + auto isOutputBox = id == OUTPUT_BOX; if (isInputBox || isOutputBox) { @@ -496,31 +492,31 @@ namespace Components HWND parent = Utils::Hook::Get(0x64A3288); - float scale = GetDpiScale(parent); + auto scale = GetDpiScale(parent); if (isInputBox) { - int newX = childX; // No change! - int newY = static_cast((newParentRect.bottom - newParentRect.top) - 65 * scale); - int newWidth = static_cast((newParentRect.right - newParentRect.left) - 29 * scale); - int newHeight = static_cast((childRect.bottom - childRect.top) * scale); // No change! + auto newX = childX; // No change! + auto newY = static_cast((newParentRect.bottom - newParentRect.top) - 65 * scale); + auto newWidth = static_cast((newParentRect.right - newParentRect.left) - 29 * scale); + auto newHeight = static_cast((childRect.bottom - childRect.top) * scale); // No change! MoveWindow(hwndChild, newX, newY, newWidth, newHeight, TRUE); } if (isOutputBox) { - int newX = childX; // No change! - int newY = childY; // No change! - int newWidth = static_cast((newParentRect.right - newParentRect.left) - 29); - - int margin = 70; + auto newX = childX; // No change! + auto newY = childY; // No change! + auto newWidth = static_cast((newParentRect.right - newParentRect.left) - 29); #ifdef REMOVE_HEADERBAR - margin = 10; + constexpr auto margin = 10; +#else + constexpr auto margin = 70; #endif - int newHeight = static_cast((newParentRect.bottom - newParentRect.top) - 74 * scale - margin); + auto newHeight = static_cast((newParentRect.bottom - newParentRect.top) - 74 * scale - margin); MoveWindow(hwndChild, newX, newY, newWidth, newHeight, TRUE); } @@ -536,23 +532,21 @@ namespace Components // around whenever clearing occurs. void Console::MakeRoomForText([[maybe_unused]] int addedCharacters) { - constexpr unsigned int maxChars = 0x4000; - constexpr unsigned int maxAffectedChars = 0x100; + constexpr auto maxChars = 0x4000; + constexpr auto maxAffectedChars = 0x100; HWND outputBox = Utils::Hook::Get(0x64A328C); - unsigned int totalChars; - unsigned int totalClearLength = 0; + auto totalClearLength = 0; char str[maxAffectedChars]; - unsigned int fetchedCharacters = static_cast(GetWindowText(outputBox, str, maxAffectedChars)); - - totalChars = GetWindowTextLengthA(outputBox); + const auto fetchedCharacters = GetWindowTextA(outputBox, str, maxAffectedChars); + auto totalChars = GetWindowTextLengthA(outputBox); while (totalChars - totalClearLength > maxChars) { - unsigned int clearLength = maxAffectedChars; // Default to full clear + auto clearLength = maxAffectedChars; // Default to full clear - for (size_t i = 0; i < fetchedCharacters; i++) + for (auto i = 0; i < fetchedCharacters; i++) { if (str[i] == '\n') { @@ -567,10 +561,10 @@ namespace Components if (totalClearLength > 0) { - SendMessage(outputBox, WM_SETREDRAW, FALSE, 0); - SendMessage(outputBox, EM_SETSEL, 0, totalClearLength); - SendMessage(outputBox, EM_REPLACESEL, FALSE, 0); - SendMessage(outputBox, WM_SETREDRAW, TRUE, 0); + SendMessageA(outputBox, WM_SETREDRAW, FALSE, 0); + SendMessageA(outputBox, EM_SETSEL, 0, totalClearLength); + SendMessageA(outputBox, EM_REPLACESEL, FALSE, 0); + SendMessageA(outputBox, WM_SETREDRAW, TRUE, 0); } Utils::Hook::Set(0x64A38B8, totalChars - totalClearLength); @@ -596,18 +590,11 @@ namespace Components { switch (Msg) { - - case WM_CREATE: { - BOOL darkMode = true; - -#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 - if (SUCCEEDED(DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, reinterpret_cast(&darkMode), sizeof(darkMode)))) - { - // cool ! - } - + BOOL darkMode = TRUE; + constexpr auto DWMWA_USE_IMMERSIVE_DARK_MODE = 20; + DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &darkMode, sizeof(darkMode)); break; } @@ -630,7 +617,6 @@ namespace Components return 0; } - // Fall through to basegame return Utils::Hook::Call(0x64DC50)(hWnd, Msg, wParam, lParam); } @@ -645,14 +631,14 @@ namespace Components void Console::ApplyConsoleStyle() { - Utils::Hook::Set(0x428A8E, 0); // Adjust logo Y pos - Utils::Hook::Set(0x428A90, 0); // Adjust logo X pos - Utils::Hook::Set(0x428AF2, 67); // Adjust output Y pos - Utils::Hook::Set(0x428AC5, 397); // Adjust input Y pos - Utils::Hook::Set(0x428951, 609); // Reduce window width - Utils::Hook::Set(0x42895D, 423); // Reduce window height - Utils::Hook::Set(0x428AC0, 597); // Reduce input width - Utils::Hook::Set(0x428AED, 596); // Reduce output width + Utils::Hook::Set(0x428A8E, 0); // Adjust logo Y pos + Utils::Hook::Set(0x428A90, 0); // Adjust logo X pos + Utils::Hook::Set(0x428AF2, 67); // Adjust output Y pos + Utils::Hook::Set(0x428AC5, 397); // Adjust input Y pos + Utils::Hook::Set(0x428951, 609); // Reduce window width + Utils::Hook::Set(0x42895D, 423); // Reduce window height + Utils::Hook::Set(0x428AC0, 597); // Reduce input width + Utils::Hook::Set(0x428AED, 596); // Reduce output width DWORD fontsInstalled; CustomConsoleFont = AddFontMemResourceEx(const_cast(reinterpret_cast(Font::Terminus::DATA)), Font::Terminus::LENGTH, 0, &fontsInstalled); @@ -680,35 +666,37 @@ namespace Components // Never reset text Utils::Hook::Nop(0x4F57DF, 0x4F57F6 - 0x4F57DF); - Utils::Hook(0x4F57DF, Console::Sys_PrintStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4F57DF, Sys_PrintStub, HOOK_JUMP).install()->quick(); } void Console::ConsoleRunner() { - Console::SkipShutdown = false; + SkipShutdown = false; Game::Sys_ShowConsole(); MSG message; - while (IsWindow(Console::GetWindow()) != FALSE && GetMessageA(&message, nullptr, 0, 0)) + while (IsWindow(GetWindow()) != FALSE && GetMessageA(&message, nullptr, 0, 0)) { TranslateMessage(&message); DispatchMessageA(&message); } - if (Console::SkipShutdown) return; + if (SkipShutdown) return; - if (Game::Sys_Milliseconds() - Console::LastRefresh > 100 && + if (Game::Sys_Milliseconds() -LastRefresh > 100 && MessageBoxA(nullptr, "The application is not responding anymore, do you want to force its termination?", "Application is not responding", MB_ICONEXCLAMATION | MB_YESNO) == IDYES) { // Force process termination // if the main thread is not responding - OutputDebugStringA("Process termination forced, as the main thread is not responding!"); +#ifdef _DEBUG + OutputDebugStringA("Process termination was forced as the main thread is not responding!"); +#endif // We can not force the termination in this thread // The destructor would be called in this thread // and would try to join this thread, which is impossible - TerminateProcess(GetCurrentProcess(), 0xFFFFFFFF); + TerminateProcess(GetCurrentProcess(), EXIT_FAILURE); } else { @@ -729,7 +717,7 @@ namespace Components va_list ap; va_start(ap, fmt); - _vsnprintf_s(buffer, _TRUNCATE, fmt, ap); + vsnprintf_s(buffer, _TRUNCATE, fmt, ap); va_end(ap); perror(buffer); @@ -759,7 +747,7 @@ namespace Components void Console::StoreSafeArea() { // Backup the original safe area - Console::OriginalSafeArea = *Game::safeArea; + OriginalSafeArea = *Game::safeArea; // Apply new safe area and border float border = 6.0f; @@ -775,12 +763,12 @@ namespace Components void Console::RestoreSafeArea() { // Restore the initial safe area - *Game::safeArea = Console::OriginalSafeArea; + *Game::safeArea = OriginalSafeArea; } void Console::SetSkipShutdown() { - Console::SkipShutdown = true; + SkipShutdown = true; } void Console::FreeNativeConsole() @@ -798,11 +786,10 @@ namespace Components void Console::ShowAsyncConsole() { - Console::ConsoleThread = std::thread(Console::ConsoleRunner); + ConsoleThread = std::thread(ConsoleRunner); } - Game::dvar_t* Console::RegisterConColor(const char* dvarName, float r, float g, float b, float a, float min, - float max, unsigned __int16 flags, const char* description) + Game::dvar_t* Console::RegisterConColor(const char* dvarName, float r, float g, float b, float a, float min, float max, unsigned __int16 flags, const char* description) { static struct { @@ -857,7 +844,7 @@ namespace Components { Command::Add("con_echo", [] { - Console::Con_ToggleConsole(); + Con_ToggleConsole(); Game::I_strncpyz(Game::g_consoleField->buffer, "\\echo ", sizeof(Game::field_t::buffer)); Game::g_consoleField->cursor = static_cast(std::strlen(Game::g_consoleField->buffer)); Game::Field_AdjustScroll(Game::ScrPlace_GetFullPlacement(), Game::g_consoleField); @@ -878,24 +865,24 @@ namespace Components Utils::Hook::Set(0x5A4400, consoleColor); // Remove the need to type '\' or '/' to send a console command - Utils::Hook::Set(0x431565, 0xEB); + Utils::Hook::Set(0x431565, 0xEB); // Internal console - Utils::Hook(0x4F690C, Console::Con_ToggleConsole, HOOK_CALL).install()->quick(); - Utils::Hook(0x4F65A5, Console::Con_ToggleConsole, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4F690C, Con_ToggleConsole, HOOK_CALL).install()->quick(); + Utils::Hook(0x4F65A5, Con_ToggleConsole, HOOK_JUMP).install()->quick(); // Patch safearea for ingame-console - Utils::Hook(0x5A50EF, Console::DrawSolidConsoleStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A50EF, DrawSolidConsoleStub, HOOK_CALL).install()->quick(); // Check for bad food ;) - Utils::Hook(0x4CB9F4, Console::GetAutoCompleteFileList, HOOK_CALL).install()->quick(); + Utils::Hook(0x4CB9F4, GetAutoCompleteFileList, HOOK_CALL).install()->quick(); // Patch console dvars - Utils::Hook(0x4829AB, Console::RegisterConColor, HOOK_CALL).install()->quick(); - Utils::Hook(0x4829EE, Console::RegisterConColor, HOOK_CALL).install()->quick(); - Utils::Hook(0x482A31, Console::RegisterConColor, HOOK_CALL).install()->quick(); - Utils::Hook(0x482A7A, Console::RegisterConColor, HOOK_CALL).install()->quick(); - Utils::Hook(0x482AC3, Console::RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x4829AB, RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x4829EE, RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x482A31, RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x482A7A, RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x482AC3, RegisterConColor, HOOK_CALL).install()->quick(); // Modify console style ApplyConsoleStyle(); @@ -909,7 +896,7 @@ namespace Components if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) { - Scheduler::Loop(Console::RefreshStatus, Scheduler::Pipeline::MAIN); + Scheduler::Loop(RefreshStatus, Scheduler::Pipeline::MAIN); } // Code below is not necessary when performing unit tests! @@ -918,8 +905,8 @@ namespace Components // External console if (Flags::HasFlag("stdout")) { - Utils::Hook(0x4B2080, Console::StdOutPrint, HOOK_JUMP).install()->quick(); - Utils::Hook(0x43D570, Console::StdOutError, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4B2080, StdOutPrint, HOOK_JUMP).install()->quick(); + Utils::Hook(0x43D570, StdOutError, HOOK_JUMP).install()->quick(); } else if (Flags::HasFlag("console") || ZoneBuilder::IsEnabled()) // ZoneBuilder uses the game's console, until the native one is adapted. { @@ -930,28 +917,28 @@ namespace Components Utils::Hook(0x60BB68, [] { - Console::ShowAsyncConsole(); + ShowAsyncConsole(); }, HOOK_CALL).install()->quick(); - Utils::Hook(0x4D69A2, []() + Utils::Hook(0x4D69A2, [] { - Console::SetSkipShutdown(); + SetSkipShutdown(); // Sys_DestroyConsole Utils::Hook::Call(0x4528A0)(); - if (Console::ConsoleThread.joinable()) + if (ConsoleThread.joinable()) { - Console::ConsoleThread.join(); + ConsoleThread.join(); } }, HOOK_CALL).install()->quick(); Scheduler::Loop([] { - Console::LastRefresh = Game::Sys_Milliseconds(); + LastRefresh = Game::Sys_Milliseconds(); }, Scheduler::Pipeline::MAIN); } - else if (Dedicated::IsEnabled()/* || ZoneBuilder::IsEnabled()*/) + else if (Dedicated::IsEnabled()) { DWORD type = GetFileType(GetStdHandle(STD_INPUT_HANDLE)); if (type != FILE_TYPE_CHAR) @@ -962,11 +949,11 @@ namespace Components Utils::Hook::Nop(0x60BB58, 11); - Utils::Hook(0x4305E0, Console::Create, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4528A0, Console::Destroy, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4B2080, Console::Print, HOOK_JUMP).install()->quick(); - Utils::Hook(0x43D570, Console::Error, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4859A5, Console::Input, HOOK_CALL).install()->quick(); + Utils::Hook(0x4305E0, Create, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4528A0, Destroy, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4B2080, Print, HOOK_JUMP).install()->quick(); + Utils::Hook(0x43D570, Error, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4859A5, Input, HOOK_CALL).install()->quick(); } else if(!Loader::IsPerformingUnitTests()) { @@ -976,10 +963,10 @@ namespace Components Console::~Console() { - Console::SetSkipShutdown(); - if (Console::ConsoleThread.joinable()) + SetSkipShutdown(); + if (ConsoleThread.joinable()) { - Console::ConsoleThread.join(); + ConsoleThread.join(); } } } diff --git a/src/Components/Modules/Console.hpp b/src/Components/Modules/Console.hpp index cd138a47..0ccad217 100644 --- a/src/Components/Modules/Console.hpp +++ b/src/Components/Modules/Console.hpp @@ -1,7 +1,5 @@ #pragma once -#include "Terminus_4.49.1.ttf.hpp" - #define OUTPUT_HEIGHT 250 #define OUTPUT_MAX_TOP (OUTPUT_HEIGHT - (Console::Height - 2)) @@ -81,21 +79,7 @@ namespace Components static LRESULT CALLBACK ConWndProc(HWND hWnd, UINT Msg, WPARAM wParam, unsigned int lParam); static ATOM CALLBACK RegisterClassHook(WNDCLASSA* lpWndClass); static BOOL CALLBACK ResizeChildWindow(HWND hwndChild, LPARAM lParam); - static HFONT CALLBACK ReplaceFont( - int cHeight, - int cWidth, - int cEscapement, - int cOrientation, - int cWeight, - DWORD bItalic, - DWORD bUnderline, - DWORD bStrikeOut, - DWORD iCharSet, - DWORD iOutPrecision, - DWORD iClipPrecision, - DWORD iQuality, - DWORD iPitchAndFamily, - LPCSTR pszFaceName); + static HFONT CALLBACK ReplaceFont(int cHeight, int cWidth, int cEscapement, int cOrientation, int cWeight, DWORD bItalic, DWORD bUnderline, DWORD bStrikeOut, DWORD iCharSet, DWORD iOutPrecision, DWORD iClipPrecision, DWORD iQuality, DWORD iPitchAndFamily, LPCSTR pszFaceName); static void ApplyConsoleStyle(); static void GetWindowPos(HWND hWnd, int* x, int* y); static void Sys_PrintStub(); diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 589aa997..b3f66475 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -8,6 +8,7 @@ namespace Components SteamID Dedicated::PlayerGuids[18][2]; Dvar::Var Dedicated::SVLanOnly; + Dvar::Var Dedicated::SVMOTD; Dvar::Var Dedicated::COMLogFilter; bool Dedicated::IsEnabled() @@ -76,7 +77,7 @@ namespace Components __asm { pushad - call Dedicated::PostInitialization + call PostInitialization popad // Start Com_EvenLoop @@ -96,7 +97,7 @@ namespace Components list.append(Utils::String::VA(" %llX", Game::svs_clients[i].steamID)); Utils::InfoString info(Game::svs_clients[i].userinfo); - list.append(Utils::String::VA(" %llX", strtoull(info.get("realsteamId").data(), nullptr, 16))); + list.append(Utils::String::VA(" %llX", std::strtoull(info.get("realsteamId").data(), nullptr, 16))); } else { @@ -127,7 +128,7 @@ namespace Components void Dedicated::Heartbeat() { // Do not send a heartbeat if sv_lanOnly is set to true - if (Dedicated::SVLanOnly.get()) + if (SVLanOnly.get()) { return; } @@ -148,18 +149,17 @@ namespace Components Dedicated::Dedicated() { - Dedicated::COMLogFilter = Dvar::Register("com_logFilter", true, + COMLogFilter = Dvar::Register("com_logFilter", true, Game::DVAR_LATCH, "Removes ~95% of unneeded lines from the log"); - if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) + if (IsEnabled() || ZoneBuilder::IsEnabled()) { // Make sure all callbacks are handled Scheduler::Loop(Steam::SteamAPI_RunCallbacks, Scheduler::Pipeline::SERVER); - Dedicated::SVLanOnly = Dvar::Register("sv_lanOnly", false, - Game::DVAR_NONE, "Don't act as node"); + SVLanOnly = Dvar::Register("sv_lanOnly", false, Game::DVAR_NONE, "Don't act as node"); - Utils::Hook(0x60BE98, Dedicated::InitDedicatedServer, HOOK_CALL).install()->quick(); + Utils::Hook(0x60BE98, InitDedicatedServer, HOOK_CALL).install()->quick(); Utils::Hook::Set(0x683370, 0xC3); // steam sometimes doesn't like the server @@ -199,7 +199,7 @@ namespace Components Utils::Hook::Set(0x5DEC04, 0); // Manually register sv_network_fps - Utils::Hook(0x4D3C7B, Dedicated::Dvar_RegisterSVNetworkFps, HOOK_CALL).install()->quick(); + Utils::Hook(0x4D3C7B, Dvar_RegisterSVNetworkFps, HOOK_CALL).install()->quick(); // r_loadForRenderer default to 0 Utils::Hook::Set(0x519DDF, 0); @@ -217,18 +217,18 @@ namespace Components Utils::Hook::Set(0x4B4D19, 0xEB); // Intercept time wrapping - Utils::Hook(0x62737D, Dedicated::TimeWrapStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x62737D, TimeWrapStub, HOOK_CALL).install()->quick(); //Utils::Hook::Set(0x62735C, 50'000); // Time wrap after 50 seconds (for testing - i don't want to wait 3 weeks) if (!ZoneBuilder::IsEnabled()) { Scheduler::Once([] { - Dvar::Register("sv_motd", "", Game::DVAR_NONE, "A custom message of the day for servers"); + SVMOTD = Dvar::Register("sv_motd", "", Game::DVAR_NONE, "A custom message of the day for servers"); }, Scheduler::Pipeline::MAIN); // Post initialization point - Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x60BFBF, PostInitializationStub, HOOK_JUMP).install()->quick(); // Transmit custom data Scheduler::Loop([] @@ -238,16 +238,16 @@ namespace Components }, Scheduler::Pipeline::SERVER, 10s); // Heartbeats - Scheduler::Once(Dedicated::Heartbeat, Scheduler::Pipeline::SERVER); - Scheduler::Loop(Dedicated::Heartbeat, Scheduler::Pipeline::SERVER, 2min); + Scheduler::Once(Heartbeat, Scheduler::Pipeline::SERVER); + Scheduler::Loop(Heartbeat, Scheduler::Pipeline::SERVER, 2min); } } else { - for (int i = 0; i < ARRAYSIZE(Dedicated::PlayerGuids); ++i) + for (int i = 0; i < ARRAYSIZE(PlayerGuids); ++i) { - Dedicated::PlayerGuids[i][0].bits = 0; - Dedicated::PlayerGuids[i][1].bits = 0; + PlayerGuids[i][0].bits = 0; + PlayerGuids[i][1].bits = 0; } // Intercept server commands @@ -255,12 +255,12 @@ namespace Components { for (int client = 0; client < 18; client++) { - Dedicated::PlayerGuids[client][0].bits = strtoull(params->get(2 * client + 1), nullptr, 16); - Dedicated::PlayerGuids[client][1].bits = strtoull(params->get(2 * client + 2), nullptr, 16); + PlayerGuids[client][0].bits = std::strtoull(params->get(2 * client + 1), nullptr, 16); + PlayerGuids[client][1].bits = std::strtoull(params->get(2 * client + 2), nullptr, 16); - if (Steam::Proxy::SteamFriends && Dedicated::PlayerGuids[client][1].bits != 0) + if (Steam::Proxy::SteamFriends && PlayerGuids[client][1].bits != 0) { - Steam::Proxy::SteamFriends->SetPlayedWith(Dedicated::PlayerGuids[client][1]); + Steam::Proxy::SteamFriends->SetPlayedWith(PlayerGuids[client][1]); } } @@ -270,9 +270,9 @@ namespace Components Scheduler::Loop([] { - if (Dedicated::IsRunning()) + if (IsRunning()) { - Dedicated::TransmitGuids(); + TransmitGuids(); } }, Scheduler::Pipeline::SERVER, 15s); } diff --git a/src/Components/Modules/Dedicated.hpp b/src/Components/Modules/Dedicated.hpp index 236ce7f2..b32e3564 100644 --- a/src/Components/Modules/Dedicated.hpp +++ b/src/Components/Modules/Dedicated.hpp @@ -9,6 +9,7 @@ namespace Components static SteamID PlayerGuids[18][2]; static Dvar::Var SVLanOnly; + static Dvar::Var SVMOTD; static Dvar::Var COMLogFilter; static bool IsEnabled(); diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index a1fc0c8e..577cd19f 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -8,6 +8,9 @@ namespace Components { static mg_mgr Mgr; + Dvar::Var Download::SV_wwwDownload; + Dvar::Var Download::SV_wwwBaseUrl; + Download::ClientDownload Download::CLDownload; std::thread Download::ServerThread; @@ -138,7 +141,7 @@ namespace Components } auto host = "http://" + download->target.getString(); - auto fastHost = Dvar::Var("sv_wwwBaseUrl").get(); + auto fastHost = SV_wwwBaseUrl.get(); if (Utils::String::StartsWith(fastHost, "https://")) { download->thread.detach(); @@ -173,7 +176,7 @@ namespace Components // -mod.ff // /-mod2 // ... - if (Dvar::Var("sv_wwwDownload").get()) + if (SV_wwwDownload.get()) { if (!Utils::String::EndsWith(fastHost, "/")) fastHost.append("/"); url = fastHost + path; @@ -720,8 +723,8 @@ namespace Components Scheduler::Once([] { - Dvar::Register("sv_wwwDownload", false, Game::DVAR_NONE, "Set to true to enable downloading maps/mods from an external server."); - Dvar::Register("sv_wwwBaseUrl", "", Game::DVAR_NONE, "Set to the base url for the external map download."); + SV_wwwDownload = Dvar::Register("sv_wwwDownload", false, Game::DVAR_NONE, "Set to true to enable downloading maps/mods from an external server."); + SV_wwwBaseUrl = Dvar::Register("sv_wwwBaseUrl", "", Game::DVAR_NONE, "Set to the base url for the external map download."); }, Scheduler::Pipeline::MAIN); } diff --git a/src/Components/Modules/Download.hpp b/src/Components/Modules/Download.hpp index 84ccfd93..c64bf030 100644 --- a/src/Components/Modules/Download.hpp +++ b/src/Components/Modules/Download.hpp @@ -13,6 +13,9 @@ namespace Components static void InitiateClientDownload(const std::string& mod, bool needPassword, bool map = false); static void InitiateMapDownload(const std::string& map, bool needPassword); + static Dvar::Var SV_wwwDownload; + static Dvar::Var SV_wwwBaseUrl; + private: class ClientDownload { diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index b3786320..a19e0361 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -583,7 +583,7 @@ namespace Components } // dlcIsTrue serves as a check if the map is a custom map and if it's missing - bool Maps::CheckMapInstalled(const char* mapname, bool error, bool dlcIsTrue) + bool Maps::CheckMapInstalled(const std::string& mapname, bool error, bool dlcIsTrue) { if (FastFiles::Exists(mapname)) return true; @@ -591,12 +591,12 @@ namespace Components { for (auto map : pack.maps) { - if (map == std::string(mapname)) + if (map == mapname) { if (error) { Logger::Error(Game::ERR_DISCONNECT, "Missing DLC pack {} ({}) containing map {} ({}).\nPlease download it to play this map.", - pack.name, pack.index, Game::UI_LocalizeMapName(mapname), mapname); + pack.name, pack.index, Game::UI_LocalizeMapName(mapname.data()), mapname); } return dlcIsTrue; diff --git a/src/Components/Modules/Maps.hpp b/src/Components/Modules/Maps.hpp index ea47c28b..915f6069 100644 --- a/src/Components/Modules/Maps.hpp +++ b/src/Components/Modules/Maps.hpp @@ -54,7 +54,7 @@ namespace Components static std::string CurrentMainZone; static const char* UserMapFiles[4]; - static bool CheckMapInstalled(const char* mapname, bool error = false, bool dlcIsTrue = false); + static bool CheckMapInstalled(const std::string& mapname, bool error = false, bool dlcIsTrue = false); static UserMapContainer* GetUserMap(); static unsigned int GetUsermapHash(const std::string& map); diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index 4126726f..295832a5 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -28,35 +28,36 @@ namespace Components Network::Address Party::Target() { - return Party::Container.target; + return Container.target; } void Party::Connect(Network::Address target) { Node::Add(target); - Party::Container.valid = true; - Party::Container.awaitingPlaylist = false; - Party::Container.joinTime = Game::Sys_Milliseconds(); - Party::Container.target = target; - Party::Container.challenge = Utils::Cryptography::Rand::GenerateChallenge(); + Container.valid = true; + Container.awaitingPlaylist = false; + Container.joinTime = Game::Sys_Milliseconds(); + Container.target = target; + Container.challenge = Utils::Cryptography::Rand::GenerateChallenge(); - Network::SendCommand(Party::Container.target, "getinfo", Party::Container.challenge); + Network::SendCommand(Container.target, "getinfo", Container.challenge); Command::Execute("openmenu popup_reconnectingtoparty"); } const char* Party::GetLobbyInfo(SteamID lobby, const std::string& key) { - if (Party::LobbyMap.contains(lobby.bits)) + if (LobbyMap.contains(lobby.bits)) { - Network::Address address = Party::LobbyMap[lobby.bits]; + Network::Address address = LobbyMap[lobby.bits]; - if (key == "addr") + if (key == "addr"s) { return Utils::String::VA("%d", address.getIP().full); } - else if (key == "port") + + if (key == "port"s) { return Utils::String::VA("%d", address.getPort()); } @@ -67,7 +68,7 @@ namespace Components void Party::RemoveLobby(SteamID lobby) { - Party::LobbyMap.erase(lobby.bits); + LobbyMap.erase(lobby.bits); } void Party::ConnectError(const std::string& message) @@ -79,17 +80,12 @@ namespace Components std::string Party::GetMotd() { - return Party::Container.motd; - } - - Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, unsigned __int16 flag, const char* description) - { - return Dvar::Register(name, 1, 1, max, Game::DVAR_INIT | flag, description).get(); + return Container.motd; } bool Party::PlaylistAwaiting() { - return Party::Container.awaitingPlaylist; + return Container.awaitingPlaylist; } void Party::PlaylistContinue() @@ -99,20 +95,20 @@ namespace Components // Ensure we can join *Game::g_lobbyCreateInProgress = false; - Party::Container.awaitingPlaylist = false; + Container.awaitingPlaylist = false; - SteamID id = Party::GenerateLobbyId(); + SteamID id = GenerateLobbyId(); // Temporary workaround // TODO: Patch the 127.0.0.1 -> loopback mapping in the party code - if (Party::Container.target.isLoopback()) + if (Container.target.isLoopback()) { if (*Game::numIP) { - Party::Container.target.setIP(*Game::localIP); - Party::Container.target.setType(Game::netadrtype_t::NA_IP); + Container.target.setIP(*Game::localIP); + Container.target.setType(Game::netadrtype_t::NA_IP); - Logger::Print("Trying to connect to party with loopback address, using a local ip instead: {}\n", Party::Container.target.getString()); + Logger::Print("Trying to connect to party with loopback address, using a local ip instead: {}\n", Container.target.getString()); } else { @@ -120,17 +116,17 @@ namespace Components } } - Party::LobbyMap[id.bits] = Party::Container.target; + LobbyMap[id.bits] = Container.target; Game::Steam_JoinLobby(id, 0); } void Party::PlaylistError(const std::string& error) { - Party::Container.valid = false; - Party::Container.awaitingPlaylist = false; + Container.valid = false; + Container.awaitingPlaylist = false; - Party::ConnectError(error); + ConnectError(error); } DWORD Party::UIDvarIntStub(char* dvar) @@ -150,7 +146,7 @@ namespace Components bool Party::IsInUserMapLobby() { - return (Party::IsInLobby() && Maps::IsUserMap(Dvar::Var("ui_mapname").get())); + return (IsInLobby() && Maps::IsUserMap(Dvar::Var("ui_mapname").get())); } bool Party::IsEnabled() @@ -160,7 +156,7 @@ namespace Components Party::Party() { - Party::PartyEnable = Dvar::Register("party_enable", Dedicated::IsEnabled(), Game::DVAR_NONE, "Enable party system"); + PartyEnable = Dvar::Register("party_enable", Dedicated::IsEnabled(), Game::DVAR_NONE, "Enable party system"); Dvar::Register("xblive_privatematch", true, Game::DVAR_INIT, ""); // various changes to SV_DirectConnect-y stuff to allow non-party joinees @@ -227,7 +223,7 @@ namespace Components Utils::Hook::Nop(0x5A8E33, 11); // Enable XP Bar - Utils::Hook(0x62A2A7, Party::UIDvarIntStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x62A2A7, UIDvarIntStub, HOOK_CALL).install()->quick(); // Set NAT to open Utils::Hook::Set(0x79D898, 1); @@ -238,7 +234,7 @@ namespace Components Utils::Hook::Nop(0x4077A1, 5); // PartyMigrate_Frame // Patch playlist stuff for non-party behavior - static Game::dvar_t* partyEnable = Party::PartyEnable.get(); + static Game::dvar_t* partyEnable = PartyEnable.get(); Utils::Hook::Set(0x4A4093, &partyEnable); Utils::Hook::Set(0x4573F1, &partyEnable); Utils::Hook::Set(0x5B1A0C, &partyEnable); @@ -248,12 +244,6 @@ namespace Components Utils::Hook::Xor(0x4573FA, 1); Utils::Hook::Xor(0x5B1A17, 1); - // Fix xstartlobby - //Utils::Hook::Set(0x5B71CD, 0xEB); - - // Patch party_minplayers to 1 and protect it - //Utils::Hook(0x4D5D51, Party::RegisterMinPlayers, HOOK_CALL).install()->quick(); - // Set ui_maxclients to sv_maxclients Utils::Hook::Set(0x42618F, "sv_maxclients"); Utils::Hook::Set(0x4D3756, "sv_maxclients"); @@ -267,9 +257,6 @@ namespace Components Utils::Hook::Xor(0x4D376D, Game::DVAR_LATCH); Utils::Hook::Xor(0x5E3789, Game::DVAR_LATCH); - // Patch Live_PlayerHasLoopbackAddr - //Utils::Hook::Set(0x418F30, 0x90C3C033); - Command::Add("connect", [](Command::Params* params) { if (params->size() < 2) @@ -284,33 +271,34 @@ namespace Components } else { - Party::Connect(Network::Address(params->get(1))); + Connect(Network::Address(params->get(1))); } }); + Command::Add("reconnect", [](Command::Params*) { - Party::Connect(Party::Container.target); + Connect(Container.target); }); if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) { Scheduler::Loop([] { - if (Party::Container.valid) + if (Container.valid) { - if ((Game::Sys_Milliseconds() - Party::Container.joinTime) > 10'000) + if ((Game::Sys_Milliseconds() - Container.joinTime) > 10'000) { - Party::Container.valid = false; - Party::ConnectError("Server connection timed out."); + Container.valid = false; + ConnectError("Server connection timed out."); } } - if (Party::Container.awaitingPlaylist) + if (Container.awaitingPlaylist) { - if ((Game::Sys_Milliseconds() - Party::Container.requestTime) > 5'000) + if ((Game::Sys_Milliseconds() - Container.requestTime) > 5'000) { - Party::Container.awaitingPlaylist = false; - Party::ConnectError("Playlist request timed out."); + Container.awaitingPlaylist = false; + ConnectError("Playlist request timed out."); } } @@ -339,8 +327,8 @@ namespace Components } else { - maxClientCount = Dvar::Var("party_maxplayers").get(); - clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); + maxClientCount = *Game::party_maxplayers ? (*Game::party_maxplayers)->current.integer : 18; + clientCount = Game::PartyHost_CountMembers(Game::g_lobbyData); } Utils::InfoString info; @@ -365,7 +353,7 @@ namespace Components info.set("voiceChat", (Voice::SV_VoiceEnabled() ? "1" : "0")); // Ensure mapname is set - if (info.get("mapname").empty() || Party::IsInLobby()) + if (info.get("mapname").empty() || IsInLobby()) { info.set("mapname", Dvar::Var("ui_mapname").get()); } @@ -374,14 +362,14 @@ namespace Components { info.set("usermaphash", Utils::String::VA("%i", Maps::GetUserMap()->getHash())); } - else if (Party::IsInUserMapLobby()) + else if (IsInUserMapLobby()) { info.set("usermaphash", Utils::String::VA("%i", Maps::GetUsermapHash(info.get("mapname")))); } if (Dedicated::IsEnabled()) { - info.set("sv_motd", Dvar::Var("sv_motd").get()); + info.set("sv_motd", Dedicated::SVMOTD.get()); } // Set matchtype @@ -402,133 +390,172 @@ namespace Components info.set("matchtype", "0"); } - info.set("wwwDownload", (Dvar::Var("sv_wwwDownload").get() ? "1" : "0")); - info.set("wwwUrl", Dvar::Var("sv_wwwBaseUrl").get()); + info.set("wwwDownload", (Download::SV_wwwDownload.get() ? "1" : "0")); + info.set("wwwUrl", Download::SV_wwwBaseUrl.get()); Network::SendCommand(address, "infoResponse", "\\" + info.build()); }); Network::OnClientPacket("infoResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data) { - Utils::InfoString info(data); + const Utils::InfoString info(data); + + const auto _0 = gsl::finally([&] + { + ServerList::Insert(address, info); + Friends::UpdateServer(address, info.get("hostname"), info.get("mapname")); + }); // Handle connection - if (Party::Container.valid) + if (!Container.valid) { - if (Party::Container.target == address) - { - // Invalidate handler for future packets - Party::Container.valid = false; - Party::Container.info = info; - - Party::Container.matchType = atoi(info.get("matchtype").data()); - uint32_t securityLevel = static_cast(atoi(info.get("securityLevel").data())); - bool isUsermap = !info.get("usermaphash").empty(); - unsigned int usermapHash = atoi(info.get("usermaphash").data()); - - std::string mod = (*Game::fs_gameDirVar)->current.string; - - // set fast server stuff here so its updated when we go to download stuff - if (info.get("wwwDownload") == "1"s) - { - Dvar::Var("sv_wwwDownload").set(true); - Dvar::Var("sv_wwwBaseUrl").set(info.get("wwwUrl")); - } - else - { - Dvar::Var("sv_wwwDownload").set(false); - Dvar::Var("sv_wwwBaseUrl").set(""); - } - - if (info.get("challenge") != Party::Container.challenge) - { - Party::ConnectError("Invalid join response: Challenge mismatch."); - } - else if (securityLevel > Auth::GetSecurityLevel()) - { - //Party::ConnectError(Utils::VA("Your security level (%d) is lower than the server's (%d)", Auth::GetSecurityLevel(), securityLevel)); - Command::Execute("closemenu popup_reconnectingtoparty"); - Auth::IncreaseSecurityLevel(securityLevel, "reconnect"); - } - else if (!Party::Container.matchType) - { - Party::ConnectError("Server is not hosting a match."); - } - else if (Party::Container.matchType > 2 || Party::Container.matchType < 0) - { - Party::ConnectError("Invalid join response: Unknown matchtype"); - } - else if (Party::Container.info.get("mapname").empty() || Party::Container.info.get("gametype").empty()) - { - Party::ConnectError("Invalid map or gametype."); - } - else if (Party::Container.info.get("isPrivate") == "1"s && !Dvar::Var("password").get().length()) - { - Party::ConnectError("A password is required to join this server! Set it at the bottom of the serverlist."); - } - else if (isUsermap && usermapHash != Maps::GetUsermapHash(info.get("mapname"))) - { - Command::Execute("closemenu popup_reconnectingtoparty"); - Download::InitiateMapDownload(info.get("mapname"), info.get("isPrivate") == "1"); - } - else if (!info.get("fs_game").empty() && Utils::String::ToLower(mod) != Utils::String::ToLower(info.get("fs_game"))) - { - Command::Execute("closemenu popup_reconnectingtoparty"); - Download::InitiateClientDownload(info.get("fs_game"), info.get("isPrivate") == "1"s); - } - else if (!Dvar::Var("fs_game").get().empty() && info.get("fs_game").empty()) - { - Game::Dvar_SetString(*Game::fs_gameDirVar, ""); - - if (Dvar::Var("cl_modVidRestart").get()) - { - Command::Execute("vid_restart", false); - } - - Command::Execute("reconnect", false); - } - else - { - if (!Maps::CheckMapInstalled(Party::Container.info.get("mapname").data(), true)) return; - - Party::Container.motd = info.get("sv_motd"); - - if (Party::Container.matchType == 1) // Party - { - // Send playlist request - Party::Container.requestTime = Game::Sys_Milliseconds(); - Party::Container.awaitingPlaylist = true; - Network::SendCommand(Party::Container.target, "getplaylist", Dvar::Var("password").get()); - - // This is not a safe method - // TODO: Fix actual error! - if (Game::CL_IsCgameInitialized()) - { - Command::Execute("disconnect", true); - } - } - else if (Party::Container.matchType == 2) // Match - { - if (atoi(Party::Container.info.get("clients").data()) >= atoi(Party::Container.info.get("sv_maxclients").data())) - { - Party::ConnectError("@EXE_SERVERISFULL"); - } - else - { - Dvar::Var("xblive_privateserver").set(true); - - Game::Menus_CloseAll(Game::uiContext); - - Game::_XSESSION_INFO hostInfo; - Game::CL_ConnectFromParty(0, &hostInfo, *Party::Container.target.get(), 0, 0, Party::Container.info.get("mapname").data(), Party::Container.info.get("gametype").data()); - } - } - } - } + return; } - ServerList::Insert(address, info); - Friends::UpdateServer(address, info.get("hostname"), info.get("mapname")); + if (Container.target == address) + { + return; + } + + // Invalidate handler for future packets + Container.valid = false; + Container.info = info; + + Container.matchType = atoi(info.get("matchtype").data()); + const auto securityLevel = static_cast(std::strtol(info.get("securityLevel").data(), nullptr, 10)); + bool isUsermap = !info.get("usermaphash").empty(); + const auto usermapHash = static_cast(std::strtol(info.get("usermaphash").data(), nullptr, 10)); + + std::string mod = (*Game::fs_gameDirVar)->current.string; + + // set fast server stuff here so its updated when we go to download stuff + if (info.get("wwwDownload") == "1"s) + { + Download::SV_wwwDownload.set(true); + Download::SV_wwwBaseUrl.set(info.get("wwwUrl")); + } + else + { + Download::SV_wwwDownload.set(false); + Download::SV_wwwBaseUrl.set(""); + } + + if (info.get("challenge") != Container.challenge) + { + ConnectError("Invalid join response: Challenge mismatch."); + return; + } + + if (securityLevel > Auth::GetSecurityLevel()) + { + + Command::Execute("closemenu popup_reconnectingtoparty"); + Auth::IncreaseSecurityLevel(securityLevel, "reconnect"); + return; + } + + if (!Container.matchType) + { + ConnectError("Server is not hosting a match."); + return; + } + + if (Container.matchType > 2 || Container.matchType < 0) + { + ConnectError("Invalid join response: Unknown matchtype"); + return; + } + + if (Container.info.get("mapname").empty() || Container.info.get("gametype").empty()) + { + ConnectError("Invalid map or gametype."); + return; + } + + if (Container.info.get("isPrivate") == "1"s && !Dvar::Var("password").get().empty()) + { + ConnectError("A password is required to join this server! Set it at the bottom of the serverlist."); + return; + } + + if (isUsermap && usermapHash != Maps::GetUsermapHash(info.get("mapname"))) + { + Command::Execute("closemenu popup_reconnectingtoparty"); + Download::InitiateMapDownload(info.get("mapname"), info.get("isPrivate") == "1"s); + return; + } + + if (!info.get("fs_game").empty() && Utils::String::ToLower(mod) != Utils::String::ToLower(info.get("fs_game"))) + { + Command::Execute("closemenu popup_reconnectingtoparty"); + Download::InitiateClientDownload(info.get("fs_game"), info.get("isPrivate") == "1"s); + return; + } + + if (*(*Game::fs_gameDirVar)->current.string && info.get("fs_game").empty()) + { + Game::Dvar_SetString(*Game::fs_gameDirVar, ""); + + if (Dvar::Var("cl_modVidRestart").get()) + { + Command::Execute("vid_restart", false); + } + + Command::Execute("reconnect", false); + return; + } + + if (!Maps::CheckMapInstalled(Container.info.get("mapname"), true)) + { + return; + } + + Container.motd = info.get("sv_motd"); + + if (Container.matchType == 1) // Party + { + // Send playlist request + Container.requestTime = Game::Sys_Milliseconds(); + Container.awaitingPlaylist = true; + Network::SendCommand(Party::Container.target, "getplaylist", Dvar::Var("password").get()); + + // This is not a safe method + // TODO: Fix actual error! + if (Game::CL_IsCgameInitialized()) + { + Command::Execute("disconnect", true); + } + } + else if (Container.matchType == 2) // Match + { + int clients; + int maxClients; + + try + { + clients = std::stoi(Container.info.get("clients")); + maxClients = std::stoi(Container.info.get("sv_maxclients")); + } + catch ([[maybe_unused]] const std::exception& ex) + { + ConnectError("Invalid info string"); + return; + } + + if (clients >= maxClients) + { + ConnectError("@EXE_SERVERISFULL"); + } + else + { + Dvar::Var("xblive_privateserver").set(true); + + Game::Menus_CloseAll(Game::uiContext); + + Game::_XSESSION_INFO hostInfo; + Game::CL_ConnectFromParty(0, &hostInfo, *Container.target.get(), 0, 0, Container.info.get("mapname").data(), Container.info.get("gametype").data()); + } + } }); } } diff --git a/src/Components/Modules/Party.hpp b/src/Components/Modules/Party.hpp index 3287cecd..cac3e7bd 100644 --- a/src/Components/Modules/Party.hpp +++ b/src/Components/Modules/Party.hpp @@ -50,8 +50,6 @@ namespace Components static SteamID GenerateLobbyId(); - static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, unsigned __int16 flag, const char* description); - static DWORD UIDvarIntStub(char* dvar); }; } diff --git a/src/Components/Modules/ServerInfo.cpp b/src/Components/Modules/ServerInfo.cpp index 1dfcba0c..a1a6fb4f 100644 --- a/src/Components/Modules/ServerInfo.cpp +++ b/src/Components/Modules/ServerInfo.cpp @@ -140,7 +140,7 @@ namespace Components if (!maxClientCount) { - maxClientCount = Dvar::Var("party_maxplayers").get(); + maxClientCount = *Game::party_maxplayers ? (*Game::party_maxplayers)->current.integer : 18; } Utils::InfoString info(Game::Dvar_InfoString_Big(Game::DVAR_SERVERINFO)); @@ -166,7 +166,7 @@ namespace Components // 1 - Party, use Steam_JoinLobby to connect // 2 - Match, use CL_ConnectFromParty to connect - if (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get()) // Party hosting + if (Party::IsEnabled() && Dvar::Var("party_host").get()) // Party hosting { info.set("matchtype", "1"); } diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index e50011bc..b2426ff2 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -113,7 +113,7 @@ namespace Components { if (server->svRunning) { - if (!sorting && !Maps::CheckMapInstalled(server->mapname.data())) + if (!sorting && !Maps::CheckMapInstalled(server->mapname)) { return Utils::String::VA("^1%s", Game::UI_LocalizeMapName(server->mapname.data())); } diff --git a/src/Game/Dvars.cpp b/src/Game/Dvars.cpp index be172a8a..d6471624 100644 --- a/src/Game/Dvars.cpp +++ b/src/Game/Dvars.cpp @@ -77,6 +77,9 @@ namespace Game const dvar_t** loc_warnings = reinterpret_cast(0x62C8700); const dvar_t** loc_warningsAsErrors = reinterpret_cast(0x62C86FC); + const dvar_t** party_minplayers = reinterpret_cast(0x1081BFC); + const dvar_t** party_maxplayers = reinterpret_cast(0x1080998); + __declspec(naked) void Dvar_SetVariant(dvar_t*, DvarValue, DvarSetSource) { static DWORD Dvar_SetVariant_t = 0x647400; diff --git a/src/Game/Dvars.hpp b/src/Game/Dvars.hpp index f54eaba7..a0d9730d 100644 --- a/src/Game/Dvars.hpp +++ b/src/Game/Dvars.hpp @@ -127,6 +127,9 @@ namespace Game extern const dvar_t** loc_warnings; extern const dvar_t** loc_warningsAsErrors; + extern const dvar_t** party_minplayers; + extern const dvar_t** party_maxplayers; + extern void Dvar_SetVariant(dvar_t* var, DvarValue value, DvarSetSource source); extern void Dvar_SetFromStringFromSource(const dvar_t* dvar, const char* string, DvarSetSource source); }