diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index 5285d2af..60945135 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -2,6 +2,23 @@ namespace Components { + WINDOW* Console::OutputWindow; + WINDOW* Console::InputWindow; + WINDOW* Console::InfoWindow; + + int Console::OutputTop = 0; + int Console::OutBuffer = 0; + int Console::LastRefresh = 0; + + int Console::Height = 25; + int Console::Width = 80; + + char Console::LineBuffer[1024] = { 0 }; + char Console::LineBuffer2[1024] = { 0 }; + int Console::LineBufferIndex = 0; + + bool Console::HasConsole = false; + char** Console::GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType) { if (path == (char*)0xBAADF00D || path == (char*)0xCDCDCDCD || IsBadReadPtr(path, 1)) return nullptr; @@ -20,66 +37,353 @@ namespace Components Utils::Hook::Set(0xA15F38, 0); } + void Console::RefreshStatus() + { + std::string mapname = Dvar::Var("mapname").Get(); + std::string hostname = Colors::Strip(Dvar::Var("sv_hostname").Get()); + SetConsoleTitle(hostname.data()); + + int clientCount = 0; + int maxclientCount = *Game::svs_numclients; + + if (maxclientCount) + { + for (int i = 0; i < maxclientCount; i++) + { + if (Game::svs_clients[i].state >= 3) + { + clientCount++; + } + } + } + else + { + //maxclientCount = Dvar::Var("sv_maxclients").Get(); + maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); + clientCount = Game::PartyHost_CountMembers((Game::PartyData_s*)0x1081C00); + } + + wclear(Console::InfoWindow); + wprintw(Console::InfoWindow, "%s : %d/%d players : map %s", hostname.data(), clientCount, maxclientCount, (mapname.size() ? mapname.data() : "none")); + wnoutrefresh(Console::InfoWindow); + } + + void Console::ShowPrompt() + { + wattron(Console::InputWindow, COLOR_PAIR(10) | A_BOLD); + wprintw(Console::InputWindow, "%s> ", VERSION_STR); + } + + void Console::RefreshOutput() + { + prefresh(Console::OutputWindow, ((Console::OutputTop > 0) ? (Console::OutputTop - 1) : 0), 0, 1, 0, Console::Height - 2, Console::Width); + } + + void Console::ScrollOutput(int amount) + { + Console::OutputTop += amount; + + if (Console::OutputTop > OUTPUT_MAX_TOP) + { + Console::OutputTop = OUTPUT_MAX_TOP; + } + else if (Console::OutputTop < 0) + { + Console::OutputTop = 0; + } + + // make it only scroll the top if there's more than HEIGHT lines + if (Console::OutBuffer >= 0) + { + Console::OutBuffer += amount; + + if (Console::OutBuffer >= Console::Height) + { + Console::OutBuffer = -1; + } + + if (Console::OutputTop < Console::Height) + { + Console::OutputTop = 0; + } + } + } + + const char* Console::Input() + { + if (!Console::HasConsole) + { + Console::ShowPrompt(); + wrefresh(Console::InputWindow); + Console::HasConsole = true; + } + + int currentTime = (int)GetTickCount64(); // Make our compiler happy + if ((currentTime - Console::LastRefresh) > 250) + { + Console::RefreshOutput(); + Console::RefreshStatus(); + Console::LastRefresh = currentTime; + } + + int c = wgetch(Console::InputWindow); + + if (c == ERR) + { + return NULL; + } + + switch (c) + { + case '\r': + case 459: // keypad enter + { + wattron(Console::OutputWindow, COLOR_PAIR(10) | A_BOLD); + wprintw(Console::OutputWindow, "%s", "]"); + + if (Console::LineBufferIndex) + { + wprintw(Console::OutputWindow, "%s", Console::LineBuffer); + } + + wprintw(Console::OutputWindow, "%s", "\n"); + wattroff(Console::OutputWindow, A_BOLD); + wclear(Console::InputWindow); + + Console::ShowPrompt(); + + wrefresh(Console::InputWindow); + + Console::ScrollOutput(1); + Console::RefreshOutput(); + + if (Console::LineBufferIndex) + { + strcpy(Console::LineBuffer2, Console::LineBuffer); + strcat(Console::LineBuffer, "\n"); + Console::LineBufferIndex = 0; + return Console::LineBuffer; + } + + break; + } + case 'c' - 'a' + 1: // ctrl-c + case 27: + { + Console::LineBuffer[0] = '\0'; + Console::LineBufferIndex = 0; + + wclear(Console::InputWindow); + + Console::ShowPrompt(); + + wrefresh(Console::InputWindow); + break; + } + case 8: // backspace + { + if (Console::LineBufferIndex > 0) + { + Console::LineBufferIndex--; + Console::LineBuffer[Console::LineBufferIndex] = '\0'; + + wprintw(Console::InputWindow, "%c %c", (char)c, (char)c); + wrefresh(Console::InputWindow); + } + break; + } + case KEY_PPAGE: + { + Console::ScrollOutput(-1); + Console::RefreshOutput(); + break; + } + case KEY_NPAGE: + { + Console::ScrollOutput(1); + Console::RefreshOutput(); + break; + } + case KEY_UP: + { + wclear(Console::InputWindow); + Console::ShowPrompt(); + wprintw(Console::InputWindow, "%s", Console::LineBuffer2); + wrefresh(Console::InputWindow); + + strcpy(Console::LineBuffer, Console::LineBuffer2); + Console::LineBufferIndex = strlen(Console::LineBuffer); + break; + } + default: + if (c <= 127 && Console::LineBufferIndex < 1022) + { + // temporary workaround , find out what overwrites our index later on + //consoleLineBufferIndex = strlen(consoleLineBuffer); + + Console::LineBuffer[Console::LineBufferIndex++] = (char)c; + Console::LineBuffer[Console::LineBufferIndex] = '\0'; + wprintw(Console::InputWindow, "%c", (char)c); + wrefresh(Console::InputWindow); + } + break; + } + + return NULL; + } + + void Console::Destroy() + { + delwin(Console::OutputWindow); + delwin(Console::InputWindow); + delwin(Console::InfoWindow); + endwin(); + } + + void Console::Create() + { + Console::OutputTop = 0; + Console::OutBuffer = 0; + Console::LastRefresh = 0; + Console::LineBufferIndex = 0; + Console::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; + } + else + { + Console::Height = 25; + Console::Width = 80; + } + + initscr(); + raw(); + noecho(); + + Console::OutputWindow = newpad(OUTPUT_HEIGHT, Console::Width); + Console::InputWindow = newwin(1, Console::Width, Console::Height - 1, 0); + Console::InfoWindow = newwin(1, Console::Width, 0, 0); + + scrollok(Console::OutputWindow, true); + scrollok(Console::InputWindow, true); + nodelay(Console::InputWindow, true); + keypad(Console::InputWindow, true); + + if (has_colors()) + { + start_color(); + init_pair(1, COLOR_BLACK, COLOR_WHITE); + init_pair(2, COLOR_WHITE, COLOR_BLACK); + init_pair(3, COLOR_RED, COLOR_BLACK); + init_pair(4, COLOR_GREEN, COLOR_BLACK); + init_pair(5, COLOR_YELLOW, COLOR_BLACK); + init_pair(6, COLOR_BLUE, COLOR_BLACK); + init_pair(7, COLOR_CYAN, COLOR_BLACK); + init_pair(8, COLOR_RED, COLOR_BLACK); + init_pair(9, COLOR_WHITE, COLOR_BLACK); + init_pair(10, COLOR_WHITE, COLOR_BLACK); + } + + wbkgd(Console::InfoWindow, COLOR_PAIR(1)); + + wrefresh(Console::InfoWindow); + wrefresh(Console::InputWindow); + + Console::RefreshOutput(); + } + + void Console::Error(const char* format, ...) + { + static char buffer[32768]; + + va_list va; + va_start(va, format); + _vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + + Game::Com_Printf(0, "ERROR:\n"); + Game::Com_Printf(0, buffer); + + Console::RefreshOutput(); + + if (IsDebuggerPresent()) + { + while (true) Sleep(5000); + } + + TerminateProcess(GetCurrentProcess(), 0xDEADDEAD); + } + + void Console::Print(const char* message) + { + const char* p = message; + while (*p != '\0') + { + if (*p == '\n') + { + Console::ScrollOutput(1); + } + + if (*p == '^') + { + char color; + p++; + + color = (*p - '0'); + + if (color < 9 && color > 0) + { + wattron(Console::OutputWindow, COLOR_PAIR(color + 2)); + p++; + continue; + } + } + + waddch(Console::OutputWindow, *p); + + p++; + } + + wattron(Console::OutputWindow, COLOR_PAIR(9)); + + int currentTime = (int)GetTickCount64(); // Make our compiler happy + + if (!Console::HasConsole) + { + Console::RefreshOutput(); + } + else if ((currentTime - Console::LastRefresh) > 100) + { + Console::RefreshOutput(); + Console::LastRefresh = currentTime; + } + } + Console::Console() { -// if (!Dedicated::IsDedicated()) -// { -// FreeConsole(); -// return; -// } -// -// initscr(); -// raw(); -// noecho(); -// -// // patch functions -// #define WIDTH 80 -// #define HEIGHT 25 -// #define OUTPUT_HEIGHT 250 -// #define OUTPUT_MAX_TOP (OUTPUT_HEIGHT - (HEIGHT - 2)) -// -// WINDOW* outputWindow = newpad(OUTPUT_HEIGHT, WIDTH); -// WINDOW* inputWindow = newwin(1, WIDTH, HEIGHT - 1, 0); -// WINDOW* infoWindow = newwin(1, WIDTH, 0, 0); -// -// scrollok(outputWindow, true); -// scrollok(inputWindow, true); -// nodelay(inputWindow, true); -// keypad(inputWindow, true); -// -// if (has_colors()) -// { -// start_color(); -// init_pair(1, COLOR_BLACK, COLOR_WHITE); -// init_pair(2, COLOR_WHITE, COLOR_BLACK); -// init_pair(3, COLOR_RED, COLOR_BLACK); -// init_pair(4, COLOR_GREEN, COLOR_BLACK); -// init_pair(5, COLOR_YELLOW, COLOR_BLACK); -// init_pair(6, COLOR_BLUE, COLOR_BLACK); -// init_pair(7, COLOR_CYAN, COLOR_BLACK); -// init_pair(8, COLOR_RED, COLOR_BLACK); -// init_pair(9, COLOR_WHITE, COLOR_BLACK); -// init_pair(10, COLOR_WHITE, COLOR_BLACK); -// //init_pair(2, COLOR_WHITE, COLOR_BLACK); -// } -// -// wbkgd(infoWindow, COLOR_PAIR(1)); -// -// wrefresh(infoWindow); -// //wrefresh(outputWindow); -// wrefresh(inputWindow); -// -// //prefresh(outputWindow, (currentOutputTop >= 1) ? currentOutputTop - 1 : 0, 0, 1, 0, HEIGHT - 2, WIDTH); -// -// waddch(outputWindow, 'g'); -// waddch(outputWindow, 'i'); -// -// prefresh(outputWindow, 0, 0, 1, 0, HEIGHT - 2, WIDTH); - // External console - if (Flags::HasFlag("console") || Dedicated::IsDedicated() || ZoneBuilder::IsEnabled()) + if (Flags::HasFlag("console") || ZoneBuilder::IsEnabled()) // ZoneBuilder uses the game's console, untile the native one is adapted. + { + FreeConsole(); + Utils::Hook::Nop(0x60BB58, 11); + } + else if(Dedicated::IsDedicated()/* || ZoneBuilder::IsEnabled()*/) { 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(); + } + else + { + FreeConsole(); } // Console '%s: %s> ' string diff --git a/src/Components/Modules/Console.hpp b/src/Components/Modules/Console.hpp index 658a9780..8db7ec8c 100644 --- a/src/Components/Modules/Console.hpp +++ b/src/Components/Modules/Console.hpp @@ -1,3 +1,6 @@ +#define OUTPUT_HEIGHT 250 +#define OUTPUT_MAX_TOP (OUTPUT_HEIGHT - (Console::Height - 2)) + namespace Components { class Console : public Component @@ -9,5 +12,35 @@ namespace Components private: static void ToggleConsole(); static char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType); + + private: + // Text-based console stuff + static WINDOW* OutputWindow; + static WINDOW* InputWindow; + static WINDOW* InfoWindow; + + static int Width; + static int Height; + + static int OutputTop; + static int OutBuffer; + static int LastRefresh; + + static char LineBuffer[1024]; + static char LineBuffer2[1024]; + static int LineBufferIndex; + + static bool HasConsole; + + static void ShowPrompt(); + static void RefreshStatus(); + static void RefreshOutput(); + static void ScrollOutput(int amount); + + static const char* Input(); + static void Print(const char* message); + static void Error(const char* format, ...); + static void Create(); + static void Destroy(); }; } diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index 4e5ae36e..0b16ae4b 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -119,7 +119,7 @@ namespace Components Party::Party() { - static Game::dvar_t* partyEnable = Dvar::Register("party_enable", false, Game::dvar_flag::DVAR_FLAG_NONE, "Enable party system").Get(); + static Game::dvar_t* partyEnable = Dvar::Register("party_enable", Dedicated::IsDedicated(), Game::dvar_flag::DVAR_FLAG_NONE, "Enable party system").Get(); Dvar::Register("xblive_privatematch", true, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED, "").Get(); // various changes to SV_DirectConnect-y stuff to allow non-party joinees diff --git a/src/Main.cpp b/src/Main.cpp index 71f917c5..078129e2 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -7,8 +7,6 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser if (ul_reason_for_call == DLL_PROCESS_ATTACH) { - FreeConsole(); - DWORD oldProtect; VirtualProtect(GetModuleHandle(NULL), 0x6C73000, PAGE_EXECUTE_READWRITE, &oldProtect); diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 63e8ab4b..bd155df7 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -29,14 +29,22 @@ #include // Submodules +// Ignore the warnings, it's no our code! #pragma warning(push) #pragma warning(disable: 4005) +#pragma warning(disable: 6001) +#pragma warning(disable: 6011) +#pragma warning(disable: 6031) +#pragma warning(disable: 6255) +#pragma warning(disable: 6258) +#pragma warning(disable: 6386) +#pragma warning(disable: 6387) #define ZLIB_CONST #define ASIO_STANDALONE #include #include -//#include +#include #include #pragma warning(pop)