[Dvar]: Don't lookup dvar every time we need it (#677)

* [Dvar]: Don't lookup dvar every time we need it

* [Console]: Cleanup the code

* [Party]: Improve source code quality

* [Party]: Update
This commit is contained in:
Edo 2022-12-31 15:03:33 +01:00 committed by GitHub
parent 676854167b
commit 86b402a5a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 428 additions and 419 deletions

View File

@ -1,6 +1,8 @@
#include <STDInclude.hpp>
#include "Console.hpp"
#include "Terminus_4.49.1.ttf.hpp"
#include <version.hpp>
#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<int>();
//maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData*>(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<int>(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<int>(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<float>(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<int>(GetTickCount64()); // Make our compiler happy
if ((currentTime - Console::LastRefresh) > 250)
auto currentTime = static_cast<int>(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<char>(c), static_cast<char>(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<int>(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<char>(c);
Console::LineBuffer[Console::LineBufferIndex] = '\0';
LineBuffer[LineBufferIndex++] = static_cast<char>(c);
LineBuffer[LineBufferIndex] = '\0';
wprintw(InputWindow, "%c", static_cast<char>(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<HWND>(0x64A3288);
float scale = GetDpiScale(parent);
auto scale = GetDpiScale(parent);
if (isInputBox)
{
int newX = childX; // No change!
int newY = static_cast<int>((newParentRect.bottom - newParentRect.top) - 65 * scale);
int newWidth = static_cast<int>((newParentRect.right - newParentRect.left) - 29 * scale);
int newHeight = static_cast<int>((childRect.bottom - childRect.top) * scale); // No change!
auto newX = childX; // No change!
auto newY = static_cast<int>((newParentRect.bottom - newParentRect.top) - 65 * scale);
auto newWidth = static_cast<int>((newParentRect.right - newParentRect.left) - 29 * scale);
auto newHeight = static_cast<int>((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<int>((newParentRect.right - newParentRect.left) - 29);
int margin = 70;
auto newX = childX; // No change!
auto newY = childY; // No change!
auto newWidth = static_cast<int>((newParentRect.right - newParentRect.left) - 29);
#ifdef REMOVE_HEADERBAR
margin = 10;
constexpr auto margin = 10;
#else
constexpr auto margin = 70;
#endif
int newHeight = static_cast<int>((newParentRect.bottom - newParentRect.top) - 74 * scale - margin);
auto newHeight = static_cast<int>((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<HWND>(0x64A328C);
unsigned int totalChars;
unsigned int totalClearLength = 0;
auto totalClearLength = 0;
char str[maxAffectedChars];
unsigned int fetchedCharacters = static_cast<unsigned int>(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<LPCVOID>(&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<LRESULT CALLBACK(HWND, UINT, WPARAM, unsigned int)>(0x64DC50)(hWnd, Msg, wParam, lParam);
}
@ -645,14 +631,14 @@ namespace Components
void Console::ApplyConsoleStyle()
{
Utils::Hook::Set<BYTE>(0x428A8E, 0); // Adjust logo Y pos
Utils::Hook::Set<BYTE>(0x428A90, 0); // Adjust logo X pos
Utils::Hook::Set<BYTE>(0x428AF2, 67); // Adjust output Y pos
Utils::Hook::Set<DWORD>(0x428AC5, 397); // Adjust input Y pos
Utils::Hook::Set<DWORD>(0x428951, 609); // Reduce window width
Utils::Hook::Set<DWORD>(0x42895D, 423); // Reduce window height
Utils::Hook::Set<DWORD>(0x428AC0, 597); // Reduce input width
Utils::Hook::Set<DWORD>(0x428AED, 596); // Reduce output width
Utils::Hook::Set<std::uint8_t>(0x428A8E, 0); // Adjust logo Y pos
Utils::Hook::Set<std::uint8_t>(0x428A90, 0); // Adjust logo X pos
Utils::Hook::Set<std::uint8_t>(0x428AF2, 67); // Adjust output Y pos
Utils::Hook::Set<std::uint32_t>(0x428AC5, 397); // Adjust input Y pos
Utils::Hook::Set<std::uint32_t>(0x428951, 609); // Reduce window width
Utils::Hook::Set<std::uint32_t>(0x42895D, 423); // Reduce window height
Utils::Hook::Set<std::uint32_t>(0x428AC0, 597); // Reduce input width
Utils::Hook::Set<std::uint32_t>(0x428AED, 596); // Reduce output width
DWORD fontsInstalled;
CustomConsoleFont = AddFontMemResourceEx(const_cast<void*>(reinterpret_cast<const void*>(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<int>(std::strlen(Game::g_consoleField->buffer));
Game::Field_AdjustScroll(Game::ScrPlace_GetFullPlacement(), Game::g_consoleField);
@ -878,24 +865,24 @@ namespace Components
Utils::Hook::Set<float*>(0x5A4400, consoleColor);
// Remove the need to type '\' or '/' to send a console command
Utils::Hook::Set<BYTE>(0x431565, 0xEB);
Utils::Hook::Set<std::uint8_t>(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<void()>(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();
}
}
}

View File

@ -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();

View File

@ -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<bool>())
if (SVLanOnly.get<bool>())
{
return;
}
@ -148,18 +149,17 @@ namespace Components
Dedicated::Dedicated()
{
Dedicated::COMLogFilter = Dvar::Register<bool>("com_logFilter", true,
COMLogFilter = Dvar::Register<bool>("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<bool>("sv_lanOnly", false,
Game::DVAR_NONE, "Don't act as node");
SVLanOnly = Dvar::Register<bool>("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<BYTE>(0x683370, 0xC3); // steam sometimes doesn't like the server
@ -199,7 +199,7 @@ namespace Components
Utils::Hook::Set<DWORD>(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<BYTE>(0x519DDF, 0);
@ -217,18 +217,18 @@ namespace Components
Utils::Hook::Set<BYTE>(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<DWORD>(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<const char*>("sv_motd", "", Game::DVAR_NONE, "A custom message of the day for servers");
SVMOTD = Dvar::Register<const char*>("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);
}

View File

@ -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();

View File

@ -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<std::string>();
auto fastHost = SV_wwwBaseUrl.get<std::string>();
if (Utils::String::StartsWith(fastHost, "https://"))
{
download->thread.detach();
@ -173,7 +176,7 @@ namespace Components
// -mod.ff
// /-mod2
// ...
if (Dvar::Var("sv_wwwDownload").get<bool>())
if (SV_wwwDownload.get<bool>())
{
if (!Utils::String::EndsWith(fastHost, "/")) fastHost.append("/");
url = fastHost + path;
@ -720,8 +723,8 @@ namespace Components
Scheduler::Once([]
{
Dvar::Register<bool>("sv_wwwDownload", false, Game::DVAR_NONE, "Set to true to enable downloading maps/mods from an external server.");
Dvar::Register<const char*>("sv_wwwBaseUrl", "", Game::DVAR_NONE, "Set to the base url for the external map download.");
SV_wwwDownload = Dvar::Register<bool>("sv_wwwDownload", false, Game::DVAR_NONE, "Set to true to enable downloading maps/mods from an external server.");
SV_wwwBaseUrl = Dvar::Register<const char*>("sv_wwwBaseUrl", "", Game::DVAR_NONE, "Set to the base url for the external map download.");
}, Scheduler::Pipeline::MAIN);
}

View File

@ -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
{

View File

@ -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;

View File

@ -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);

View File

@ -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<int>(name, 1, 1, max, Game::DVAR_INIT | flag, description).get<Game::dvar_t*>();
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<const char*>()));
return (IsInLobby() && Maps::IsUserMap(Dvar::Var("ui_mapname").get<const char*>()));
}
bool Party::IsEnabled()
@ -160,7 +156,7 @@ namespace Components
Party::Party()
{
Party::PartyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsEnabled(), Game::DVAR_NONE, "Enable party system");
PartyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsEnabled(), Game::DVAR_NONE, "Enable party system");
Dvar::Register<bool>("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<int>(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<Game::dvar_t*>();
static Game::dvar_t* partyEnable = PartyEnable.get<Game::dvar_t*>();
Utils::Hook::Set<Game::dvar_t**>(0x4A4093, &partyEnable);
Utils::Hook::Set<Game::dvar_t**>(0x4573F1, &partyEnable);
Utils::Hook::Set<Game::dvar_t**>(0x5B1A0C, &partyEnable);
@ -248,12 +244,6 @@ namespace Components
Utils::Hook::Xor<BYTE>(0x4573FA, 1);
Utils::Hook::Xor<BYTE>(0x5B1A17, 1);
// Fix xstartlobby
//Utils::Hook::Set<BYTE>(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<const char*>(0x42618F, "sv_maxclients");
Utils::Hook::Set<const char*>(0x4D3756, "sv_maxclients");
@ -267,9 +257,6 @@ namespace Components
Utils::Hook::Xor<DWORD>(0x4D376D, Game::DVAR_LATCH);
Utils::Hook::Xor<DWORD>(0x5E3789, Game::DVAR_LATCH);
// Patch Live_PlayerHasLoopbackAddr
//Utils::Hook::Set<DWORD>(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<int>();
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData*>(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<const char*>());
}
@ -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<std::string>());
info.set("sv_motd", Dedicated::SVMOTD.get<std::string>());
}
// Set matchtype
@ -402,133 +390,172 @@ namespace Components
info.set("matchtype", "0");
}
info.set("wwwDownload", (Dvar::Var("sv_wwwDownload").get<bool>() ? "1" : "0"));
info.set("wwwUrl", Dvar::Var("sv_wwwBaseUrl").get<std::string>());
info.set("wwwDownload", (Download::SV_wwwDownload.get<bool>() ? "1" : "0"));
info.set("wwwUrl", Download::SV_wwwBaseUrl.get<std::string>());
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<uint32_t>(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<std::string>().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<std::string>().empty() && info.get("fs_game").empty())
{
Game::Dvar_SetString(*Game::fs_gameDirVar, "");
if (Dvar::Var("cl_modVidRestart").get<bool>())
{
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<std::string>());
// 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<uint32_t>(std::strtol(info.get("securityLevel").data(), nullptr, 10));
bool isUsermap = !info.get("usermaphash").empty();
const auto usermapHash = static_cast<uint32_t>(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<std::string>().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<bool>())
{
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<std::string>());
// 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());
}
}
});
}
}

View File

@ -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);
};
}

View File

@ -140,7 +140,7 @@ namespace Components
if (!maxClientCount)
{
maxClientCount = Dvar::Var("party_maxplayers").get<int>();
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<bool>() && Dvar::Var("party_host").get<bool>()) // Party hosting
if (Party::IsEnabled() && Dvar::Var("party_host").get<bool>()) // Party hosting
{
info.set("matchtype", "1");
}

View File

@ -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()));
}

View File

@ -77,6 +77,9 @@ namespace Game
const dvar_t** loc_warnings = reinterpret_cast<const dvar_t**>(0x62C8700);
const dvar_t** loc_warningsAsErrors = reinterpret_cast<const dvar_t**>(0x62C86FC);
const dvar_t** party_minplayers = reinterpret_cast<const dvar_t**>(0x1081BFC);
const dvar_t** party_maxplayers = reinterpret_cast<const dvar_t**>(0x1080998);
__declspec(naked) void Dvar_SetVariant(dvar_t*, DvarValue, DvarSetSource)
{
static DWORD Dvar_SetVariant_t = 0x647400;

View File

@ -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);
}