Add legacy menus (#249)
* fix legacy menu files * minor changes * make setclientdvar behave more like older games Co-authored-by: quaK <38787176+Joelrau@users.noreply.github.com>
This commit is contained in:
parent
6a5eae3a0e
commit
8e7d30b484
220
src/client/component/menus.cpp
Normal file
220
src/client/component/menus.cpp
Normal file
@ -0,0 +1,220 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "menus.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "console.hpp"
|
||||
#include "command.hpp"
|
||||
|
||||
#include "utils/hook.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace menus
|
||||
{
|
||||
void* ui_info_array;
|
||||
std::string script_main_menu;
|
||||
|
||||
bool UI_AllowScriptMenuResponse()
|
||||
{
|
||||
return *reinterpret_cast<bool*>(0x3532A7C_b);
|
||||
}
|
||||
|
||||
bool UI_Started()
|
||||
{
|
||||
return *reinterpret_cast<bool*>(0x2ED2074_b);
|
||||
}
|
||||
|
||||
bool UI_KeysBypassMenu()
|
||||
{
|
||||
game::dvar_t* cl_bypassMouseInput = game::Dvar_FindVar("cl_bypassMouseInput");
|
||||
if (cl_bypassMouseInput && cl_bypassMouseInput->current.enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CL_ShowSystemCursor(int a1)
|
||||
{
|
||||
return utils::hook::invoke<void>(0x5BAA60_b, a1);
|
||||
}
|
||||
|
||||
void CL_GetCursorPos(tagPOINT* position)
|
||||
{
|
||||
return utils::hook::invoke<void>(0x5BA800_b, position);
|
||||
}
|
||||
|
||||
int Menu_Count()
|
||||
{
|
||||
return *reinterpret_cast<int*>(0x352F9B8_b);
|
||||
}
|
||||
|
||||
void* Menus_FindByName(void* dc, const char* name)
|
||||
{
|
||||
return utils::hook::invoke<void*>(0x1AC810_b, dc, name);
|
||||
}
|
||||
|
||||
void Menus_Open(void* dc, void* menu, int a3)
|
||||
{
|
||||
return utils::hook::invoke<void>(0x1E1296_b, dc, menu, a3);
|
||||
}
|
||||
|
||||
void Display_MouseMove(void* dc)
|
||||
{
|
||||
return utils::hook::invoke<void>(0x180B70_b, dc);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
game::XAssetHeader load_script_menu_internal(const char* menu)
|
||||
{
|
||||
const char* menu_file = utils::string::va("ui_mp/scriptmenus/%s.menu", menu);
|
||||
return game::DB_FindXAssetHeader(game::ASSET_TYPE_MENUFILE, menu_file, 1);
|
||||
}
|
||||
|
||||
bool load_script_menu(int client_num, const char* menu)
|
||||
{
|
||||
game::XAssetHeader asset = load_script_menu_internal(menu);
|
||||
if (asset.data)
|
||||
{
|
||||
game::UI_AddMenuList(ui_info_array, asset.data, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cg_precache_script_menu(int client_num, int config_string_index)
|
||||
{
|
||||
const char* menu = game::CL_GetConfigString(config_string_index);
|
||||
if (menu)
|
||||
{
|
||||
if (!load_script_menu(client_num, menu))
|
||||
{
|
||||
game::Com_Error(game::ERR_DROP, "Could not load script menu file %s", menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
utils::hook::detour cg_set_config_values_hook;
|
||||
void cg_set_config_values_stub(int client_num)
|
||||
{
|
||||
cg_set_config_values_hook.invoke<void>(client_num);
|
||||
|
||||
auto nesting = game::R_PopRemoteScreenUpdate();
|
||||
for (auto i = 3432; i < (3432 + 50); i++)
|
||||
{
|
||||
cg_precache_script_menu(client_num, i);
|
||||
}
|
||||
game::R_PushRemoteScreenUpdate(nesting);
|
||||
}
|
||||
|
||||
void ui_mouse_event(int client_num, int x, int y)
|
||||
{
|
||||
auto scrPlaceFull = game::ScrPlace_GetViewPlacement();
|
||||
auto vX = x / (game::ScrPlace_HiResGetScaleX() * scrPlaceFull->scaleVirtualToFull[0]);
|
||||
auto vY = y / (game::ScrPlace_HiResGetScaleY() * scrPlaceFull->scaleVirtualToFull[1]);
|
||||
*reinterpret_cast<float*>(0x352E590_b) = vX; // cursorX
|
||||
*reinterpret_cast<float*>(0x352E594_b) = vY; // cursorY
|
||||
int isCursorVisible = vX >= 0.0 && vX <= 640.0 && vY >= 0.0 && vY <= 480.0;
|
||||
|
||||
if (isCursorVisible)
|
||||
{
|
||||
auto menu_count = Menu_Count();
|
||||
if (menu_count > 0)
|
||||
{
|
||||
*reinterpret_cast<float*>(reinterpret_cast<std::uintptr_t>(ui_info_array) + 16) = vX; // cursor X
|
||||
*reinterpret_cast<float*>(reinterpret_cast<std::uintptr_t>(ui_info_array) + 20) = vY; // cursor Y
|
||||
|
||||
*reinterpret_cast<int*>(reinterpret_cast<std::uintptr_t>(ui_info_array) + 24) = game::Sys_Milliseconds() + 200; // cursor time until ready to move
|
||||
|
||||
*reinterpret_cast<int*>(reinterpret_cast<std::uintptr_t>(ui_info_array) + 28) = isCursorVisible; // ingame cursor visible
|
||||
|
||||
Display_MouseMove(ui_info_array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ui_mouse_fix(int cx_, int cy_, int dx_, int dy_)
|
||||
{
|
||||
if ((*game::keyCatchers & 0x10) != 0 && !UI_KeysBypassMenu())
|
||||
{
|
||||
tagPOINT cursor;
|
||||
|
||||
CL_ShowSystemCursor(0);
|
||||
CL_GetCursorPos(&cursor);
|
||||
|
||||
ui_mouse_event(0, cursor.x, cursor.y);
|
||||
return 0;
|
||||
}
|
||||
return utils::hook::invoke<int>(0x1384C0_b, cx_, cy_, dx_, dy_);
|
||||
}
|
||||
|
||||
bool open_script_main_menu()
|
||||
{
|
||||
if (!script_main_menu.empty())
|
||||
{
|
||||
void* menu = Menus_FindByName(ui_info_array, script_main_menu.data());
|
||||
if (menu)
|
||||
{
|
||||
Menus_Open(ui_info_array, menu, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ui_set_active_menu_stub(int client_num, int idx)
|
||||
{
|
||||
if (open_script_main_menu())
|
||||
{
|
||||
*game::keyCatchers = *game::keyCatchers & 1 | 0x10;
|
||||
return;
|
||||
}
|
||||
return utils::hook::invoke<void>(0x1E4D80_b, client_num, idx); // UI_SetActiveMenu
|
||||
}
|
||||
}
|
||||
|
||||
void set_script_main_menu(const std::string& menu)
|
||||
{
|
||||
script_main_menu = menu;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (!game::environment::is_mp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ui_info_array = reinterpret_cast<void*>(0x352E580_b);
|
||||
|
||||
// add back legacy menu precache
|
||||
cg_set_config_values_hook.create(0x11AC50_b, cg_set_config_values_stub);
|
||||
|
||||
// add legacy menu mouse fix
|
||||
utils::hook::call(0x5BA535_b, ui_mouse_fix);
|
||||
|
||||
// add script main menu (ESC)
|
||||
utils::hook::call(0x135C82_b, ui_set_active_menu_stub);
|
||||
|
||||
command::add("openmenu", [](const command::params& params)
|
||||
{
|
||||
if (params.size() != 2)
|
||||
{
|
||||
console::info("usage: openmenu <name>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
*game::keyCatchers = *game::keyCatchers & 1 | 0x10;
|
||||
game::Menus_OpenByName(0, params.get(1));
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(menus::component)
|
6
src/client/component/menus.hpp
Normal file
6
src/client/component/menus.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace menus
|
||||
{
|
||||
void set_script_main_menu(const std::string& menu);
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "menus.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
@ -63,17 +64,66 @@ namespace patches
|
||||
return com_register_dvars_hook.invoke<void>();
|
||||
}
|
||||
|
||||
utils::hook::detour set_client_dvar_from_server_hook;
|
||||
utils::hook::detour cg_set_client_dvar_from_server_hook;
|
||||
|
||||
void set_client_dvar_from_server_stub(void* clientNum, void* cgameGlob, const char* dvar, const char* value)
|
||||
void cg_set_client_dvar_from_server_stub(void* clientNum, void* cgameGlob, const char* dvar_hash, const char* value)
|
||||
{
|
||||
const auto dvar_lowercase = utils::string::to_lower(dvar);
|
||||
if (dvar_lowercase == "cg_fov"s || dvar_lowercase == "cg_fovMin"s)
|
||||
int hash = atoi(dvar_hash);
|
||||
auto* dvar = game::Dvar_FindMalleableVar(hash);
|
||||
|
||||
if (hash == game::generateHashValue("cg_fov") ||
|
||||
hash == game::generateHashValue("cg_fovMin") ||
|
||||
hash == game::generateHashValue("cg_fovScale"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
set_client_dvar_from_server_hook.invoke<void>(0x11AA90_b, clientNum, cgameGlob, dvar, value);
|
||||
if (hash == game::generateHashValue("g_scriptMainMenu"))
|
||||
{
|
||||
menus::set_script_main_menu(value);
|
||||
}
|
||||
|
||||
// register new dvar
|
||||
if (!dvar)
|
||||
{
|
||||
game::Dvar_RegisterString(hash, "", value, game::DVAR_FLAG_EXTERNAL);
|
||||
return;
|
||||
}
|
||||
|
||||
// only set if dvar has no flags or has cheat flag or has external flag
|
||||
if (dvar->flags == game::DVAR_FLAG_NONE ||
|
||||
(dvar->flags & game::DVAR_FLAG_CHEAT) != 0 ||
|
||||
(dvar->flags & game::DVAR_FLAG_EXTERNAL) != 0)
|
||||
{
|
||||
game::Dvar_SetFromStringFromSource(dvar, value, game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
|
||||
}
|
||||
|
||||
// original code
|
||||
int index = 0;
|
||||
auto result = utils::hook::invoke<bool>(0x4745E0_b, dvar, &index); // NetConstStrings_SV_GetNetworkDvarIndex
|
||||
if (result)
|
||||
{
|
||||
std::string index_str = std::to_string(index);
|
||||
return cg_set_client_dvar_from_server_hook.invoke<void>(clientNum, cgameGlob, index_str.data(), value);
|
||||
}
|
||||
}
|
||||
|
||||
game::dvar_t* get_client_dvar(const char* name)
|
||||
{
|
||||
game::dvar_t* dvar = game::Dvar_FindVar(name);
|
||||
if (!dvar)
|
||||
{
|
||||
static game::dvar_t dummy{0};
|
||||
dummy.hash = game::generateHashValue(name);
|
||||
return &dummy;
|
||||
}
|
||||
return dvar;
|
||||
}
|
||||
|
||||
bool get_client_dvar_hash(game::dvar_t* dvar, int* hash)
|
||||
{
|
||||
*hash = dvar->hash;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
|
||||
@ -386,11 +436,19 @@ namespace patches
|
||||
utils::hook::inject(0x54DCE5_b, VERSION);
|
||||
|
||||
// prevent servers overriding our fov
|
||||
set_client_dvar_from_server_hook.create(0x11AA90_b, set_client_dvar_from_server_stub);
|
||||
utils::hook::nop(0x17DA96_b, 0x16);
|
||||
utils::hook::nop(0xE00BE_b, 0x17);
|
||||
utils::hook::set<uint8_t>(0x307F39_b, 0xEB);
|
||||
|
||||
// make setclientdvar behave like older games
|
||||
cg_set_client_dvar_from_server_hook.create(0x11AA90_b, cg_set_client_dvar_from_server_stub);
|
||||
utils::hook::call(0x407EC5_b, get_client_dvar_hash); // setclientdvar
|
||||
utils::hook::call(0x4087C1_b, get_client_dvar_hash); // setclientdvars
|
||||
utils::hook::call(0x407E8E_b, get_client_dvar); // setclientdvar
|
||||
utils::hook::call(0x40878A_b, get_client_dvar); // setclientdvars
|
||||
utils::hook::set<uint8_t>(0x407EB6_b, 0xEB); // setclientdvar
|
||||
utils::hook::set<uint8_t>(0x4087B2_b, 0xEB); // setclientdvars
|
||||
|
||||
// some [data validation] anti tamper thing that kills performance
|
||||
dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ);
|
||||
|
||||
@ -439,4 +497,4 @@ namespace patches
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(patches::component)
|
||||
REGISTER_COMPONENT(patches::component)
|
@ -882,6 +882,7 @@ namespace game
|
||||
DVAR_FLAG_SAVED = 0x1,
|
||||
DVAR_FLAG_LATCHED = 0x2,
|
||||
DVAR_FLAG_CHEAT = 0x4,
|
||||
DVAR_FLAG_EXTERNAL = 0x100,
|
||||
DVAR_FLAG_REPLICATED = 0x8,
|
||||
DVAR_FLAG_WRITE = 0x800,
|
||||
DVAR_FLAG_READ = 0x2000,
|
||||
|
@ -60,6 +60,7 @@ namespace game
|
||||
|
||||
WEAK symbol<bool()> CL_IsCgameInitialized{0x1A3210, 0x33C640};
|
||||
WEAK symbol<void(int a1)> CL_VirtualLobbyShutdown{0x0, 0x0};
|
||||
WEAK symbol<const char* (int configStringIndex)> CL_GetConfigString{0x0, 0x33B820};
|
||||
|
||||
WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x41BAD0, 0x1857D0};
|
||||
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x41A600, 0x183EB0};
|
||||
@ -70,6 +71,7 @@ namespace game
|
||||
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0x41B5F0, 0x185390};
|
||||
WEAK symbol<void(const char*, const char*,
|
||||
DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x41BD90, 0x185BD0};
|
||||
WEAK symbol<void(dvar_t* dvar, const char* string, DvarSetSource source)> Dvar_SetFromStringFromSource{0x0, 0x185C60};
|
||||
|
||||
WEAK symbol<dvar_t*(int hash, const char* name, bool value,
|
||||
unsigned int flags)> Dvar_RegisterBool{0x419220, 0x182340};
|
||||
@ -139,6 +141,9 @@ namespace game
|
||||
#define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \
|
||||
H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S, CP, CC, game::R_GetSomething(S))
|
||||
|
||||
WEAK symbol<int()> R_PopRemoteScreenUpdate{0x0, 0x6A6D60};
|
||||
WEAK symbol<void(int)> R_PushRemoteScreenUpdate{0x0, 0x6A6E60};
|
||||
|
||||
WEAK symbol<char* (GfxImage* image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount,
|
||||
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const D3D11_SUBRESOURCE_DATA* initData)> Image_Setup{0x560740, 0x683890};
|
||||
|
||||
@ -157,6 +162,8 @@ namespace game
|
||||
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x2E8FC0, 0x459CD0};
|
||||
|
||||
WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x1BCED0, 0x362840};
|
||||
WEAK symbol<float()> ScrPlace_HiResGetScaleX{0x0, 0x362910};
|
||||
WEAK symbol<float()> ScrPlace_HiResGetScaleY{0x0, 0x362930};
|
||||
|
||||
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)>
|
||||
DB_EnumXAssets_Internal{0x1F0BF0, 0x394C60};
|
||||
@ -174,6 +181,8 @@ namespace game
|
||||
WEAK symbol<void()> LUI_LeaveCriticalSection{0xF6C40, 0x26BDC0};
|
||||
|
||||
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x4F43C0, 0x389F70};
|
||||
WEAK symbol<void(int clientNum, const char* menu)> Menus_OpenByName{0x0, 0x1E1270};
|
||||
WEAK symbol<void(int clientNum, const char* menu)> Menus_CloseByName{0x0, 0x1DA4C0};
|
||||
|
||||
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x3C0F50, 0x507FD0};
|
||||
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x3C1210, 0x5083A0};
|
||||
@ -216,6 +225,7 @@ namespace game
|
||||
WEAK symbol<const char*(const char*)> UI_GetGameTypeDisplayName{0x0, 0x4DD8C0};
|
||||
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0x3F3AA0, 0x1E35B0};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0x3F5D90, 0x0};
|
||||
WEAK symbol<void(void* dc, void* menuList, int close)> UI_AddMenuList{0x0, 0x1D9960};
|
||||
|
||||
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x3840A0, 0x4E8BC0};
|
||||
|
||||
@ -270,7 +280,7 @@ namespace game
|
||||
WEAK symbol<int> svs_numclients{0x0, 0x2DC338C};
|
||||
WEAK symbol<int> gameTime{0x0, 0x7361F9C};
|
||||
|
||||
WEAK symbol<int> sv_serverId_value{0x0, 0x0};
|
||||
WEAK symbol<int> sv_serverId_value{0x0, 0xB7F9630};
|
||||
|
||||
WEAK symbol<bool> virtualLobby_loaded{0x0, 0x2E6EC9D};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user